diff --git a/examples/fbcon/CMakeLists.txt b/examples/fbcon/CMakeLists.txt new file mode 100644 index 00000000000..0af45b1b9a6 --- /dev/null +++ b/examples/fbcon/CMakeLists.txt @@ -0,0 +1,34 @@ +# ############################################################################## +# apps/examples/fbcon/CMakeLists.txt +# +# 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. +# +# ############################################################################## + +if(CONFIG_EXAMPLES_FBCON) + nuttx_add_application( + NAME + fbcon + STACKSIZE + ${CONFIG_EXAMPLES_FBCON_STACKSIZE} + MODULE + ${CONFIG_EXAMPLES_FBCON} + SRCS + fbcon_main.c) + target_sources(apps PRIVATE fbcon_main.c) +endif() diff --git a/examples/fbcon/Kconfig b/examples/fbcon/Kconfig new file mode 100644 index 00000000000..eedf2213df4 --- /dev/null +++ b/examples/fbcon/Kconfig @@ -0,0 +1,205 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_FBCON + tristate "Framebuffer console example" + default n + ---help--- + Enable the Framebuffer console example. + This example allows STDOUT and/or STDERR to be redirected and displayed + on a framebuffer character device. + + A chosen builtin app can be spawned - such as "nsh" - to create a + console and, if required, STDIN can be intercepted to allow input + characters to be interpreted and pre-processed before passing them + on to the spawned app. + + Relies on and uses NX and NXFONTS + +if EXAMPLES_FBCON + +comment "Console STDIN and/or STDOUT setup" + +config EXAMPLES_FBCON_PIPE_STDOUT + bool "Pipe stdout to this console" + default y + +config EXAMPLES_FBCON_PIPE_STDERR + bool "Pipe stderr to this console" + default y + +config EXAMPLES_FBCON_PIPE_STDIN + bool "Pipe stdin via this console" + default y + ---help--- + This is usually needed if the spawned App is "nsh" as we need to + intercept input characters in some cases + +comment "Console Framebuffer setup" + +config EXAMPLES_FBCON_DEF_FB + string "Default framebuffer driver" + default "/dev/fb0" + ---help--- + Default framebuffer drivers. This selection can be overridden from + the command line. + +comment "BPP setup" + +choice EXAMPLES_FBCON_BPP_SELECTION + prompt "BPP Configuration" + default EXAMPLES_FBCON_BPP_NX_DEFAULT + +config EXAMPLES_FBCON_BPP_NX_DEFAULT + bool "Use smallest BPP as enabled via NXFONTS setup" + +config EXAMPLES_FBCON_CUSTOM_BPP + bool "Choose custom BPP (must not be disabled in NX)" + +choice EXAMPLES_FBCON_CUSTOM_BPP + prompt "Custom Font pixel depth (BPP - Bits Per Pixel)" + depends on EXAMPLES_FBCON_CUSTOM_BPP + ---help--- + Note: The required BPP must not be disabled via the NXFONT setup + +config EXAMPLES_FBCON_1BPP + bool "1 BPP" + depends on !NX_DISABLE_1BPP + +config EXAMPLES_FBCON_2BPP + bool "2 BPP" + depends on !NX_DISABLE_2BPP + +config EXAMPLES_FBCON_4BPP + bool "4 BPP" + depends on !NX_DISABLE_4BPP + +config EXAMPLES_FBCON_8BPP + bool "8 BPP" + depends on !NX_DISABLE_8BPP + +config EXAMPLES_FBCON_16BPP + bool "16 BPP" + depends on !NX_DISABLE_16BPP + +config EXAMPLES_FBCON_24BPP + bool "24 BPP" + depends on !NX_DISABLE_24BPP + +config EXAMPLES_FBCON_32BPP + bool "32 BPP" + depends on !NX_DISABLE_32BPP + +endchoice # EXAMPLES_FBCON_CUSTOM_BPP +endchoice # EXAMPLES_FBCON_BPP_SELECTION + +comment "Console appearance" + +config EXAMPLES_FBCON_SHOW_WELCOME + bool "Display welcome messages on stdout and/or stdin" + default y + +config EXAMPLES_FBCON_DEFAULT_COLORS + bool "Use Default Colors (white characters on black background)" + default y + +if !EXAMPLES_FBCON_DEFAULT_COLORS + +config EXAMPLES_FBCON_BGCOLOR + hex "Background color" + default 0x0 + ---help--- + The color of the background. Default depends on config + EXAMPLES_FBCON_BPP. + +config EXAMPLES_FBCON_FCOLOR + hex "Console font color" + default 0x0 + ---help--- + The color of the fonts used by the console. +endif # !EXAMPLES_FBCON_DEFAULT_COLORS + +config EXAMPLES_FBCON_LINESPACING + int "Line spacing" + default 0 + range 0 4 + ---help--- + The vertical distance between lines is the sum of (1) the vertical + bounding box dimension of the font, and (2) this additional line + space. This value may be zero, but not negative. + +config EXAMPLES_FBCON_NOWRAP + bool "No wrap" + default n + ---help--- + By default, lines will wrap when the test reaches the right hand side + of the window. This setting can be defining to change this behavior so + that the text is simply truncated until a new line is encountered. + +choice EXAMPLES_FBCON_FONT + prompt "Font Configuration" + default EXAMPLES_FBCON_DEFAULT_FONT + +config EXAMPLES_FBCON_DEFAULT_FONT + bool "Use Default Font" + +config EXAMPLES_FBCON_CUSTOM_FONTID + bool "Use Custom Font ID" + +config EXAMPLES_FBCON_FONTID + int "Custom font ID" + depends on EXAMPLES_FBCON_CUSTOM_FONTID + default 0 + ---help--- + Selects the font used by the console (see font ID numbers + in include/nuttx/nx/nxfonts.h) + +endchoice # EXAMPLES_FBCON_FONT + +config EXAMPLES_FBCON_CURSORCHAR + int "Character code to use as the cursor" + default 95 + ---help--- + The bitmap code to use as the cursor. Default '_' (95) + +comment "FB Console App Escape code decoding" + +config EXAMPLES_FBCON_VT100_DECODE + bool "Decode VT100 Escape Codes" + default y + ---help-- + Decode VT100 ESC codes - only minimal supporting functions + +comment "FB Console spawn task configuration" + +config EXAMPLES_FBCON_SPAWN_TASK + string "Built-in application, or task, to spawn after console setup" + default "nsh" + ---help--- + The required App must be enabled via Kconfig, of course. Its + priority and stack size will be determined by this example and passed + on during the spawn of the chosen app. + +comment "FB Console App stack and priority options and glyph cache size" + +config EXAMPLES_FBCON_STACKSIZE + int "Stack Size" + default DEFAULT_TASK_STACKSIZE + ---help--- + The stacksize to use when starting the example. + +config EXAMPLES_FBCON_PRIORITY + int "Task Priority" + default 100 + ---help--- + The priority of the example. + +config EXAMPLES_FBCON_GLCACHE + int "Glyph cache size" + default 94 + ---help--- + Size of the glyph cache. Default allows all usual ASCII characters to be cached + +endif # EXAMPLES_FBCON diff --git a/examples/fbcon/Make.defs b/examples/fbcon/Make.defs new file mode 100644 index 00000000000..21336dda1c1 --- /dev/null +++ b/examples/fbcon/Make.defs @@ -0,0 +1,25 @@ +############################################################################ +# apps/examples/fbcon/Make.defs +# +# 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. +# +############################################################################ + +ifneq ($(CONFIG_EXAMPLES_FBCON),) +CONFIGURED_APPS += $(APPDIR)/examples/fbcon +endif diff --git a/examples/fbcon/Makefile b/examples/fbcon/Makefile new file mode 100644 index 00000000000..f5795ce1773 --- /dev/null +++ b/examples/fbcon/Makefile @@ -0,0 +1,36 @@ +############################################################################ +# apps/examples/fbcon/Makefile +# +# 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. +# +############################################################################ + +include $(APPDIR)/Make.defs + +# NuttX NX Framebuffer Console Example. + +MAINSRC = fbcon_main.c + +# NXTEXT built-in application info + +PROGNAME = fbcon +PRIORITY = $(CONFIG_EXAMPLES_FBCON_PRIORITY) +STACKSIZE = $(CONFIG_EXAMPLES_FBCON_STACKSIZE) +MODULE = $(CONFIG_EXAMPLES_FBCON) + +include $(APPDIR)/Application.mk diff --git a/examples/fbcon/fbcon_main.c b/examples/fbcon/fbcon_main.c new file mode 100644 index 00000000000..2aa95e0c5de --- /dev/null +++ b/examples/fbcon/fbcon_main.c @@ -0,0 +1,2655 @@ +/**************************************************************************** + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 []\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; +} +