nuttx-apps/graphics/twm4nx/apps/cclock.cxx
Xiang Xiao 857158451b Unify the void cast usage
1.Remove void cast for function because many place ignore the returned value witout cast
2.Replace void cast for variable with UNUSED macro

Change-Id: Ie644129a563244a6397036789c4c3ea83c4e9b09
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2020-01-02 23:21:01 +08:00

660 lines
17 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// apps/graphics/twm4nx/src/cnxterm.cxx
// Clock window
//
// Copyright (C) 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 nor the names of its contributors may 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 <ctime>
#include <cstring>
#include <csched>
#include <cassert>
#include <semaphore.h>
#include <debug.h>
#include <nuttx/semaphore.h>
#include "nshlib/nshlib.h"
#include "graphics/nxwidgets/cwidgetcontrol.hxx"
#include "graphics/nxglyphs.hxx"
#include "graphics/twm4nx/twm4nx_config.hxx"
#include "graphics/twm4nx/ctwm4nx.hxx"
#include "graphics/twm4nx/cwindow.hxx"
#include "graphics/twm4nx/cwindowfactory.hxx"
#include "graphics/twm4nx/cmainmenu.hxx"
#include "graphics/slcd.hxx"
#include "graphics/twm4nx/apps/clock_config.hxx"
#include "graphics/twm4nx/apps/cclock.hxx"
/////////////////////////////////////////////////////////////////////////////
// Private Types
/////////////////////////////////////////////////////////////////////////////
namespace Twm4Nx
{
/////////////////////////////////////////////////////////////////////////////
// Private Types
/////////////////////////////////////////////////////////////////////////////
/**
* This structure is used to pass start up parameters to the Clock task
* and to assure the the Clock is successfully started.
*/
struct SClock
{
FAR CClock *that; /**< The CClock 'this' pointer */
sem_t exclSem; /**< Sem that gives exclusive access to this structure */
sem_t waitSem; /**< Sem that posted when the task is initialized */
bool success; /**< True if successfully initialized */
};
/////////////////////////////////////////////////////////////////////////////
// Private Data
/////////////////////////////////////////////////////////////////////////////
/**
* This global data structure is used to pass start parameters to Clock
* task and to assure that the Clock is successfully started.
*/
static struct SClock GClockVars;
}
/////////////////////////////////////////////////////////////////////////////
// CClock Method Implementations
/////////////////////////////////////////////////////////////////////////////
using namespace Twm4Nx;
/**
* CClock constructor
*
* @param twm4nx. The Twm4Nx session instance
*/
CClock::CClock(FAR CTwm4Nx *twm4nx)
{
// Save/initialize the context data
m_twm4nx = twm4nx;
m_window = (FAR CWindow *)0;
m_slcd = (FAR SLcd::CSLcd *)0;
// The clock task is not running
m_pid = (pid_t)-1;
// This is un-neccessary but helpful in debugging to have a known value
std::memset(m_digits, 0, CLOCK_NDIGITS * sizeof(struct SClockDigit));
}
/**
* CClock destructor
*/
CClock::~CClock(void)
{
// There would be a problem if we were stopped with the Clock task
// running... that should never happen but we'll check anyway:
stop();
// Destroy the CSLcd instance
if (m_window != (FAR CWindow *)0)
{
delete m_window;
}
// Destroy the CSLcd instance
if (m_slcd != (FAR SLcd::CSLcd *)0)
{
delete m_slcd;
}
}
/**
* CClock initializers. Perform miscellaneous post-construction
* initialization that may fail (and hence is not appropriate to be
* done in the constructor)
*
* @return True if the Clock application was successfully initialized.
*/
bool CClock::initialize(void)
{
// Call CWindowFactory::createWindow() to create a window for the Clock
// application. Customizations:
//
// Flags:
// WFLAGS_NO_MENU_BUTTON No menu button in the toolbar
// WFLAGS_NO_RESIZE_BUTTON No resize button in the toolbar
// WFLAGS_HIDDEN Window is initially hidden
//
// Null Icon manager means to use the system, common Icon Manager
NXWidgets::CNxString name("Clock");
uint8_t wflags = (WFLAGS_NO_MENU_BUTTON | WFLAGS_NO_RESIZE_BUTTON |
WFLAGS_HIDDEN);
FAR CWindowFactory *factory = m_twm4nx->getWindowFactory();
m_window = factory->createWindow(name, &CONFIG_TWM4NX_CLOCK_ICON,
(FAR CIconMgr *)0, wflags);
if (m_window == (FAR CWindow *)0)
{
twmerr("ERROR: Failed to create CWindow\n");
return false;
}
// Get the minimum tool bar
// Configure events needed by the Clock applications
struct SAppEvents events;
events.eventObj = (FAR void *)this;
events.redrawEvent = EVENT_CLOCK_REDRAW;
events.resizeEvent = EVENT_CLOCK_RESIZE;
events.mouseEvent = EVENT_CLOCK_XYINPUT;
events.kbdEvent = EVENT_CLOCK_KBDINPUT;
events.closeEvent = EVENT_CLOCK_CLOSE;
events.deleteEvent = EVENT_CLOCK_DELETE;
bool success = m_window->configureEvents(events);
if (!success)
{
delete m_window;
m_window = (FAR CWindow *)0;
return false;
}
// Create an instance of the segment LCD emulation
m_slcd = new SLcd::CSLcd(m_window->getNxWindow(),
CONFIG_TWM4NX_CLOCK_HEIGHT);
if (m_slcd == (FAR SLcd::CSLcd *)0)
{
twmerr("ERROR: Failed to create SLcd::CSLcd instance\n");
delete m_window;
m_window = (FAR CWindow *)0;
return false;
}
// Set the correct size and position of the window based on the SLCD
// character height.
nxgl_coord_t segmentWidth = m_slcd->getWidth();
nxgl_coord_t gapWidth = segmentWidth / 2;
struct nxgl_size_s windowSize;
windowSize.w = 4 * (segmentWidth + CONFIG_TWM4NX_CLOCK_HSPACING) + gapWidth;
windowSize.h = m_slcd->getHeight() + 2 * CONFIG_TWM4NX_CLOCK_HSPACING;
// Check against the minimum toolbar width
nxgl_coord_t minWidth = minimumToolbarWidth(m_twm4nx, name, wflags);
nxgl_coord_t xOffset = CONFIG_TWM4NX_CLOCK_HSPACING;
if (windowSize.w < minWidth)
{
xOffset += (minWidth - windowSize.w) / 2;
windowSize.w = minWidth;
}
// Resize and position the frame. The window is initially position in
// the upper left hand corner of the display.
struct nxgl_size_s frameSize;
m_window->windowToFrameSize(&windowSize, &frameSize);
struct nxgl_point_s framePos;
framePos.x = 0;
framePos.y = 0;
if (!m_window->resizeFrame(&frameSize, &framePos))
{
delete m_window;
m_window = (FAR CWindow *)0;
delete m_slcd;
m_slcd = (FAR SLcd::CSLcd *)0;
return false;
}
// Initialize the SLCD digit horizontal positions
m_digits[0].xOffset = xOffset;
m_digits[1].xOffset = m_digits[0].xOffset + segmentWidth +
CONFIG_TWM4NX_CLOCK_HSPACING;
m_digits[2].xOffset = m_digits[1].xOffset + segmentWidth +
gapWidth;
m_digits[3].xOffset = m_digits[2].xOffset + segmentWidth +
CONFIG_TWM4NX_CLOCK_HSPACING;
// Display the initial time
redraw();
// Now we can show the completed window
return m_window->showWindow();
}
/**
* Start the application (perhaps in the minimized state).
*
* @return True if the application was successfully started.
*/
bool CClock::run(void)
{
// Some sanity checking
if (m_pid >= 0)
{
twmerr("ERROR: All ready running?\n");
return false;
}
// Get exclusive access to the global data structure
if (sem_wait(&GClockVars.exclSem) != 0)
{
// This might fail if a signal is received while we are waiting. Or,
// perhaps if the task is canceled.
twmerr("ERROR: Failed to get semaphore\n");
return false;
}
// Initialize the rest of parameter passing area for temporary use to
// start the new Clock task
sem_init(&GClockVars.waitSem, 0, 0);
sem_setprotocol(&GClockVars.waitSem, SEM_PRIO_NONE);
GClockVars.that = this;
GClockVars.success = false;
// Start the Clock task
sched_lock();
m_pid = task_create("Clock", CONFIG_TWM4NX_CLOCK_PRIO,
CONFIG_TWM4NX_CLOCK_STACKSIZE, clock,
(FAR char * const *)0);
// Did we successfully start the Clock task?
bool success = true;
if (m_pid < 0)
{
twmerr("ERROR: Failed to create the Clock task\n");
success = 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(&GClockVars.waitSem, &abstime);
sched_unlock();
if (ret < 0 || !GClockVars.success)
{
// sem_timedwait failed OR the Clock task reported a
// failure. Stop the application
twmerr("ERROR: Failed start the Clock task\n");
stop();
success = false;
}
}
sem_destroy(&GClockVars.waitSem);
sem_post(&GClockVars.exclSem);
return success;
}
/**
* This is the close window event handler. It will stop the Clock
* application thread.
*/
void CClock::stop(void)
{
// Delete the Clock task if it is still running (this could strand
// resources).
if (m_pid >= 0)
{
pid_t pid = m_pid;
m_pid = (pid_t)-1;
// Then delete the NSH task, possibly stranding resources
std::task_delete(pid);
}
}
/**
* This is the Clock task.
*/
int CClock::clock(int argc, char *argv[])
{
// Get the 'this' pointer
FAR CClock *This = GClockVars.that;
// Inform the parent thread that we successfully initialized
GClockVars.success = true;
sem_post(&GClockVars.waitSem);
// Loop forever. When the window is deleted, this task will be brutally
// terminated via task_delete()
for (; ; )
{
// Update the clock
This->update();
// Then sleep for a minute. REVISIT: It might make sense to check
// the time at a much higher rate?
sleep(60);
}
return EXIT_FAILURE; // Never get here
}
/**
* Handle Twm4Nx events. This overrides a method from CTwm4NXEvent
*
* @param eventmsg. The received NxWidget WINDOW event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CClock::event(FAR struct SEventMsg *eventmsg)
{
bool success = true;
switch (eventmsg->eventID)
{
case EVENT_CLOCK_REDRAW: // Redraw event (doesn't happen)
redraw(); // Redraw the whole window
break;
case EVENT_CLOCK_CLOSE: // Window close event
stop(); // Stop the Clock thread
break;
default:
success = false;
break;
}
return success;
}
/**
* Update the Clock.
*/
void CClock::update(void)
{
// Get the current time.
FAR struct std::timespec ts;
int ret = std::clock_gettime(CLOCK_REALTIME, &ts);
if (ret < 0)
{
twmerr("ERROR: clock_gettime() failed\n");
return;
}
// Break out the time
//
// tm_hour - The number of hours past midnight, in the range 0 to 23.
// tm_min - The number of minutes after the hour, in the range 0 to 59.
struct std::tm tm;
if (std::gmtime_r(&ts.tv_sec, &tm) == (FAR struct std::tm *)0)
{
twmerr("ERROR: gmtime_r() failed\n");
return;
}
// Convert hours and minutes into SLCD segment codes
//
// Conversion of 24 hour clock (0-23) to twelve hour (1-12)
// 0 -> 12 6 -> 6 12 -> 12 18 -> 6
// 1 -> 1 7 -> 7 13 -> 1 19 -> 7
// 2 -> 2 8 -> 8 14 -> 2 20 -> 8
// 3 -> 3 9 -> 9 15 -> 3 21 -> 9
// 4 -> 4 10 -> 10 16 -> 4 22 -> 10
// 5 -> 5 11 -> 11 17 -> 5 23 -> 11
unsigned int hour = tm.tm_hour; /* Range 0-23 */
if (hour == 0)
{
hour = 12;
}
else if (hour > 12) /* Range 1-23 */
{
hour -= 12; /* Range 1-11 */
}
uint8_t codes[CLOCK_NDIGITS];
char ascii = (hour > 9) ? '1' : ' ';
m_slcd->convert(ascii, codes[0]);
ascii = (hour % 10) + '0';
m_slcd->convert(ascii, codes[1]);
ascii = (tm.tm_min / 10) + '0';
m_slcd->convert(ascii, codes[2]);
ascii = (tm.tm_min % 10) + '0';
m_slcd->convert(ascii, codes[3]);
struct nxgl_point_s pos;
pos.y = CONFIG_TWM4NX_CLOCK_VSPACING;
// Then show each segment LCD
// There might be a more efficient way to do this than to erase the entire
// SLCD on each update.
for (int i = 0; i < CLOCK_NDIGITS; i++)
{
if (m_digits[i].segments != codes[i])
{
pos.x = m_digits[i].xOffset;
m_slcd->erase(pos);
m_slcd->show(codes[i], pos);
m_digits[i].segments = codes[i];
}
}
}
/**
* Redraw the entire clock.
*/
void CClock::redraw(void)
{
// Get the size of the window
struct nxgl_size_s windowSize;
m_window->getWindowSize(&windowSize);
// Create a bounding box
struct nxgl_rect_s windowRect;
windowRect.pt1.x = 0;
windowRect.pt1.y = 0;
windowRect.pt2.x = windowSize.w - 1;
windowRect.pt2.y = windowSize.h - 1;
// Fill the entire window with the clock background color
FAR NXWidgets::INxWindow *nxWindow = m_window->getNxWindow();
if (!nxWindow->fill(&windowRect, SLCD_BACKGROUND))
{
twmerr("ERROR: Failed to fill window");
return;
}
// Reset the saved codes to force a complete update
for (int i = 0; i < CLOCK_NDIGITS; i++)
{
m_digits[i].segments = 0;
}
// Then update the Clock window
update();
}
/////////////////////////////////////////////////////////////////////////////
// CClockFactory Method Implementations
/////////////////////////////////////////////////////////////////////////////
/**
* CClockFactory Initializer. Performs parts of the instance
* construction that may fail. In this implementation, it will
* initialize the NSH library and register an menu item in the
* Main Menu.
*/
bool CClockFactory::initialize(FAR CTwm4Nx *twm4nx)
{
// Save the session instance
m_twm4nx = twm4nx;
// Initialize the parameter passing area. Other fields are initialized
// by CClock thread-specific logic.
sem_init(&GClockVars.exclSem, 0, 1);
// Register an entry with the Main menu. When selected, this will
// Case the start
FAR CMainMenu *cmain = twm4nx->getMainMenu();
return cmain->addApplication(this);
}
/**
* Handle CClockFactory events. This overrides a method from
* CTwm4NXEvent
*
* @param eventmsg. The received NxWidget WINDOW event message.
* @return True if the message was properly handled. false is
* return on any failure.
*/
bool CClockFactory::event(FAR struct SEventMsg *eventmsg)
{
bool success = true;
switch (eventmsg->eventID)
{
case EVENT_CLOCK_START: // Main menu selection
startFunction(); // Create a new Clock instance
break;
default:
success = false;
break;
}
return success;
}
/**
* Create and start a new instance of CClock.
*/
bool CClockFactory::startFunction(void)
{
// Instantiate the Nxterm application, providing only the session session
// instance to the constructor
FAR CClock *clock = new CClock(m_twm4nx);
if (clock == (FAR CClock *)0)
{
twmerr("ERROR: Failed to instantiate CClock\n");
return false;
}
// Initialize the Clock application
if (!clock->initialize())
{
twmerr("ERROR: Failed to initialize CClock instance\n");
delete clock;
return false;
}
// Start the Clock application instance
if (!clock->run())
{
twmerr("ERROR: Failed to start the Clock application\n");
delete clock;
return false;
}
return true;
}