source: mainline/uspace/srv/hid/display/seat.c@ 978c9bc5

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

Pointer needs to be drawn as part of ds_display_paint

Everything needs to be painted from ds_display_paint. The pointers were
painted in an immediate fashion when they changed. Any window update
would wipe them.

  • 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 are 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 isect;
254 gfx_rect_t envelope;
255 errno_t rc;
256
257 ds_seat_get_pointer_rect(seat, &new_rect);
258
259 gfx_rect_clip(old_rect, &new_rect, &isect);
260 if (gfx_rect_is_empty(&isect)) {
261 /* Rectangles do not intersect. Repaint them separately. */
262 rc = ds_display_paint(seat->display, &new_rect);
263 if (rc != EOK)
264 return rc;
265
266 rc = ds_display_paint(seat->display, old_rect);
267 if (rc != EOK)
268 return rc;
269 } else {
270 /*
271 * Rectangles intersect. As an optimization, repaint them
272 * in a single operation.
273 */
274 gfx_rect_envelope(old_rect, &new_rect, &envelope);
275
276 rc = ds_display_paint(seat->display, &envelope);
277 if (rc != EOK)
278 return rc;
279 }
280
281 return EOK;
282}
283
284/** Post pointing device event to the seat
285 *
286 * Update pointer position and generate position event.
287 *
288 * @param seat Seat
289 * @param event Event
290 *
291 * @return EOK on success or an error code
292 */
293errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
294{
295 ds_display_t *disp = seat->display;
296 gfx_coord2_t npos;
297 gfx_rect_t old_rect;
298 ds_window_t *wnd;
299 pos_event_t pevent;
300 errno_t rc;
301
302 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
303
304 /* Focus window on button press */
305 if (event->type == PTD_PRESS && event->btn_num == 1) {
306 if (wnd != NULL) {
307 ds_seat_set_focus(seat, wnd);
308 }
309 }
310
311 if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
312 pevent.pos_id = 0;
313 pevent.type = (event->type == PTD_PRESS) ?
314 POS_PRESS : POS_RELEASE;
315 pevent.btn_num = event->btn_num;
316 pevent.hpos = seat->pntpos.x;
317 pevent.vpos = seat->pntpos.y;
318
319 rc = ds_seat_post_pos_event(seat, &pevent);
320 if (rc != EOK)
321 return rc;
322 }
323
324 if (event->type == PTD_MOVE) {
325 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
326 gfx_coord2_clip(&npos, &disp->rect, &npos);
327
328 ds_seat_get_pointer_rect(seat, &old_rect);
329 seat->pntpos = npos;
330
331 pevent.pos_id = 0;
332 pevent.type = POS_UPDATE;
333 pevent.btn_num = 0;
334 pevent.hpos = seat->pntpos.x;
335 pevent.vpos = seat->pntpos.y;
336
337 rc = ds_seat_post_pos_event(seat, &pevent);
338 if (rc != EOK)
339 return rc;
340
341 ds_seat_repaint_pointer(seat, &old_rect);
342 }
343
344 if (event->type == PTD_ABS_MOVE) {
345 /*
346 * Project input device area onto display area. Technically
347 * we probably want to project onto the area of a particular
348 * display device. The tricky part is figuring out which
349 * display device the input device is associated with.
350 */
351 gfx_coord2_project(&event->apos, &event->abounds,
352 &disp->rect, &npos);
353
354 gfx_coord2_clip(&npos, &disp->rect, &npos);
355
356 ds_seat_get_pointer_rect(seat, &old_rect);
357 seat->pntpos = npos;
358
359 pevent.pos_id = 0;
360 pevent.type = POS_UPDATE;
361 pevent.btn_num = 0;
362 pevent.hpos = seat->pntpos.x;
363 pevent.vpos = seat->pntpos.y;
364
365 rc = ds_seat_post_pos_event(seat, &pevent);
366 if (rc != EOK)
367 return rc;
368
369 ds_seat_repaint_pointer(seat, &old_rect);
370 }
371
372 return EOK;
373}
374
375/** Post position event to seat.
376 *
377 * Deliver event to relevant windows.
378 *
379 * @param seat Seat
380 * @param event Position event
381 */
382errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
383{
384 ds_window_t *wnd;
385 errno_t rc;
386
387 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
388 if (wnd != NULL) {
389 /* Moving over a window */
390 ds_seat_set_client_cursor(seat, wnd->cursor);
391
392 rc = ds_window_post_pos_event(wnd, event);
393 if (rc != EOK)
394 return rc;
395 } else {
396 /* Not over a window */
397 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
398 }
399
400 if (seat->focus != wnd) {
401 rc = ds_window_post_pos_event(seat->focus, event);
402 if (rc != EOK)
403 return rc;
404 }
405
406 return EOK;
407}
408
409/** Paint seat pointer.
410 *
411 * @param seat Seat whose pointer to paint
412 * @param rect Clipping rectangle
413 */
414errno_t ds_seat_paint_pointer(ds_seat_t *seat, gfx_rect_t *rect)
415{
416 ds_cursor_t *cursor;
417
418 cursor = ds_seat_get_cursor(seat);
419 (void) rect; // XXX ds_cursor_paint should accept a clipping rectangle
420 return ds_cursor_paint(cursor, &seat->pntpos);
421}
422
423/** @}
424 */
Note: See TracBrowser for help on using the repository browser.