2 * Copyright 2003-2012, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
6 * Sikosis, Jérôme Duval
7 * yourpalal, Alex Wilson
11 #include "MediaWindow.h"
16 #include <Application.h>
19 #include <CardLayout.h>
23 #include <IconUtils.h>
24 #include <LayoutBuilder.h>
26 #include <MediaRoster.h>
27 #include <MediaTheme.h>
28 #include <Resources.h>
31 #include <ScrollView.h>
32 #include <SeparatorView.h>
33 #include <SpaceLayoutItem.h>
34 #include <StorageKit.h>
39 #include "MediaIcons.h"
40 #include "MidiSettingsView.h"
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Media Window"
46 const uint32 ML_SELECTED_NODE
= 'MlSN';
47 const uint32 ML_RESTART_THREAD_FINISHED
= 'MlRF';
50 class NodeListItemUpdater
: public MediaListItem::Visitor
{
52 typedef void (NodeListItem::*UpdateMethod
)(bool);
54 NodeListItemUpdater(NodeListItem
* target
, UpdateMethod action
)
62 virtual void Visit(AudioMixerListItem
*){}
63 virtual void Visit(DeviceListItem
*){}
64 virtual void Visit(MidiListItem
*){}
65 virtual void Visit(NodeListItem
* item
)
67 item
->Accept(fComparator
);
68 (item
->*(fAction
))(fComparator
.result
== 0);
73 NodeListItem::Comparator fComparator
;
78 MediaWindow::SmartNode::SmartNode(const BMessenger
& notifyHandler
)
81 fMessenger(notifyHandler
)
86 MediaWindow::SmartNode::~SmartNode()
93 MediaWindow::SmartNode::SetTo(const dormant_node_info
* info
)
99 fNode
= new media_node();
100 BMediaRoster
* roster
= BMediaRoster::Roster();
102 status_t status
= B_OK
;
103 media_node_id node_id
;
104 if (roster
->GetInstancesFor(info
->addon
, info
->flavor_id
, &node_id
) == B_OK
)
105 status
= roster
->GetNodeFor(node_id
, fNode
);
107 status
= roster
->InstantiateDormantNode(*info
, fNode
, B_FLAVOR_IS_GLOBAL
);
109 if (status
!= B_OK
) {
110 fprintf(stderr
, "SmartNode::SetTo error with node %" B_PRId32
111 ": %s\n", fNode
->node
, strerror(status
));
114 status
= roster
->StartWatching(fMessenger
, *fNode
, B_MEDIA_WILDCARD
);
115 if (status
!= B_OK
) {
116 fprintf(stderr
, "SmartNode::SetTo can't start watching for"
117 " node %" B_PRId32
"\n", fNode
->node
);
123 MediaWindow::SmartNode::SetTo(const media_node
& node
)
126 fNode
= new media_node(node
);
127 BMediaRoster
* roster
= BMediaRoster::Roster();
128 roster
->StartWatching(fMessenger
, *fNode
, B_MEDIA_WILDCARD
);
133 MediaWindow::SmartNode::IsSet()
135 return fNode
!= NULL
;
139 MediaWindow::SmartNode::operator media_node()
149 MediaWindow::SmartNode::_FreeNode()
154 BMediaRoster
* roster
= BMediaRoster::Roster();
155 if (roster
!= NULL
) {
156 status_t status
= roster
->StopWatching(fMessenger
,
157 *fNode
, B_MEDIA_WILDCARD
);
158 if (status
!= B_OK
) {
159 fprintf(stderr
, "SmartNode::_FreeNode can't unwatch"
160 " media services for node %" B_PRId32
"\n", fNode
->node
);
163 roster
->ReleaseNode(*fNode
);
164 if (status
!= B_OK
) {
165 fprintf(stderr
, "SmartNode::_FreeNode can't release"
166 " node %" B_PRId32
"\n", fNode
->node
);
177 MediaWindow::MediaWindow(BRect frame
)
179 BWindow(frame
, B_TRANSLATE_SYSTEM_NAME("Media"), B_TITLED_WINDOW
,
180 B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS
),
181 fCurrentNode(BMessenger(this)),
183 fAudioInputs(5, true),
184 fAudioOutputs(5, true),
185 fVideoInputs(5, true),
186 fVideoOutputs(5, true),
192 BMediaRoster
* roster
= BMediaRoster::Roster();
193 roster
->StartWatching(BMessenger(this, this),
194 B_MEDIA_SERVER_STARTED
);
195 roster
->StartWatching(BMessenger(this, this),
196 B_MEDIA_SERVER_QUIT
);
200 MediaWindow::~MediaWindow()
206 BRect rect
= Frame();
208 snprintf(buffer
, 512, "# MediaPrefs Settings\n rect = %i,%i,%i,%i\n",
209 int(rect
.left
), int(rect
.top
), int(rect
.right
), int(rect
.bottom
));
212 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) == B_OK
) {
213 path
.Append(SETTINGS_FILE
);
214 BFile
file(path
.Path(), B_READ_WRITE
| B_CREATE_FILE
| B_ERASE_FILE
);
215 if (file
.InitCheck() == B_OK
)
216 file
.Write(buffer
, strlen(buffer
));
219 BMediaRoster
* roster
= BMediaRoster::CurrentRoster();
220 roster
->StopWatching(BMessenger(this, this),
221 B_MEDIA_SERVER_STARTED
);
222 roster
->StartWatching(BMessenger(this, this),
223 B_MEDIA_SERVER_QUIT
);
228 MediaWindow::InitCheck()
235 MediaWindow::SelectNode(const dormant_node_info
* node
)
237 fCurrentNode
.SetTo(node
);
239 fTitleView
->SetLabel(node
->name
);
244 MediaWindow::SelectAudioSettings(const char* title
)
246 fContentLayout
->SetVisibleItem(fContentLayout
->IndexOfView(fAudioView
));
247 fTitleView
->SetLabel(title
);
252 MediaWindow::SelectVideoSettings(const char* title
)
254 fContentLayout
->SetVisibleItem(fContentLayout
->IndexOfView(fVideoView
));
255 fTitleView
->SetLabel(title
);
260 MediaWindow::SelectAudioMixer(const char* title
)
262 media_node mixerNode
;
263 BMediaRoster
* roster
= BMediaRoster::Roster();
264 roster
->GetAudioMixer(&mixerNode
);
265 fCurrentNode
.SetTo(mixerNode
);
267 fTitleView
->SetLabel(title
);
272 MediaWindow::SelectMidiSettings(const char* title
)
274 fContentLayout
->SetVisibleItem(fContentLayout
->IndexOfView(fMidiView
));
275 fTitleView
->SetLabel(title
);
280 MediaWindow::UpdateInputListItem(MediaListItem::media_type type
,
281 const dormant_node_info
* node
)
283 NodeListItem
compareTo(node
, type
);
284 NodeListItemUpdater
updater(&compareTo
, &NodeListItem::SetDefaultInput
);
285 for (int32 i
= 0; i
< fListView
->CountItems(); i
++) {
286 MediaListItem
* item
= static_cast<MediaListItem
*>(fListView
->ItemAt(i
));
287 item
->Accept(updater
);
289 fListView
->Invalidate();
294 MediaWindow::UpdateOutputListItem(MediaListItem::media_type type
,
295 const dormant_node_info
* node
)
297 NodeListItem
compareTo(node
, type
);
298 NodeListItemUpdater
updater(&compareTo
, &NodeListItem::SetDefaultOutput
);
299 for (int32 i
= 0; i
< fListView
->CountItems(); i
++) {
300 MediaListItem
* item
= static_cast<MediaListItem
*>(fListView
->ItemAt(i
));
301 item
->Accept(updater
);
303 fListView
->Invalidate();
308 MediaWindow::QuitRequested()
310 status_t exit
= B_OK
;
311 if (fRestartThread
> 0) {
312 wait_for_thread(fRestartThread
, &exit
);
314 fprintf(stderr
, "MediaWindow::QuitRequested wait_for_thread"
315 " returned with an error: %s\n", strerror(exit
));
318 // Stop watching the MediaRoster
319 fCurrentNode
.SetTo(NULL
);
320 be_app
->PostMessage(B_QUIT_REQUESTED
);
326 MediaWindow::MessageReceived(BMessage
* message
)
328 switch (message
->what
) {
329 case ML_RESTART_THREAD_FINISHED
:
334 case ML_RESTART_MEDIA_SERVER
:
336 fRestartThread
= spawn_thread(&MediaWindow::_RestartMediaServices
,
337 "restart_thread", B_NORMAL_PRIORITY
, this);
338 if (fRestartThread
< 0)
339 fprintf(stderr
, "couldn't create restart thread\n");
341 resume_thread(fRestartThread
);
345 case B_MEDIA_WEB_CHANGED
:
346 case ML_SELECTED_NODE
:
348 PRINT_OBJECT(*message
);
350 MediaListItem
* item
= static_cast<MediaListItem
*>(
351 fListView
->ItemAt(fListView
->CurrentSelection()));
355 fCurrentNode
.SetTo(NULL
);
357 item
->AlterWindow(this);
361 case B_MEDIA_SERVER_STARTED
:
362 case B_MEDIA_SERVER_QUIT
:
364 PRINT_OBJECT(*message
);
370 BWindow::MessageReceived(message
);
376 // #pragma mark - private
380 MediaWindow::_InitWindow()
382 fListView
= new BListView("media_list_view");
383 fListView
->SetSelectionMessage(new BMessage(ML_SELECTED_NODE
));
384 fListView
->SetExplicitMinSize(BSize(140, B_SIZE_UNSET
));
386 // Add ScrollView to Media Menu for pretty border
387 BScrollView
* scrollView
= new BScrollView("listscroller",
388 fListView
, 0, false, false, B_FANCY_BORDER
);
391 fTitleView
= new BSeparatorView(B_HORIZONTAL
, B_FANCY_BORDER
);
392 fTitleView
->SetLabel(B_TRANSLATE("Audio settings"));
393 fTitleView
->SetFont(be_bold_font
);
395 fContentLayout
= new BCardLayout();
396 new BView("content view", 0, fContentLayout
);
397 fContentLayout
->Owner()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR
));
398 fContentLayout
->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED
, B_SIZE_UNSET
));
400 fAudioView
= new AudioSettingsView();
401 fContentLayout
->AddView(fAudioView
);
403 fVideoView
= new VideoSettingsView();
404 fContentLayout
->AddView(fVideoView
);
406 fMidiView
= new MidiSettingsView();
407 fContentLayout
->AddView(fMidiView
);
410 BLayoutBuilder::Group
<>(this, B_HORIZONTAL
)
411 .SetInsets(B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
,
412 B_USE_DEFAULT_SPACING
, B_USE_DEFAULT_SPACING
)
413 .Add(scrollView
, 0.0f
)
414 .AddGroup(B_VERTICAL
)
415 .SetInsets(0, 0, 0, 0)
417 .Add(fContentLayout
);
420 fInitCheck
= _InitMedia(true);
421 if (fInitCheck
!= B_OK
)
422 PostMessage(B_QUIT_REQUESTED
);
429 MediaWindow::_InitMedia(bool first
)
432 BMediaRoster
* roster
= BMediaRoster::Roster(&err
);
434 if (first
&& err
!= B_OK
) {
435 BAlert
* alert
= new BAlert("start_media_server",
436 B_TRANSLATE("Could not connect to the media server.\n"
437 "Would you like to start it ?"),
439 B_TRANSLATE("Start media server"), NULL
,
440 B_WIDTH_AS_USUAL
, B_WARNING_ALERT
);
441 alert
->SetShortcut(0, B_ESCAPE
);
442 if (alert
->Go() == 0)
447 launch_media_server();
452 bool isVideoSelected
= true;
453 if (!first
&& fListView
->ItemAt(0) != NULL
454 && fListView
->ItemAt(0)->IsSelected())
455 isVideoSelected
= false;
457 while (fListView
->CountItems() > 0)
458 delete fListView
->RemoveItem((int32
)0);
464 // Add video nodes first. They might have an additional audio
465 // output or input, but still should be listed as video node.
466 _AddNodeItems(fVideoOutputs
, MediaListItem::VIDEO_TYPE
);
467 _AddNodeItems(fVideoInputs
, MediaListItem::VIDEO_TYPE
);
468 _AddNodeItems(fAudioOutputs
, MediaListItem::AUDIO_TYPE
);
469 _AddNodeItems(fAudioInputs
, MediaListItem::AUDIO_TYPE
);
471 fAudioView
->AddOutputNodes(fAudioOutputs
);
472 fAudioView
->AddInputNodes(fAudioInputs
);
473 fVideoView
->AddOutputNodes(fVideoOutputs
);
474 fVideoView
->AddInputNodes(fVideoInputs
);
476 // build our list view
477 DeviceListItem
* audio
= new DeviceListItem(B_TRANSLATE("Audio settings"),
478 MediaListItem::AUDIO_TYPE
);
479 fListView
->AddItem(audio
);
481 MidiListItem
* midi
= new MidiListItem(B_TRANSLATE("MIDI Settings"));
482 fListView
->AddItem(midi
);
484 MediaListItem
* video
= new DeviceListItem(B_TRANSLATE("Video settings"),
485 MediaListItem::VIDEO_TYPE
);
486 fListView
->AddItem(video
);
488 MediaListItem
* mixer
= new AudioMixerListItem(B_TRANSLATE("Audio mixer"));
489 fListView
->AddItem(mixer
);
491 fListView
->SortItems(&MediaListItem::Compare
);
492 _UpdateListViewMinWidth();
494 // Set default nodes for our setting views
495 media_node defaultNode
;
496 dormant_node_info nodeInfo
;
500 if (roster
->GetAudioInput(&defaultNode
) == B_OK
) {
501 roster
->GetDormantNodeFor(defaultNode
, &nodeInfo
);
502 fAudioView
->SetDefaultInput(&nodeInfo
);
503 // this causes our listview to be updated as well
506 if (roster
->GetAudioOutput(&defaultNode
, &outputID
, &outputName
) == B_OK
) {
507 roster
->GetDormantNodeFor(defaultNode
, &nodeInfo
);
508 fAudioView
->SetDefaultOutput(&nodeInfo
);
509 fAudioView
->SetDefaultChannel(outputID
);
510 // this causes our listview to be updated as well
513 if (roster
->GetVideoInput(&defaultNode
) == B_OK
) {
514 roster
->GetDormantNodeFor(defaultNode
, &nodeInfo
);
515 fVideoView
->SetDefaultInput(&nodeInfo
);
516 // this causes our listview to be updated as well
519 if (roster
->GetVideoOutput(&defaultNode
) == B_OK
) {
520 roster
->GetDormantNodeFor(defaultNode
, &nodeInfo
);
521 fVideoView
->SetDefaultOutput(&nodeInfo
);
522 // this causes our listview to be updated as well
526 fListView
->Select(fListView
->IndexOf(mixer
));
527 else if (isVideoSelected
)
528 fListView
->Select(fListView
->IndexOf(video
));
530 fListView
->Select(fListView
->IndexOf(audio
));
539 MediaWindow::_FindNodes()
541 _FindNodes(B_MEDIA_RAW_AUDIO
, B_PHYSICAL_OUTPUT
, fAudioOutputs
);
542 _FindNodes(B_MEDIA_RAW_AUDIO
, B_PHYSICAL_INPUT
, fAudioInputs
);
543 _FindNodes(B_MEDIA_ENCODED_AUDIO
, B_PHYSICAL_OUTPUT
, fAudioOutputs
);
544 _FindNodes(B_MEDIA_ENCODED_AUDIO
, B_PHYSICAL_INPUT
, fAudioInputs
);
545 _FindNodes(B_MEDIA_RAW_VIDEO
, B_PHYSICAL_OUTPUT
, fVideoOutputs
);
546 _FindNodes(B_MEDIA_RAW_VIDEO
, B_PHYSICAL_INPUT
, fVideoInputs
);
547 _FindNodes(B_MEDIA_ENCODED_VIDEO
, B_PHYSICAL_OUTPUT
, fVideoOutputs
);
548 _FindNodes(B_MEDIA_ENCODED_VIDEO
, B_PHYSICAL_INPUT
, fVideoInputs
);
553 MediaWindow::_FindNodes(media_type type
, uint64 kind
, NodeList
& into
)
555 dormant_node_info nodeInfo
[64];
556 int32 nodeInfoCount
= 64;
559 media_format
* nodeInputFormat
= NULL
;
560 media_format
* nodeOutputFormat
= NULL
;
563 // output nodes must be BBufferConsumers => they have an input format
564 // input nodes must be BBufferProducers => they have an output format
565 if ((kind
& B_PHYSICAL_OUTPUT
) != 0)
566 nodeInputFormat
= &format
;
567 else if ((kind
& B_PHYSICAL_INPUT
) != 0)
568 nodeOutputFormat
= &format
;
572 BMediaRoster
* roster
= BMediaRoster::Roster();
574 if (roster
->GetDormantNodes(nodeInfo
, &nodeInfoCount
, nodeInputFormat
,
575 nodeOutputFormat
, NULL
, kind
) != B_OK
) {
576 // TODO: better error reporting!
577 fprintf(stderr
, "error\n");
581 for (int32 i
= 0; i
< nodeInfoCount
; i
++) {
582 PRINT(("node : %s, media_addon %i, flavor_id %i\n",
583 nodeInfo
[i
].name
, (int)nodeInfo
[i
].addon
,
584 (int)nodeInfo
[i
].flavor_id
));
586 dormant_node_info
* info
= new dormant_node_info();
587 strncpy(info
->name
, nodeInfo
[i
].name
, B_MEDIA_NAME_LENGTH
);
588 info
->flavor_id
= nodeInfo
[i
].flavor_id
;
589 info
->addon
= nodeInfo
[i
].addon
;
596 MediaWindow::_AddNodeItems(NodeList
& list
, MediaListItem::media_type type
)
598 int32 count
= list
.CountItems();
599 for (int32 i
= 0; i
< count
; i
++) {
600 dormant_node_info
* info
= list
.ItemAt(i
);
601 if (_FindNodeListItem(info
) == NULL
)
602 fListView
->AddItem(new NodeListItem(info
, type
));
608 MediaWindow::_EmptyNodeLists()
610 fAudioOutputs
.MakeEmpty();
611 fAudioInputs
.MakeEmpty();
612 fVideoOutputs
.MakeEmpty();
613 fVideoInputs
.MakeEmpty();
618 MediaWindow::_FindNodeListItem(dormant_node_info
* info
)
620 NodeListItem
audioItem(info
, MediaListItem::AUDIO_TYPE
);
621 NodeListItem
videoItem(info
, MediaListItem::VIDEO_TYPE
);
623 NodeListItem::Comparator
audioComparator(&audioItem
);
624 NodeListItem::Comparator
videoComparator(&videoItem
);
626 for (int32 i
= 0; i
< fListView
->CountItems(); i
++) {
627 MediaListItem
* item
= static_cast<MediaListItem
*>(fListView
->ItemAt(i
));
628 item
->Accept(audioComparator
);
629 if (audioComparator
.result
== 0)
630 return static_cast<NodeListItem
*>(item
);
632 item
->Accept(videoComparator
);
633 if (videoComparator
.result
== 0)
634 return static_cast<NodeListItem
*>(item
);
641 MediaWindow::_UpdateListViewMinWidth()
644 for (int32 i
= 0; i
< fListView
->CountItems(); i
++) {
645 BListItem
* item
= fListView
->ItemAt(i
);
646 width
= max_c(width
, item
->Width());
648 fListView
->SetExplicitMinSize(BSize(width
, B_SIZE_UNSET
));
649 fListView
->InvalidateLayout();
654 MediaWindow::_RestartMediaServices(void* data
)
656 MediaWindow
* window
= (MediaWindow
*)data
;
658 shutdown_media_server();
659 launch_media_server();
661 return window
->PostMessage(ML_RESTART_THREAD_FINISHED
);
666 MediaWindow::_ClearParamView()
668 BLayoutItem
* item
= fContentLayout
->VisibleItem();
672 BView
* view
= item
->View();
673 if (view
!= fVideoView
&& view
!= fAudioView
&& view
!= fMidiView
) {
674 fContentLayout
->RemoveItem(item
);
684 MediaWindow::_MakeParamView()
686 if (!fCurrentNode
.IsSet())
690 BMediaRoster
* roster
= BMediaRoster::Roster();
691 if (roster
->GetParameterWebFor(fCurrentNode
, &fParamWeb
) == B_OK
) {
692 BRect
hint(fContentLayout
->Frame());
693 BView
* paramView
= BMediaTheme::ViewFor(fParamWeb
, &hint
);
695 fContentLayout
->AddView(paramView
);
696 fContentLayout
->SetVisibleItem(fContentLayout
->CountItems() - 1);
701 _MakeEmptyParamView();
706 MediaWindow::_MakeEmptyParamView()
710 BStringView
* stringView
= new BStringView("noControls",
711 B_TRANSLATE("This hardware has no controls."));
713 BSize
unlimited(B_SIZE_UNLIMITED
, B_SIZE_UNLIMITED
);
714 stringView
->SetExplicitMaxSize(unlimited
);
716 BAlignment
centered(B_ALIGN_HORIZONTAL_CENTER
,
717 B_ALIGN_VERTICAL_CENTER
);
718 stringView
->SetExplicitAlignment(centered
);
719 stringView
->SetAlignment(B_ALIGN_CENTER
);
721 fContentLayout
->AddView(stringView
);
722 fContentLayout
->SetVisibleItem(fContentLayout
->CountItems() - 1);
724 rgb_color panel
= stringView
->LowColor();
725 stringView
->SetHighColor(tint_color(panel
,
726 B_DISABLED_LABEL_TINT
));