Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/lib/ui/src/ui.c

    rd55ab823 r8279aab  
    11/*
    2  * Copyright (c) 2020 Jiri Svoboda
     2 * Copyright (c) 2025 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3434 */
    3535
     36#include <adt/list.h>
     37#include <ctype.h>
    3638#include <display.h>
    3739#include <errno.h>
    3840#include <fibril.h>
     41#include <fibril_synch.h>
     42#include <gfx/color.h>
     43#include <gfx/cursor.h>
     44#include <gfx/render.h>
     45#include <io/console.h>
     46#include <stdbool.h>
    3947#include <stdlib.h>
     48#include <str.h>
    4049#include <task.h>
     50#include <types/common.h>
     51#include <ui/clickmatic.h>
    4152#include <ui/ui.h>
     53#include <ui/wdecor.h>
     54#include <ui/window.h>
     55#include "../private/wdecor.h"
     56#include "../private/window.h"
    4257#include "../private/ui.h"
    4358
     59/** Parse output specification.
     60 *
     61 * Output specification has the form <proto>@<service> where proto is
     62 * eiher 'disp' for display service, 'cons' for console, 'null'
     63 * for dummy output. Service is a location ID service name (e.g. hid/display).
     64 *
     65 * @param ospec Output specification
     66 * @param ws Place to store window system type (protocol)
     67 * @param osvc Place to store pointer to output service name
     68 * @param ridev_id Place to store input device ID
     69 * @return EOK on success, EINVAL if syntax is invalid, ENOMEM if out of
     70 *         memory
     71 */
     72static errno_t ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
     73    char **osvc, sysarg_t *ridev_id)
     74{
     75        const char *cp;
     76        const char *qm;
     77        const char *endptr;
     78        uint64_t idev_id;
     79        errno_t rc;
     80
     81        *ridev_id = 0;
     82
     83        cp = ospec;
     84        while (isalpha(*cp))
     85                ++cp;
     86
     87        /* Window system / protocol */
     88        if (*cp == '@') {
     89                if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
     90                        *ws = ui_ws_display;
     91                } else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) {
     92                        *ws = ui_ws_console;
     93                } else if (str_lcmp(ospec, "null@", str_length("null@")) == 0) {
     94                        *ws = ui_ws_null;
     95                } else if (str_lcmp(ospec, "@", str_length("@")) == 0) {
     96                        *ws = ui_ws_any;
     97                } else {
     98                        *ws = ui_ws_unknown;
     99                }
     100
     101                ++cp;
     102        } else {
     103                *ws = ui_ws_display;
     104        }
     105
     106        /* Output service is the part before question mark */
     107        qm = str_chr(cp, '?');
     108        if (qm != NULL) {
     109                *osvc = str_ndup(cp, qm - cp);
     110        } else {
     111                /* No question mark */
     112                *osvc = str_dup(cp);
     113        }
     114
     115        if (*osvc == NULL)
     116                return ENOMEM;
     117
     118        if (qm != NULL) {
     119                /* The part after the question mark */
     120                cp = qm + 1;
     121
     122                /* Input device ID parameter */
     123                if (str_lcmp(cp, "idev=", str_length("idev=")) == 0) {
     124                        cp += str_length("idev=");
     125
     126                        rc = str_uint64_t(cp, &endptr, 10, false, &idev_id);
     127                        if (rc != EOK)
     128                                goto error;
     129
     130                        *ridev_id = idev_id;
     131                        cp = endptr;
     132                }
     133        }
     134
     135        if (*cp != '\0') {
     136                rc = EINVAL;
     137                goto error;
     138        }
     139
     140        return EOK;
     141error:
     142        free(*osvc);
     143        *osvc = NULL;
     144        return rc;
     145}
     146
    44147/** Create new user interface.
    45148 *
    46149 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use
    47  *              the default output
     150 *              the default display service, UI_CONSOLE_DEFAULT to use
     151 *              the default console service, UI_DISPLAY_NULL to use
     152 *              dummy output.
    48153 * @param rui Place to store pointer to new UI
    49154 * @return EOK on success or an error code
     
    53158        errno_t rc;
    54159        display_t *display;
     160        console_ctrl_t *console;
     161        console_gc_t *cgc;
     162        ui_winsys_t ws;
     163        char *osvc;
     164        sysarg_t cols;
     165        sysarg_t rows;
     166        sysarg_t idev_id;
    55167        ui_t *ui;
    56168
    57         rc = display_open(ospec, &display);
     169        rc = ui_ospec_parse(ospec, &ws, &osvc, &idev_id);
    58170        if (rc != EOK)
    59171                return rc;
    60172
    61         rc = ui_create_disp(display, &ui);
     173        if (ws == ui_ws_display || ws == ui_ws_any) {
     174                rc = display_open((str_cmp(osvc, "") != 0) ? osvc :
     175                    DISPLAY_DEFAULT, &display);
     176                if (rc != EOK)
     177                        goto disp_fail;
     178
     179                rc = ui_create_disp(display, &ui);
     180                if (rc != EOK) {
     181                        display_close(display);
     182                        goto disp_fail;
     183                }
     184
     185                free(osvc);
     186                ui->myoutput = true;
     187                ui->idev_id = idev_id;
     188                *rui = ui;
     189                return EOK;
     190        }
     191
     192disp_fail:
     193        if (ws == ui_ws_console || ws == ui_ws_any) {
     194                console = console_init(stdin, stdout);
     195                if (console == NULL)
     196                        goto cons_fail;
     197
     198                rc = console_get_size(console, &cols, &rows);
     199                if (rc != EOK) {
     200                        console_done(console);
     201                        goto cons_fail;
     202                }
     203
     204                console_cursor_visibility(console, false);
     205
     206                /* ws == ui_ws_console */
     207                rc = ui_create_cons(console, &ui);
     208                if (rc != EOK) {
     209                        console_done(console);
     210                        goto cons_fail;
     211                }
     212
     213                rc = console_gc_create(console, NULL, &cgc);
     214                if (rc != EOK) {
     215                        ui_destroy(ui);
     216                        console_done(console);
     217                        goto cons_fail;
     218                }
     219
     220                free(osvc);
     221
     222                ui->cgc = cgc;
     223                ui->rect.p0.x = 0;
     224                ui->rect.p0.y = 0;
     225                ui->rect.p1.x = cols;
     226                ui->rect.p1.y = rows;
     227
     228                (void) ui_paint(ui);
     229                ui->myoutput = true;
     230                *rui = ui;
     231                return EOK;
     232        }
     233
     234cons_fail:
     235        if (ws == ui_ws_null) {
     236                free(osvc);
     237                rc = ui_create_disp(NULL, &ui);
     238                if (rc != EOK)
     239                        return rc;
     240
     241                ui->myoutput = true;
     242                *rui = ui;
     243                return EOK;
     244        }
     245
     246        free(osvc);
     247        return EINVAL;
     248}
     249
     250/** Create new user interface using console service.
     251 *
     252 * @param rui Place to store pointer to new UI
     253 * @return EOK on success or an error code
     254 */
     255errno_t ui_create_cons(console_ctrl_t *console, ui_t **rui)
     256{
     257        ui_t *ui;
     258        errno_t rc;
     259
     260        ui = calloc(1, sizeof(ui_t));
     261        if (ui == NULL)
     262                return ENOMEM;
     263
     264        rc = ui_clickmatic_create(ui, &ui->clickmatic);
    62265        if (rc != EOK) {
    63                 display_close(display);
    64                 return rc;
    65         }
    66 
    67         ui->display = display;
    68         ui->myoutput = true;
     266                free(ui);
     267                return rc;
     268        }
     269
     270        ui->console = console;
     271        list_initialize(&ui->windows);
     272        fibril_mutex_initialize(&ui->lock);
    69273        *rui = ui;
    70274        return EOK;
     
    80284{
    81285        ui_t *ui;
     286        errno_t rc;
    82287
    83288        ui = calloc(1, sizeof(ui_t));
     
    85290                return ENOMEM;
    86291
     292        rc = ui_clickmatic_create(ui, &ui->clickmatic);
     293        if (rc != EOK) {
     294                free(ui);
     295                return rc;
     296        }
     297
    87298        ui->display = disp;
     299        list_initialize(&ui->windows);
     300        fibril_mutex_initialize(&ui->lock);
    88301        *rui = ui;
    89302        return EOK;
     
    99312                return;
    100313
    101         if (ui->myoutput)
    102                 display_close(ui->display);
     314        if (ui->myoutput) {
     315                if (ui->cgc != NULL)
     316                        console_gc_delete(ui->cgc);
     317                if (ui->console != NULL) {
     318                        console_cursor_visibility(ui->console, true);
     319                        console_done(ui->console);
     320                }
     321                if (ui->display != NULL)
     322                        display_close(ui->display);
     323        }
     324
    103325        free(ui);
     326}
     327
     328static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
     329{
     330        ui_window_t *awnd;
     331        ui_evclaim_t claim;
     332        pos_event_t pos;
     333
     334        awnd = ui_window_get_active(ui);
     335        if (awnd == NULL)
     336                return;
     337
     338        switch (event->type) {
     339        case CEV_KEY:
     340                ui_lock(ui);
     341                ui_window_send_kbd(awnd, &event->ev.key);
     342                ui_unlock(ui);
     343                break;
     344        case CEV_POS:
     345                pos = event->ev.pos;
     346                /* Translate event to window-relative coordinates */
     347                pos.hpos -= awnd->dpos.x;
     348                pos.vpos -= awnd->dpos.y;
     349
     350                claim = ui_wdecor_pos_event(awnd->wdecor, &pos);
     351                /* Note: If event is claimed, awnd might not be valid anymore */
     352                if (claim == ui_unclaimed) {
     353                        ui_lock(ui);
     354                        ui_window_send_pos(awnd, &pos);
     355                        ui_unlock(ui);
     356                }
     357
     358                break;
     359        case CEV_RESIZE:
     360                ui_lock(ui);
     361                ui_window_send_resize(awnd);
     362                ui_unlock(ui);
     363                break;
     364        }
    104365}
    105366
     
    113374void ui_run(ui_t *ui)
    114375{
    115         task_retval(0);
    116 
    117         while (!ui->quit)
    118                 fibril_usleep(100000);
     376        cons_event_t event;
     377        usec_t timeout;
     378        errno_t rc;
     379
     380        /* Only return command prompt if we are running in a separate window */
     381        if (ui->display != NULL)
     382                task_retval(0);
     383
     384        while (!ui->quit) {
     385                if (ui->console != NULL) {
     386                        timeout = 100000;
     387                        rc = console_get_event_timeout(ui->console,
     388                            &event, &timeout);
     389
     390                        /* Do we actually have an event? */
     391                        if (rc == EOK) {
     392                                ui_cons_event_process(ui, &event);
     393                        } else if (rc != ETIMEOUT) {
     394                                /* Error, quit */
     395                                break;
     396                        }
     397                } else {
     398                        fibril_usleep(100000);
     399                }
     400        }
     401}
     402
     403/** Repaint UI (only used in fullscreen mode).
     404 *
     405 * This is used when an area is exposed in fullscreen mode.
     406 *
     407 * @param ui UI
     408 * @return @c EOK on success or an error code
     409 */
     410errno_t ui_paint(ui_t *ui)
     411{
     412        errno_t rc;
     413        gfx_context_t *gc;
     414        ui_window_t *awnd;
     415        gfx_color_t *color = NULL;
     416
     417        /* In case of null output */
     418        if (ui->cgc == NULL)
     419                return EOK;
     420
     421        gc = console_gc_get_ctx(ui->cgc);
     422
     423        rc = gfx_color_new_ega(0x11, &color);
     424        if (rc != EOK)
     425                return rc;
     426
     427        rc = gfx_set_color(gc, color);
     428        if (rc != EOK) {
     429                gfx_color_delete(color);
     430                return rc;
     431        }
     432
     433        rc = gfx_fill_rect(gc, &ui->rect);
     434        if (rc != EOK) {
     435                gfx_color_delete(color);
     436                return rc;
     437        }
     438
     439        gfx_color_delete(color);
     440
     441        /* XXX Should repaint all windows */
     442        awnd = ui_window_get_active(ui);
     443        if (awnd == NULL)
     444                return EOK;
     445
     446        rc = ui_wdecor_paint(awnd->wdecor);
     447        if (rc != EOK)
     448                return rc;
     449
     450        return ui_window_paint(awnd);
     451}
     452
     453/** Free up console for other users.
     454 *
     455 * Release console resources for another application (that the current
     456 * task is starting). After the other application finishes, resume
     457 * operation with ui_resume(). No calls to UI must happen inbetween
     458 * and no events must be processed (i.e. the calling function must not
     459 * return control to UI.
     460 *
     461 * @param ui UI
     462 * @return EOK on success or an error code
     463 */
     464errno_t ui_suspend(ui_t *ui)
     465{
     466        errno_t rc;
     467
     468        assert(!ui->suspended);
     469
     470        if (ui->cgc == NULL) {
     471                ui->suspended = true;
     472                return EOK;
     473        }
     474
     475        (void) console_set_caption(ui->console, "");
     476        rc = console_gc_suspend(ui->cgc);
     477        if (rc != EOK)
     478                return rc;
     479
     480        ui->suspended = true;
     481        return EOK;
     482}
     483
     484/** Resume suspended UI.
     485 *
     486 * Reclaim console resources (after child application has finished running)
     487 * and restore UI operation previously suspended by calling ui_suspend().
     488 *
     489 * @param ui UI
     490 * @return EOK on success or an error code
     491 */
     492errno_t ui_resume(ui_t *ui)
     493{
     494        errno_t rc;
     495        ui_window_t *awnd;
     496        sysarg_t col;
     497        sysarg_t row;
     498        cons_event_t ev;
     499
     500        assert(ui->suspended);
     501
     502        if (ui->cgc == NULL) {
     503                ui->suspended = false;
     504                return EOK;
     505        }
     506
     507        rc = console_get_pos(ui->console, &col, &row);
     508        if (rc != EOK)
     509                return rc;
     510
     511        /*
     512         * Here's a little heuristic to help determine if we need
     513         * to pause before returning to the UI. If we are in the
     514         * top-left corner, chances are the screen is empty and
     515         * there is no need to pause.
     516         */
     517        if (col != 0 || row != 0) {
     518                printf("Press any key or button to continue...\n");
     519
     520                while (true) {
     521                        rc = console_get_event(ui->console, &ev);
     522                        if (rc != EOK)
     523                                return EIO;
     524
     525                        if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
     526                                break;
     527
     528                        if (ev.type == CEV_POS && ev.ev.pos.type == POS_PRESS)
     529                                break;
     530                }
     531        }
     532
     533        rc = console_gc_resume(ui->cgc);
     534        if (rc != EOK)
     535                return rc;
     536
     537        ui->suspended = false;
     538
     539        awnd = ui_window_get_active(ui);
     540        if (awnd != NULL)
     541                (void) console_set_caption(ui->console, awnd->wdecor->caption);
     542
     543        rc = gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
     544        if (rc != EOK)
     545                return rc;
     546
     547        return EOK;
     548}
     549
     550/** Determine if UI is suspended.
     551 *
     552 * @param ui UI
     553 * @return @c true iff UI is suspended
     554 */
     555bool ui_is_suspended(ui_t *ui)
     556{
     557        return ui->suspended;
     558}
     559
     560/** Lock UI.
     561 *
     562 * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock()
     563 * must be used when accessing UI resources from a fibril (as opposed to
     564 * from a window callback).
     565 *
     566 * @param ui UI
     567 */
     568void ui_lock(ui_t *ui)
     569{
     570        if (ui->display != NULL)
     571                display_lock(ui->display);
     572        fibril_mutex_lock(&ui->lock);
     573}
     574
     575/** Unlock UI.
     576 *
     577 * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock()
     578 * must be used when accessing window resources from a fibril (as opposed to
     579 * from a window callback).
     580 *
     581 * @param ui UI
     582 */
     583void ui_unlock(ui_t *ui)
     584{
     585        fibril_mutex_unlock(&ui->lock);
     586        if (ui->display != NULL)
     587                display_unlock(ui->display);
    119588}
    120589
     
    132601}
    133602
     603/** Determine if we are running in text mode.
     604 *
     605 * @param ui User interface
     606 * @return @c true iff we are running in text mode
     607 */
     608bool ui_is_textmode(ui_t *ui)
     609{
     610        /*
     611         * XXX Currently console is always text and display is always
     612         * graphics, but this need not always be true.
     613         */
     614        return (ui->console != NULL);
     615}
     616
     617/** Determine if we are emulating windows.
     618 *
     619 * @param ui User interface
     620 * @return @c true iff we are running in text mode
     621 */
     622bool ui_is_fullscreen(ui_t *ui)
     623{
     624        return (ui->display == NULL);
     625}
     626
     627/** Get UI screen rectangle.
     628 *
     629 * @param ui User interface
     630 * @param rect Place to store bounding rectangle
     631 */
     632errno_t ui_get_rect(ui_t *ui, gfx_rect_t *rect)
     633{
     634        display_info_t info;
     635        sysarg_t cols, rows;
     636        errno_t rc;
     637
     638        if (ui->display != NULL) {
     639                rc = display_get_info(ui->display, &info);
     640                if (rc != EOK)
     641                        return rc;
     642
     643                *rect = info.rect;
     644        } else if (ui->console != NULL) {
     645                rc = console_get_size(ui->console, &cols, &rows);
     646                if (rc != EOK)
     647                        return rc;
     648
     649                rect->p0.x = 0;
     650                rect->p0.y = 0;
     651                rect->p1.x = cols;
     652                rect->p1.y = rows;
     653        } else {
     654                return ENOTSUP;
     655        }
     656
     657        return EOK;
     658}
     659
     660/** Get clickmatic from UI.
     661 *
     662 * @pararm ui UI
     663 * @return Clickmatic
     664 */
     665ui_clickmatic_t *ui_get_clickmatic(ui_t *ui)
     666{
     667        return ui->clickmatic;
     668}
     669
    134670/** @}
    135671 */
Note: See TracChangeset for help on using the changeset viewer.