New Sample Program: WDEF

This commit is contained in:
Wolfgang Thaller 2018-01-06 03:09:59 +01:00
parent f98a7672f3
commit e608b466ce
8 changed files with 617 additions and 0 deletions

View File

@ -49,6 +49,7 @@ add_subdirectory(Samples/Raytracer)
if(CMAKE_SYSTEM_NAME MATCHES Retro68)
add_subdirectory(Samples/Launcher)
add_subdirectory(Samples/SystemExtension)
add_subdirectory(Samples/WDEF)
endif()
enable_testing()

View File

@ -301,6 +301,10 @@ Intended for System 6 without Multifinder.
Shows a simple and useless dialog box. Demonstrates how to use Rez, the resource compiler.
The binary is in Retro68-build/build-target/Samples/Dialog/.
### Sample Program: WDEF
On the one hand, this is an example for a very basic multi window application with menus and desk accessories.
On the other hand, it shows how to write code resources like WDEF window definition procedures.
License
-------

View File

@ -0,0 +1,66 @@
# Copyright 2015 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/>.
# To use this example as a standalone project using CMake:
# mkdir build
# cd build
# cmake .. -DCMAKE_TOOLCHAIN_FILE=path/to/Retro68-build/toolchain/cmake/retro68.toolchain.cmake
# make
cmake_minimum_required(VERSION 2.8)
# This sample program contains a custom window definition procedure (WDEF) defined in wdef.c.
# It is used in two different ways:
# 1. The 80s way: compiled as a separate 'WDEF' code resource.
# 2. The 90s way: compiled as part of the application.
# First, let's build a separate code resource:
add_executable(WDEF wdef.c)
set_target_properties(WDEF PROPERTIES
# tell wdef.c that it is being compiled as a code resource
COMPILE_DEFINITIONS "COMPILING_AS_CODE_RESOURCE"
COMPILE_OPTIONS -ffunction-sections # make things smaller
# set a linker flag that says we want a flat piece
# of code in a data file, specify entry point,
# and add -Wl,-gc-sections to make things smaller.
LINK_FLAGS "-Wl,--mac-flat -Wl,-eMYWINDOWDEFPROC -Wl,-gc-sections")
# wrap the compiled WDEF into a resource
add_custom_command(
OUTPUT WDEF.rsrc.bin
COMMAND ${REZ} -I ${REZ_INCLUDE_PATH}
${CMAKE_CURRENT_SOURCE_DIR}/wdef.r
-o WDEF.rsrc.bin
DEPENDS WDEF wdef.r)
# Now build the application
add_application(WDEFShell
wdefshell.c
wdefshell.r
wdef.c # the WDEF as a plain source file in the application
# the separately compiled WDEF resource
${CMAKE_CURRENT_BINARY_DIR}/WDEF.rsrc.bin
)
# Again, add some options to make things smaller.
set_target_properties(Dialog PROPERTIES COMPILE_OPTIONS -ffunction-sections)
set_target_properties(Dialog PROPERTIES LINK_FLAGS "-Wl,-gc-sections")

9
Samples/WDEF/README.md Normal file
View File

@ -0,0 +1,9 @@
WDEF sample program
===================
This sample program serves several purposes.
1. It shows how to write a basic classic Mac application with windows, menus and desk accessories.
2. It shows how to write a very primitive custom window definition (WDEF).
3. It shows how to compile a WDEF (or a similar code resource) and include it in an application.
4. It shows how to avoid 3. and include the window definition procedure directly in the application.

105
Samples/WDEF/wdef.c Normal file
View File

@ -0,0 +1,105 @@
#include <Quickdraw.h>
#include <Windows.h>
#include <Fonts.h>
#ifdef COMPILING_AS_CODE_RESOURCE
#include <Retro68Runtime.h>
#include <LowMem.h>
#endif
pascal long MyWindowDefProc(short varCode, WindowRef window, short message, long param)
{
#ifdef COMPILING_AS_CODE_RESOURCE
// If we are inside a code resource, we have two problems.
// First, our machine code doesn't yet know where in RAM it is located, so things
// will crash as soon as we call a function or access a global variable.
// The following call, part of libretro, fixes that:
RETRO68_RELOCATE();
// Next, Quickdraw's global variables are stored as part of the application's
// global variables. If we acces "qd.", we'll get our own copy, which QuickDraw knows
// nothing about. So, let's get a pointer to the real thing:
QDGlobalsPtr qdPtr = (QDGlobalsPtr)( (*(Ptr*)LMGetCurrentA5()) + 4 - sizeof(QDGlobals) );
// alternatively, we could just avoid accessing QuickDraw globals. In our case, that would mean
// using GetPort instead of qdPtr->thePort, and not using qdPtr->white and qdPtr->ltGray.
#else
// We're part of the real application, we could be using qd. in the first place:
QDGlobalsPtr qdPtr = &qd;
#endif
WindowPeek peek = (WindowPeek) window;
switch(message)
{
case kWindowMsgDraw:
{
RgnHandle rgn = NewRgn();
DiffRgn(peek->strucRgn, peek->contRgn, rgn);
FillRgn(rgn, peek->hilited ? &qdPtr->white : &qdPtr->ltGray);
Point savePen = qdPtr->thePort->pnSize;
if(peek->hilited)
PenSize(3,3);
FrameRgn(rgn);
PenSize(savePen.h, savePen.v);
DisposeRgn(rgn);
short saveFace = qdPtr->thePort->txFace;
Rect r = (*peek->contRgn)->rgnBBox;
MoveTo(r.left, r.top - 10);
HLock((Handle) peek->titleHandle);
DrawString(*peek->titleHandle);
HUnlock((Handle) peek->titleHandle);
}
break;
case kWindowMsgHitTest:
{
Point p;
p.v = param >> 16;
p.h = param & 0xFFFF;
if(PtInRgn(p, peek->strucRgn))
{
if(PtInRgn(p, peek->contRgn))
return wInContent;
else
return wInDrag;
}
else
return wNoHit;
}
break;
case kWindowMsgCalculateShape:
{
Rect r = window->portRect;
OffsetRect(&r, -window->portBits.bounds.left,
-window->portBits.bounds.top);
RectRgn(peek->contRgn, &r);
InsetRect(&r, -10, -10);
RectRgn(peek->strucRgn, &r);
HLock((Handle) peek->titleHandle);
short width = StringWidth(*peek->titleHandle);
HUnlock((Handle) peek->titleHandle);
width += 20;
if(width > r.right - r.left - 10)
width = r.right - r.left - 10;
RgnHandle rgn = NewRgn();
SetRectRgn(rgn, r.left, r.top-20, r.left + width, r.top);
UnionRgn(rgn, peek->strucRgn, peek->strucRgn);
DisposeRgn(rgn);
}
break;
case kWindowMsgInitialize:
break;
case kWindowMsgCleanUp:
break;
case kWindowMsgDrawGrowOutline:
break;
case kWindowMsgDrawGrowBox:
break;
}
return 0;
}

5
Samples/WDEF/wdef.r Normal file
View File

@ -0,0 +1,5 @@
#include "Retro68.r"
data 'WDEF' (129) {
$$read("WDEF")
};

295
Samples/WDEF/wdefshell.c Normal file
View File

@ -0,0 +1,295 @@
/*
Copyright 2017 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>
// in wdef.c
extern pascal long MyWindowDefProc(short varCode, WindowRef window, short message, long param);
static Rect initialWindowRect, nextWindowRect;
enum
{
kMenuApple = 128,
kMenuFile,
kMenuEdit
};
enum
{
kItemAbout = 1,
kItemNewDoc = 1,
kItemNewRounded = 2,
kItemNewCustomFromStub = 3,
kItemNewCustomFromRes = 4,
kItemClose = 5,
kItemQuit = 7
};
void MakeNewWindow(ConstStr255Param title, short procID)
{
if(nextWindowRect.bottom > qd.screenBits.bounds.bottom
|| nextWindowRect.right > qd.screenBits.bounds.right)
{
nextWindowRect = initialWindowRect;
}
WindowRef w = NewWindow(NULL, &nextWindowRect, title, true, procID, (WindowPtr) -1, true, 0);
OffsetRect(&nextWindowRect, 15, 15);
}
void InitCustomWDEF()
{
/* The 10-byte code resource stub trick.
*
* The bytes in this resource are 68K machine code for
* move.l L1(pc), -(sp) | 2F3A 0004
* rts | 4E75
* L1: dc.l 0x00000000 | 0000 0000
*
* The application loads this resource and replaces the final four bytes
* with the address of MyWindowDefProc.
*/
Handle h = GetResource('WDEF', 128);
HLock(h);
*(WindowDefProcPtr*)(*h + 6) = &MyWindowDefProc;
// By the way, this was the only part of this file relevant for dealing
// with custom WDEFs.
}
void ShowAboutBox()
{
WindowRef w = GetNewWindow(128, NULL, (WindowPtr) - 1);
MacMoveWindow(w,
qd.screenBits.bounds.right/2 - w->portRect.right/2,
qd.screenBits.bounds.bottom/2 - w->portRect.bottom/2,
false);
ShowWindow(w);
SetPort(w);
Handle h = GetResource('TEXT', 128);
HLock(h);
Rect r = w->portRect;
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(w) // Close menu item: enabled if there is a window
EnableItem(m,kItemClose);
else
DisableItem(m,kItemClose);
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();
else
{
GetMenuItemText(GetMenu(128), menuItem, str);
OpenDeskAcc(str);
}
}
else if(menuID == kMenuFile)
{
switch(menuItem)
{
case kItemNewDoc:
GetIndString(str,128,1);
MakeNewWindow(str, documentProc); // plain document window
break;
case kItemNewRounded:
GetIndString(str,128,2);
MakeNewWindow(str, 16); // rounded document window
break;
case kItemNewCustomFromStub:
GetIndString(str,128,3);
MakeNewWindow(str, 128*16); // custom window, loaded via 10-byte stub
break;
case kItemNewCustomFromRes:
GetIndString(str,128,4);
MakeNewWindow(str, 129*16); // custom window, compiled as resource
break;
case kItemClose: // close
w = FrontWindow();
if(w)
{
if(GetWindowKind(w) < 0)
CloseDeskAcc(GetWindowKind(w));
else
DisposeWindow(FrontWindow());
}
break;
case kItemQuit:
ExitToShell();
break;
}
}
else if(menuID == kMenuEdit)
{
if(!SystemEdit(menuItem - 1))
{
// edit command not handled by desk accessory
}
}
HiliteMenu(0);
}
void DoUpdate(WindowRef w)
{
SetPort(w);
BeginUpdate(w);
Rect r;
SetRect(&r, 20,20,120,120);
FrameOval(&r);
OffsetRect(&r, 32, 32);
FillRoundRect(&r, 16, 16, &qd.ltGray);
FrameRoundRect(&r, 16, 16);
OffsetRect(&r, 32, 32);
FillRect(&r, &qd.gray);
FrameRect(&r);
EndUpdate(w);
}
int main()
{
InitGraf(&qd.thePort);
InitFonts();
InitWindows();
InitMenus();
TEInit();
InitDialogs(NULL);
SetMenuBar(GetNewMBar(128));
AppendResMenu(GetMenu(128), 'DRVR');
DrawMenuBar();
InitCustomWDEF();
InitCursor();
Rect r;
SetRect(&initialWindowRect,20,60,400,260);
nextWindowRect = initialWindowRect;
for(;;)
{
EventRecord e;
WindowRef win;
SystemTask();
if(GetNextEvent(everyEvent, &e))
{
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;
case inSysWindow:
SystemClick(&e, win);
break;
}
break;
case updateEvt:
DoUpdate((WindowRef)e.message);
break;
}
}
}
return 0;
}

132
Samples/WDEF/wdefshell.r Normal file
View File

@ -0,0 +1,132 @@
/*
Copyright 2017 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 WDEF Shell...", noIcon, noKey, noMark, plain;
"-", noIcon, noKey, noMark, plain;
}
};
resource 'MENU' (129) {
129, textMenuProc;
allEnabled, enabled;
"File";
{
"New Document Window", noIcon, "N", noMark, plain;
"New Rounded Window", noIcon, "M", noMark, plain;
"New Custom Window (10-byte stub)", noIcon, "1", noMark, plain;
"New Custom Window (code resource)", noIcon, "2", noMark, plain;
"Close", noIcon, "W", noMark, plain;
"-", noIcon, noKey, noMark, plain;
"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 };
};
resource 'STR#' (128) {
{
"Standard Document Window",
"Rounded Document Window",
"Custom Window (10-byte stub)",
"Custom Window (code resource)";
}
};
data 'TEXT' (128) {
"About WDEF Shell\r\r"
"A Sample program that tries to show both how to write a basic classic Mac application, "
"and how to write a custom window definition procedure (WDEF) using Retro68.\r"
"Other code resources (CDEF, MDEF, LDEF, ...) aren't that much different.\r"
"\r\rWritten in 2018, so everything here has been obsolete for two decades."
};
resource 'WIND' (128) {
{0, 0, 220, 320}, altDBoxProc;
invisible;
noGoAway;
0, "";
noAutoCenter;
};
/* The 10-byte code resource stub trick.
*
* The bytes in this resource are 68K machine code for
* move.l L1(pc), -(sp) | 2F3A 0004
* rts | 4E75
* L1: dc.l 0x00000000 | 0000 0000
*
* The application loads this resource and replaces the final four bytes
* with the address of the WDEF function in wdef.c, which is compiled as part
* of the application.
*/
data 'WDEF' (128) {
$"2F3A 0004 4E75 0000 0000"
};
resource 'SIZE' (-1) {
dontSaveScreen,
acceptSuspendResumeEvents,
enableOptionSwitch,
canBackground,
multiFinderAware,
backgroundAndForeground,
dontGetFrontClicks,
ignoreChildDiedEvents,
is32BitCompatible,
isHighLevelEventAware,
onlyLocalHLEvents,
notStationeryAware,
reserved,
reserved,
reserved,
reserved,
#ifdef TARGET_API_MAC_CARBON
500 * 1024, // Carbon apparently needs additional memory.
500 * 1024
#else
100 * 1024,
100 * 1024
#endif
};