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

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

Switch to previous (last) window when cycling

Focusing a window brings it to top, i.e. first in the list (the order of
which is the stacking order). Thus the formerly first window becomes
the second. Switching to the next window thus always cycles trough
the same two windows. Switching to the previous (actualy last window,
there is no previous window) will cycle through all the windows.

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