| /**************************************************************************** |
| * apps/examples/fbcon/fbcon_main.c |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The |
| * ASF licenses this file to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance with the |
| * License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations |
| * under the License. |
| * |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Included Files |
| ****************************************************************************/ |
| |
| #include <nuttx/config.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/boardctl.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <errno.h> |
| #include <assert.h> |
| #include <fcntl.h> |
| #include <nuttx/video/fb.h> |
| #include <nuttx/nx/nxfonts.h> |
| #include <nuttx/video/fb.h> |
| #include <ctype.h> |
| #include <spawn.h> |
| #include <debug.h> |
| #include <poll.h> |
| #include <nuttx/ascii.h> |
| #include <nuttx/lib/builtin.h> |
| |
| #ifndef CONFIG_NX |
| # error This application requires NX Graphics |
| #endif |
| |
| /* NSH Redirection requires Pipes */ |
| |
| #ifndef CONFIG_DEV_PIPE_SIZE |
| # error FIFO and Named Pipe Drivers should be enabled in the configuration |
| #endif |
| |
| #ifdef CONFIG_NSH_CLE |
| # warning FBCON console does not support much VT100/EMACS/CLE type functionality yet |
| #endif |
| |
| #if !defined(CONFIG_EXAMPLES_FBCON_PIPE_STDOUT) && \ |
| !defined(CONFIG_EXAMPLES_FBCON_PIPE_STDOUT) |
| # warning FBCON is not configured for either stdout or stderr! |
| #endif |
| |
| /**************************************************************************** |
| * Pre-processor Definitions |
| ****************************************************************************/ |
| |
| /* stdout and stderr configuration */ |
| |
| #define READ_PIPE 0 |
| #define WRITE_PIPE 1 |
| #define POLL_BUFSIZE 64 |
| #define VT100_MAX_SEQUENCE 6 /* Max length of VT100 sequence to check */ |
| |
| /* VT100 codes that can be decoded */ |
| |
| #define VT100_CLEAREOL {ASCII_ESC, '[', 'K'} /* Clear line from cursor right */ |
| #define VT100_CLEARLINE {ASCII_ESC, '[', '2', 'K'} /* Clear entire line */ |
| #define VT100_CURSOROFF {ASCII_ESC, '[', '?', '2', '5', 'l'} /* Cursor OFF */ |
| #define VT100_CURSORON {ASCII_ESC, '[', '?', '2', '5', 'h'} /* Cursor ON */ |
| #define VT100_CURSORL {ASCII_ESC, '[', '*', 'D'} /* Move cursor left (* = [1...99]) columns */ |
| |
| /* Configuration ************************************************************/ |
| |
| /* Pixel depth. If none provided, pick the smallest enabled pixel depth */ |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_BPP_NX_DEFAULT |
| # if !defined(CONFIG_NX_DISABLE_1BPP) |
| # define FBCON_BPP 1 |
| # elif !defined(CONFIG_NX_DISABLE_2BPP) |
| # define FBCON_BPP 2 |
| # elif !defined(CONFIG_NX_DISABLE_4BPP) |
| # define FBCON_BPP 4 |
| # elif !defined(CONFIG_NX_DISABLE_8BPP) |
| # define FBCON_BPP 8 |
| # elif !defined(CONFIG_NX_DISABLE_16BPP) |
| # define FBCON_BPP 16 |
| # elif !defined(CONFIG_NX_DISABLE_24BPP) |
| # define FBCON_BPP 24 |
| # elif !defined(CONFIG_NX_DISABLE_32BPP) |
| # define FBCON_BPP 32 |
| # else |
| # error "No pixel depth enabled" |
| # endif |
| #elif defined(CONFIG_EXAMPLES_FBCON_1BPP) |
| # define FBCON_BPP 1 |
| #elif defined(CONFIG_EXAMPLES_FBCON_2BPP) |
| # define FBCON_BPP 2 |
| #elif defined(CONFIG_EXAMPLES_FBCON_4BPP) |
| # define FBCON_BPP 4 |
| #elif defined(CONFIG_EXAMPLES_FBCON_8BPP) |
| # define FBCON_BPP 8 |
| #elif defined(CONFIG_EXAMPLES_FBCON_16BPP) |
| # define FBCON_BPP 16 |
| #elif defined(CONFIG_EXAMPLES_FBCON_24BPP) |
| # define FBCON_BPP 24 |
| #elif defined(CONFIG_EXAMPLES_FBCON_32BPP) |
| # define FBCON_BPP 32 |
| #endif |
| |
| /* Select renderer */ |
| |
| #if (FBCON_BPP == 1) |
| # define RENDERER nxf_convert_1bpp |
| #elif (FBCON_BPP == 2) |
| # define RENDERER nxf_convert_2bpp |
| #elif (FBCON_BPP == 4) |
| # define RENDERER nxf_convert_4bpp |
| #elif (FBCON_BPP == 8) |
| # define RENDERER nxf_convert_8bpp |
| #elif (FBCON_BPP == 16) |
| # define RENDERER nxf_convert_16bpp |
| #elif (FBCON_BPP == 24) |
| # define RENDERER nxf_convert_24bpp |
| #elif (FBCON_BPP == 32) |
| # define RENDERER nxf_convert_32bpp |
| #else |
| # error "Unsupported CONFIG_EXAMPLES_FBCON_BPP" |
| #endif |
| |
| /* Background and font color, if defaults chosen */ |
| |
| #ifndef CONFIG_EXAMPLES_FBCON_BGCOLOR |
| # if (FBCON_BPP == 24) || (FBCON_BPP == 32) |
| # define FBCON_BGCOLOR 0x00000000 |
| # elif FBCON_BPP == 16 |
| # define FBCON_BGCOLOR 0x0000 |
| # else |
| # define FBCON_BGCOLOR 0x0 |
| # endif |
| #else |
| # define FBCON_BGCOLOR CONFIG_EXAMPLES_FBCON_BGCOLOR |
| #endif |
| |
| #ifndef CONFIG_EXAMPLES_FBCON_FCOLOR |
| # if (FBCON_BPP == 32) || (FBCON_BPP == 24) |
| # define FBCON_FCOLOR 0xffffff |
| # elif FBCON_BPP == 16 |
| # define FBCON_FCOLOR 0xffff |
| # else |
| # define FBCON_FCOLOR 0xff |
| # endif |
| #else |
| # define FBCON_FCOLOR CONFIG_EXAMPLES_FBCON_FCOLOR |
| #endif |
| |
| /* Console font ID */ |
| |
| #ifndef CONFIG_EXAMPLES_FBCON_FONTID |
| # ifndef NXFONT_DEFAULT |
| # error NXFONT_DEFAULT not defined |
| # endif |
| # define FBCON_FONTID NXFONT_DEFAULT |
| #else |
| # define FBCON_FONTID CONFIG_EXAMPLES_FBCON_FONTID |
| #endif |
| |
| /* Font glyph caching */ |
| |
| #ifndef CONFIG_EXAMPLES_FBCON_GLCACHE |
| # define FBCON_GLCACHE 16 |
| #else |
| # define FBCON_GLCACHE CONFIG_EXAMPLES_FBCON_GLCACHE |
| #endif |
| |
| /* Bitmap flags */ |
| |
| #define BMFLAGS_NOGLYPH (1 << 0) /* No glyph available, use space */ |
| #define BM_ISSPACE(bm) (((bm)->flags & BMFLAGS_NOGLYPH) != 0) |
| |
| /* Sizes and maximums */ |
| |
| #define MAX_USECNT 255 /* Limit to range of a uint8_t */ |
| |
| /* Line spacing. Space (in rows) between lines. */ |
| |
| #define FBCON_LINESPACING CONFIG_EXAMPLES_FBCON_LINESPACING |
| |
| /* Cursor character */ |
| |
| #define FBCON_CURSORCHAR CONFIG_EXAMPLES_FBCON_CURSORCHAR |
| |
| /* Spawn task */ |
| |
| #define SPAWN_TASK CONFIG_EXAMPLES_FBCON_SPAWN_TASK |
| |
| /**************************************************************************** |
| * Private Types |
| ****************************************************************************/ |
| |
| enum exitcode_e |
| { |
| FBCON_EXIT_SUCCESS = 0, |
| FBCON_EXIT_FAIL, |
| FBCON_EXIT_FD, |
| FBCON_EXIT_GETVINFO, |
| FBCON_EXIT_GETPINFO, |
| FBCON_EXIT_FBMEM, |
| FBCON_EXIT_FBMEM2, |
| FBCON_EXIT_FBIO_OVERLAY_INFO, |
| FBCON_EXIT_FBIO_SELECT_OVERLAY, |
| FBCON_EXIT_FONTOPEN, |
| FBCON_EXIT_SETBGCOLOR, |
| FBCON_EXIT_STDOUT_PIPE_FAILED, |
| FBCON_EXIT_STDERR_PIPE_FAILED, |
| FBCON_EXIT_STDIN_PIPE_FAILED, |
| FBCON_EXIT_LOCAL_STDERR_PIPE_FAILED, |
| FBCON_EXIT_LOCAL_STDOUT_PIPE_FAILED, |
| FBCON_EXIT_POSIX_SPAWN_ACTIONS_INIT_FAILED, |
| FBCON_EXIT_POSIX_SPAWN_FAILED, |
| FBCON_EXIT_APP_INDEX_UNAVAILABLE, |
| FBCON_EXIT_APP_POSIX_ATTRIB_INIT_FAILED, |
| }; |
| |
| /* Describes one cached glyph bitmap */ |
| |
| struct fbcon_glyph_s |
| { |
| uint8_t code; /* Character code */ |
| uint8_t height; /* Height of this glyph (in rows) */ |
| uint8_t width; /* Width of this glyph (in pixels) */ |
| uint8_t stride; /* Width of the glyph row (bytes) */ |
| uint8_t usecnt; /* Use count */ |
| FAR uint8_t *bitmap; /* Allocated bitmap memory */ |
| }; |
| |
| /* Describes on character on the display */ |
| |
| struct fbcon_bitmap_s |
| { |
| uint8_t code; /* Character code */ |
| uint8_t flags; /* See BMFLAGS_* */ |
| struct nxgl_point_s pos; /* Character position */ |
| }; |
| |
| /* Describes the state of one text display */ |
| |
| struct fbcon_state_s |
| { |
| int fd_fb; |
| FAR void *fbcon_font; |
| struct fb_videoinfo_s *vinfo; |
| struct fb_planeinfo_s *pinfo; |
| #ifdef CONFIG_FB_OVERLAY |
| struct fb_overlayinfo_s oinfo; |
| #endif |
| FAR void *fbmem; |
| #if 0 |
| /* Revisit needed - no support got dual framebuffers as yet */ |
| |
| FAR void *fbmem2; |
| FAR void *act_fbmem; |
| #endif |
| uint32_t mem2_yoffset; |
| |
| /* The following describe the console */ |
| |
| nxgl_mxpixel_t bcolor; /* Console background color */ |
| nxgl_mxpixel_t fcolor; /* Console font color */ |
| struct nxgl_size_s wsize; /* Console size */ |
| uint8_t fheight; /* Max height of a font in pixels */ |
| uint8_t fwidth; /* Max width of a font in pixels */ |
| uint8_t spwidth; /* The width of a space */ |
| |
| /* These describe all text already added to the display */ |
| |
| uint8_t maxglyphs; /* Size of the glyph[] array */ |
| uint16_t maxchars; /* Size of the bm[] array */ |
| uint32_t nchars; /* Numb of chars in the bm[] array */ |
| |
| FAR struct fbcon_bitmap_s *bm; /* List of bitmaps on the display */ |
| FAR struct fbcon_glyph_s *glyph; /* Cache of rendered fonts in use */ |
| FAR struct fbcon_bitmap_s cursor; /* Character to use for cursor */ |
| |
| /* VT100 escape sequence processing */ |
| |
| char seq[VT100_MAX_SEQUENCE]; /* Buffered chars */ |
| uint8_t nseq; /* Num buffered chars */ |
| int nwild; /* Num wild collected */ |
| int wildval; /* Wildcard value */ |
| }; |
| |
| typedef void (*seqhandler_t)(FAR struct fbcon_state_s *st); |
| |
| /* Identifies the state of the VT100 escape sequence processing */ |
| |
| enum fbcon_vt100state_e |
| { |
| VT100_NOT_CONSUMED = 0, /* Character is not part of a VT100 escape sequence */ |
| VT100_CONSUMED, /* Character was consumed as part of the VT100 escape processing */ |
| VT100_PROCESSED, /* The full VT100 escape sequence was processed */ |
| VT100_ABORT /* Invalid/unsupported character in buffered escape sequence */ |
| }; |
| |
| struct vt100_sequence_s |
| { |
| FAR const char *seq; |
| seqhandler_t handler; |
| uint8_t size; |
| }; |
| |
| /**************************************************************************** |
| * Private Function Prototypes |
| ****************************************************************************/ |
| #if 0 |
| /* Revisit needed = no support for dual framebuffers yet */ |
| |
| static int fb_init_mem2(FAR struct fbcon_state_s *st); |
| #endif |
| static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s *pinfo); |
| static int fbcon_initialize(FAR struct fbcon_state_s *st); |
| static void fbcon_newline(FAR struct fbcon_state_s *st); |
| static int fbcon_hidechar(FAR FAR struct fbcon_state_s *st, |
| FAR const struct fbcon_bitmap_s *bm); |
| static enum fbcon_vt100state_e fbcon_vt100(FAR struct fbcon_state_s *st, |
| char ch); |
| static void fbcon_fillchar(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *rect, |
| FAR const struct fbcon_bitmap_s *bm); |
| static int fbcon_fill(FAR struct fbcon_state_s *st, |
| FAR struct nxgl_rect_s *rect, |
| FAR nxgl_mxpixel_t *color); |
| static void fbcon_fillspace(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *rect, |
| FAR const struct fbcon_bitmap_s *bm); |
| static int fbcon_bitmap(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *dest, |
| FAR const uint32_t *src, |
| unsigned int stride); |
| static int fbcon_backspace(FAR struct fbcon_state_s *st); |
| static void fbcon_home(FAR struct fbcon_state_s *st); |
| static inline void fbcon_movedisplay(FAR struct fbcon_state_s *st, |
| int bottom, int scrollheight); |
| static inline void fbcon_scroll(FAR struct fbcon_state_s *st, |
| int scrollheight); |
| static void fbcon_freeglyph(FAR struct fbcon_glyph_s *glyph); |
| static inline FAR struct fbcon_glyph_s * |
| fbcon_allocglyph(FAR struct fbcon_state_s *st); |
| static FAR struct fbcon_glyph_s * |
| fbcon_findglyph(FAR struct fbcon_state_s *st, uint8_t ch); |
| static inline FAR struct fbcon_glyph_s * |
| fbcon_renderglyph(FAR struct fbcon_state_s *st, |
| FAR const struct nx_fontbitmap_s *fbm, uint8_t ch); |
| static int fbcon_fontsize(FAR void *hfont, uint8_t ch, |
| FAR struct nxgl_size_s *size); |
| static FAR struct fbcon_glyph_s * |
| fbcon_getglyph(FAR struct fbcon_state_s *st, uint8_t ch); |
| static FAR const struct fbcon_bitmap_s * |
| fbcon_addchar(FAR struct fbcon_state_s *st, uint8_t ch); |
| static void fbcon_write(FAR struct fbcon_state_s *st, |
| FAR char *buffer, size_t buflen); |
| static bool has_input(int fd); |
| static void poll_std_streams(FAR struct fbcon_state_s *st); |
| static void fbcon_putc(FAR struct fbcon_state_s *st, uint8_t ch); |
| static enum fbcon_vt100state_e fbcon_vt100seq( |
| FAR struct fbcon_state_s *st, int seqsize); |
| static FAR const struct vt100_sequence_s * |
| fbcon_vt100part(FAR struct fbcon_state_s *st, int seqsize); |
| static enum fbcon_vt100state_e fbcon_vt100(FAR struct fbcon_state_s *st, |
| char ch); |
| static void fbcon_erasetoeol(FAR struct fbcon_state_s *st); |
| static void fbcon_clearline(FAR struct fbcon_state_s *st); |
| static void fbcon_showcursor(FAR struct fbcon_state_s *st); |
| static void fbcon_hidecursor(FAR struct fbcon_state_s *st); |
| static void fbcon_cursorl(FAR struct fbcon_state_s *st); |
| |
| /**************************************************************************** |
| * Private Data |
| ****************************************************************************/ |
| |
| /* Pipes for NSH Shell: stdin, stdout, stderr */ |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDIN |
| static int g_nsh_stdin[2]; |
| #endif |
| static int g_nsh_stdout[2]; |
| static int g_nsh_stderr[2]; |
| |
| /* The VT100 sequences supported by FBCON */ |
| |
| static const char g_erasetoeol[] = VT100_CLEAREOL; |
| static const char g_clearline[] = VT100_CLEARLINE; |
| static const char g_cursoroff[] = VT100_CURSOROFF; |
| static const char g_cursoron[] = VT100_CURSORON; |
| static const char g_cursorl[] = VT100_CURSORL; |
| |
| static const struct vt100_sequence_s g_vt100sequences[] = |
| { |
| {g_erasetoeol, fbcon_erasetoeol, sizeof(g_erasetoeol)}, |
| {g_clearline, fbcon_clearline, sizeof(g_clearline)}, |
| {g_cursoroff, fbcon_hidecursor, sizeof(g_cursoroff)}, |
| {g_cursoron, fbcon_showcursor, sizeof(g_cursoroff)}, |
| {g_cursorl, fbcon_cursorl, sizeof(g_cursorl)}, |
| {NULL, NULL, 0} |
| }; |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_SHOW_WELCOME |
| # ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDOUT |
| static const char g_stdout_hello[] = "Hello FBCON stdout fprintf output!"; |
| # endif |
| # ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDERR |
| static const char g_stderr_hello[] = "Hello FBCON stderr fprintf output!"; |
| # endif |
| #endif |
| |
| /**************************************************************************** |
| * Public Data |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Private Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: fbcon_showcursor |
| * |
| * Description: |
| * Render the cursor character at the current display position. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_showcursor(FAR struct fbcon_state_s *st) |
| { |
| int lineheight; |
| |
| if ((st->cursor.pos.x + st->fwidth) > st->wsize.w) |
| { |
| #ifndef CONFIG_EXAMPLES_FBCON_NOWRAP |
| /* No.. move to the next line */ |
| |
| fbcon_newline(st); |
| #else |
| return OK; |
| #endif |
| } |
| |
| /* Check if we need to scroll up */ |
| |
| lineheight = st->fheight + FBCON_LINESPACING; |
| while (st->cursor.pos.y >= st->wsize.h + lineheight) |
| { |
| fbcon_scroll(st, lineheight); |
| } |
| |
| /* Render the cursor glyph onto the display. */ |
| |
| fbcon_fillchar(st, NULL, &st->cursor); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_hidecursor |
| * |
| * Description: |
| * Remove the cursor character at the current display position. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_hidecursor(FAR struct fbcon_state_s *st) |
| { |
| fbcon_hidechar(st, &st->cursor); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_cursorl |
| * |
| * Description: |
| * Move the cursor position to the left n characters. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_cursorl(FAR struct fbcon_state_s *st) |
| { |
| int i; |
| |
| /* Revisit needed. It seems backspace 1 less than calculated. |
| * This is illogical and yet to be explained and is perhaps related to |
| * nsh itself when nsh is the spawned app/task. |
| */ |
| |
| for (i = 1; i < st->wildval; i++) |
| { |
| fbcon_backspace(st); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_erasetoeol |
| * |
| * Description: |
| * Handle the erase-to-eol VT100 escape sequence. |
| * Erase from and including cursor position |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_erasetoeol(FAR struct fbcon_state_s *st) |
| { |
| struct nxgl_rect_s rect; |
| |
| /* Create a bounding box the size of the remaining iine */ |
| |
| rect.pt1.x = st->cursor.pos.x; |
| rect.pt2.x = st->wsize.w - 1; |
| rect.pt1.y = st->cursor.pos.y; |
| rect.pt2.y = rect.pt1.y + st->fheight + FBCON_LINESPACING - 1; |
| |
| /* Clear the region */ |
| |
| if (fbcon_fill(st, &rect, &st->bcolor) < 0) |
| { |
| gerr("ERROR: fbcon_fill failed: %d\n", errno); |
| } |
| |
| /* Because we were clearing from the cursor position, there's no need |
| * to modify st->nchar as there are no characters after the cursor. |
| */ |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_clearline |
| * |
| * Description: |
| * Handle the clearline VT100 escape sequence |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * The index of the match in g_vt100sequences[] |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_clearline(FAR struct fbcon_state_s *st) |
| { |
| int i; |
| FAR struct fbcon_bitmap_s *bm; |
| struct nxgl_rect_s rect; |
| |
| /* Create a bounding box the size of the iine */ |
| |
| rect.pt1.x = 0; |
| rect.pt2.x = st->wsize.w - 1; |
| rect.pt1.y = st->cursor.pos.y + FBCON_LINESPACING; |
| rect.pt2.y = st->cursor.pos.y + st->fheight + FBCON_LINESPACING - 1; |
| |
| /* Clear the region */ |
| |
| if (fbcon_fill(st, &rect, &st->bcolor) < 0) |
| { |
| gerr("ERROR: fbcon_fill failed: %d\n", errno); |
| } |
| |
| st->cursor.pos.x = st->spwidth; |
| |
| /* Decrement nchar for each character within in the bounding box */ |
| |
| i = st->nchars; |
| while (--i > 0) |
| { |
| bm = &st->bm[i]; |
| if (bm->pos.y <= rect.pt1.y && |
| bm->pos.y + st->fheight >= rect.pt2.y) |
| { |
| st->nchars--; |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_bitmap |
| * |
| * Description: |
| * Copy a rectangular region of a larger image into the rectangle in the |
| * specified window. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * dest - Describes the rectangular region on the display that will |
| * receive the bit map. |
| * src - The start of the source image. |
| * stride - The width of the full source image in pixels. |
| * |
| * Returned Value: |
| * OK on success; ERROR on failure with errno set appropriately |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_bitmap(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *dest, |
| FAR const uint32_t *src, |
| unsigned int stride) |
| { |
| FAR uint32_t *dst; |
| FAR uint8_t *row; |
| struct fb_area_s area; |
| int x; |
| int y; |
| |
| area.h = dest->pt2.y - dest->pt1.y + 1; |
| area.w = dest->pt2.x - dest->pt1.x + 1; |
| area.x = dest->pt1.x; |
| area.y = dest->pt1.y; |
| |
| row = (FAR uint8_t *)st->fbmem + st->pinfo->stride * area.y; |
| for (y = 0; y < area.h; y++) |
| { |
| dst = ((FAR uint32_t *)row) + area.x; |
| for (x = 0; x < area.w; x++) |
| { |
| *dst++ = *(uint32_t *)src++; |
| } |
| |
| row += st->pinfo->stride; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_movedisplay |
| * |
| * Description: |
| * This function implements the data movement for the scroll operation. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * bottom - Start of the vacated area at the bottom to be cleared |
| * scrollheight - The distance the display must be scrolled |
| * |
| * Returned Value: |
| * OK on success; ERROR on failure with errno set appropriately |
| * |
| ****************************************************************************/ |
| |
| static inline void fbcon_movedisplay(struct fbcon_state_s *st, int bottom, |
| int scrollheight) |
| { |
| FAR struct fbcon_bitmap_s *bm; |
| struct nxgl_rect_s rect; |
| nxgl_coord_t row; |
| int ret; |
| int i; |
| |
| /* Move each row, one at a time. They could all be moved at once but since |
| * the region is cleared, then re-written, the effect would not be good. |
| * Below the region is also cleared and re-written, |
| * however, in much smaller chunks. |
| */ |
| |
| rect.pt1.x = 0; |
| rect.pt2.x = st->wsize.w - 1; |
| |
| for (row = FBCON_LINESPACING; row < bottom; row += scrollheight) |
| { |
| /* Create a bounding box the size of one row of characters */ |
| |
| rect.pt1.y = row; |
| rect.pt2.y = row + scrollheight - 1; |
| |
| /* Clear the region */ |
| |
| ret = fbcon_fill(st, &rect, &st->bcolor); |
| if (ret < 0) |
| { |
| gerr("ERROR: fbcon_fill failed: %d\n", errno); |
| } |
| |
| /* Fill each character that might lie within in the bounding box */ |
| |
| for (i = 0; i < st->nchars; i++) |
| { |
| bm = &st->bm[i]; |
| if (bm->pos.y <= rect.pt1.y && |
| bm->pos.y + st->fheight >= rect.pt2.y) |
| { |
| fbcon_fillchar(st, &rect, bm); |
| } |
| } |
| } |
| |
| /* Finally, clear the vacated part of the display */ |
| |
| rect.pt1.y = bottom; |
| rect.pt2.y = st->wsize.h - 1; |
| |
| ret = fbcon_fill(st, &rect, &st->bcolor); |
| if (ret < 0) |
| { |
| fprintf(stderr, "fbcon_movedisplay: fbcon_fill failed: %d\n", errno); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_scroll |
| * |
| * Description: |
| * Scroll the display up by a certain amount |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * scrollheight - The distance the display must be scrolled |
| * |
| * Returned Value: |
| * OK on success; ERROR on failure with errno set appropriately |
| * |
| ****************************************************************************/ |
| |
| static inline void fbcon_scroll(struct fbcon_state_s *st, int scrollheight) |
| { |
| int i; |
| int j; |
| |
| /* Adjust the vertical position of each character */ |
| |
| for (i = 0; i < st->nchars; ) |
| { |
| FAR struct fbcon_bitmap_s *bm = &st->bm[i]; |
| |
| /* Has any part of this character scrolled off the screen? */ |
| |
| if (bm->pos.y < scrollheight + FBCON_LINESPACING) |
| { |
| /* Yes... Delete the character by moving all of the data */ |
| |
| for (j = i; j < st->nchars - 1; j++) |
| { |
| memcpy(&st->bm[j], &st->bm[j + 1], |
| sizeof(struct fbcon_bitmap_s)); |
| } |
| |
| /* Decrement the number of cached characters ('i' is not |
| * incremented in this case because it already points to the next |
| * character) |
| */ |
| |
| st->nchars--; |
| } |
| |
| /* No.. just decrement its vertical position (moving it "up" the |
| * display by one line). |
| */ |
| |
| else |
| { |
| bm->pos.y -= scrollheight; |
| |
| /* We are keeping this one so increment to the next character */ |
| |
| i++; |
| } |
| } |
| |
| /* And move the next display position up by one line as well */ |
| |
| st->cursor.pos.y -= scrollheight; |
| |
| /* Move the display in the range of 0-height up one scrollheight. */ |
| |
| fbcon_movedisplay(st, st->cursor.pos.y, scrollheight); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_freeglyph |
| * |
| * Description: |
| * Clear the specified glyph structure |
| * |
| * Input Parameters: |
| * glyph - pointer to glyph structure to be freed |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_freeglyph(FAR struct fbcon_glyph_s *glyph) |
| { |
| if (glyph->bitmap) |
| { |
| free(glyph->bitmap); |
| } |
| |
| memset(glyph, 0, sizeof(struct fbcon_glyph_s)); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_allocglyph |
| * |
| * Description: |
| * Allocate a glyph structure |
| * |
| * Input Parameters: |
| * glyph - pointer to glyph structure to be allocated |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static inline FAR struct fbcon_glyph_s * |
| fbcon_allocglyph(FAR struct fbcon_state_s *st) |
| { |
| FAR struct fbcon_glyph_s *glyph = NULL; |
| FAR struct fbcon_glyph_s *luglyph = NULL; |
| uint8_t luusecnt; |
| int i; |
| |
| /* Search through the glyph cache looking for an unused glyph. Also, keep |
| * track of the least used glyph as well. We need that if we have to |
| * replace a glyph in the cache. |
| */ |
| |
| for (i = 0; i < st->maxglyphs; i++) |
| { |
| /* Is this glyph in use? */ |
| |
| glyph = &st->glyph[i]; |
| if (!glyph->usecnt) |
| { |
| /* No.. return this glyph with a use count of one */ |
| |
| glyph->usecnt = 1; |
| return glyph; |
| } |
| |
| /* Yes.. check for the least recently used */ |
| |
| if (!luglyph || glyph->usecnt < luglyph->usecnt) |
| { |
| luglyph = glyph; |
| } |
| } |
| |
| /* If we get here, the glyph cache is full. We replace the least used |
| * glyph with the one we need now. (luglyph can't be NULL). |
| */ |
| |
| luusecnt = luglyph->usecnt; |
| fbcon_freeglyph(luglyph); |
| |
| /* But lets decrement all of the usecnts so that the new one one be so |
| * far behind in the counts as the older ones. |
| */ |
| |
| if (luusecnt > 1) |
| { |
| uint8_t decr = luusecnt - 1; |
| |
| for (i = 0; i < st->maxglyphs; i++) |
| { |
| /* Is this glyph in use? */ |
| |
| glyph = &st->glyph[i]; |
| if (glyph->usecnt > decr) |
| { |
| glyph->usecnt -= decr; |
| } |
| } |
| } |
| |
| /* Then return the least used glyph */ |
| |
| luglyph->usecnt = 1; |
| return luglyph; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_findglyph |
| * |
| * Description: |
| * Try and find a glyph in the cache for a given character |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * ch - the character |
| * |
| * Returned Value: |
| * a pointer to the glyph structure if found or NULL if not |
| * |
| ****************************************************************************/ |
| |
| static FAR struct fbcon_glyph_s * |
| fbcon_findglyph(FAR struct fbcon_state_s *st, uint8_t ch) |
| { |
| int i; |
| |
| /* Try to find the glyph in the cache of pre-rendered glyphs */ |
| |
| for (i = 0; i < st->maxglyphs; i++) |
| { |
| FAR struct fbcon_glyph_s *glyph = &st->glyph[i]; |
| if (glyph->usecnt > 0 && glyph->code == ch) |
| { |
| /* Increment the use count (unless it is already at the max) */ |
| |
| if (glyph->usecnt < MAX_USECNT) |
| { |
| glyph->usecnt++; |
| } |
| |
| /* And return the glyph that we found */ |
| |
| return glyph; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_renderglyph |
| * |
| * Description: |
| * Render a character as a glyph based on the font bitmap and metrics |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * fbm - pointer to the character bitmap/metrics |
| * ch - the character to be rendered |
| * |
| * Returned Value: |
| * a pointer to the rendered glyph structure |
| * |
| ****************************************************************************/ |
| |
| static inline FAR struct fbcon_glyph_s * |
| fbcon_renderglyph(FAR struct fbcon_state_s *st, |
| FAR const struct nx_fontbitmap_s *fbm, uint8_t ch) |
| { |
| FAR struct fbcon_glyph_s *glyph = NULL; |
| FAR nxgl_mxpixel_t *ptr; |
| #if FBCON_BPP < 8 |
| nxgl_mxpixel_t pixel; |
| #endif |
| int bmsize; |
| int row; |
| int col; |
| int ret; |
| |
| /* Make sure that there is room for another glyph */ |
| |
| ginfo("ch=%c [%02x]\n", isprint(ch) ? ch : '.', ch); |
| |
| /* Allocate the glyph (always succeeds) */ |
| |
| glyph = fbcon_allocglyph(st); |
| glyph->code = ch; |
| |
| /* Get the dimensions of the glyph */ |
| |
| glyph->width = fbm->metric.width + fbm->metric.xoffset; |
| glyph->height = fbm->metric.height + fbm->metric.yoffset; |
| |
| /* Allocate memory to hold the glyph with its offsets */ |
| |
| glyph->stride = (glyph->width * FBCON_BPP + 7) / 8; |
| bmsize = glyph->stride * glyph->height; |
| glyph->bitmap = (FAR uint8_t *)malloc(bmsize); |
| |
| if (glyph->bitmap) |
| { |
| /* Initialize the glyph memory to the background color */ |
| |
| #if FBCON_BPP < 8 |
| pixel = st->bcolor; |
| # if FBCON_BPP == 1 |
| /* Pack 1-bit pixels into a 2-bits */ |
| |
| pixel &= 0x01; |
| pixel = (pixel) << 1 | pixel; |
| # endif |
| # if FBCON_BPP < 4 |
| /* Pack 2-bit pixels into a nibble */ |
| |
| pixel &= 0x03; |
| pixel = (pixel) << 2 | pixel; |
| # endif |
| |
| /* Pack 4-bit nibbles into a byte */ |
| |
| pixel &= 0x0f; |
| pixel = (pixel) << 4 | pixel; |
| |
| ptr = (FAR nxgl_mxpixel_t *)glyph->bitmap; |
| for (row = 0; row < glyph->height; row++) |
| { |
| for (col = 0; col < glyph->stride; col++) |
| { |
| /* Transfer the packed bytes into the buffer */ |
| |
| *ptr++ = pixel; |
| } |
| } |
| |
| #elif FBCON_BPP == 24 |
| # error "Additional logic is needed here for 24bpp support" |
| |
| #else /* FBCON_BPP = {8,16,32} */ |
| |
| ptr = (FAR nxgl_mxpixel_t *)glyph->bitmap; |
| for (row = 0; row < glyph->height; row++) |
| { |
| /* Just copy the color value into the glyph memory */ |
| |
| for (col = 0; col < glyph->width; col++) |
| { |
| *ptr++ = st->bcolor; |
| } |
| } |
| #endif |
| |
| /* Then render the glyph into the allocated memory */ |
| |
| ret = RENDERER((FAR nxgl_mxpixel_t *)glyph->bitmap, |
| glyph->height, glyph->width, glyph->stride, |
| fbm, st->fcolor); |
| if (ret < 0) |
| { |
| /* Actually, the RENDERER never returns a failure */ |
| |
| gerr("ERROR: fbcon_renderglyph: RENDERER failed\n"); |
| fbcon_freeglyph(glyph); |
| glyph = NULL; |
| } |
| } |
| |
| return glyph; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_fontsize |
| * |
| * Description: |
| * Get the size of a given character bitmap |
| * |
| * Input Parameters: |
| * font - the font of interest |
| * ch - the character of interest |
| * size _ pointer to the structure to return the character size |
| * |
| * Returned Value: |
| * Success or ERROR |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_fontsize(FAR void *hfont, uint8_t ch, |
| FAR struct nxgl_size_s *size) |
| { |
| FAR const struct nx_fontbitmap_s *fbm; |
| |
| /* No, it is not cached... Does the code map to a font? */ |
| |
| fbm = nxf_getbitmap(hfont, ch); |
| if (fbm) |
| { |
| /* Yes.. return the font size */ |
| |
| size->w = fbm->metric.width + fbm->metric.xoffset; |
| size->h = fbm->metric.height + fbm->metric.yoffset; |
| return OK; |
| } |
| |
| return ERROR; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_getglyph |
| * |
| * Description: |
| * Get rendered glyph data for a given character |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * ch - the character to be rendered |
| * |
| * Returned Value: |
| * a pointer to the glyph data |
| * |
| ****************************************************************************/ |
| |
| static FAR struct fbcon_glyph_s * |
| fbcon_getglyph(FAR struct fbcon_state_s *st, uint8_t ch) |
| { |
| FAR struct fbcon_glyph_s *glyph; |
| FAR const struct nx_fontbitmap_s *fbm; |
| |
| /* First, try to find the glyph in the cache of pre-rendered glyphs */ |
| |
| glyph = fbcon_findglyph(st, ch); |
| if (!glyph) |
| { |
| /* No, it is not cached... Does the code map to a font? */ |
| |
| fbm = nxf_getbitmap(st->fbcon_font, ch); |
| if (fbm) |
| { |
| /* Yes.. render the glyph */ |
| |
| glyph = fbcon_renderglyph(st, fbm, ch); |
| } |
| } |
| |
| return glyph; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_hidechar |
| * |
| * Description: |
| * Erase a character from the display. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * bm - pointer to the character bitmap to be erased |
| * |
| * Returned Value: |
| * Success or error value |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_hidechar(FAR struct fbcon_state_s *st, |
| FAR const struct fbcon_bitmap_s *bm) |
| { |
| struct nxgl_rect_s bounds; |
| struct nxgl_size_s fsize; |
| int ret; |
| |
| /* Get the size of the font glyph. If fbcon_fontsize, then the |
| * character will have been rendered as a space, and no display |
| * modification is required (not an error). |
| */ |
| |
| ret = fbcon_fontsize(st->fbcon_font, bm->code, &fsize); |
| if (ret < 0) |
| { |
| /* It was rendered as a space. */ |
| |
| return OK; |
| } |
| |
| /* Construct a bounding box for the glyph */ |
| |
| bounds.pt1.x = bm->pos.x; |
| bounds.pt1.y = bm->pos.y; |
| bounds.pt2.x = bm->pos.x + fsize.w - 1; |
| bounds.pt2.y = bm->pos.y + fsize.h - 1; |
| |
| /* Fill the bitmap region with the background color, erasing the |
| * character from the display. |
| */ |
| |
| return fbcon_fill(st, &bounds, &st->bcolor); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_addchar |
| * |
| * Description: |
| * Find or create a bitmap structurefor a character |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * ch - the character to be rendered |
| * |
| * Returned Value: |
| * Pointer to the character bitmap |
| * |
| ****************************************************************************/ |
| |
| static FAR const struct fbcon_bitmap_s * |
| fbcon_addchar(FAR struct fbcon_state_s *st, |
| uint8_t ch) |
| { |
| FAR struct fbcon_bitmap_s *bm = NULL; |
| FAR struct fbcon_glyph_s *glyph; |
| |
| /* Setup the bitmap information */ |
| |
| bm = &st->bm[st->nchars]; |
| bm->code = ch; |
| bm->flags = 0; |
| bm->pos.x = st->cursor.pos.x; |
| bm->pos.y = st->cursor.pos.y; |
| |
| /* Find (or create) the matching glyph */ |
| |
| glyph = fbcon_getglyph(st, ch); |
| if (!glyph) |
| { |
| /* No, there is no font for this code. Just mark this as a space. */ |
| |
| bm->flags |= BMFLAGS_NOGLYPH; |
| |
| /* Set up the next character position */ |
| |
| st->cursor.pos.x += st->spwidth; |
| } |
| else |
| { |
| /* Set up the next character position */ |
| |
| st->cursor.pos.x += glyph->width; |
| } |
| |
| /* Increment nchars to retain this character */ |
| |
| if (st->nchars < st->maxchars) |
| { |
| st->nchars++; |
| } |
| |
| return bm; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_fill |
| * |
| * Description: |
| * Fill a region with a colour. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * rect - Describes the rectangular region on the display to be filled |
| * color - The color to fill the rectangle with |
| * |
| * Returned Value: |
| * OK on success; ERROR on failure with errno set appropriately |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_fill(FAR struct fbcon_state_s *st, |
| FAR struct nxgl_rect_s *rect, |
| FAR nxgl_mxpixel_t *color) |
| { |
| FAR uint32_t *dest; |
| FAR uint8_t *row; |
| struct fb_area_s area; |
| int x; |
| int y; |
| |
| area.h = rect->pt2.y - rect->pt1.y + 1; |
| area.w = rect->pt2.x - rect->pt1.x + 1; |
| area.x = rect->pt1.x; |
| area.y = rect->pt1.y; |
| |
| row = (FAR uint8_t *)st->fbmem + st->pinfo->stride * area.y; |
| for (y = 0; y < area.h; y++) |
| { |
| dest = (FAR uint32_t *)row + area.x; |
| for (x = 0; x < area.w; x++) |
| { |
| *dest++ = *color; |
| } |
| |
| row += st->pinfo->stride; |
| } |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_backspace |
| * |
| * Description: |
| * Remove the last character from the window. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * OK on success; ERROR on failure with errno set appropriately |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_backspace(FAR struct fbcon_state_s *st) |
| { |
| FAR struct fbcon_bitmap_s *bm; |
| int ndx; |
| int ret = -ENOENT; |
| |
| /* Is there a character on the display? */ |
| |
| if (st->nchars > 0) |
| { |
| /* Yes.. Get the index to the last bitmap on the display */ |
| |
| ndx = st->nchars - 1; |
| bm = &st->bm[ndx]; |
| |
| /* Erase the character from the display */ |
| |
| ret = fbcon_hidechar(st, bm); |
| |
| /* The current position to the location where the last character was */ |
| |
| st->cursor.pos.x = bm->pos.x; |
| st->cursor.pos.y = bm->pos.y; |
| |
| /* Decrement nchars to discard this character */ |
| |
| st->nchars = ndx; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_home |
| * |
| * Description: |
| * Set the next character position to the top-left corner of the display. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_home(FAR struct fbcon_state_s *st) |
| { |
| /* The first character is one space from the left */ |
| |
| st->cursor.pos.x = st->spwidth; |
| |
| /* And FBCON_LINESPACING lines from the top */ |
| |
| st->cursor.pos.y = FBCON_LINESPACING; |
| |
| /* And reset number of characters in the bm buffer */ |
| |
| st->nchars = 0; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_newline |
| * |
| * Description: |
| * Set the next character position to the beginning of the next line. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void fbcon_newline(FAR struct fbcon_state_s *st) |
| { |
| /* Carriage return: The first character is one space from the left */ |
| |
| st->cursor.pos.x = st->spwidth; |
| |
| /* Linefeed: Down the max font height + FBCON_LINESPACING */ |
| |
| st->cursor.pos.y += (st->fheight + FBCON_LINESPACING); |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_putc |
| * |
| * Description: |
| * Render the specified character at the current display position. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * ch - the character to be rendered |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void fbcon_putc(FAR struct fbcon_state_s *st, uint8_t ch) |
| { |
| FAR const struct fbcon_bitmap_s *bm; |
| int lineheight; |
| |
| /* Ignore carriage returns */ |
| |
| if (ch == '\r') |
| { |
| return; |
| } |
| |
| /* Handle backspace (treating both BS and DEL as backspace) */ |
| |
| if (ch == ASCII_BS || ch == ASCII_DEL) |
| { |
| fbcon_backspace(st); |
| return; |
| } |
| |
| /* Will another character fit on this line? */ |
| |
| if (st->cursor.pos.x + st->fwidth > st->wsize.w) |
| { |
| #ifndef CONFIG_EXAMPLES_FBCON_NOWRAP |
| /* No.. move to the next line */ |
| |
| fbcon_newline(st); |
| |
| /* If we were about to output a newline character, then don't */ |
| |
| if (ch == ASCII_LF) |
| { |
| return; |
| } |
| #else |
| /* No.. Ignore all further characters until a newline is encountered */ |
| |
| if (ch != ASCII_LF) |
| { |
| return; |
| } |
| #endif |
| } |
| |
| /* If it is a newline character, then just perform the logical newline |
| * operation. |
| */ |
| |
| if (ch == ASCII_LF) |
| { |
| fbcon_newline(st); |
| return; |
| } |
| |
| /* Check if we need to scroll up */ |
| |
| lineheight = st->fheight + FBCON_LINESPACING; |
| while (st->cursor.pos.y > st->wsize.h - lineheight) |
| { |
| fbcon_scroll(st, lineheight); |
| } |
| |
| /* Find the glyph associated with the character and render it |
| * onto the display. |
| */ |
| |
| bm = fbcon_addchar(st, ch); |
| if (bm) |
| { |
| fbcon_fillchar(st, NULL, bm); |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_fillspace |
| * |
| * Description: |
| * Handle the special case of a space being displayed |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * rect- Describes the rectangular region on the display that will |
| * receive the space. |
| * bm - pointer to the character bitmap structure with the location |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_fillspace(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *rect, |
| FAR const struct fbcon_bitmap_s *bm) |
| { |
| struct nxgl_rect_s bounds; |
| struct nxgl_rect_s intersection; |
| int ret; |
| |
| /* Construct a bounding box for the glyph */ |
| |
| bounds.pt1.x = bm->pos.x; |
| bounds.pt1.y = bm->pos.y; |
| bounds.pt2.x = bm->pos.x + st->spwidth - 1; |
| bounds.pt2.y = bm->pos.y + st->fheight - 1; |
| |
| /* Should this also be clipped to a region in the window? */ |
| |
| if (rect != NULL) |
| { |
| /* Get the intersection of the redraw region and the character bitmap */ |
| |
| nxgl_rectintersect(&intersection, rect, &bounds); |
| } |
| else |
| { |
| /* The intersection is the whole glyph */ |
| |
| nxgl_rectcopy(&intersection, &bounds); |
| } |
| |
| /* Check for empty intersections */ |
| |
| if (!nxgl_nullrect(&intersection)) |
| { |
| /* Fill the bitmap region with the background color, erasing the |
| * character from the display. |
| */ |
| |
| ret = fbcon_fill(st, &intersection, &st->bcolor); |
| if (ret < 0) |
| { |
| gerr("ERROR: fill() method failed: %d\n", ret); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_fillchar |
| * |
| * Description: |
| * Implement the character display |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * rect- Describes the rectangular region on the display that will |
| * receive the space. |
| * bm - pointer to the character bitmap structure with the location |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| void fbcon_fillchar(FAR struct fbcon_state_s *st, |
| FAR const struct nxgl_rect_s *rect, |
| FAR const struct fbcon_bitmap_s *bm) |
| { |
| FAR struct fbcon_glyph_s *glyph; |
| struct nxgl_rect_s bounds; |
| struct nxgl_rect_s intersection; |
| struct nxgl_size_s fsize; |
| int ret; |
| |
| /* Handle the special case of spaces which have no glyph bitmap */ |
| |
| if (BM_ISSPACE(bm)) |
| { |
| fbcon_fillspace(st, rect, bm); |
| return; |
| } |
| |
| /* Get the size of the font glyph (which may not have been created yet) */ |
| |
| ret = fbcon_fontsize(st->fbcon_font, bm->code, &fsize); |
| if (ret < 0) |
| { |
| /* This would mean that there is no bitmap for the character code and |
| * that the font would be rendered as a space. But this case should |
| * never happen here because the BM_ISSPACE() should have already |
| * found all such cases. |
| */ |
| |
| return; |
| } |
| |
| /* Construct a bounding box for the glyph */ |
| |
| bounds.pt1.x = bm->pos.x; |
| bounds.pt1.y = bm->pos.y; |
| bounds.pt2.x = bm->pos.x + fsize.w - 1; |
| bounds.pt2.y = bm->pos.y + fsize.h - 1; |
| |
| /* Should this also be clipped to a region in the window? */ |
| |
| if (rect != NULL) |
| { |
| /* Get the intersection of the redraw region and the character bitmap */ |
| |
| nxgl_rectintersect(&intersection, rect, &bounds); |
| } |
| else |
| { |
| /* The intersection is the whole glyph */ |
| |
| nxgl_rectcopy(&intersection, &bounds); |
| } |
| |
| /* Check for empty intersections */ |
| |
| if (!nxgl_nullrect(&intersection)) |
| { |
| FAR const void *src; |
| |
| /* Find (or create) the glyph that goes with this font */ |
| |
| glyph = fbcon_getglyph(st, bm->code); |
| if (!glyph) |
| { |
| /* Shouldn't happen */ |
| |
| return; |
| } |
| |
| /* Blit the font bitmap into the window */ |
| |
| src = (FAR const void *)glyph->bitmap; |
| |
| ret = fbcon_bitmap(st, &intersection, src, |
| (unsigned int)glyph->stride); |
| if (ret < 0) |
| { |
| gerr("ERROR: fbcon_fillchar: fbcon_bitmapwindow failed: %d\n", |
| ret); |
| } |
| } |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_vt100part |
| * |
| * Description: |
| * Return the next entry that is a partial match to the sequence. |
| * |
| * Input Parameters: |
| * st - Driver data structure |
| * seqsize - The number of bytes in the sequence |
| * |
| * Returned Value: |
| * A pointer to the matching sequence in g_vt100sequences[] |
| * |
| ****************************************************************************/ |
| |
| FAR const struct vt100_sequence_s * |
| fbcon_vt100part(FAR struct fbcon_state_s *st, int seqsize) |
| { |
| FAR static const struct vt100_sequence_s *seq; |
| static int ndx; |
| static int i; |
| static bool aborted; |
| static int numw; |
| static bool collecting; |
| |
| /* Search from the beginning of the sequence table */ |
| |
| for (ndx = 0; g_vt100sequences[ndx].seq; ndx++) |
| { |
| seq = &g_vt100sequences[ndx]; |
| |
| /* Is this sequence big enough? */ |
| |
| aborted = false; |
| numw = 0; |
| st->nwild = 0; |
| |
| /* compare characters received to those in the decodable sequences */ |
| |
| collecting = false; |
| for (i = 0; i < seqsize; i++) |
| { |
| if (seq->seq[i] == '*') |
| { |
| /* This sequence has a wildcard */ |
| |
| if ((st->seq[i] < ASCII_0) || (st->seq[i] > ASCII_9)) |
| { |
| aborted = true; |
| break; |
| } |
| |
| if (++numw > 1) |
| { |
| /* This logic only allows collection of one wildcard |
| * per VT100 sequence |
| */ |
| |
| aborted = true; |
| break; |
| } |
| |
| collecting = true; |
| st->wildval = st->seq[i] - ASCII_0; /* convert from ASCII */ |
| } |
| else if (collecting) |
| { |
| if ((st->seq[i] >= ASCII_0) && (st->seq[i] <= ASCII_9)) |
| { |
| st->wildval *= 10; |
| st->wildval += st->seq[i] - ASCII_0; |
| st->nwild++; |
| } |
| else |
| { |
| collecting = false; |
| } |
| } |
| else if (st->seq[i] != seq->seq[i]) |
| { |
| aborted = true; |
| break; |
| } |
| } |
| |
| if (!aborted) |
| { |
| return seq; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_vt100seq |
| * |
| * Description: |
| * Determine if the new sequence is a part of a supported VT100 escape |
| * sequence. |
| * |
| * Input Parameters: |
| * st - Driver data structure |
| * seqsize - The number of bytes in the sequence |
| * |
| * Returned Value: |
| * state - See enum fbcon_vt100state_e; |
| * |
| ****************************************************************************/ |
| |
| static enum fbcon_vt100state_e fbcon_vt100seq( |
| FAR struct fbcon_state_s *st, int seqsize) |
| { |
| FAR const struct vt100_sequence_s *seq; |
| enum fbcon_vt100state_e ret; |
| |
| /* Is there any VT100 escape sequence that matches what we have |
| * buffered so far? |
| */ |
| |
| seq = fbcon_vt100part(st, seqsize); |
| if (seq) |
| { |
| /* Yes.. if the size of that escape sequence is the same as that |
| * buffered, then we have an exact match. |
| */ |
| |
| if (seqsize >= seq->size + st->nwild) |
| { |
| /* Process the VT100 sequence */ |
| |
| seq->handler(st); |
| st->nseq = 0; |
| return VT100_PROCESSED; |
| } |
| |
| /* The 'seqsize' is still smaller than the potential match(es). We |
| * will need to collect more characters before we can make a decision. |
| * Return an indication that we have consumed the character. |
| */ |
| |
| return VT100_CONSUMED; |
| } |
| |
| /* We get here on a failure. The buffer sequence is not part of any |
| * supported VT100 escape sequence. If seqsize > 1 then we need to |
| * return a special value because we have to re-process the buffered |
| * data. |
| */ |
| |
| ret = seqsize > 1 ? VT100_ABORT : VT100_NOT_CONSUMED; |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_vt100 |
| * |
| * Description: |
| * Test if the newly received byte is part of a VT100 escape sequence |
| * |
| * Input Parameters: |
| * st - Driver data structure |
| * ch - The newly received character |
| * |
| * Returned Value: |
| * state - See enum fbcon_vt100state_e; |
| * |
| ****************************************************************************/ |
| |
| static enum fbcon_vt100state_e fbcon_vt100(FAR struct fbcon_state_s *st, |
| char ch) |
| { |
| enum fbcon_vt100state_e ret; |
| int seqsize; |
| |
| /* If we have no buffered characters, then 'ch' must be the first character |
| * of an escape sequence. |
| */ |
| |
| if (st->nseq < 1) |
| { |
| /* The first character of an escape sequence must be an an escape |
| * character (!). |
| */ |
| |
| if (ch != ASCII_ESC) |
| { |
| return VT100_NOT_CONSUMED; |
| } |
| |
| /* Add the escape character to the buffer but don't bother with any |
| * further checking. |
| */ |
| |
| st->seq[0] = ASCII_ESC; |
| st->nseq = 1; |
| |
| return VT100_CONSUMED; |
| } |
| |
| /* Temporarily add the next character to the buffer */ |
| |
| seqsize = st->nseq; |
| st->seq[seqsize] = ch; |
| |
| /* Then check if this sequence is part of an a valid escape sequence */ |
| |
| seqsize++; |
| ret = fbcon_vt100seq(st, seqsize); |
| if (ret == VT100_CONSUMED) |
| { |
| /* The newly added character is indeed part of a VT100 escape sequence |
| * (which is still incomplete). Keep it in the buffer. |
| */ |
| |
| st->nseq = seqsize; |
| } |
| |
| return ret; |
| } |
| |
| /**************************************************************************** |
| * Name: fbcon_write |
| * |
| * Description: |
| * Put a sequence of characters on the display. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * buffer - pointer to the character array to be displayed |
| * buflen - number of characters to be displayed |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void fbcon_write(FAR struct fbcon_state_s *st, |
| FAR char *buffer, size_t buflen) |
| { |
| char ch; |
| enum fbcon_vt100state_e state; |
| ssize_t remaining; |
| |
| fbcon_hidecursor(st); |
| for (remaining = buflen; remaining > 0; remaining--) |
| { |
| ch = *buffer++; |
| |
| #if CONFIG_EXAMPLES_FBCON_VT100_DECODE |
| /* Check if this character is part of a VT100 escape sequence */ |
| |
| do |
| { |
| /* Is the character part of a VT100 escape sequnce? */ |
| |
| state = fbcon_vt100(st, ch); |
| switch (state) |
| { |
| /* Character is not part of a VT100 escape sequence (and no |
| * characters are buffer. |
| */ |
| |
| default: |
| case VT100_NOT_CONSUMED: |
| { |
| /* We can output the character to the window */ |
| |
| fbcon_putc(st, (uint8_t)ch); |
| } |
| break; |
| |
| /* The full VT100 escape sequence was processed (and the new |
| * character was consumed) |
| */ |
| |
| case VT100_PROCESSED: |
| |
| /* Character was consumed as part of the VT100 escape processing |
| * (but the escape sequence is still incomplete. |
| */ |
| |
| case VT100_CONSUMED: |
| { |
| /* Do nothing... the VT100 logic owns the character */ |
| } |
| break; |
| |
| /* Invalid/unsupported character in escape sequence */ |
| |
| case VT100_ABORT: |
| { |
| int i; |
| |
| /* Add the first unhandled character to the window */ |
| |
| fbcon_putc(st, (uint8_t)st->seq[0]); |
| |
| /* Move all buffer characters down one */ |
| |
| for (i = 1; i < st->nseq; i++) |
| { |
| st->seq[i - 1] = st->seq[i]; |
| } |
| |
| st->nseq--; |
| |
| /* Then loop again and check if what remains is part of a |
| * VT100 escape sequence. We could speed this up by |
| * checking if st->seq[0] == ASCII_ESC. |
| */ |
| } |
| break; |
| } |
| } |
| while (state == VT100_ABORT); |
| #else |
| /* Just output the character */ |
| |
| fbcon_putc(st, ch); |
| #endif /* CONFIG_EXAMPLES_FBCON_VT100_DECODE */ |
| } |
| |
| fbcon_showcursor(st); |
| } |
| |
| /**************************************************************************** |
| * Name: fbdev_get_pinfo |
| * |
| * Description: |
| * Get plane information for a framebuffer |
| * Note - does not support dual framebuffer memory |
| * |
| * Input Parameters: |
| * fd - file descriptor of the framebuffer |
| * pinfo - pointer to the3 structure for the plane info |
| * |
| * Returned Value: |
| * OK |
| * |
| ****************************************************************************/ |
| |
| static int fbdev_get_pinfo(int fd, FAR struct fb_planeinfo_s *pinfo) |
| { |
| if (ioctl(fd, FBIOGET_PLANEINFO, (unsigned long)((uintptr_t)pinfo)) < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", errcode); |
| return EXIT_FAILURE; |
| } |
| |
| /* Only these pixel depths are supported. viinfo.fmt is ignored, only |
| * certain color formats are supported. |
| */ |
| |
| if (pinfo->bpp != 32 && pinfo->bpp != 24 && |
| pinfo->bpp != 16 && pinfo->bpp != 8 && |
| pinfo->bpp != 1) |
| { |
| gerr("ERROR: bpp=%u not supported\n", pinfo->bpp); |
| return EXIT_FAILURE; |
| } |
| |
| return OK; |
| } |
| |
| #if 0 |
| /* Revisit needed = no support for dual framebuffers yet */ |
| |
| /**************************************************************************** |
| * Name: fb_init_mem2 |
| * |
| * Description: |
| * Initialise the memory for the second framebuffer |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * OK |
| * |
| ****************************************************************************/ |
| |
| static int fb_init_mem2(FAR struct fbcon_state_s *st) |
| { |
| int ret; |
| uintptr_t buf_offset; |
| struct fb_planeinfo_s pinfo; |
| |
| memset(&pinfo, 0, sizeof(pinfo)); |
| pinfo.display = st->pinfo->display + 1; |
| |
| if ((ret = fbdev_get_pinfo(st->fd_fb, &pinfo)) < 0) |
| { |
| return EXIT_FAILURE; |
| } |
| |
| /* Check bpp */ |
| |
| if (pinfo.bpp != st->pinfo->bpp) |
| { |
| gerr("ERROR: fbmem2 is incorrect"); |
| return -EINVAL; |
| } |
| |
| /* Check the buffer address offset, |
| * It needs to be divisible by pinfo->stride |
| */ |
| |
| buf_offset = pinfo.fbmem - st->fbmem; |
| |
| if ((buf_offset % st->pinfo->stride) != 0) |
| { |
| gerr("ERROR: It is detected that buf_offset(%" PRIuPTR ") " |
| "and stride(%d) are not divisible, please ensure " |
| "that the driver handles the address offset by itself.\n", |
| buf_offset, st->pinfo->stride); |
| } |
| |
| /* Calculate the address and yoffset of fbmem2 */ |
| |
| if (buf_offset == 0) |
| { |
| /* Use consecutive fbmem2. */ |
| |
| st->mem2_yoffset = st->vinfo->yres; |
| st->fbmem2 = pinfo.fbmem + st->mem2_yoffset * pinfo.stride; |
| gerr("ERROR: Use consecutive fbmem2 = %p, yoffset = %" PRIu32"\n", |
| st->fbmem2, st->mem2_yoffset); |
| } |
| else |
| { |
| /* Use non-consecutive fbmem2. */ |
| |
| st->mem2_yoffset = buf_offset / st->pinfo->stride; |
| st->fbmem2 = pinfo.fbmem; |
| gerr("ERROR: Use non-consecutive fbmem2 = %p, yoffset = %" PRIu32"\n", |
| st->fbmem2, st->mem2_yoffset); |
| } |
| |
| return OK; |
| } |
| #endif |
| |
| /**************************************************************************** |
| * Name: fbcon_initialize |
| * |
| * Description: |
| * Initialise the Framebuffer Console |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * Success or failure code |
| * |
| ****************************************************************************/ |
| |
| static int fbcon_initialize(FAR struct fbcon_state_s *st) |
| { |
| struct nxgl_rect_s rect; |
| FAR const struct nx_font_s *fontset; |
| int ret; |
| |
| /* Get the configured font handles */ |
| |
| st->fbcon_font = nxf_getfonthandle(FBCON_FONTID); |
| if (!st->fbcon_font) |
| { |
| gerr("ERROR: fbcon_main: Failed to get console font handle: %d\n", |
| errno); |
| return FBCON_EXIT_FONTOPEN; |
| } |
| |
| st->bcolor = FBCON_BGCOLOR; |
| st->fcolor = FBCON_FCOLOR; |
| |
| rect.pt1.x = 0; |
| rect.pt1.y = 0; |
| rect.pt2.x = st->vinfo->xres - 1; |
| rect.pt2.y = st->vinfo->yres - 1; |
| ret = fbcon_fill(st, &rect, &st->bcolor); |
| if (ret < 0) |
| { |
| gerr("ERROR: fbcon_main: fbcon_setbgcolor failed: %d\n", errno); |
| return FBCON_EXIT_SETBGCOLOR; |
| } |
| |
| fontset = nxf_getfontset(st->fbcon_font); |
| st->fheight = fontset->mxheight; |
| st->fwidth = fontset->mxwidth; |
| st->spwidth = fontset->spwidth; |
| |
| /* we use the entire LCD area for the console */ |
| |
| st->wsize.h = st->vinfo->yres; |
| st->wsize.w = st->vinfo->xres; |
| |
| st->cursor.code = FBCON_CURSORCHAR; |
| if (FBCON_CURSORCHAR != ASCII_SPACE) |
| { |
| st->cursor.flags = 0; |
| } |
| else |
| { |
| st->cursor.flags = BMFLAGS_NOGLYPH; |
| } |
| |
| st->nchars = 0; |
| st->maxglyphs = FBCON_GLCACHE; |
| |
| /* The first character is one space from the left |
| * and FBCON_LINESEPARATION lines from the top |
| */ |
| |
| st->cursor.pos.x = st->spwidth; |
| st->cursor.pos.y = FBCON_LINESPACING; |
| st->maxchars = (st->wsize.w / st->fwidth) * |
| (st->wsize.h / (st->fheight + FBCON_LINESPACING) - 1); |
| |
| st->bm = malloc(st->maxchars * sizeof(struct fbcon_bitmap_s)); |
| if (st->bm == NULL) |
| { |
| gerr("ERROR: Unable to allocate display buffer memory"); |
| return -ENOMEM; |
| } |
| |
| memset(st->bm, 0, st->maxchars * sizeof(struct fbcon_bitmap_s)); |
| |
| st->glyph = malloc(FBCON_GLCACHE * (sizeof(struct fbcon_glyph_s))); |
| if (st->glyph == NULL) |
| { |
| gerr("ERROR: Unable to allocate glyph cache memory"); |
| return -ENOMEM; |
| } |
| |
| memset(st->glyph, 0, FBCON_GLCACHE * (sizeof(struct fbcon_glyph_s))); |
| |
| st->nseq = 0; |
| |
| return OK; |
| } |
| |
| /**************************************************************************** |
| * Name: has_input |
| * |
| * Description: |
| * Return true if a File Descriptor has data to be read. |
| * |
| * Input Parameters: |
| * fd - File Descriptor to be checked |
| * |
| * Returned Value: |
| * True if File Descriptor has data to be read; False otherwise |
| * |
| ****************************************************************************/ |
| |
| static bool has_input(int fd) |
| { |
| int ret; |
| |
| /* Poll the File Descriptor for input */ |
| |
| struct pollfd fdp; |
| fdp.fd = fd; |
| fdp.events = POLLIN; |
| ret = poll(&fdp, /* File Descriptors */ |
| 1, /* Number of File Descriptors */ |
| 0); /* Poll Timeout (Milliseconds) */ |
| |
| if (ret > 0) |
| { |
| /* If poll is OK and there is input */ |
| |
| if ((fdp.revents & POLLIN) != 0) |
| { |
| /* Report that there is input */ |
| |
| return true; |
| } |
| |
| /* Else report no input */ |
| |
| return false; |
| } |
| else if (ret == 0) |
| { |
| /* If timeout, report no input */ |
| |
| return false; |
| } |
| else if (ret < 0) |
| { |
| /* Handle error */ |
| |
| fprintf(stderr, "poll failed: %d, fd=%d\n", ret, fd); |
| return false; |
| } |
| |
| /* Never comes here */ |
| |
| assert(false); |
| return false; |
| } |
| |
| /**************************************************************************** |
| * Name: poll_std_streams |
| * |
| * Description: |
| * Poll NSH stdout and stderr for output and display the output. |
| * |
| * Input Parameters: |
| * st - pointer to FBCON status structure |
| * |
| * Returned Value: |
| * None |
| * |
| ****************************************************************************/ |
| |
| static void poll_std_streams(FAR struct fbcon_state_s *st) |
| { |
| ssize_t num_ch; |
| static char buf[POLL_BUFSIZE]; |
| |
| assert(g_nsh_stdout[READ_PIPE] != 0); |
| assert(g_nsh_stderr[READ_PIPE] != 0); |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDIN |
| assert(g_nsh_stdin[READ_PIPE] != 0); |
| #endif |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDERR |
| /* Poll NSH stderr to check if there's output to be processed */ |
| |
| if (has_input(g_nsh_stderr[READ_PIPE])) |
| { |
| /* Write it to display */ |
| |
| num_ch = read(g_nsh_stderr[READ_PIPE], buf, POLL_BUFSIZE); |
| if (num_ch > 0) |
| { |
| /* display */ |
| |
| fbcon_write(st, buf, num_ch); |
| } |
| } |
| #endif /* CONFIG_EXAMPLES_FBCON_PIPE_STDERR */ |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDOUT |
| /* Poll NSH stdout to check if there's output to be processed */ |
| |
| if (has_input(g_nsh_stdout[READ_PIPE])) |
| { |
| /* Read the output from NSH stdout */ |
| |
| num_ch = read(g_nsh_stdout[READ_PIPE], buf, POLL_BUFSIZE); |
| if (num_ch > 0) |
| { |
| /* Write it to display */ |
| |
| fbcon_write(st, buf, num_ch); |
| } |
| } |
| #endif /* CONFIG_EXAMPLES_FBCON_PIPE_STDOUT */ |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDIN |
| if (has_input(STDIN_FILENO)) |
| { |
| /* Get and process a character */ |
| |
| ssize_t num = 0; |
| char ch; |
| num_ch = read(STDIN_FILENO, &ch, 1); |
| |
| if (num_ch != 1) |
| { |
| fprintf(stderr, "STDIN read failed\n"); |
| return; |
| } |
| |
| /* copy it to spawned app/process */ |
| |
| num = write(g_nsh_stdin[WRITE_PIPE], &ch, 1); |
| if (num != num_ch) |
| { |
| fprintf(stderr, "STDIN write failed\n"); |
| return; |
| } |
| |
| # if defined(CONFIG_NSH_READLINE) |
| fbcon_write(st, buf, num_ch); |
| # elif defined(CONFIG_NSH_CLE) |
| if (ch == ASCII_LF) |
| { |
| fbcon_write(st, &ch, 1); |
| } |
| # endif |
| } |
| #endif /* CONFIG_EXAMPLES_FBCON_PIPE_STDIN */ |
| } |
| |
| /**************************************************************************** |
| * Public Functions |
| ****************************************************************************/ |
| |
| /**************************************************************************** |
| * Name: fbcon_main |
| * |
| * Description: |
| * fbcon entry point |
| * |
| ****************************************************************************/ |
| |
| int main(int argc, FAR char *argv[]) |
| { |
| FAR struct fbcon_state_s *st; |
| int index; |
| int ret; |
| pid_t pid; |
| posix_spawn_file_actions_t actions; |
| posix_spawnattr_t attr; |
| FAR const struct builtin_s *builtin; |
| int exitcode = FBCON_EXIT_SUCCESS; |
| FAR const char *fbdev = CONFIG_EXAMPLES_FBCON_DEF_FB; |
| |
| /* There is a single optional argument: The path to the framebuffer |
| * driver. |
| */ |
| |
| if (argc == 2) |
| { |
| fbdev = argv[1]; |
| } |
| else if (argc != 1) |
| { |
| fprintf(stderr, "ERROR: Single argument required\n"); |
| fprintf(stderr, "USAGE: %s [<fb-driver-path>]\n", argv[0]); |
| return FBCON_EXIT_FAIL; |
| } |
| |
| st = malloc(sizeof(struct fbcon_state_s)); |
| if (st == NULL) |
| { |
| gerr("ERROR: Unable to allocate fbcon_state_s memory"); |
| return -ENOMEM; |
| } |
| |
| st->pinfo = malloc(sizeof(struct fb_planeinfo_s)); |
| if (st->pinfo == NULL) |
| { |
| gerr("ERROR: Unable to allocate pinfo memory"); |
| return -ENOMEM; |
| } |
| |
| st->vinfo = malloc(sizeof(struct fb_videoinfo_s)); |
| if (st->vinfo == NULL) |
| { |
| gerr("ERROR: Unable to allocate vinfo memory"); |
| return -ENOMEM; |
| } |
| |
| /* Open the framebuffer driver */ |
| |
| st->fd_fb = open(fbdev, O_RDWR); |
| if (st->fd_fb < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: Failed to open %s: %d\n", fbdev, errcode); |
| return FBCON_EXIT_FD; |
| } |
| |
| /* Get the characteristics of the framebuffer */ |
| |
| ret = ioctl(st->fd_fb, FBIOGET_VIDEOINFO, |
| (unsigned long)((uintptr_t)st->vinfo)); |
| if (ret < 0) |
| { |
| int errcode = errno; |
| fprintf(stderr, "ERROR: ioctl(FBIOGET_VIDEOINFO) failed: %d\n", |
| errcode); |
| exitcode = FBCON_EXIT_GETVINFO; |
| goto errout; |
| } |
| |
| #ifdef CONFIG_FB_OVERLAY |
| gerr("ERROR: noverlays: %u\n", st->vinfo->noverlays); |
| |
| /* Select the first overlay, which should be the composed framebuffer */ |
| |
| ret = ioctl(st->fd_fb, FBIO_SELECT_OVERLAY, 0); |
| if (ret < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: ioctl(FBIO_SELECT_OVERLAY) failed: %d\n", errcode); |
| ret = FBCON_EXIT_FBIO_SELECT_OVERLAY |
| goto errout; |
| } |
| |
| /* Get the first overlay information */ |
| |
| st->oinfo.overlay = 0; |
| ret = ioctl(st->fd_fb, FBIOGET_OVERLAYINFO, |
| (unsigned long)((uintptr_t)&st->oinfo)); |
| if (ret < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: ioctl(FBIOGET_OVERLAYINFO) failed: %d\n", errcode); |
| ret = FBCON_EXIT_FBIO_OVERLAY_INFO |
| goto errout; |
| } |
| |
| /* select default framebuffer layer */ |
| |
| ret = ioctl(st->fd_fb, FBIO_SELECT_OVERLAY, FB_NO_OVERLAY); |
| if (ret < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: ioctl(FBIO_SELECT_OVERLAY) failed: %d\n", errcode); |
| ret = FBCON_EXIT_FBIO_SELECT_OVERLAY |
| goto errout; |
| } |
| |
| #endif |
| |
| if ((ret = fbdev_get_pinfo(st->fd_fb, st->pinfo)) < 0) |
| { |
| ret = FBCON_EXIT_GETPINFO; |
| goto errout; |
| } |
| |
| /* mmap() the framebuffer. |
| * |
| * NOTE: In the FLAT build the frame buffer address returned by the |
| * FBIOGET_PLANEINFO IOCTL command will be the same as the framebuffer |
| * address. mmap(), however, is the preferred way to get the framebuffer |
| * address because in the KERNEL build, it will perform the necessary |
| * address mapping to make the memory accessible to the application. |
| */ |
| |
| st->fbmem = mmap(NULL, st->pinfo->fblen, PROT_READ | PROT_WRITE, |
| MAP_SHARED | MAP_FILE, st->fd_fb, 0); |
| if (st->fbmem == MAP_FAILED) |
| { |
| gerr("ERROR: ioctl(FBIOGET_PLANEINFO) failed: %d\n", errno); |
| ret = FBCON_EXIT_FBMEM; |
| goto errout; |
| } |
| |
| #if 0 |
| /* Revisit needed = no support for dual framebuffers yet */ |
| |
| if (st->pinfo->yres_virtual == (st->vinfo->yres * 2)) |
| { |
| if ((ret = fb_init_mem2(st)) < 0) |
| { |
| ret = FBCON_EXIT_FBMEM2; |
| goto errout; |
| } |
| } |
| |
| #endif |
| ret = fbcon_initialize(st); |
| if (ret != OK) |
| { |
| exitcode = ret; |
| goto errout; |
| } |
| |
| fbcon_home(st); |
| fbcon_showcursor(st); |
| |
| /* Pipe and duplicate STDOUT and/or STDERR */ |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDOUT |
| ret = pipe(g_nsh_stdout); |
| if (ret < 0) |
| { |
| _err("stdout pipe failed: %d\n", errno); |
| return ERROR; |
| } |
| |
| close(STDOUT_FILENO); |
| dup2(g_nsh_stdout[WRITE_PIPE], 1); |
| #endif |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDERR |
| ret = pipe(g_nsh_stderr); |
| if (ret < 0) |
| { |
| _err("stderr pipe failed: %d\n", errno); |
| return ERROR; |
| } |
| |
| close(STDERR_FILENO); |
| dup2(g_nsh_stderr[WRITE_PIPE], 2); |
| #endif |
| |
| ret = posix_spawn_file_actions_init(&actions); |
| if (ret != 0) |
| { |
| gerr("ERROR: unable to init spawn sctiond for" |
| "requested task " "\"""%s""\"" ".\n", SPAWN_TASK); |
| exitcode = FBCON_EXIT_POSIX_SPAWN_ACTIONS_INIT_FAILED; |
| goto errout; |
| } |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDIN |
| /* Spawned app/task may need to intercept and forward STDIN |
| * otherwise any waiting input characters can be missed if read by the |
| * spawned app/task first. |
| */ |
| |
| ret = pipe(g_nsh_stdin); |
| if (ret < 0) |
| { |
| gerr("ERROR: stdin pipe failed: %d\n", errno); |
| exitcode = FBCON_EXIT_STDIN_PIPE_FAILED; |
| goto errout; |
| } |
| |
| posix_spawn_file_actions_addclose(&actions, STDIN_FILENO); |
| posix_spawn_file_actions_adddup2(&actions, g_nsh_stdin[READ_PIPE], |
| STDIN_FILENO); |
| |
| #endif /* CONFIG_EXAMPLES_FBCON_PIPE_STDIN */ |
| |
| index = builtin_isavail(SPAWN_TASK); |
| if (index < 0) |
| { |
| gerr("ERROR: requested task " "\"""%s""\"" " not available.\n", |
| SPAWN_TASK); |
| exitcode = FBCON_EXIT_APP_INDEX_UNAVAILABLE; |
| goto errout; |
| } |
| |
| builtin = builtin_for_index(index); |
| if (builtin == NULL) |
| { |
| gerr("ERROR: requested task " "\"""%s""\"" |
| " has no scheduling parameters.\n", SPAWN_TASK); |
| exitcode = FBCON_EXIT_APP_INDEX_UNAVAILABLE; |
| goto errout; |
| } |
| |
| /* Set up for app/task spawn */ |
| |
| ret = posix_spawnattr_init(&attr); |
| if (ret != 0) |
| { |
| gerr("ERROR: unable to init spawn attributes for" |
| "requested task " "\"""%s""\"" ".\n", SPAWN_TASK); |
| exitcode = FBCON_EXIT_APP_POSIX_ATTRIB_INIT_FAILED; |
| goto errout; |
| } |
| |
| attr.stacksize = builtin->stacksize; |
| attr.priority = builtin->priority; |
| |
| /* Spawn required application */ |
| |
| ret = posix_spawn(&pid, /* Returned Task ID */ |
| SPAWN_TASK, /* Task Path */ |
| &actions, /* Replace STDIN and/or STDOUT and/or STDIN */ |
| &attr, /* Attributes of app/task */ |
| NULL, /* Arguments */ |
| NULL); /* No environment */ |
| if (ret < 0) |
| { |
| int errcode = errno; |
| gerr("ERROR: posix_spawn failed: %d\n", errcode); |
| exitcode = FBCON_EXIT_POSIX_SPAWN_FAILED; |
| } |
| |
| #ifdef CONFIG_EXAMPLES_FBCON_SHOW_WELCOME |
| # ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDOUT |
| fprintf(stdout, "%s\n", g_stdout_hello); |
| fflush(stdout); |
| # endif |
| # ifdef CONFIG_EXAMPLES_FBCON_PIPE_STDERR |
| fprintf(stderr, "%s\n", g_stderr_hello); |
| fflush(stderr); |
| # endif |
| #endif |
| |
| for (; ; ) |
| { |
| poll_std_streams(st); |
| usleep(10000); |
| } |
| |
| errout: |
| close(st->fd_fb); |
| fprintf(stderr, "FBCON exiting with error %d\n", exitcode); |
| return exitcode; |
| } |
| |