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

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

Distinguish between focus switching and evacuation

It's the same thing if there are at least two windows. With just
one window, switching focus should do nothing. Evacuation should
lead to no window being focused. (Fixes crash when pressing
Alt-Shift/Ctrl-Shift with just one window left).

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