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

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

Fix null pointer dereference when last window is destroyed

  • Property mode set to 100644
File size: 8.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 "display.h"
43#include "seat.h"
44#include "window.h"
45
46/** Create seat.
47 *
48 * @param display Parent display
49 * @param rseat Place to store pointer to new seat.
50 * @return EOK on success, ENOMEM if out of memory
51 */
52errno_t ds_seat_create(ds_display_t *display, ds_seat_t **rseat)
53{
54 ds_seat_t *seat;
55
56 seat = calloc(1, sizeof(ds_seat_t));
57 if (seat == NULL)
58 return ENOMEM;
59
60 ds_display_add_seat(display, seat);
61 seat->pntpos.x = 0;
62 seat->pntpos.y = 0;
63
64 *rseat = seat;
65 return EOK;
66}
67
68/** Destroy seat.
69 *
70 * @param seat Seat
71 */
72void ds_seat_destroy(ds_seat_t *seat)
73{
74 ds_display_remove_seat(seat);
75 free(seat);
76}
77
78/** Set seat focus to a window.
79 *
80 * @param seat Seat
81 * @param wnd Window to focus
82 */
83void ds_seat_set_focus(ds_seat_t *seat, ds_window_t *wnd)
84{
85 if (seat->focus != NULL)
86 ds_window_post_unfocus_event(seat->focus);
87
88 seat->focus = wnd;
89
90 if (wnd != NULL) {
91 ds_window_post_focus_event(wnd);
92 ds_window_bring_to_top(wnd);
93 }
94}
95
96/** Evacuate focus from window.
97 *
98 * If seat's focus is @a wnd, it will be set to a different window.
99 *
100 * @param seat Seat
101 * @param wnd Window to evacuate focus from
102 */
103void ds_seat_evac_focus(ds_seat_t *seat, ds_window_t *wnd)
104{
105 ds_window_t *nwnd;
106
107 if (seat->focus == wnd) {
108 nwnd = ds_display_next_window(wnd);
109 if (nwnd == NULL)
110 nwnd = ds_display_first_window(wnd->display);
111 if (nwnd == wnd)
112 nwnd = NULL;
113
114 ds_seat_set_focus(seat, nwnd);
115 }
116}
117
118/** Post keyboard event to the seat's focused window.
119 *
120 * @param seat Seat
121 * @param event Event
122 *
123 * @return EOK on success or an error code
124 */
125errno_t ds_seat_post_kbd_event(ds_seat_t *seat, kbd_event_t *event)
126{
127 ds_window_t *dwindow;
128 bool alt_or_shift;
129
130 alt_or_shift = event->mods & (KM_SHIFT | KM_ALT);
131 if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_TAB) {
132 /* On Alt-Tab or Shift-Tab, switch focus to next window */
133 ds_seat_evac_focus(seat, seat->focus);
134 return EOK;
135 }
136
137 dwindow = seat->focus;
138 if (dwindow == NULL)
139 return EOK;
140
141 return ds_window_post_kbd_event(dwindow, event);
142}
143
144/** Draw cross at seat pointer position.
145 *
146 * @param seat Seat
147 * @param len Cross length
148 * @param w Cross extra width
149 * @param color Color
150 *
151 * @return EOK on success or an error code
152 */
153static errno_t ds_seat_draw_cross(ds_seat_t *seat, gfx_coord_t len,
154 gfx_coord_t w, gfx_color_t *color)
155{
156 gfx_context_t *gc;
157 gfx_rect_t rect, r0;
158 errno_t rc;
159
160 gc = ds_display_get_gc(seat->display);
161 if (gc == NULL)
162 return EOK;
163
164 rc = gfx_set_color(gc, color);
165 if (rc != EOK)
166 goto error;
167
168 r0.p0.x = -len;
169 r0.p0.y = -w;
170 r0.p1.x = +len + 1;
171 r0.p1.y = +w + 1;
172 gfx_rect_translate(&seat->pntpos, &r0, &rect);
173
174 rc = gfx_fill_rect(gc, &rect);
175 if (rc != EOK)
176 goto error;
177
178 r0.p0.x = -w;
179 r0.p0.y = -len;
180 r0.p1.x = +w + 1;
181 r0.p1.y = +len + 1;
182 gfx_rect_translate(&seat->pntpos, &r0, &rect);
183
184 rc = gfx_fill_rect(gc, &rect);
185 if (rc != EOK)
186 goto error;
187
188 return EOK;
189error:
190 return rc;
191}
192
193/** Draw seat pointer
194 *
195 * @param seat Seat
196 *
197 * @return EOK on success or an error code
198 */
199static errno_t ds_seat_draw_pointer(ds_seat_t *seat)
200{
201 errno_t rc;
202 gfx_color_t *black = NULL;
203 gfx_color_t *white;
204
205 rc = gfx_color_new_rgb_i16(0, 0, 0, &black);
206 if (rc != EOK)
207 goto error;
208
209 rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &white);
210 if (rc != EOK)
211 goto error;
212
213 rc = ds_seat_draw_cross(seat, 8, 1, black);
214 if (rc != EOK)
215 goto error;
216
217 rc = ds_seat_draw_cross(seat, 8, 0, white);
218 if (rc != EOK)
219 goto error;
220
221 gfx_color_delete(black);
222 gfx_color_delete(white);
223
224 return EOK;
225error:
226 if (black != NULL)
227 gfx_color_delete(black);
228 if (white != NULL)
229 gfx_color_delete(white);
230 return rc;
231}
232
233/** Clear seat pointer
234 *
235 * @param seat Seat
236 *
237 * @return EOK on success or an error code
238 */
239static errno_t ds_seat_clear_pointer(ds_seat_t *seat)
240{
241 gfx_rect_t rect;
242
243 rect.p0.x = seat->pntpos.x - 8;
244 rect.p0.y = seat->pntpos.y - 8;
245 rect.p1.x = seat->pntpos.x + 8 + 1;
246 rect.p1.y = seat->pntpos.y + 8 + 1;
247
248 return ds_display_paint(seat->display, &rect);
249}
250
251/** Post pointing device event to the seat
252 *
253 * Update pointer position and generate position event.
254 *
255 * @param seat Seat
256 * @param event Event
257 *
258 * @return EOK on success or an error code
259 */
260#include <stdio.h>
261errno_t ds_seat_post_ptd_event(ds_seat_t *seat, ptd_event_t *event)
262{
263 ds_display_t *disp = seat->display;
264 gfx_coord2_t npos;
265 ds_window_t *wnd;
266 pos_event_t pevent;
267 errno_t rc;
268
269 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
270
271 /* Focus window on button press */
272 if (event->type == PTD_PRESS && event->btn_num == 1) {
273 if (wnd != NULL) {
274 ds_seat_set_focus(seat, wnd);
275 }
276 }
277
278 if (event->type == PTD_PRESS || event->type == PTD_RELEASE) {
279 pevent.pos_id = 0;
280 pevent.type = (event->type == PTD_PRESS) ?
281 POS_PRESS : POS_RELEASE;
282 pevent.btn_num = event->btn_num;
283 pevent.hpos = seat->pntpos.x;
284 pevent.vpos = seat->pntpos.y;
285
286 rc = ds_seat_post_pos_event(seat, &pevent);
287 if (rc != EOK)
288 return rc;
289 }
290
291 if (event->type == PTD_MOVE) {
292 gfx_coord2_add(&seat->pntpos, &event->dmove, &npos);
293 gfx_coord2_clip(&npos, &disp->rect, &npos);
294
295 (void) ds_seat_clear_pointer(seat);
296 seat->pntpos = npos;
297
298 pevent.pos_id = 0;
299 pevent.type = POS_UPDATE;
300 pevent.btn_num = 0;
301 pevent.hpos = seat->pntpos.x;
302 pevent.vpos = seat->pntpos.y;
303
304 rc = ds_seat_post_pos_event(seat, &pevent);
305 if (rc != EOK)
306 return rc;
307
308 (void) ds_seat_draw_pointer(seat);
309 }
310
311 if (event->type == PTD_ABS_MOVE) {
312 /*
313 * Project input device area onto display area. Technically
314 * we probably want to project onto the area of a particular
315 * display device. The tricky part is figuring out which
316 * display device the input device is associated with.
317 */
318 gfx_coord2_project(&event->apos, &event->abounds,
319 &disp->rect, &npos);
320
321 gfx_coord2_clip(&npos, &disp->rect, &npos);
322
323 (void) ds_seat_clear_pointer(seat);
324 seat->pntpos = npos;
325
326 pevent.pos_id = 0;
327 pevent.type = POS_UPDATE;
328 pevent.btn_num = 0;
329 pevent.hpos = seat->pntpos.x;
330 pevent.vpos = seat->pntpos.y;
331
332 rc = ds_seat_post_pos_event(seat, &pevent);
333 if (rc != EOK)
334 return rc;
335
336 (void) ds_seat_draw_pointer(seat);
337 }
338
339 return EOK;
340}
341
342/** Post position event to seat.
343 *
344 * Deliver event to relevant windows.
345 *
346 * @param seat Seat
347 * @param event Position event
348 */
349errno_t ds_seat_post_pos_event(ds_seat_t *seat, pos_event_t *event)
350{
351 ds_window_t *wnd;
352 errno_t rc;
353
354 wnd = ds_display_window_by_pos(seat->display, &seat->pntpos);
355 if (wnd != NULL) {
356 rc = ds_window_post_pos_event(wnd, event);
357 if (rc != EOK)
358 return rc;
359 }
360
361 if (seat->focus != wnd) {
362 rc = ds_window_post_pos_event(seat->focus, event);
363 if (rc != EOK)
364 return rc;
365 }
366
367 return EOK;
368}
369
370/** @}
371 */
Note: See TracBrowser for help on using the repository browser.