source: mainline/uspace/srv/hid/display/client.c@ 1215db9

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1215db9 was 98735eb, checked in by jxsvoboda <5887334+jxsvoboda@…>, 4 years ago

Purge events from client event queue when destroying window

If the client had events queued for a particular window and that got
destroyed, later these events, pointing to freed (and reused) memory
would be handed to the client with the wrong window ID (which was
read from the wrong/recycled memory block).

  • Property mode set to 100644
File size: 9.8 KB
Line 
1/*
2 * Copyright (c) 2021 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 client
34 */
35
36#include <adt/list.h>
37#include <errno.h>
38#include <stdlib.h>
39#include "client.h"
40#include "display.h"
41#include "seat.h"
42#include "window.h"
43
44/** Create client.
45 *
46 * @param display Parent display
47 * @param cb Client callbacks
48 * @param cb_arg Callback argument
49 * @param rclient Place to store pointer to new client.
50 * @return EOK on success, ENOMEM if out of memory
51 */
52errno_t ds_client_create(ds_display_t *display, ds_client_cb_t *cb,
53 void *cb_arg, ds_client_t **rclient)
54{
55 ds_client_t *client;
56
57 client = calloc(1, sizeof(ds_client_t));
58 if (client == NULL)
59 return ENOMEM;
60
61 list_initialize(&client->windows);
62 list_initialize(&client->events);
63 client->cb = cb;
64 client->cb_arg = cb_arg;
65
66 ds_display_add_client(display, client);
67
68 *rclient = client;
69 return EOK;
70}
71
72/** Destroy client.
73 *
74 * @param client Client
75 */
76void ds_client_destroy(ds_client_t *client)
77{
78 ds_window_t *window;
79
80 window = ds_client_first_window(client);
81 while (window != NULL) {
82 ds_window_destroy(window);
83 window = ds_client_first_window(client);
84 }
85
86 assert(list_empty(&client->windows));
87 ds_display_remove_client(client);
88 free(client);
89}
90
91/** Add window to client.
92 *
93 * @param client client
94 * @param wnd Window
95 */
96void ds_client_add_window(ds_client_t *client, ds_window_t *wnd)
97{
98 assert(wnd->client == NULL);
99 assert(!link_used(&wnd->lcwindows));
100
101 wnd->client = client;
102 wnd->id = client->display->next_wnd_id++;
103 list_append(&wnd->lcwindows, &client->windows);
104}
105
106/** Remove window from client.
107 *
108 * @param wnd Window
109 */
110void ds_client_remove_window(ds_window_t *wnd)
111{
112 ds_seat_t *seat;
113
114 /* Make sure window is no longer focused in any seat */
115 seat = ds_display_first_seat(wnd->display);
116 while (seat != NULL) {
117 ds_seat_evac_wnd_refs(seat, wnd);
118 seat = ds_display_next_seat(seat);
119 }
120
121 /* Make sure no event in the queue is referencing the window */
122 ds_client_purge_window_events(wnd->client, wnd);
123
124 list_remove(&wnd->lcwindows);
125 wnd->client = NULL;
126}
127
128/** Find window by ID.
129 *
130 * @param client Client
131 * @param id Window ID
132 */
133ds_window_t *ds_client_find_window(ds_client_t *client, ds_wnd_id_t id)
134{
135 ds_window_t *wnd;
136
137 // TODO Make this faster
138 wnd = ds_client_first_window(client);
139 while (wnd != NULL) {
140 if (wnd->id == id)
141 return wnd;
142 wnd = ds_client_next_window(wnd);
143 }
144
145 return NULL;
146}
147
148/** Get first window in client.
149 *
150 * @param client Client
151 * @return First window or @c NULL if there is none
152 */
153ds_window_t *ds_client_first_window(ds_client_t *client)
154{
155 link_t *link = list_first(&client->windows);
156
157 if (link == NULL)
158 return NULL;
159
160 return list_get_instance(link, ds_window_t, lcwindows);
161}
162
163/** Get next window in client.
164 *
165 * @param wnd Current window
166 * @return Next window or @c NULL if there is none
167 */
168ds_window_t *ds_client_next_window(ds_window_t *wnd)
169{
170 link_t *link = list_next(&wnd->lcwindows, &wnd->client->windows);
171
172 if (link == NULL)
173 return NULL;
174
175 return list_get_instance(link, ds_window_t, lcwindows);
176}
177
178/** Get next event from client event queue.
179 *
180 * @param client Client
181 * @param ewindow Place to store pointer to window receiving the event
182 * @param event Place to store event
183 * @return Graphic context
184 */
185errno_t ds_client_get_event(ds_client_t *client, ds_window_t **ewindow,
186 display_wnd_ev_t *event)
187{
188 link_t *link;
189 ds_window_ev_t *wevent;
190
191 link = list_first(&client->events);
192 if (link == NULL)
193 return ENOENT;
194
195 wevent = list_get_instance(link, ds_window_ev_t, levents);
196 list_remove(link);
197
198 *ewindow = wevent->window;
199 *event = wevent->event;
200 free(wevent);
201 return EOK;
202}
203
204/** Purge events from client event queue referring to a window.
205 *
206 * @param client Client
207 * @param window Window
208 */
209void ds_client_purge_window_events(ds_client_t *client,
210 ds_window_t *window)
211{
212 link_t *cur;
213 link_t *next;
214 ds_window_ev_t *wevent;
215
216 cur = list_first(&client->events);
217 while (cur != NULL) {
218 next = list_next(cur, &client->events);
219 wevent = list_get_instance(cur, ds_window_ev_t, levents);
220
221 if (wevent->window == window)
222 list_remove(cur);
223
224 cur = next;
225 }
226}
227
228/** Post close event to the client's message queue.
229 *
230 * @param client Client
231 * @param ewindow Window that the message is targetted to
232 *
233 * @return EOK on success or an error code
234 */
235errno_t ds_client_post_close_event(ds_client_t *client, ds_window_t *ewindow)
236{
237 ds_window_ev_t *wevent;
238
239 wevent = calloc(1, sizeof(ds_window_ev_t));
240 if (wevent == NULL)
241 return ENOMEM;
242
243 wevent->window = ewindow;
244 wevent->event.etype = wev_close;
245 list_append(&wevent->levents, &client->events);
246
247 /* Notify the client */
248 // TODO Do not send more than once until client drains the queue
249 if (client->cb != NULL && client->cb->ev_pending != NULL)
250 client->cb->ev_pending(client->cb_arg);
251
252 return EOK;
253}
254
255/** Post focus event to the client's message queue.
256 *
257 * @param client Client
258 * @param ewindow Window that the message is targetted to
259 *
260 * @return EOK on success or an error code
261 */
262errno_t ds_client_post_focus_event(ds_client_t *client, ds_window_t *ewindow)
263{
264 ds_window_ev_t *wevent;
265
266 wevent = calloc(1, sizeof(ds_window_ev_t));
267 if (wevent == NULL)
268 return ENOMEM;
269
270 wevent->window = ewindow;
271 wevent->event.etype = wev_focus;
272 list_append(&wevent->levents, &client->events);
273
274 /* Notify the client */
275 // TODO Do not send more than once until client drains the queue
276 if (client->cb != NULL && client->cb->ev_pending != NULL)
277 client->cb->ev_pending(client->cb_arg);
278
279 return EOK;
280}
281
282/** Post keyboard event to the client's message queue.
283 *
284 * @param client Client
285 * @param ewindow Window that the message is targetted to
286 * @param event Event
287 *
288 * @return EOK on success or an error code
289 */
290errno_t ds_client_post_kbd_event(ds_client_t *client, ds_window_t *ewindow,
291 kbd_event_t *event)
292{
293 ds_window_ev_t *wevent;
294
295 wevent = calloc(1, sizeof(ds_window_ev_t));
296 if (wevent == NULL)
297 return ENOMEM;
298
299 wevent->window = ewindow;
300 wevent->event.etype = wev_kbd;
301 wevent->event.ev.kbd = *event;
302 list_append(&wevent->levents, &client->events);
303
304 /* Notify the client */
305 // TODO Do not send more than once until client drains the queue
306 client->cb->ev_pending(client->cb_arg);
307
308 return EOK;
309}
310
311/** Post position event to the client's message queue.
312 *
313 * @param client Client
314 * @param ewindow Window that the message is targetted to
315 * @param event Event
316 *
317 * @return EOK on success or an error code
318 */
319errno_t ds_client_post_pos_event(ds_client_t *client, ds_window_t *ewindow,
320 pos_event_t *event)
321{
322 ds_window_ev_t *wevent;
323
324 wevent = calloc(1, sizeof(ds_window_ev_t));
325 if (wevent == NULL)
326 return ENOMEM;
327
328 wevent->window = ewindow;
329 wevent->event.etype = wev_pos;
330 wevent->event.ev.pos = *event;
331 list_append(&wevent->levents, &client->events);
332
333 /* Notify the client */
334 // TODO Do not send more than once until client drains the queue
335 if (client->cb != NULL && client->cb->ev_pending != NULL)
336 client->cb->ev_pending(client->cb_arg);
337
338 return EOK;
339}
340
341/** Post resize event to the client's message queue.
342 *
343 * @param client Client
344 * @param ewindow Window that the message is targetted to
345 * @param rect New window rectangle
346 *
347 * @return EOK on success or an error code
348 */
349errno_t ds_client_post_resize_event(ds_client_t *client, ds_window_t *ewindow,
350 gfx_rect_t *rect)
351{
352 ds_window_ev_t *wevent;
353
354 wevent = calloc(1, sizeof(ds_window_ev_t));
355 if (wevent == NULL)
356 return ENOMEM;
357
358 wevent->window = ewindow;
359 wevent->event.etype = wev_resize;
360 wevent->event.ev.resize.rect = *rect;
361 list_append(&wevent->levents, &client->events);
362
363 /* Notify the client */
364 // TODO Do not send more than once until client drains the queue
365 if (client->cb != NULL && client->cb->ev_pending != NULL)
366 client->cb->ev_pending(client->cb_arg);
367
368 return EOK;
369}
370
371/** Post unfocus event to the client's message queue.
372 *
373 * @param client Client
374 * @param ewindow Window that the message is targetted to
375 *
376 * @return EOK on success or an error code
377 */
378errno_t ds_client_post_unfocus_event(ds_client_t *client, ds_window_t *ewindow)
379{
380 ds_window_ev_t *wevent;
381
382 wevent = calloc(1, sizeof(ds_window_ev_t));
383 if (wevent == NULL)
384 return ENOMEM;
385
386 wevent->window = ewindow;
387 wevent->event.etype = wev_unfocus;
388 list_append(&wevent->levents, &client->events);
389
390 /* Notify the client */
391 // TODO Do not send more than once until client drains the queue
392 if (client->cb != NULL && client->cb->ev_pending != NULL)
393 client->cb->ev_pending(client->cb_arg);
394
395 return EOK;
396}
397
398/** @}
399 */
Note: See TracBrowser for help on using the repository browser.