source: mainline/uspace/srv/hid/display/client.c

Last change on this file was 46a47c0, checked in by Jiri Svoboda <jiri@…>, 2 years ago

Make sure window is only show as inactive when it loses last focus

This currently affects the title bar and also the cursor in Terminal.

  • Property mode set to 100644
File size: 10.0 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 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 EOK on success, ENOENT if event queue is empty
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 * @param event Focus event data
260 *
261 * @return EOK on success or an error code
262 */
263errno_t ds_client_post_focus_event(ds_client_t *client, ds_window_t *ewindow,
264 display_wnd_focus_ev_t *event)
265{
266 ds_window_ev_t *wevent;
267
268 wevent = calloc(1, sizeof(ds_window_ev_t));
269 if (wevent == NULL)
270 return ENOMEM;
271
272 wevent->window = ewindow;
273 wevent->event.etype = wev_focus;
274 wevent->event.ev.focus = *event;
275 list_append(&wevent->levents, &client->events);
276
277 /* Notify the client */
278 // TODO Do not send more than once until client drains the queue
279 if (client->cb != NULL && client->cb->ev_pending != NULL)
280 client->cb->ev_pending(client->cb_arg);
281
282 return EOK;
283}
284
285/** Post keyboard event to the client's message queue.
286 *
287 * @param client Client
288 * @param ewindow Window that the message is targetted to
289 * @param event Event
290 *
291 * @return EOK on success or an error code
292 */
293errno_t ds_client_post_kbd_event(ds_client_t *client, ds_window_t *ewindow,
294 kbd_event_t *event)
295{
296 ds_window_ev_t *wevent;
297
298 wevent = calloc(1, sizeof(ds_window_ev_t));
299 if (wevent == NULL)
300 return ENOMEM;
301
302 wevent->window = ewindow;
303 wevent->event.etype = wev_kbd;
304 wevent->event.ev.kbd = *event;
305 list_append(&wevent->levents, &client->events);
306
307 /* Notify the client */
308 // TODO Do not send more than once until client drains the queue
309 client->cb->ev_pending(client->cb_arg);
310
311 return EOK;
312}
313
314/** Post position event to the client's message queue.
315 *
316 * @param client Client
317 * @param ewindow Window that the message is targetted to
318 * @param event Event
319 *
320 * @return EOK on success or an error code
321 */
322errno_t ds_client_post_pos_event(ds_client_t *client, ds_window_t *ewindow,
323 pos_event_t *event)
324{
325 ds_window_ev_t *wevent;
326
327 wevent = calloc(1, sizeof(ds_window_ev_t));
328 if (wevent == NULL)
329 return ENOMEM;
330
331 wevent->window = ewindow;
332 wevent->event.etype = wev_pos;
333 wevent->event.ev.pos = *event;
334 list_append(&wevent->levents, &client->events);
335
336 /* Notify the client */
337 // TODO Do not send more than once until client drains the queue
338 if (client->cb != NULL && client->cb->ev_pending != NULL)
339 client->cb->ev_pending(client->cb_arg);
340
341 return EOK;
342}
343
344/** Post resize event to the client's message queue.
345 *
346 * @param client Client
347 * @param ewindow Window that the message is targetted to
348 * @param rect New window rectangle
349 *
350 * @return EOK on success or an error code
351 */
352errno_t ds_client_post_resize_event(ds_client_t *client, ds_window_t *ewindow,
353 gfx_rect_t *rect)
354{
355 ds_window_ev_t *wevent;
356
357 wevent = calloc(1, sizeof(ds_window_ev_t));
358 if (wevent == NULL)
359 return ENOMEM;
360
361 wevent->window = ewindow;
362 wevent->event.etype = wev_resize;
363 wevent->event.ev.resize.rect = *rect;
364 list_append(&wevent->levents, &client->events);
365
366 /* Notify the client */
367 // TODO Do not send more than once until client drains the queue
368 if (client->cb != NULL && client->cb->ev_pending != NULL)
369 client->cb->ev_pending(client->cb_arg);
370
371 return EOK;
372}
373
374/** Post unfocus event to the client's message queue.
375 *
376 * @param client Client
377 * @param ewindow Window that the message is targetted to
378 * @param event Unfocus event data
379 *
380 * @return EOK on success or an error code
381 */
382errno_t ds_client_post_unfocus_event(ds_client_t *client, ds_window_t *ewindow,
383 display_wnd_unfocus_ev_t *event)
384{
385 ds_window_ev_t *wevent;
386
387 wevent = calloc(1, sizeof(ds_window_ev_t));
388 if (wevent == NULL)
389 return ENOMEM;
390
391 wevent->window = ewindow;
392 wevent->event.etype = wev_unfocus;
393 wevent->event.ev.unfocus = *event;
394 list_append(&wevent->levents, &client->events);
395
396 /* Notify the client */
397 // TODO Do not send more than once until client drains the queue
398 if (client->cb != NULL && client->cb->ev_pending != NULL)
399 client->cb->ev_pending(client->cb_arg);
400
401 return EOK;
402}
403
404/** @}
405 */
Note: See TracBrowser for help on using the repository browser.