xref: /haiku/src/servers/midi/DeviceWatcher.cpp (revision 03187b607b2b5eec7ee059f1ead09bdba14991fb)
1 /*
2  * Copyright 2004-2009, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Matthijs Hollemans
7  *		Jerome Leveque
8  *		Philippe Houdoin
9  */
10 
11 #include "DeviceWatcher.h"
12 #include "PortDrivers.h"
13 
14 #include <Application.h>
15 #include <Bitmap.h>
16 #include <Directory.h>
17 #include <Entry.h>
18 #include <File.h>
19 #include <Path.h>
20 #include <Resources.h>
21 #include <Roster.h>
22 #include <PathMonitor.h>
23 
24 #include <new>
25 using std::nothrow;
26 
27 using namespace BPrivate;
28 using BPrivate::HashMap;
29 using BPrivate::HashString;
30 
31 
32 const char *kDevicesRoot = "/dev/midi";
33 
34 
35 class DeviceEndpoints {
36 public:
37 	DeviceEndpoints(int fd, MidiPortConsumer* consumer, MidiPortProducer* producer)
38 		:  fFD(fd), fConsumer(consumer), fProducer(producer)
39 	{
40 	}
41 
42 	int 				fFD;
43 	MidiPortConsumer*		fConsumer;
44 	MidiPortProducer*		fProducer;
45 };
46 
47 
48 
49 DeviceWatcher::DeviceWatcher()
50 	: BLooper("MIDI devices watcher"),
51 	fDeviceEndpointsMap()
52 {
53 	// TODO: add support for vector icons
54 
55 	fLargeIcon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
56 	fMiniIcon  = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
57 
58 	app_info info;
59 	be_app->GetAppInfo(&info);
60 	BFile file(&info.ref, B_READ_ONLY);
61 
62 	BResources res;
63 	if (res.SetTo(&file) == B_OK) {
64 		size_t size;
65 		const void* bits;
66 
67 		bits = res.LoadResource(B_LARGE_ICON_TYPE, 10, &size);
68 		fLargeIcon->SetBits(bits, size, 0, B_CMAP8);
69 
70 		bits = res.LoadResource(B_MINI_ICON_TYPE, 11, &size);
71 		fMiniIcon->SetBits(bits, size, 0, B_CMAP8);
72 	}
73 
74 	Start();
75 }
76 
77 
78 DeviceWatcher::~DeviceWatcher()
79 {
80 	Stop();
81 
82 	delete fLargeIcon;
83 	delete fMiniIcon;
84 }
85 
86 
87 status_t
88 DeviceWatcher::Start()
89 {
90 	// Do an initial scan
91     _ScanDevices(kDevicesRoot);
92 
93 	// Okay, now just watch for any change
94 	return BPathMonitor::StartWatching(kDevicesRoot, B_ENTRY_CREATED
95 		| B_ENTRY_REMOVED | B_ENTRY_MOVED | B_WATCH_FILES_ONLY
96 		| B_WATCH_RECURSIVELY, this);
97 }
98 
99 
100 status_t
101 DeviceWatcher::Stop()
102 {
103 	return BPathMonitor::StopWatching(kDevicesRoot, this);
104 }
105 
106 
107 void
108 DeviceWatcher::MessageReceived(BMessage* message)
109 {
110 	if (message->what != B_PATH_MONITOR)
111 		return;
112 
113 	int32 opcode;
114 	if (message->FindInt32("opcode", &opcode) != B_OK)
115 		return;
116 
117 	// message->PrintToStream();
118 
119 	const char* path;
120 	if (message->FindString("path", &path) != B_OK)
121 		return;
122 
123 	switch (opcode) {
124 		case B_ENTRY_CREATED: {
125 			_AddDevice(path);
126 			break;
127 		}
128 		case B_ENTRY_REMOVED: {
129 			_RemoveDevice(path);
130 			break;
131 		}
132 	}
133 }
134 
135 
136 // #pragma mark -
137 
138 
139 void
140 DeviceWatcher::_ScanDevices(const char* path)
141 {
142 	BDirectory dir(path);
143 	if (dir.InitCheck() != B_OK)
144 		return;
145 
146 	BEntry entry;
147 	while (dir.GetNextEntry(&entry) == B_OK)  {
148 		BPath name;
149 		entry.GetPath(&name);
150 		if (entry.IsDirectory())
151 			_ScanDevices(name.Path());
152 		else
153            	_AddDevice(name.Path());
154 	}
155 }
156 
157 
158 void
159 DeviceWatcher::_AddDevice(const char* path)
160 {
161 	if ( fDeviceEndpointsMap.ContainsKey(path) )
162 		// Already known
163 		return;
164 
165 	int fd = open(path, O_RDWR | O_EXCL);
166 	if (fd < 0)
167 		return;
168 
169 	// printf("DeviceWatcher::_AddDevice(\"%s\");\n", path);
170 
171 
172 	MidiPortConsumer* consumer = new MidiPortConsumer(fd, path);
173 	_SetIcons(consumer);
174 	// printf("Register %s MidiPortConsumer\n", consumer->Name());
175 	consumer->Register();
176 
177 	MidiPortProducer* producer = new MidiPortProducer(fd, path);
178 	_SetIcons(producer);
179 	// printf("Register %s MidiPortProducer\n", producer->Name());
180 	producer->Register();
181 
182 	DeviceEndpoints * deviceEndpoints = new DeviceEndpoints(fd, consumer, producer);
183 	fDeviceEndpointsMap.Put(path, deviceEndpoints);
184 }
185 
186 
187 void
188 DeviceWatcher::_RemoveDevice(const char* path)
189 {
190 	// printf("DeviceWatcher::_RemoveDevice(\"%s\");\n", path);
191 
192 	DeviceEndpoints * deviceEndpoints = fDeviceEndpointsMap.Get(path);
193 	if (!deviceEndpoints)
194 		return;
195 
196 	close(deviceEndpoints->fFD);
197 
198 	deviceEndpoints->fConsumer->Unregister();
199 	deviceEndpoints->fProducer->Unregister();
200 
201 	delete deviceEndpoints->fConsumer;
202 	delete deviceEndpoints->fProducer;
203 
204 	fDeviceEndpointsMap.Remove(path);
205 }
206 
207 
208 void
209 DeviceWatcher::_SetIcons(BMidiEndpoint* endpoint)
210 {
211 	BMessage msg;
212 
213 	// TODO: handle Haiku vector icon type
214 
215 	msg.AddData("be:large_icon", B_LARGE_ICON_TYPE, fLargeIcon->Bits(),
216 		fLargeIcon->BitsLength());
217 
218 	msg.AddData("be:mini_icon", B_MINI_ICON_TYPE, fMiniIcon->Bits(),
219 		fMiniIcon->BitsLength());
220 
221 	endpoint->SetProperties(&msg);
222 }
223