source: mainline/uspace/srv/hid/compositor/compositor.c@ b5416c3

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b5416c3 was b5416c3, checked in by Martin Sucha <sucha14@…>, 13 years ago

Use point structure instead of two variables for position in some places.

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