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

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

Factor out input protocol client code.

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