The out-of-the-box demo application flashed on all display modules before they are shipped.

Dependencies:   DMBasicGUI DMSupport

This is the software that is flashed on the LPC4088 Display Modules before they are shipped from Embedded Artists.

Information

This project works on both the 4.3" and 5" display modules but requires different file systems to handle the different display resolutions.

For the 4.3" displays first drag-n-drop the media/fs_480_raw.fs5 (if you are using the new DAPLINK firmware use fs_480_raw.hex) file to the MBED drive and then drag-n-drop the demo itself. This way both the file system and software are up to date.

For the 5" displays first drag-n-drop the media/fs_800_raw.fsF (if you are using the new DAPLINK firmware use fs_800_raw.hex) file to the MBED drive and then drag-n-drop the demo itself. This way both the file system and software are up to date.

There is a prebuilt version of the demo binary here.

This is what it looks like on a 4.3" display:

/media/uploads/embeddedartists/demo480_cap_000.png /media/uploads/embeddedartists/demo480_cap_001.png /media/uploads/embeddedartists/demo480_cap_002.png /media/uploads/embeddedartists/demo480_cap_004.png /media/uploads/embeddedartists/demo480_cap_006.png /media/uploads/embeddedartists/demo480_cap_007.png /media/uploads/embeddedartists/demo480_cap_008.png
The first slide from the Slideshow:
/media/uploads/embeddedartists/demo480_cap_003.png
A couple of images from the Image Viewer
/media/uploads/embeddedartists/demo480_cap_009.png /media/uploads/embeddedartists/demo480_cap_010.png

AppLauncherSpecial.cpp

Committer:
alindvall
Date:
2015-03-20
Revision:
0:b94e330c98ac
Child:
2:229f88d6f56b

File content as of revision 0:b94e330c98ac:

/*
 *  Copyright 2014 Embedded Artists AB
 *
 *  Licensed 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 "mbed.h"
#include "AppLauncherSpecial.h"
#include "lpc_swim_image.h"
#include "lpc_swim_font.h"
#include "Button.h"
#include "ImageButton.h"
#include "image_data.h"

/******************************************************************************
 * Defines and typedefs
 *****************************************************************************/
 
#define APP_PREFIX  "[Launcher*] "

#define NO_APPLICATION  (-1)

#define APP_SIGID_TOUCH  0x1

/******************************************************************************
 * Private variables
 *****************************************************************************/

static int appToLaunch = NO_APPLICATION;
static osThreadId appThread;

/******************************************************************************
 * Private functions
 *****************************************************************************/

static void buttonClicked(uint32_t x)
{
    if (appToLaunch == NO_APPLICATION) {
        appToLaunch = (int)x;
    }
}

void AppLauncherSpecial::draw()
{
    // Prepare fullscreen
    swim_window_open(_win, 
                     _disp->width(), _disp->height(),         // full size
                     (COLOR_T*)_fb,
                     0,0,_disp->width()-1, _disp->height()-1, // window position and size
                     0,                                     // border
                     BLACK, WHITE, /*WHITE, BLACK,*/ BLACK);                    // colors: pen, backgr, forgr
    //swim_set_title(_win, "Demo Program", WHITE);

    swim_put_image(_win, _bgImg.pixels, _bgImg.width, _bgImg.height);
    
    if (_supportsCalibration) {
      const char* msg = "(Press physical UserButton >2s to calibrate touch)";
      int w, h;
      swim_get_string_bounds(_win, msg, &w, &h);
      swim_set_font_transparency(_win, 0); // 0=Transparent, 1=Opaque
      swim_put_text_xy(_win, msg, (_disp->width()-w)/2, _disp->height()-(3*h)/2);
      swim_set_font_transparency(_win, 1); // 0=Transparent, 1=Opaque
    }
    
    for (int i = 0; i < _usedButtons; i++) {
        _buttons[i]->draw();
    }
}

void AppLauncherSpecial::onTouchEvent()
{
    _newTouchEvent = true;
    osSignalSet(appThread, APP_SIGID_TOUCH);
}

void AppLauncherSpecial::onButtonEvent()
{
    _newButtonEvent = true;
    osSignalSet(appThread, APP_SIGID_TOUCH);
}

static void onTimeoutEvent(void const* arg)
{
    *((bool*)arg) = true;
    osSignalSet(appThread, APP_SIGID_TOUCH);
}

/******************************************************************************
 * Public functions
 *****************************************************************************/

AppLauncherSpecial::AppLauncherSpecial(int iconWidth, int iconHeight) : 
    _disp(NULL), _win(NULL), _fb(NULL), _usedButtons(0), 
    _resBg(NULL), _iconWidth(iconWidth), _iconHeight(iconHeight)
{
    _bgImg.pointerToFree = NULL;
    _bgImg.pixels = NULL;
    _currentTime[0] = '\0';

    for (int i = 0; i < NumberOfButtons; i++) {
        _buttons[i] = NULL;
    }
    bool r;
    int n;
    if (DMBoard::instance().touchPanel()->info(&r, &n, &_supportsCalibration) != TouchPanel::TouchError_Ok) {
      _supportsCalibration = false;
    }
}

AppLauncherSpecial::~AppLauncherSpecial()
{
    teardown();
}

bool AppLauncherSpecial::setup()
{
    RtosLog* log = DMBoard::instance().logger();

    _disp = DMBoard::instance().display();
    _win = (SWIM_WINDOW_T*)malloc(sizeof(SWIM_WINDOW_T));
    _fb = _disp->allocateFramebuffer();
    
    if (_win == NULL || _fb == NULL) {
        log->printf(APP_PREFIX"Failed to allocate memory for framebuffer\r\n");
        return false;
    }
    
    if (Image::decode(_resBg, Image::RES_16BIT, &_bgImg) != 0) {
        log->printf(APP_PREFIX"Failed to load background image\n");
        return false;
    }
    
    return true;
}

void AppLauncherSpecial::runToCompletion()
{
    DMBoard* board = &DMBoard::instance();
    RtosLog* log = board->logger();
    
    appThread = osThreadGetId();

    // Draw something on the framebuffer before using it so that it doesn't look garbled
    draw();
    
    // Start display in default mode (16-bit)
    Display::DisplayError disperr = _disp->powerUp(_fb);
    if (disperr != Display::DisplayError_Ok) {
        log->printf(APP_PREFIX"Failed to initialize the display, got error %d\r\n", disperr);
        return;
    }
    
    // Render the current time in the upper right corner once every second
    bool updateClock = false;
    RtosTimer updateClockTimer(onTimeoutEvent, osTimerPeriodic, &updateClock);
    updateClockTimer.start(1000);
    int clockXOff, clockYOff;
    swim_get_string_bounds(_win, "Fri Mar 20 10:32:58 2015", &clockXOff, &clockYOff);
    clockXOff = _win->xpvmax - clockXOff - 20;
    clockYOff = 5;
    
    // To keep track of the button pushes
    bool buttonPressed = false;
    bool buttonTimeout = false;
    InterruptIn button(P2_10);
    button.rise(this, &AppLauncherSpecial::onButtonEvent);
    button.fall(this, &AppLauncherSpecial::onButtonEvent);
    RtosTimer  buttonTimer(onTimeoutEvent, osTimerOnce, &buttonTimeout);
    
    // To prevent the "exit" click of a launched application to launch
    // a new application. This could happen on a multitouch display if
    // the launched applications' exit/ok/cancel button is located on
    // top of one of the buttons in the launcher.
    Timer tExit;
    tExit.start();
    
    // Wait for touches
    TouchPanel* touch = board->touchPanel();
    FunctionPointer* fpOld = touch->setListener(new FunctionPointer(this, &AppLauncherSpecial::onTouchEvent));
    touch_coordinate_t coord;
    while (true) {
        Thread::signal_wait(APP_SIGID_TOUCH);
        if (_newTouchEvent) {
          if (buttonPressed) {
            // cancel
            buttonPressed = false;
            buttonTimer.stop();
          }
          _newTouchEvent = false;
          if (touch->read(coord) != TouchPanel::TouchError_Ok) {
            log->printf("Failed to read touch coordinate\n");
            continue;
          }
          // Process the touch coordinate for each button
          for (int i = 0; i < NumberOfButtons; i++) {
            if (_buttons[i] != NULL) {
              if (_buttons[i]->handle(coord.x, coord.y, coord.z > 0)) {
                _buttons[i]->draw();
              }
            }
          }
        } else if (buttonTimeout) {
          if (board->buttonPressed()) {
            appToLaunch = CalibrationApp;
          }
          buttonPressed = false;
          buttonTimeout = false;
        } else if (_newButtonEvent) {
          _newButtonEvent = false;
          if (board->buttonPressed()) {
            buttonPressed = true;
            buttonTimer.start(2000);
          } else {
            buttonPressed = false;
            buttonTimer.stop();
          }
          continue;
        } else if (updateClock) {
          updateClock = false;
          time_t seconds = time(0);
          sprintf(_currentTime, "%-25s", ctime(&seconds));
          swim_put_text_xy(_win, _currentTime, clockXOff, clockYOff);
          continue;
        }
        
        if (appToLaunch != NO_APPLICATION) {
          if (tExit.read_ms() < 500) {
            appToLaunch = NO_APPLICATION;
            continue;
          }
          App* a = NULL;
          if (_callback != NULL) {
            a = _callback(appToLaunch);
          }
          if (a != NULL) {
            if (a->setup()) {
              a->runToCompletion();
              a->teardown();
            }
            delete a;            
          }
          tExit.reset();
          appToLaunch = NO_APPLICATION;
        }
    }
    
    // restore old touch listener (get our listener in return)
    fpOld = touch->setListener(fpOld);
    delete fpOld;
}

bool AppLauncherSpecial::teardown()
{
    if (_win != NULL) {
        free(_win);
        _win = NULL;
    }
    if (_fb != NULL) {
        free(_fb);
        _fb = NULL;
    }
    for (int i = 0; i < NumberOfButtons; i++) {
        _buttons[i] = NULL;
        if (_buttons[i] != NULL) {
            delete _buttons[i];
            _buttons[i] = NULL;
        }
    }
    if (_bgImg.pointerToFree != NULL) {
        free(_bgImg.pointerToFree);
        _bgImg.pointerToFree = NULL;
    }    
    return true;
}

void AppLauncherSpecial::setAppCreatorFunc(App*(*callback)(uint32_t buttonID))
{
    _callback = callback;
}

bool AppLauncherSpecial::addButton(uint32_t buttonID, const char* caption)
{
    int idx = _usedButtons++;
    int xspace = ((_disp->width() - ButtonColumns * ButtonWidth) / (ButtonColumns + 1));
    int yspace = ((_disp->height() - TitleHeight - ButtonRows * ButtonHeight) / (ButtonRows + 1));
    
    _buttons[idx] = new Button(caption, (COLOR_T*)_fb, 
                              xspace + (ButtonWidth + xspace)*(idx%ButtonColumns), 
                              TitleHeight + yspace + (ButtonHeight + yspace)*(idx/ButtonColumns), 
                              ButtonWidth, ButtonHeight);
    _buttons[idx]->setAction(buttonClicked, buttonID);
    //_buttons[idx]->draw();
    return true;
}

bool AppLauncherSpecial::addImageButton(uint32_t buttonID, const char* imgUp, const char* imgDown)
{
    return addImageButton(buttonID, NULL, BLACK, imgUp, imgDown);
}

bool AppLauncherSpecial::addImageButton(uint32_t buttonID, const char* caption, COLOR_T color, const char* imgUp, const char* imgDown)
{
    int idx = _usedButtons++;
    int xspace = ((_disp->width() - ButtonColumns * _iconWidth) / (ButtonColumns + 1));
    int yspace = ((_disp->height() - TitleHeight - ButtonRows * _iconHeight) / (ButtonRows + 1));
    
    ImageButton* img =  new ImageButton((COLOR_T*)_fb, 
                              xspace + (_iconWidth + xspace)*(idx%ButtonColumns), 
                              TitleHeight + yspace + (_iconHeight + yspace)*(idx/ButtonColumns), 
                              _iconWidth, _iconHeight, caption, color);
    if (img->loadImages(imgUp, imgDown)) {
      _buttons[idx] = img;
      _buttons[idx]->setAction(buttonClicked, buttonID);
      //_buttons[idx]->draw();
      return true;
    } else {
      //DMBoard::instance().logger()->printf("Failed to load image for buttonID %u, %s[%s]\n", buttonID, imgUp, imgDown==NULL?"":imgDown);
      return false;
    }
}

bool AppLauncherSpecial::addImageButton(uint32_t buttonID, const unsigned char* imgUp, unsigned int imgUpSize, const unsigned char* imgDown, unsigned int imgDownSize)
{
    return addImageButton(buttonID, NULL, BLACK, imgUp, imgUpSize, imgDown, imgDownSize);
}

bool AppLauncherSpecial::addImageButton(uint32_t buttonID, const char* caption, COLOR_T color, const unsigned char* imgUp, unsigned int imgUpSize, const unsigned char* imgDown, unsigned int imgDownSize)
{
    int idx = _usedButtons++;
    int xspace = ((_disp->width() - ButtonColumns * _iconWidth) / (ButtonColumns + 1));
    int yspace = ((_disp->height() - TitleHeight - ButtonRows * _iconHeight) / (ButtonRows + 1));
    
    ImageButton* img =  new ImageButton((COLOR_T*)_fb, 
                              xspace + (_iconWidth + xspace)*(idx%ButtonColumns), 
                              TitleHeight + yspace + (_iconHeight + yspace)*(idx/ButtonColumns), 
                              _iconWidth, _iconHeight, caption, color);
    img->setTransparency(RED);
    if (img->loadImages(imgUp, imgUpSize, imgDown, imgDownSize)) {
      _buttons[idx] = img;
      _buttons[idx]->setAction(buttonClicked, buttonID);
      //_buttons[idx]->draw();
      return true;
    } else {
      //DMBoard::instance().logger()->printf("Failed to load image for buttonID %u, %s[%s]\n", buttonID, imgUp, imgDown==NULL?"":imgDown);
      return false;
    }
}

bool AppLauncherSpecial::addImageButton(uint32_t buttonID, const char* caption, COLOR_T color, Resource* resUp, Resource* resDown)
{
    int idx = _usedButtons++;
    int xspace = ((_disp->width() - ButtonColumns * _iconWidth) / (ButtonColumns + 1));
    int yspace = ((_disp->height() - TitleHeight - ButtonRows * _iconHeight) / (ButtonRows + 1));
    
    ImageButton* img =  new ImageButton((COLOR_T*)_fb, 
                              xspace + (_iconWidth + xspace)*(idx%ButtonColumns), 
                              TitleHeight + yspace + (_iconHeight + yspace)*(idx/ButtonColumns), 
                              _iconWidth, _iconHeight, caption, color);
    if (img->loadImages(resUp, resDown)) {
      _buttons[idx] = img;
      _buttons[idx]->setAction(buttonClicked, buttonID);
      //_buttons[idx]->draw();
      return true;
    } else {
      //DMBoard::instance().logger()->printf("Failed to load image for buttonID %u, %s[%s]\n", buttonID, imgUp, imgDown==NULL?"":imgDown);
      return false;
    }
}

void AppLauncherSpecial::addResource(Resources id, Resource* res)
{
    _resBg = res;
}