Revision 331

Date:
2015/12/10 13:18:26
Author:
alanb@RISCID.ORG
Revision Log:
Added TaskWindow class to run a RISC OS TaskWindow
Files:

Legend:

 
Added
 
Removed
 
Modified
  • trunk/docsrc/history.h

     
    16 16 * - Added new class LockExtentPosition to keep gadget edges locked to the side of the windows extent
    17 17 * - Fixed memory leak in show_message/show_message_as_menu and class MessageWindow
    18 18 * - Added close_command method to show_message/show_message_as_menu and class MessageWindow to set a command to be run when the message window is closed.
    19 * - Added TaskWindow class to run a child task using the RISC OS TaskWindow command
    19 20 *
    20 21 * <B>0.7.3 Alpha December 2014</B>
    21 22 *
  • trunk/tbx/taskwindow.cc

     
    1 /*
    2 * tbx RISC OS toolbox library
    3 *
    4 * Copyright (C) 2015 Alan Buckley All Rights Reserved.
    5 *
    6 * Permission is hereby granted, free of charge, to any person obtaining a
    7 * copy of this software and associated documentation files (the "Software"),
    8 * to deal in the Software without restriction, including without limitation
    9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10 * and/or sell copies of the Software, and to permit persons to whom the
    11 * Software is furnished to do so, subject to the following conditions:
    12 *
    13 * The above copyright notice and this permission notice shall be included
    14 * in all copies or substantial portions of the Software.
    15 *
    16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    22 * THE SOFTWARE.
    23 */
    24 #include "taskwindow.h"
    25 #include "swixcheck.h"
    26 #include "application.h"
    27
    28 #include <swis.h>
    29 #include <sstream>
    30 #include <stdexcept>
    31 #include <cstring>
    32 #include <iomanip>
    33 #include <algorithm>
    34
    35 #include <iostream>
    36
    37 namespace tbx
    38 {
    39
    40 /**
    41 * Used to generate the next id for task window startup
    42 */
    43 unsigned int TaskWindow::_next_txt_id = 1;
    44
    45
    46 /**
    47 * Uninitialised task window.
    48 * At least the command must be set before calling run.
    49 */
    50 TaskWindow::TaskWindow() :
    51 _wimp_slot(0),
    52 _options(0),
    53 _child_task(0),
    54 _txt_id(0),
    55 _running(false),
    56 _router(0)
    57 {
    58 }
    59
    60 /**
    61 * Construct a task window.
    62 * Call the run method to start the task window
    63 * @param command command to run
    64 * @param name name of the child task
    65 * @param wimp_slot size in KB of the wimp slot for the task or 0 to use the Next
    66 * slot from the RISC OS task manager.
    67 * @param options one or more of the Options enum or 0 for none.
    68 */
    69 TaskWindow::TaskWindow(std::string command, std::string name, int wimp_slot /*= 0*/, unsigned int options /*= 0*/) :
    70 _command(command),
    71 _name(name),
    72 _wimp_slot(wimp_slot),
    73 _options(options),
    74 _child_task(0),
    75 _txt_id(0),
    76 _running(false),
    77 _router(0)
    78 {
    79 }
    80
    81 /**
    82 * Destructor, remove interest in the task window.
    83 * The destructor does not stop a running task, just
    84 * removes any listeners.
    85 */
    86 TaskWindow::~TaskWindow()
    87 {
    88 delete _router;
    89 }
    90
    91 /**
    92 * Add listener to detect when the child task is started
    93 * @param listener listener to add
    94 */
    95 void TaskWindow::add_started_listener(TaskWindowStartedListener *listener)
    96 {
    97 if (!_router) _router = new Router(this);
    98 _router->_started_listeners.push_back(listener);
    99 }
    100
    101 /**
    102 * Remove listener to detect when the child task is started
    103 * @param listener to remove
    104 */
    105 void TaskWindow::remove_started_listener(TaskWindowStartedListener *listener)
    106 {
    107 if (_router)
    108 {
    109 std::vector<TaskWindowStartedListener *>::iterator found = std::find(
    110 _router->_started_listeners.begin(),
    111 _router->_started_listeners.end(),
    112 listener);
    113 if (found != _router->_started_listeners.end())
    114 {
    115 _router->_started_listeners.erase(found);
    116 }
    117 }
    118 }
    119
    120 /**
    121 * Add listener to detect when the child task has finished
    122 * @param listener listener to add
    123 */
    124 void TaskWindow::add_finished_listener(TaskWindowFinishedListener *listener)
    125 {
    126 if (!_router) _router = new Router(this);
    127 _router->_finished_listeners.push_back(listener);
    128 }
    129
    130 /**
    131 * Remove listener to detect when the child task has finished
    132 * @param listener to remove
    133 */
    134 void TaskWindow::remove_finished_listener(TaskWindowFinishedListener *listener)
    135 {
    136 if (_router)
    137 {
    138 std::vector<TaskWindowFinishedListener *>::iterator found = std::find(
    139 _router->_finished_listeners.begin(),
    140 _router->_finished_listeners.end(),
    141 listener);
    142 if (found != _router->_finished_listeners.end())
    143 {
    144 _router->_finished_listeners.erase(found);
    145 }
    146 }
    147 }
    148
    149 /**
    150 * Add listener to capture output from the child task.
    151 *
    152 * One or more of these listeners must be added before the task window
    153 * is run or the output will go to an external window.
    154 * @param listener listener to add
    155 */
    156 void TaskWindow::add_output_listener(TaskWindowOutputListener *listener)
    157 {
    158 if (!_router) _router = new Router(this);
    159 if (_router->_output_listeners.empty())
    160 {
    161 tbx::app()->add_user_message_listener(0x808C1, _router); // TaskWindow_Output
    162 tbx::app()->add_recorded_message_listener(0x808C1, _router);
    163 }
    164 _router->_output_listeners.push_back(listener);
    165 }
    166
    167 /**
    168 * Remove listener to detect output from the child task
    169 * @param listener to remove
    170 */
    171 void TaskWindow::remove_output_listener(TaskWindowOutputListener *listener)
    172 {
    173 if (_router)
    174 {
    175 std::vector<TaskWindowOutputListener *>::iterator found = std::find(
    176 _router->_output_listeners.begin(),
    177 _router->_output_listeners.end(),
    178 listener);
    179 if (found != _router->_output_listeners.end())
    180 {
    181 _router->_output_listeners.erase(found);
    182 }
    183 if (_router->_output_listeners.empty())
    184 {
    185 tbx::app()->remove_user_message_listener(0x808C1, _router); // TaskWindow_Output
    186 tbx::app()->remove_recorded_message_listener(0x808C1, _router);
    187 }
    188 }
    189 }
    190
    191 /**
    192 * Run the child task.
    193 *
    194 * At least one listener (of any type) must be added before calling this
    195 * method for the running() and child_task() field to be updated.
    196 *
    197 * If any listeners are added then output from the task window will
    198 * be directed to this application. Use the output listener to process
    199 * this output.
    200 *
    201 * If no listeners are added the task will be run by an external
    202 * application. As mentioned above in this case this application
    203 * has no knowledge of the state of the child task.
    204 *
    205 * @throws std::logic_error if the command to run has not been set, or the
    206 * command is already running.
    207 * @throws tbx::OsError call to start the task failed
    208 */
    209 void TaskWindow::run()
    210 {
    211 if (_command.empty()) throw std::logic_error("command not set before run");
    212 if (_running) throw std::logic_error("Cannot run command while this TaskWindow is running");
    213
    214 std::ostringstream ex;
    215 ex << "TaskWindow \"" << _command << "\"";
    216 if (_wimp_slot) ex << " -wimpslot " << _wimp_slot << "K";
    217 if (!_name.empty()) ex << " -name \"" << _name << "\"";
    218 if (_options & ALLOW_CONTROL) ex << " -ctrl";
    219 if (_options & DISPLAY) ex << " -display";
    220 if (_options & QUIT) ex << " -quit";
    221
    222 ex << std::setw(8) << std::hex << std::setfill('0');
    223
    224 if (_router)
    225 {
    226 ex << " -task &" << tbx::app()->task_handle();
    227 _txt_id = _next_txt_id++;
    228 ex << " -txt &" << _txt_id;
    229 }
    230
    231 std::string to_run = ex.str();
    232
    233 int started;
    234 tbx::swix_check(_swix(Wimp_StartTask,_IN(0) | _OUT(0) , to_run.c_str(), &started));
    235 // Can only check the lifetime if any listeners have been added.
    236 if (_router) _running = true;
    237 }
    238
    239 /**
    240 * Send input to the task window.
    241 *
    242 * You can only send up to 232 chars this way.
    243 * Use the normal Wimp data transfer to send more characters.
    244 * @param text to send.
    245 * @param number of characters to send or -1 to count the length of a
    246 * 0 terminated string.
    247 * @throws std::logic_error if you try to send an empty string,
    248 * a string with too many characters, or to a child task that hasn't started.
    249 */
    250 void TaskWindow::send_input(const char *text, int size /*= -1*/)
    251 {
    252 if (_child_task == 0) throw std::logic_error("Child task must be running before sending input");
    253 if (size == -1) size = std::strlen(text);
    254 if (size == 0) throw std::logic_error("Cannot send an empty string to a task window");
    255 if (size > 232) throw std::logic_error("Can only send a maximum of 232 characters with send_input");
    256
    257 int msg_size = (size + 3) / 4;
    258 tbx::WimpMessage send(0x808c0, msg_size+6); // TaskWindow_SendInput
    259 send[5] = size;
    260 std::memcpy(send.str(6), text, size);
    261 send.send(tbx::WimpMessage::User, _child_task);
    262 }
    263
    264 /**
    265 * Send input to the task window.
    266 *
    267 * You can only send up to 232 chars this way.
    268 * Use the normal Wimp data transfer to send more characters.
    269 * @param text to send.
    270 * @throws std::logic_error if you try to send and empty string,
    271 * a string with too many characters, or to a child task that hasn't started.
    272 */
    273 void TaskWindow::send_input(const std::string &text)
    274 {
    275 send_input(text.c_str(), (int)text.size());
    276 }
    277
    278 /**
    279 * Suspend the current child task
    280 *
    281 * @throws std::logic_error if the task has not been started
    282 */
    283 void TaskWindow::suspend()
    284 {
    285 if (_child_task == 0) throw std::logic_error("Child task must be running before it can be suspended");
    286 tbx::WimpMessage msg(0x808C6, 5); // TaskWindow_Suspend
    287 msg.send(tbx::WimpMessage::User, _child_task);
    288 }
    289
    290 /**
    291 * Resume the current child task after it has been suspended with a
    292 * call to suspend.
    293 *
    294 * @throws std::logic_error if the task has not been started
    295 */
    296 void TaskWindow::resume()
    297 {
    298 if (_child_task == 0) throw std::logic_error("Child task must be running before it can be resumed");
    299 tbx::WimpMessage msg(0x808C7, 5); // TaskWindow_Resume
    300 msg.send(tbx::WimpMessage::User, _child_task);
    301 }
    302 /**
    303 * Kill the current child task
    304 *
    305 * @throws std::logic_error if the task has not been started
    306 */
    307 void TaskWindow::kill()
    308 {
    309 if (_child_task == 0) throw std::logic_error("Child task must be running before it can be killed");
    310 tbx::WimpMessage msg(0x808C4, 5); // TaskWindow_Morite
    311 msg.send(tbx::WimpMessage::User, _child_task);
    312 }
    313
    314 /**
    315 * Construct router to convert Wimp messages to task window listeners
    316 */
    317 TaskWindow::Router::Router(TaskWindow *me) :
    318 _me(me)
    319 {
    320 // always add start and end so we can monitor lifetime
    321 tbx::app()->add_user_message_listener(0x808C2, this); // TaskWindow_Ego
    322 tbx::app()->add_recorded_message_listener(0x808C2, this);
    323 tbx::app()->add_user_message_listener(0x808C3, this); // TaskWindow_Morio
    324 tbx::app()->add_recorded_message_listener(0x808C3, this);
    325 }
    326
    327 /**
    328 * Stop listeners
    329 */
    330 TaskWindow::Router::~Router()
    331 {
    332 tbx::app()->remove_user_message_listener(0x808C2, this);
    333 tbx::app()->remove_recorded_message_listener(0x808C2, this);
    334 tbx::app()->remove_user_message_listener(0x808C3, this);
    335 tbx::app()->remove_recorded_message_listener(0x808C3, this);
    336 if (!_output_listeners.empty())
    337 {
    338 tbx::app()->remove_user_message_listener(0x808C1, this);
    339 tbx::app()->remove_recorded_message_listener(0x808C1, this);
    340 }
    341 }
    342
    343 /**
    344 * Set state of TaskWindow and forward any events
    345 */
    346 void TaskWindow::Router::user_message(tbx::WimpMessageEvent &event)
    347 {
    348 switch (event.message().message_id())
    349 {
    350 case 0x808C2: // TaskWindow_Ego - started listener
    351 if (event.message()[5] == (int)_me->_txt_id)
    352 {
    353 _me->_child_task = event.message().sender_task_handle();
    354 for (std::vector<TaskWindowStartedListener *>::iterator i = _started_listeners.begin();
    355 i != _started_listeners.end(); ++i)
    356 {
    357 (*i)->taskwindow_started(*_me);
    358 }
    359 }
    360 break;
    361
    362 case 0x808C3: // TaskWindow_Morio - finished listener
    363 if ((int)_me->_child_task == event.message().sender_task_handle())
    364 {
    365 _me->_child_task = 0;
    366 _me->_running = false;
    367 for (std::vector<TaskWindowFinishedListener *>::iterator i = _finished_listeners.begin();
    368 i != _finished_listeners.end(); ++i)
    369 {
    370 (*i)->taskwindow_finished(*_me);
    371 }
    372 }
    373 break;
    374
    375 case 0x808C1: // TaskWindow_Output - Output listener
    376 if ((int)_me->_child_task == event.message().sender_task_handle())
    377 {
    378 int size = event.message()[5];
    379 const char *text = event.message().str(6);
    380 for (std::vector<TaskWindowOutputListener *>::iterator i = _output_listeners.begin();
    381 i != _output_listeners.end(); ++i)
    382 {
    383 (*i)->taskwindow_output(*_me, size, text);
    384 }
    385 }
    386 break;
    387 }
    388 }
    389
    390 /**
    391 * Taskwindow event sent recorded so process and acknowledge
    392 */
    393 void TaskWindow::Router::recorded_message(tbx::WimpMessageEvent &event, int reply_to)
    394 {
    395 bool old_running = _me->_running;
    396 // Base processing same as user_message
    397 user_message(event);
    398 // But send an acknowledgement if for this task window
    399 if (old_running != _me->_running || (int)_me->_child_task == event.message().sender_task_handle())
    400 {
    401 tbx::WimpMessage ack(event.message());
    402 ack.your_ref(ack.my_ref());
    403 ack.send(tbx::WimpMessage::Acknowledge, reply_to);
    404 }
    405 }
    406
    407 } /* End of namespace tbx */
  • trunk/tbx/taskwindow.h

     
    1 /*
    2 * tbx RISC OS toolbox library
    3 *
    4 * Copyright (C) 2015 Alan Buckley All Rights Reserved.
    5 *
    6 * Permission is hereby granted, free of charge, to any person obtaining a
    7 * copy of this software and associated documentation files (the "Software"),
    8 * to deal in the Software without restriction, including without limitation
    9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
    10 * and/or sell copies of the Software, and to permit persons to whom the
    11 * Software is furnished to do so, subject to the following conditions:
    12 *
    13 * The above copyright notice and this permission notice shall be included
    14 * in all copies or substantial portions of the Software.
    15 *
    16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    22 * THE SOFTWARE.
    23 */
    24 #ifndef TBX_TASKWINDOW_H
    25 #define TBX_TASKWINDOW_H
    26
    27 #include <string>
    28 #include <vector>
    29 #include "listener.h"
    30 #include "wimpmessagelistener.h"
    31
    32 namespace tbx
    33 {
    34
    35 class TaskWindowStartedListener;
    36 class TaskWindowFinishedListener;
    37 class TaskWindowOutputListener;
    38
    39 /**
    40 * Class to start a child task in a RISC OS task window
    41 */
    42 class TaskWindow
    43 {
    44 std::string _command;
    45 std::string _name;
    46 int _wimp_slot;
    47 unsigned int _options;
    48 unsigned int _child_task;
    49 unsigned int _txt_id;
    50 bool _running;
    51 static unsigned int _next_txt_id;
    52 class Router :
    53 public tbx::WimpUserMessageListener,
    54 public tbx::WimpRecordedMessageListener
    55 {
    56 TaskWindow *_me;
    57 public:
    58 Router(TaskWindow *me);
    59 ~Router();
    60 std::vector<TaskWindowStartedListener *> _started_listeners;
    61 std::vector<TaskWindowFinishedListener *> _finished_listeners;
    62 std::vector<TaskWindowOutputListener *> _output_listeners;
    63
    64 virtual void user_message(tbx::WimpMessageEvent &event);
    65 virtual void recorded_message(tbx::WimpMessageEvent &event, int reply_to);
    66 } *_router;
    67 friend Router;
    68 public:
    69 /**
    70 * Task window run options
    71 */
    72 enum Options
    73 {
    74 /**
    75 * Allow control characters to be sent to the output
    76 */
    77 ALLOW_CONTROL=1,
    78 /**
    79 * Show the taskwindow output window before any output comes from the task
    80 */
    81 DISPLAY=2,
    82 /**
    83 * Quit the task windo wht the child task finishes
    84 */
    85 QUIT=4
    86 };
    87
    88 TaskWindow();
    89 TaskWindow(std::string command, std::string name, int wimp_slot = 0, unsigned int options = 0);
    90 ~TaskWindow();
    91
    92 /**
    93 * Get the command run by this task window
    94 *
    95 * @returns task window command
    96 */
    97 const std::string &command() const { return _command; }
    98 /**
    99 * Set the command run by this task window.
    100 * Changing this will have no effect on a currently running task window
    101 * @param command command to run
    102 */
    103 void command(const std::string &command) { _command = command; }
    104 /**
    105 * Get the task name for the task window
    106 * @returns the task window name
    107 */
    108 const std::string &name() const { return _name; }
    109 /**
    110 * Set the name of the task window
    111 * Changing this will have no effect on a currently running task window
    112 * @param name the name for the task window
    113 */
    114 void name(const std::string &name) { _name = name; }
    115 /**
    116 * Get the wimp slot size in KB for the task window
    117 */
    118 int wimp_slot() const { return _wimp_slot; }
    119 /**
    120 * Set the wimp slot for the task window
    121 * Changing this will have no effect on a currently running task window
    122 * @param slot wimp slot size in KB for the task window
    123 */
    124 void wimp_slot(int slot) { _wimp_slot = slot; }
    125
    126 /**
    127 * The options applied when the task window is executed.
    128 * zero or more of the Options enum,
    129 */
    130 unsigned int options() const { return _options; }
    131 /**
    132 * Set the options when the task window is executed
    133 * Changing this will have no effect on a currently running task window
    134 * @param new_opts one or more of the options enum.
    135 */
    136 void options(unsigned int new_opts) { _options = new_opts; }
    137
    138 /**
    139 * Check if task window is running.
    140 * A task window is running from the time the run function is called
    141 * until it finishes.
    142 * @returns true if the task window is running
    143 */
    144 bool running() const { return _running; }
    145 /**
    146 * task id of the child task.
    147 * This is zero until the task has started and zero again once if finishes.
    148 * @returns child task id or 0.
    149 */
    150 unsigned int child_task() const { return _child_task; }
    151
    152 void add_started_listener(TaskWindowStartedListener *listener);
    153 void remove_started_listener(TaskWindowStartedListener *listener);
    154 void add_finished_listener(TaskWindowFinishedListener *listener);
    155 void remove_finished_listener(TaskWindowFinishedListener *listener);
    156 void add_output_listener(TaskWindowOutputListener *listener);
    157 void remove_output_listener(TaskWindowOutputListener *listener);
    158
    159 void run();
    160
    161 void send_input(const char *text, int size = -1);
    162 void send_input(const std::string &text);
    163 void suspend();
    164 void resume();
    165 void kill();
    166 };
    167
    168 /**
    169 * Listener for when a task window has started
    170 */
    171 class TaskWindowStartedListener : public tbx::Listener
    172 {
    173 public:
    174 TaskWindowStartedListener() {}
    175 virtual ~TaskWindowStartedListener() {}
    176
    177 /**
    178 * Called when the task window has started
    179 * @param task_window - the task window that has started
    180 */
    181 virtual void taskwindow_started(TaskWindow &task_window) = 0;
    182 };
    183
    184 /**
    185 * Listener for when a task window has finished
    186 */
    187 class TaskWindowFinishedListener : public tbx::Listener
    188 {
    189 public:
    190 TaskWindowFinishedListener() {}
    191 virtual ~TaskWindowFinishedListener() {}
    192
    193 /**
    194 * Called when the task window has finished
    195 * @param task_window - the task window that has finished
    196 */
    197 virtual void taskwindow_finished(TaskWindow &task_window) = 0;
    198 };
    199
    200 /**
    201 * Listener for output from a task window
    202 */
    203 class TaskWindowOutputListener : public tbx::Listener
    204 {
    205 public:
    206 TaskWindowOutputListener() {}
    207 virtual ~TaskWindowOutputListener() {}
    208
    209 /**
    210 * Called when there is output from the task window has
    211 * @param task_window the task window with output
    212 * @param size number of bytes of output
    213 * @param text output text
    214 */
    215 virtual void taskwindow_output(TaskWindow &task_window, int size, const char *text) = 0;
    216 };
    217
    218 }; /* end of namespace tbx */
    219
    220 #endif