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
RevLine 
[cf32dbd]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>
[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{
[b0a94854]92 if (seat->focus != NULL)
93 ds_window_post_unfocus_event(seat->focus);
94
[cf32dbd]95 seat->focus = wnd;
[b0a94854]96
[ef30659]97 if (wnd != NULL) {
[b0a94854]98 ds_window_post_focus_event(wnd);
[ef30659]99 ds_window_bring_to_top(wnd);
100 }
[cf32dbd]101}
102
[4fbdc3d]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 */
[cf32dbd]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) {
[fd777a2]115 nwnd = ds_display_next_window(wnd);
[cf32dbd]116 if (nwnd == NULL)
[fd777a2]117 nwnd = ds_display_first_window(wnd->display);
[cf32dbd]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{
[79949f3]134 ds_window_t *dwindow;
135 bool alt_or_shift;
[cf32dbd]136
[79949f3]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;
[cf32dbd]145 if (dwindow == NULL)
146 return EOK;
147
[338d0935]148 return ds_window_post_kbd_event(dwindow, event);
[cf32dbd]149}
150
[9901f267]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;
[978c9bc5]187 gfx_rect_t old_rect;
[9901f267]188
189 old_cursor = ds_seat_get_cursor(seat);
190 new_cursor = ds_seat_compute_cursor(seat->wm_cursor, cursor);
191
[978c9bc5]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 }
[9901f267]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;
[978c9bc5]212 gfx_rect_t old_rect;
[9901f267]213
214 old_cursor = ds_seat_get_cursor(seat);
215 new_cursor = ds_seat_compute_cursor(cursor, seat->client_cursor);
216
[978c9bc5]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 }
[9901f267]224}
225
[978c9bc5]226/** Get rectangle covered by pointer.
[4fbdc3d]227 *
228 * @param seat Seat
[978c9bc5]229 * @param rect Place to store rectangle
[4fbdc3d]230 */
[978c9bc5]231void ds_seat_get_pointer_rect(ds_seat_t *seat, gfx_rect_t *rect)
[4fbdc3d]232{
[9901f267]233 ds_cursor_t *cursor;
234
235 cursor = ds_seat_get_cursor(seat);
[978c9bc5]236 ds_cursor_get_rect(cursor, &seat->pntpos, rect);
[4fbdc3d]237}
238
[978c9bc5]239/** Repaint seat pointer
240 *
241 * Repaint the pointer after it has moved or changed. This is done by
[d70e7b7b]242 * repainting the area of the display previously (@a old_rect) and currently
[978c9bc5]243 * covered by the pointer.
[28db46b]244 *
245 * @param seat Seat
[978c9bc5]246 * @param old_rect Rectangle previously covered by pointer
[28db46b]247 *
248 * @return EOK on success or an error code
249 */
[978c9bc5]250static errno_t ds_seat_repaint_pointer(ds_seat_t *seat, gfx_rect_t *old_rect)
[28db46b]251{
[978c9bc5]252 gfx_rect_t new_rect;
253 gfx_rect_t envelope;
254 errno_t rc;
[9901f267]255
[978c9bc5]256 ds_seat_get_pointer_rect(seat, &new_rect);
[28db46b]257
[6301a24f]258 if (gfx_rect_is_incident(old_rect, &new_rect)) {
[978c9bc5]259 /* Rectangles do not intersect. Repaint them separately. */
260 rc = ds_display_paint(seat->display, &new_rect);
261 if (rc != EOK)
262 return rc;
[28db46b]263
[978c9bc5]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;
[28db46b]280}
281
[4fbdc3d]282/** Post pointing device event to the seat
[a40ae0d]283 *
284 * Update pointer position and generate position event.
[4fbdc3d]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{
[e1f2079]293 ds_display_t *disp = seat->display;
[4fbdc3d]294 gfx_coord2_t npos;
[978c9bc5]295 gfx_rect_t old_rect;
[a40ae0d]296 ds_window_t *wnd;
297 pos_event_t pevent;
298 errno_t rc;
[4fbdc3d]299
[a40ae0d]300 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
301
[4fbdc3d]302 /* Focus window on button press */
[a40ae0d]303 if (event->type == PTD_PRESS && event->btn_num == 1) {
[4fbdc3d]304 if (wnd != NULL) {
305 ds_seat_set_focus(seat, wnd);
306 }
307 }
308
[a40ae0d]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
[4fbdc3d]322 if (event->type == PTD_MOVE) {
323 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
[e1f2079]324 gfx_coord2_clip(&npos, &disp->rect, &npos);
[4fbdc3d]325
[978c9bc5]326 ds_seat_get_pointer_rect(seat, &old_rect);
[4fbdc3d]327 seat->pntpos = npos;
[a40ae0d]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
[978c9bc5]339 ds_seat_repaint_pointer(seat, &old_rect);
[4fbdc3d]340 }
341
[1388f7f0]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
[978c9bc5]354 ds_seat_get_pointer_rect(seat, &old_rect);
[1388f7f0]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
[978c9bc5]367 ds_seat_repaint_pointer(seat, &old_rect);
[1388f7f0]368 }
369
[4fbdc3d]370 return EOK;
371}
372
[a40ae0d]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);
[bc492d5]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
[a40ae0d]397 if (wnd != NULL) {
[9242ad9]398 /* Moving over a window */
[9901f267]399 ds_seat_set_client_cursor(seat, wnd->cursor);
[9242ad9]400
[a40ae0d]401 rc = ds_window_post_pos_event(wnd, event);
402 if (rc != EOK)
403 return rc;
[9242ad9]404 } else {
405 /* Not over a window */
[9901f267]406 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
[a40ae0d]407 }
408
409 return EOK;
410}
411
[978c9bc5]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);
[62018a0]422 return ds_cursor_paint(cursor, &seat->pntpos, rect);
[978c9bc5]423}
424
[cf32dbd]425/** @}
426 */
Note: See TracBrowser for help on using the repository browser.