mirror of
https://github.com/apache/nuttx-apps.git
synced 2025-07-04 11:04:13 +08:00
2656 lines
71 KiB
C
2656 lines
71 KiB
C
/****************************************************************************
|
|
* 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;
|
|
}
|
|
|