RemoteDrawingEngine: Reduce RP_READ_BITMAP result timeout.
[haiku.git] / src / bin / desklink / MediaReplicant.cpp
blob6f06b93f9030cac94a04f4f1cf580aa8118181e3
1 /*
2 * Copyright 2003-2013, Haiku. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Jérôme Duval
7 * François Revol
8 * Marcus Overhagen
9 * Jonas Sundström
10 * Axel Dörfler, axeld@pinc-software.de.
11 * Stephan Aßmus <superstippi@gmx.de>
12 * Puck Meerburg, puck@puckipedia.nl
16 //! Volume control, and media shortcuts in Deskbar
19 #include <new>
20 #include <stdio.h>
22 #include <Alert.h>
23 #include <Bitmap.h>
24 #include <Catalog.h>
25 #include <Entry.h>
26 #include <File.h>
27 #include <FindDirectory.h>
28 #include <IconUtils.h>
29 #include <MenuItem.h>
30 #include <Path.h>
31 #include <PopUpMenu.h>
32 #include <Roster.h>
33 #include <String.h>
34 #include <StringView.h>
35 #include <TextView.h>
36 #include <ToolTip.h>
37 #include <ToolTipManager.h>
39 #include "desklink.h"
40 #include "iconfile.h"
41 #include "MixerControl.h"
42 #include "VolumeWindow.h"
45 #undef B_TRANSLATION_CONTEXT
46 #define B_TRANSLATION_CONTEXT "MediaReplicant"
49 static const uint32 kMsgOpenMediaSettings = 'mese';
50 static const uint32 kMsgOpenSoundSettings = 'sose';
51 static const uint32 kMsgOpenMediaPlayer = 'omep';
52 static const uint32 kMsgToggleBeep = 'tdbp';
53 static const uint32 kMsgVolumeWhich = 'svwh';
55 static const char* kReplicantName = "MediaReplicant";
56 // R5 name needed, Media prefs manel removes by name
58 static const char* kSettingsFile = "x-vnd.Haiku-desklink";
61 class VolumeToolTip : public BTextToolTip {
62 public:
63 VolumeToolTip(int32 which = VOLUME_USE_MIXER)
65 BTextToolTip(""),
66 fWhich(which)
70 virtual ~VolumeToolTip()
74 virtual void AttachedToWindow()
76 Update();
79 void SetWhich(int32 which)
81 fWhich = which;
84 void Update()
86 if (!Lock())
87 return;
89 BTextView* view = (BTextView*)View();
91 if (fMuteMessage.Length() != 0)
92 view->SetText(fMuteMessage.String());
93 else {
94 MixerControl control;
95 control.Connect(fWhich);
97 BString text;
98 text.SetToFormat(B_TRANSLATE("%g dB"), control.Volume());
99 view->SetText(text.String());
101 Unlock();
104 void SetMuteMessage(const char* message)
106 fMuteMessage = message == NULL ? "" : message;
109 private:
110 int32 fWhich;
111 BString fMuteMessage;
115 class MediaReplicant : public BView {
116 public:
117 MediaReplicant(BRect frame, const char* name,
118 uint32 resizeMask = B_FOLLOW_ALL,
119 uint32 flags = B_WILL_DRAW | B_NAVIGABLE
120 | B_PULSE_NEEDED);
121 MediaReplicant(BMessage* archive);
123 virtual ~MediaReplicant();
125 // archiving overrides
126 static MediaReplicant* Instantiate(BMessage* data);
127 virtual status_t Archive(BMessage* data, bool deep = true) const;
129 // BView overrides
130 virtual void AttachedToWindow();
131 virtual void MouseDown(BPoint point);
132 virtual void Draw(BRect updateRect);
133 virtual void MessageReceived(BMessage* message);
135 private:
136 status_t _LaunchByPath(const char* path);
137 status_t _LaunchBySignature(const char* signature);
138 void _Launch(const char* prettyName,
139 const char* signature, directory_which base,
140 const char* fileName);
141 void _LoadSettings();
142 void _SaveSettings();
143 void _Init();
145 void _DisconnectMixer();
146 status_t _ConnectMixer();
148 MixerControl* fMixerControl;
150 BBitmap* fIcon;
151 BBitmap* fMutedIcon;
152 VolumeWindow* fVolumeSlider;
153 bool fDontBeep;
154 // don't beep on volume change
155 int32 fVolumeWhich;
156 // which volume parameter to act on (Mixer/Phys.Output)
157 bool fMuted;
161 MediaReplicant::MediaReplicant(BRect frame, const char* name,
162 uint32 resizeMask, uint32 flags)
164 BView(frame, name, resizeMask, flags),
165 fVolumeSlider(NULL),
166 fMuted(false)
168 _Init();
172 MediaReplicant::MediaReplicant(BMessage* message)
174 BView(message),
175 fVolumeSlider(NULL),
176 fMuted(false)
178 _Init();
182 MediaReplicant::~MediaReplicant()
184 delete fIcon;
185 _SaveSettings();
186 _DisconnectMixer();
190 MediaReplicant*
191 MediaReplicant::Instantiate(BMessage* data)
193 if (!validate_instantiation(data, kReplicantName))
194 return NULL;
196 return new(std::nothrow) MediaReplicant(data);
200 status_t
201 MediaReplicant::Archive(BMessage* data, bool deep) const
203 status_t status = BView::Archive(data, deep);
204 if (status < B_OK)
205 return status;
207 return data->AddString("add_on", kAppSignature);
211 void
212 MediaReplicant::AttachedToWindow()
214 AdoptParentColors();
216 _ConnectMixer();
218 BView::AttachedToWindow();
222 void
223 MediaReplicant::Draw(BRect rect)
225 SetDrawingMode(B_OP_OVER);
226 DrawBitmap(fMuted ? fMutedIcon : fIcon);
230 void
231 MediaReplicant::MouseDown(BPoint point)
233 int32 buttons = B_PRIMARY_MOUSE_BUTTON;
234 if (Looper() != NULL && Looper()->CurrentMessage() != NULL)
235 Looper()->CurrentMessage()->FindInt32("buttons", &buttons);
237 BPoint where = ConvertToScreen(point);
239 if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
240 BPopUpMenu* menu = new BPopUpMenu("", false, false);
241 menu->SetFont(be_plain_font);
243 menu->AddItem(new BMenuItem(
244 B_TRANSLATE("Media preferences" B_UTF8_ELLIPSIS),
245 new BMessage(kMsgOpenMediaSettings)));
246 menu->AddItem(new BMenuItem(
247 B_TRANSLATE("Sound preferences" B_UTF8_ELLIPSIS),
248 new BMessage(kMsgOpenSoundSettings)));
250 menu->AddSeparatorItem();
252 menu->AddItem(new BMenuItem(B_TRANSLATE("Open MediaPlayer"),
253 new BMessage(kMsgOpenMediaPlayer)));
255 menu->AddSeparatorItem();
257 BMenu* subMenu = new BMenu(B_TRANSLATE("Options"));
258 menu->AddItem(subMenu);
260 BMenuItem* item = new BMenuItem(B_TRANSLATE("Control physical output"),
261 new BMessage(kMsgVolumeWhich));
262 item->SetMarked(fVolumeWhich == VOLUME_USE_PHYS_OUTPUT);
263 subMenu->AddItem(item);
265 item = new BMenuItem(B_TRANSLATE("Beep"),
266 new BMessage(kMsgToggleBeep));
267 item->SetMarked(!fDontBeep);
268 subMenu->AddItem(item);
270 menu->SetTargetForItems(this);
271 subMenu->SetTargetForItems(this);
273 menu->Go(where, true, true, BRect(where - BPoint(4, 4),
274 where + BPoint(4, 4)));
276 } else if ((buttons & B_TERTIARY_MOUSE_BUTTON) != 0) {
277 if (fMixerControl != NULL) {
278 fMixerControl->SetMute(!fMuted);
279 fMuted = fMixerControl->Mute();
280 VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip());
281 if (tip != NULL) {
282 tip->SetMuteMessage(fMuted ? B_TRANSLATE("Muted"): NULL);
283 tip->Update();
284 ShowToolTip(tip);
286 Invalidate();
289 } else {
290 // Show VolumeWindow
291 fVolumeSlider = new VolumeWindow(BRect(where.x, where.y,
292 where.x + 207, where.y + 19), fDontBeep, fVolumeWhich);
293 fVolumeSlider->Show();
298 void
299 MediaReplicant::MessageReceived(BMessage* message)
301 switch (message->what) {
302 case kMsgOpenMediaPlayer:
303 _Launch("MediaPlayer", "application/x-vnd.Haiku-MediaPlayer",
304 B_SYSTEM_APPS_DIRECTORY, "MediaPlayer");
305 break;
307 case kMsgOpenMediaSettings:
308 _Launch("Media Preferences", "application/x-vnd.Haiku-Media",
309 B_SYSTEM_PREFERENCES_DIRECTORY, "Media");
310 break;
312 case kMsgOpenSoundSettings:
313 _Launch("Sounds Preferences", "application/x-vnd.Haiku-Sounds",
314 B_SYSTEM_PREFERENCES_DIRECTORY, "Sounds");
315 break;
317 case kMsgToggleBeep:
319 BMenuItem* item;
320 if (message->FindPointer("source", (void**)&item) != B_OK)
321 return;
323 item->SetMarked(!item->IsMarked());
324 fDontBeep = !item->IsMarked();
325 break;
328 case kMsgVolumeWhich:
330 BMenuItem* item;
331 if (message->FindPointer("source", (void**)&item) != B_OK)
332 return;
334 item->SetMarked(!item->IsMarked());
335 fVolumeWhich = item->IsMarked()
336 ? VOLUME_USE_PHYS_OUTPUT : VOLUME_USE_MIXER;
338 if (_ConnectMixer() != B_OK
339 && fVolumeWhich == VOLUME_USE_PHYS_OUTPUT) {
340 // unable to switch to physical output
341 item->SetMarked(false);
342 fVolumeWhich = VOLUME_USE_MIXER;
343 _ConnectMixer();
346 if (VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip())) {
347 tip->SetWhich(fVolumeWhich);
348 tip->Update();
350 break;
353 case B_MOUSE_WHEEL_CHANGED:
355 float deltaY;
356 if (message->FindFloat("be:wheel_delta_y", &deltaY) == B_OK
357 && deltaY != 0.0 && fMixerControl != NULL) {
358 fMixerControl->ChangeVolumeBy(deltaY < 0 ? 6 : -6);
360 VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip());
361 if (tip != NULL) {
362 tip->Update();
363 ShowToolTip(tip);
366 break;
369 case B_MEDIA_NEW_PARAMETER_VALUE:
371 if (fMixerControl != NULL && !fMixerControl->Connected())
372 return;
374 bool setMuted = fMixerControl->Mute();
375 if (setMuted != fMuted) {
376 fMuted = setMuted;
377 VolumeToolTip* tip = dynamic_cast<VolumeToolTip*>(ToolTip());
378 if (tip != NULL) {
379 tip->SetMuteMessage(fMuted ? B_TRANSLATE("Muted") : NULL);
380 tip->Update();
382 Invalidate();
384 break;
387 case B_MEDIA_SERVER_STARTED:
388 _ConnectMixer();
389 break;
391 case B_MEDIA_NODE_CREATED:
393 // It's not enough to wait for B_MEDIA_SERVER_STARTED message, as
394 // the mixer will still be getting loaded by the media server
395 media_node mixerNode;
396 media_node_id mixerNodeID;
397 BMediaRoster* roster = BMediaRoster::CurrentRoster();
398 if (roster != NULL
399 && message->FindInt32("media_node_id",&mixerNodeID) == B_OK
400 && roster->GetNodeFor(mixerNodeID, &mixerNode) == B_OK) {
401 if (mixerNode.kind == B_SYSTEM_MIXER) {
402 _ConnectMixer();
403 roster->ReleaseNode(mixerNode);
406 break;
409 default:
410 BView::MessageReceived(message);
411 break;
416 status_t
417 MediaReplicant::_LaunchByPath(const char* path)
419 entry_ref ref;
420 status_t status = get_ref_for_path(path, &ref);
421 if (status != B_OK)
422 return status;
424 status = be_roster->Launch(&ref);
425 if (status != B_ALREADY_RUNNING)
426 return status;
428 // The application runs already, bring it to front
430 app_info appInfo;
431 status = be_roster->GetAppInfo(&ref, &appInfo);
432 if (status != B_OK)
433 return status;
435 return be_roster->ActivateApp(appInfo.team);
439 status_t
440 MediaReplicant::_LaunchBySignature(const char* signature)
442 status_t status = be_roster->Launch(signature);
443 if (status != B_ALREADY_RUNNING)
444 return status;
446 // The application runs already, bring it to front
448 app_info appInfo;
449 status = be_roster->GetAppInfo(signature, &appInfo);
450 if (status != B_OK)
451 return status;
453 return be_roster->ActivateApp(appInfo.team);
457 void
458 MediaReplicant::_Launch(const char* prettyName, const char* signature,
459 directory_which base, const char* fileName)
461 BPath path;
462 status_t status = find_directory(base, &path);
463 if (status == B_OK)
464 path.Append(fileName);
466 // launch the application
467 if (_LaunchBySignature(signature) != B_OK
468 && _LaunchByPath(path.Path()) != B_OK) {
469 BString message = B_TRANSLATE("Couldn't launch ");
470 message << prettyName;
472 BAlert* alert = new BAlert(B_TRANSLATE("desklink"), message.String(),
473 B_TRANSLATE("OK"));
474 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
475 alert->Go();
480 void
481 MediaReplicant::_LoadSettings()
483 fDontBeep = false;
484 fVolumeWhich = VOLUME_USE_MIXER;
486 BPath path;
487 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
488 return;
490 path.Append(kSettingsFile);
492 BFile settings(path.Path(), B_READ_ONLY);
493 if (settings.InitCheck() < B_OK)
494 return;
496 BMessage msg;
497 if (msg.Unflatten(&settings) < B_OK)
498 return;
500 msg.FindInt32("volwhich", &fVolumeWhich);
501 msg.FindBool("dontbeep", &fDontBeep);
505 void
506 MediaReplicant::_SaveSettings()
508 BPath path;
509 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path, false) < B_OK)
510 return;
512 path.Append(kSettingsFile);
514 BFile settings(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE);
515 if (settings.InitCheck() < B_OK)
516 return;
518 BMessage msg('CNFG');
519 msg.AddInt32("volwhich", fVolumeWhich);
520 msg.AddBool("dontbeep", fDontBeep);
522 ssize_t size = 0;
523 msg.Flatten(&settings, &size);
527 void
528 MediaReplicant::_Init()
530 fIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1),
531 B_RGBA32);
532 BIconUtils::GetVectorIcon(kSpeakerIcon, sizeof(kSpeakerIcon), fIcon);
534 fMutedIcon = new BBitmap(BRect(0, 0, kSpeakerWidth - 1, kSpeakerHeight - 1),
535 B_RGBA32);
536 BIconUtils::GetVectorIcon(kMutedSpeakerIcon, sizeof(kMutedSpeakerIcon),
537 fMutedIcon);
539 _LoadSettings();
541 SetToolTip(new VolumeToolTip(fVolumeWhich));
545 void
546 MediaReplicant::_DisconnectMixer()
548 BMediaRoster* roster = BMediaRoster::CurrentRoster();
549 if (roster == NULL)
550 return;
552 roster->StopWatching(this, B_MEDIA_SERVER_STARTED | B_MEDIA_NODE_CREATED);
554 if (fMixerControl->MuteNode() != media_node::null) {
555 roster->StopWatching(this, fMixerControl->MuteNode(),
556 B_MEDIA_NEW_PARAMETER_VALUE);
559 delete fMixerControl;
560 fMixerControl = NULL;
564 status_t
565 MediaReplicant::_ConnectMixer()
567 _DisconnectMixer();
569 BMediaRoster* roster = BMediaRoster::Roster();
570 if (roster == NULL)
571 return B_ERROR;
573 roster->StartWatching(this, B_MEDIA_SERVER_STARTED | B_MEDIA_NODE_CREATED);
575 fMixerControl = new MixerControl(fVolumeWhich);
577 const char* errorString = NULL;
578 float volume = 0.0;
579 fMixerControl->Connect(fVolumeWhich, &volume, &errorString);
581 if (errorString != NULL) {
582 SetToolTip(errorString);
583 return B_ERROR;
586 if (fMixerControl->MuteNode() != media_node::null) {
587 roster->StartWatching(this, fMixerControl->MuteNode(),
588 B_MEDIA_NEW_PARAMETER_VALUE);
589 fMuted = fMixerControl->Mute();
592 return B_OK;
596 // #pragma mark -
599 extern "C" BView*
600 instantiate_deskbar_item(void)
602 return new MediaReplicant(BRect(0, 0, 16, 16), kReplicantName);