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

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

Properly clip cursor when repainting a part of the display

Not doing so was causing excessively large update rectangles, increasing
CPU usage and display artifacts.

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