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

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

Deliver close event to popup window when appropriate

That is, when focus changes or when user clicks outside of the
popup window.

  • Property mode set to 100644
File size: 11.6 KB
RevLine 
[cf32dbd]1/*
[9e84d2c]2 * Copyright (c) 2021 Jiri Svoboda
[cf32dbd]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>
[4fbdc3d]38#include <gfx/color.h>
39#include <gfx/render.h>
[cf32dbd]40#include <stdlib.h>
41#include "client.h"
[4d8002d]42#include "cursor.h"
[cf32dbd]43#include "display.h"
44#include "seat.h"
45#include "window.h"
46
[978c9bc5]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 *);
[9901f267]49
[cf32dbd]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);
[c2250702]65 seat->pntpos.x = 0;
66 seat->pntpos.y = 0;
[cf32dbd]67
[9901f267]68 seat->client_cursor = display->cursor[dcurs_arrow];
69 seat->wm_cursor = NULL;
[4d8002d]70
[cf32dbd]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
[4fbdc3d]85/** Set seat focus to a window.
86 *
87 * @param seat Seat
88 * @param wnd Window to focus
89 */
[cf32dbd]90void ds_seat_set_focus(ds_seat_t *seat, ds_window_t *wnd)
91{
[554a5f1]92 if (wnd == seat->focus) {
93 /* Focus is not changing */
94 return;
95 }
96
[b0a94854]97 if (seat->focus != NULL)
98 ds_window_post_unfocus_event(seat->focus);
99
[cf32dbd]100 seat->focus = wnd;
[b0a94854]101
[ef30659]102 if (wnd != NULL) {
[b0a94854]103 ds_window_post_focus_event(wnd);
[ef30659]104 ds_window_bring_to_top(wnd);
105 }
[d7f82635]106
107 /* When focus changes, popup window should be closed */
108 ds_seat_set_popup(seat, NULL);
[cf32dbd]109}
110
[9e84d2c]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{
[d7f82635]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
[9e84d2c]127 seat->popup = wnd;
128}
129
130/** Evacuate seat references to window.
[4fbdc3d]131 *
132 * If seat's focus is @a wnd, it will be set to a different window.
[9e84d2c]133 * If seat's popup window is @a wnd, it will be set to NULL.
[4fbdc3d]134 *
135 * @param seat Seat
136 * @param wnd Window to evacuate focus from
137 */
[9e84d2c]138void ds_seat_evac_wnd_refs(ds_seat_t *seat, ds_window_t *wnd)
[cf32dbd]139{
140 ds_window_t *nwnd;
141
142 if (seat->focus == wnd) {
[08f345f]143 nwnd = ds_display_prev_window(wnd);
[cf32dbd]144 if (nwnd == NULL)
[08f345f]145 nwnd = ds_display_last_window(wnd->display);
[cf32dbd]146 if (nwnd == wnd)
147 nwnd = NULL;
148
149 ds_seat_set_focus(seat, nwnd);
150 }
[9e84d2c]151
152 if (seat->popup == wnd)
[d7f82635]153 ds_seat_set_popup(seat, NULL);
[cf32dbd]154}
155
[0da03df]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
[cf32dbd]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{
[79949f3]186 ds_window_t *dwindow;
187 bool alt_or_shift;
[cf32dbd]188
[79949f3]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 */
[0da03df]192 ds_seat_switch_focus(seat);
[79949f3]193 return EOK;
194 }
195
[9e84d2c]196 dwindow = seat->popup;
197 if (dwindow == NULL)
198 dwindow = seat->focus;
199
[cf32dbd]200 if (dwindow == NULL)
201 return EOK;
202
[338d0935]203 return ds_window_post_kbd_event(dwindow, event);
[cf32dbd]204}
205
[9901f267]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;
[978c9bc5]242 gfx_rect_t old_rect;
[9901f267]243
244 old_cursor = ds_seat_get_cursor(seat);
245 new_cursor = ds_seat_compute_cursor(seat->wm_cursor, cursor);
246
[978c9bc5]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 }
[9901f267]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;
[978c9bc5]267 gfx_rect_t old_rect;
[9901f267]268
269 old_cursor = ds_seat_get_cursor(seat);
270 new_cursor = ds_seat_compute_cursor(cursor, seat->client_cursor);
271
[978c9bc5]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 }
[9901f267]279}
280
[978c9bc5]281/** Get rectangle covered by pointer.
[4fbdc3d]282 *
283 * @param seat Seat
[978c9bc5]284 * @param rect Place to store rectangle
[4fbdc3d]285 */
[978c9bc5]286void ds_seat_get_pointer_rect(ds_seat_t *seat, gfx_rect_t *rect)
[4fbdc3d]287{
[9901f267]288 ds_cursor_t *cursor;
289
290 cursor = ds_seat_get_cursor(seat);
[978c9bc5]291 ds_cursor_get_rect(cursor, &seat->pntpos, rect);
[4fbdc3d]292}
293
[978c9bc5]294/** Repaint seat pointer
295 *
296 * Repaint the pointer after it has moved or changed. This is done by
[d70e7b7b]297 * repainting the area of the display previously (@a old_rect) and currently
[978c9bc5]298 * covered by the pointer.
[28db46b]299 *
300 * @param seat Seat
[978c9bc5]301 * @param old_rect Rectangle previously covered by pointer
[28db46b]302 *
303 * @return EOK on success or an error code
304 */
[978c9bc5]305static errno_t ds_seat_repaint_pointer(ds_seat_t *seat, gfx_rect_t *old_rect)
[28db46b]306{
[978c9bc5]307 gfx_rect_t new_rect;
308 gfx_rect_t envelope;
309 errno_t rc;
[9901f267]310
[978c9bc5]311 ds_seat_get_pointer_rect(seat, &new_rect);
[28db46b]312
[6301a24f]313 if (gfx_rect_is_incident(old_rect, &new_rect)) {
[978c9bc5]314 /* Rectangles do not intersect. Repaint them separately. */
315 rc = ds_display_paint(seat->display, &new_rect);
316 if (rc != EOK)
317 return rc;
[28db46b]318
[978c9bc5]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;
[28db46b]335}
336
[4fbdc3d]337/** Post pointing device event to the seat
[a40ae0d]338 *
339 * Update pointer position and generate position event.
[4fbdc3d]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{
[e1f2079]348 ds_display_t *disp = seat->display;
[4fbdc3d]349 gfx_coord2_t npos;
[978c9bc5]350 gfx_rect_t old_rect;
[a40ae0d]351 ds_window_t *wnd;
352 pos_event_t pevent;
353 errno_t rc;
[4fbdc3d]354
[a40ae0d]355 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
356
[4fbdc3d]357 /* Focus window on button press */
[a40ae0d]358 if (event->type == PTD_PRESS && event->btn_num == 1) {
[9e84d2c]359 if (wnd != NULL && (wnd->flags & wndf_popup) == 0) {
[4fbdc3d]360 ds_seat_set_focus(seat, wnd);
361 }
362 }
363
[a40ae0d]364 if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
365 pevent.pos_id = 0;
366 pevent.type = (event->type == PTD_PRESS) ?
367 POS_PRESS : POS_RELEASE;
368 pevent.btn_num = event->btn_num;
369 pevent.hpos = seat->pntpos.x;
370 pevent.vpos = seat->pntpos.y;
371
372 rc = ds_seat_post_pos_event(seat, &pevent);
373 if (rc != EOK)
374 return rc;
375 }
376
[4fbdc3d]377 if (event->type == PTD_MOVE) {
378 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
[e1f2079]379 gfx_coord2_clip(&npos, &disp->rect, &npos);
[4fbdc3d]380
[978c9bc5]381 ds_seat_get_pointer_rect(seat, &old_rect);
[4fbdc3d]382 seat->pntpos = npos;
[a40ae0d]383
384 pevent.pos_id = 0;
385 pevent.type = POS_UPDATE;
386 pevent.btn_num = 0;
387 pevent.hpos = seat->pntpos.x;
388 pevent.vpos = seat->pntpos.y;
389
390 rc = ds_seat_post_pos_event(seat, &pevent);
391 if (rc != EOK)
392 return rc;
393
[978c9bc5]394 ds_seat_repaint_pointer(seat, &old_rect);
[4fbdc3d]395 }
396
[1388f7f0]397 if (event->type == PTD_ABS_MOVE) {
398 /*
399 * Project input device area onto display area. Technically
400 * we probably want to project onto the area of a particular
401 * display device. The tricky part is figuring out which
402 * display device the input device is associated with.
403 */
404 gfx_coord2_project(&event->apos, &event->abounds,
405 &disp->rect, &npos);
406
407 gfx_coord2_clip(&npos, &disp->rect, &npos);
408
[978c9bc5]409 ds_seat_get_pointer_rect(seat, &old_rect);
[1388f7f0]410 seat->pntpos = npos;
411
412 pevent.pos_id = 0;
413 pevent.type = POS_UPDATE;
414 pevent.btn_num = 0;
415 pevent.hpos = seat->pntpos.x;
416 pevent.vpos = seat->pntpos.y;
417
418 rc = ds_seat_post_pos_event(seat, &pevent);
419 if (rc != EOK)
420 return rc;
421
[978c9bc5]422 ds_seat_repaint_pointer(seat, &old_rect);
[1388f7f0]423 }
424
[4fbdc3d]425 return EOK;
426}
427
[a40ae0d]428/** Post position event to seat.
429 *
430 * Deliver event to relevant windows.
431 *
432 * @param seat Seat
433 * @param event Position event
434 */
435errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
436{
437 ds_window_t *wnd;
438 errno_t rc;
439
440 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
[d7f82635]441
442 /* Click outside popup window */
443 if (event->type == POS_PRESS && wnd != seat->popup) {
444 /* Close popup window */
445 ds_seat_set_popup(seat, NULL);
446 }
447
448 /* Deliver event to popup window. */
[5823aef3]449 if (seat->popup != NULL) {
[9e84d2c]450 rc = ds_window_post_pos_event(seat->popup, event);
451 if (rc != EOK)
452 return rc;
453 }
454
[0da03df]455 if (seat->focus != wnd && seat->focus != NULL) {
[bc492d5]456 rc = ds_window_post_pos_event(seat->focus, event);
457 if (rc != EOK)
458 return rc;
459
460 /* Only deliver release events to the focused window */
461 if (event->type == POS_RELEASE)
462 return EOK;
463 }
464
[a40ae0d]465 if (wnd != NULL) {
[9242ad9]466 /* Moving over a window */
[9901f267]467 ds_seat_set_client_cursor(seat, wnd->cursor);
[9242ad9]468
[5823aef3]469 /*
470 * Only deliver event if we didn't already deliver it
471 * to the same window above.
472 */
473 if (wnd != seat->popup) {
474 rc = ds_window_post_pos_event(wnd, event);
475 if (rc != EOK)
476 return rc;
477 }
[9242ad9]478 } else {
479 /* Not over a window */
[9901f267]480 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
[a40ae0d]481 }
482
483 return EOK;
484}
485
[978c9bc5]486/** Paint seat pointer.
487 *
488 * @param seat Seat whose pointer to paint
489 * @param rect Clipping rectangle
490 */
491errno_t ds_seat_paint_pointer(ds_seat_t *seat, gfx_rect_t *rect)
492{
493 ds_cursor_t *cursor;
494
495 cursor = ds_seat_get_cursor(seat);
[62018a0]496 return ds_cursor_paint(cursor, &seat->pntpos, rect);
[978c9bc5]497}
498
[cf32dbd]499/** @}
500 */
Note: See TracBrowser for help on using the repository browser.