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

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

Display configuration utility and server support

Currently we can only create, list and delete seats using the
'disp' utility (but no way to assign devices).

  • Property mode set to 100644
File size: 12.7 KB
Line 
1/*
2 * Copyright (c) 2023 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 <str.h>
42#include "client.h"
43#include "cursor.h"
44#include "display.h"
45#include "seat.h"
46#include "window.h"
47
48static void ds_seat_get_pointer_rect(ds_seat_t *, gfx_rect_t *);
49static errno_t ds_seat_repaint_pointer(ds_seat_t *, gfx_rect_t *);
50
51/** Create seat.
52 *
53 * @param display Parent display
54 * @param name Seat name
55 * @param rseat Place to store pointer to new seat.
56 * @return EOK on success, ENOMEM if out of memory
57 */
58errno_t ds_seat_create(ds_display_t *display, const char *name,
59 ds_seat_t **rseat)
60{
61 ds_seat_t *seat;
62 ds_seat_t *s0;
63
64 s0 = ds_display_first_seat(display);
65 while (s0 != NULL) {
66 if (str_cmp(s0->name, name) == 0)
67 return EEXIST;
68 s0 = ds_display_next_seat(s0);
69 }
70
71 seat = calloc(1, sizeof(ds_seat_t));
72 if (seat == NULL)
73 return ENOMEM;
74
75 seat->name = str_dup(name);
76 if (seat->name == NULL) {
77 free(seat);
78 return ENOMEM;
79 }
80
81 ds_display_add_seat(display, seat);
82 seat->pntpos.x = 0;
83 seat->pntpos.y = 0;
84
85 seat->client_cursor = display->cursor[dcurs_arrow];
86 seat->wm_cursor = NULL;
87
88 *rseat = seat;
89 return EOK;
90}
91
92/** Destroy seat.
93 *
94 * @param seat Seat
95 */
96void ds_seat_destroy(ds_seat_t *seat)
97{
98 ds_display_remove_seat(seat);
99 free(seat->name);
100 free(seat);
101}
102
103/** Set seat focus to a window.
104 *
105 * @param seat Seat
106 * @param wnd Window to focus
107 */
108void ds_seat_set_focus(ds_seat_t *seat, ds_window_t *wnd)
109{
110 errno_t rc;
111
112 if (wnd == seat->focus) {
113 /* Focus is not changing */
114 return;
115 }
116
117 if (wnd != NULL) {
118 rc = ds_window_unminimize(wnd);
119 if (rc != EOK)
120 return;
121 }
122
123 if (seat->focus != NULL)
124 ds_window_post_unfocus_event(seat->focus);
125
126 seat->focus = wnd;
127
128 if (wnd != NULL) {
129 ds_window_post_focus_event(wnd);
130 ds_window_bring_to_top(wnd);
131 }
132
133 /* When focus changes, popup window should be closed */
134 ds_seat_set_popup(seat, NULL);
135}
136
137/** Set seat popup window.
138 *
139 * @param seat Seat
140 * @param wnd Popup window
141 */
142void ds_seat_set_popup(ds_seat_t *seat, ds_window_t *wnd)
143{
144 if (wnd == seat->popup)
145 return;
146
147 if (seat->popup != NULL) {
148 /* Window is no longer the popup window, send close request */
149 ds_client_post_close_event(seat->popup->client,
150 seat->popup);
151 }
152
153 seat->popup = wnd;
154}
155
156/** Evacuate seat references to window.
157 *
158 * If seat's focus is @a wnd, it will be set to NULL.
159 * If seat's popup window is @a wnd, it will be set to NULL.
160 *
161 * @param seat Seat
162 * @param wnd Window to evacuate references from
163 */
164void ds_seat_evac_wnd_refs(ds_seat_t *seat, ds_window_t *wnd)
165{
166 if (seat->focus == wnd)
167 ds_seat_set_focus(seat, NULL);
168
169 if (seat->popup == wnd)
170 ds_seat_set_popup(seat, NULL);
171}
172
173/** Unfocus window.
174 *
175 * If seat's focus is @a wnd, it will be set to a different window
176 * that is not minimized, preferably not a system window.
177 *
178 * @param seat Seat
179 * @param wnd Window to remove focus from
180 */
181void ds_seat_unfocus_wnd(ds_seat_t *seat, ds_window_t *wnd)
182{
183 ds_window_t *nwnd;
184
185 if (seat->focus != wnd)
186 return;
187
188 /* Find alternate window that is neither system nor minimized */
189 nwnd = ds_window_find_alt(wnd, ~(wndf_minimized | wndf_system));
190
191 if (nwnd == NULL) {
192 /* Find alternate window that is not minimized */
193 nwnd = ds_window_find_alt(wnd, ~wndf_minimized);
194 }
195
196 ds_seat_set_focus(seat, nwnd);
197}
198
199/** Switch focus to another window.
200 *
201 * @param seat Seat
202 * @param wnd Window to evacuate focus from
203 */
204void ds_seat_switch_focus(ds_seat_t *seat)
205{
206 ds_window_t *nwnd;
207
208 /* Find alternate window that is not a system window */
209 nwnd = ds_window_find_alt(seat->focus, ~wndf_system);
210
211 /* Only switch focus if there is another window */
212 if (nwnd != NULL)
213 ds_seat_set_focus(seat, nwnd);
214}
215
216/** Post keyboard event to the seat's focused window.
217 *
218 * @param seat Seat
219 * @param event Event
220 *
221 * @return EOK on success or an error code
222 */
223errno_t ds_seat_post_kbd_event(ds_seat_t *seat, kbd_event_t *event)
224{
225 ds_window_t *dwindow;
226 bool alt_or_shift;
227
228 alt_or_shift = event->mods & (KM_SHIFT | KM_ALT);
229 if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_TAB) {
230 /* On Alt-Tab or Shift-Tab, switch focus to next window */
231 ds_seat_switch_focus(seat);
232 return EOK;
233 }
234
235 dwindow = seat->popup;
236 if (dwindow == NULL)
237 dwindow = seat->focus;
238
239 if (dwindow == NULL)
240 return EOK;
241
242 return ds_window_post_kbd_event(dwindow, event);
243}
244
245/** Get current cursor used by seat.
246 *
247 * @param wmcurs WM curor
248 * @param ccurs Client cursor
249 * @return
250 */
251static ds_cursor_t *ds_seat_compute_cursor(ds_cursor_t *wmcurs, ds_cursor_t *ccurs)
252{
253 if (wmcurs != NULL)
254 return wmcurs;
255
256 return ccurs;
257}
258
259/** Get current cursor used by seat.
260 *
261 * @param seat Seat
262 * @return Current cursor
263 */
264static ds_cursor_t *ds_seat_get_cursor(ds_seat_t *seat)
265{
266 return ds_seat_compute_cursor(seat->wm_cursor, seat->client_cursor);
267}
268
269/** Set client cursor.
270 *
271 * Set cursor selected by client. This may update the actual cursor
272 * if WM is not overriding the cursor.
273 *
274 * @param seat Seat
275 * @param cursor Client cursor
276 */
277static void ds_seat_set_client_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
278{
279 ds_cursor_t *old_cursor;
280 ds_cursor_t *new_cursor;
281 gfx_rect_t old_rect;
282
283 old_cursor = ds_seat_get_cursor(seat);
284 new_cursor = ds_seat_compute_cursor(seat->wm_cursor, cursor);
285
286 if (new_cursor != old_cursor) {
287 ds_seat_get_pointer_rect(seat, &old_rect);
288 seat->client_cursor = cursor;
289 ds_seat_repaint_pointer(seat, &old_rect);
290 } else {
291 seat->client_cursor = cursor;
292 }
293}
294
295/** Set WM cursor.
296 *
297 * Set cursor override for window management.
298 *
299 * @param seat Seat
300 * @param cursor WM cursor override or @c NULL not to override the cursor
301 */
302void ds_seat_set_wm_cursor(ds_seat_t *seat, ds_cursor_t *cursor)
303{
304 ds_cursor_t *old_cursor;
305 ds_cursor_t *new_cursor;
306 gfx_rect_t old_rect;
307
308 old_cursor = ds_seat_get_cursor(seat);
309 new_cursor = ds_seat_compute_cursor(cursor, seat->client_cursor);
310
311 if (new_cursor != old_cursor) {
312 ds_seat_get_pointer_rect(seat, &old_rect);
313 seat->wm_cursor = cursor;
314 ds_seat_repaint_pointer(seat, &old_rect);
315 } else {
316 seat->wm_cursor = cursor;
317 }
318}
319
320/** Get rectangle covered by pointer.
321 *
322 * @param seat Seat
323 * @param rect Place to store rectangle
324 */
325void ds_seat_get_pointer_rect(ds_seat_t *seat, gfx_rect_t *rect)
326{
327 ds_cursor_t *cursor;
328
329 cursor = ds_seat_get_cursor(seat);
330 ds_cursor_get_rect(cursor, &seat->pntpos, rect);
331}
332
333/** Repaint seat pointer
334 *
335 * Repaint the pointer after it has moved or changed. This is done by
336 * repainting the area of the display previously (@a old_rect) and currently
337 * covered by the pointer.
338 *
339 * @param seat Seat
340 * @param old_rect Rectangle previously covered by pointer
341 *
342 * @return EOK on success or an error code
343 */
344static errno_t ds_seat_repaint_pointer(ds_seat_t *seat, gfx_rect_t *old_rect)
345{
346 gfx_rect_t new_rect;
347 gfx_rect_t envelope;
348 errno_t rc;
349
350 ds_seat_get_pointer_rect(seat, &new_rect);
351
352 if (gfx_rect_is_incident(old_rect, &new_rect)) {
353 /* Rectangles do not intersect. Repaint them separately. */
354 rc = ds_display_paint(seat->display, &new_rect);
355 if (rc != EOK)
356 return rc;
357
358 rc = ds_display_paint(seat->display, old_rect);
359 if (rc != EOK)
360 return rc;
361 } else {
362 /*
363 * Rectangles intersect. As an optimization, repaint them
364 * in a single operation.
365 */
366 gfx_rect_envelope(old_rect, &new_rect, &envelope);
367
368 rc = ds_display_paint(seat->display, &envelope);
369 if (rc != EOK)
370 return rc;
371 }
372
373 return EOK;
374}
375
376/** Post pointing device event to the seat
377 *
378 * Update pointer position and generate position event.
379 *
380 * @param seat Seat
381 * @param event Event
382 *
383 * @return EOK on success or an error code
384 */
385errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
386{
387 ds_display_t *disp = seat->display;
388 gfx_coord2_t npos;
389 gfx_rect_t old_rect;
390 ds_window_t *wnd;
391 pos_event_t pevent;
392 errno_t rc;
393
394 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
395
396 /* Focus window on button press */
397 if (event->type == PTD_PRESS && event->btn_num == 1) {
398 if (wnd != NULL && (wnd->flags & wndf_popup) == 0) {
399 ds_seat_set_focus(seat, wnd);
400 }
401 }
402
403 if (event->type == PTD_PRESS || event->type == PTD_RELEASE ||
404 event->type == PTD_DCLICK) {
405 pevent.pos_id = event->pos_id;
406 switch (event->type) {
407 case PTD_PRESS:
408 pevent.type = POS_PRESS;
409 break;
410 case PTD_RELEASE:
411 pevent.type = POS_RELEASE;
412 break;
413 case PTD_DCLICK:
414 pevent.type = POS_DCLICK;
415 break;
416 default:
417 assert(false);
418 }
419
420 pevent.btn_num = event->btn_num;
421 pevent.hpos = seat->pntpos.x;
422 pevent.vpos = seat->pntpos.y;
423
424 rc = ds_seat_post_pos_event(seat, &pevent);
425 if (rc != EOK)
426 return rc;
427 }
428
429 if (event->type == PTD_MOVE) {
430 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
431 gfx_coord2_clip(&npos, &disp->rect, &npos);
432
433 ds_seat_get_pointer_rect(seat, &old_rect);
434 seat->pntpos = npos;
435
436 pevent.pos_id = event->pos_id;
437 pevent.type = POS_UPDATE;
438 pevent.btn_num = 0;
439 pevent.hpos = seat->pntpos.x;
440 pevent.vpos = seat->pntpos.y;
441
442 rc = ds_seat_post_pos_event(seat, &pevent);
443 if (rc != EOK)
444 return rc;
445
446 ds_seat_repaint_pointer(seat, &old_rect);
447 }
448
449 if (event->type == PTD_ABS_MOVE) {
450 /*
451 * Project input device area onto display area. Technically
452 * we probably want to project onto the area of a particular
453 * display device. The tricky part is figuring out which
454 * display device the input device is associated with.
455 */
456 gfx_coord2_project(&event->apos, &event->abounds,
457 &disp->rect, &npos);
458
459 gfx_coord2_clip(&npos, &disp->rect, &npos);
460
461 ds_seat_get_pointer_rect(seat, &old_rect);
462 seat->pntpos = npos;
463
464 pevent.pos_id = event->pos_id;
465 pevent.type = POS_UPDATE;
466 pevent.btn_num = 0;
467 pevent.hpos = seat->pntpos.x;
468 pevent.vpos = seat->pntpos.y;
469
470 rc = ds_seat_post_pos_event(seat, &pevent);
471 if (rc != EOK)
472 return rc;
473
474 ds_seat_repaint_pointer(seat, &old_rect);
475 }
476
477 return EOK;
478}
479
480/** Post position event to seat.
481 *
482 * Deliver event to relevant windows.
483 *
484 * @param seat Seat
485 * @param event Position event
486 */
487errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
488{
489 ds_window_t *wnd;
490 errno_t rc;
491
492 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
493
494 /* Click outside popup window */
495 if (event->type == POS_PRESS && wnd != seat->popup) {
496 /* Close popup window */
497 ds_seat_set_popup(seat, NULL);
498 }
499
500 /* Deliver event to popup window. */
501 if (seat->popup != NULL) {
502 rc = ds_window_post_pos_event(seat->popup, event);
503 if (rc != EOK)
504 return rc;
505 }
506
507 if (seat->focus != wnd && seat->focus != NULL) {
508 rc = ds_window_post_pos_event(seat->focus, event);
509 if (rc != EOK)
510 return rc;
511
512 /* Only deliver release events to the focused window */
513 if (event->type == POS_RELEASE)
514 return EOK;
515 }
516
517 if (wnd != NULL) {
518 /* Moving over a window */
519 ds_seat_set_client_cursor(seat, wnd->cursor);
520
521 /*
522 * Only deliver event if we didn't already deliver it
523 * to the same window above.
524 */
525 if (wnd != seat->popup) {
526 rc = ds_window_post_pos_event(wnd, event);
527 if (rc != EOK)
528 return rc;
529 }
530 } else {
531 /* Not over a window */
532 ds_seat_set_client_cursor(seat, seat->display->cursor[dcurs_arrow]);
533 }
534
535 return EOK;
536}
537
538/** Paint seat pointer.
539 *
540 * @param seat Seat whose pointer to paint
541 * @param rect Clipping rectangle
542 */
543errno_t ds_seat_paint_pointer(ds_seat_t *seat, gfx_rect_t *rect)
544{
545 ds_cursor_t *cursor;
546
547 cursor = ds_seat_get_cursor(seat);
548 return ds_cursor_paint(cursor, &seat->pntpos, rect);
549}
550
551/** @}
552 */
Note: See TracBrowser for help on using the repository browser.