* Copyright 2017 Julian Harnath <julian.harnath@rwth-aachen.de>
* All rights reserved. Distributed under the terms of the MIT license.
*/
#include "BarberPole.h"
#include <AutoLocker.h>
#include <ControlLook.h>
#include <Locker.h>
#include <ObjectList.h>
#include <Messenger.h>
#include <pthread.h>
#include <stdio.h>
Keeps a list of all barber poles of this team and runs its own
thread to invalidate them in regular intervals.
*/
class MachineRoom
{
private:
enum {
kSpinInterval = 20000
};
private:
MachineRoom()
:
fMessengers(20)
{
fSpinLoopLock = create_sem(0, "BarberPole lock");
fSpinLoopThread = spawn_thread(&MachineRoom::_StartSpinLoop,
"The Barber Machine", B_DISPLAY_PRIORITY, this);
resume_thread(fSpinLoopThread);
}
public:
static void AttachBarberPole(BarberPole* pole)
{
_InitializeIfNeeded();
sInstance->_Attach(pole);
}
static void DetachBarberPole(BarberPole* pole)
{
sInstance->_Detach(pole);
}
private:
static void _Initialize()
{
sInstance = new MachineRoom();
}
static status_t _StartSpinLoop(void* instance)
{
static_cast<MachineRoom*>(instance)->_SpinLoop();
return B_OK;
}
static void _InitializeIfNeeded()
{
pthread_once(&sOnceControl, &MachineRoom::_Initialize);
}
void _Attach(BarberPole* pole)
{
AutoLocker<BLocker> locker(fLock);
bool wasEmpty = fMessengers.IsEmpty();
BMessenger* messenger = new BMessenger(pole);
fMessengers.AddItem(messenger);
if (wasEmpty)
release_sem(fSpinLoopLock);
}
void _Detach(BarberPole* pole)
{
AutoLocker<BLocker> locker(fLock);
for (int32 i = 0; i < fMessengers.CountItems(); i++) {
BMessenger* messenger = fMessengers.ItemAt(i);
if (messenger->Target(NULL) == pole) {
fMessengers.RemoveItem(messenger, true);
break;
}
}
if (fMessengers.IsEmpty())
acquire_sem(fSpinLoopLock);
}
void _SpinLoop()
{
for (;;) {
AutoLocker<BLocker> locker(fLock);
for (int32 i = 0; i < fMessengers.CountItems(); i++) {
BMessenger* messenger = fMessengers.ItemAt(i);
messenger->SendMessage(BarberPole::kRefreshMessage);
}
locker.Unset();
acquire_sem(fSpinLoopLock);
release_sem(fSpinLoopLock);
snooze(kSpinInterval);
}
}
private:
static MachineRoom* sInstance;
static pthread_once_t sOnceControl;
thread_id fSpinLoopThread;
sem_id fSpinLoopLock;
BLocker fLock;
BObjectList<BMessenger, true> fMessengers;
};
MachineRoom* MachineRoom::sInstance = NULL;
pthread_once_t MachineRoom::sOnceControl = PTHREAD_ONCE_INIT;
BarberPole::BarberPole(const char* name)
:
BView(name, B_WILL_DRAW | B_FRAME_EVENTS | B_FULL_UPDATE_ON_RESIZE),
fIsSpinning(false),
fSpinSpeed(0.05),
fColors(NULL),
fNumColors(0),
fScrollOffset(0.0),
fStripeWidth(0.0),
fNumStripes(0)
{
rgb_color defaultColors[2];
rgb_color otherColor = tint_color(ui_color(B_STATUS_BAR_COLOR), 1.3);
otherColor.alpha = 50;
defaultColors[0] = otherColor;
defaultColors[1] = B_TRANSPARENT_COLOR;
SetColors(defaultColors, 2);
}
BarberPole::~BarberPole()
{
Stop();
delete[] fColors;
}
void
BarberPole::MessageReceived(BMessage* message)
{
switch (message->what) {
case kRefreshMessage:
_Spin();
break;
default:
BView::MessageReceived(message);
break;
}
}
void
BarberPole::Draw(BRect updateRect)
{
if (fIsSpinning)
_DrawSpin(updateRect);
else
_DrawNonSpin(updateRect);
}
void
BarberPole::_DrawSpin(BRect updateRect)
{
float position = -fStripeWidth * (fNumColors + 0.5) + fScrollOffset;
BRect bounds = Bounds();
bounds.InsetBy(-2, -2);
be_control_look->DrawStatusBar(this, bounds, updateRect,
ui_color(B_PANEL_BACKGROUND_COLOR), ui_color(B_STATUS_BAR_COLOR),
bounds.Width());
SetDrawingMode(B_OP_ALPHA);
uint32 colorIndex = 0;
for (uint32 i = 0; i < fNumStripes; i++) {
SetHighColor(fColors[colorIndex]);
colorIndex++;
if (colorIndex >= fNumColors)
colorIndex = 0;
BRect stripeFrame = fStripe.Frame();
fStripe.MapTo(stripeFrame,
stripeFrame.OffsetToCopy(position, 0.0));
FillPolygon(&fStripe);
position += fStripeWidth;
}
SetDrawingMode(B_OP_COPY);
bounds = Bounds();
be_control_look->DrawBorder(this, bounds, updateRect,
ui_color(B_PANEL_BACKGROUND_COLOR), B_PLAIN_BORDER);
}
spinning going on. The logic to render the striped background comes
from the 'drivesetup' application.
*/
void
BarberPole::_DrawNonSpin(BRect updateRect)
{
static const pattern kStripes = { { 0xc7, 0x8f, 0x1f, 0x3e, 0x7c,
0xf8, 0xf1, 0xe3 } };
BRect bounds = Bounds();
SetHighUIColor(B_PANEL_BACKGROUND_COLOR, B_DARKEN_1_TINT);
SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
FillRect(bounds, kStripes);
be_control_look->DrawBorder(this, bounds, updateRect,
ui_color(B_PANEL_BACKGROUND_COLOR), B_PLAIN_BORDER);
}
void
BarberPole::FrameResized(float width, float height)
{
fStripeWidth = (width / 4) + 5;
if (fStripeWidth > 200)
fStripeWidth = 200;
BPoint stripePoints[4];
stripePoints[0].Set(fStripeWidth * 0.5, 0.0);
stripePoints[1].Set(fStripeWidth * 1.5, 0.0);
stripePoints[2].Set(fStripeWidth, height);
stripePoints[3].Set(0.0, height);
fStripe = BPolygon(stripePoints, 4);
fNumStripes = (int32)ceilf((width) / fStripeWidth) + 1 + fNumColors;
}
BSize
BarberPole::MinSize()
{
BSize result = BView::MinSize();
if (result.width < 50)
result.SetWidth(50);
if (result.height < 5)
result.SetHeight(5);
return result;
}
void
BarberPole::Start()
{
if (fIsSpinning)
return;
MachineRoom::AttachBarberPole(this);
fIsSpinning = true;
}
void
BarberPole::Stop()
{
if (!fIsSpinning)
return;
MachineRoom::DetachBarberPole(this);
fIsSpinning = false;
Invalidate();
}
void
BarberPole::SetSpinSpeed(float speed)
{
if (speed > 1.0f)
speed = 1.0f;
if (speed < -1.0f)
speed = -1.0f;
fSpinSpeed = speed;
}
void
BarberPole::SetColors(const rgb_color* colors, uint32 numColors)
{
delete[] fColors;
rgb_color* colorsCopy = new rgb_color[numColors];
for (uint32 i = 0; i < numColors; i++)
colorsCopy[i] = colors[i];
fColors = colorsCopy;
fNumColors = numColors;
}
void
BarberPole::_Spin()
{
fScrollOffset += fStripeWidth / (1.0f / fSpinSpeed);
if (fScrollOffset >= fStripeWidth * fNumColors) {
fScrollOffset = 0;
}
Invalidate();
}