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

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5823aef3 was 5823aef3, checked in by jxsvoboda <5887334+jxsvoboda@…>, 4 years ago

Fix delivery of release events to popup window

  • Property mode set to 100644
File size: 11.3 KB
Line 
1/*
2 * Copyright (c) 2021 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
108/** Set seat popup window.
109 *
110 * @param seat Seat
111 * @param wnd Popup window
112 */
113void ds_seat_set_popup(ds_seat_t *seat, ds_window_t *wnd)
114{
115 seat->popup = wnd;
116}
117
118/** Evacuate seat references to window.
119 *
120 * If seat's focus is @a wnd, it will be set to a different window.
121 * If seat's popup window is @a wnd, it will be set to NULL.
122 *
123 * @param seat Seat
124 * @param wnd Window to evacuate focus from
125 */
126void ds_seat_evac_wnd_refs(ds_seat_t *seat, ds_window_t *wnd)
127{
128 ds_window_t *nwnd;
129
130 if (seat->focus == wnd) {
131 nwnd = ds_display_prev_window(wnd);
132 if (nwnd == NULL)
133 nwnd = ds_display_last_window(wnd->display);
134 if (nwnd == wnd)
135 nwnd = NULL;
136
137 ds_seat_set_focus(seat, nwnd);
138 }
139
140 if (seat->popup == wnd)
141 seat->popup = NULL;
142}
143
144/** Switch focus to another window.
145 *
146 * @param seat Seat
147 * @param wnd Window to evacuate focus from
148 */
149void ds_seat_switch_focus(ds_seat_t *seat)
150{
151 ds_window_t *nwnd;
152
153 if (seat->focus != NULL)
154 nwnd = ds_display_prev_window(seat->focus);
155 else
156 nwnd = NULL;
157
158 if (nwnd == NULL)
159 nwnd = ds_display_last_window(seat->display);
160
161 if (nwnd != NULL)
162 ds_seat_set_focus(seat, nwnd);
163}
164
165/** Post keyboard event to the seat's focused window.
166 *
167 * @param seat Seat
168 * @param event Event
169 *
170 * @return EOK on success or an error code
171 */
172errno_t ds_seat_post_kbd_event(ds_seat_t *seat, kbd_event_t *event)
173{
174 ds_window_t *dwindow;
175 bool alt_or_shift;
176
177 alt_or_shift = event->mods & (KM_SHIFT | KM_ALT);
178 if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_TAB) {
179 /* On Alt-Tab or Shift-Tab, switch focus to next window */
180 ds_seat_switch_focus(seat);
181 return EOK;
182 }
183
184 dwindow = seat->popup;
185 if (dwindow == NULL)
186 dwindow = seat->focus;
187
188 if (dwindow == NULL)
189 return EOK;
190
191 return ds_window_post_kbd_event(dwindow, event);
192}
193
194/** Get current cursor used by seat.
195 *
196 * @param wmcurs WM curor
197 * @param ccurs Client cursor
198 * @return
199 */
200static ds_cursor_t *ds_seat_compute_cursor(ds_cursor_t *wmcurs, ds_cursor_t *ccurs)
201{
202 if (wmcurs != NULL)
203 return wmcurs;
204
205 return ccurs;
206}
207
208/** Get current cursor used by seat.
209 *
210 * @param seat Seat
211 * @return Current cursor
212 */
213static ds_cursor_t *ds_seat_get_cursor(ds_seat_t *seat)
214{
215 return ds_seat_compute_cursor(seat->wm_cursor, seat->client_cursor);
216}
217
218/** Set client cursor.
219 *
220 * Set cursor selected by client. This may update the actual cursor
221 * if WM is not overriding the cursor.
222 *
223 * @param seat Seat
224 * @param cursor Client cursor
225 */
226static void ds_seat_set_client_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
227{
228 ds_cursor_t *old_cursor;
229 ds_cursor_t *new_cursor;
230 gfx_rect_t old_rect;
231
232 old_cursor = ds_seat_get_cursor(seat);
233 new_cursor = ds_seat_compute_cursor(seat->wm_cursor, cursor);
234
235 if (new_cursor != old_cursor) {
236 ds_seat_get_pointer_rect(seat, &old_rect);
237 seat->client_cursor = cursor;
238 ds_seat_repaint_pointer(seat, &old_rect);
239 } else {
240 seat->client_cursor = cursor;
241 }
242}
243
244/** Set WM cursor.
245 *
246 * Set cursor override for window management.
247 *
248 * @param seat Seat
249 * @param cursor WM cursor override or @c NULL not to override the cursor
250 */
251void ds_seat_set_wm_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
252{
253 ds_cursor_t *old_cursor;
254 ds_cursor_t *new_cursor;
255 gfx_rect_t old_rect;
256
257 old_cursor = ds_seat_get_cursor(seat);
258 new_cursor = ds_seat_compute_cursor(cursor, seat->client_cursor);
259
260 if (new_cursor != old_cursor) {
261 ds_seat_get_pointer_rect(seat, &old_rect);
262 seat->wm_cursor = cursor;
263 ds_seat_repaint_pointer(seat, &old_rect);
264 } else {
265 seat->wm_cursor = cursor;
266 }
267}
268
269/** Get rectangle covered by pointer.
270 *
271 * @param seat Seat
272 * @param rect Place to store rectangle
273 */
274void ds_seat_get_pointer_rect(ds_seat_t *seat, gfx_rect_t *rect)
275{
276 ds_cursor_t *cursor;
277
278 cursor = ds_seat_get_cursor(seat);
279 ds_cursor_get_rect(cursor, &seat->pntpos, rect);
280}
281
282/** Repaint seat pointer
283 *
284 * Repaint the pointer after it has moved or changed. This is done by
285 * repainting the area of the display previously (@a old_rect) and currently
286 * covered by the pointer.
287 *
288 * @param seat Seat
289 * @param old_rect Rectangle previously covered by pointer
290 *
291 * @return EOK on success or an error code
292 */
293static errno_t ds_seat_repaint_pointer(ds_seat_t *seat, gfx_rect_t *old_rect)
294{
295 gfx_rect_t new_rect;
296 gfx_rect_t envelope;
297 errno_t rc;
298
299 ds_seat_get_pointer_rect(seat, &new_rect);
300
301 if (gfx_rect_is_incident(old_rect, &new_rect)) {
302 /* Rectangles do not intersect. Repaint them separately. */
303 rc = ds_display_paint(seat->display, &new_rect);
304 if (rc != EOK)
305 return rc;
306
307 rc = ds_display_paint(seat->display, old_rect);
308 if (rc != EOK)
309 return rc;
310 } else {
311 /*
312 * Rectangles intersect. As an optimization, repaint them
313 * in a single operation.
314 */
315 gfx_rect_envelope(old_rect, &new_rect, &envelope);
316
317 rc = ds_display_paint(seat->display, &envelope);
318 if (rc != EOK)
319 return rc;
320 }
321
322 return EOK;
323}
324
325/** Post pointing device event to the seat
326 *
327 * Update pointer position and generate position event.
328 *
329 * @param seat Seat
330 * @param event Event
331 *
332 * @return EOK on success or an error code
333 */
334errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
335{
336 ds_display_t *disp = seat->display;
337 gfx_coord2_t npos;
338 gfx_rect_t old_rect;
339 ds_window_t *wnd;
340 pos_event_t pevent;
341 errno_t rc;
342
343 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
344
345 /* Focus window on button press */
346 if (event->type == PTD_PRESS && event->btn_num == 1) {
347 if (wnd != NULL && (wnd->flags & wndf_popup) == 0) {
348 ds_seat_set_focus(seat, wnd);
349 }
350 }
351
352 if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
353 pevent.pos_id = 0;
354 pevent.type = (event->type == PTD_PRESS) ?
355 POS_PRESS : POS_RELEASE;
356 pevent.btn_num = event->btn_num;
357 pevent.hpos = seat->pntpos.x;
358 pevent.vpos = seat->pntpos.y;
359
360 rc = ds_seat_post_pos_event(seat, &pevent);
361 if (rc != EOK)
362 return rc;
363 }
364
365 if (event->type == PTD_MOVE) {
366 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
367 gfx_coord2_clip(&npos, &disp->rect, &npos);
368
369 ds_seat_get_pointer_rect(seat, &old_rect);
370 seat->pntpos = npos;
371
372 pevent.pos_id = 0;
373 pevent.type = POS_UPDATE;
374 pevent.btn_num = 0;
375 pevent.hpos = seat->pntpos.x;
376 pevent.vpos = seat->pntpos.y;
377
378 rc = ds_seat_post_pos_event(seat, &pevent);
379 if (rc != EOK)
380 return rc;
381
382 ds_seat_repaint_pointer(seat, &old_rect);
383 }
384
385 if (event->type == PTD_ABS_MOVE) {
386 /*
387 * Project input device area onto display area. Technically
388 * we probably want to project onto the area of a particular
389 * display device. The tricky part is figuring out which
390 * display device the input device is associated with.
391 */
392 gfx_coord2_project(&event->apos, &event->abounds,
393 &disp->rect, &npos);
394
395 gfx_coord2_clip(&npos, &disp->rect, &npos);
396
397 ds_seat_get_pointer_rect(seat, &old_rect);
398 seat->pntpos = npos;
399
400 pevent.pos_id = 0;
401 pevent.type = POS_UPDATE;
402 pevent.btn_num = 0;
403 pevent.hpos = seat->pntpos.x;
404 pevent.vpos = seat->pntpos.y;
405
406 rc = ds_seat_post_pos_event(seat, &pevent);
407 if (rc != EOK)
408 return rc;
409
410 ds_seat_repaint_pointer(seat, &old_rect);
411 }
412
413 return EOK;
414}
415
416/** Post position event to seat.
417 *
418 * Deliver event to relevant windows.
419 *
420 * @param seat Seat
421 * @param event Position event
422 */
423errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
424{
425 ds_window_t *wnd;
426 errno_t rc;
427
428 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
429 /*
430 * Deliver event to popup window, unless the pointer is over
431 * it (in which case it will be delivered to that window
432 * below, anyway.
433 */
434 if (seat->popup != NULL) {
435 rc = ds_window_post_pos_event(seat->popup, event);
436 if (rc != EOK)
437 return rc;
438 }
439
440 if (seat->focus != wnd && seat->focus != NULL) {
441 rc = ds_window_post_pos_event(seat->focus, event);
442 if (rc != EOK)
443 return rc;
444
445 /* Only deliver release events to the focused window */
446 if (event->type == POS_RELEASE)
447 return EOK;
448 }
449
450 if (wnd != NULL) {
451 /* Moving over a window */
452 ds_seat_set_client_cursor(seat, wnd->cursor);
453
454 /*
455 * Only deliver event if we didn't already deliver it
456 * to the same window above.
457 */
458 if (wnd != seat->popup) {
459 rc = ds_window_post_pos_event(wnd, event);
460 if (rc != EOK)
461 return rc;
462 }
463 } else {
464 /* Not over a window */
465 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
466 }
467
468 return EOK;
469}
470
471/** Paint seat pointer.
472 *
473 * @param seat Seat whose pointer to paint
474 * @param rect Clipping rectangle
475 */
476errno_t ds_seat_paint_pointer(ds_seat_t *seat, gfx_rect_t *rect)
477{
478 ds_cursor_t *cursor;
479
480 cursor = ds_seat_get_cursor(seat);
481 return ds_cursor_paint(cursor, &seat->pntpos, rect);
482}
483
484/** @}
485 */
Note: See TracBrowser for help on using the repository browser.