Changes in uspace/lib/ui/src/ui.c [bb14312:26edcd6] in mainline


Ignore:
File:
1 edited

Legend:

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

    rbb14312 r26edcd6  
    11/*
    2  * Copyright (c) 2021 Jiri Svoboda
     2 * Copyright (c) 2026 Jiri Svoboda
    33 * All rights reserved.
    44 *
     
    3939#include <errno.h>
    4040#include <fibril.h>
     41#include <fibril_synch.h>
     42#include <gfx/color.h>
     43#include <gfx/cursor.h>
     44#include <gfx/render.h>
    4145#include <io/console.h>
    4246#include <stdbool.h>
     
    4448#include <str.h>
    4549#include <task.h>
     50#include <types/common.h>
     51#include <ui/clickmatic.h>
    4652#include <ui/ui.h>
    4753#include <ui/wdecor.h>
    4854#include <ui/window.h>
     55#include "../private/wdecor.h"
    4956#include "../private/window.h"
    5057#include "../private/ui.h"
     
    5360 *
    5461 * Output specification has the form <proto>@<service> where proto is
    55  * eiher 'disp' for display service or 'cons' for console. Service
    56  * is a location ID service name (e.g. hid/display).
     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).
    5764 *
    5865 * @param ospec Output specification
    5966 * @param ws Place to store window system type (protocol)
    6067 * @param osvc Place to store pointer to output service name
    61  */
    62 static void ui_ospec_parse(const char *ospec, ui_winsys_t *ws,
    63     const char **osvc)
     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)
    6474{
    6575        const char *cp;
    66 
    67         if (ospec == UI_DISPLAY_DEFAULT) {
    68                 *ws = ui_ws_display;
    69                 *osvc = DISPLAY_DEFAULT;
    70                 return;
    71         }
     76        const char *qm;
     77        const char *endptr;
     78        uint64_t idev_id;
     79        errno_t rc;
     80
     81        *ridev_id = 0;
    7282
    7383        cp = ospec;
     
    7585                ++cp;
    7686
     87        /* Window system / protocol */
    7788        if (*cp == '@') {
    7889                if (str_lcmp(ospec, "disp@", str_length("disp@")) == 0) {
     
    8091                } else if (str_lcmp(ospec, "cons@", str_length("cons@")) == 0) {
    8192                        *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;
    8297                } else {
    8398                        *ws = ui_ws_unknown;
    8499                }
    85100
    86                 if (cp[1] != '\0')
    87                         *osvc = cp + 1;
    88                 else
    89                         *osvc = NULL;
     101                ++cp;
    90102        } else {
    91103                *ws = ui_ws_display;
    92                 *osvc = ospec;
    93         }
     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;
    94145}
    95146
     
    97148 *
    98149 * @param ospec Output specification or @c UI_DISPLAY_DEFAULT to use
    99  *              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.
    100153 * @param rui Place to store pointer to new UI
    101154 * @return EOK on success or an error code
     
    108161        console_gc_t *cgc;
    109162        ui_winsys_t ws;
    110         const char *osvc;
     163        char *osvc;
     164        sysarg_t cols;
     165        sysarg_t rows;
     166        sysarg_t idev_id;
    111167        ui_t *ui;
    112168
    113         ui_ospec_parse(ospec, &ws, &osvc);
    114 
    115         if (ws == ui_ws_display) {
    116                 rc = display_open(osvc, &display);
     169        rc = ui_ospec_parse(ospec, &ws, &osvc, &idev_id);
     170        if (rc != EOK)
     171                return rc;
     172
     173        if (ws == ui_ws_display || ws == ui_ws_any) {
     174                rc = display_open((str_cmp(osvc, "") != 0) ? osvc :
     175                    DISPLAY_DEFAULT, &display);
    117176                if (rc != EOK)
    118                         return rc;
     177                        goto disp_fail;
    119178
    120179                rc = ui_create_disp(display, &ui);
    121180                if (rc != EOK) {
    122181                        display_close(display);
    123                         return rc;
    124                 }
    125         } else if (ws == ui_ws_console) {
     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) {
    126194                console = console_init(stdin, stdout);
    127195                if (console == NULL)
    128                         return EIO;
     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                }
    129203
    130204                console_cursor_visibility(console, false);
     
    134208                if (rc != EOK) {
    135209                        console_done(console);
    136                         return rc;
     210                        goto cons_fail;
    137211                }
    138212
     
    141215                        ui_destroy(ui);
    142216                        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)
    143239                        return rc;
    144                 }
    145 
    146                 ui->cgc = cgc;
    147         } else {
    148                 return EINVAL;
    149         }
    150 
    151         ui->myoutput = true;
    152         *rui = ui;
    153         return EOK;
     240
     241                ui->myoutput = true;
     242                *rui = ui;
     243                return EOK;
     244        }
     245
     246        free(osvc);
     247        return EINVAL;
    154248}
    155249
     
    162256{
    163257        ui_t *ui;
     258        errno_t rc;
    164259
    165260        ui = calloc(1, sizeof(ui_t));
     
    167262                return ENOMEM;
    168263
     264        rc = ui_clickmatic_create(ui, &ui->clickmatic);
     265        if (rc != EOK) {
     266                free(ui);
     267                return rc;
     268        }
     269
    169270        ui->console = console;
    170271        list_initialize(&ui->windows);
     272        fibril_mutex_initialize(&ui->lock);
    171273        *rui = ui;
    172274        return EOK;
     
    182284{
    183285        ui_t *ui;
     286        errno_t rc;
    184287
    185288        ui = calloc(1, sizeof(ui_t));
     
    187290                return ENOMEM;
    188291
     292        rc = ui_clickmatic_create(ui, &ui->clickmatic);
     293        if (rc != EOK) {
     294                free(ui);
     295                return rc;
     296        }
     297
    189298        ui->display = disp;
    190299        list_initialize(&ui->windows);
     300        fibril_mutex_initialize(&ui->lock);
    191301        *rui = ui;
    192302        return EOK;
     
    216326}
    217327
     328/** Resize UI after screen size change.
     329 *
     330 * @param ui UI
     331 * @param rect New screen rectangle.
     332 */
     333static void ui_resize(ui_t *ui, gfx_rect_t *rect)
     334{
     335        ui_window_t *wnd;
     336
     337        ui->rect = *rect;
     338
     339        /* Reposition/resize windows */
     340        wnd = ui_window_first(ui);
     341        while (wnd != NULL) {
     342                ui_window_update_placement(wnd);
     343                wnd = ui_window_next(wnd);
     344        }
     345
     346        /*
     347         * XXX Resizing cleared console GC so we need to repaint the
     348         * background.
     349         */
     350        (void)ui_paint(ui);
     351}
     352
    218353static void ui_cons_event_process(ui_t *ui, cons_event_t *event)
    219354{
    220355        ui_window_t *awnd;
    221356        ui_evclaim_t claim;
     357        pos_event_t pos;
     358        sysarg_t cols, rows;
     359        gfx_rect_t rect;
     360        errno_t rc;
    222361
    223362        awnd = ui_window_get_active(ui);
     
    227366        switch (event->type) {
    228367        case CEV_KEY:
     368                ui_lock(ui);
    229369                ui_window_send_kbd(awnd, &event->ev.key);
     370                ui_unlock(ui);
    230371                break;
    231372        case CEV_POS:
    232                 claim = ui_wdecor_pos_event(awnd->wdecor, &event->ev.pos);
     373                pos = event->ev.pos;
     374                /* Translate event to window-relative coordinates */
     375                pos.hpos -= awnd->dpos.x;
     376                pos.vpos -= awnd->dpos.y;
     377
     378                claim = ui_wdecor_pos_event(awnd->wdecor, &pos);
    233379                /* Note: If event is claimed, awnd might not be valid anymore */
    234                 if (claim == ui_unclaimed)
    235                         ui_window_send_pos(awnd, &event->ev.pos);
     380                if (claim == ui_unclaimed) {
     381                        ui_lock(ui);
     382                        ui_window_send_pos(awnd, &pos);
     383                        ui_unlock(ui);
     384                }
     385
     386                break;
     387        case CEV_RESIZE:
     388                rc = console_gc_resize(ui->cgc);
     389                if (rc != EOK) {
     390                        /* XXX No good way to recover. */
     391                        console_done(ui->console);
     392                        exit(1);
     393                }
     394
     395                rc = console_get_size(ui->console, &cols, &rows);
     396                if (rc != EOK) {
     397                        /* XXX No good way to recover. */
     398                        console_done(ui->console);
     399                        exit(1);
     400                }
     401
     402                ui_lock(ui);
     403
     404                rect.p0.x = 0;
     405                rect.p0.y = 0;
     406                rect.p1.x = cols;
     407                rect.p1.y = rows;
     408
     409                ui_resize(ui, &rect);
     410                ui_unlock(ui);
    236411                break;
    237412        }
     
    284459{
    285460        errno_t rc;
     461        gfx_context_t *gc;
     462        ui_window_t *wnd;
     463        gfx_color_t *color = NULL;
     464
     465        /* In case of null output */
     466        if (ui->cgc == NULL)
     467                return EOK;
     468
     469        gc = console_gc_get_ctx(ui->cgc);
     470
     471        rc = gfx_color_new_ega(0x11, &color);
     472        if (rc != EOK)
     473                return rc;
     474
     475        rc = gfx_set_color(gc, color);
     476        if (rc != EOK) {
     477                gfx_color_delete(color);
     478                return rc;
     479        }
     480
     481        rc = gfx_fill_rect(gc, &ui->rect);
     482        if (rc != EOK) {
     483                gfx_color_delete(color);
     484                return rc;
     485        }
     486
     487        gfx_color_delete(color);
     488
     489        /* Repaint all windows */
     490        wnd = ui_window_first(ui);
     491        while (wnd != NULL) {
     492                rc = ui_wdecor_paint(wnd->wdecor);
     493                if (rc != EOK)
     494                        return rc;
     495
     496                rc = ui_window_paint(wnd);
     497                if (rc != EOK)
     498                        return rc;
     499
     500                wnd = ui_window_next(wnd);
     501        }
     502
     503        return EOK;
     504}
     505
     506/** Free up console for other users.
     507 *
     508 * Release console resources for another application (that the current
     509 * task is starting). After the other application finishes, resume
     510 * operation with ui_resume(). No calls to UI must happen inbetween
     511 * and no events must be processed (i.e. the calling function must not
     512 * return control to UI.
     513 *
     514 * @param ui UI
     515 * @return EOK on success or an error code
     516 */
     517errno_t ui_suspend(ui_t *ui)
     518{
     519        errno_t rc;
     520
     521        assert(!ui->suspended);
     522
     523        if (ui->cgc == NULL) {
     524                ui->suspended = true;
     525                return EOK;
     526        }
     527
     528        (void) console_set_caption(ui->console, "");
     529        rc = console_gc_suspend(ui->cgc);
     530        if (rc != EOK)
     531                return rc;
     532
     533        ui->suspended = true;
     534        return EOK;
     535}
     536
     537/** Resume suspended UI.
     538 *
     539 * Reclaim console resources (after child application has finished running)
     540 * and restore UI operation previously suspended by calling ui_suspend().
     541 *
     542 * @param ui UI
     543 * @return EOK on success or an error code
     544 */
     545errno_t ui_resume(ui_t *ui)
     546{
     547        errno_t rc;
    286548        ui_window_t *awnd;
    287 
    288         /* XXX Should repaint all windows */
     549        sysarg_t col;
     550        sysarg_t row;
     551        cons_event_t ev;
     552
     553        assert(ui->suspended);
     554
     555        if (ui->cgc == NULL) {
     556                ui->suspended = false;
     557                return EOK;
     558        }
     559
     560        rc = console_get_pos(ui->console, &col, &row);
     561        if (rc != EOK)
     562                return rc;
     563
     564        /*
     565         * Here's a little heuristic to help determine if we need
     566         * to pause before returning to the UI. If we are in the
     567         * top-left corner, chances are the screen is empty and
     568         * there is no need to pause.
     569         */
     570        if (col != 0 || row != 0) {
     571                printf("Press any key or button to continue...\n");
     572
     573                while (true) {
     574                        rc = console_get_event(ui->console, &ev);
     575                        if (rc != EOK)
     576                                return EIO;
     577
     578                        if (ev.type == CEV_KEY && ev.ev.key.type == KEY_PRESS)
     579                                break;
     580
     581                        if (ev.type == CEV_POS && ev.ev.pos.type == POS_PRESS)
     582                                break;
     583                }
     584        }
     585
     586        rc = console_gc_resume(ui->cgc);
     587        if (rc != EOK)
     588                return rc;
     589
     590        ui->suspended = false;
     591
    289592        awnd = ui_window_get_active(ui);
    290         if (awnd == NULL)
    291                 return EOK;
    292 
    293         rc = ui_wdecor_paint(awnd->wdecor);
     593        if (awnd != NULL)
     594                (void) console_set_caption(ui->console, awnd->wdecor->caption);
     595
     596        rc = gfx_cursor_set_visible(console_gc_get_ctx(ui->cgc), false);
    294597        if (rc != EOK)
    295598                return rc;
    296599
    297         return ui_window_paint(awnd);
     600        return EOK;
     601}
     602
     603/** Determine if UI is suspended.
     604 *
     605 * @param ui UI
     606 * @return @c true iff UI is suspended
     607 */
     608bool ui_is_suspended(ui_t *ui)
     609{
     610        return ui->suspended;
     611}
     612
     613/** Lock UI.
     614 *
     615 * Block UI from calling window callbacks. @c ui_lock() and @c ui_unlock()
     616 * must be used when accessing UI resources from a fibril (as opposed to
     617 * from a window callback).
     618 *
     619 * @param ui UI
     620 */
     621void ui_lock(ui_t *ui)
     622{
     623        if (ui->display != NULL)
     624                display_lock(ui->display);
     625        fibril_mutex_lock(&ui->lock);
     626}
     627
     628/** Unlock UI.
     629 *
     630 * Allow UI to call window callbacks. @c ui_lock() and @c ui_unlock()
     631 * must be used when accessing window resources from a fibril (as opposed to
     632 * from a window callback).
     633 *
     634 * @param ui UI
     635 */
     636void ui_unlock(ui_t *ui)
     637{
     638        fibril_mutex_unlock(&ui->lock);
     639        if (ui->display != NULL)
     640                display_unlock(ui->display);
    298641}
    299642
     
    335678}
    336679
     680/** Get UI screen rectangle.
     681 *
     682 * @param ui User interface
     683 * @param rect Place to store bounding rectangle
     684 */
     685errno_t ui_get_rect(ui_t *ui, gfx_rect_t *rect)
     686{
     687        display_info_t info;
     688        sysarg_t cols, rows;
     689        errno_t rc;
     690
     691        if (ui->display != NULL) {
     692                rc = display_get_info(ui->display, &info);
     693                if (rc != EOK)
     694                        return rc;
     695
     696                *rect = info.rect;
     697        } else if (ui->console != NULL) {
     698                rc = console_get_size(ui->console, &cols, &rows);
     699                if (rc != EOK)
     700                        return rc;
     701
     702                rect->p0.x = 0;
     703                rect->p0.y = 0;
     704                rect->p1.x = cols;
     705                rect->p1.y = rows;
     706        } else {
     707                return ENOTSUP;
     708        }
     709
     710        return EOK;
     711}
     712
     713/** Get clickmatic from UI.
     714 *
     715 * @pararm ui UI
     716 * @return Clickmatic
     717 */
     718ui_clickmatic_t *ui_get_clickmatic(ui_t *ui)
     719{
     720        return ui->clickmatic;
     721}
     722
    337723/** @}
    338724 */
Note: See TracChangeset for help on using the changeset viewer.