source: mainline/uspace/srv/hid/compositor/compositor.c@ 1bb9833

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1bb9833 was 67472b9b, checked in by Jan Vesely <jano.vesely@…>, 12 years ago

libc, libdrv: Move graph dev iface to libdrv.

  • Property mode set to 100644
File size: 61.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 compositor
30 * @{
31 */
32/** @file
33 */
34
35#include <sys/types.h>
36#include <stdbool.h>
37#include <errno.h>
38#include <str_error.h>
39#include <byteorder.h>
40#include <stdio.h>
41#include <libc.h>
42
43#include <align.h>
44#include <as.h>
45#include <malloc.h>
46
47#include <atomic.h>
48#include <fibril_synch.h>
49#include <adt/prodcons.h>
50#include <adt/list.h>
51#include <io/input.h>
52#include <ipc/graph.h>
53#include <ipc/window.h>
54
55#include <async.h>
56#include <loc.h>
57#include <devman.h>
58
59#include <event.h>
60#include <graph_iface.h>
61#include <io/keycode.h>
62#include <io/mode.h>
63#include <io/visualizer.h>
64#include <io/window.h>
65
66#include <transform.h>
67#include <rectangle.h>
68#include <surface.h>
69#include <cursor.h>
70#include <source.h>
71#include <drawctx.h>
72#include <codec/tga.h>
73
74#include "images.h"
75#include "compositor.h"
76
77#define NAME "compositor"
78#define NAMESPACE "comp"
79
80/* Until there is blitter support and some further optimizations, window
81 * animations are too slow to be practically usable. */
82#ifndef ANIMATE_WINDOW_TRANSFORMS
83#define ANIMATE_WINDOW_TRANSFORMS 0
84#endif
85
86static char *server_name;
87static sysarg_t coord_origin;
88static pixel_t bg_color;
89
90typedef struct {
91 link_t link;
92 atomic_t ref_cnt;
93 service_id_t in_dsid;
94 service_id_t out_dsid;
95 prodcons_t queue;
96 transform_t transform;
97 double dx;
98 double dy;
99 double fx;
100 double fy;
101 double angle;
102 uint8_t opacity;
103 surface_t *surface;
104} window_t;
105
106static service_id_t winreg_id;
107static sysarg_t window_id = 0;
108static FIBRIL_MUTEX_INITIALIZE(window_list_mtx);
109static LIST_INITIALIZE(window_list);
110static double scale_back_x;
111static double scale_back_y;
112
113typedef struct {
114 link_t link;
115 sysarg_t id;
116 uint8_t state;
117 desktop_point_t pos;
118 sysarg_t btn_num;
119 desktop_point_t btn_pos;
120 desktop_vector_t accum;
121 sysarg_t grab_flags;
122 bool pressed;
123 cursor_t cursor;
124 window_t ghost;
125 desktop_vector_t accum_ghost;
126} pointer_t;
127
128static sysarg_t pointer_id = 0;
129static FIBRIL_MUTEX_INITIALIZE(pointer_list_mtx);
130static LIST_INITIALIZE(pointer_list);
131
132typedef struct {
133 link_t link;
134 service_id_t dsid;
135 vslmode_t mode;
136 async_sess_t *sess;
137 desktop_point_t pos;
138 surface_t *surface;
139} viewport_t;
140
141static desktop_rect_t viewport_bound_rect;
142static FIBRIL_MUTEX_INITIALIZE(viewport_list_mtx);
143static LIST_INITIALIZE(viewport_list);
144
145static FIBRIL_MUTEX_INITIALIZE(discovery_mtx);
146
147/** Input server proxy */
148static input_t *input;
149
150static int comp_key_press(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
151static int comp_mouse_move(input_t *, int, int);
152static int comp_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
153static int comp_mouse_button(input_t *, int, int);
154
155static input_ev_ops_t input_ev_ops = {
156 .key = comp_key_press,
157 .move = comp_mouse_move,
158 .abs_move = comp_abs_move,
159 .button = comp_mouse_button
160};
161
162static void input_disconnect(void);
163
164
165static pointer_t *input_pointer(input_t *input)
166{
167 return input->user;
168}
169
170static pointer_t *pointer_create()
171{
172 pointer_t *p = (pointer_t *) malloc(sizeof(pointer_t));
173 if (!p) {
174 return NULL;
175 }
176
177 link_initialize(&p->link);
178 p->pos.x = coord_origin;
179 p->pos.y = coord_origin;
180 p->btn_num = 1;
181 p->btn_pos = p->pos;
182 p->accum.x = 0;
183 p->accum.y = 0;
184 p->grab_flags = GF_EMPTY;
185 p->pressed = false;
186 p->state = 0;
187 cursor_init(&p->cursor, CURSOR_DECODER_EMBEDDED, NULL);
188
189 /* Ghost window for transformation animation. */
190 transform_identity(&p->ghost.transform);
191 transform_translate(&p->ghost.transform, coord_origin, coord_origin);
192 p->ghost.dx = coord_origin;
193 p->ghost.dy = coord_origin;
194 p->ghost.fx = 1;
195 p->ghost.fy = 1;
196 p->ghost.angle = 0;
197 p->ghost.opacity = 255;
198 p->ghost.surface = NULL;
199 p->accum_ghost.x = 0;
200 p->accum_ghost.y = 0;
201
202 return p;
203}
204
205static void pointer_destroy(pointer_t *p)
206{
207 if (p) {
208 cursor_release(&p->cursor);
209 free(p);
210 }
211}
212
213static window_t *window_create(sysarg_t x_offset, sysarg_t y_offset)
214{
215 window_t *win = (window_t *) malloc(sizeof(window_t));
216 if (!win) {
217 return NULL;
218 }
219
220 link_initialize(&win->link);
221 atomic_set(&win->ref_cnt, 0);
222 prodcons_initialize(&win->queue);
223 transform_identity(&win->transform);
224 transform_translate(&win->transform,
225 coord_origin + x_offset, coord_origin + y_offset);
226 win->dx = coord_origin + x_offset;
227 win->dy = coord_origin + y_offset;
228 win->fx = 1;
229 win->fy = 1;
230 win->angle = 0;
231 win->opacity = 255;
232 win->surface = NULL;
233
234 return win;
235}
236
237static void window_destroy(window_t *win)
238{
239 if (win && atomic_get(&win->ref_cnt) == 0) {
240 while (!list_empty(&win->queue.list)) {
241 window_event_t *event = (window_event_t *) list_first(&win->queue.list);
242 list_remove(&event->link);
243 free(event);
244 }
245
246 if (win->surface) {
247 surface_destroy(win->surface);
248 }
249 free(win);
250 }
251}
252
253static bool comp_coord_to_client(sysarg_t x_in, sysarg_t y_in, transform_t win_trans,
254 sysarg_t x_lim, sysarg_t y_lim, sysarg_t *x_out, sysarg_t *y_out)
255{
256 double x = x_in;
257 double y = y_in;
258 transform_invert(&win_trans);
259 transform_apply_affine(&win_trans, &x, &y);
260
261 /* Since client coordinate origin is (0, 0), it is necessary to check
262 * coordinates to avoid underflow. Moreover, it is convenient to also
263 * check against provided upper limits to determine whether the converted
264 * coordinates are within the client window. */
265 if (x < 0 || y < 0) {
266 return false;
267 } else {
268 (*x_out) = (sysarg_t) (x + 0.5);
269 (*y_out) = (sysarg_t) (y + 0.5);
270
271 if ((*x_out) >= x_lim || (*y_out) >= y_lim) {
272 return false;
273 } else {
274 return true;
275 }
276 }
277}
278
279static void comp_coord_from_client(double x_in, double y_in, transform_t win_trans,
280 sysarg_t *x_out, sysarg_t *y_out)
281{
282 double x = x_in;
283 double y = y_in;
284 transform_apply_affine(&win_trans, &x, &y);
285
286 /* It is assumed that compositor coordinate origin is chosen in such way,
287 * that underflow/overflow here would be unlikely. */
288 (*x_out) = (sysarg_t) (x + 0.5);
289 (*y_out) = (sysarg_t) (y + 0.5);
290}
291
292static void comp_coord_bounding_rect(double x_in, double y_in,
293 double w_in, double h_in, transform_t win_trans,
294 sysarg_t *x_out, sysarg_t *y_out, sysarg_t *w_out, sysarg_t *h_out)
295{
296 if (w_in > 0 && h_in > 0) {
297 sysarg_t x[4];
298 sysarg_t y[4];
299 comp_coord_from_client(x_in, y_in, win_trans, &x[0], &y[0]);
300 comp_coord_from_client(x_in + w_in - 1, y_in, win_trans, &x[1], &y[1]);
301 comp_coord_from_client(x_in + w_in - 1, y_in + h_in - 1, win_trans, &x[2], &y[2]);
302 comp_coord_from_client(x_in, y_in + h_in - 1, win_trans, &x[3], &y[3]);
303 (*x_out) = x[0];
304 (*y_out) = y[0];
305 (*w_out) = x[0];
306 (*h_out) = y[0];
307 for (int i = 1; i < 4; ++i) {
308 (*x_out) = (x[i] < (*x_out)) ? x[i] : (*x_out);
309 (*y_out) = (y[i] < (*y_out)) ? y[i] : (*y_out);
310 (*w_out) = (x[i] > (*w_out)) ? x[i] : (*w_out);
311 (*h_out) = (y[i] > (*h_out)) ? y[i] : (*h_out);
312 }
313 (*w_out) = (*w_out) - (*x_out) + 1;
314 (*h_out) = (*h_out) - (*y_out) + 1;
315 } else {
316 (*x_out) = 0;
317 (*y_out) = 0;
318 (*w_out) = 0;
319 (*h_out) = 0;
320 }
321}
322
323static void comp_restrict_pointers(void)
324{
325 fibril_mutex_lock(&viewport_list_mtx);
326
327 sysarg_t x_res = coord_origin;
328 sysarg_t y_res = coord_origin;
329 sysarg_t w_res = 0;
330 sysarg_t h_res = 0;
331
332 if (!list_empty(&viewport_list)) {
333 viewport_t *vp = (viewport_t *) list_first(&viewport_list);
334 x_res = vp->pos.x;
335 y_res = vp->pos.y;
336 surface_get_resolution(vp->surface, &w_res, &h_res);
337 }
338
339 list_foreach(viewport_list, link, viewport_t, vp) {
340 sysarg_t w_vp, h_vp;
341 surface_get_resolution(vp->surface, &w_vp, &h_vp);
342 rectangle_union(
343 x_res, y_res, w_res, h_res,
344 vp->pos.x, vp->pos.y, w_vp, h_vp,
345 &x_res, &y_res, &w_res, &h_res);
346 }
347
348 viewport_bound_rect.x = x_res;
349 viewport_bound_rect.y = y_res;
350 viewport_bound_rect.w = w_res;
351 viewport_bound_rect.h = h_res;
352
353 fibril_mutex_unlock(&viewport_list_mtx);
354
355 fibril_mutex_lock(&pointer_list_mtx);
356
357 list_foreach(pointer_list, link, pointer_t, ptr) {
358 ptr->pos.x = ptr->pos.x > viewport_bound_rect.x ? ptr->pos.x : viewport_bound_rect.x;
359 ptr->pos.y = ptr->pos.y > viewport_bound_rect.y ? ptr->pos.y : viewport_bound_rect.y;
360 ptr->pos.x = ptr->pos.x < viewport_bound_rect.x + viewport_bound_rect.w ?
361 ptr->pos.x : viewport_bound_rect.x + viewport_bound_rect.w;
362 ptr->pos.y = ptr->pos.y < viewport_bound_rect.y + viewport_bound_rect.h ?
363 ptr->pos.y : viewport_bound_rect.y + viewport_bound_rect.h;
364 }
365
366 fibril_mutex_unlock(&pointer_list_mtx);
367}
368
369static void comp_damage(sysarg_t x_dmg_glob, sysarg_t y_dmg_glob,
370 sysarg_t w_dmg_glob, sysarg_t h_dmg_glob)
371{
372 fibril_mutex_lock(&viewport_list_mtx);
373 fibril_mutex_lock(&window_list_mtx);
374 fibril_mutex_lock(&pointer_list_mtx);
375
376 list_foreach(viewport_list, link, viewport_t, vp) {
377 /* Determine what part of the viewport must be updated. */
378 sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
379 surface_get_resolution(vp->surface, &w_dmg_vp, &h_dmg_vp);
380 bool isec_vp = rectangle_intersect(
381 x_dmg_glob, y_dmg_glob, w_dmg_glob, h_dmg_glob,
382 vp->pos.x, vp->pos.y, w_dmg_vp, h_dmg_vp,
383 &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
384
385 if (isec_vp) {
386
387 /* Paint background color. */
388 for (sysarg_t y = y_dmg_vp - vp->pos.y; y < y_dmg_vp - vp->pos.y + h_dmg_vp; ++y) {
389 pixel_t *dst = pixelmap_pixel_at(
390 surface_pixmap_access(vp->surface), x_dmg_vp - vp->pos.x, y);
391 sysarg_t count = w_dmg_vp;
392 while (count-- != 0) {
393 *dst++ = bg_color;
394 }
395 }
396 surface_add_damaged_region(vp->surface,
397 x_dmg_vp - vp->pos.x, y_dmg_vp - vp->pos.y, w_dmg_vp, h_dmg_vp);
398
399 transform_t transform;
400 source_t source;
401 drawctx_t context;
402
403 source_init(&source);
404 source_set_filter(&source, filter_nearest);
405 drawctx_init(&context, vp->surface);
406 drawctx_set_compose(&context, compose_over);
407 drawctx_set_source(&context, &source);
408
409 /* For each window. */
410 for (link_t *link = window_list.head.prev;
411 link != &window_list.head; link = link->prev) {
412
413 /* Determine what part of the window intersects with the
414 * updated area of the current viewport. */
415 window_t *win = list_get_instance(link, window_t, link);
416 if (!win->surface) {
417 continue;
418 }
419 sysarg_t x_dmg_win, y_dmg_win, w_dmg_win, h_dmg_win;
420 surface_get_resolution(win->surface, &w_dmg_win, &h_dmg_win);
421 comp_coord_bounding_rect(0, 0, w_dmg_win, h_dmg_win, win->transform,
422 &x_dmg_win, &y_dmg_win, &w_dmg_win, &h_dmg_win);
423 bool isec_win = rectangle_intersect(
424 x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp,
425 x_dmg_win, y_dmg_win, w_dmg_win, h_dmg_win,
426 &x_dmg_win, &y_dmg_win, &w_dmg_win, &h_dmg_win);
427
428 if (isec_win) {
429 /* Prepare conversion from global coordinates to viewport
430 * coordinates. */
431 transform = win->transform;
432 double_point_t pos;
433 pos.x = vp->pos.x;
434 pos.y = vp->pos.y;
435 transform_translate(&transform, -pos.x, -pos.y);
436
437 source_set_transform(&source, transform);
438 source_set_texture(&source, win->surface, false);
439 source_set_alpha(&source, PIXEL(win->opacity, 0, 0, 0));
440
441 drawctx_transfer(&context,
442 x_dmg_win - vp->pos.x, y_dmg_win - vp->pos.y, w_dmg_win, h_dmg_win);
443 }
444 }
445
446 list_foreach(pointer_list, link, pointer_t, ptr) {
447 if (ptr->ghost.surface) {
448
449 sysarg_t x_bnd_ghost, y_bnd_ghost, w_bnd_ghost, h_bnd_ghost;
450 sysarg_t x_dmg_ghost, y_dmg_ghost, w_dmg_ghost, h_dmg_ghost;
451 surface_get_resolution(ptr->ghost.surface, &w_bnd_ghost, &h_bnd_ghost);
452 comp_coord_bounding_rect(0, 0, w_bnd_ghost, h_bnd_ghost, ptr->ghost.transform,
453 &x_bnd_ghost, &y_bnd_ghost, &w_bnd_ghost, &h_bnd_ghost);
454 bool isec_ghost = rectangle_intersect(
455 x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp,
456 x_bnd_ghost, y_bnd_ghost, w_bnd_ghost, h_bnd_ghost,
457 &x_dmg_ghost, &y_dmg_ghost, &w_dmg_ghost, &h_dmg_ghost);
458
459 if (isec_ghost) {
460 /* FIXME: Ghost is currently drawn based on the bounding
461 * rectangle of the window, which is sufficient as long
462 * as the windows can be rotated only by 90 degrees.
463 * For ghost to be compatible with arbitrary-angle
464 * rotation, it should be drawn as four lines adjusted
465 * by the transformation matrix. That would however
466 * require to equip libdraw with line drawing functionality. */
467
468 transform_t transform = ptr->ghost.transform;
469 double_point_t pos;
470 pos.x = vp->pos.x;
471 pos.y = vp->pos.y;
472 transform_translate(&transform, -pos.x, -pos.y);
473
474 pixel_t ghost_color;
475
476 if (y_bnd_ghost == y_dmg_ghost) {
477 for (sysarg_t x = x_dmg_ghost - vp->pos.x;
478 x < x_dmg_ghost - vp->pos.x + w_dmg_ghost; ++x) {
479 ghost_color = surface_get_pixel(vp->surface,
480 x, y_dmg_ghost - vp->pos.y);
481 surface_put_pixel(vp->surface,
482 x, y_dmg_ghost - vp->pos.y, INVERT(ghost_color));
483 }
484 }
485
486 if (y_bnd_ghost + h_bnd_ghost == y_dmg_ghost + h_dmg_ghost) {
487 for (sysarg_t x = x_dmg_ghost - vp->pos.x;
488 x < x_dmg_ghost - vp->pos.x + w_dmg_ghost; ++x) {
489 ghost_color = surface_get_pixel(vp->surface,
490 x, y_dmg_ghost - vp->pos.y + h_dmg_ghost - 1);
491 surface_put_pixel(vp->surface,
492 x, y_dmg_ghost - vp->pos.y + h_dmg_ghost - 1, INVERT(ghost_color));
493 }
494 }
495
496 if (x_bnd_ghost == x_dmg_ghost) {
497 for (sysarg_t y = y_dmg_ghost - vp->pos.y;
498 y < y_dmg_ghost - vp->pos.y + h_dmg_ghost; ++y) {
499 ghost_color = surface_get_pixel(vp->surface,
500 x_dmg_ghost - vp->pos.x, y);
501 surface_put_pixel(vp->surface,
502 x_dmg_ghost - vp->pos.x, y, INVERT(ghost_color));
503 }
504 }
505
506 if (x_bnd_ghost + w_bnd_ghost == x_dmg_ghost + w_dmg_ghost) {
507 for (sysarg_t y = y_dmg_ghost - vp->pos.y;
508 y < y_dmg_ghost - vp->pos.y + h_dmg_ghost; ++y) {
509 ghost_color = surface_get_pixel(vp->surface,
510 x_dmg_ghost - vp->pos.x + w_dmg_ghost - 1, y);
511 surface_put_pixel(vp->surface,
512 x_dmg_ghost - vp->pos.x + w_dmg_ghost - 1, y, INVERT(ghost_color));
513 }
514 }
515 }
516
517 }
518 }
519
520 list_foreach(pointer_list, link, pointer_t, ptr) {
521
522 /* Determine what part of the pointer intersects with the
523 * updated area of the current viewport. */
524 sysarg_t x_dmg_ptr, y_dmg_ptr, w_dmg_ptr, h_dmg_ptr;
525 surface_t *sf_ptr = ptr->cursor.states[ptr->state];
526 surface_get_resolution(sf_ptr, &w_dmg_ptr, &h_dmg_ptr);
527 bool isec_ptr = rectangle_intersect(
528 x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp,
529 ptr->pos.x, ptr->pos.y, w_dmg_ptr, h_dmg_ptr,
530 &x_dmg_ptr, &y_dmg_ptr, &w_dmg_ptr, &h_dmg_ptr);
531
532 if (isec_ptr) {
533 /* Pointer is currently painted directly by copying pixels.
534 * However, it is possible to draw the pointer similarly
535 * as window by using drawctx_transfer. It would allow
536 * more sophisticated control over drawing, but would also
537 * cost more regarding the performance. */
538
539 sysarg_t x_vp = x_dmg_ptr - vp->pos.x;
540 sysarg_t y_vp = y_dmg_ptr - vp->pos.y;
541 sysarg_t x_ptr = x_dmg_ptr - ptr->pos.x;
542 sysarg_t y_ptr = y_dmg_ptr - ptr->pos.y;
543
544 for (sysarg_t y = 0; y < h_dmg_ptr; ++y) {
545 pixel_t *src = pixelmap_pixel_at(
546 surface_pixmap_access(sf_ptr), x_ptr, y_ptr + y);
547 pixel_t *dst = pixelmap_pixel_at(
548 surface_pixmap_access(vp->surface), x_vp, y_vp + y);
549 sysarg_t count = w_dmg_ptr;
550 while (count-- != 0) {
551 *dst = (*src & 0xff000000) ? *src : *dst;
552 ++dst; ++src;
553 }
554 }
555 surface_add_damaged_region(vp->surface, x_vp, y_vp, w_dmg_ptr, h_dmg_ptr);
556 }
557
558 }
559 }
560 }
561
562 fibril_mutex_unlock(&pointer_list_mtx);
563 fibril_mutex_unlock(&window_list_mtx);
564
565 /* Notify visualizers about updated regions. */
566 list_foreach(viewport_list, link, viewport_t, vp) {
567 sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
568 surface_get_damaged_region(vp->surface, &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
569 surface_reset_damaged_region(vp->surface);
570 visualizer_update_damaged_region(
571 vp->sess, x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp, 0, 0);
572 }
573
574 fibril_mutex_unlock(&viewport_list_mtx);
575}
576
577static void comp_window_get_event(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
578{
579 window_event_t *event = (window_event_t *) prodcons_consume(&win->queue);
580
581 ipc_callid_t callid;
582 size_t len;
583
584 if (!async_data_read_receive(&callid, &len)) {
585 async_answer_0(iid, EINVAL);
586 free(event);
587 return;
588 }
589 int rc = async_data_read_finalize(callid, event, len);
590 if (rc != EOK) {
591 async_answer_0(iid, ENOMEM);
592 free(event);
593 return;
594 }
595 async_answer_0(iid, EOK);
596
597 free(event);
598}
599
600static void comp_window_damage(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
601{
602 double x = IPC_GET_ARG1(*icall);
603 double y = IPC_GET_ARG2(*icall);
604 double width = IPC_GET_ARG3(*icall);
605 double height = IPC_GET_ARG4(*icall);
606
607 if (width == 0 || height == 0) {
608 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
609 } else {
610 fibril_mutex_lock(&window_list_mtx);
611 sysarg_t x_dmg_glob, y_dmg_glob, w_dmg_glob, h_dmg_glob;
612 comp_coord_bounding_rect(x - 1, y - 1, width + 2, height + 2,
613 win->transform, &x_dmg_glob, &y_dmg_glob, &w_dmg_glob, &h_dmg_glob);
614 fibril_mutex_unlock(&window_list_mtx);
615 comp_damage(x_dmg_glob, y_dmg_glob, w_dmg_glob, h_dmg_glob);
616 }
617
618 async_answer_0(iid, EOK);
619}
620
621static void comp_window_grab(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
622{
623 sysarg_t pos_id = IPC_GET_ARG1(*icall);
624 sysarg_t grab_flags = IPC_GET_ARG2(*icall);
625
626 fibril_mutex_lock(&pointer_list_mtx);
627 list_foreach(pointer_list, link, pointer_t, pointer) {
628 if (pointer->id == pos_id) {
629 pointer->grab_flags = pointer->pressed ? grab_flags : GF_EMPTY;
630 // TODO change pointer->state according to grab_flags
631 break;
632 }
633 }
634 fibril_mutex_unlock(&pointer_list_mtx);
635
636 if ((grab_flags & GF_RESIZE_X) || (grab_flags & GF_RESIZE_Y)) {
637 scale_back_x = 1;
638 scale_back_y = 1;
639 }
640
641 async_answer_0(iid, EOK);
642}
643
644static void comp_window_resize(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
645{
646 int rc;
647
648 ipc_callid_t callid;
649 size_t size;
650 unsigned int flags;
651
652 /* Start sharing resized window with client. */
653 if (!async_share_out_receive(&callid, &size, &flags)) {
654 async_answer_0(iid, EINVAL);
655 return;
656 }
657 void *new_cell_storage;
658 rc = async_share_out_finalize(callid, &new_cell_storage);
659 if ((rc != EOK) || (new_cell_storage == AS_MAP_FAILED)) {
660 async_answer_0(iid, ENOMEM);
661 return;
662 }
663
664 /* Create new surface for the resized window. */
665 surface_t *new_surface = surface_create(
666 IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall),
667 new_cell_storage, SURFACE_FLAG_SHARED);
668 if (!new_surface) {
669 as_area_destroy(new_cell_storage);
670 async_answer_0(iid, ENOMEM);
671 return;
672 }
673
674 /* Switch new surface with old surface and calculate damage. */
675 fibril_mutex_lock(&window_list_mtx);
676
677 sysarg_t old_width = 0;
678 sysarg_t old_height = 0;
679 if (win->surface) {
680 surface_get_resolution(win->surface, &old_width, &old_height);
681 surface_destroy(win->surface);
682 }
683
684 win->surface = new_surface;
685
686 sysarg_t new_width = 0;
687 sysarg_t new_height = 0;
688 surface_get_resolution(win->surface, &new_width, &new_height);
689
690 sysarg_t x, y;
691 sysarg_t width = old_width > new_width ? old_width : new_width;
692 sysarg_t height = old_height > new_height ? old_height : new_height;
693 comp_coord_bounding_rect(0, 0, width, height, win->transform, &x, &y, &width, &height);
694
695 fibril_mutex_unlock(&window_list_mtx);
696
697 comp_damage(x, y, width, height);
698
699 async_answer_0(iid, EOK);
700}
701
702static void comp_post_event_win(window_event_t *event, window_t *target)
703{
704 fibril_mutex_lock(&window_list_mtx);
705
706 list_foreach(window_list, link, window_t, window) {
707 if (window == target) {
708 prodcons_produce(&window->queue, &event->link);
709 fibril_mutex_unlock(&window_list_mtx);
710 return;
711 }
712 }
713
714 fibril_mutex_unlock(&window_list_mtx);
715 free(event);
716}
717
718static void comp_post_event_top(window_event_t *event)
719{
720 fibril_mutex_lock(&window_list_mtx);
721 window_t *win = (window_t *) list_first(&window_list);
722 if (win) {
723 prodcons_produce(&win->queue, &event->link);
724 } else {
725 free(event);
726 }
727 fibril_mutex_unlock(&window_list_mtx);
728}
729
730static void comp_window_close(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
731{
732 /* Stop managing the window. */
733 fibril_mutex_lock(&window_list_mtx);
734 list_remove(&win->link);
735 window_t *win_focus = (window_t *) list_first(&window_list);
736 window_event_t *event_focus = (window_event_t *) malloc(sizeof(window_event_t));
737 if (event_focus) {
738 link_initialize(&event_focus->link);
739 event_focus->type = ET_WINDOW_FOCUS;
740 }
741 fibril_mutex_unlock(&window_list_mtx);
742
743 if (event_focus && win_focus) {
744 comp_post_event_win(event_focus, win_focus);
745 }
746
747 loc_service_unregister(win->in_dsid);
748 loc_service_unregister(win->out_dsid);
749
750 /* In case the client was killed, input fibril of the window might be
751 * still blocked on the condition within comp_window_get_event. */
752 window_event_t *event_dummy = (window_event_t *) malloc(sizeof(window_event_t));
753 if (event_dummy) {
754 link_initialize(&event_dummy->link);
755 prodcons_produce(&win->queue, &event_dummy->link);
756 }
757
758 /* Calculate damage. */
759 sysarg_t x = 0;
760 sysarg_t y = 0;
761 sysarg_t width = 0;
762 sysarg_t height = 0;
763 if (win->surface) {
764 surface_get_resolution(win->surface, &width, &height);
765 comp_coord_bounding_rect(
766 0, 0, width, height, win->transform, &x, &y, &width, &height);
767 }
768
769 comp_damage(x, y, width, height);
770
771 async_answer_0(iid, EOK);
772}
773
774static void comp_window_close_request(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
775{
776 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
777 if (event == NULL) {
778 async_answer_0(iid, ENOMEM);
779 return;
780 }
781
782 link_initialize(&event->link);
783 event->type = ET_WINDOW_CLOSE;
784
785 prodcons_produce(&win->queue, &event->link);
786 async_answer_0(iid, EOK);
787}
788
789static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
790{
791 ipc_call_t call;
792 ipc_callid_t callid;
793 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*icall);
794
795 /* Allocate resources for new window and register it to the location service. */
796 if (service_id == winreg_id) {
797 async_answer_0(iid, EOK);
798
799 callid = async_get_call(&call);
800 if (IPC_GET_IMETHOD(call) == WINDOW_REGISTER) {
801 fibril_mutex_lock(&window_list_mtx);
802
803 window_t *win = window_create(IPC_GET_ARG1(call), IPC_GET_ARG2(call));
804 if (!win) {
805 async_answer_2(callid, ENOMEM, 0, 0);
806 return;
807 }
808
809 char name_in[LOC_NAME_MAXLEN + 1];
810 snprintf(name_in, LOC_NAME_MAXLEN, "%s%s/win%zuin", NAMESPACE,
811 server_name, window_id);
812
813 char name_out[LOC_NAME_MAXLEN + 1];
814 snprintf(name_out, LOC_NAME_MAXLEN, "%s%s/win%zuout", NAMESPACE,
815 server_name, window_id);
816
817 ++window_id;
818
819 if (loc_service_register(name_in, &win->in_dsid) != EOK) {
820 window_destroy(win);
821 async_answer_2(callid, EINVAL, 0, 0);
822 return;
823 }
824
825 if (loc_service_register(name_out, &win->out_dsid) != EOK) {
826 loc_service_unregister(win->in_dsid);
827 window_destroy(win);
828 async_answer_2(callid, EINVAL, 0, 0);
829 return;
830 }
831
832 window_t *win_unfocus = (window_t *) list_first(&window_list);
833 list_prepend(&win->link, &window_list);
834
835 window_event_t *event_unfocus = (window_event_t *) malloc(sizeof(window_event_t));
836 if (event_unfocus) {
837 link_initialize(&event_unfocus->link);
838 event_unfocus->type = ET_WINDOW_UNFOCUS;
839 }
840
841 async_answer_2(callid, EOK, win->in_dsid, win->out_dsid);
842 fibril_mutex_unlock(&window_list_mtx);
843
844 if (event_unfocus && win_unfocus) {
845 comp_post_event_win(event_unfocus, win_unfocus);
846 }
847
848 return;
849 } else {
850 async_answer_0(callid, EINVAL);
851 return;
852 }
853 }
854
855 /* Match the client with pre-allocated window. */
856 window_t *win = NULL;
857 fibril_mutex_lock(&window_list_mtx);
858 list_foreach(window_list, link, window_t, cur) {
859 if (cur->in_dsid == service_id || cur->out_dsid == service_id) {
860 win = cur;
861 break;
862 }
863 }
864 fibril_mutex_unlock(&window_list_mtx);
865
866 if (win) {
867 atomic_inc(&win->ref_cnt);
868 async_answer_0(iid, EOK);
869 } else {
870 async_answer_0(iid, EINVAL);
871 return;
872 }
873
874 /* Each client establishes two separate connections. */
875 if (win->in_dsid == service_id) {
876 while (true) {
877 callid = async_get_call(&call);
878
879 if (!IPC_GET_IMETHOD(call)) {
880 async_answer_0(callid, EOK);
881 atomic_dec(&win->ref_cnt);
882 window_destroy(win);
883 return;
884 }
885
886 switch (IPC_GET_IMETHOD(call)) {
887 case WINDOW_GET_EVENT:
888 comp_window_get_event(win, callid, &call);
889 break;
890 default:
891 async_answer_0(callid, EINVAL);
892 }
893 }
894 } else if (win->out_dsid == service_id) {
895 while (true) {
896 callid = async_get_call(&call);
897
898 if (!IPC_GET_IMETHOD(call)) {
899 comp_window_close(win, callid, &call);
900 atomic_dec(&win->ref_cnt);
901 window_destroy(win);
902 return;
903 }
904
905 switch (IPC_GET_IMETHOD(call)) {
906 case WINDOW_DAMAGE:
907 comp_window_damage(win, callid, &call);
908 break;
909 case WINDOW_GRAB:
910 comp_window_grab(win, callid, &call);
911 break;
912 case WINDOW_RESIZE:
913 comp_window_resize(win, callid, &call);
914 break;
915 case WINDOW_CLOSE:
916 /* Postpone the closing until the phone is hung up to cover
917 * the case when the client is killed abruptly. */
918 async_answer_0(callid, EOK);
919 break;
920 case WINDOW_CLOSE_REQUEST:
921 comp_window_close_request(win, callid, &call);
922 break;
923 default:
924 async_answer_0(callid, EINVAL);
925 }
926 }
927 }
928}
929
930static void comp_mode_change(viewport_t *vp, ipc_callid_t iid, ipc_call_t *icall)
931{
932 int rc;
933 sysarg_t mode_idx = IPC_GET_ARG2(*icall);
934 fibril_mutex_lock(&viewport_list_mtx);
935
936 /* Retrieve the mode that shall be set. */
937 vslmode_t new_mode;
938 rc = visualizer_get_mode(vp->sess, &new_mode, mode_idx);
939 if (rc != EOK) {
940 fibril_mutex_unlock(&viewport_list_mtx);
941 async_answer_0(iid, EINVAL);
942 return;
943 }
944
945 /* Create surface with respect to the retrieved mode. */
946 surface_t *new_surface = surface_create(new_mode.screen_width,
947 new_mode.screen_height, NULL, SURFACE_FLAG_SHARED);
948 if (!new_surface) {
949 fibril_mutex_unlock(&viewport_list_mtx);
950 async_answer_0(iid, ENOMEM);
951 return;
952 }
953
954 /* Try to set the mode and share out the surface. */
955 rc = visualizer_set_mode(vp->sess,
956 new_mode.index, new_mode.version, surface_direct_access(new_surface));
957 if (rc != EOK) {
958 surface_destroy(new_surface);
959 fibril_mutex_unlock(&viewport_list_mtx);
960 async_answer_0(iid, rc);
961 return;
962 }
963
964 /* Destroy old surface and update viewport. */
965 surface_destroy(vp->surface);
966 vp->mode = new_mode;
967 vp->surface = new_surface;
968
969 fibril_mutex_unlock(&viewport_list_mtx);
970 async_answer_0(iid, EOK);
971
972 comp_restrict_pointers();
973 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
974}
975
976static void viewport_destroy(viewport_t *vp)
977{
978 if (vp) {
979 visualizer_yield(vp->sess);
980 surface_destroy(vp->surface);
981 async_hangup(vp->sess);
982 free(vp);
983 }
984}
985
986static void comp_visualizer_disconnect(viewport_t *vp, ipc_callid_t iid, ipc_call_t *icall)
987{
988 /* Release viewport resources. */
989 fibril_mutex_lock(&viewport_list_mtx);
990 list_remove(&vp->link);
991 viewport_destroy(vp);
992
993 /* Terminate compositor if there are no more viewports. */
994 if (list_empty(&viewport_list)) {
995 fibril_mutex_unlock(&viewport_list_mtx);
996 loc_service_unregister(winreg_id);
997 input_disconnect();
998
999 /* Close all clients and their windows. */
1000 fibril_mutex_lock(&window_list_mtx);
1001 list_foreach(window_list, link, window_t, win) {
1002 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1003 if (event) {
1004 link_initialize(&event->link);
1005 event->type = WINDOW_CLOSE;
1006 prodcons_produce(&win->queue, &event->link);
1007 }
1008 }
1009 fibril_mutex_unlock(&window_list_mtx);
1010
1011 async_answer_0(iid, EOK);
1012
1013 /* All fibrils of the compositor will terminate soon. */
1014 } else {
1015 fibril_mutex_unlock(&viewport_list_mtx);
1016 async_answer_0(iid, EOK);
1017
1018 comp_restrict_pointers();
1019 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1020 }
1021}
1022
1023static void vsl_notifications(ipc_callid_t iid, ipc_call_t *icall, void *arg)
1024{
1025 viewport_t *vp = NULL;
1026 fibril_mutex_lock(&viewport_list_mtx);
1027 list_foreach(viewport_list, link, viewport_t, cur) {
1028 if (cur->dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
1029 vp = cur;
1030 break;
1031 }
1032 }
1033 fibril_mutex_unlock(&viewport_list_mtx);
1034
1035 if (!vp) {
1036 return;
1037 }
1038
1039 /* Ignore parameters, the connection is already opened. */
1040 while (true) {
1041 ipc_call_t call;
1042 ipc_callid_t callid = async_get_call(&call);
1043
1044 if (!IPC_GET_IMETHOD(call)) {
1045 async_hangup(vp->sess);
1046 return;
1047 }
1048
1049 switch (IPC_GET_IMETHOD(call)) {
1050 case VISUALIZER_MODE_CHANGE:
1051 comp_mode_change(vp, callid, &call);
1052 break;
1053 case VISUALIZER_DISCONNECT:
1054 comp_visualizer_disconnect(vp, callid, &call);
1055 return;
1056 default:
1057 async_answer_0(callid, EINVAL);
1058 }
1059 }
1060}
1061
1062static async_sess_t *vsl_connect(const char *svc)
1063{
1064 int rc;
1065 async_sess_t *sess;
1066 service_id_t dsid;
1067 devman_handle_t handle;
1068
1069 rc = loc_service_get_id(svc, &dsid, 0);
1070 if (rc != EOK) {
1071 return NULL;
1072 }
1073
1074 rc = devman_fun_sid_to_handle(dsid, &handle);
1075 if (rc == EOK) {
1076 sess = devman_device_connect(EXCHANGE_SERIALIZE, handle, 0);
1077 if (sess == NULL) {
1078 printf("%s: Unable to connect to visualizer %s\n", NAME, svc);
1079 return NULL;
1080 }
1081 rc = graph_dev_connect(sess);
1082 if (rc != EOK) {
1083 return NULL;
1084 }
1085 } else if (rc == ENOENT) {
1086 sess = loc_service_connect(EXCHANGE_SERIALIZE, dsid, 0);
1087 if (sess == NULL) {
1088 printf("%s: Unable to connect to visualizer %s\n", NAME, svc);
1089 return NULL;
1090 }
1091 } else {
1092 return NULL;
1093 }
1094
1095 async_exch_t *exch = async_exchange_begin(sess);
1096 rc = async_connect_to_me(exch, dsid, 0, 0, vsl_notifications, NULL);
1097 async_exchange_end(exch);
1098
1099 if (rc != EOK) {
1100 async_hangup(sess);
1101 printf("%s: Unable to create callback connection to service %s (%s)\n",
1102 NAME, svc, str_error(rc));
1103 return NULL;
1104 }
1105
1106 return sess;
1107}
1108
1109static viewport_t *viewport_create(const char *vsl_name)
1110{
1111 int rc;
1112
1113 viewport_t *vp = (viewport_t *) malloc(sizeof(viewport_t));
1114 if (!vp) {
1115 return NULL;
1116 }
1117
1118 link_initialize(&vp->link);
1119 vp->pos.x = coord_origin;
1120 vp->pos.y = coord_origin;
1121
1122 /* Establish output bidirectional connection. */
1123 vp->sess = vsl_connect(vsl_name);
1124 rc = loc_service_get_id(vsl_name, &vp->dsid, 0);
1125 if (vp->sess == NULL || rc != EOK) {
1126 free(vp);
1127 return NULL;
1128 }
1129
1130 /* Claim the given visualizer. */
1131 rc = visualizer_claim(vp->sess, 0);
1132 if (rc != EOK) {
1133 async_hangup(vp->sess);
1134 free(vp);
1135 printf("%s: Unable to claim visualizer (%s)\n", NAME, str_error(rc));
1136 return NULL;
1137 }
1138
1139 /* Retrieve the default mode. */
1140 rc = visualizer_get_default_mode(vp->sess, &vp->mode);
1141 if (rc != EOK) {
1142 visualizer_yield(vp->sess);
1143 async_hangup(vp->sess);
1144 free(vp);
1145 printf("%s: Unable to retrieve mode (%s)\n", NAME, str_error(rc));
1146 return NULL;
1147 }
1148
1149 /* Create surface with respect to the retrieved mode. */
1150 vp->surface = surface_create(vp->mode.screen_width, vp->mode.screen_height,
1151 NULL, SURFACE_FLAG_SHARED);
1152 if (vp->surface == NULL) {
1153 visualizer_yield(vp->sess);
1154 async_hangup(vp->sess);
1155 free(vp);
1156 printf("%s: Unable to create surface (%s)\n", NAME, str_error(rc));
1157 return NULL;
1158 }
1159
1160 /* Try to set the mode and share out the surface. */
1161 rc = visualizer_set_mode(vp->sess,
1162 vp->mode.index, vp->mode.version, surface_direct_access(vp->surface));
1163 if (rc != EOK) {
1164 visualizer_yield(vp->sess);
1165 surface_destroy(vp->surface);
1166 async_hangup(vp->sess);
1167 free(vp);
1168 printf("%s: Unable to set mode (%s)\n", NAME, str_error(rc));
1169 return NULL;
1170 }
1171
1172 return vp;
1173}
1174
1175static void comp_recalc_transform(window_t *win)
1176{
1177 transform_t translate;
1178 transform_identity(&translate);
1179 transform_translate(&translate, win->dx, win->dy);
1180
1181 transform_t scale;
1182 transform_identity(&scale);
1183 if (win->fx != 1 || win->fy != 1) {
1184 transform_scale(&scale, win->fx, win->fy);
1185 }
1186
1187 transform_t rotate;
1188 transform_identity(&rotate);
1189 if (win->angle != 0) {
1190 transform_rotate(&rotate, win->angle);
1191 }
1192
1193 transform_t transform;
1194 transform_t temp;
1195 transform_identity(&transform);
1196 temp = transform;
1197 transform_multiply(&transform, &temp, &translate);
1198 temp = transform;
1199 transform_multiply(&transform, &temp, &rotate);
1200 temp = transform;
1201 transform_multiply(&transform, &temp, &scale);
1202
1203
1204 win->transform = transform;
1205}
1206
1207static void comp_window_animate(pointer_t *pointer, window_t *win,
1208 sysarg_t *dmg_x, sysarg_t *dmg_y, sysarg_t *dmg_width, sysarg_t *dmg_height)
1209{
1210 /* window_list_mtx locked by caller */
1211 /* pointer_list_mtx locked by caller */
1212
1213 int dx = pointer->accum.x;
1214 int dy = pointer->accum.y;
1215 pointer->accum.x = 0;
1216 pointer->accum.y = 0;
1217
1218 bool move = (pointer->grab_flags & GF_MOVE_X) || (pointer->grab_flags & GF_MOVE_Y);
1219 bool scale = (pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_SCALE_Y);
1220 bool resize = (pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y);
1221
1222 sysarg_t width, height;
1223 surface_get_resolution(win->surface, &width, &height);
1224
1225 if (move) {
1226 double cx = 0;
1227 double cy = 0;
1228 if (pointer->grab_flags & GF_MOVE_X) {
1229 cx = 1;
1230 }
1231 if (pointer->grab_flags & GF_MOVE_Y) {
1232 cy = 1;
1233 }
1234
1235 if ((scale || resize) && (win->angle != 0)) {
1236 transform_t rotate;
1237 transform_identity(&rotate);
1238 transform_rotate(&rotate, win->angle);
1239 transform_apply_linear(&rotate, &cx, &cy);
1240 }
1241
1242 cx = (cx < 0) ? (-1 * cx) : cx;
1243 cy = (cy < 0) ? (-1 * cy) : cy;
1244
1245 win->dx += (cx * dx);
1246 win->dy += (cy * dy);
1247 }
1248
1249 if (scale || resize) {
1250 double _dx = dx;
1251 double _dy = dy;
1252 if (win->angle != 0) {
1253 transform_t unrotate;
1254 transform_identity(&unrotate);
1255 transform_rotate(&unrotate, -win->angle);
1256 transform_apply_linear(&unrotate, &_dx, &_dy);
1257 }
1258 _dx = (pointer->grab_flags & GF_MOVE_X) ? -_dx : _dx;
1259 _dy = (pointer->grab_flags & GF_MOVE_Y) ? -_dy : _dy;
1260
1261 if ((pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_RESIZE_X)) {
1262 double fx = 1.0 + (_dx / ((width - 1) * win->fx));
1263 if (fx > 0) {
1264#if ANIMATE_WINDOW_TRANSFORMS == 0
1265 if (scale) win->fx *= fx;
1266#endif
1267#if ANIMATE_WINDOW_TRANSFORMS == 1
1268 win->fx *= fx;
1269#endif
1270 scale_back_x *= fx;
1271 }
1272 }
1273
1274 if ((pointer->grab_flags & GF_SCALE_Y) || (pointer->grab_flags & GF_RESIZE_Y)) {
1275 double fy = 1.0 + (_dy / ((height - 1) * win->fy));
1276 if (fy > 0) {
1277#if ANIMATE_WINDOW_TRANSFORMS == 0
1278 if (scale) win->fy *= fy;
1279#endif
1280#if ANIMATE_WINDOW_TRANSFORMS == 1
1281 win->fy *= fy;
1282#endif
1283 scale_back_y *= fy;
1284 }
1285 }
1286 }
1287
1288 sysarg_t x1, y1, width1, height1;
1289 sysarg_t x2, y2, width2, height2;
1290 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1291 &x1, &y1, &width1, &height1);
1292 comp_recalc_transform(win);
1293 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1294 &x2, &y2, &width2, &height2);
1295 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1296 dmg_x, dmg_y, dmg_width, dmg_height);
1297}
1298
1299#if ANIMATE_WINDOW_TRANSFORMS == 0
1300static void comp_ghost_animate(pointer_t *pointer,
1301 desktop_rect_t *rect1, desktop_rect_t *rect2, desktop_rect_t *rect3, desktop_rect_t *rect4)
1302{
1303 /* window_list_mtx locked by caller */
1304 /* pointer_list_mtx locked by caller */
1305
1306 int dx = pointer->accum_ghost.x;
1307 int dy = pointer->accum_ghost.y;
1308 pointer->accum_ghost.x = 0;
1309 pointer->accum_ghost.y = 0;
1310
1311 bool move = (pointer->grab_flags & GF_MOVE_X) || (pointer->grab_flags & GF_MOVE_Y);
1312 bool scale = (pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_SCALE_Y);
1313 bool resize = (pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y);
1314
1315 sysarg_t width, height;
1316 surface_get_resolution(pointer->ghost.surface, &width, &height);
1317
1318 if (move) {
1319 double cx = 0;
1320 double cy = 0;
1321 if (pointer->grab_flags & GF_MOVE_X) {
1322 cx = 1;
1323 }
1324 if (pointer->grab_flags & GF_MOVE_Y) {
1325 cy = 1;
1326 }
1327
1328 if (scale || resize) {
1329 transform_t rotate;
1330 transform_identity(&rotate);
1331 transform_rotate(&rotate, pointer->ghost.angle);
1332 transform_apply_linear(&rotate, &cx, &cy);
1333 }
1334
1335 cx = (cx < 0) ? (-1 * cx) : cx;
1336 cy = (cy < 0) ? (-1 * cy) : cy;
1337
1338 pointer->ghost.dx += (cx * dx);
1339 pointer->ghost.dy += (cy * dy);
1340 }
1341
1342 if (scale || resize) {
1343 double _dx = dx;
1344 double _dy = dy;
1345 transform_t unrotate;
1346 transform_identity(&unrotate);
1347 transform_rotate(&unrotate, -pointer->ghost.angle);
1348 transform_apply_linear(&unrotate, &_dx, &_dy);
1349 _dx = (pointer->grab_flags & GF_MOVE_X) ? -_dx : _dx;
1350 _dy = (pointer->grab_flags & GF_MOVE_Y) ? -_dy : _dy;
1351
1352 if ((pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_RESIZE_X)) {
1353 double fx = 1.0 + (_dx / ((width - 1) * pointer->ghost.fx));
1354 pointer->ghost.fx *= fx;
1355 }
1356
1357 if ((pointer->grab_flags & GF_SCALE_Y) || (pointer->grab_flags & GF_RESIZE_Y)) {
1358 double fy = 1.0 + (_dy / ((height - 1) * pointer->ghost.fy));
1359 pointer->ghost.fy *= fy;
1360 }
1361 }
1362
1363 sysarg_t x1, y1, width1, height1;
1364 sysarg_t x2, y2, width2, height2;
1365 comp_coord_bounding_rect(0, 0, width, height, pointer->ghost.transform,
1366 &x1, &y1, &width1, &height1);
1367 comp_recalc_transform(&pointer->ghost);
1368 comp_coord_bounding_rect(0, 0, width, height, pointer->ghost.transform,
1369 &x2, &y2, &width2, &height2);
1370
1371 sysarg_t x_u, y_u, w_u, h_u;
1372 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1373 &x_u, &y_u, &w_u, &h_u);
1374
1375 sysarg_t x_i, y_i, w_i, h_i;
1376 rectangle_intersect(x1, y1, width1, height1, x2, y2, width2, height2,
1377 &x_i, &y_i, &w_i, &h_i);
1378
1379 if (w_i == 0 || h_i == 0) {
1380 rect1->x = x_u; rect2->x = 0; rect3->x = 0; rect4->x = 0;
1381 rect1->y = y_u; rect2->y = 0; rect3->y = 0; rect4->y = 0;
1382 rect1->w = w_u; rect2->w = 0; rect3->w = 0; rect4->w = 0;
1383 rect1->h = h_u; rect2->h = 0; rect3->h = 0; rect4->h = 0;
1384 } else {
1385 rect1->x = x_u;
1386 rect1->y = y_u;
1387 rect1->w = x_i - x_u + 1;
1388 rect1->h = h_u;
1389
1390 rect2->x = x_u;
1391 rect2->y = y_u;
1392 rect2->w = w_u;
1393 rect2->h = y_i - y_u + 1;
1394
1395 rect3->x = x_i + w_i - 1;
1396 rect3->y = y_u;
1397 rect3->w = w_u - w_i - x_i + x_u + 1;
1398 rect3->h = h_u;
1399
1400 rect4->x = x_u;
1401 rect4->y = y_i + h_i - 1;
1402 rect4->w = w_u;
1403 rect4->h = h_u - h_i - y_i + y_u + 1;
1404 }
1405}
1406#endif
1407
1408static int comp_abs_move(input_t *input, unsigned x , unsigned y,
1409 unsigned max_x, unsigned max_y)
1410{
1411 /* XXX TODO Use absolute coordinates directly */
1412
1413 pointer_t *pointer = input_pointer(input);
1414
1415 sysarg_t width, height;
1416
1417 fibril_mutex_lock(&viewport_list_mtx);
1418 if (list_empty(&viewport_list)) {
1419 printf("No viewport found\n");
1420 fibril_mutex_unlock(&viewport_list_mtx);
1421 return EOK; /* XXX */
1422 }
1423 link_t *link = list_first(&viewport_list);
1424 viewport_t *vp = list_get_instance(link, viewport_t, link);
1425 surface_get_resolution(vp->surface, &width, &height);
1426 desktop_point_t vp_pos = vp->pos;
1427 fibril_mutex_unlock(&viewport_list_mtx);
1428
1429 desktop_point_t pos_in_viewport;
1430 pos_in_viewport.x = x * width / max_x;
1431 pos_in_viewport.y = y * height / max_y;
1432
1433 /* Calculate offset from pointer */
1434 fibril_mutex_lock(&pointer_list_mtx);
1435 desktop_vector_t delta;
1436 delta.x = (vp_pos.x + pos_in_viewport.x) - pointer->pos.x;
1437 delta.y = (vp_pos.y + pos_in_viewport.y) - pointer->pos.y;
1438 fibril_mutex_unlock(&pointer_list_mtx);
1439
1440 return comp_mouse_move(input, delta.x, delta.y);
1441}
1442
1443static int comp_mouse_move(input_t *input, int dx, int dy)
1444{
1445 pointer_t *pointer = input_pointer(input);
1446
1447 /* Update pointer position. */
1448 fibril_mutex_lock(&pointer_list_mtx);
1449 desktop_point_t old_pos = pointer->pos;
1450 sysarg_t cursor_width;
1451 sysarg_t cursor_height;
1452 surface_get_resolution(pointer->cursor.states[pointer->state],
1453 &cursor_width, &cursor_height);
1454 if (pointer->pos.x + dx < viewport_bound_rect.x) {
1455 dx = -1 * (pointer->pos.x - viewport_bound_rect.x);
1456 }
1457 if (pointer->pos.y + dy < viewport_bound_rect.y) {
1458 dy = -1 * (pointer->pos.y - viewport_bound_rect.y);
1459 }
1460 if (pointer->pos.x + dx > viewport_bound_rect.x + viewport_bound_rect.w) {
1461 dx = (viewport_bound_rect.x + viewport_bound_rect.w - pointer->pos.x);
1462 }
1463 if (pointer->pos.y + dy > viewport_bound_rect.y + viewport_bound_rect.h) {
1464 dy = (viewport_bound_rect.y + viewport_bound_rect.h - pointer->pos.y);
1465 }
1466 pointer->pos.x += dx;
1467 pointer->pos.y += dy;
1468 fibril_mutex_unlock(&pointer_list_mtx);
1469 comp_damage(old_pos.x, old_pos.y, cursor_width, cursor_height);
1470 comp_damage(old_pos.x + dx, old_pos.y + dy, cursor_width, cursor_height);
1471
1472 fibril_mutex_lock(&window_list_mtx);
1473 fibril_mutex_lock(&pointer_list_mtx);
1474 window_t *top = (window_t *) list_first(&window_list);
1475 if (top && top->surface) {
1476
1477 if (pointer->grab_flags == GF_EMPTY) {
1478 /* Notify top-level window about move event. */
1479 bool within_client = false;
1480 sysarg_t point_x, point_y;
1481 sysarg_t width, height;
1482 surface_get_resolution(top->surface, &width, &height);
1483 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1484 top->transform, width, height, &point_x, &point_y);
1485
1486 window_event_t *event = NULL;
1487 if (within_client) {
1488 event = (window_event_t *) malloc(sizeof(window_event_t));
1489 if (event) {
1490 link_initialize(&event->link);
1491 event->type = ET_POSITION_EVENT;
1492 event->data.pos.pos_id = pointer->id;
1493 event->data.pos.type = POS_UPDATE;
1494 event->data.pos.btn_num = pointer->btn_num;
1495 event->data.pos.hpos = point_x;
1496 event->data.pos.vpos = point_y;
1497 }
1498 }
1499
1500 fibril_mutex_unlock(&pointer_list_mtx);
1501 fibril_mutex_unlock(&window_list_mtx);
1502
1503 if (event) {
1504 comp_post_event_top(event);
1505 }
1506
1507 } else {
1508 /* Pointer is grabbed by top-level window action. */
1509 pointer->accum.x += dx;
1510 pointer->accum.y += dy;
1511 pointer->accum_ghost.x += dx;
1512 pointer->accum_ghost.y += dy;
1513#if ANIMATE_WINDOW_TRANSFORMS == 0
1514 if (pointer->ghost.surface == NULL) {
1515 pointer->ghost.surface = top->surface;
1516 pointer->ghost.dx = top->dx;
1517 pointer->ghost.dy = top->dy;
1518 pointer->ghost.fx = top->fx;
1519 pointer->ghost.fy = top->fy;
1520 pointer->ghost.angle = top->angle;
1521 pointer->ghost.transform = top->transform;
1522 }
1523 desktop_rect_t dmg_rect1, dmg_rect2, dmg_rect3, dmg_rect4;
1524 comp_ghost_animate(pointer, &dmg_rect1, &dmg_rect2, &dmg_rect3, &dmg_rect4);
1525#endif
1526#if ANIMATE_WINDOW_TRANSFORMS == 1
1527 sysarg_t x, y, width, height;
1528 comp_window_animate(pointer, top, &x, &y, &width, &height);
1529#endif
1530 fibril_mutex_unlock(&pointer_list_mtx);
1531 fibril_mutex_unlock(&window_list_mtx);
1532#if ANIMATE_WINDOW_TRANSFORMS == 0
1533 comp_damage(dmg_rect1.x, dmg_rect1.y, dmg_rect1.w, dmg_rect1.h);
1534 comp_damage(dmg_rect2.x, dmg_rect2.y, dmg_rect2.w, dmg_rect2.h);
1535 comp_damage(dmg_rect3.x, dmg_rect3.y, dmg_rect3.w, dmg_rect3.h);
1536 comp_damage(dmg_rect4.x, dmg_rect4.y, dmg_rect4.w, dmg_rect4.h);
1537#endif
1538#if ANIMATE_WINDOW_TRANSFORMS == 1
1539 comp_damage(x, y, width, height);
1540#endif
1541 }
1542 } else {
1543 fibril_mutex_unlock(&pointer_list_mtx);
1544 fibril_mutex_unlock(&window_list_mtx);
1545 }
1546
1547 return EOK;
1548}
1549
1550static int comp_mouse_button(input_t *input, int bnum, int bpress)
1551{
1552 pointer_t *pointer = input_pointer(input);
1553
1554 fibril_mutex_lock(&window_list_mtx);
1555 fibril_mutex_lock(&pointer_list_mtx);
1556 window_t *win = NULL;
1557 sysarg_t point_x = 0;
1558 sysarg_t point_y = 0;
1559 sysarg_t width, height;
1560 bool within_client = false;
1561
1562 /* Determine the window which the mouse click belongs to. */
1563 list_foreach(window_list, link, window_t, cw) {
1564 win = cw;
1565 if (win->surface) {
1566 surface_get_resolution(win->surface, &width, &height);
1567 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1568 win->transform, width, height, &point_x, &point_y);
1569 }
1570 if (within_client) {
1571 break;
1572 }
1573 }
1574
1575 /* Check whether the window is top-level window. */
1576 window_t *top = (window_t *) list_first(&window_list);
1577 if (!win || !top) {
1578 fibril_mutex_unlock(&pointer_list_mtx);
1579 fibril_mutex_unlock(&window_list_mtx);
1580 return EOK;
1581 }
1582
1583 window_event_t *event_top = NULL;
1584 window_event_t *event_unfocus = NULL;
1585 window_t *win_unfocus = NULL;
1586 sysarg_t dmg_x, dmg_y;
1587 sysarg_t dmg_width = 0;
1588 sysarg_t dmg_height = 0;
1589
1590#if ANIMATE_WINDOW_TRANSFORMS == 0
1591 desktop_rect_t dmg_rect1, dmg_rect2, dmg_rect3, dmg_rect4;
1592#endif
1593
1594 if (bpress) {
1595 pointer->btn_pos = pointer->pos;
1596 pointer->btn_num = bnum;
1597 pointer->pressed = true;
1598
1599 /* Bring the window to the foreground. */
1600 if ((win != top) && within_client) {
1601 win_unfocus = (window_t *) list_first(&window_list);
1602 list_remove(&win->link);
1603 list_prepend(&win->link, &window_list);
1604 event_unfocus = (window_event_t *) malloc(sizeof(window_event_t));
1605 if (event_unfocus) {
1606 link_initialize(&event_unfocus->link);
1607 event_unfocus->type = ET_WINDOW_UNFOCUS;
1608 }
1609 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1610 &dmg_x, &dmg_y, &dmg_width, &dmg_height);
1611 }
1612
1613 /* Notify top-level window about mouse press. */
1614 if (within_client) {
1615 event_top = (window_event_t *) malloc(sizeof(window_event_t));
1616 if (event_top) {
1617 link_initialize(&event_top->link);
1618 event_top->type = ET_POSITION_EVENT;
1619 event_top->data.pos.pos_id = pointer->id;
1620 event_top->data.pos.type = POS_PRESS;
1621 event_top->data.pos.btn_num = bnum;
1622 event_top->data.pos.hpos = point_x;
1623 event_top->data.pos.vpos = point_y;
1624 }
1625 pointer->grab_flags = GF_EMPTY;
1626 }
1627
1628 } else if (pointer->pressed && pointer->btn_num == (unsigned)bnum) {
1629 pointer->pressed = false;
1630
1631#if ANIMATE_WINDOW_TRANSFORMS == 0
1632 sysarg_t pre_x = 0;
1633 sysarg_t pre_y = 0;
1634 sysarg_t pre_width = 0;
1635 sysarg_t pre_height = 0;
1636
1637 if (pointer->grab_flags != GF_EMPTY) {
1638 if (pointer->ghost.surface) {
1639 comp_ghost_animate(pointer, &dmg_rect1, &dmg_rect2, &dmg_rect3, &dmg_rect4);
1640 pointer->ghost.surface = NULL;
1641 }
1642 comp_window_animate(pointer, top, &pre_x, &pre_y, &pre_width, &pre_height);
1643 dmg_x = pre_x;
1644 dmg_y = pre_y;
1645 dmg_width = pre_width;
1646 dmg_height = pre_height;
1647 }
1648#endif
1649
1650 if ((pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y)) {
1651
1652 surface_get_resolution(top->surface, &width, &height);
1653#if ANIMATE_WINDOW_TRANSFORMS == 1
1654 top->fx *= (1.0 / scale_back_x);
1655 top->fy *= (1.0 / scale_back_y);
1656 comp_recalc_transform(top);
1657#endif
1658
1659 /* Commit proper resize action. */
1660 event_top = (window_event_t *) malloc(sizeof(window_event_t));
1661 if (event_top) {
1662 link_initialize(&event_top->link);
1663 event_top->type = ET_WINDOW_RESIZE;
1664
1665 int dx = (int) (((double) width) * (scale_back_x - 1.0));
1666 int dy = (int) (((double) height) * (scale_back_y - 1.0));
1667
1668 if (pointer->grab_flags & GF_RESIZE_X) {
1669 event_top->data.rsz.width =
1670 ((((int) width) + dx) >= 0) ? (width + dx) : 0;
1671 } else {
1672 event_top->data.rsz.width = width;
1673 }
1674
1675 if (pointer->grab_flags & GF_RESIZE_Y) {
1676 event_top->data.rsz.height =
1677 ((((int) height) + dy) >= 0) ? (height + dy) : 0;
1678 } else {
1679 event_top->data.rsz.height = height;
1680 }
1681 }
1682
1683 pointer->grab_flags = GF_EMPTY;
1684
1685 } else if (within_client && (pointer->grab_flags == GF_EMPTY) && (top == win)) {
1686
1687 /* Notify top-level window about mouse release. */
1688 event_top = (window_event_t *) malloc(sizeof(window_event_t));
1689 if (event_top) {
1690 link_initialize(&event_top->link);
1691 event_top->type = ET_POSITION_EVENT;
1692 event_top->data.pos.pos_id = pointer->id;
1693 event_top->data.pos.type = POS_RELEASE;
1694 event_top->data.pos.btn_num = bnum;
1695 event_top->data.pos.hpos = point_x;
1696 event_top->data.pos.vpos = point_y;
1697 }
1698 pointer->grab_flags = GF_EMPTY;
1699
1700 } else {
1701 pointer->grab_flags = GF_EMPTY;
1702 }
1703
1704 }
1705
1706 fibril_mutex_unlock(&pointer_list_mtx);
1707 fibril_mutex_unlock(&window_list_mtx);
1708
1709#if ANIMATE_WINDOW_TRANSFORMS == 0
1710 comp_damage(dmg_rect1.x, dmg_rect1.y, dmg_rect1.w, dmg_rect1.h);
1711 comp_damage(dmg_rect2.x, dmg_rect2.y, dmg_rect2.w, dmg_rect2.h);
1712 comp_damage(dmg_rect3.x, dmg_rect3.y, dmg_rect3.w, dmg_rect3.h);
1713 comp_damage(dmg_rect4.x, dmg_rect4.y, dmg_rect4.w, dmg_rect4.h);
1714#endif
1715
1716 if (dmg_width > 0 && dmg_height > 0) {
1717 comp_damage(dmg_x, dmg_y, dmg_width, dmg_height);
1718 }
1719
1720 if (event_unfocus && win_unfocus) {
1721 comp_post_event_win(event_unfocus, win_unfocus);
1722 }
1723
1724 if (event_top) {
1725 comp_post_event_top(event_top);
1726 }
1727
1728 return EOK;
1729}
1730
1731static int comp_key_press(input_t *input, kbd_event_type_t type, keycode_t key,
1732 keymod_t mods, wchar_t c)
1733{
1734 bool win_transform = (mods & KM_ALT) && (
1735 key == KC_W || key == KC_S || key == KC_A || key == KC_D ||
1736 key == KC_Q || key == KC_E || key == KC_R || key == KC_F);
1737 bool win_resize = (mods & KM_ALT) && (
1738 key == KC_T || key == KC_G || key == KC_B || key == KC_N);
1739 bool win_opacity = (mods & KM_ALT) && (
1740 key == KC_C || key == KC_V);
1741 bool win_close = (mods & KM_ALT) && (key == KC_X);
1742 bool win_switch = (mods & KM_ALT) && (key == KC_TAB);
1743 bool viewport_move = (mods & KM_ALT) && (
1744 key == KC_I || key == KC_K || key == KC_J || key == KC_L);
1745 bool viewport_change = (mods & KM_ALT) && (
1746 key == KC_O || key == KC_P);
1747 bool kconsole_switch = (mods & KM_ALT) && (key == KC_M);
1748 bool compositor_test = (mods & KM_ALT) && (key == KC_H);
1749
1750 bool filter = (type == KEY_RELEASE) && (win_transform || win_resize ||
1751 win_opacity || win_close || win_switch || viewport_move ||
1752 viewport_change || kconsole_switch || compositor_test);
1753
1754 if (filter) {
1755 /* no-op */
1756 } else if (win_transform) {
1757 fibril_mutex_lock(&window_list_mtx);
1758 window_t *win = (window_t *) list_first(&window_list);
1759 if (win && win->surface) {
1760 switch (key) {
1761 case KC_W:
1762 win->dy += -20;
1763 break;
1764 case KC_S:
1765 win->dy += 20;
1766 break;
1767 case KC_A:
1768 win->dx += -20;
1769 break;
1770 case KC_D:
1771 win->dx += 20;
1772 break;
1773 case KC_Q:
1774 win->angle += (PI / 2);
1775 break;
1776 case KC_E:
1777 win->angle += -(PI / 2);
1778 break;
1779 case KC_R:
1780 win->fx *= 0.95;
1781 win->fy *= 0.95;
1782 break;
1783 case KC_F:
1784 win->fx *= 1.05;
1785 win->fy *= 1.05;
1786 break;
1787 default:
1788 break;
1789 }
1790
1791 /* Transform the window and calculate damage. */
1792 sysarg_t x, y, width, height;
1793 surface_get_resolution(win->surface, &width, &height);
1794 sysarg_t x1, y1, width1, height1;
1795 sysarg_t x2, y2, width2, height2;
1796 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1797 &x1, &y1, &width1, &height1);
1798 comp_recalc_transform(win);
1799 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1800 &x2, &y2, &width2, &height2);
1801 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1802 &x, &y, &width, &height);
1803 fibril_mutex_unlock(&window_list_mtx);
1804
1805 comp_damage(x, y, width, height);
1806 } else {
1807 fibril_mutex_unlock(&window_list_mtx);
1808 }
1809 } else if (win_resize) {
1810 fibril_mutex_lock(&window_list_mtx);
1811 window_t *win = (window_t *) list_first(&window_list);
1812 if (win && win->surface) {
1813 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1814 if (event == NULL) {
1815 fibril_mutex_unlock(&window_list_mtx);
1816 return ENOMEM;
1817 }
1818
1819 sysarg_t width, height;
1820 surface_get_resolution(win->surface, &width, &height);
1821
1822 link_initialize(&event->link);
1823 event->type = ET_WINDOW_RESIZE;
1824
1825 switch (key) {
1826 case KC_T:
1827 event->data.rsz.width = width;
1828 event->data.rsz.height = (height >= 20) ? height - 20 : 0;
1829 break;
1830 case KC_G:
1831 event->data.rsz.width = width;
1832 event->data.rsz.height = height + 20;
1833 break;
1834 case KC_B:
1835 event->data.rsz.width = (width >= 20) ? width - 20 : 0;;
1836 event->data.rsz.height = height;
1837 break;
1838 case KC_N:
1839 event->data.rsz.width = width + 20;
1840 event->data.rsz.height = height;
1841 break;
1842 default:
1843 event->data.rsz.width = 0;
1844 event->data.rsz.height = 0;
1845 break;
1846 }
1847
1848 fibril_mutex_unlock(&window_list_mtx);
1849 comp_post_event_top(event);
1850 } else {
1851 fibril_mutex_unlock(&window_list_mtx);
1852 }
1853 } else if (win_opacity) {
1854 fibril_mutex_lock(&window_list_mtx);
1855 window_t *win = (window_t *) list_first(&window_list);
1856 if (win && win->surface) {
1857 switch (key) {
1858 case KC_C:
1859 if (win->opacity > 0) {
1860 win->opacity -= 5;
1861 }
1862 break;
1863 case KC_V:
1864 if (win->opacity < 255) {
1865 win->opacity += 5;
1866 }
1867 break;
1868 default:
1869 break;
1870 }
1871
1872 /* Calculate damage. */
1873 sysarg_t x, y, width, height;
1874 surface_get_resolution(win->surface, &width, &height);
1875 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1876 &x, &y, &width, &height);
1877 fibril_mutex_unlock(&window_list_mtx);
1878
1879 comp_damage(x, y, width, height);
1880 } else {
1881 fibril_mutex_unlock(&window_list_mtx);
1882 }
1883 } else if (win_close) {
1884 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1885 if (event == NULL)
1886 return ENOMEM;
1887
1888 link_initialize(&event->link);
1889 event->type = ET_WINDOW_CLOSE;
1890
1891 comp_post_event_top(event);
1892 } else if (win_switch) {
1893 fibril_mutex_lock(&window_list_mtx);
1894 if (!list_empty(&window_list)) {
1895 window_t *win1 = (window_t *) list_first(&window_list);
1896 list_remove(&win1->link);
1897 list_append(&win1->link, &window_list);
1898 window_t *win2 = (window_t *) list_first(&window_list);
1899
1900 window_event_t *event1 = (window_event_t *) malloc(sizeof(window_event_t));
1901 if (event1) {
1902 link_initialize(&event1->link);
1903 event1->type = ET_WINDOW_UNFOCUS;
1904 }
1905
1906 window_event_t *event2 = (window_event_t *) malloc(sizeof(window_event_t));
1907 if (event2) {
1908 link_initialize(&event2->link);
1909 event2->type = ET_WINDOW_FOCUS;
1910 }
1911
1912 sysarg_t x1 = 0;
1913 sysarg_t y1 = 0;
1914 sysarg_t width1 = 0;
1915 sysarg_t height1 = 0;
1916 if (win1->surface) {
1917 sysarg_t width, height;
1918 surface_get_resolution(win1->surface, &width, &height);
1919 comp_coord_bounding_rect(0, 0, width, height, win1->transform,
1920 &x1, &y1, &width1, &height1);
1921 }
1922
1923 sysarg_t x2 = 0;
1924 sysarg_t y2 = 0;
1925 sysarg_t width2 = 0;
1926 sysarg_t height2 = 0;
1927 if (win2->surface) {
1928 sysarg_t width, height;
1929 surface_get_resolution(win2->surface, &width, &height);
1930 comp_coord_bounding_rect(0, 0, width, height, win2->transform,
1931 &x2, &y2, &width2, &height2);
1932 }
1933
1934 sysarg_t x, y, width, height;
1935 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1936 &x, &y, &width, &height);
1937
1938 fibril_mutex_unlock(&window_list_mtx);
1939
1940 if (event1 && win1) {
1941 comp_post_event_win(event1, win1);
1942 }
1943
1944 if (event2 && win2) {
1945 comp_post_event_win(event2, win2);
1946 }
1947
1948 comp_damage(x, y, width, height);
1949 } else {
1950 fibril_mutex_unlock(&window_list_mtx);
1951 }
1952 } else if (viewport_move) {
1953 fibril_mutex_lock(&viewport_list_mtx);
1954 viewport_t *vp = (viewport_t *) list_first(&viewport_list);
1955 if (vp) {
1956 switch (key) {
1957 case KC_I:
1958 vp->pos.x += 0;
1959 vp->pos.y += -20;
1960 break;
1961 case KC_K:
1962 vp->pos.x += 0;
1963 vp->pos.y += 20;
1964 break;
1965 case KC_J:
1966 vp->pos.x += -20;
1967 vp->pos.y += 0;
1968 break;
1969 case KC_L:
1970 vp->pos.x += 20;
1971 vp->pos.y += 0;
1972 break;
1973 default:
1974 vp->pos.x += 0;
1975 vp->pos.y += 0;
1976 break;
1977 }
1978
1979 sysarg_t x = vp->pos.x;
1980 sysarg_t y = vp->pos.y;
1981 sysarg_t width, height;
1982 surface_get_resolution(vp->surface, &width, &height);
1983 fibril_mutex_unlock(&viewport_list_mtx);
1984
1985 comp_restrict_pointers();
1986 comp_damage(x, y, width, height);
1987 } else {
1988 fibril_mutex_unlock(&viewport_list_mtx);
1989 }
1990 } else if (viewport_change) {
1991 fibril_mutex_lock(&viewport_list_mtx);
1992
1993 viewport_t *vp;
1994 switch (key) {
1995 case KC_O:
1996 vp = (viewport_t *) list_first(&viewport_list);
1997 if (vp) {
1998 list_remove(&vp->link);
1999 list_append(&vp->link, &viewport_list);
2000 }
2001 break;
2002 case KC_P:
2003 vp = (viewport_t *) list_last(&viewport_list);
2004 if (vp) {
2005 list_remove(&vp->link);
2006 list_prepend(&vp->link, &viewport_list);
2007 }
2008 break;
2009 default:
2010 break;
2011 }
2012
2013 fibril_mutex_unlock(&viewport_list_mtx);
2014 } else if (kconsole_switch) {
2015 __SYSCALL0(SYS_DEBUG_ACTIVATE_CONSOLE);
2016 } else if (compositor_test) {
2017 fibril_mutex_lock(&window_list_mtx);
2018
2019 window_t *red_win = window_create(0, 0);
2020 red_win->surface = surface_create(250, 150, NULL, 0);
2021 pixel_t red_pix = PIXEL(255, 240, 0, 0);
2022 for (sysarg_t y = 0; y < 150; ++y) {
2023 for (sysarg_t x = 0; x < 250; ++x) {
2024 surface_put_pixel(red_win->surface, x, y, red_pix);
2025 }
2026 }
2027 list_prepend(&red_win->link, &window_list);
2028
2029 window_t *blue_win = window_create(0, 0);
2030 blue_win->surface = surface_create(200, 100, NULL, 0);
2031 pixel_t blue_pix = PIXEL(255, 0, 0, 240);
2032 for (sysarg_t y = 0; y < 100; ++y) {
2033 for (sysarg_t x = 0; x < 200; ++x) {
2034 surface_put_pixel(blue_win->surface, x, y, blue_pix);
2035 }
2036 }
2037 list_prepend(&blue_win->link, &window_list);
2038
2039 window_t *nameic_win = window_create(0, 0);
2040 nameic_win->surface = decode_tga((void *) nameic_tga, nameic_tga_size, 0);
2041 list_prepend(&nameic_win->link, &window_list);
2042
2043 fibril_mutex_unlock(&window_list_mtx);
2044 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
2045 } else {
2046 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
2047 if (event == NULL)
2048 return ENOMEM;
2049
2050 link_initialize(&event->link);
2051 event->type = ET_KEYBOARD_EVENT;
2052 event->data.kbd.type = type;
2053 event->data.kbd.key = key;
2054 event->data.kbd.mods = mods;
2055 event->data.kbd.c = c;
2056
2057 comp_post_event_top(event);
2058 }
2059
2060 return EOK;
2061}
2062
2063static int input_connect(const char *svc)
2064{
2065 async_sess_t *sess;
2066 service_id_t dsid;
2067
2068 int rc = loc_service_get_id(svc, &dsid, 0);
2069 if (rc != EOK) {
2070 printf("%s: Input service %s not found\n", NAME, svc);
2071 return rc;
2072 }
2073
2074 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
2075 if (sess == NULL) {
2076 printf("%s: Unable to connect to input service %s\n", NAME,
2077 svc);
2078 return EIO;
2079 }
2080
2081 fibril_mutex_lock(&pointer_list_mtx);
2082 pointer_t *pointer = pointer_create();
2083 if (pointer != NULL) {
2084 pointer->id = pointer_id++;
2085 list_append(&pointer->link, &pointer_list);
2086 }
2087 fibril_mutex_unlock(&pointer_list_mtx);
2088
2089 if (pointer == NULL) {
2090 printf("%s: Cannot create pointer.\n", NAME);
2091 async_hangup(sess);
2092 return ENOMEM;
2093 }
2094
2095 rc = input_open(sess, &input_ev_ops, pointer, &input);
2096 if (rc != EOK) {
2097 async_hangup(sess);
2098 printf("%s: Unable to communicate with service %s (%s)\n",
2099 NAME, svc, str_error(rc));
2100 return rc;
2101 }
2102
2103 return EOK;
2104}
2105
2106static void input_disconnect(void)
2107{
2108 pointer_t *pointer = input->user;
2109 input_close(input);
2110 pointer_destroy(pointer);
2111}
2112
2113static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
2114{
2115 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
2116}
2117
2118static int discover_viewports(void)
2119{
2120 /* Create viewports and connect them to visualizers. */
2121 category_id_t cat_id;
2122 int rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
2123 if (rc != EOK) {
2124 printf("%s: Failed to get visualizer category.\n", NAME);
2125 return -1;
2126 }
2127
2128 service_id_t *svcs;
2129 size_t svcs_cnt = 0;
2130 rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
2131 if (rc != EOK || svcs_cnt == 0) {
2132 printf("%s: Failed to get visualizer category services.\n", NAME);
2133 return -1;
2134 }
2135
2136 fibril_mutex_lock(&viewport_list_mtx);
2137 for (size_t i = 0; i < svcs_cnt; ++i) {
2138 bool exists = false;
2139 list_foreach(viewport_list, link, viewport_t, vp) {
2140 if (vp->dsid == svcs[i]) {
2141 exists = true;
2142 break;
2143 }
2144 }
2145
2146 if (exists)
2147 continue;
2148
2149 char *svc_name;
2150 rc = loc_service_get_name(svcs[i], &svc_name);
2151 if (rc == EOK) {
2152 viewport_t *vp = viewport_create(svc_name);
2153 if (vp != NULL) {
2154 list_append(&vp->link, &viewport_list);
2155 }
2156 }
2157 }
2158 fibril_mutex_unlock(&viewport_list_mtx);
2159
2160 /* TODO damage only newly added viewports */
2161 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
2162 return EOK;
2163}
2164
2165static void category_change_cb(void)
2166{
2167 fibril_mutex_lock(&discovery_mtx);
2168 discover_viewports();
2169 fibril_mutex_unlock(&discovery_mtx);
2170}
2171
2172static int compositor_srv_init(char *input_svc, char *name)
2173{
2174 /* Coordinates of the central pixel. */
2175 coord_origin = UINT32_MAX / 4;
2176
2177 /* Color of the viewport background. Must be opaque. */
2178 bg_color = PIXEL(255, 69, 51, 103);
2179
2180 /* Register compositor server. */
2181 async_set_client_connection(client_connection);
2182 int rc = loc_server_register(NAME);
2183 if (rc != EOK) {
2184 printf("%s: Unable to register server (%s)\n", NAME, str_error(rc));
2185 return -1;
2186 }
2187
2188 /* Register interrupt handler to switch back from kconsole. */
2189 async_set_interrupt_received(interrupt_received);
2190 rc = event_subscribe(EVENT_KCONSOLE, 0);
2191 if (rc != EOK) {
2192 printf("%s: Failed to register kconsole notifications (%s)\n",
2193 NAME, str_error(rc));
2194 }
2195
2196 server_name = name;
2197
2198 char svc[LOC_NAME_MAXLEN + 1];
2199 snprintf(svc, LOC_NAME_MAXLEN, "%s/%s", NAMESPACE, server_name);
2200
2201 service_id_t service_id;
2202 rc = loc_service_register(svc, &service_id);
2203 if (rc != EOK) {
2204 printf("%s: Unable to register service %s\n", NAME, svc);
2205 return rc;
2206 }
2207
2208 /* Prepare window registrator (entrypoint for clients). */
2209 char winreg[LOC_NAME_MAXLEN + 1];
2210 snprintf(winreg, LOC_NAME_MAXLEN, "%s%s/winreg", NAMESPACE, server_name);
2211 if (loc_service_register(winreg, &winreg_id) != EOK) {
2212 printf("%s: Unable to register service %s\n", NAME, winreg);
2213 return -1;
2214 }
2215
2216 /* Establish input bidirectional connection. */
2217 rc = input_connect(input_svc);
2218 if (rc != EOK) {
2219 printf("%s: Failed to connect to input service.\n", NAME);
2220 return rc;
2221 }
2222
2223 rc = loc_register_cat_change_cb(category_change_cb);
2224 if (rc != EOK) {
2225 printf("%s: Failed to register category change callback\n", NAME);
2226 input_disconnect();
2227 return rc;
2228 }
2229
2230 rc = discover_viewports();
2231 if (rc != EOK) {
2232 input_disconnect();
2233 return rc;
2234 }
2235
2236 if (list_empty(&viewport_list)) {
2237 printf("%s: Failed to get viewports.\n", NAME);
2238 input_disconnect();
2239 return -1;
2240 }
2241
2242 comp_restrict_pointers();
2243 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
2244
2245
2246 return EOK;
2247}
2248
2249static void usage(char *name)
2250{
2251 printf("Usage: %s <input_dev> <server_name>\n", name);
2252}
2253
2254int main(int argc, char *argv[])
2255{
2256 if (argc < 3) {
2257 usage(argv[0]);
2258 return 1;
2259 }
2260
2261 printf("%s: HelenOS Compositor server\n", NAME);
2262
2263 int rc = compositor_srv_init(argv[1], argv[2]);
2264 if (rc != EOK)
2265 return rc;
2266
2267 printf("%s: Accepting connections\n", NAME);
2268 task_retval(0);
2269 async_manager();
2270
2271 /* Never reached */
2272 return 0;
2273}
2274
2275/** @}
2276 */
Note: See TracBrowser for help on using the repository browser.