2 * Copyright 2017 Julian Harnath <julian.harnath@rwth-aachen.de>
3 * All rights reserved. Distributed under the terms of the MIT license.
7 #include "BarberPole.h"
9 #include <AutoLocker.h>
10 #include <ControlLook.h>
12 #include <ObjectList.h>
13 #include <Messenger.h>
19 // #pragma mark - MachineRoom
22 /*! The machine room spins all the barber poles.
23 Keeps a list of all barber poles of this team and runs its own
24 thread to invalidate them in regular intervals.
30 kSpinInterval
= 20000 // us
38 fSpinLoopLock
= create_sem(0, "BarberPole lock");
39 fSpinLoopThread
= spawn_thread(&MachineRoom::_StartSpinLoop
,
40 "The Barber Machine", B_DISPLAY_PRIORITY
, this);
41 resume_thread(fSpinLoopThread
);
45 static void AttachBarberPole(BarberPole
* pole
)
47 _InitializeIfNeeded();
48 sInstance
->_Attach(pole
);
51 static void DetachBarberPole(BarberPole
* pole
)
53 sInstance
->_Detach(pole
);
57 static void _Initialize()
59 sInstance
= new MachineRoom();
62 static status_t
_StartSpinLoop(void* instance
)
64 static_cast<MachineRoom
*>(instance
)->_SpinLoop();
68 static void _InitializeIfNeeded()
70 pthread_once(&sOnceControl
, &MachineRoom::_Initialize
);
73 void _Attach(BarberPole
* pole
)
75 AutoLocker
<BLocker
> locker(fLock
);
77 bool wasEmpty
= fMessengers
.IsEmpty();
79 BMessenger
* messenger
= new BMessenger(pole
);
80 fMessengers
.AddItem(messenger
);
83 release_sem(fSpinLoopLock
);
86 void _Detach(BarberPole
* pole
)
88 AutoLocker
<BLocker
> locker(fLock
);
90 for (int32 i
= 0; i
< fMessengers
.CountItems(); i
++) {
91 BMessenger
* messenger
= fMessengers
.ItemAt(i
);
92 if (messenger
->Target(NULL
) == pole
) {
93 fMessengers
.RemoveItem(messenger
, true);
98 if (fMessengers
.IsEmpty())
99 acquire_sem(fSpinLoopLock
);
105 AutoLocker
<BLocker
> locker(fLock
);
107 for (int32 i
= 0; i
< fMessengers
.CountItems(); i
++) {
108 BMessenger
* messenger
= fMessengers
.ItemAt(i
);
109 messenger
->SendMessage(BarberPole::kRefreshMessage
);
114 acquire_sem(fSpinLoopLock
);
115 release_sem(fSpinLoopLock
);
117 snooze(kSpinInterval
);
122 static MachineRoom
* sInstance
;
123 static pthread_once_t sOnceControl
;
125 thread_id fSpinLoopThread
;
126 sem_id fSpinLoopLock
;
129 BObjectList
<BMessenger
> fMessengers
;
133 MachineRoom
* MachineRoom::sInstance
= NULL
;
134 pthread_once_t
MachineRoom::sOnceControl
= PTHREAD_ONCE_INIT
;
137 // #pragma mark - BarberPole
140 BarberPole::BarberPole(const char* name
)
142 BView(name
, B_WILL_DRAW
| B_FRAME_EVENTS
),
151 // Default colors, chosen from system color scheme
152 rgb_color defaultColors
[2];
153 rgb_color otherColor
= tint_color(ui_color(B_STATUS_BAR_COLOR
), 1.3);
154 otherColor
.alpha
= 50;
155 defaultColors
[0] = otherColor
;
156 defaultColors
[1] = B_TRANSPARENT_COLOR
;
157 SetColors(defaultColors
, 2);
161 BarberPole::~BarberPole()
169 BarberPole::MessageReceived(BMessage
* message
)
171 switch (message
->what
) {
172 case kRefreshMessage
:
177 BView::MessageReceived(message
);
184 BarberPole::Draw(BRect updateRect
)
188 // Draw color stripes
189 float position
= -fStripeWidth
* (fNumColors
+ 0.5) + fScrollOffset
;
190 // Starting position: beginning of the second color cycle
191 // The + 0.5 is so we start out without a partially visible stripe
192 // on the left side (makes it simpler to loop)
193 BRect bounds
= Bounds();
194 bounds
.InsetBy(-2, -2);
195 be_control_look
->DrawStatusBar(this, bounds
, updateRect
,
196 ui_color(B_PANEL_BACKGROUND_COLOR
), ui_color(B_STATUS_BAR_COLOR
),
198 SetDrawingMode(B_OP_ALPHA
);
199 uint32 colorIndex
= 0;
200 for (uint32 i
= 0; i
< fNumStripes
; i
++) {
201 SetHighColor(fColors
[colorIndex
]);
203 if (colorIndex
>= fNumColors
)
206 BRect stripeFrame
= fStripe
.Frame();
207 fStripe
.MapTo(stripeFrame
,
208 stripeFrame
.OffsetToCopy(position
, 0.0));
209 FillPolygon(&fStripe
);
211 position
+= fStripeWidth
;
213 SetDrawingMode(B_OP_COPY
);
215 // Draw box around it
216 BRect bounds
= Bounds();
217 be_control_look
->DrawBorder(this, bounds
, updateRect
,
218 ui_color(B_PANEL_BACKGROUND_COLOR
), B_PLAIN_BORDER
);
223 BarberPole::FrameResized(float width
, float height
)
225 // Choose stripe width so that at least 2 full stripes fit into the view,
226 // but with a minimum of 5px. Larger views get wider stripes, but they
227 // grow slower than the view and are capped to a maximum of 200px.
228 fStripeWidth
= (width
/ 4) + 5;
229 if (fStripeWidth
> 200)
232 BPoint stripePoints
[4];
233 stripePoints
[0].Set(fStripeWidth
* 0.5, 0.0); // top left
234 stripePoints
[1].Set(fStripeWidth
* 1.5, 0.0); // top right
235 stripePoints
[2].Set(fStripeWidth
, height
); // bottom right
236 stripePoints
[3].Set(0.0, height
); // bottom left
238 fStripe
= BPolygon(stripePoints
, 4);
240 fNumStripes
= (int32
)ceilf((width
) / fStripeWidth
) + 1 + fNumColors
;
241 // Number of color stripes drawn in total for the barber pole, the
242 // user-visible part is a "window" onto the complete pole. We need
243 // as many stripes as are visible, an extra one on the right side
244 // (will be partially visible, that's the + 1); and then a whole color
245 // cycle of strips extra which we scroll into until we loop.
247 // Example with 3 colors and a visible area of 2*fStripeWidth (which means
248 // that 2 will be fully visible, and a third one partially):
250 // X___________v______v___
251 // / 1 / 2 / 3 / 1 / 2 / 3 /
252 // `````````````````````````
253 // Pole is scrolled to the right into the visible region, which is marked
254 // between the two 'v'. Once the left edge of the visible area reaches
255 // point X, we can jump back to the initial region position.
260 BarberPole::MinSize()
271 MachineRoom::AttachBarberPole(this);
281 MachineRoom::DetachBarberPole(this);
288 BarberPole::SetSpinSpeed(float speed
)
299 BarberPole::SetColors(const rgb_color
* colors
, uint32 numColors
)
302 rgb_color
* colorsCopy
= new rgb_color
[numColors
];
303 for (uint32 i
= 0; i
< numColors
; i
++)
304 colorsCopy
[i
] = colors
[i
];
306 fColors
= colorsCopy
;
307 fNumColors
= numColors
;
314 fScrollOffset
+= fStripeWidth
/ (1.0f
/ fSpinSpeed
);
315 if (fScrollOffset
>= fStripeWidth
* fNumColors
) {
316 // Cycle completed, jump back to where we started
320 //Parent()->Invalidate();