source: mainline/uspace/lib/dispcfg/src/dispcfg.c

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

UI display configuration utility

In addition to the command-line utility 'disp', we introduce its UI
equivalent 'display-cfg'. Currently this allows the user to configure
seats in a very comfortable way.

  • Property mode set to 100644
File size: 12.3 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 libdispcfg
30 * @{
31 */
32/**
33 * @file
34 * @brief Display configuration protocol client
35 */
36
37#include <async.h>
38#include <dispcfg.h>
39#include <errno.h>
40#include <fibril_synch.h>
41#include <ipc/dispcfg.h>
42#include <ipc/services.h>
43#include <loc.h>
44#include <mem.h>
45#include <stdlib.h>
46#include <str.h>
47#include "../private/dispcfg.h"
48
49static errno_t dispcfg_callback_create(dispcfg_t *);
50static void dispcfg_cb_conn(ipc_call_t *, void *);
51
52/** Open display configuration service.
53 *
54 * @param wmname Display configuration service name or @c NULL to use default
55 * @param cb Display configuration callbacks
56 * @param cb_arg Callback argument
57 * @param rdispcfg Place to store pointer to display configuration session
58 * @return EOK on success or an error code
59 */
60errno_t dispcfg_open(const char *wmname, dispcfg_cb_t *cb, void *cb_arg,
61 dispcfg_t **rdispcfg)
62{
63 service_id_t dispcfg_svc;
64 dispcfg_t *dispcfg;
65 errno_t rc;
66
67 dispcfg = calloc(1, sizeof(dispcfg_t));
68 if (dispcfg == NULL)
69 return ENOMEM;
70
71 dispcfg->cb = cb;
72 dispcfg->cb_arg = cb_arg;
73
74 fibril_mutex_initialize(&dispcfg->lock);
75 fibril_condvar_initialize(&dispcfg->cv);
76
77 if (wmname == NULL)
78 wmname = SERVICE_NAME_DISPCFG;
79
80 rc = loc_service_get_id(wmname, &dispcfg_svc, 0);
81 if (rc != EOK) {
82 free(dispcfg);
83 return ENOENT;
84 }
85
86 dispcfg->sess = loc_service_connect(dispcfg_svc, INTERFACE_DISPCFG,
87 0);
88 if (dispcfg->sess == NULL) {
89 free(dispcfg);
90 return ENOENT;
91 }
92
93 rc = dispcfg_callback_create(dispcfg);
94 if (rc != EOK) {
95 async_hangup(dispcfg->sess);
96 free(dispcfg);
97 return EIO;
98 }
99
100 *rdispcfg = dispcfg;
101 return EOK;
102}
103
104/** Create callback connection from display configuration service.
105 *
106 * @param dispcfg Display configuration
107 * @return EOK on success or an error code
108 */
109static errno_t dispcfg_callback_create(dispcfg_t *dispcfg)
110{
111 async_exch_t *exch = async_exchange_begin(dispcfg->sess);
112
113 aid_t req = async_send_0(exch, DISPCFG_CALLBACK_CREATE, NULL);
114
115 port_id_t port;
116 errno_t rc = async_create_callback_port(exch, INTERFACE_DISPCFG_CB, 0, 0,
117 dispcfg_cb_conn, dispcfg, &port);
118
119 async_exchange_end(exch);
120
121 if (rc != EOK)
122 return rc;
123
124 errno_t retval;
125 async_wait_for(req, &retval);
126
127 return retval;
128}
129
130/** Close display configuration service.
131 *
132 * @param dispcfg Display configuration
133 */
134void dispcfg_close(dispcfg_t *dispcfg)
135{
136 fibril_mutex_lock(&dispcfg->lock);
137 async_hangup(dispcfg->sess);
138 dispcfg->sess = NULL;
139
140 /* Wait for callback handler to terminate */
141
142 while (!dispcfg->cb_done)
143 fibril_condvar_wait(&dispcfg->cv, &dispcfg->lock);
144 fibril_mutex_unlock(&dispcfg->lock);
145
146 free(dispcfg);
147}
148
149/** Get seat list.
150 *
151 * @param dispcfg Display configuration
152 * @param rlist Place to store pointer to new seat list structure
153 * @return EOK on success or an error code
154 */
155errno_t dispcfg_get_seat_list(dispcfg_t *dispcfg, dispcfg_seat_list_t **rlist)
156{
157 async_exch_t *exch;
158 aid_t req;
159 ipc_call_t answer;
160 dispcfg_seat_list_t *list;
161 sysarg_t nseats;
162 sysarg_t *seats;
163 errno_t rc;
164
165 exch = async_exchange_begin(dispcfg->sess);
166 req = async_send_0(exch, DISPCFG_GET_SEAT_LIST, &answer);
167
168 /* Receive seat list length */
169 rc = async_data_read_start(exch, &nseats, sizeof (nseats));
170 if (rc != EOK) {
171 async_exchange_end(exch);
172 async_wait_for(req, &rc);
173 return rc;
174 }
175
176 seats = calloc(nseats, sizeof(sysarg_t));
177 if (seats == NULL) {
178 async_exchange_end(exch);
179 async_forget(req);
180 return ENOMEM;
181 }
182
183 /* Receive seat list */
184 rc = async_data_read_start(exch, seats, nseats * sizeof (sysarg_t));
185 async_exchange_end(exch);
186
187 if (rc != EOK) {
188 async_forget(req);
189 return rc;
190 }
191
192 async_wait_for(req, &rc);
193 if (rc != EOK)
194 return rc;
195
196 list = calloc(1, sizeof(dispcfg_seat_list_t));
197 if (list == NULL)
198 return ENOMEM;
199
200 list->nseats = nseats;
201 list->seats = seats;
202 *rlist = list;
203 return EOK;
204}
205
206/** Free seat list.
207 *
208 * @param list Seat list
209 */
210void dispcfg_free_seat_list(dispcfg_seat_list_t *list)
211{
212 free(list->seats);
213 free(list);
214}
215
216/** Get seat information.
217 *
218 * @param dispcfg Display configuration
219 * @param seat_id Seat ID
220 * @param rinfo Place to store pointer to new seat information structure
221 * @return EOK on success or an error code
222 */
223errno_t dispcfg_get_seat_info(dispcfg_t *dispcfg, sysarg_t seat_id,
224 dispcfg_seat_info_t **rinfo)
225{
226 async_exch_t *exch;
227 aid_t req;
228 ipc_call_t answer;
229 dispcfg_seat_info_t *info;
230 sysarg_t namesize;
231 char *name;
232 errno_t rc;
233
234 exch = async_exchange_begin(dispcfg->sess);
235 req = async_send_1(exch, DISPCFG_GET_SEAT_INFO, seat_id, &answer);
236
237 /* Receive name size */
238 rc = async_data_read_start(exch, &namesize, sizeof (namesize));
239 if (rc != EOK) {
240 async_exchange_end(exch);
241 async_wait_for(req, &rc);
242 return rc;
243 }
244
245 name = calloc(namesize + 1, sizeof(char));
246 if (name == NULL) {
247 async_exchange_end(exch);
248 async_forget(req);
249 return ENOMEM;
250 }
251
252 /* Receive name */
253 rc = async_data_read_start(exch, name, namesize);
254 async_exchange_end(exch);
255
256 if (rc != EOK) {
257 async_forget(req);
258 return rc;
259 }
260
261 async_wait_for(req, &rc);
262 if (rc != EOK)
263 return rc;
264
265 /* Null-terminate the string */
266 name[namesize] = '\0';
267
268 info = calloc(1, sizeof(dispcfg_seat_info_t));
269 if (info == NULL)
270 return ENOMEM;
271
272 info->name = name;
273 *rinfo = info;
274 return EOK;
275}
276
277/** Free seat information.
278 *
279 * @param info Seat information
280 */
281void dispcfg_free_seat_info(dispcfg_seat_info_t *info)
282{
283 free(info->name);
284 free(info);
285}
286
287/** Create seat.
288 *
289 * @param dispcfg Display configuration session
290 * @param name Seat name
291 * @param rseat_id Place to store ID of the new seat
292 * @return EOK on success or an error code
293 */
294errno_t dispcfg_seat_create(dispcfg_t *dispcfg, const char *name,
295 sysarg_t *rseat_id)
296{
297 async_exch_t *exch;
298 aid_t req;
299 ipc_call_t answer;
300 size_t name_size;
301 errno_t rc;
302
303 name_size = str_size(name);
304
305 exch = async_exchange_begin(dispcfg->sess);
306 req = async_send_0(exch, DISPCFG_SEAT_CREATE, &answer);
307
308 /* Write name */
309 rc = async_data_write_start(exch, name, name_size);
310 async_exchange_end(exch);
311 if (rc != EOK) {
312 async_forget(req);
313 return rc;
314 }
315
316 async_wait_for(req, &rc);
317 if (rc != EOK)
318 return rc;
319
320 *rseat_id = ipc_get_arg1(&answer);
321 return EOK;
322}
323
324/** Delete seat.
325 *
326 * @param dispcfg Display configuration
327 * @param seat_id Seat ID
328 * @return EOK on success or an error code
329 */
330errno_t dispcfg_seat_delete(dispcfg_t *dispcfg, sysarg_t seat_id)
331{
332 async_exch_t *exch;
333 errno_t rc;
334
335 exch = async_exchange_begin(dispcfg->sess);
336 rc = async_req_1_0(exch, DISPCFG_SEAT_DELETE, seat_id);
337
338 async_exchange_end(exch);
339 return rc;
340}
341
342/** Assign device to seat.
343 *
344 * @param dispcfg Display configuration
345 * @param svc_id Device service ID
346 * @param seat_id Seat ID
347 * @return EOK on success or an error code
348 */
349errno_t dispcfg_dev_assign(dispcfg_t *dispcfg, sysarg_t svc_id,
350 sysarg_t seat_id)
351{
352 async_exch_t *exch;
353 errno_t rc;
354
355 exch = async_exchange_begin(dispcfg->sess);
356 rc = async_req_2_0(exch, DISPCFG_DEV_ASSIGN, svc_id, seat_id);
357
358 async_exchange_end(exch);
359 return rc;
360}
361
362/** Unassign device from any specific seat.
363 *
364 * The device will fall back to the default seat.
365 *
366 * @param dispcfg Display configuration
367 * @param svc_id Device service ID
368 * @return EOK on success or an error code
369 */
370errno_t dispcfg_dev_unassign(dispcfg_t *dispcfg, sysarg_t svc_id)
371{
372 async_exch_t *exch;
373 errno_t rc;
374
375 exch = async_exchange_begin(dispcfg->sess);
376 rc = async_req_1_0(exch, DISPCFG_DEV_UNASSIGN, svc_id);
377
378 async_exchange_end(exch);
379 return rc;
380}
381
382/** Get list of devices assigned to a seat.
383 *
384 * @param dispcfg Display configuration
385 * @param seat_id Seat ID
386 * @param rlist Place to store pointer to new device list structure
387 * @return EOK on success or an error code
388 */
389errno_t dispcfg_get_asgn_dev_list(dispcfg_t *dispcfg, sysarg_t seat_id,
390 dispcfg_dev_list_t **rlist)
391{
392 async_exch_t *exch;
393 aid_t req;
394 ipc_call_t answer;
395 dispcfg_dev_list_t *list;
396 sysarg_t ndevs;
397 sysarg_t *devs;
398 errno_t rc;
399
400 exch = async_exchange_begin(dispcfg->sess);
401 req = async_send_1(exch, DISPCFG_GET_ASGN_DEV_LIST, seat_id, &answer);
402
403 /* Receive device list length */
404 rc = async_data_read_start(exch, &ndevs, sizeof (ndevs));
405 if (rc != EOK) {
406 async_exchange_end(exch);
407 async_wait_for(req, &rc);
408 return rc;
409 }
410
411 devs = calloc(ndevs, sizeof(sysarg_t));
412 if (devs == NULL) {
413 async_exchange_end(exch);
414 async_forget(req);
415 return ENOMEM;
416 }
417
418 /* Receive device list */
419 rc = async_data_read_start(exch, devs, ndevs * sizeof (sysarg_t));
420 async_exchange_end(exch);
421
422 if (rc != EOK) {
423 async_forget(req);
424 return rc;
425 }
426
427 async_wait_for(req, &rc);
428 if (rc != EOK)
429 return rc;
430
431 list = calloc(1, sizeof(dispcfg_dev_list_t));
432 if (list == NULL)
433 return ENOMEM;
434
435 list->ndevs = ndevs;
436 list->devs = devs;
437 *rlist = list;
438 return EOK;
439}
440
441/** Free device list.
442 *
443 * @param list Device list
444 */
445void dispcfg_free_dev_list(dispcfg_dev_list_t *list)
446{
447 free(list->devs);
448 free(list);
449}
450
451/** Get display configuration event.
452 *
453 * @param dispcfg Display configuration
454 * @param event Place to store event
455 * @return EOK on success or an error code
456 */
457static errno_t dispcfg_get_event(dispcfg_t *dispcfg, dispcfg_ev_t *event)
458{
459 async_exch_t *exch;
460 ipc_call_t answer;
461 aid_t req;
462 errno_t rc;
463
464 exch = async_exchange_begin(dispcfg->sess);
465 req = async_send_0(exch, DISPCFG_GET_EVENT, &answer);
466 rc = async_data_read_start(exch, event, sizeof(*event));
467 async_exchange_end(exch);
468 if (rc != EOK) {
469 async_forget(req);
470 return rc;
471 }
472
473 async_wait_for(req, &rc);
474 if (rc != EOK)
475 return rc;
476
477 return EOK;
478}
479
480/** Display configuration events are pending.
481 *
482 * @param dispcfg Display configuration
483 * @param icall Call data
484 */
485static void dispcfg_ev_pending(dispcfg_t *dispcfg, ipc_call_t *icall)
486{
487 errno_t rc;
488 dispcfg_ev_t event;
489
490 while (true) {
491 fibril_mutex_lock(&dispcfg->lock);
492
493 if (dispcfg->sess != NULL)
494 rc = dispcfg_get_event(dispcfg, &event);
495 else
496 rc = ENOENT;
497
498 fibril_mutex_unlock(&dispcfg->lock);
499
500 if (rc != EOK)
501 break;
502
503 switch (event.etype) {
504 case dcev_seat_added:
505 if (dispcfg->cb != NULL &&
506 dispcfg->cb->seat_added != NULL) {
507 dispcfg->cb->seat_added(dispcfg->cb_arg,
508 event.seat_id);
509 }
510 break;
511 case dcev_seat_removed:
512 if (dispcfg->cb != NULL &&
513 dispcfg->cb->seat_removed != NULL) {
514 dispcfg->cb->seat_removed(dispcfg->cb_arg,
515 event.seat_id);
516 }
517 break;
518 }
519 }
520
521 async_answer_0(icall, EOK);
522}
523
524/** Callback connection handler.
525 *
526 * @param icall Connect call data
527 * @param arg Argument, dispcfg_t *
528 */
529static void dispcfg_cb_conn(ipc_call_t *icall, void *arg)
530{
531 dispcfg_t *dispcfg = (dispcfg_t *) arg;
532
533 while (true) {
534 ipc_call_t call;
535 async_get_call(&call);
536
537 if (!ipc_get_imethod(&call)) {
538 /* Hangup */
539 async_answer_0(&call, EOK);
540 goto out;
541 }
542
543 switch (ipc_get_imethod(&call)) {
544 case DISPCFG_EV_PENDING:
545 dispcfg_ev_pending(dispcfg, &call);
546 break;
547 default:
548 async_answer_0(&call, ENOTSUP);
549 break;
550 }
551 }
552
553out:
554 fibril_mutex_lock(&dispcfg->lock);
555 dispcfg->cb_done = true;
556 fibril_mutex_unlock(&dispcfg->lock);
557 fibril_condvar_broadcast(&dispcfg->cv);
558}
559
560/** @}
561 */
Note: See TracBrowser for help on using the repository browser.