ToolsMenu/toolrun.c

184 rindas
5.5 KiB
C
Executable File

/*
ToolsMenu - Add tools to the Workbench Tools menu
Copyright (C) 2015, 2018 Kim Fastrup Larsen
This program is free software: you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation, either ver-
sion 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be use-
ful, but WITHOUT ANY WARRANTY; without even the implied war-
ranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public Li-
cense along with this program. If not, see
<http://www.gnu.org/licenses/>.
The author can be contacted on <kimflarsen@hotmail.com>
*/
#include <dos/dos.h>
#include <dos/dostags.h>
#include <exec/memory.h>
#include <workbench/startup.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/icon_protos.h>
#ifdef USE_PRAGMAS
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/icon.h>
#endif
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "toolrun.h"
#define MIN_STACK_SIZE 4000
/* We send this extended WBStartup to any tool that we launch. It will
contain the Workbench arguments passed to run_tool(). Once the tool quits,
it will reply to this message, and at that point we call the clean_up
function (also passed to run_tool()) to let it clean up the arguments. */
typedef struct {
struct WBStartup wb_startup;
Tr_clean_up *clean_up;
void *user_data;
} Startmsg;
/* The tools we run will be told to reply to this port when they quit. */
static struct MsgPort *reply_port;
/* The number of tools started that haven't quit yet. */
static int running;
static void free_startmsg(Startmsg *msg)
{
FreeVec(msg->wb_startup.sm_ToolWindow);
FreeMem(msg, sizeof *msg);
}
/* Create the reply port and return its signal bit as a mask. */
ULONG tr_set_up()
{
if ((reply_port = CreateMsgPort()) == NULL)
exit(RETURN_FAIL);
return 1UL << reply_port->mp_SigBit;
}
Bool tr_run_tool(struct WBArg *arg_list, LONG num_args,
Tr_clean_up *clean_up, void *user_data)
{
BPTR old_dir;
struct DiskObject *disk_obj;
Startmsg *startmsg;
LONG stack_size;
BPTR home_dir;
struct Process *process;
/* Load the tool's icon and check that it really is a Workbench tool. */
old_dir = CurrentDir(arg_list[0].wa_Lock);
if ((disk_obj = GetDiskObject(arg_list[0].wa_Name)) == NULL)
goto run_tool_failed1;
if (disk_obj->do_Type != WBTOOL)
goto run_tool_failed2;
/* Use the stacksize specified in the icon unless it's too small. */
if ((stack_size = disk_obj->do_StackSize) < MIN_STACK_SIZE)
stack_size = MIN_STACK_SIZE;
/* Set up startup message. */
if ((startmsg=AllocMem(sizeof *startmsg,MEMF_PUBLIC|MEMF_CLEAR)) == NULL)
goto run_tool_failed2;
startmsg->wb_startup.sm_ArgList = arg_list;
startmsg->wb_startup.sm_NumArgs = num_args;
startmsg->clean_up = clean_up;
startmsg->user_data = user_data;
/* We copy the icon's ToolWindow if it isn't NULL. */
if (disk_obj->do_ToolWindow != NULL &&
(startmsg->wb_startup.sm_ToolWindow = copy_of(disk_obj->do_ToolWindow))
== NULL)
goto run_tool_failed3;
/* Load the tool. */
if ((startmsg->wb_startup.sm_Segment = NewLoadSegTags(
arg_list[0].wa_Name, TAG_DONE)) == NULL)
goto run_tool_failed3;
/* Get a lock for PROGDIR. */
if ((home_dir = DupLock(arg_list[0].wa_Lock)) == 0)
goto run_tool_failed4;
/* Run the tool as a non-CLI process. */
if ((process = CreateNewProcTags(
NP_Seglist, startmsg->wb_startup.sm_Segment,
NP_FreeSeglist, (LONG) TRUE,
NP_Input, NULL,
NP_CloseInput, (LONG) FALSE,
NP_Output, NULL,
NP_CloseOutput, (LONG) FALSE,
NP_Error, NULL,
NP_CloseError, (LONG) FALSE,
NP_CurrentDir, NULL,
NP_StackSize, stack_size,
NP_Name, arg_list[0].wa_Name,
NP_Priority, 0L,
NP_ConsoleTask, NULL,
NP_WindowPtr, NULL,
NP_HomeDir, home_dir,
TAG_DONE)) == NULL)
goto run_tool_failed5;
/* We have started the process, so we mustn't do anything after this point
that might fail. Send the startup message to the tool. */
startmsg->wb_startup.sm_Process = &process->pr_MsgPort;
startmsg->wb_startup.sm_Message.mn_ReplyPort = reply_port;
PutMsg(startmsg->wb_startup.sm_Process, (struct Message *) startmsg);
++running;
FreeDiskObject(disk_obj);
CurrentDir(old_dir);
return YES;
run_tool_failed5:
UnLock(home_dir);
run_tool_failed4:
UnLoadSeg(startmsg->wb_startup.sm_Segment);
run_tool_failed3:
free_startmsg(startmsg);
run_tool_failed2:
FreeDiskObject(disk_obj);
run_tool_failed1:
CurrentDir(old_dir);
return NO;
}
/* When a tool started by tr_run_tool() quits, we receive a reply to our
startup message. At this point we call the clean up function provided,
then clean up the message itself. */
void tr_handle_signal()
{
Startmsg *msg;
while ((msg = (Startmsg *) GetMsg(reply_port)) != NULL) {
(*msg->clean_up)(msg->wb_startup.sm_ArgList,
msg->wb_startup.sm_NumArgs, msg->user_data);
free_startmsg(msg);
--running;
}
}
/* Wait for all currently running tools to quit before cleaning up the
reply port and returning control to the caller. */
void tr_clean_up()
{
while (running > 0 ) {
WaitPort(reply_port);
tr_handle_signal();
}
DeleteMsgPort(reply_port);
}