Clapkit (CLassic APplication KIT) is a framework for developing applications for Classic Mac OS (System 7.x - Mac OS 9.x), basically a C++/object-oriented wrapper for Macintosh Toolbox functions.
It builds using Retro68 on modern systems and is designed to make application development for Mac OS 7.x - 9.x easier by doing most of the manual work one has to do when it comes to Toolbox programming.
The goal is to eventually add Carbon/Cocoa and even Win32 support to allow cross-platform development.
Warning
Clapkit is in very early stages of development. A lot of things are either not supported or how they work are subject to change.
Features
- "Modern-style" wrappers for basic Macintosh Toolbox functions.
- Easily create windows (CKWindow), controls (CKButton, CKLabel, etc.) and objects (like timers with CKTimer) using modern C++ syntax and lambda expressions.
- Basic networking support with event-based MacTCP wrapper implementation.
- Simple debug tools including debug logs and leaks checking.
Screenshot
Installation
To 'import' Clapkit, you can use FetchContent:
include(FetchContent)
FetchContent_Declare(
clapkit
GIT_REPOSITORY https://github.com/macinlink/clapkit.git
GIT_TAG main
)
FetchContent_MakeAvailable(clapkit)
target_link_libraries(my_project PRIVATE clapkit)
You may need CMakeLists.txt and .vscode/c_cpp_properties.json modified to point to your Retro68 installation. Example:
{
"configurations": [
{
"name": "Mac",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/**/**",
"~/Retro68/InterfacesAndLibraries/**",
"~/Retro68-build/toolchain/m68k-apple-macos/include/**",
"~/git/clapkit/**"
],
"defines": [],
"compilerPath": "",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "gcc-arm"
}
],
"version": 4
}
Usage
Note: For a sample app, check out The Kitchen Sink in the the Examples
folder.
Create an object for your app by subclassing CApp.
++
class MyNewApp:
public CKApp {
public:
MyNewApp() { }
~MyNewApp() { }
};
Defines the main entry point for a Clapkit application.
Definition ckApp.h:57
Then 'start' your app:
++
#define CKNew
Use instead of new.
Definition ckMemory.h:66
You can create a simple window using just a few lines of code:
++
label->
rect->origin->x = 10;
label->
rect->origin->y = 10;
CKProperty< CKRect > rect
Definition ckControl.h:105
Defines a text label.
Definition ckLabel.h:24
virtual void SetText(const char *text)
Definition ckLabel.cpp:230
Defines a window. Window type (modal, document) is determined by CKWindowInitParams and CKWindowType.
Definition ckWindow.h:89
void Center()
Definition ckWindow.cpp:185
bool AddControl(CKControl *control)
Definition ckWindow.cpp:216
void SetTitle(const char *title)
Definition ckWindow.cpp:125
void Show()
Definition ckWindow.cpp:152
Defines a rectangular area.
Definition ckTypes.h:179
Initialization parameters for a CKWindow.
Definition ckWindow.h:44
Each control type is in it's own header file that you need to import:
Add Event Handlers to listen to clicks, keydowns, etc:
++
app->CKNewMsgBoxNote("Thanks for clicking me!", nullptr, "No problem!");
});
virtual void AddHandler(CKEventType type, CKEventHandlerFunc cb)
Definition ckObject.cpp:28
@ click
User clicked on window/control.
#define kCKButtonHeight
Suggested button height for platform.
Definition ckPlatform.h:26
Defines an event raised by the framework, mostly for user actions.
Definition ckTypes.h:490
You'll need to write a run-loop as well:
++
while(1) {
int result = app->Loop(5);
if (result != 0) {
break;
}
}
A test app
A simple app can be as short as this:
++
int main() {
app->
CKNewMsgBoxNote(
"Hello world!",
nullptr,
"OK",
nullptr, [app](
int button) {
});
delete app;
return 0;
}
void CKQuit()
Definition ckApp.cpp:142
int CKLoop(int waitTime=60)
Main event loop, handling events. Needs to be called as much as possible.
Definition ckApp.cpp:77
CKWindow * CKNewMsgBoxNote(const char *message, const char *title=nullptr, const char *btnOk="OK", const char *btnCancel=nullptr, std::function< void(int button)> callback=0)
Create and show an alert. Alerts are non-blocking. btnCancel is optional and only shown if set to a n...
Definition ckApp.cpp:266
Networking
Though currently extremely buggy, a TCP client is also available via MacTCP.
++
char data[256];
sprintf(data, "GET / HTTP/1.1\nHost: google.com\n\n");
socket->
Write(data, strlen(data));
});
short readBytes;
lbl2->SetText("Read failed!");
return;
}
CKLog(
"Read %d bytes.", readBytes);
});
#define CKLog(s,...)
Wrapper for logging.
Definition ckUtils.h:45
Defines a client TCP socket.
Definition ckNetClient.h:21
virtual CKError Write(const void *data, UInt32 len)
Definition ckNetClient.cpp:122
virtual CKError Read(void *out, short len, short *actuallyRead)
Definition ckNetClient.cpp:91
virtual CKError Connect(const char *address, UInt16 port)
Definition ckNetClient.cpp:25
#define CKMalloc(sz)
Use instead of malloc.
Definition ckMemory.h:54
#define CKFree(ptr)
Use instead of free.
Definition ckMemory.h:60
int32_t CKError
Return type for functions that might return an error. Also see: CKPass and CKErrorCode.
Definition ckTypes.h:26
#define CKPass
Functions that return CKError return CKPass on success.
Definition ckTypes.h:32
@ tcpReceivedData
TCP socket has data to read.
@ tcpConnected
TCP socket has connected.
Debugging & Leaks Checking
If compiled using debug mode (cmake -DCMAKE_BUILD_TYPE=Debug
), Clapkit will not only print debug messages into the Macsbug console using user breaks, but will also keep track of memory usage and try to list leaks on quit, by creating a text file on the same folder the app was run.
Writing debug logs is very simple - just use CKLog
. They'll be stripped out in Release mode.
++
CKLog(
"The current count is %d", count);
Contributing
Clapkit is in very, very early stages of development so any kind of contributions are welcome. For code contributions, please check out .clang-format and make sure your code fits the style.
While Carbon/Cocoa/Win32 is an eventual goal, the current focus is on Mac OS 7.x-9.x so please refrain from any Carbonization, etc. effort for just now.