LaunchAPPL/Serial: start a nice GUI for the server

This commit is contained in:
Wolfgang Thaller 2018-04-23 21:32:43 +02:00
parent e2967f3bb9
commit fc9f941891
5 changed files with 526 additions and 160 deletions

View File

@ -149,7 +149,7 @@ bool SerialLauncher::Go(int timeout)
read(&tmp, 4);
tmp = ntohl(tmp);
return false;
return true;
}

View File

@ -1,6 +1,8 @@
add_application(LaunchAPPLServer CONSOLE
main.cc
add_application(LaunchAPPLServer
LaunchAPPLServer.r
LaunchAPPLServer.cc
MacSerialStream.h
MacSerialStream.cc)
target_link_libraries(LaunchAPPLServer LaunchAPPLCommon)
set_target_properties(LaunchAPPLServer PROPERTIES LINK_FLAGS "-Wl,-gc-sections")

View File

@ -0,0 +1,408 @@
/*
Copyright 2018 Wolfgang Thaller.
This file is part of Retro68.
Retro68 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 version 3 of the License, or
(at your option) any later version.
Retro68 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty 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 License
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
*/
#include <Quickdraw.h>
#include <Windows.h>
#include <Menus.h>
#include <Fonts.h>
#include <Resources.h>
#include <TextEdit.h>
#include <TextUtils.h>
#include <Dialogs.h>
#include <Devices.h>
#include "MacSerialStream.h"
#include <ReliableStream.h>
#include <Processes.h>
#include <string.h>
#include <stdio.h>
#include <UnreliableStream.h>
enum
{
kMenuApple = 128,
kMenuFile,
kMenuEdit
};
enum
{
kItemAbout = 1,
kItemQuit = 1
};
void ShowAboutBox()
{
WindowRef w = GetNewWindow(128, NULL, (WindowPtr) -1);
#if !TARGET_API_MAC_CARBON
MacMoveWindow(w,
qd.screenBits.bounds.right/2 - w->portRect.right/2,
qd.screenBits.bounds.bottom/2 - w->portRect.bottom/2,
false);
#endif
ShowWindow(w);
#if TARGET_API_MAC_CARBON
SetPortWindowPort(w);
#else
SetPort(w);
#endif
Handle h = GetResource('TEXT', 128);
HLock(h);
#if TARGET_API_MAC_CARBON
Rect r;
GetWindowPortBounds(w,&r);
#else
Rect r = w->portRect;
#endif
InsetRect(&r, 10,10);
TETextBox(*h, GetHandleSize(h), &r, teJustLeft);
ReleaseResource(h);
while(!Button())
;
while(Button())
;
FlushEvents(everyEvent, 0);
DisposeWindow(w);
}
void UpdateMenus()
{
MenuRef m = GetMenu(kMenuFile);
WindowRef w = FrontWindow();
#if TARGET_API_MAC_CARBON
#define EnableItem EnableMenuItem
#define DisableItem DisableMenuItem
#endif
m = GetMenu(kMenuEdit);
if(w && GetWindowKind(w) < 0)
{
// Desk accessory in front: Enable edit menu items
EnableItem(m,1);
EnableItem(m,3);
EnableItem(m,4);
EnableItem(m,5);
EnableItem(m,6);
}
else
{
// Application window or nothing in front, disable edit menu
DisableItem(m,1);
DisableItem(m,3);
DisableItem(m,4);
DisableItem(m,5);
DisableItem(m,6);
}
}
void DoMenuCommand(long menuCommand)
{
Str255 str;
WindowRef w;
short menuID = menuCommand >> 16;
short menuItem = menuCommand & 0xFFFF;
if(menuID == kMenuApple)
{
if(menuItem == kItemAbout)
ShowAboutBox();
#if !TARGET_API_MAC_CARBON
else
{
GetMenuItemText(GetMenu(128), menuItem, str);
OpenDeskAcc(str);
}
#endif
}
else if(menuID == kMenuFile)
{
switch(menuItem)
{
case kItemQuit:
ExitToShell();
break;
}
}
else if(menuID == kMenuEdit)
{
#if !TARGET_API_MAC_CARBON
if(!SystemEdit(menuItem - 1))
#endif
{
// edit command not handled by desk accessory
}
}
HiliteMenu(0);
}
WindowPtr statusWindow;
Str255 statusString = "\p";
int progressDone, progressTotal = 0;
void DoUpdate(WindowRef w)
{
if(w != statusWindow)
return;
#if TARGET_API_MAC_CARBON
SetPortWindowPort(w);
#else
SetPort(w);
#endif
BeginUpdate(w);
EraseRect(&w->portRect);
MoveTo(10,20);
DrawString(statusString);
Rect r;
if(progressTotal)
{
SetRect(&r, 10, 40, w->portRect.right-10, 60);
FrameRect(&r);
SetRect(&r, 10, 40, 10 + (w->portRect.right-20) * progressDone / progressTotal, 60);
PaintRect(&r);
}
EndUpdate(w);
}
enum class AppStatus
{
ready = 1,
downloading = 2,
running = 3,
uploading = 4
};
void SetStatus(AppStatus stat, int done = 0, int total = 0)
{
GetIndString(statusString,128,(short)stat);
progressTotal = total;
progressDone = done;
SetPort(statusWindow);
InvalRect(&statusWindow->portRect);
}
class Listener : public StreamListener
{
uint32_t dataSize, rsrcSize;
uint32_t remainingSize;
short refNum;
public:
enum class State
{
size,
data,
rsrc,
launch,
stop
};
State state = State::size;
size_t onReceive(const uint8_t* p, size_t n)
{
switch(state)
{
case State::size:
{
if(n < 8)
return 0;
dataSize = *(const uint32_t*)p;
rsrcSize = *(const uint32_t*)(p+4);
SetStatus(AppStatus::downloading, 0, dataSize + rsrcSize);
printf("Data Size: %u / %u\n", dataSize, rsrcSize);
FSDelete("\pRetro68App", 0);
Create("\pRetro68App", 0, '????', 'APPL');
OpenDF("\pRetro68App", 0, &refNum);
state = State::data;
remainingSize = dataSize;
return 8;
}
case State::data:
{
long count = n < remainingSize ? n : remainingSize;
FSWrite(refNum, &count, p);
remainingSize -= count;
SetStatus(AppStatus::downloading, dataSize - remainingSize, dataSize + rsrcSize);
if(remainingSize)
return count;
FSClose(refNum);
OpenRF("\pRetro68App", 0, &refNum);
state = State::rsrc;
remainingSize = rsrcSize;
return count;
}
case State::rsrc:
{
long count = n < remainingSize ? n : remainingSize;
FSWrite(refNum, &count, p);
remainingSize -= count;
SetStatus(AppStatus::downloading, dataSize + rsrcSize - remainingSize, dataSize + rsrcSize);
if(remainingSize)
return count;
FSClose(refNum);
SetStatus(AppStatus::running);
state = State::launch;
return count;
}
}
}
};
int main()
{
#if !TARGET_API_MAC_CARBON
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(NULL);
#endif
SetMenuBar(GetNewMBar(128));
AppendResMenu(GetMenu(128), 'DRVR');
DrawMenuBar();
InitCursor();
statusWindow = GetNewWindow(129, NULL, (WindowPtr) -1);
SetStatus(AppStatus::ready);
MacSerialStream stream;
//#define SIMULATE_ERRORS
#ifdef SIMULATE_ERRORS
UnreliableStream uStream(stream);
ReliableStream rStream(uStream);
#else
ReliableStream rStream(stream);
#endif
Listener listener;
rStream.setListener(&listener);
for(;;)
{
EventRecord e;
WindowRef win;
#if 0 && !TARGET_API_MAC_CARBON
SystemTask();
if(GetNextEvent(everyEvent, &e))
#else
// actually, we should be using WaitNextEvent
// on everything starting from System 7
if(WaitNextEvent(everyEvent, &e, 10, NULL))
#endif
{
switch(e.what)
{
case keyDown:
if(e.modifiers & cmdKey)
{
UpdateMenus();
DoMenuCommand(MenuKey(e.message & charCodeMask));
}
break;
case mouseDown:
switch(FindWindow(e.where, &win))
{
case inGoAway:
if(TrackGoAway(win, e.where))
DisposeWindow(win);
break;
case inDrag:
DragWindow(win, e.where, &qd.screenBits.bounds);
break;
case inMenuBar:
UpdateMenus();
DoMenuCommand( MenuSelect(e.where) );
break;
case inContent:
SelectWindow(win);
break;
#if !TARGET_API_MAC_CARBON
case inSysWindow:
SystemClick(&e, win);
break;
#endif
}
break;
case updateEvt:
DoUpdate((WindowRef)e.message);
break;
}
}
stream.idle();
if(listener.state == Listener::State::launch)
{
stream.close();
{
LaunchParamBlockRec lpb;
memset(&lpb, 0, sizeof(lpb));
lpb.reserved1 = (unsigned long) "\pRetro68App";
lpb.reserved2 = 0;
lpb.launchBlockID = extendedBlock;
lpb.launchEPBLength = 6;
lpb.launchFileFlags = 0;
lpb.launchControlFlags = 0xC000;
printf("Launching...\n");
OSErr err = LaunchApplication(&lpb);
listener.state = Listener::State::size;
uint32_t zero = 0;
stream.write(&zero, 4);
stream.flushWrite();
}
}
}
return 0;
}

View File

@ -0,0 +1,113 @@
/*
Copyright 2018 Wolfgang Thaller.
This file is part of Retro68.
Retro68 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 version 3 of the License, or
(at your option) any later version.
Retro68 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty 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 License
along with Retro68. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Processes.r"
#include "Menus.r"
#include "Windows.r"
#include "MacTypes.r"
resource 'MENU' (128) {
128, textMenuProc;
allEnabled, enabled;
apple;
{
"About LaunchAPPLServer...", noIcon, noKey, noMark, plain;
"-", noIcon, noKey, noMark, plain;
}
};
resource 'MENU' (129) {
129, textMenuProc;
allEnabled, enabled;
"File";
{
"Quit", noIcon, "Q", noMark, plain;
}
};
resource 'MENU' (130) {
130, textMenuProc;
0, enabled;
"Edit";
{
"Undo", noIcon, "Z", noMark, plain;
"-", noIcon, noKey, noMark, plain;
"Cut", noIcon, "X", noMark, plain;
"Copy", noIcon, "C", noMark, plain;
"Paste", noIcon, "V", noMark, plain;
"Clear", noIcon, noKey, noMark, plain;
}
};
resource 'MBAR' (128) {
{ 128, 129, 130 };
};
data 'TEXT' (128) {
"About LaunchAPPLShell\r\r"
"Listens on the modem port for an application sent by Retro68's LaunchAPPL tool."
};
resource 'WIND' (128, "About") {
{0, 0, 150, 320}, altDBoxProc;
invisible;
noGoAway;
0, "";
noAutoCenter;
};
resource 'WIND' (129, "Main") {
{30, 10, 200, 400}, noGrowDocProc;
visible;
noGoAway;
0, "Retro68 Application Launching Server";
centerMainScreen;
};
resource 'STR#' (128) {
{
"Ready.";
"Downloading Application...";
"Running Application...";
"Sending Results...";
}
};
resource 'SIZE' (-1) {
dontSaveScreen,
acceptSuspendResumeEvents,
enableOptionSwitch,
canBackground,
multiFinderAware,
backgroundAndForeground,
dontGetFrontClicks,
ignoreChildDiedEvents,
is32BitCompatible,
reserved,
reserved,
reserved,
reserved,
reserved,
reserved,
reserved,
256 * 1024,
256 * 1024
};

View File

@ -1,157 +0,0 @@
#include <stdio.h>
#include <Events.h>
#include <string.h>
#include "MacSerialStream.h"
#include <ReliableStream.h>
#include <Processes.h>
#include <UnreliableStream.h>
/*
class DumpToConsole : public StreamListener
{
public:
size_t onReceive(const uint8_t* p, size_t n)
{
for(int i = 0; i < n; i++)
putchar(p[i]);
return n;
}
};*/
class Listener : public StreamListener
{
uint32_t size;
short refNum;
public:
enum class State
{
dataSize,
data,
rsrcSize,
rsrc,
launch,
stop
};
State state = State::dataSize;
size_t onReceive(const uint8_t* p, size_t n)
{
switch(state)
{
case State::dataSize:
{
if(n < 4)
return 0;
size = *(const uint32_t*)p;
printf("Data Size: %u\n", size);
FSDelete("\pRetro68App", 0);
Create("\pRetro68App", 0, '????', 'APPL');
OpenDF("\pRetro68App", 0, &refNum);
state = State::data;
return 4 + onReceive(p+4, n-4);
}
case State::data:
{
long count = n < size ? n : size;
FSWrite(refNum, &count, p);
if(count < size)
return count;
FSClose(refNum);
state = State::rsrcSize;
return count;
}
case State::rsrcSize:
{
if(n < 4)
return 0;
size = *(const uint32_t*)p;
printf("Rsrc Size: %u\n", size);
OpenRF("\pRetro68App", 0, &refNum);
state = State::rsrc;
return 4;
}
case State::rsrc:
{
long count = n < size ? n : size;
FSWrite(refNum, &count, p);
if(count < size)
{
size -= count;
return count;
}
FSClose(refNum);
state = State::launch;
return count;
}
}
}
};
int main()
{
OSErr err;
short outRefNum, inRefNum;
printf("Hello.\n");
{
MacSerialStream stream;
//#define SIMULATE_ERRORS
#ifdef SIMULATE_ERRORS
UnreliableStream uStream(stream);
ReliableStream rStream(uStream);
#else
ReliableStream rStream(stream);
#endif
Listener listener;
rStream.setListener(&listener);
while(!Button())
{
stream.idle();
if(listener.state == Listener::State::launch)
{
stream.close();
{
LaunchParamBlockRec lpb;
memset(&lpb, 0, sizeof(lpb));
lpb.reserved1 = (unsigned long) "\pRetro68App";
lpb.reserved2 = 0;
lpb.launchBlockID = extendedBlock;
lpb.launchEPBLength = 6;
lpb.launchFileFlags = 0;
lpb.launchControlFlags = 0xC000;
printf("Launching...\n");
OSErr err = LaunchApplication(&lpb);
printf("Still here after launch (err = %d). Press Enter to exit.\n", (int)err);
getchar();
return 0;
}
}
}
}
return 0;
}