nuttx-apps/graphics/nxwm/src/cnxterm.cxx

687 lines
18 KiB
C++
Raw Normal View History

/********************************************************************************************
* apps/graphics/nxwm/src/cnxterm.cxx
*
* Copyright (C) 2012. 2014, 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
* me be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
********************************************************************************************/
/********************************************************************************************
* Included Files
********************************************************************************************/
#include <nuttx/config.h>
#include <cstdio>
#include <cstdlib>
#include <cunistd>
#include <ctime>
#include <sys/boardctl.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sched.h>
#include <assert.h>
#include <debug.h>
2016-07-21 23:38:52 +02:00
#include "nshlib/nshlib.h"
#include "graphics/nxwidgets/cwidgetcontrol.hxx"
#include "graphics/nxwm/nxwmconfig.hxx"
#include "graphics/nxglyphs.hxx"
#include "graphics/nxwm/cnxterm.hxx"
/********************************************************************************************
* Pre-Processor Definitions
********************************************************************************************/
/* Configuration ****************************************************************************/
#ifdef CONFIG_NSH_USBKBD
# warning You probably do not really want CONFIG_NSH_USBKBD, try CONFIG_NXWM_KEYBOARD_USBHOST
#endif
/********************************************************************************************
* Private Types
********************************************************************************************/
namespace NxWM
{
/**
* This structure is used to pass start up parameters to the NxTerm task and to assure the
* the NxTerm is successfully started.
*/
struct SNxTerm
{
FAR void *console; /**< The console 'this' pointer use with on_exit() */
sem_t exclSem; /**< Sem that gives exclusive access to this structure */
sem_t waitSem; /**< Sem that posted when the task is initialized */
NXTKWINDOW hwnd; /**< Window handle */
NXTERM nxterm; /**< NxTerm handle */
int minor; /**< Next device minor number */
struct nxterm_window_s wndo; /**< Describes the NxTerm window */
bool result; /**< True if successfully initialized */
};
/********************************************************************************************
* Private Data
********************************************************************************************/
/**
* This global data structure is used to pass start parameters to NxTerm task and to
* assure that the NxTerm is successfully started.
*/
static struct SNxTerm g_nxtermvars;
}
/********************************************************************************************
* Private Functions
********************************************************************************************/
/********************************************************************************************
* CNxTerm Method Implementations
********************************************************************************************/
using namespace NxWM;
/**
* CNxTerm constructor
*
* @param window. The application window
*/
CNxTerm::CNxTerm(CTaskbar *taskbar, CApplicationWindow *window)
{
// Save the constructor data
m_taskbar = taskbar;
m_window = window;
2019-09-29 20:53:39 +02:00
// The NxTerm is not running
m_pid = -1;
m_nxterm = 0;
// Add our personalized window label
NXWidgets::CNxString myName = getName();
window->setWindowLabel(myName);
// Add our callbacks with the application window
window->registerCallbacks(static_cast<IApplicationCallback *>(this));
}
/**
* CNxTerm destructor
*
* @param window. The application window
*/
CNxTerm::~CNxTerm(void)
{
// There would be a problem if we were stopped with the NxTerm task
// running... that should never happen but we'll check anyway:
stop();
// Although we didn't create it, we are responsible for deleting the
// application window
delete m_window;
}
/**
* Each implementation of IApplication must provide a method to recover
* the contained CApplicationWindow instance.
*/
IApplicationWindow *CNxTerm::getWindow(void) const
{
return static_cast<IApplicationWindow*>(m_window);
}
/**
* Get the icon associated with the application
*
* @return An instance if IBitmap that may be used to rend the
* application's icon. This is an new IBitmap instance that must
* be deleted by the caller when it is no long needed.
*/
NXWidgets::IBitmap *CNxTerm::getIcon(void)
{
NXWidgets::CRlePaletteBitmap *bitmap =
new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_NXTERM_ICON);
return bitmap;
}
/**
* Get the name string associated with the application
*
* @return A copy if CNxString that contains the name of the application.
*/
NXWidgets::CNxString CNxTerm::getName(void)
{
return NXWidgets::CNxString("NuttShell");
}
/**
* Start the application (perhaps in the minimized state).
*
* @return True if the application was successfully started.
*/
bool CNxTerm::run(void)
{
// Some sanity checking
if (m_pid >= 0 || m_nxterm != 0)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: All ready running or connected\n");
return false;
}
// Get exclusive access to the global data structure
if (sem_wait(&g_nxtermvars.exclSem) != 0)
{
// This might fail if a signal is received while we are waiting.
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed to get semaphore\n");
return false;
}
// Recover the NXTK window instance contained in the application window
NXWidgets::INxWindow *window = m_window->getWindow();
// Get the widget control associated with the NXTK window
NXWidgets::CWidgetControl *control = window->getWidgetControl();
// Get the window handle from the widget control
g_nxtermvars.hwnd = control->getWindowHandle();
// Describe the NxTerm
g_nxtermvars.wndo.wcolor[0] = CONFIG_NXWM_NXTERM_WCOLOR;
g_nxtermvars.wndo.fcolor[0] = CONFIG_NXWM_NXTERM_FONTCOLOR;
g_nxtermvars.wndo.fontid = CONFIG_NXWM_NXTERM_FONTID;
// Remember the device minor number (before it is incremented)
m_minor = g_nxtermvars.minor;
// Get the size of the window
window->getSize(&g_nxtermvars.wndo.wsize);
// Start the NxTerm task
g_nxtermvars.console = (FAR void *)this;
g_nxtermvars.result = false;
g_nxtermvars.nxterm = 0;
sched_lock();
m_pid = task_create("NxTerm", CONFIG_NXWM_NXTERM_PRIO,
CONFIG_NXWM_NXTERM_STACKSIZE, nxterm,
(FAR char * const *)0);
// Did we successfully start the NxTerm task?
bool result = true;
if (m_pid < 0)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed to create the NxTerm task\n");
result = false;
}
else
{
// Wait for up to two seconds for the task to initialize
struct timespec abstime;
clock_gettime(CLOCK_REALTIME, &abstime);
abstime.tv_sec += 2;
int ret = sem_timedwait(&g_nxtermvars.waitSem, &abstime);
sched_unlock();
if (ret == OK && g_nxtermvars.result)
{
// Re-direct NX keyboard input to the new NxTerm driver
DEBUGASSERT(g_nxtermvars.nxterm != 0);
#ifdef CONFIG_NXTERM_NXKBDIN
window->redirectNxTerm(g_nxtermvars.nxterm);
#endif
// Save the handle to use in the stop method
m_nxterm = g_nxtermvars.nxterm;
}
else
{
// sem_timedwait failed OR the NxTerm task reported a
// failure. Stop the application
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed start the NxTerm task\n");
stop();
result = false;
}
}
sem_post(&g_nxtermvars.exclSem);
return result;
}
/**
* Stop the application.
*/
void CNxTerm::stop(void)
{
// Delete the NxTerm task if it is still running (this could strand
// resources). If we get here due to CTaskbar::stopApplication() processing
// initialed by CNxTerm::exitHandler, then do *not* delete the task (it
// is already being deleted).
if (m_pid >= 0)
{
// Calling task_delete() will also invoke the on_exit() handler. We se
// m_pid = -1 before calling task_delete() to let the on_exit() handler,
// CNxTerm::exitHandler(), know that it should not do anything
2014-04-14 00:26:44 +02:00
pid_t pid = m_pid;
m_pid = -1;
// Then delete the NSH task, possibly stranding resources
task_delete(pid);
}
2014-04-14 00:26:44 +02:00
// Destroy the NX console device
if (m_nxterm)
{
// Re-store NX keyboard input routing
#ifdef CONFIG_NXTERM_NXKBDIN
NXWidgets::INxWindow *window = m_window->getWindow();
window->redirectNxTerm((NXTERM)0);
#endif
// Unlink the NxTerm driver
// Construct the driver name using this minor number
char devname[32];
snprintf(devname, 32, "/dev/nxterm%d", m_minor);
unlink(devname);
m_nxterm = 0;
}
}
/**
* Destroy the application and free all of its resources. This method
* will initiate blocking of messages from the NX server. The server
* will flush the window message queue and reply with the blocked
* message. When the block message is received by CWindowMessenger,
* it will send the destroy message to the start window task which
* will, finally, safely delete the application.
*/
void CNxTerm::destroy(void)
{
// Block any further window messages
m_window->block(this);
// Make sure that the application is stopped
stop();
}
/**
* The application window is hidden (either it is minimized or it is
* maximized, but not at the top of the hierarchy
*/
void CNxTerm::hide(void)
{
// Disable drawing and events
}
/**
* Redraw the entire window. The application has been maximized or
* otherwise moved to the top of the hierarchy. This method is call from
* CTaskbar when the application window must be displayed
*/
void CNxTerm::redraw(void)
{
// Recover the NXTK window instance contained in the application window
NXWidgets::INxWindow *window = m_window->getWindow();
// Get the size of the window
struct nxgl_size_s windowSize;
window->getSize(&windowSize);
// Redraw the entire NxTerm window
struct boardioc_nxterm_ioctl_s iocargs;
struct nxtermioc_redraw_s redraw;
redraw.handle = m_nxterm;
redraw.rect.pt1.x = 0;
redraw.rect.pt1.y = 0;
redraw.rect.pt2.x = windowSize.w - 1;
redraw.rect.pt2.y = windowSize.h - 1;
redraw.more = false;
iocargs.cmd = NXTERMIOC_NXTERM_REDRAW;
iocargs.arg = (uintptr_t)&redraw;
boardctl(BOARDIOC_NXTERM_IOCTL, (uintptr_t)&iocargs);
}
/**
* Report of this is a "normal" window or a full screen window. The
* primary purpose of this method is so that window manager will know
* whether or not it show draw the task bar.
*
* @return True if this is a full screen window.
*/
bool CNxTerm::isFullScreen(void) const
{
return m_window->isFullScreen();
}
/**
* This is the NxTerm task. This function first redirects output to the
* console window then calls to start the NSH logic.
*/
int CNxTerm::nxterm(int argc, char *argv[])
{
// To stop compiler complaining about "jump to label crosses initialization
// of 'int fd'
int fd = -1;
int ret = OK;
// Set up an on_exit() event that will be called when this task exits
if (on_exit(exitHandler, g_nxtermvars.console) != 0)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: on_exit failed\n");
goto errout;
}
// Use the window handle to create the NX console
struct boardioc_nxterm_create_s nxcreate;
nxcreate.nxterm = (FAR void *)0;
nxcreate.hwnd = g_nxtermvars.hwnd;
nxcreate.wndo = g_nxtermvars.wndo;
nxcreate.type = BOARDIOC_XTERM_FRAMED;
nxcreate.minor = g_nxtermvars.minor;
ret = boardctl(BOARDIOC_NXTERM, (uintptr_t)&nxcreate);
if (ret < 0)
{
gerr("ERROR: boardctl(BOARDIOC_NXTERM) failed: %d\n", errno);
goto errout;
}
g_nxtermvars.nxterm = nxcreate.nxterm;
DEBUGASSERT(g_nxtermvars.nxterm != NULL);
// Construct the driver name using this minor number
char devname[32];
snprintf(devname, 32, "/dev/nxterm%d", g_nxtermvars.minor);
// Increment the minor number while it is protect by the semaphore
g_nxtermvars.minor++;
// Open the NxTerm driver
#ifdef CONFIG_NXTERM_NXKBDIN
fd = open(devname, O_RDWR);
#else
fd = open(devname, O_WRONLY);
#endif
if (fd < 0)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed open the console device\n");
unlink(devname);
goto errout;
}
// Now re-direct stdout and stderr so that they use the NX console driver.
// Notes: (1) stdin is retained (file descriptor 0, probably the the serial
// console). (2) Don't bother trying to put debug instrumentation in the
// following because it will end up in the NxTerm window.
std::fflush(stdout);
std::fflush(stderr);
#ifdef CONFIG_NXTERM_NXKBDIN
std::dup2(fd, 0);
#endif
std::dup2(fd, 1);
std::dup2(fd, 2);
// And we can close our original driver file descriptor
if (fd > 2)
{
std::close(fd);
}
// Inform the parent thread that we successfully initialized
g_nxtermvars.result = true;
sem_post(&g_nxtermvars.waitSem);
// Run the NSH console
#ifdef CONFIG_NSH_CONSOLE
nsh_consolemain(argc, argv);
#endif
// We get here if the NSH console should exits. nsh_consolemain() ALWAYS
// exits by calling nsh_exit() (which is a pointer to nsh_consoleexit())
// which, in turn, calls exit()
return EXIT_SUCCESS;
errout:
g_nxtermvars.nxterm = 0;
g_nxtermvars.result = false;
sem_post(&g_nxtermvars.waitSem);
return EXIT_FAILURE;
}
/**
* This is the NxTerm task exit handler. It registered with on_exit()
* and called automatically when the nxterm task exits.
*/
void CNxTerm::exitHandler(int code, FAR void *arg)
{
CNxTerm *This = (CNxTerm *)arg;
// If we got here because of the task_delete() call in CNxTerm::stop(),
// then m_pid will be set to -1 to let us know that we do not need to do
// anything
if (This->m_pid >= 0)
{
// Set m_pid to -1 to prevent calling detlete_task() in CNxTerm::stop().
// CNxTerm::stop() is called by the processing initiated by the following
// call to CTaskbar::stopApplication()
This->m_pid = -1;
// Remove the NxTerm application from the taskbar
This->m_taskbar->stopApplication(This);
}
}
/**
* Called when the window minimize button is pressed.
*/
void CNxTerm::minimize(void)
{
m_taskbar->minimizeApplication(static_cast<IApplication*>(this));
}
/**
* Called when the window close button is pressed.
*/
void CNxTerm::close(void)
{
m_taskbar->stopApplication(static_cast<IApplication*>(this));
}
/**
* CNxTermFactory Constructor
*
* @param taskbar. The taskbar instance used to terminate the console
*/
CNxTermFactory::CNxTermFactory(CTaskbar *taskbar)
{
m_taskbar = taskbar;
}
/**
* Create a new instance of an CNxTerm (as IApplication).
*/
IApplication *CNxTermFactory::create(void)
{
// Call CTaskBar::openFullScreenWindow to create a full screen window for
// the NxTerm application
CApplicationWindow *window = m_taskbar->openApplicationWindow();
if (!window)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed to create CApplicationWindow\n");
return (IApplication *)0;
}
// Open the window (it is hot in here)
if (!window->open())
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed to open CApplicationWindow\n");
delete window;
return (IApplication *)0;
}
// Instantiate the application, providing the window to the application's
// constructor
CNxTerm *nxterm = new CNxTerm(m_taskbar, window);
if (!nxterm)
{
2016-06-11 23:54:33 +02:00
gerr("ERROR: Failed to instantiate CNxTerm\n");
delete window;
return (IApplication *)0;
}
return static_cast<IApplication*>(nxterm);
}
/**
* Get the icon associated with the application
*
* @return An instance if IBitmap that may be used to rend the
* application's icon. This is an new IBitmap instance that must
* be deleted by the caller when it is no long needed.
*/
NXWidgets::IBitmap *CNxTermFactory::getIcon(void)
{
NXWidgets::CRlePaletteBitmap *bitmap =
new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_NXTERM_ICON);
return bitmap;
}
/**
* One time NSH initialization. This function must be called exactly
* once during the boot-up sequence to initialize the NSH library.
*
* @return True on successful initialization
*/
bool NxWM::nshlibInitialize(void)
{
// Initialize the global data structure
sem_init(&g_nxtermvars.exclSem, 0, 1);
sem_init(&g_nxtermvars.waitSem, 0, 0);
// Initialize the NSH library
nsh_initialize();
// If the Telnet console is selected as a front-end, then start the
// Telnet daemon.
#ifdef CONFIG_NSH_TELNET
int ret = nsh_telnetstart(AF_UNSPEC);
if (ret < 0)
{
// The daemon is NOT running!
return false;
}
#endif
return true;
}