source: mainline/uspace/srv/hid/display/seat.c@ a6492460

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a6492460 was 60ebe63, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Store positioning device ID in position events

  • Property mode set to 100644
File size: 11.9 KB
Line 
1/*
2 * Copyright (c) 2022 Jiri Svoboda
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 display
30 * @{
31 */
32/**
33 * @file Display server seat
34 */
35
36#include <adt/list.h>
37#include <errno.h>
38#include <gfx/color.h>
39#include <gfx/render.h>
40#include <stdlib.h>
41#include "client.h"
42#include "cursor.h"
43#include "display.h"
44#include "seat.h"
45#include "window.h"
46
47static void ds_seat_get_pointer_rect(ds_seat_t *, gfx_rect_t *);
48static errno_t ds_seat_repaint_pointer(ds_seat_t *, gfx_rect_t *);
49
50/** Create seat.
51 *
52 * @param display Parent display
53 * @param rseat Place to store pointer to new seat.
54 * @return EOK on success, ENOMEM if out of memory
55 */
56errno_t ds_seat_create(ds_display_t *display, ds_seat_t **rseat)
57{
58 ds_seat_t *seat;
59
60 seat = calloc(1, sizeof(ds_seat_t));
61 if (seat == NULL)
62 return ENOMEM;
63
64 ds_display_add_seat(display, seat);
65 seat->pntpos.x = 0;
66 seat->pntpos.y = 0;
67
68 seat->client_cursor = display->cursor[dcurs_arrow];
69 seat->wm_cursor = NULL;
70
71 *rseat = seat;
72 return EOK;
73}
74
75/** Destroy seat.
76 *
77 * @param seat Seat
78 */
79void ds_seat_destroy(ds_seat_t *seat)
80{
81 ds_display_remove_seat(seat);
82 free(seat);
83}
84
85/** Set seat focus to a window.
86 *
87 * @param seat Seat
88 * @param wnd Window to focus
89 */
90void ds_seat_set_focus(ds_seat_t *seat, ds_window_t *wnd)
91{
92 if (wnd == seat->focus) {
93 /* Focus is not changing */
94 return;
95 }
96
97 if (seat->focus != NULL)
98 ds_window_post_unfocus_event(seat->focus);
99
100 seat->focus = wnd;
101
102 if (wnd != NULL) {
103 ds_window_post_focus_event(wnd);
104 ds_window_bring_to_top(wnd);
105 }
106
107 /* When focus changes, popup window should be closed */
108 ds_seat_set_popup(seat, NULL);
109}
110
111/** Set seat popup window.
112 *
113 * @param seat Seat
114 * @param wnd Popup window
115 */
116void ds_seat_set_popup(ds_seat_t *seat, ds_window_t *wnd)
117{
118 if (wnd == seat->popup)
119 return;
120
121 if (seat->popup != NULL) {
122 /* Window is no longer the popup window, send close request */
123 ds_client_post_close_event(seat->popup->client,
124 seat->popup);
125 }
126
127 seat->popup = wnd;
128}
129
130/** Evacuate seat references to window.
131 *
132 * If seat's focus is @a wnd, it will be set to a different window.
133 * If seat's popup window is @a wnd, it will be set to NULL.
134 *
135 * @param seat Seat
136 * @param wnd Window to evacuate focus from
137 */
138void ds_seat_evac_wnd_refs(ds_seat_t *seat, ds_window_t *wnd)
139{
140 ds_window_t *nwnd;
141
142 if (seat->focus == wnd) {
143 nwnd = ds_display_prev_window(wnd);
144 if (nwnd == NULL)
145 nwnd = ds_display_last_window(wnd->display);
146 if (nwnd == wnd)
147 nwnd = NULL;
148
149 ds_seat_set_focus(seat, nwnd);
150 }
151
152 if (seat->popup == wnd)
153 ds_seat_set_popup(seat, NULL);
154}
155
156/** Switch focus to another window.
157 *
158 * @param seat Seat
159 * @param wnd Window to evacuate focus from
160 */
161void ds_seat_switch_focus(ds_seat_t *seat)
162{
163 ds_window_t *nwnd;
164
165 if (seat->focus != NULL)
166 nwnd = ds_display_prev_window(seat->focus);
167 else
168 nwnd = NULL;
169
170 if (nwnd == NULL)
171 nwnd = ds_display_last_window(seat->display);
172
173 if (nwnd != NULL)
174 ds_seat_set_focus(seat, nwnd);
175}
176
177/** Post keyboard event to the seat's focused window.
178 *
179 * @param seat Seat
180 * @param event Event
181 *
182 * @return EOK on success or an error code
183 */
184errno_t ds_seat_post_kbd_event(ds_seat_t *seat, kbd_event_t *event)
185{
186 ds_window_t *dwindow;
187 bool alt_or_shift;
188
189 alt_or_shift = event->mods & (KM_SHIFT | KM_ALT);
190 if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_TAB) {
191 /* On Alt-Tab or Shift-Tab, switch focus to next window */
192 ds_seat_switch_focus(seat);
193 return EOK;
194 }
195
196 dwindow = seat->popup;
197 if (dwindow == NULL)
198 dwindow = seat->focus;
199
200 if (dwindow == NULL)
201 return EOK;
202
203 return ds_window_post_kbd_event(dwindow, event);
204}
205
206/** Get current cursor used by seat.
207 *
208 * @param wmcurs WM curor
209 * @param ccurs Client cursor
210 * @return
211 */
212static ds_cursor_t *ds_seat_compute_cursor(ds_cursor_t *wmcurs, ds_cursor_t *ccurs)
213{
214 if (wmcurs != NULL)
215 return wmcurs;
216
217 return ccurs;
218}
219
220/** Get current cursor used by seat.
221 *
222 * @param seat Seat
223 * @return Current cursor
224 */
225static ds_cursor_t *ds_seat_get_cursor(ds_seat_t *seat)
226{
227 return ds_seat_compute_cursor(seat->wm_cursor, seat->client_cursor);
228}
229
230/** Set client cursor.
231 *
232 * Set cursor selected by client. This may update the actual cursor
233 * if WM is not overriding the cursor.
234 *
235 * @param seat Seat
236 * @param cursor Client cursor
237 */
238static void ds_seat_set_client_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
239{
240 ds_cursor_t *old_cursor;
241 ds_cursor_t *new_cursor;
242 gfx_rect_t old_rect;
243
244 old_cursor = ds_seat_get_cursor(seat);
245 new_cursor = ds_seat_compute_cursor(seat->wm_cursor, cursor);
246
247 if (new_cursor != old_cursor) {
248 ds_seat_get_pointer_rect(seat, &old_rect);
249 seat->client_cursor = cursor;
250 ds_seat_repaint_pointer(seat, &old_rect);
251 } else {
252 seat->client_cursor = cursor;
253 }
254}
255
256/** Set WM cursor.
257 *
258 * Set cursor override for window management.
259 *
260 * @param seat Seat
261 * @param cursor WM cursor override or @c NULL not to override the cursor
262 */
263void ds_seat_set_wm_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
264{
265 ds_cursor_t *old_cursor;
266 ds_cursor_t *new_cursor;
267 gfx_rect_t old_rect;
268
269 old_cursor = ds_seat_get_cursor(seat);
270 new_cursor = ds_seat_compute_cursor(cursor, seat->client_cursor);
271
272 if (new_cursor != old_cursor) {
273 ds_seat_get_pointer_rect(seat, &old_rect);
274 seat->wm_cursor = cursor;
275 ds_seat_repaint_pointer(seat, &old_rect);
276 } else {
277 seat->wm_cursor = cursor;
278 }
279}
280
281/** Get rectangle covered by pointer.
282 *
283 * @param seat Seat
284 * @param rect Place to store rectangle
285 */
286void ds_seat_get_pointer_rect(ds_seat_t *seat, gfx_rect_t *rect)
287{
288 ds_cursor_t *cursor;
289
290 cursor = ds_seat_get_cursor(seat);
291 ds_cursor_get_rect(cursor, &seat->pntpos, rect);
292}
293
294/** Repaint seat pointer
295 *
296 * Repaint the pointer after it has moved or changed. This is done by
297 * repainting the area of the display previously (@a old_rect) and currently
298 * covered by the pointer.
299 *
300 * @param seat Seat
301 * @param old_rect Rectangle previously covered by pointer
302 *
303 * @return EOK on success or an error code
304 */
305static errno_t ds_seat_repaint_pointer(ds_seat_t *seat, gfx_rect_t *old_rect)
306{
307 gfx_rect_t new_rect;
308 gfx_rect_t envelope;
309 errno_t rc;
310
311 ds_seat_get_pointer_rect(seat, &new_rect);
312
313 if (gfx_rect_is_incident(old_rect, &new_rect)) {
314 /* Rectangles do not intersect. Repaint them separately. */
315 rc = ds_display_paint(seat->display, &new_rect);
316 if (rc != EOK)
317 return rc;
318
319 rc = ds_display_paint(seat->display, old_rect);
320 if (rc != EOK)
321 return rc;
322 } else {
323 /*
324 * Rectangles intersect. As an optimization, repaint them
325 * in a single operation.
326 */
327 gfx_rect_envelope(old_rect, &new_rect, &envelope);
328
329 rc = ds_display_paint(seat->display, &envelope);
330 if (rc != EOK)
331 return rc;
332 }
333
334 return EOK;
335}
336
337/** Post pointing device event to the seat
338 *
339 * Update pointer position and generate position event.
340 *
341 * @param seat Seat
342 * @param event Event
343 *
344 * @return EOK on success or an error code
345 */
346errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
347{
348 ds_display_t *disp = seat->display;
349 gfx_coord2_t npos;
350 gfx_rect_t old_rect;
351 ds_window_t *wnd;
352 pos_event_t pevent;
353 errno_t rc;
354
355 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
356
357 /* Focus window on button press */
358 if (event->type == PTD_PRESS && event->btn_num == 1) {
359 if (wnd != NULL && (wnd->flags & wndf_popup) == 0) {
360 ds_seat_set_focus(seat, wnd);
361 }
362 }
363
364 if (event->type == PTD_PRESS || event->type == PTD_RELEASE ||
365 event->type == PTD_DCLICK) {
366 pevent.pos_id = event->pos_id;
367 switch (event->type) {
368 case PTD_PRESS:
369 pevent.type = POS_PRESS;
370 break;
371 case PTD_RELEASE:
372 pevent.type = POS_RELEASE;
373 break;
374 case PTD_DCLICK:
375 pevent.type = POS_DCLICK;
376 break;
377 default:
378 assert(false);
379 }
380
381 pevent.btn_num = event->btn_num;
382 pevent.hpos = seat->pntpos.x;
383 pevent.vpos = seat->pntpos.y;
384
385 rc = ds_seat_post_pos_event(seat, &pevent);
386 if (rc != EOK)
387 return rc;
388 }
389
390 if (event->type == PTD_MOVE) {
391 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
392 gfx_coord2_clip(&npos, &disp->rect, &npos);
393
394 ds_seat_get_pointer_rect(seat, &old_rect);
395 seat->pntpos = npos;
396
397 pevent.pos_id = event->pos_id;
398 pevent.type = POS_UPDATE;
399 pevent.btn_num = 0;
400 pevent.hpos = seat->pntpos.x;
401 pevent.vpos = seat->pntpos.y;
402
403 rc = ds_seat_post_pos_event(seat, &pevent);
404 if (rc != EOK)
405 return rc;
406
407 ds_seat_repaint_pointer(seat, &old_rect);
408 }
409
410 if (event->type == PTD_ABS_MOVE) {
411 /*
412 * Project input device area onto display area. Technically
413 * we probably want to project onto the area of a particular
414 * display device. The tricky part is figuring out which
415 * display device the input device is associated with.
416 */
417 gfx_coord2_project(&event->apos, &event->abounds,
418 &disp->rect, &npos);
419
420 gfx_coord2_clip(&npos, &disp->rect, &npos);
421
422 ds_seat_get_pointer_rect(seat, &old_rect);
423 seat->pntpos = npos;
424
425 pevent.pos_id = event->pos_id;
426 pevent.type = POS_UPDATE;
427 pevent.btn_num = 0;
428 pevent.hpos = seat->pntpos.x;
429 pevent.vpos = seat->pntpos.y;
430
431 rc = ds_seat_post_pos_event(seat, &pevent);
432 if (rc != EOK)
433 return rc;
434
435 ds_seat_repaint_pointer(seat, &old_rect);
436 }
437
438 return EOK;
439}
440
441/** Post position event to seat.
442 *
443 * Deliver event to relevant windows.
444 *
445 * @param seat Seat
446 * @param event Position event
447 */
448errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
449{
450 ds_window_t *wnd;
451 errno_t rc;
452
453 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
454
455 /* Click outside popup window */
456 if (event->type == POS_PRESS && wnd != seat->popup) {
457 /* Close popup window */
458 ds_seat_set_popup(seat, NULL);
459 }
460
461 /* Deliver event to popup window. */
462 if (seat->popup != NULL) {
463 rc = ds_window_post_pos_event(seat->popup, event);
464 if (rc != EOK)
465 return rc;
466 }
467
468 if (seat->focus != wnd && seat->focus != NULL) {
469 rc = ds_window_post_pos_event(seat->focus, event);
470 if (rc != EOK)
471 return rc;
472
473 /* Only deliver release events to the focused window */
474 if (event->type == POS_RELEASE)
475 return EOK;
476 }
477
478 if (wnd != NULL) {
479 /* Moving over a window */
480 ds_seat_set_client_cursor(seat, wnd->cursor);
481
482 /*
483 * Only deliver event if we didn't already deliver it
484 * to the same window above.
485 */
486 if (wnd != seat->popup) {
487 rc = ds_window_post_pos_event(wnd, event);
488 if (rc != EOK)
489 return rc;
490 }
491 } else {
492 /* Not over a window */
493 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
494 }
495
496 return EOK;
497}
498
499/** Paint seat pointer.
500 *
501 * @param seat Seat whose pointer to paint
502 * @param rect Clipping rectangle
503 */
504errno_t ds_seat_paint_pointer(ds_seat_t *seat, gfx_rect_t *rect)
505{
506 ds_cursor_t *cursor;
507
508 cursor = ds_seat_get_cursor(seat);
509 return ds_cursor_paint(cursor, &seat->pntpos, rect);
510}
511
512/** @}
513 */
Note: See TracBrowser for help on using the repository browser.