I chose the solution where I divide the desktop among different programs. I can now create widgets on the desktop. It does only drawing, no clicking, dragging or resizing yet. I'll do that later. With clicking, dragging and resizing it will be possible to have a dock like in OSX.
It continues to work when Finder quits. If Finder is running, then it continues to draw icons on the desktop. You should quit Finder, except if you are developing widgets and you have no Finder replacement.
Widgets have to be applications or appe extensions. Typically you program widgets as appe extensions and the rest of the desktop as an application. You can resort to offscreen drawing to avoid flickering (unlike Finder). Widgets may be stopped in the debugger. This doesn't cause the extension to crash.
This works now as a 68K extension. This is already so fast that you don't notice it, but I'll write a PPC version anyway.
Errors are logged in the "Log" file in the "OS 9.3" folder in the documents folder to make debugging easier.
You need to install the extension and include this library, which is fairly simple:
//========================================
// File: Desktop.h
//========================================
#pragma once
class Desktop
{
public:
// Global functions.
static Bool Init_class();
static Bool Claim(OSType creator,
RgnHandle claimRgn,
UInt32 &ref);
static Bool ClaimRest(OSType creator,
UInt32 &ref);
static Bool Release(OSType creator);
static Bool ReleaseRest();
static Bool Check();
static Bool CheckPart(UInt32 ref,
bool &needsUpdate);
static Bool BeginUpdate(UInt32 ref);
static Bool EndUpdate(UInt32 ref);
static Bool Refresh(UInt32 ref);
// Forbidden.
private:
Desktop();
};
claimRgn is the region of the desktop that you would like to have. Sometimes this won't be possible because part of the region is already taken. Then you get what's left over, or nothing. With every claim or release the desktop regions are recalculated.
BeginUpdate and EndUpdate set and restore the right port and clipping and clear the update region.
This will be more complicated when I have done the clicking, dragging and resizing.
The following example is a widget that draws a white ball with a black border:
//========================================
// File: Ball.cp
//========================================
#include "Ball.h"
#include "BallApp.h"
int main()
{
SetDebugThrow_(debugAction_Alert);
SetDebugSignal_(debugAction_Alert);
InitializeHeap(3);
UQDGlobals::InitializeToolbox();
new LGrowZone(20000);
GuiError::Init_class();
#ifdef __DEBUG__
Error::Show("\pDebug version");
#endif
if (!Desktop::Init_class())
{
Return_0("\pInit Desktop class fails");
}
BallApp app;
app.Run();
return 0;
}
//========================================
// File: BallApp.h
//========================================
#pragma once
class BallApp : public LApplication
{
private:
// Derivation.
typedef LApplication base;
// Global data.
static const OSType creator=
#ifdef __DEBUG__
'ball'
#else
'Ball'
#endif
;
// Member data.
Rect m_bounds;
RgnHandle m_claimRgn;
UInt32 m_ref;
// Construction.
public:
BallApp();
// Destruction.
virtual ~BallApp();
// Forbidden.
private:
BallApp(const BallApp &original);
void operator=(const BallApp &original);
// LApplication.
public:
virtual void ProcessNextEvent();
protected:
virtual void Initialize();
// Manipulators.
private:
Bool HandleDesktop();
void Draw();
};
//========================================
// File: BallApp.cp
//========================================
#include "BallApp.h"
BallApp::BallApp()
: m_ref(0)
{
::SetRect(&m_bounds,
100,
100,
200,
200);
m_claimRgn=::NewRgn();
::OpenRgn();
::FrameOval(&m_bounds);
::CloseRgn(m_claimRgn);
}
BallApp::~BallApp()
{
if (m_ref)
{
if (!Desktop::Release(creator))
{
Return("\pRelease fails");
}
}
::DisposeRgn(m_claimRgn);
}
void BallApp::ProcessNextEvent()
{
EventRecord macEvent;
if (IsOnDuty())
{
UEventMgr::GetMouseAndModifiers(macEvent);
AdjustCursor(macEvent);
}
SetUpdateCommandStatus(false);
Boolean gotEvent=::WaitNextEvent(everyEvent,
&macEvent,
mSleepTime,
mMouseRgn);
if (!HandleDesktop())
{
EXIT("\pHandle desktop fails");
}
if (LAttachable::ExecuteAttachments(msg_Event,
&macEvent))
{
if (gotEvent)
{
DispatchEvent(macEvent);
}
else
{
UseIdleTime(macEvent);
}
}
LPeriodical::DevoteTimeToRepeaters(macEvent);
if (IsOnDuty() and GetUpdateCommandStatus())
{
UpdateMenus();
}
}
void BallApp::Initialize()
{
// Rem: m_claimRgn will be copied to system memory,
// but I keep it around just in case.
// For example, I might want to claim another area
// minus this one.
if (!Desktop::Claim(creator,
m_claimRgn,
m_ref))
{
EXIT("\pClaim fails");
}
}
Bool BallApp::HandleDesktop()
{
if (!Desktop::Check())
{
Return_false("\pCheck fails");
}
bool needsUpdate;
if (!Desktop::CheckPart(m_ref,
needsUpdate))
{
Return_false("\pCheck part fails");
}
if (!needsUpdate)
{
return True;
}
if (!Desktop::BeginUpdate(m_ref))
{
Return_false("\pBegin update fails");
}
Draw();
if (!Desktop::EndUpdate(m_ref))
{
Return_false("\pEnd update fails");
}
return True;
}
void BallApp::Draw()
{
::RGBForeColor(&Color_Black);
::RGBBackColor(&Color_White);
::EraseRgn(m_claimRgn);
::FrameRgn(m_claimRgn);
}
Now I concentrate on the problem of colored window frames. See in the attachment the sort of windows that I want.