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

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

Implement absolute move events in compositor in a simple way.

  • Property mode set to 100644
File size: 47.6 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 Use absolute coordinates directly */
1078
1079 pointer_t *pointer = input_pointer(input);
1080
1081 sysarg_t width, height;
1082
1083 fibril_mutex_lock(&viewport_list_mtx);
1084 if (list_empty(&viewport_list)) {
1085 printf("No viewport found\n");
1086 fibril_mutex_unlock(&viewport_list_mtx);
1087 return EOK; /* XXX */
1088 }
1089 link_t *link = list_first(&viewport_list);
1090 viewport_t *vp = list_get_instance(link, viewport_t, link);
1091 surface_get_resolution(vp->surface, &width, &height);
1092 desktop_point_t vp_pos = vp->pos;
1093 fibril_mutex_unlock(&viewport_list_mtx);
1094
1095 desktop_point_t pos_in_viewport;
1096 pos_in_viewport.x = x * width / max_x;
1097 pos_in_viewport.y = y * height / max_y;
1098
1099 /* Calculate offset from pointer */
1100 fibril_mutex_lock(&pointer_list_mtx);
1101 desktop_vector_t delta;
1102 delta.x = (vp_pos.x + pos_in_viewport.x) - pointer->pos.x;
1103 delta.y = (vp_pos.y + pos_in_viewport.y) - pointer->pos.y;
1104 fibril_mutex_unlock(&pointer_list_mtx);
1105
1106 return comp_mouse_move(input, delta.x, delta.y);
1107}
1108
1109static int comp_mouse_move(input_t *input, int dx, int dy)
1110{
1111 pointer_t *pointer = input_pointer(input);
1112
1113 /* Update pointer position. */
1114 fibril_mutex_lock(&pointer_list_mtx);
1115 desktop_point_t old_pos = pointer->pos;
1116 sysarg_t cursor_width;
1117 sysarg_t cursor_height;
1118 surface_get_resolution(pointer->cursor.states[pointer->state],
1119 &cursor_width, &cursor_height);
1120 pointer->pos.x += dx;
1121 pointer->pos.y += dy;
1122 fibril_mutex_unlock(&pointer_list_mtx);
1123 comp_damage(old_pos.x, old_pos.y, cursor_width, cursor_height);
1124 comp_damage(old_pos.x + dx, old_pos.y + dy, cursor_width, cursor_height);
1125
1126 fibril_mutex_lock(&window_list_mtx);
1127 window_t *top = (window_t *) list_first(&window_list);
1128 if (top && top->surface) {
1129
1130 if (pointer->grab_flags == GF_EMPTY) {
1131 /* Notify top-level window about move event. */
1132 bool within_client = false;
1133 sysarg_t point_x, point_y;
1134 sysarg_t width, height;
1135 surface_get_resolution(top->surface, &width, &height);
1136 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1137 top->transform, width, height, &point_x, &point_y);
1138 fibril_mutex_unlock(&window_list_mtx);
1139
1140 if (within_client) {
1141 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1142 if (event) {
1143 link_initialize(&event->link);
1144 event->type = ET_POSITION_EVENT;
1145 event->data.pos.pos_id = pointer->id;
1146 event->data.pos.type = POS_UPDATE;
1147 event->data.pos.btn_num = pointer->btn_num;
1148 event->data.pos.hpos = point_x;
1149 event->data.pos.vpos = point_y;
1150 comp_post_event(event);
1151 }
1152 }
1153 } else {
1154 /* Pointer is grabbed by top-level window action. */
1155 pointer->accum.x += dx;
1156 pointer->accum.y += dy;
1157#if ANIMATE_WINDOW_TRANSFORMS == 1
1158 sysarg_t x, y, width, height;
1159 comp_window_animate(pointer, top, &x, &y, &width, &height);
1160#endif
1161 fibril_mutex_unlock(&window_list_mtx);
1162#if ANIMATE_WINDOW_TRANSFORMS == 1
1163 comp_damage(x, y, width, height);
1164#endif
1165 }
1166 } else {
1167 fibril_mutex_unlock(&window_list_mtx);
1168 }
1169
1170 return EOK;
1171}
1172
1173static int comp_mouse_button(input_t *input, int bnum, int bpress)
1174{
1175 pointer_t *pointer = input_pointer(input);
1176
1177 if (bpress) {
1178 pointer->btn_pos = pointer->pos;
1179 pointer->btn_num = bnum;
1180 pointer->pressed = true;
1181
1182 /* Check whether mouse press belongs to the top-level window. */
1183 fibril_mutex_lock(&window_list_mtx);
1184 window_t *win = (window_t *) list_first(&window_list);
1185 if (!win || !win->surface) {
1186 fibril_mutex_unlock(&window_list_mtx);
1187 return EOK;
1188 }
1189 sysarg_t x, y, width, height;
1190 surface_get_resolution(win->surface, &width, &height);
1191 bool within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1192 win->transform, width, height, &x, &y);
1193 fibril_mutex_unlock(&window_list_mtx);
1194
1195 /* Send mouse press to the top-level window. */
1196 if (within_client) {
1197 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1198 if (event) {
1199 link_initialize(&event->link);
1200 event->type = ET_POSITION_EVENT;
1201 event->data.pos.pos_id = pointer->id;
1202 event->data.pos.type = POS_PRESS;
1203 event->data.pos.btn_num = bnum;
1204 event->data.pos.hpos = x;
1205 event->data.pos.vpos = y;
1206 comp_post_event(event);
1207 } else {
1208 return ENOMEM;
1209 }
1210 }
1211 } else if (pointer->pressed && pointer->btn_num == (unsigned)bnum) {
1212 pointer->pressed = false;
1213
1214 fibril_mutex_lock(&window_list_mtx);
1215 window_t *win = NULL;
1216 sysarg_t point_x = 0;
1217 sysarg_t point_y = 0;
1218 sysarg_t width, height;
1219 bool within_client = false;
1220
1221 /* Determine the window which the mouse release belongs to. */
1222 list_foreach(window_list, link) {
1223 win = list_get_instance(link, window_t, link);
1224 if (win->surface) {
1225 surface_get_resolution(win->surface, &width, &height);
1226 within_client = comp_coord_to_client(pointer->pos.x, pointer->pos.y,
1227 win->transform, width, height, &point_x, &point_y);
1228 }
1229 if (within_client) {
1230 break;
1231 }
1232 }
1233
1234 /* Check whether the window is top-level window. */
1235 window_t *top = (window_t *) list_first(&window_list);
1236 if (!win || !top) {
1237 pointer->grab_flags = GF_EMPTY;
1238 fibril_mutex_unlock(&window_list_mtx);
1239 return EOK;
1240 }
1241
1242 window_event_t *event = NULL;
1243 sysarg_t dmg_x, dmg_y;
1244 sysarg_t dmg_width = 0;
1245 sysarg_t dmg_height = 0;
1246
1247 sysarg_t pre_x = 0;
1248 sysarg_t pre_y = 0;
1249 sysarg_t pre_width = 0;
1250 sysarg_t pre_height = 0;
1251
1252#if ANIMATE_WINDOW_TRANSFORMS == 0
1253 if (pointer->grab_flags != GF_EMPTY) {
1254 comp_window_animate(pointer, top, &pre_x, &pre_y, &pre_width, &pre_height);
1255 dmg_x = pre_x;
1256 dmg_y = pre_y;
1257 dmg_width = pre_width;
1258 dmg_height = pre_height;
1259 }
1260#endif
1261
1262 if ((pointer->grab_flags & GF_RESIZE_X) || (pointer->grab_flags & GF_RESIZE_Y)) {
1263
1264 surface_get_resolution(top->surface, &width, &height);
1265 top->fx *= (1.0 / scale_back_x);
1266 top->fy *= (1.0 / scale_back_y);
1267 comp_recalc_transform(top);
1268
1269 /* Commit proper resize action. */
1270 event = (window_event_t *) malloc(sizeof(window_event_t));
1271 if (event) {
1272 link_initialize(&event->link);
1273 event->type = ET_WINDOW_RESIZE;
1274
1275 int dx = (int) (((double) width) * (scale_back_x - 1.0));
1276 int dy = (int) (((double) height) * (scale_back_y - 1.0));
1277
1278 if (pointer->grab_flags & GF_RESIZE_X) {
1279 event->data.rsz.width =
1280 ((((int) width) + dx) >= 0) ? (width + dx) : 0;
1281 } else {
1282 event->data.rsz.width = width;
1283 }
1284
1285 if (pointer->grab_flags & GF_RESIZE_Y) {
1286 event->data.rsz.height =
1287 ((((int) height) + dy) >= 0) ? (height + dy) : 0;
1288 } else {
1289 event->data.rsz.height = height;
1290 }
1291 }
1292
1293 pointer->grab_flags = GF_EMPTY;
1294
1295 } else if (within_client && (pointer->grab_flags == GF_EMPTY) && (top == win)) {
1296
1297 /* Notify top-level window about mouse release. */
1298 event = (window_event_t *) malloc(sizeof(window_event_t));
1299 if (event) {
1300 link_initialize(&event->link);
1301 event->type = ET_POSITION_EVENT;
1302 event->data.pos.pos_id = pointer->id;
1303 event->data.pos.type = POS_RELEASE;
1304 event->data.pos.btn_num = bnum;
1305 event->data.pos.hpos = point_x;
1306 event->data.pos.vpos = point_y;
1307 }
1308 pointer->grab_flags = GF_EMPTY;
1309
1310 } else if (within_client && (pointer->grab_flags == GF_EMPTY) && (bnum == 1)) {
1311
1312 /* Bring the window to the foreground. */
1313 list_remove(&win->link);
1314 list_prepend(&win->link, &window_list);
1315 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1316 &dmg_x, &dmg_y, &dmg_width, &dmg_height);
1317
1318 } else {
1319 pointer->grab_flags = GF_EMPTY;
1320 }
1321
1322 fibril_mutex_unlock(&window_list_mtx);
1323
1324 if (dmg_width > 0 && dmg_height > 0) {
1325 comp_damage(dmg_x, dmg_y, dmg_width, dmg_height);
1326 }
1327
1328 if (event) {
1329 comp_post_event(event);
1330 }
1331 }
1332
1333 return EOK;
1334}
1335
1336static int comp_key_press(input_t *input, kbd_event_type_t type, keycode_t key,
1337 keymod_t mods, wchar_t c)
1338{
1339 bool win_transform = (mods & KM_ALT) && (
1340 key == KC_W || key == KC_S || key == KC_A || key == KC_D ||
1341 key == KC_Q || key == KC_E || key == KC_R || key == KC_F);
1342 bool win_resize = (mods & KM_ALT) && (
1343 key == KC_T || key == KC_G || key == KC_B || key == KC_N);
1344 bool win_opacity = (mods & KM_ALT) && (
1345 key == KC_C || key == KC_V);
1346 bool win_close = (mods & KM_ALT) && (key == KC_X);
1347 bool win_switch = (mods & KM_ALT) && (key == KC_TAB);
1348 bool viewport_move = (mods & KM_ALT) && (
1349 key == KC_I || key == KC_K || key == KC_J || key == KC_L);
1350 bool viewport_change = (mods & KM_ALT) && (
1351 key == KC_O || key == KC_P);
1352 bool kconsole_switch = (mods & KM_ALT) && (key == KC_M);
1353 bool compositor_test = (mods & KM_ALT) && (key == KC_H);
1354
1355 bool filter = (type == KEY_RELEASE) && (win_transform || win_resize ||
1356 win_opacity || win_close || win_switch || viewport_move ||
1357 viewport_change || kconsole_switch || compositor_test);
1358
1359 if (filter) {
1360 /* no-op */
1361 } else if (win_transform) {
1362 fibril_mutex_lock(&window_list_mtx);
1363 window_t *win = (window_t *) list_first(&window_list);
1364 if (win && win->surface) {
1365 switch (key) {
1366 case KC_W:
1367 win->dy += -20;
1368 break;
1369 case KC_S:
1370 win->dy += 20;
1371 break;
1372 case KC_A:
1373 win->dx += -20;
1374 break;
1375 case KC_D:
1376 win->dx += 20;
1377 break;
1378 case KC_Q:
1379 win->angle += (PI / 2);
1380 break;
1381 case KC_E:
1382 win->angle += -(PI / 2);
1383 break;
1384 case KC_R:
1385 win->fx *= 0.95;
1386 win->fy *= 0.95;
1387 break;
1388 case KC_F:
1389 win->fx *= 1.05;
1390 win->fy *= 1.05;
1391 break;
1392 default:
1393 break;
1394 }
1395
1396 /* Transform the window and calculate damage. */
1397 sysarg_t x, y, width, height;
1398 surface_get_resolution(win->surface, &width, &height);
1399 sysarg_t x1, y1, width1, height1;
1400 sysarg_t x2, y2, width2, height2;
1401 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1402 &x1, &y1, &width1, &height1);
1403 comp_recalc_transform(win);
1404 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1405 &x2, &y2, &width2, &height2);
1406 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1407 &x, &y, &width, &height);
1408 fibril_mutex_unlock(&window_list_mtx);
1409
1410 comp_damage(x, y, width, height);
1411 } else {
1412 fibril_mutex_unlock(&window_list_mtx);
1413 }
1414 } else if (win_resize) {
1415 fibril_mutex_lock(&window_list_mtx);
1416 window_t *win = (window_t *) list_first(&window_list);
1417 if (win && win->surface) {
1418 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1419 if (event == NULL) {
1420 fibril_mutex_unlock(&window_list_mtx);
1421 return ENOMEM;
1422 }
1423
1424 sysarg_t width, height;
1425 surface_get_resolution(win->surface, &width, &height);
1426
1427 link_initialize(&event->link);
1428 event->type = ET_WINDOW_RESIZE;
1429
1430 switch (key) {
1431 case KC_T:
1432 event->data.rsz.width = width;
1433 event->data.rsz.height = (height >= 20) ? height - 20 : 0;
1434 break;
1435 case KC_G:
1436 event->data.rsz.width = width;
1437 event->data.rsz.height = height + 20;
1438 break;
1439 case KC_B:
1440 event->data.rsz.width = (width >= 20) ? width - 20 : 0;;
1441 event->data.rsz.height = height;
1442 break;
1443 case KC_N:
1444 event->data.rsz.width = width + 20;
1445 event->data.rsz.height = height;
1446 break;
1447 default:
1448 event->data.rsz.width = 0;
1449 event->data.rsz.height = 0;
1450 break;
1451 }
1452
1453 fibril_mutex_unlock(&window_list_mtx);
1454 comp_post_event(event);
1455 } else {
1456 fibril_mutex_unlock(&window_list_mtx);
1457 }
1458 } else if (win_opacity) {
1459 fibril_mutex_lock(&window_list_mtx);
1460 window_t *win = (window_t *) list_first(&window_list);
1461 if (win && win->surface) {
1462 switch (key) {
1463 case KC_C:
1464 if (win->opacity > 0) {
1465 win->opacity -= 5;
1466 }
1467 break;
1468 case KC_V:
1469 if (win->opacity < 255) {
1470 win->opacity += 5;
1471 }
1472 break;
1473 default:
1474 break;
1475 }
1476
1477 /* Calculate damage. */
1478 sysarg_t x, y, width, height;
1479 surface_get_resolution(win->surface, &width, &height);
1480 comp_coord_bounding_rect(0, 0, width, height, win->transform,
1481 &x, &y, &width, &height);
1482 fibril_mutex_unlock(&window_list_mtx);
1483
1484 comp_damage(x, y, width, height);
1485 } else {
1486 fibril_mutex_unlock(&window_list_mtx);
1487 }
1488 } else if (win_close) {
1489 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1490 if (event == NULL)
1491 return ENOMEM;
1492
1493 link_initialize(&event->link);
1494 event->type = ET_WINDOW_CLOSE;
1495
1496 comp_post_event(event);
1497 } else if (win_switch) {
1498 fibril_mutex_lock(&window_list_mtx);
1499 if (!list_empty(&window_list)) {
1500 window_t *win1 = (window_t *) list_first(&window_list);
1501 list_remove(&win1->link);
1502 list_append(&win1->link, &window_list);
1503 window_t *win2 = (window_t *) list_first(&window_list);
1504
1505 sysarg_t x1 = 0;
1506 sysarg_t y1 = 0;
1507 sysarg_t width1 = 0;
1508 sysarg_t height1 = 0;
1509 if (win1->surface) {
1510 sysarg_t width, height;
1511 surface_get_resolution(win1->surface, &width, &height);
1512 comp_coord_bounding_rect(0, 0, width, height, win1->transform,
1513 &x1, &y1, &width1, &height1);
1514 }
1515
1516 sysarg_t x2 = 0;
1517 sysarg_t y2 = 0;
1518 sysarg_t width2 = 0;
1519 sysarg_t height2 = 0;
1520 if (win2->surface) {
1521 sysarg_t width, height;
1522 surface_get_resolution(win2->surface, &width, &height);
1523 comp_coord_bounding_rect(0, 0, width, height, win2->transform,
1524 &x2, &y2, &width2, &height2);
1525 }
1526
1527 sysarg_t x, y, width, height;
1528 rectangle_union(x1, y1, width1, height1, x2, y2, width2, height2,
1529 &x, &y, &width, &height);
1530
1531 fibril_mutex_unlock(&window_list_mtx);
1532 comp_damage(x, y, width, height);
1533 } else {
1534 fibril_mutex_unlock(&window_list_mtx);
1535 }
1536 } else if (viewport_move) {
1537 fibril_mutex_lock(&viewport_list_mtx);
1538 viewport_t *vp = (viewport_t *) list_first(&viewport_list);
1539 if (vp) {
1540 switch (key) {
1541 case KC_I:
1542 vp->pos.x += 0;
1543 vp->pos.y += -20;
1544 break;
1545 case KC_K:
1546 vp->pos.x += 0;
1547 vp->pos.y += 20;
1548 break;
1549 case KC_J:
1550 vp->pos.x += -20;
1551 vp->pos.y += 0;
1552 break;
1553 case KC_L:
1554 vp->pos.x += 20;
1555 vp->pos.y += 0;
1556 break;
1557 default:
1558 vp->pos.x += 0;
1559 vp->pos.y += 0;
1560 break;
1561 }
1562
1563 sysarg_t x = vp->pos.x;
1564 sysarg_t y = vp->pos.y;
1565 sysarg_t width, height;
1566 surface_get_resolution(vp->surface, &width, &height);
1567 fibril_mutex_unlock(&viewport_list_mtx);
1568
1569 comp_damage(x, y, width, height);
1570 } else {
1571 fibril_mutex_unlock(&viewport_list_mtx);
1572 }
1573 } else if (viewport_change) {
1574 fibril_mutex_lock(&viewport_list_mtx);
1575
1576 viewport_t *vp;
1577 switch (key) {
1578 case KC_O:
1579 vp = (viewport_t *) list_first(&viewport_list);
1580 if (vp) {
1581 list_remove(&vp->link);
1582 list_append(&vp->link, &viewport_list);
1583 }
1584 break;
1585 case KC_P:
1586 vp = (viewport_t *) list_last(&viewport_list);
1587 if (vp) {
1588 list_remove(&vp->link);
1589 list_prepend(&vp->link, &viewport_list);
1590 }
1591 break;
1592 default:
1593 break;
1594 }
1595
1596 fibril_mutex_unlock(&viewport_list_mtx);
1597 } else if (kconsole_switch) {
1598 __SYSCALL0(SYS_DEBUG_ACTIVATE_CONSOLE);
1599 } else if (compositor_test) {
1600 fibril_mutex_lock(&window_list_mtx);
1601
1602 window_t *red_win = window_create();
1603 red_win->surface = surface_create(250, 150, NULL, 0);
1604 pixel_t red_pix = PIXEL(255, 240, 0, 0);
1605 for (sysarg_t y = 0; y < 150; ++y) {
1606 for (sysarg_t x = 0; x < 250; ++x) {
1607 surface_put_pixel(red_win->surface, x, y, red_pix);
1608 }
1609 }
1610 list_prepend(&red_win->link, &window_list);
1611
1612 window_t *blue_win = window_create();
1613 blue_win->surface = surface_create(200, 100, NULL, 0);
1614 pixel_t blue_pix = PIXEL(255, 0, 0, 240);
1615 for (sysarg_t y = 0; y < 100; ++y) {
1616 for (sysarg_t x = 0; x < 200; ++x) {
1617 surface_put_pixel(blue_win->surface, x, y, blue_pix);
1618 }
1619 }
1620 list_prepend(&blue_win->link, &window_list);
1621
1622 window_t *helenos_win = window_create();
1623 helenos_win->surface = decode_tga((void *) helenos_tga, helenos_tga_size, 0);
1624 list_prepend(&helenos_win->link, &window_list);
1625
1626 window_t *nameic_win = window_create();
1627 nameic_win->surface = decode_tga((void *) nameic_tga, nameic_tga_size, 0);
1628 list_prepend(&nameic_win->link, &window_list);
1629
1630 fibril_mutex_unlock(&window_list_mtx);
1631 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1632 } else {
1633 window_event_t *event = (window_event_t *) malloc(sizeof(window_event_t));
1634 if (event == NULL)
1635 return ENOMEM;
1636
1637 link_initialize(&event->link);
1638 event->type = ET_KEYBOARD_EVENT;
1639 event->data.kbd.type = type;
1640 event->data.kbd.key = key;
1641 event->data.kbd.mods = mods;
1642 event->data.kbd.c = c;
1643
1644 comp_post_event(event);
1645 }
1646
1647 return EOK;
1648}
1649
1650static int input_connect(const char *svc)
1651{
1652 async_sess_t *sess;
1653 service_id_t dsid;
1654
1655 int rc = loc_service_get_id(svc, &dsid, 0);
1656 if (rc != EOK) {
1657 printf("%s: Input service %s not found\n", NAME, svc);
1658 return rc;
1659 }
1660
1661 sess = loc_service_connect(EXCHANGE_ATOMIC, dsid, 0);
1662 if (sess == NULL) {
1663 printf("%s: Unable to connect to input service %s\n", NAME,
1664 svc);
1665 return EIO;
1666 }
1667
1668 fibril_mutex_lock(&pointer_list_mtx);
1669 pointer_t *pointer = pointer_create();
1670 if (pointer != NULL) {
1671 pointer->id = pointer_id++;
1672 list_append(&pointer->link, &pointer_list);
1673 }
1674 fibril_mutex_unlock(&pointer_list_mtx);
1675
1676 if (pointer == NULL) {
1677 printf("%s: Cannot create pointer.\n", NAME);
1678 async_hangup(sess);
1679 return ENOMEM;
1680 }
1681
1682 rc = input_open(sess, &input_ev_ops, pointer, &input);
1683 if (rc != EOK) {
1684 async_hangup(sess);
1685 printf("%s: Unable to communicate with service %s (%s)\n",
1686 NAME, svc, str_error(rc));
1687 return rc;
1688 }
1689
1690 return EOK;
1691}
1692
1693static void input_disconnect(void)
1694{
1695 pointer_t *pointer = input->user;
1696 input_close(input);
1697 pointer_destroy(pointer);
1698}
1699
1700static void interrupt_received(ipc_callid_t callid, ipc_call_t *call)
1701{
1702 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1703}
1704
1705static int compositor_srv_init(char *input_svc, char *name)
1706{
1707 /* Coordinates of the central pixel. */
1708 coord_origin = UINT32_MAX / 2;
1709
1710 /* Color of the viewport background. Must be opaque. */
1711 bg_color = PIXEL(255, 75, 70, 75);
1712
1713 /* Register compositor server. */
1714 async_set_client_connection(client_connection);
1715 int rc = loc_server_register(NAME);
1716 if (rc != EOK) {
1717 printf("%s: Unable to register server (%s)\n", NAME, str_error(rc));
1718 return -1;
1719 }
1720
1721 /* Register interrupt handler to switch back from kconsole. */
1722 async_set_interrupt_received(interrupt_received);
1723 rc = event_subscribe(EVENT_KCONSOLE, 0);
1724 if (rc != EOK) {
1725 printf("%s: Failed to register kconsole notifications (%s)\n",
1726 NAME, str_error(rc));
1727 }
1728
1729 server_name = name;
1730
1731 char svc[LOC_NAME_MAXLEN + 1];
1732 snprintf(svc, LOC_NAME_MAXLEN, "%s/%s", NAMESPACE, server_name);
1733
1734 service_id_t service_id;
1735 rc = loc_service_register(svc, &service_id);
1736 if (rc != EOK) {
1737 printf("%s: Unable to register service %s\n", NAME, svc);
1738 return rc;
1739 }
1740
1741 /* Prepare window registrator (entrypoint for clients). */
1742 char winreg[LOC_NAME_MAXLEN + 1];
1743 snprintf(winreg, LOC_NAME_MAXLEN, "%s%s/winreg", NAMESPACE, server_name);
1744 if (loc_service_register(winreg, &winreg_id) != EOK) {
1745 printf("%s: Unable to register service %s\n", NAME, winreg);
1746 return -1;
1747 }
1748
1749 /* Establish input bidirectional connection. */
1750 rc = input_connect(input_svc);
1751 if (rc != EOK)
1752 return rc;
1753
1754 /* Create viewports and connect them to visualizers. */
1755 category_id_t cat_id;
1756 rc = loc_category_get_id("visualizer", &cat_id, IPC_FLAG_BLOCKING);
1757 if (rc != EOK) {
1758 input_disconnect();
1759 return -1;
1760 }
1761
1762 service_id_t *svcs;
1763 size_t svcs_cnt = 0;
1764 rc = loc_category_get_svcs(cat_id, &svcs, &svcs_cnt);
1765 if (rc != EOK || svcs_cnt == 0) {
1766 input_disconnect();
1767 return -1;
1768 }
1769
1770 for (size_t i = 0; i < svcs_cnt; ++i) {
1771 char *svc_name;
1772 rc = loc_service_get_name(svcs[i], &svc_name);
1773 if (rc == EOK) {
1774 viewport_t *vp = viewport_create(svc_name);
1775 if (vp != NULL) {
1776 list_append(&vp->link, &viewport_list);
1777 }
1778 }
1779 }
1780
1781 if (list_empty(&viewport_list)) {
1782 input_disconnect();
1783 return -1;
1784 }
1785
1786 comp_damage(0, 0, UINT32_MAX, UINT32_MAX);
1787
1788 return EOK;
1789}
1790
1791static void usage(char *name)
1792{
1793 printf("Usage: %s <input_dev> <server_name>\n", name);
1794}
1795
1796int main(int argc, char *argv[])
1797{
1798 if (argc < 3) {
1799 usage(argv[0]);
1800 return 1;
1801 }
1802
1803 printf("%s: HelenOS Compositor server\n", NAME);
1804
1805 int rc = compositor_srv_init(argv[1], argv[2]);
1806 if (rc != EOK)
1807 return rc;
1808
1809 printf("%s: Accepting connections\n", NAME);
1810 task_retval(0);
1811 async_manager();
1812
1813 /* Never reached */
1814 return 0;
1815}
1816
1817/** @}
1818 */
Note: See TracBrowser for help on using the repository browser.