From 20958add7b2acc205e71569a3f318196baf7b7cf Mon Sep 17 00:00:00 2001 From: patacongo Date: Wed, 16 May 2012 22:43:40 +0000 Subject: NxWM: Add IApplicationFactory so that we can create multiple instances of applications; And, as a result of that, re-vamp entire threading model. this is now buggier than it was before git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@4742 42af7a65-404d-4744-a932-0658087f49c3 --- NxWidgets/nxwm/src/capplicationwindow.cxx | 12 ++ NxWidgets/nxwm/src/ccalibration.cxx | 74 ++++++++++- NxWidgets/nxwm/src/cfullscreenwindow.cxx | 12 ++ NxWidgets/nxwm/src/cnxconsole.cxx | 72 +++++++++++ NxWidgets/nxwm/src/cstartwindow.cxx | 196 ++++++++++++++++++++++++++---- NxWidgets/nxwm/src/ctaskbar.cxx | 9 +- NxWidgets/nxwm/src/cwindowcontrol.cxx | 159 ++++++++++++++++++------ 7 files changed, 471 insertions(+), 63 deletions(-) (limited to 'NxWidgets/nxwm/src') diff --git a/NxWidgets/nxwm/src/capplicationwindow.cxx b/NxWidgets/nxwm/src/capplicationwindow.cxx index df8c69f11..15d4a4a9d 100644 --- a/NxWidgets/nxwm/src/capplicationwindow.cxx +++ b/NxWidgets/nxwm/src/capplicationwindow.cxx @@ -413,6 +413,18 @@ NXWidgets::INxWindow *CApplicationWindow::getWindow(void) const return static_cast(m_window); } +/** + * Recover the contained window control + * + * @return. The window control used by this application + */ + +CWindowControl *CApplicationWindow::getWindowControl(void) const +{ + NXWidgets::CWidgetControl *control = m_window->getWidgetControl(); + return static_cast(control); +} + /** * Set the window label * diff --git a/NxWidgets/nxwm/src/ccalibration.cxx b/NxWidgets/nxwm/src/ccalibration.cxx index 2f9adb2e0..248aca838 100644 --- a/NxWidgets/nxwm/src/ccalibration.cxx +++ b/NxWidgets/nxwm/src/ccalibration.cxx @@ -1,5 +1,5 @@ /**************************************************************************** - * NxWidgets/nxwm/src/capplicationwindow.cxx + * NxWidgets/nxwm/src/ccalibration.cxx * * Copyright (C) 2012 Gregory Nutt. All rights reserved. * Author: Gregory Nutt @@ -107,6 +107,11 @@ CCalibration::CCalibration(CTaskbar *taskbar, CFullScreenWindow *window, CCalibration::~CCalibration(void) { + // Make sure that the application is not running (it should already + // have been stopped) + + stop(); + // Although we did not create the window, the rule is that I have to dispose // of it @@ -788,4 +793,71 @@ bool CCalibration::createCalibrationData(struct SCalibrationData &data) return true; } +/** + * CCalibrationFactory Constructor + * + * @param taskbar. The taskbar instance used to terminate calibration + * @param touchscreen. An instance of the class that wraps the + * touchscreen device. + */ + +CCalibrationFactory::CCalibrationFactory(CTaskbar *taskbar, CTouchscreen *touchscreen) +{ + m_taskbar = taskbar; + m_touchscreen = touchscreen; +} + +/** + * Create a new instance of an CCalibration (as IApplication). + */ + +IApplication *CCalibrationFactory::create(void) +{ + // Call CTaskBar::openFullScreenWindow to create a full screen window for + // the calibation application + + CFullScreenWindow *window = m_taskbar->openFullScreenWindow(); + if (!window) + { + gdbg("ERROR: Failed to create CFullScreenWindow\n"); + return (IApplication *)0; + } + + // Open the window (it is hot in here) + + if (!window->open()) + { + gdbg("ERROR: Failed to open CFullScreenWindow \n"); + delete window; + return (IApplication *)0; + } + // Instantiate the application, providing the window to the application's + // constructor + + CCalibration *calibration = new CCalibration(m_taskbar, window, m_touchscreen); + if (!calibration) + { + gdbg("ERROR: Failed to instantiate CCalibration\n"); + delete window; + return (IApplication *)0; + } + + return static_cast(calibration); +} + +/** + * 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 *CCalibrationFactory::getIcon(void) +{ + NXWidgets::CRlePaletteBitmap *bitmap = + new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_CALIBRATION_ICON); + + return bitmap; +} diff --git a/NxWidgets/nxwm/src/cfullscreenwindow.cxx b/NxWidgets/nxwm/src/cfullscreenwindow.cxx index 7d63b02d7..40647d8e9 100644 --- a/NxWidgets/nxwm/src/cfullscreenwindow.cxx +++ b/NxWidgets/nxwm/src/cfullscreenwindow.cxx @@ -127,6 +127,18 @@ NXWidgets::INxWindow *CFullScreenWindow::getWindow(void) const return static_cast(m_window); } +/** + * Recover the contained window control + * + * @return. The window control used by this application + */ + +CWindowControl *CFullScreenWindow::getWindowControl(void) const +{ + NXWidgets::CWidgetControl *control = m_window->getWidgetControl(); + return static_cast(control); +} + /** * Set the window label * diff --git a/NxWidgets/nxwm/src/cnxconsole.cxx b/NxWidgets/nxwm/src/cnxconsole.cxx index ec43c5656..577bf661e 100644 --- a/NxWidgets/nxwm/src/cnxconsole.cxx +++ b/NxWidgets/nxwm/src/cnxconsole.cxx @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -141,6 +142,11 @@ CNxConsole::CNxConsole(CTaskbar *taskbar, CApplicationWindow *window) CNxConsole::~CNxConsole(void) { + // There would be a problem if we were stopped with the NxConsole 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 @@ -504,6 +510,72 @@ void CNxConsole::close(void) m_taskbar->stopApplication(static_cast(this)); } +/** + * CNxConsoleFactory Constructor + * + * @param taskbar. The taskbar instance used to terminate the console + */ + +CNxConsoleFactory::CNxConsoleFactory(CTaskbar *taskbar) +{ + m_taskbar = taskbar; +} + +/** + * Create a new instance of an CNxConsole (as IApplication). + */ + +IApplication *CNxConsoleFactory::create(void) +{ + // Call CTaskBar::openFullScreenWindow to create a full screen window for + // the NxConsole application + + CApplicationWindow *window = m_taskbar->openApplicationWindow(); + if (!window) + { + gdbg("ERROR: Failed to create CApplicationWindow\n"); + return (IApplication *)0; + } + + // Open the window (it is hot in here) + + if (!window->open()) + { + gdbg("ERROR: Failed to open CApplicationWindow\n"); + delete window; + return (IApplication *)0; + } + + // Instantiate the application, providing the window to the application's + // constructor + + CNxConsole *nxconsole = new CNxConsole(m_taskbar, window); + if (!nxconsole) + { + gdbg("ERROR: Failed to instantiate CNxConsole\n"); + delete window; + return (IApplication *)0; + } + + return static_cast(nxconsole); +} + +/** + * 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 *CNxConsoleFactory::getIcon(void) +{ + NXWidgets::CRlePaletteBitmap *bitmap = + new NXWidgets::CRlePaletteBitmap(&CONFIG_NXWM_NXCONSOLE_ICON); + + return bitmap; +} + /** * One time NSH initialization. This function must be called exactly * once during the boot-up sequence to initialize the NSH library. diff --git a/NxWidgets/nxwm/src/cstartwindow.cxx b/NxWidgets/nxwm/src/cstartwindow.cxx index de35662d5..1c997fbb7 100644 --- a/NxWidgets/nxwm/src/cstartwindow.cxx +++ b/NxWidgets/nxwm/src/cstartwindow.cxx @@ -39,6 +39,13 @@ #include +#include +#include +#include +#include + +#include + #include "cwidgetcontrol.hxx" #include "nxwmconfig.hxx" @@ -51,7 +58,7 @@ ********************************************************************************************/ /******************************************************************************************** - * CNxConsole Method Implementations + * CStartWindow Method Implementations ********************************************************************************************/ using namespace NxWM; @@ -70,6 +77,10 @@ CStartWindow::CStartWindow(CTaskbar *taskbar, CApplicationWindow *window) m_taskbar = taskbar; m_window = window; + // The start window task is not running + + m_taskId = -1; + // Add our personalized window label NXWidgets::CNxString myName = getName(); @@ -86,6 +97,11 @@ CStartWindow::CStartWindow(CTaskbar *taskbar, CApplicationWindow *window) CStartWindow::~CStartWindow(void) { + // There would be a problem if we were stopped with the start window 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 @@ -96,7 +112,7 @@ CStartWindow::~CStartWindow(void) // Then stop and delete all applications - stopAllApplications(); + removeAllApplications(); } /** @@ -144,9 +160,24 @@ NXWidgets::CNxString CStartWindow::getName(void) bool CStartWindow::run(void) { - // We don't have a thread of execution. We only respond to button presses + // Some sanity checking - return true; + if (m_taskId >= 0) + { + // The start window task is already running??? + + return false; + } + + // Start the start window task + + m_taskId = TASK_CREATE("StartWindow", CONFIG_NXWM_STARTWINDOW_PRIO, + CONFIG_NXWM_STARTWINDOW_STACKSIZE, startWindow, + (FAR const char **)0); + + // Did we successfully start the NxConsole task? + + return m_taskId >= 0; } /** @@ -155,7 +186,20 @@ bool CStartWindow::run(void) void CStartWindow::stop(void) { - // We don't have a thread of execution. We only respond to button presses + // Delete the start window task --- what are we doing? This should never + // happen because the start window task is persistent! + + if (m_taskId >= 0) + { + // Call task_delete(), possibly stranding resources + + pid_t pid = m_taskId; + m_taskId = -1; + + // Then delete the NSH task + + task_delete(pid); + } } /** @@ -293,21 +337,17 @@ bool CStartWindow::isFullScreen(void) const } /** - * Add the application to the start window. The general sequence for - * setting up the start window is: + * Add the application to the start window. The general sequence is: * - * 1. Call CTaskBar::openApplicationWindow to create a window for the start window, - * 2. Use the window to instantiate CStartWindow - * 3. Call CStartWindow::addApplication numerous times to install applications - * in the start window. - * 4. Call CTaskBar::startApplication (initially minimized) to start the start - * window application. + * 1. Call IAppicationFactory::create to a new instance of the application + * 2. Call CStartWindow::addApplication to add the application to the + * start window. * * @param app. The new application to add to the start window * @return true on success */ -bool CStartWindow::addApplication(IApplication *app) +bool CStartWindow::addApplication(IApplicationFactory *app) { // Recover the NXTK window instance contained in the application window @@ -418,18 +458,17 @@ void CStartWindow::getIconBounds(void) * Stop all applications */ -void CStartWindow::stopAllApplications(void) +void CStartWindow::removeAllApplications(void) { - // Stop all applications and remove them from the task bar. Clearly, there + // Stop all applications and remove them from the start window. Clearly, there // are some ordering issues here... On an orderly system shutdown, disconnection // should really occur priority to deleting instances while (!m_slots.empty()) { - // Stop the application (and remove it from the task bar) + // Remove the application factory from the start menu - IApplication *app = m_slots.at(0).app; - m_taskbar->stopApplication(app); + IApplicationFactory *app = m_slots.at(0).app; // Now, delete the image and the application @@ -459,12 +498,125 @@ void CStartWindow::handleActionEvent(const NXWidgets::CWidgetEventArgs &e) NXWidgets::CImage *image = m_slots.at(i).image; if (image->isClicked()) { - // Start a new copy of the application + // Create a new copy of the application - m_taskbar->startApplication(m_slots.at(i).app, false); + IApplication *app = m_slots.at(i).app->create(); + if (app) + { + // Start the new copy of the application + + if (m_taskbar->startApplication(app, false)) + { + // Then break out of the loop + + break; + } + else + { + // If we cannot start the app. Destroy the + // instance we created and see what happens next. + + CWindowControl *control = app->getWindowControl(); + control->destroy(app); + } + } + } + } +} - // Then break out of the loop +/** + * This is the start window task. This function receives window events from + * the NX listener threads indirectly through this sequence: + * + * 1. NX listener thread receives a windows event. This may be a + * positional change notification, a redraw request, or mouse or + * keyboard input. + * 2. The NX listener thread performs the callback by calling a + * NXWidgets::CCallback method associated with the window. + * 3. NXWidgets::CCallback calls into NXWidgets::CWidgetControl to process + * the event. + * 4. NXWidgets::CWidgetControl records the new state data and raises a + * window event. + * 5. NXWidgets::CWindowEventHandlerList will give the event to + * NxWM::CWindowControl. + * 6. NxWM::CWindowControl will send the a message on a well-known message + * queue. + * 7. This CStartWindow::startWindow task will receive and process that + * message. + */ + +int CStartWindow::startWindow(int argc, char *argv[]) +{ + /* Open a well-known message queue for reading */ + + struct mq_attr attr; + attr.mq_maxmsg = CONFIG_NXWM_STARTWINDOW_MXMSGS; + attr.mq_msgsize = sizeof(struct SStartWindowMessage); + attr.mq_flags = 0; + + mqd_t mqd = mq_open(CONFIG_NXWM_STARTWINDOW_MQNAME, O_RDONLY|O_CREAT, 0666, &attr); + if (mqd == (mqd_t)-1) + { + gdbg("ERROR: mq_open(%s) failed: %d\n", CONFIG_NXWM_STARTWINDOW_MQNAME, errno); + return EXIT_FAILURE; + } + + // Now loop forever, receiving and processing messages. Ultimately, all + // widget driven events (button presses, etc.) are driven by this logic + // on this thread. + + for (;;) + { + // Receive the next message + + struct SStartWindowMessage msg; + ssize_t nbytes = mq_receive(mqd, &msg, sizeof(struct SStartWindowMessage), 0); + if (nbytes < 0) + { + // EINTR is not an error. The wait was interrupted by a signal and + // we just need to try reading again. + + if (errno != EINTR) + { + gdbg("ERROR: mq_receive failed: %d\n", errno); + } + } + while (nbytes < 0); + + gvdbg("Received msgid=%d nbytes=%d\n", msg.msgId, nbytes); + DEBUGASSERT(nbytes = sizeof(struct SStartWindowMessage) && msg.instance); + + // Dispatch the message to the appropriate CWidgetControl and to the + // appropriate CWidgetControl method + + switch (msg.msgId) + { + break; + + case MSGID_MOUSE_INPUT: // New mouse input is available + case MSGID_KEYBOARD_INPUT: // New keyboard input is available + { + // Handle all new window input events by calling the CWidgetControl::pollEvents() method + + NXWidgets::CWidgetControl *control = (NXWidgets::CWidgetControl *)msg.instance; + control->pollEvents(); + } + break; + + case MSGID_DESTROY_APP: // Destroy the application + { + // Handle all destroy application events + + gdbg("Deleting app=%p\n", msg.instance); + IApplication *app = (IApplication *)msg.instance; + delete app; + } + break; + case MSGID_POSITIONAL_CHANGE: // Change in window positional data (not used) + case MSGID_REDRAW_REQUEST: // Request to redraw a portion of the window (not used) + default: + gdbg("ERROR: Unrecognized or unsupported msgId: %d\n", (int)msg.msgId); break; } } diff --git a/NxWidgets/nxwm/src/ctaskbar.cxx b/NxWidgets/nxwm/src/ctaskbar.cxx index ec3d8460b..ab686f81c 100644 --- a/NxWidgets/nxwm/src/ctaskbar.cxx +++ b/NxWidgets/nxwm/src/ctaskbar.cxx @@ -609,7 +609,9 @@ bool CTaskbar::stopApplication(IApplication *app) hideApplicationWindow(app); - // Stop the application (whatever this means to the application) + // Stop the application (whatever this means to the application). We + // separate stopping from destroying to get the application a chance + // to put things in order before being destroyed. app->stop(); @@ -631,6 +633,11 @@ bool CTaskbar::stopApplication(IApplication *app) } } + // destroy the application + + CWindowControl *control = app->getWindowControl(); + control->destroy(app); + // Re-draw the new top, non-minimized application bool ret = redrawTopWindow(); diff --git a/NxWidgets/nxwm/src/cwindowcontrol.cxx b/NxWidgets/nxwm/src/cwindowcontrol.cxx index 841397a14..2c575d35b 100644 --- a/NxWidgets/nxwm/src/cwindowcontrol.cxx +++ b/NxWidgets/nxwm/src/cwindowcontrol.cxx @@ -36,9 +36,16 @@ /******************************************************************************************** * Included Files ********************************************************************************************/ - + #include +#include +#include + +#include + +#include "nxwmconfig.hxx" +#include "cstartwindow.hxx" #include "cwindowcontrol.hxx" /******************************************************************************************** @@ -62,6 +69,20 @@ using namespace NxWM; CWindowControl::CWindowControl(FAR const NXWidgets::CWidgetStyle *style) : NXWidgets::CWidgetControl(style) { + // Open a message queue to communicate with the start window task. We need to create + // the message queue if it does not exist. + + struct mq_attr attr; + attr.mq_maxmsg = CONFIG_NXWM_STARTWINDOW_MXMSGS; + attr.mq_msgsize = sizeof(struct SStartWindowMessage); + attr.mq_flags = 0; + + m_mqd = mq_open(CONFIG_NXWM_STARTWINDOW_MQNAME, O_WRONLY|O_CREAT, 0666, &attr); + if (m_mqd == (mqd_t)-1) + { + gdbg("ERROR: mq_open(%s) failed: %d\n", CONFIG_NXWM_STARTWINDOW_MQNAME, errno); + } + // Add ourself as the window callback addWindowEventHandler(this); @@ -73,11 +94,41 @@ CWindowControl::CWindowControl(FAR const NXWidgets::CWidgetStyle *style) CWindowControl::~CWindowControl(void) { + // Close the message queue + + (void)mq_close(m_mqd); + // Remove ourself from the window callback removeWindowEventHandler(this); } +/** + * Destroy the application window and everything in it. This is + * handled by CWindowControl (vs just calling the destructors) because + * in the case where an application destroys itself (because of pressing + * the stop button), then we need to unwind and get out of the application + * logic before destroying all of its objects. + */ + +void CWindowControl::destroy(IApplication *app) +{ + // Send a message to destroy the window isntance at a later time + + struct SStartWindowMessage outmsg; + outmsg.msgId = MSGID_DESTROY_APP; + outmsg.instance = (FAR void *)app; + + gdbg("Sending MSGID_DESTROY_APP with instance=%p\n", app); + int ret = mq_send(m_mqd, &outmsg, sizeof(struct SStartWindowMessage), + CONFIG_NXWM_STARTWINDOW_MXMPRIO); + if (ret < 0) + { + gdbg("ERROR: mq_send failed: %d\n", errno); + } + +} + /** * Handle an NX window mouse input event. * @@ -89,24 +140,40 @@ void CWindowControl::handleMouseEvent(void) { // The logic path here is tortuous but flexible: // - // 1. A listener thread receives mouse input and injects that into NX - // 2. In the multi-user mode, this will send a message to the NX server - // 3. The NX server will determine which window gets the mouse input - // and send a message to the listener. - // 4. The listener will call the NX message dispatcher will will do the - // message callback. - // 5. The callback goes into an instance of NXWidgets::CCallback that is - // part of the CWidget control. - // 6. That callback will update mouse information then raise the - // mouse event, - // 7. Which will finally call this function -- still running deep on the - // stack in the listener thread. - // 8. This function will then call back into the widget control to process - // the mouse input. - - // Perform the poll - - pollEvents(); + // 1. A listener thread receives mouse or touchscreen input and injects + // that into NX via nx_mousein + // 2. In the multi-user mode, this will send a message to the NX server + // 3. The NX server will determine which window gets the mouse input + // and send a window event message to the NX listener thread. + // 4. The NX listener thread receives a windows event. The NX listener thread + // which is part of CTaskBar and was created when NX server connection was + // established). This event may be a positional change notification, a + // redraw request, or mouse or keyboard input. In this case, mouse input. + // 5. The NX listener thread handles the message by calling nx_eventhandler(). + // nx_eventhandler() dispatches the message by calling a method in the + // NXWidgets::CCallback instance associated with the window. + // NXWidgets::CCallback is a part of the CWidgetControl. + // 6. NXWidgets::CCallback calls into NXWidgets::CWidgetControl to process + // the event. + // 7. NXWidgets::CWidgetControl records the new state data and raises a + // window event. + // 8. NXWidgets::CWindowEventHandlerList will give the event to this method + // NxWM::CWindowControl. + // 9. This NxWM::CWindowControl method will send the a message on a well- + // known message queue. + // 10. This CStartWindow::startWindow task will receive and process that + // message by calling CWidgetControl::pollEvents() + + struct SStartWindowMessage outmsg; + outmsg.msgId = MSGID_MOUSE_INPUT; + outmsg.instance = (FAR void *)static_cast(this); + + int ret = mq_send(m_mqd, &outmsg, sizeof(struct SStartWindowMessage), + CONFIG_NXWM_STARTWINDOW_MXMPRIO); + if (ret < 0) + { + gdbg("ERROR: mq_send failed: %d\n", errno); + } } #endif @@ -119,25 +186,39 @@ void CWindowControl::handleKeyboardEvent(void) { // The logic path here is tortuous but flexible: // - // 1. A listener thread receives keyboard input and injects that into NX - // 2. In the multi-user mode, this will send a message to the NX server - // 3. The NX server will determine which window gets the keyboard input - // and send a message to the listener. - // 4. The listener will call the NX message dispatcher will will do the - // message callback. - // 5. The callback goes into an instance of NXWidgets::CCallback that is - // part of the CWidget control. - // 6. That callback will update keyboard information then raise the - // keyboard event, - // 7. Which will finally call this function -- still running deep on the - // stack in the listener thread. - // 8. This function will then call back into the widget control to process - // the keyboard input. - - // Perform the poll - - pollEvents(); + // 1. A listener thread receives keyboard input and injects that into NX + // via nx_kbdin. + // 2. In the multi-user mode, this will send a message to the NX server + // 3. The NX server will determine which window gets the keyboard input + // and send a window event message to the NX listener thread. + // 4. The NX listener thread receives a windows event. The NX listener thread + // which is part of CTaskBar and was created when NX server connection was + // established). This event may be a positional change notification, a + // redraw request, or mouse or keyboard input. In this case, keyboard input. + // 5. The NX listener thread handles the message by calling nx_eventhandler(). + // nx_eventhandler() dispatches the message by calling a method in the + // NXWidgets::CCallback instance associated with the window. + // NXWidgets::CCallback is a part of the CWidgetControl. + // 6. NXWidgets::CCallback calls into NXWidgets::CWidgetControl to process + // the event. + // 7. NXWidgets::CWidgetControl records the new state data and raises a + // window event. + // 8. NXWidgets::CWindowEventHandlerList will give the event to this method + // NxWM::CWindowControl. + // 9. This NxWM::CWindowControl method will send the a message on a well- + // known message queue. + // 10. This CStartWindow::startWindow task will receive and process that + // message by calling CWidgetControl::pollEvents() + + struct SStartWindowMessage outmsg; + outmsg.msgId = MSGID_KEYBOARD_INPUT; + outmsg.instance = (FAR void *)static_cast(this); + + int ret = mq_send(m_mqd, &outmsg, sizeof(struct SStartWindowMessage), + CONFIG_NXWM_STARTWINDOW_MXMPRIO); + if (ret < 0) + { + gdbg("ERROR: mq_send failed: %d\n", errno); + } } #endif - - -- cgit v1.2.3