/* 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 . The author can be contacted on */ #include #include #include #include #include #include #include #ifdef USE_PRAGMAS #include #include #include #endif #include #include #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); }