source: mainline/uspace/srv/hid/compositor/compositor.c@ 03f0b02

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

Resolved Ticket #478 (Single-pixel line not redrawn in vterm).

  • Property mode set to 100644
File size: 47.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 <bool.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 <device/graph_dev.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 sysarg_t id;
93 uint8_t state;
94 desktop_point_t pos;
95 sysarg_t btn_num;
96 desktop_point_t btn_pos;
97 desktop_vector_t accum;
98 sysarg_t grab_flags;
99 bool pressed;
100 cursor_t cursor;
101} pointer_t;
102
103static sysarg_t pointer_id = 0;
104static FIBRIL_MUTEX_INITIALIZE(pointer_list_mtx);
105static LIST_INITIALIZE(pointer_list);
106
107typedef struct {
108 link_t link;
109 service_id_t in_dsid;
110 service_id_t out_dsid;
111 prodcons_t queue;
112 transform_t transform;
113 double dx;
114 double dy;
115 double fx;
116 double fy;
117 double angle;
118 uint8_t opacity;
119 surface_t *surface;
120} window_t;
121
122static service_id_t winreg_id;
123static sysarg_t window_id = 0;
124static FIBRIL_MUTEX_INITIALIZE(window_list_mtx);
125static LIST_INITIALIZE(window_list);
126static double scale_back_x;
127static double scale_back_y;
128
129typedef struct {
130 link_t link;
131 service_id_t dsid;
132 vslmode_t mode;
133 async_sess_t *sess;
134 desktop_point_t pos;
135 surface_t *surface;
136} viewport_t;
137
138static FIBRIL_MUTEX_INITIALIZE(viewport_list_mtx);
139static LIST_INITIALIZE(viewport_list);
140
141/** Input server proxy */
142static input_t *input;
143
144static int comp_key_press(input_t *, kbd_event_type_t, keycode_t, keymod_t, wchar_t);
145static int comp_mouse_move(input_t *, int, int);
146static int comp_abs_move(input_t *, unsigned, unsigned, unsigned, unsigned);
147static int comp_mouse_button(input_t *, int, int);
148
149static input_ev_ops_t input_ev_ops = {
150 .key = comp_key_press,
151 .move = comp_mouse_move,
152 .abs_move = comp_abs_move,
153 .button = comp_mouse_button
154};
155
156static void input_disconnect(void);
157
158
159static pointer_t *input_pointer(input_t *input)
160{
161 return input->user;
162}
163
164static pointer_t *pointer_create()
165{
166 pointer_t *p = (pointer_t *) malloc(sizeof(pointer_t));
167 if (!p) {
168 return NULL;
169 }
170
171 link_initialize(&p->link);
172 p->pos.x = coord_origin;
173 p->pos.y = coord_origin;
174 p->btn_num = 1;
175 p->btn_pos = p->pos;
176 p->accum.x = 0;
177 p->accum.y = 0;
178 p->grab_flags = GF_EMPTY;
179 p->pressed = false;
180 p->state = 0;
181 cursor_init(&p->cursor, CURSOR_DECODER_EMBEDDED, NULL);
182
183 return p;
184}
185
186static void pointer_destroy(pointer_t *p)
187{
188 if (p) {
189 cursor_release(&p->cursor);
190 free(p);
191 }
192}
193
194static window_t *window_create()
195{
196 window_t *win = (window_t *) malloc(sizeof(window_t));
197 if (!win) {
198 return NULL;
199 }
200
201 link_initialize(&win->link);
202 prodcons_initialize(&win->queue);
203 transform_identity(&win->transform);
204 transform_translate(&win->transform, coord_origin, coord_origin);
205 win->dx = coord_origin;
206 win->dy = coord_origin;
207 win->fx = 1;
208 win->fy = 1;
209 win->angle = 0;
210 win->opacity = 255;
211 win->surface = NULL;
212
213 return win;
214}
215
216static void window_destroy(window_t *win)
217{
218 if (win) {
219 if (win->surface) {
220 surface_destroy(win->surface);
221 }
222 free(win);
223 }
224}
225
226static bool comp_coord_to_client(sysarg_t x_in, sysarg_t y_in, transform_t win_trans,
227 sysarg_t x_lim, sysarg_t y_lim, sysarg_t *x_out, sysarg_t *y_out)
228{
229 double x = x_in;
230 double y = y_in;
231 transform_invert(&win_trans);
232 transform_apply_affine(&win_trans, &x, &y);
233
234 /* Since client coordinate origin is (0, 0), it is necessary to check
235 * coordinates to avoid underflow. Moreover, it is convenient to also
236 * check against provided upper limits to determine whether the converted
237 * coordinates are within the client window. */
238 if (x < 0 || y < 0) {
239 return false;
240 } else {
241 (*x_out) = (sysarg_t) (x + 0.5);
242 (*y_out) = (sysarg_t) (y + 0.5);
243
244 if ((*x_out) >= x_lim || (*y_out) >= y_lim) {
245 return false;
246 } else {
247 return true;
248 }
249 }
250}
251
252static void comp_coord_from_client(sysarg_t x_in, sysarg_t y_in, transform_t win_trans,
253 sysarg_t *x_out, sysarg_t *y_out)
254{
255 double x = x_in;
256 double y = y_in;
257 transform_apply_affine(&win_trans, &x, &y);
258
259 /* It is assumed that compositor coordinate origin is chosen in such way,
260 * that underflow/overflow here would be unlikely. */
261 (*x_out) = (sysarg_t) (x + 0.5);
262 (*y_out) = (sysarg_t) (y + 0.5);
263}
264
265static void comp_coord_bounding_rect(sysarg_t x_in, sysarg_t y_in,
266 sysarg_t w_in, sysarg_t h_in, transform_t win_trans,
267 sysarg_t *x_out, sysarg_t *y_out, sysarg_t *w_out, sysarg_t *h_out)
268{
269 if (w_in > 0 && h_in > 0) {
270 sysarg_t x[4];
271 sysarg_t y[4];
272 comp_coord_from_client(x_in, y_in, win_trans, &x[0], &y[0]);
273 comp_coord_from_client(x_in + w_in - 1, y_in, win_trans, &x[1], &y[1]);
274 comp_coord_from_client(x_in + w_in - 1, y_in + h_in - 1, win_trans, &x[2], &y[2]);
275 comp_coord_from_client(x_in, y_in + h_in - 1, win_trans, &x[3], &y[3]);
276 (*x_out) = x[0];
277 (*y_out) = y[0];
278 (*w_out) = x[0];
279 (*h_out) = y[0];
280 for (int i = 1; i < 4; ++i) {
281 (*x_out) = (x[i] < (*x_out)) ? x[i] : (*x_out);
282 (*y_out) = (y[i] < (*y_out)) ? y[i] : (*y_out);
283 (*w_out) = (x[i] > (*w_out)) ? x[i] : (*w_out);
284 (*h_out) = (y[i] > (*h_out)) ? y[i] : (*h_out);
285 }
286 (*w_out) = (*w_out) - (*x_out) + 1;
287 (*h_out) = (*h_out) - (*y_out) + 1;
288 } else {
289 (*w_out) = 0;
290 (*h_out) = 0;
291 }
292}
293
294static void comp_damage(sysarg_t x_dmg_glob, sysarg_t y_dmg_glob,
295 sysarg_t w_dmg_glob, sysarg_t h_dmg_glob)
296{
297 fibril_mutex_lock(&viewport_list_mtx);
298 fibril_mutex_lock(&window_list_mtx);
299 fibril_mutex_lock(&pointer_list_mtx);
300
301 list_foreach(viewport_list, link) {
302
303 /* Determine what part of the viewport must be updated. */
304 viewport_t *vp = list_get_instance(link, viewport_t, link);
305 sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
306 surface_get_resolution(vp->surface, &w_dmg_vp, &h_dmg_vp);
307 bool isec_vp = rectangle_intersect(
308 x_dmg_glob, y_dmg_glob, w_dmg_glob, h_dmg_glob,
309 vp->pos.x, vp->pos.y, w_dmg_vp, h_dmg_vp,
310 &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
311
312 if (isec_vp) {
313
314 /* Paint background color. */
315 for (sysarg_t y = y_dmg_vp - vp->pos.y; y < y_dmg_vp - vp->pos.y + h_dmg_vp; ++y) {
316 for (sysarg_t x = x_dmg_vp - vp->pos.x; x < x_dmg_vp - vp->pos.x + w_dmg_vp; ++x) {
317 surface_put_pixel(vp->surface, x, y, bg_color);
318 }
319 }
320
321 transform_t transform;
322 source_t source;
323 drawctx_t context;
324
325 source_init(&source);
326 source_set_filter(&source, filter_nearest);
327 drawctx_init(&context, vp->surface);
328 drawctx_set_compose(&context, compose_over);
329 drawctx_set_source(&context, &source);
330
331 /* For each window. */
332 for (link_t *link = window_list.head.prev;
333 link != &window_list.head; link = link->prev) {
334
335 /* Determine what part of the window intersects with the
336 * updated area of the current viewport. */
337 window_t *win = list_get_instance(link, window_t, link);
338 if (!win->surface) {
339 continue;
340 }
341 sysarg_t x_dmg_win, y_dmg_win, w_dmg_win, h_dmg_win;
342 surface_get_resolution(win->surface, &w_dmg_win, &h_dmg_win);
343 comp_coord_bounding_rect(0, 0, w_dmg_win, h_dmg_win, win->transform,
344 &x_dmg_win, &y_dmg_win, &w_dmg_win, &h_dmg_win);
345 bool isec_win = rectangle_intersect(
346 x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp,
347 x_dmg_win, y_dmg_win, w_dmg_win, h_dmg_win,
348 &x_dmg_win, &y_dmg_win, &w_dmg_win, &h_dmg_win);
349
350 if (isec_win) {
351 /* Prepare conversion from global coordinates to viewport
352 * coordinates. */
353 transform = win->transform;
354 double_point_t pos;
355 pos.x = vp->pos.x;
356 pos.y = vp->pos.y;
357 transform_translate(&transform, -pos.x, -pos.y);
358
359 source_set_transform(&source, transform);
360 source_set_texture(&source, win->surface, false);
361 source_set_alpha(&source, PIXEL(win->opacity, 0, 0, 0));
362
363 drawctx_transfer(&context,
364 x_dmg_win - vp->pos.x, y_dmg_win - vp->pos.y, w_dmg_win, h_dmg_win);
365 }
366 }
367
368 list_foreach(pointer_list, link) {
369
370 /* Determine what part of the pointer intersects with the
371 * updated area of the current viewport. */
372 pointer_t *ptr = list_get_instance(link, pointer_t, link);
373 sysarg_t x_dmg_ptr, y_dmg_ptr, w_dmg_ptr, h_dmg_ptr;
374 surface_t *sf_ptr = ptr->cursor.states[ptr->state];
375 surface_get_resolution(sf_ptr, &w_dmg_ptr, &h_dmg_ptr);
376 bool isec_ptr = rectangle_intersect(
377 x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp,
378 ptr->pos.x, ptr->pos.y, w_dmg_ptr, h_dmg_ptr,
379 &x_dmg_ptr, &y_dmg_ptr, &w_dmg_ptr, &h_dmg_ptr);
380
381 if (isec_ptr) {
382 /* Pointer is currently painted directly by copying pixels.
383 * However, it is possible to draw the painter similarly
384 * as window by using drawctx_transfer. It would allow
385 * more sophisticated control over drawing, but would also
386 * cost more regarding the performance. */
387
388 pixel_t pix = 0;
389 sysarg_t x_vp = x_dmg_ptr - vp->pos.x;
390 sysarg_t y_vp = y_dmg_ptr - vp->pos.y;
391 sysarg_t x_ptr = x_dmg_ptr - ptr->pos.x;
392 sysarg_t y_ptr = y_dmg_ptr - ptr->pos.y;
393
394 for (sysarg_t y = 0; y < h_dmg_ptr; ++y) {
395 for (sysarg_t x = 0; x < w_dmg_ptr; ++x) {
396 pix = surface_get_pixel(sf_ptr, x_ptr + x, y_ptr + y);
397 if (ALPHA(pix) == 255) {
398 surface_put_pixel(vp->surface, x_vp + x, y_vp + y, pix);
399 }
400 }
401 }
402 }
403 }
404 }
405 }
406
407 fibril_mutex_unlock(&pointer_list_mtx);
408 fibril_mutex_unlock(&window_list_mtx);
409
410 /* Notify visualizers about updated regions. */
411 list_foreach(viewport_list, link) {
412 viewport_t *vp = list_get_instance(link, viewport_t, link);
413 sysarg_t x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp;
414 surface_get_damaged_region(vp->surface, &x_dmg_vp, &y_dmg_vp, &w_dmg_vp, &h_dmg_vp);
415 surface_reset_damaged_region(vp->surface);
416 visualizer_update_damaged_region(
417 vp->sess, x_dmg_vp, y_dmg_vp, w_dmg_vp, h_dmg_vp, 0, 0);
418 }
419
420 fibril_mutex_unlock(&viewport_list_mtx);
421}
422
423static void comp_window_get_event(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
424{
425 window_event_t *event = (window_event_t *) prodcons_consume(&win->queue);
426
427 ipc_callid_t callid;
428 size_t len;
429
430 if (!async_data_read_receive(&callid, &len)) {
431 async_answer_0(iid, EINVAL);
432 free(event);
433 return;
434 }
435 int rc = async_data_read_finalize(callid, event, len);
436 if (rc != EOK) {
437 async_answer_0(iid, ENOMEM);
438 free(event);
439 return;
440 }
441 async_answer_0(iid, EOK);
442
443 free(event);
444}
445
446static void comp_window_damage(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
447{
448 sysarg_t x = IPC_GET_ARG1(*icall);
449 sysarg_t y = IPC_GET_ARG2(*icall);
450 sysarg_t width = IPC_GET_ARG3(*icall);
451 sysarg_t height = IPC_GET_ARG4(*icall);
452
453 if (width == 0 || height == 0) {
454 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
455 } else {
456 fibril_mutex_lock(&window_list_mtx);
457 comp_coord_bounding_rect(x, y, width, height,
458 win->transform, &x, &y, &width, &height);
459 fibril_mutex_unlock(&window_list_mtx);
460 comp_damage(x, y, width, height);
461 }
462
463 async_answer_0(iid, EOK);
464}
465
466static void comp_window_grab(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
467{
468 sysarg_t pos_id = IPC_GET_ARG1(*icall);
469 sysarg_t grab_flags = IPC_GET_ARG2(*icall);
470
471 fibril_mutex_lock(&pointer_list_mtx);
472 list_foreach(pointer_list, link) {
473 pointer_t *pointer = list_get_instance(link, pointer_t, link);
474 if (pointer->id == pos_id) {
475 pointer->grab_flags = grab_flags;
476 // TODO change pointer->state according to grab_flags
477 break;
478 }
479 }
480 fibril_mutex_unlock(&pointer_list_mtx);
481
482 if ((grab_flags & GF_RESIZE_X) || (grab_flags & GF_RESIZE_Y)) {
483 scale_back_x = 1;
484 scale_back_y = 1;
485 }
486
487 async_answer_0(iid, EOK);
488}
489
490static void comp_window_resize(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
491{
492 int rc;
493
494 ipc_callid_t callid;
495 size_t size;
496 unsigned int flags;
497
498 /* Start sharing resized window with client. */
499 if (!async_share_out_receive(&callid, &size, &flags)) {
500 async_answer_0(iid, EINVAL);
501 return;
502 }
503 void *new_cell_storage;
504 rc = async_share_out_finalize(callid, &new_cell_storage);
505 if ((rc != EOK) || (new_cell_storage == AS_MAP_FAILED)) {
506 async_answer_0(iid, ENOMEM);
507 return;
508 }
509
510 /* Create new surface for the resized window. */
511 surface_t *new_surface = surface_create(
512 IPC_GET_ARG1(*icall), IPC_GET_ARG2(*icall),
513 new_cell_storage, SURFACE_FLAG_SHARED);
514 if (!new_surface) {
515 as_area_destroy(new_cell_storage);
516 async_answer_0(iid, ENOMEM);
517 return;
518 }
519
520 /* Switch new surface with old surface and calculate damage. */
521 fibril_mutex_lock(&window_list_mtx);
522
523 sysarg_t old_width = 0;
524 sysarg_t old_height = 0;
525 if (win->surface) {
526 surface_get_resolution(win->surface, &old_width, &old_height);
527 surface_destroy(win->surface);
528 }
529
530 win->surface = new_surface;
531
532 sysarg_t new_width = 0;
533 sysarg_t new_height = 0;
534 surface_get_resolution(win->surface, &new_width, &new_height);
535
536 sysarg_t x, y;
537 sysarg_t width = old_width > new_width ? old_width : new_width;
538 sysarg_t height = old_height > new_height ? old_height : new_height;
539 comp_coord_bounding_rect(0, 0, width, height, win->transform, &x, &y, &width, &height);
540
541 fibril_mutex_unlock(&window_list_mtx);
542
543 comp_damage(x, y, width, height);
544
545 async_answer_0(iid, EOK);
546}
547
548static void comp_window_close(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
549{
550 /* Stop managing the window. */
551 fibril_mutex_lock(&window_list_mtx);
552 list_remove(&win->link);
553 fibril_mutex_unlock(&window_list_mtx);
554
555 /* Calculate damage. */
556 sysarg_t x = 0;
557 sysarg_t y = 0;
558 sysarg_t width = 0;
559 sysarg_t height = 0;
560 if (win->surface) {
561 surface_get_resolution(win->surface, &width, &height);
562 comp_coord_bounding_rect(
563 0, 0, width, height, win->transform, &x, &y, &width, &height);
564 }
565
566 /* Release window resources. */
567 loc_service_unregister(win->in_dsid);
568 loc_service_unregister(win->out_dsid);
569 while (!list_empty(&win->queue.list)) {
570 list_remove(list_first(&win->queue.list));
571 }
572 window_destroy(win);
573
574 comp_damage(x, y, width, height);
575
576 async_answer_0(iid, EOK);
577}
578
579static void comp_window_close_request(window_t *win, ipc_callid_t iid, ipc_call_t *icall)
580{
581 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
582 if (event == NULL) {
583 async_answer_0(iid, ENOMEM);
584 return;
585 }
586
587 link_initialize(&event->link);
588 event->type = ET_WINDOW_CLOSE;
589
590 prodcons_produce(&win->queue, &event->link);
591 async_answer_0(iid, EOK);
592}
593
594static void client_connection(ipc_callid_t iid, ipc_call_t *icall, void *arg)
595{
596 ipc_call_t call;
597 ipc_callid_t callid;
598 service_id_t service_id = (service_id_t) IPC_GET_ARG1(*icall);
599
600 /* Allocate resources for new window and register it to the location service. */
601 if (service_id == winreg_id) {
602 async_answer_0(iid, EOK);
603
604 callid = async_get_call(&call);
605 if (IPC_GET_IMETHOD(call) == WINDOW_REGISTER) {
606 fibril_mutex_lock(&window_list_mtx);
607
608 window_t *win = window_create();
609 if (!win) {
610 async_answer_2(callid, ENOMEM, 0, 0);
611 return;
612 }
613
614 char name_in[LOC_NAME_MAXLEN + 1];
615 snprintf(name_in, LOC_NAME_MAXLEN, "%s%s/win%zuin", NAMESPACE,
616 server_name, window_id);
617
618 char name_out[LOC_NAME_MAXLEN + 1];
619 snprintf(name_out, LOC_NAME_MAXLEN, "%s%s/win%zuout", NAMESPACE,
620 server_name, window_id);
621
622 ++window_id;
623
624 if (loc_service_register(name_in, &win->in_dsid) != EOK) {
625 window_destroy(win);
626 async_answer_2(callid, EINVAL, 0, 0);
627 return;
628 }
629
630 if (loc_service_register(name_out, &win->out_dsid) != EOK) {
631 loc_service_unregister(win->in_dsid);
632 window_destroy(win);
633 async_answer_2(callid, EINVAL, 0, 0);
634 return;
635 }
636
637 list_prepend(&win->link, &window_list);
638
639 async_answer_2(callid, EOK, win->in_dsid, win->out_dsid);
640 fibril_mutex_unlock(&window_list_mtx);
641 return;
642 } else {
643 async_answer_0(callid, EINVAL);
644 return;
645 }
646 }
647
648 /* Match the client with pre-allocated window. */
649 window_t *win = NULL;
650 fibril_mutex_lock(&window_list_mtx);
651 list_foreach(window_list, link) {
652 window_t *cur = list_get_instance(link, window_t, link);
653 if (cur->in_dsid == service_id || cur->out_dsid == service_id) {
654 win = cur;
655 break;
656 }
657 }
658 fibril_mutex_unlock(&window_list_mtx);
659
660 if (win) {
661 async_answer_0(iid, EOK);
662 } else {
663 async_answer_0(iid, EINVAL);
664 return;
665 }
666
667 /* Each client establishes two separate connections. */
668 if (win->in_dsid == service_id) {
669 while (true) {
670 callid = async_get_call(&call);
671
672 if (!IPC_GET_IMETHOD(call)) {
673 async_answer_0(callid, EINVAL);
674 return;
675 }
676
677 switch (IPC_GET_IMETHOD(call)) {
678 case WINDOW_GET_EVENT:
679 comp_window_get_event(win, callid, &call);
680 break;
681 default:
682 async_answer_0(callid, EINVAL);
683 }
684 }
685 } else if (win->out_dsid == service_id) {
686 while (true) {
687 callid = async_get_call(&call);
688
689 if (!IPC_GET_IMETHOD(call)) {
690 async_answer_0(callid, EINVAL);
691 return;
692 }
693
694 switch (IPC_GET_IMETHOD(call)) {
695 case WINDOW_DAMAGE:
696 comp_window_damage(win, callid, &call);
697 break;
698 case WINDOW_GRAB:
699 comp_window_grab(win, callid, &call);
700 break;
701 case WINDOW_RESIZE:
702 comp_window_resize(win, callid, &call);
703 break;
704 case WINDOW_CLOSE:
705 comp_window_close(win, callid, &call);
706 break;
707 case WINDOW_CLOSE_REQUEST:
708 comp_window_close_request(win, callid, &call);
709 break;
710 default:
711 async_answer_0(callid, EINVAL);
712 }
713 }
714 }
715}
716
717static void comp_mode_change(viewport_t *vp, ipc_callid_t iid, ipc_call_t *icall)
718{
719 int rc;
720 sysarg_t mode_idx = IPC_GET_ARG2(*icall);
721 fibril_mutex_lock(&viewport_list_mtx);
722
723 /* Retrieve the mode that shall be set. */
724 vslmode_t new_mode;
725 rc = visualizer_get_mode(vp->sess, &new_mode, mode_idx);
726 if (rc != EOK) {
727 fibril_mutex_unlock(&viewport_list_mtx);
728 async_answer_0(iid, EINVAL);
729 return;
730 }
731
732 /* Create surface with respect to the retrieved mode. */
733 surface_t *new_surface = surface_create(new_mode.screen_width,
734 new_mode.screen_height, NULL, SURFACE_FLAG_SHARED);
735 if (!new_surface) {
736 fibril_mutex_unlock(&viewport_list_mtx);
737 async_answer_0(iid, ENOMEM);
738 return;
739 }
740
741 /* Try to set the mode and share out the surface. */
742 rc = visualizer_set_mode(vp->sess,
743 new_mode.index, new_mode.version, surface_direct_access(new_surface));
744 if (rc != EOK) {
745 surface_destroy(new_surface);
746 fibril_mutex_unlock(&viewport_list_mtx);
747 async_answer_0(iid, rc);
748 return;
749 }
750
751 /* Destroy old surface and update viewport. */
752 surface_destroy(vp->surface);
753 vp->mode = new_mode;
754 vp->surface = new_surface;
755
756 fibril_mutex_unlock(&viewport_list_mtx);
757 async_answer_0(iid, EOK);
758
759 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
760}
761
762static void viewport_destroy(viewport_t *vp)
763{
764 if (vp) {
765 visualizer_yield(vp->sess);
766 surface_destroy(vp->surface);
767 async_hangup(vp->sess);
768 free(vp);
769 }
770}
771
772static void comp_visualizer_disconnect(viewport_t *vp, ipc_callid_t iid, ipc_call_t *icall)
773{
774 /* Release viewport resources. */
775 fibril_mutex_lock(&viewport_list_mtx);
776 list_remove(&vp->link);
777 viewport_destroy(vp);
778
779 /* Terminate compositor if there are no more viewports. */
780 if (list_empty(&viewport_list)) {
781 fibril_mutex_unlock(&viewport_list_mtx);
782 loc_service_unregister(winreg_id);
783 input_disconnect();
784
785 /* Close all clients and their windows. */
786 fibril_mutex_lock(&window_list_mtx);
787 list_foreach(window_list, link) {
788 window_t *win = list_get_instance(link, window_t, link);
789 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
790 if (event) {
791 link_initialize(&event->link);
792 event->type = WINDOW_CLOSE;
793 prodcons_produce(&win->queue, &event->link);
794 }
795 }
796 fibril_mutex_unlock(&window_list_mtx);
797
798 async_answer_0(iid, EOK);
799
800 /* All fibrils of the compositor will terminate soon. */
801 } else {
802 fibril_mutex_unlock(&viewport_list_mtx);
803 async_answer_0(iid, EOK);
804 }
805}
806
807static void vsl_notifications(ipc_callid_t iid, ipc_call_t *icall, void *arg)
808{
809 viewport_t *vp = NULL;
810 fibril_mutex_lock(&viewport_list_mtx);
811 list_foreach(viewport_list, link) {
812 viewport_t *cur = list_get_instance(link, viewport_t, link);
813 if (cur->dsid == (service_id_t) IPC_GET_ARG1(*icall)) {
814 vp = cur;
815 break;
816 }
817 }
818 fibril_mutex_unlock(&viewport_list_mtx);
819
820 if (!vp) {
821 return;
822 }
823
824 /* Ignore parameters, the connection is already opened. */
825 while (true) {
826 ipc_call_t call;
827 ipc_callid_t callid = async_get_call(&call);
828
829 if (!IPC_GET_IMETHOD(call)) {
830 async_hangup(vp->sess);
831 return;
832 }
833
834 switch (IPC_GET_IMETHOD(call)) {
835 case VISUALIZER_MODE_CHANGE:
836 comp_mode_change(vp, callid, &call);
837 break;
838 case VISUALIZER_DISCONNECT:
839 comp_visualizer_disconnect(vp, callid, &call);
840 return;
841 default:
842 async_answer_0(callid, EINVAL);
843 }
844 }
845}
846
847static async_sess_t *vsl_connect(const char *svc)
848{
849 int rc;
850 async_sess_t *sess;
851 service_id_t dsid;
852 devman_handle_t handle;
853
854 rc = loc_service_get_id(svc, &dsid, 0);
855 if (rc != EOK) {
856 return NULL;
857 }
858
859 rc = devman_fun_sid_to_handle(dsid, &handle);
860 if (rc == EOK) {
861 sess = devman_device_connect(EXCHANGE_SERIALIZE, handle, 0);
862 if (sess == NULL) {
863 printf("%s: Unable to connect to visualizer %s\n", NAME, svc);
864 return NULL;
865 }
866 rc = graph_dev_connect(sess);
867 if (rc != EOK) {
868 return NULL;
869 }
870 } else if (rc == ENOENT) {
871 sess = loc_service_connect(EXCHANGE_SERIALIZE, dsid, 0);
872 if (sess == NULL) {
873 printf("%s: Unable to connect to visualizer %s\n", NAME, svc);
874 return NULL;
875 }
876 } else {
877 return NULL;
878 }
879
880 async_exch_t *exch = async_exchange_begin(sess);
881 rc = async_connect_to_me(exch, dsid, 0, 0, vsl_notifications, NULL);
882 async_exchange_end(exch);
883
884 if (rc != EOK) {
885 async_hangup(sess);
886 printf("%s: Unable to create callback connection to service %s (%s)\n",
887 NAME, svc, str_error(rc));
888 return NULL;
889 }
890
891 return sess;
892}
893
894static viewport_t *viewport_create(const char *vsl_name)
895{
896 int rc;
897
898 viewport_t *vp = (viewport_t *) malloc(sizeof(viewport_t));
899 if (!vp) {
900 return NULL;
901 }
902
903 link_initialize(&vp->link);
904 vp->pos.x = coord_origin;
905 vp->pos.y = coord_origin;
906
907 /* Establish output bidirectional connection. */
908 vp->sess = vsl_connect(vsl_name);
909 rc = loc_service_get_id(vsl_name, &vp->dsid, 0);
910 if (vp->sess == NULL || rc != EOK) {
911 free(vp);
912 return NULL;
913 }
914
915 /* Claim the given visualizer. */
916 rc = visualizer_claim(vp->sess, 0);
917 if (rc != EOK) {
918 async_hangup(vp->sess);
919 free(vp);
920 printf("%s: Unable to claim visualizer (%s)\n", NAME, str_error(rc));
921 return NULL;
922 }
923
924 /* Retrieve the default mode. */
925 rc = visualizer_get_default_mode(vp->sess, &vp->mode);
926 if (rc != EOK) {
927 visualizer_yield(vp->sess);
928 async_hangup(vp->sess);
929 free(vp);
930 printf("%s: Unable to retrieve mode (%s)\n", NAME, str_error(rc));
931 return NULL;
932 }
933
934 /* Create surface with respect to the retrieved mode. */
935 vp->surface = surface_create(vp->mode.screen_width, vp->mode.screen_height,
936 NULL, SURFACE_FLAG_SHARED);
937 if (vp->surface == NULL) {
938 visualizer_yield(vp->sess);
939 async_hangup(vp->sess);
940 free(vp);
941 printf("%s: Unable to create surface (%s)\n", NAME, str_error(rc));
942 return NULL;
943 }
944
945 /* Try to set the mode and share out the surface. */
946 rc = visualizer_set_mode(vp->sess,
947 vp->mode.index, vp->mode.version, surface_direct_access(vp->surface));
948 if (rc != EOK) {
949 visualizer_yield(vp->sess);
950 surface_destroy(vp->surface);
951 async_hangup(vp->sess);
952 free(vp);
953 printf("%s: Unable to set mode (%s)\n", NAME, str_error(rc));
954 return NULL;
955 }
956
957 return vp;
958}
959
960static void comp_post_event(window_event_t *event)
961{
962 fibril_mutex_lock(&window_list_mtx);
963 window_t *win = (window_t *) list_first(&window_list);
964 if (win) {
965 prodcons_produce(&win->queue, &event->link);
966 } else {
967 free(event);
968 }
969 fibril_mutex_unlock(&window_list_mtx);
970}
971
972static void comp_recalc_transform(window_t *win)
973{
974 transform_t translate;
975 transform_identity(&translate);
976 transform_translate(&translate, win->dx, win->dy);
977
978 transform_t scale;
979 transform_identity(&scale);
980 transform_scale(&scale, win->fx, win->fy);
981
982 transform_t rotate;
983 transform_identity(&rotate);
984 transform_rotate(&rotate, win->angle);
985
986 transform_t transform;
987 transform_t temp;
988 transform_identity(&transform);
989 temp = transform;
990 transform_multiply(&transform, &temp, &translate);
991 temp = transform;
992 transform_multiply(&transform, &temp, &rotate);
993 temp = transform;
994 transform_multiply(&transform, &temp, &scale);
995
996
997 win->transform = transform;
998}
999
1000static void comp_window_animate(pointer_t *pointer, window_t *win,
1001 sysarg_t *dmg_x, sysarg_t *dmg_y, sysarg_t *dmg_width, sysarg_t *dmg_height)
1002{
1003 /* window_list_mtx locked by caller */
1004
1005 int dx = pointer->accum.x;
1006 int dy = pointer->accum.y;
1007 pointer->accum.x = 0;
1008 pointer->accum.y = 0;
1009
1010 bool move = (pointer->grab_flags & GF_MOVE_X) || (pointer->grab_flags & GF_MOVE_Y);
1011 bool scale = (pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_SCALE_Y);
1012 bool resize = (pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y);
1013
1014 sysarg_t width, height;
1015 surface_get_resolution(win->surface, &width, &height);
1016
1017 if (move) {
1018 double cx = 0;
1019 double cy = 0;
1020 if (pointer->grab_flags & GF_MOVE_X) {
1021 cx = 1;
1022 }
1023 if (pointer->grab_flags & GF_MOVE_Y) {
1024 cy = 1;
1025 }
1026
1027 if (scale || resize) {
1028 transform_t rotate;
1029 transform_identity(&rotate);
1030 transform_rotate(&rotate, win->angle);
1031 transform_apply_linear(&rotate, &cx, &cy);
1032 }
1033
1034 cx = (cx < 0) ? (-1 * cx) : cx;
1035 cy = (cy < 0) ? (-1 * cy) : cy;
1036
1037 win->dx += (cx * dx);
1038 win->dy += (cy * dy);
1039 }
1040
1041 if (scale || resize) {
1042 double _dx = dx;
1043 double _dy = dy;
1044 transform_t unrotate;
1045 transform_identity(&unrotate);
1046 transform_rotate(&unrotate, -win->angle);
1047 transform_apply_linear(&unrotate, &_dx, &_dy);
1048 _dx = (pointer->grab_flags & GF_MOVE_X) ? -_dx : _dx;
1049 _dy = (pointer->grab_flags & GF_MOVE_Y) ? -_dy : _dy;
1050
1051 if ((pointer->grab_flags & GF_SCALE_X) || (pointer->grab_flags & GF_RESIZE_X)) {
1052 double fx = 1.0 + (_dx / (width * win->fx));
1053 if (fx > 0) {
1054 win->fx *= fx;
1055 scale_back_x *= fx;
1056 }
1057 }
1058
1059 if ((pointer->grab_flags & GF_SCALE_Y) || (pointer->grab_flags & GF_RESIZE_Y)) {
1060 double fy = 1.0 + (_dy / (height * win->fy));
1061 if (fy > 0) {
1062 win->fy *= fy;
1063 scale_back_y *= fy;
1064 }
1065 }
1066 }
1067
1068 sysarg_t x1, y1, width1, height1;
1069 sysarg_t x2, y2, width2, height2;
1070 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1071 &x1, &y1, &width1, &height1);
1072 comp_recalc_transform(win);
1073 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1074 &x2, &y2, &width2, &height2);
1075 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1076 dmg_x, dmg_y, dmg_width, dmg_height);
1077}
1078
1079static int comp_abs_move(input_t *input, unsigned x , unsigned y,
1080 unsigned max_x, unsigned max_y)
1081{
1082 /* XXX TODO Use absolute coordinates directly */
1083
1084 pointer_t *pointer = input_pointer(input);
1085
1086 sysarg_t width, height;
1087
1088 fibril_mutex_lock(&viewport_list_mtx);
1089 if (list_empty(&viewport_list)) {
1090 printf("No viewport found\n");
1091 fibril_mutex_unlock(&viewport_list_mtx);
1092 return EOK; /* XXX */
1093 }
1094 link_t *link = list_first(&viewport_list);
1095 viewport_t *vp = list_get_instance(link, viewport_t, link);
1096 surface_get_resolution(vp->surface, &width, &height);
1097 desktop_point_t vp_pos = vp->pos;
1098 fibril_mutex_unlock(&viewport_list_mtx);
1099
1100 desktop_point_t pos_in_viewport;
1101 pos_in_viewport.x = x * width / max_x;
1102 pos_in_viewport.y = y * height / max_y;
1103
1104 /* Calculate offset from pointer */
1105 fibril_mutex_lock(&pointer_list_mtx);
1106 desktop_vector_t delta;
1107 delta.x = (vp_pos.x + pos_in_viewport.x) - pointer->pos.x;
1108 delta.y = (vp_pos.y + pos_in_viewport.y) - pointer->pos.y;
1109 fibril_mutex_unlock(&pointer_list_mtx);
1110
1111 return comp_mouse_move(input, delta.x, delta.y);
1112}
1113
1114static int comp_mouse_move(input_t *input, int dx, int dy)
1115{
1116 pointer_t *pointer = input_pointer(input);
1117
1118 /* Update pointer position. */
1119 fibril_mutex_lock(&pointer_list_mtx);
1120 desktop_point_t old_pos = pointer->pos;
1121 sysarg_t cursor_width;
1122 sysarg_t cursor_height;
1123 surface_get_resolution(pointer->cursor.states[pointer->state],
1124 &cursor_width, &cursor_height);
1125 pointer->pos.x += dx;
1126 pointer->pos.y += dy;
1127 fibril_mutex_unlock(&pointer_list_mtx);
1128 comp_damage(old_pos.x, old_pos.y, cursor_width, cursor_height);
1129 comp_damage(old_pos.x + dx, old_pos.y + dy, cursor_width, cursor_height);
1130
1131 fibril_mutex_lock(&window_list_mtx);
1132 window_t *top = (window_t *) list_first(&window_list);
1133 if (top && top->surface) {
1134
1135 if (pointer->grab_flags == GF_EMPTY) {
1136 /* Notify top-level window about move event. */
1137 bool within_client = false;
1138 sysarg_t point_x, point_y;
1139 sysarg_t width, height;
1140 surface_get_resolution(top->surface, &width, &height);
1141 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1142 top->transform, width, height, &point_x, &point_y);
1143 fibril_mutex_unlock(&window_list_mtx);
1144
1145 if (within_client) {
1146 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1147 if (event) {
1148 link_initialize(&event->link);
1149 event->type = ET_POSITION_EVENT;
1150 event->data.pos.pos_id = pointer->id;
1151 event->data.pos.type = POS_UPDATE;
1152 event->data.pos.btn_num = pointer->btn_num;
1153 event->data.pos.hpos = point_x;
1154 event->data.pos.vpos = point_y;
1155 comp_post_event(event);
1156 }
1157 }
1158 } else {
1159 /* Pointer is grabbed by top-level window action. */
1160 pointer->accum.x += dx;
1161 pointer->accum.y += dy;
1162#if ANIMATE_WINDOW_TRANSFORMS == 1
1163 sysarg_t x, y, width, height;
1164 comp_window_animate(pointer, top, &x, &y, &width, &height);
1165#endif
1166 fibril_mutex_unlock(&window_list_mtx);
1167#if ANIMATE_WINDOW_TRANSFORMS == 1
1168 comp_damage(x, y, width, height);
1169#endif
1170 }
1171 } else {
1172 fibril_mutex_unlock(&window_list_mtx);
1173 }
1174
1175 return EOK;
1176}
1177
1178static int comp_mouse_button(input_t *input, int bnum, int bpress)
1179{
1180 pointer_t *pointer = input_pointer(input);
1181
1182 if (bpress) {
1183 pointer->btn_pos = pointer->pos;
1184 pointer->btn_num = bnum;
1185 pointer->pressed = true;
1186
1187 /* Check whether mouse press belongs to the top-level window. */
1188 fibril_mutex_lock(&window_list_mtx);
1189 window_t *win = (window_t *) list_first(&window_list);
1190 if (!win || !win->surface) {
1191 fibril_mutex_unlock(&window_list_mtx);
1192 return EOK;
1193 }
1194 sysarg_t x, y, width, height;
1195 surface_get_resolution(win->surface, &width, &height);
1196 bool within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1197 win->transform, width, height, &x, &y);
1198 fibril_mutex_unlock(&window_list_mtx);
1199
1200 /* Send mouse press to the top-level window. */
1201 if (within_client) {
1202 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1203 if (event) {
1204 link_initialize(&event->link);
1205 event->type = ET_POSITION_EVENT;
1206 event->data.pos.pos_id = pointer->id;
1207 event->data.pos.type = POS_PRESS;
1208 event->data.pos.btn_num = bnum;
1209 event->data.pos.hpos = x;
1210 event->data.pos.vpos = y;
1211 comp_post_event(event);
1212 } else {
1213 return ENOMEM;
1214 }
1215 }
1216 } else if (pointer->pressed && pointer->btn_num == (unsigned)bnum) {
1217 pointer->pressed = false;
1218
1219 fibril_mutex_lock(&window_list_mtx);
1220 window_t *win = NULL;
1221 sysarg_t point_x = 0;
1222 sysarg_t point_y = 0;
1223 sysarg_t width, height;
1224 bool within_client = false;
1225
1226 /* Determine the window which the mouse release belongs to. */
1227 list_foreach(window_list, link) {
1228 win = list_get_instance(link, window_t, link);
1229 if (win->surface) {
1230 surface_get_resolution(win->surface, &width, &height);
1231 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1232 win->transform, width, height, &point_x, &point_y);
1233 }
1234 if (within_client) {
1235 break;
1236 }
1237 }
1238
1239 /* Check whether the window is top-level window. */
1240 window_t *top = (window_t *) list_first(&window_list);
1241 if (!win || !top) {
1242 pointer->grab_flags = GF_EMPTY;
1243 fibril_mutex_unlock(&window_list_mtx);
1244 return EOK;
1245 }
1246
1247 window_event_t *event = NULL;
1248 sysarg_t dmg_x, dmg_y;
1249 sysarg_t dmg_width = 0;
1250 sysarg_t dmg_height = 0;
1251
1252 sysarg_t pre_x = 0;
1253 sysarg_t pre_y = 0;
1254 sysarg_t pre_width = 0;
1255 sysarg_t pre_height = 0;
1256
1257#if ANIMATE_WINDOW_TRANSFORMS == 0
1258 if (pointer->grab_flags != GF_EMPTY) {
1259 comp_window_animate(pointer, top, &pre_x, &pre_y, &pre_width, &pre_height);
1260 dmg_x = pre_x;
1261 dmg_y = pre_y;
1262 dmg_width = pre_width;
1263 dmg_height = pre_height;
1264 }
1265#endif
1266
1267 if ((pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y)) {
1268
1269 surface_get_resolution(top->surface, &width, &height);
1270 top->fx *= (1.0 / scale_back_x);
1271 top->fy *= (1.0 / scale_back_y);
1272 comp_recalc_transform(top);
1273
1274 /* Commit proper resize action. */
1275 event = (window_event_t *) malloc(sizeof(window_event_t));
1276 if (event) {
1277 link_initialize(&event->link);
1278 event->type = ET_WINDOW_RESIZE;
1279
1280 int dx = (int) (((double) width) * (scale_back_x - 1.0));
1281 int dy = (int) (((double) height) * (scale_back_y - 1.0));
1282
1283 if (pointer->grab_flags & GF_RESIZE_X) {
1284 event->data.rsz.width =
1285 ((((int) width) + dx) >= 0) ? (width + dx) : 0;
1286 } else {
1287 event->data.rsz.width = width;
1288 }
1289
1290 if (pointer->grab_flags & GF_RESIZE_Y) {
1291 event->data.rsz.height =
1292 ((((int) height) + dy) >= 0) ? (height + dy) : 0;
1293 } else {
1294 event->data.rsz.height = height;
1295 }
1296 }
1297
1298 pointer->grab_flags = GF_EMPTY;
1299
1300 } else if (within_client && (pointer->grab_flags == GF_EMPTY) && (top == win)) {
1301
1302 /* Notify top-level window about mouse release. */
1303 event = (window_event_t *) malloc(sizeof(window_event_t));
1304 if (event) {
1305 link_initialize(&event->link);
1306 event->type = ET_POSITION_EVENT;
1307 event->data.pos.pos_id = pointer->id;
1308 event->data.pos.type = POS_RELEASE;
1309 event->data.pos.btn_num = bnum;
1310 event->data.pos.hpos = point_x;
1311 event->data.pos.vpos = point_y;
1312 }
1313 pointer->grab_flags = GF_EMPTY;
1314
1315 } else if (within_client && (pointer->grab_flags == GF_EMPTY) && (bnum == 1)) {
1316
1317 /* Bring the window to the foreground. */
1318 list_remove(&win->link);
1319 list_prepend(&win->link, &window_list);
1320 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1321 &dmg_x, &dmg_y, &dmg_width, &dmg_height);
1322
1323 } else {
1324 pointer->grab_flags = GF_EMPTY;
1325 }
1326
1327 fibril_mutex_unlock(&window_list_mtx);
1328
1329 if (dmg_width > 0 && dmg_height > 0) {
1330 comp_damage(dmg_x, dmg_y, dmg_width, dmg_height);
1331 }
1332
1333 if (event) {
1334 comp_post_event(event);
1335 }
1336 }
1337
1338 return EOK;
1339}
1340
1341static int comp_key_press(input_t *input, kbd_event_type_t type, keycode_t key,
1342 keymod_t mods, wchar_t c)
1343{
1344 bool win_transform = (mods & KM_ALT) && (
1345 key == KC_W || key == KC_S || key == KC_A || key == KC_D ||
1346 key == KC_Q || key == KC_E || key == KC_R || key == KC_F);
1347 bool win_resize = (mods & KM_ALT) && (
1348 key == KC_T || key == KC_G || key == KC_B || key == KC_N);
1349 bool win_opacity = (mods & KM_ALT) && (
1350 key == KC_C || key == KC_V);
1351 bool win_close = (mods & KM_ALT) && (key == KC_X);
1352 bool win_switch = (mods & KM_ALT) && (key == KC_TAB);
1353 bool viewport_move = (mods & KM_ALT) && (
1354 key == KC_I || key == KC_K || key == KC_J || key == KC_L);
1355 bool viewport_change = (mods & KM_ALT) && (
1356 key == KC_O || key == KC_P);
1357 bool kconsole_switch = (mods & KM_ALT) && (key == KC_M);
1358 bool compositor_test = (mods & KM_ALT) && (key == KC_H);
1359
1360 bool filter = (type == KEY_RELEASE) && (win_transform || win_resize ||
1361 win_opacity || win_close || win_switch || viewport_move ||
1362 viewport_change || kconsole_switch || compositor_test);
1363
1364 if (filter) {
1365 /* no-op */
1366 } else if (win_transform) {
1367 fibril_mutex_lock(&window_list_mtx);
1368 window_t *win = (window_t *) list_first(&window_list);
1369 if (win && win->surface) {
1370 switch (key) {
1371 case KC_W:
1372 win->dy += -20;
1373 break;
1374 case KC_S:
1375 win->dy += 20;
1376 break;
1377 case KC_A:
1378 win->dx += -20;
1379 break;
1380 case KC_D:
1381 win->dx += 20;
1382 break;
1383 case KC_Q:
1384 win->angle += (PI / 2);
1385 break;
1386 case KC_E:
1387 win->angle += -(PI / 2);
1388 break;
1389 case KC_R:
1390 win->fx *= 0.95;
1391 win->fy *= 0.95;
1392 break;
1393 case KC_F:
1394 win->fx *= 1.05;
1395 win->fy *= 1.05;
1396 break;
1397 default:
1398 break;
1399 }
1400
1401 /* Transform the window and calculate damage. */
1402 sysarg_t x, y, width, height;
1403 surface_get_resolution(win->surface, &width, &height);
1404 sysarg_t x1, y1, width1, height1;
1405 sysarg_t x2, y2, width2, height2;
1406 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1407 &x1, &y1, &width1, &height1);
1408 comp_recalc_transform(win);
1409 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1410 &x2, &y2, &width2, &height2);
1411 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1412 &x, &y, &width, &height);
1413 fibril_mutex_unlock(&window_list_mtx);
1414
1415 comp_damage(x, y, width, height);
1416 } else {
1417 fibril_mutex_unlock(&window_list_mtx);
1418 }
1419 } else if (win_resize) {
1420 fibril_mutex_lock(&window_list_mtx);
1421 window_t *win = (window_t *) list_first(&window_list);
1422 if (win && win->surface) {
1423 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1424 if (event == NULL) {
1425 fibril_mutex_unlock(&window_list_mtx);
1426 return ENOMEM;
1427 }
1428
1429 sysarg_t width, height;
1430 surface_get_resolution(win->surface, &width, &height);
1431
1432 link_initialize(&event->link);
1433 event->type = ET_WINDOW_RESIZE;
1434
1435 switch (key) {
1436 case KC_T:
1437 event->data.rsz.width = width;
1438 event->data.rsz.height = (height >= 20) ? height - 20 : 0;
1439 break;
1440 case KC_G:
1441 event->data.rsz.width = width;
1442 event->data.rsz.height = height + 20;
1443 break;
1444 case KC_B:
1445 event->data.rsz.width = (width >= 20) ? width - 20 : 0;;
1446 event->data.rsz.height = height;
1447 break;
1448 case KC_N:
1449 event->data.rsz.width = width + 20;
1450 event->data.rsz.height = height;
1451 break;
1452 default:
1453 event->data.rsz.width = 0;
1454 event->data.rsz.height = 0;
1455 break;
1456 }
1457
1458 fibril_mutex_unlock(&window_list_mtx);
1459 comp_post_event(event);
1460 } else {
1461 fibril_mutex_unlock(&window_list_mtx);
1462 }
1463 } else if (win_opacity) {
1464 fibril_mutex_lock(&window_list_mtx);
1465 window_t *win = (window_t *) list_first(&window_list);
1466 if (win && win->surface) {
1467 switch (key) {
1468 case KC_C:
1469 if (win->opacity > 0) {
1470 win->opacity -= 5;
1471 }
1472 break;
1473 case KC_V:
1474 if (win->opacity < 255) {
1475 win->opacity += 5;
1476 }
1477 break;
1478 default:
1479 break;
1480 }
1481
1482 /* Calculate damage. */
1483 sysarg_t x, y, width, height;
1484 surface_get_resolution(win->surface, &width, &height);
1485 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1486 &x, &y, &width, &height);
1487 fibril_mutex_unlock(&window_list_mtx);
1488
1489 comp_damage(x, y, width, height);
1490 } else {
1491 fibril_mutex_unlock(&window_list_mtx);
1492 }
1493 } else if (win_close) {
1494 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1495 if (event == NULL)
1496 return ENOMEM;
1497
1498 link_initialize(&event->link);
1499 event->type = ET_WINDOW_CLOSE;
1500
1501 comp_post_event(event);
1502 } else if (win_switch) {
1503 fibril_mutex_lock(&window_list_mtx);
1504 if (!list_empty(&window_list)) {
1505 window_t *win1 = (window_t *) list_first(&window_list);
1506 list_remove(&win1->link);
1507 list_append(&win1->link, &window_list);
1508 window_t *win2 = (window_t *) list_first(&window_list);
1509
1510 sysarg_t x1 = 0;
1511 sysarg_t y1 = 0;
1512 sysarg_t width1 = 0;
1513 sysarg_t height1 = 0;
1514 if (win1->surface) {
1515 sysarg_t width, height;
1516 surface_get_resolution(win1->surface, &width, &height);
1517 comp_coord_bounding_rect(0, 0, width, height, win1->transform,
1518 &x1, &y1, &width1, &height1);
1519 }
1520
1521 sysarg_t x2 = 0;
1522 sysarg_t y2 = 0;
1523 sysarg_t width2 = 0;
1524 sysarg_t height2 = 0;
1525 if (win2->surface) {
1526 sysarg_t width, height;
1527 surface_get_resolution(win2->surface, &width, &height);
1528 comp_coord_bounding_rect(0, 0, width, height, win2->transform,
1529 &x2, &y2, &width2, &height2);
1530 }
1531
1532 sysarg_t x, y, width, height;
1533 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1534 &x, &y, &width, &height);
1535
1536 fibril_mutex_unlock(&window_list_mtx);
1537 comp_damage(x, y, width, height);
1538 } else {
1539 fibril_mutex_unlock(&window_list_mtx);
1540 }
1541 } else if (viewport_move) {
1542 fibril_mutex_lock(&viewport_list_mtx);
1543 viewport_t *vp = (viewport_t *) list_first(&viewport_list);
1544 if (vp) {
1545 switch (key) {
1546 case KC_I:
1547 vp->pos.x += 0;
1548 vp->pos.y += -20;
1549 break;
1550 case KC_K:
1551 vp->pos.x += 0;
1552 vp->pos.y += 20;
1553 break;
1554 case KC_J:
1555 vp->pos.x += -20;
1556 vp->pos.y += 0;
1557 break;
1558 case KC_L:
1559 vp->pos.x += 20;
1560 vp->pos.y += 0;
1561 break;
1562 default:
1563 vp->pos.x += 0;
1564 vp->pos.y += 0;
1565 break;
1566 }
1567
1568 sysarg_t x = vp->pos.x;
1569 sysarg_t y = vp->pos.y;
1570 sysarg_t width, height;
1571 surface_get_resolution(vp->surface, &width, &height);
1572 fibril_mutex_unlock(&viewport_list_mtx);
1573
1574 comp_damage(x, y, width, height);
1575 } else {
1576 fibril_mutex_unlock(&viewport_list_mtx);
1577 }
1578 } else if (viewport_change) {
1579 fibril_mutex_lock(&viewport_list_mtx);
1580
1581 viewport_t *vp;
1582 switch (key) {
1583 case KC_O:
1584 vp = (viewport_t *) list_first(&viewport_list);
1585 if (vp) {
1586 list_remove(&vp->link);
1587 list_append(&vp->link, &viewport_list);
1588 }
1589 break;
1590 case KC_P:
1591 vp = (viewport_t *) list_last(&viewport_list);
1592 if (vp) {
1593 list_remove(&vp->link);
1594 list_prepend(&vp->link, &viewport_list);
1595 }
1596 break;
1597 default:
1598 break;
1599 }
1600
1601 fibril_mutex_unlock(&viewport_list_mtx);
1602 } else if (kconsole_switch) {
1603 __SYSCALL0(SYS_DEBUG_ACTIVATE_CONSOLE);
1604 } else if (compositor_test) {
1605 fibril_mutex_lock(&window_list_mtx);
1606
1607 window_t *red_win = window_create();
1608 red_win->surface = surface_create(250, 150, NULL, 0);
1609 pixel_t red_pix = PIXEL(255, 240, 0, 0);
1610 for (sysarg_t y = 0; y < 150; ++y) {
1611 for (sysarg_t x = 0; x < 250; ++x) {
1612 surface_put_pixel(red_win->surface, x, y, red_pix);
1613 }
1614 }
1615 list_prepend(&red_win->link, &window_list);
1616
1617 window_t *blue_win = window_create();
1618 blue_win->surface = surface_create(200, 100, NULL, 0);
1619 pixel_t blue_pix = PIXEL(255, 0, 0, 240);
1620 for (sysarg_t y = 0; y < 100; ++y) {
1621 for (sysarg_t x = 0; x < 200; ++x) {
1622 surface_put_pixel(blue_win->surface, x, y, blue_pix);
1623 }
1624 }
1625 list_prepend(&blue_win->link, &window_list);
1626
1627 window_t *helenos_win = window_create();
1628 helenos_win->surface = decode_tga((void *) helenos_tga, helenos_tga_size, 0);
1629 list_prepend(&helenos_win->link, &window_list);
1630
1631 window_t *nameic_win = window_create();
1632 nameic_win->surface = decode_tga((void *) nameic_tga, nameic_tga_size, 0);
1633 list_prepend(&nameic_win->link, &window_list);
1634
1635 fibril_mutex_unlock(&window_list_mtx);
1636 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1637 } else {
1638 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1639 if (event == NULL)
1640 return ENOMEM;
1641
1642 link_initialize(&event->link);
1643 event->type = ET_KEYBOARD_EVENT;
1644 event->data.kbd.type = type;
1645 event->data.kbd.key = key;
1646 event->data.kbd.mods = mods;
1647 event->data.kbd.c = c;
1648
1649 comp_post_event(event);
1650 }
1651
1652 return EOK;
1653}
1654
1655static int input_connect(const char *svc)
1656{
1657 async_sess_t *sess;
1658 service_id_t dsid;
1659
1660 int rc = loc_service_get_id(svc, &dsid, 0);
1661 if (rc != EOK) {
1662 printf("%s: Input service %s not found\n", NAME, svc);
1663 return rc;
1664 }
1665
1666 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
1667 if (sess == NULL) {
1668 printf("%s: Unable to connect to input service %s\n", NAME,
1669 svc);
1670 return EIO;
1671 }
1672
1673 fibril_mutex_lock(&pointer_list_mtx);
1674 pointer_t *pointer = pointer_create();
1675 if (pointer != NULL) {
1676 pointer->id = pointer_id++;
1677 list_append(&pointer->link, &pointer_list);
1678 }
1679 fibril_mutex_unlock(&pointer_list_mtx);
1680
1681 if (pointer == NULL) {
1682 printf("%s: Cannot create pointer.\n", NAME);
1683 async_hangup(sess);
1684 return ENOMEM;
1685 }
1686
1687 rc = input_open(sess, &input_ev_ops, pointer, &input);
1688 if (rc != EOK) {
1689 async_hangup(sess);
1690 printf("%s: Unable to communicate with service %s (%s)\n",
1691 NAME, svc, str_error(rc));
1692 return rc;
1693 }
1694
1695 return EOK;
1696}
1697
1698static void input_disconnect(void)
1699{
1700 pointer_t *pointer = input->user;
1701 input_close(input);
1702 pointer_destroy(pointer);
1703}
1704
1705static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
1706{
1707 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1708}
1709
1710static int compositor_srv_init(char *input_svc, char *name)
1711{
1712 /* Coordinates of the central pixel. */
1713 coord_origin = UINT32_MAX / 2;
1714
1715 /* Color of the viewport background. Must be opaque. */
1716 bg_color = PIXEL(255, 75, 70, 75);
1717
1718 /* Register compositor server. */
1719 async_set_client_connection(client_connection);
1720 int rc = loc_server_register(NAME);
1721 if (rc != EOK) {
1722 printf("%s: Unable to register server (%s)\n", NAME, str_error(rc));
1723 return -1;
1724 }
1725
1726 /* Register interrupt handler to switch back from kconsole. */
1727 async_set_interrupt_received(interrupt_received);
1728 rc = event_subscribe(EVENT_KCONSOLE, 0);
1729 if (rc != EOK) {
1730 printf("%s: Failed to register kconsole notifications (%s)\n",
1731 NAME, str_error(rc));
1732 }
1733
1734 server_name = name;
1735
1736 char svc[LOC_NAME_MAXLEN + 1];
1737 snprintf(svc, LOC_NAME_MAXLEN, "%s/%s", NAMESPACE, server_name);
1738
1739 service_id_t service_id;
1740 rc = loc_service_register(svc, &service_id);
1741 if (rc != EOK) {
1742 printf("%s: Unable to register service %s\n", NAME, svc);
1743 return rc;
1744 }
1745
1746 /* Prepare window registrator (entrypoint for clients). */
1747 char winreg[LOC_NAME_MAXLEN + 1];
1748 snprintf(winreg, LOC_NAME_MAXLEN, "%s%s/winreg", NAMESPACE, server_name);
1749 if (loc_service_register(winreg, &winreg_id) != EOK) {
1750 printf("%s: Unable to register service %s\n", NAME, winreg);
1751 return -1;
1752 }
1753
1754 /* Establish input bidirectional connection. */
1755 rc = input_connect(input_svc);
1756 if (rc != EOK)
1757 return rc;
1758
1759 /* Create viewports and connect them to visualizers. */
1760 category_id_t cat_id;
1761 rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
1762 if (rc != EOK) {
1763 input_disconnect();
1764 return -1;
1765 }
1766
1767 service_id_t *svcs;
1768 size_t svcs_cnt = 0;
1769 rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
1770 if (rc != EOK || svcs_cnt == 0) {
1771 input_disconnect();
1772 return -1;
1773 }
1774
1775 for (size_t i = 0; i < svcs_cnt; ++i) {
1776 char *svc_name;
1777 rc = loc_service_get_name(svcs[i], &svc_name);
1778 if (rc == EOK) {
1779 viewport_t *vp = viewport_create(svc_name);
1780 if (vp != NULL) {
1781 list_append(&vp->link, &viewport_list);
1782 }
1783 }
1784 }
1785
1786 if (list_empty(&viewport_list)) {
1787 input_disconnect();
1788 return -1;
1789 }
1790
1791 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1792
1793 return EOK;
1794}
1795
1796static void usage(char *name)
1797{
1798 printf("Usage: %s <input_dev> <server_name>\n", name);
1799}
1800
1801int main(int argc, char *argv[])
1802{
1803 if (argc < 3) {
1804 usage(argv[0]);
1805 return 1;
1806 }
1807
1808 printf("%s: HelenOS Compositor server\n", NAME);
1809
1810 int rc = compositor_srv_init(argv[1], argv[2]);
1811 if (rc != EOK)
1812 return rc;
1813
1814 printf("%s: Accepting connections\n", NAME);
1815 task_retval(0);
1816 async_manager();
1817
1818 /* Never reached */
1819 return 0;
1820}
1821
1822/** @}
1823 */
Note: See TracBrowser for help on using the repository browser.