xref: /haiku/src/servers/midi/DeviceWatcher.cpp (revision 9f81ca838ce7b92b5689e57d3f86765db4705a7b)
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  *		Pete Goodeve
10  */
11 
12 #include "debug.h"
13 #include "DeviceWatcher.h"
14 #include "PortDrivers.h"
15 
16 #include <errno.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <new>
20 
21 #include <Application.h>
22 #include <Bitmap.h>
23 #include <Directory.h>
24 #include <Entry.h>
25 #include <File.h>
26 #include <IconUtils.h>
27 #include <Path.h>
28 #include <PathMonitor.h>
29 #include <Resources.h>
30 #include <Roster.h>
31 
32 using std::nothrow;
33 
34 using namespace BPrivate;
35 using BPrivate::HashMap;
36 using BPrivate::HashString;
37 
38 
39 const char *kDevicesRoot = "/dev/midi";
40 
41 
42 class DeviceEndpoints {
43 public:
DeviceEndpoints(int fd,MidiPortConsumer * consumer,MidiPortProducer * producer)44 	DeviceEndpoints(int fd, MidiPortConsumer* consumer, MidiPortProducer* producer)
45 		:  fFD(fd), fConsumer(consumer), fProducer(producer)
46 	{
47 	}
48 
49 	int 				fFD;
50 	MidiPortConsumer*	fConsumer;
51 	MidiPortProducer*	fProducer;
52 };
53 
54 
55 
DeviceWatcher()56 DeviceWatcher::DeviceWatcher()
57 	: BLooper("MIDI devices watcher"),
58 	fDeviceEndpointsMap(), fVectorIconData(NULL), fVectorIconDataSize(0),
59 	fLargeIcon(NULL), fMiniIcon(NULL)
60 {
61 	// Load midi endpoint vector icon data
62 	app_info info;
63 	be_app->GetAppInfo(&info);
64 	BFile file(&info.ref, B_READ_ONLY);
65 
66 	BResources resources;
67 	if (resources.SetTo(&file) == B_OK) {
68 		size_t dataSize;
69 		// Load MIDI port endpoint vector icon
70 		const uint8* data = (const uint8*)resources.LoadResource(
71 			B_VECTOR_ICON_TYPE,	"endpoint_vector_icon", &dataSize);
72 
73 		if (data != NULL && dataSize > 0)
74 			fVectorIconData = new(std::nothrow) uint8[dataSize];
75 
76 		if (fVectorIconData) {
77 			// data is own by resources local object: copy its content for
78 			// later use
79 			memcpy(fVectorIconData, data, dataSize);
80 			fVectorIconDataSize = dataSize;
81 		}
82 	}
83 
84 	// Render 32x32 and 16x16 B_CMAP8 icons for R5 compatibility
85 	if (fVectorIconData != NULL) {
86 		fLargeIcon = new(std::nothrow) BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
87 		fMiniIcon  = new(std::nothrow) BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
88 
89 		if (BIconUtils::GetVectorIcon(fVectorIconData, fVectorIconDataSize,
90 			fLargeIcon) != B_OK) {
91 			delete fLargeIcon;
92 			fLargeIcon = NULL;
93 		}
94 		if (BIconUtils::GetVectorIcon(fVectorIconData, fVectorIconDataSize,
95 			fMiniIcon) != B_OK) {
96 			delete fMiniIcon;
97 			fMiniIcon = NULL;
98 		}
99 	}
100 
101 	Start();
102 }
103 
104 
~DeviceWatcher()105 DeviceWatcher::~DeviceWatcher()
106 {
107 	Stop();
108 
109 	delete fLargeIcon;
110 	delete fMiniIcon;
111 	delete[] fVectorIconData;
112 }
113 
114 
115 status_t
Start()116 DeviceWatcher::Start()
117 {
118 	// Do an initial scan
119 
120 	// We need to do this from a separate thread, otherwise we will deadlock.
121 	// The reason is that we instantiate a BMidiRoster object, which sends a
122 	// message to the midi_server to register itself, and blocks until it gets
123 	// a response. But since we _are_ the midi_server we will never be able to
124 	// send that response if our main thread is already blocking.
125 
126     resume_thread(spawn_thread(_InitialDevicesScanThread,
127 		"Initial devices scan", B_NORMAL_PRIORITY, this));
128 
129 	// And watch for any change
130 	return BPathMonitor::StartWatching(kDevicesRoot,
131 		B_WATCH_FILES_ONLY | B_WATCH_RECURSIVELY, this);
132 }
133 
134 
135 status_t
Stop()136 DeviceWatcher::Stop()
137 {
138 	return BPathMonitor::StopWatching(kDevicesRoot, this);
139 }
140 
141 
142 void
MessageReceived(BMessage * message)143 DeviceWatcher::MessageReceived(BMessage* message)
144 {
145 	if (message->what != B_PATH_MONITOR)
146 		return;
147 
148 	int32 opcode;
149 	if (message->FindInt32("opcode", &opcode) != B_OK)
150 		return;
151 
152 	// message->PrintToStream();
153 
154 	const char* path;
155 	if (message->FindString("path", &path) != B_OK)
156 		return;
157 
158 	switch (opcode) {
159 		case B_ENTRY_CREATED: {
160 			_AddDevice(path);
161 			break;
162 		}
163 		case B_ENTRY_REMOVED: {
164 			_RemoveDevice(path);
165 			break;
166 		}
167 	}
168 }
169 
170 
171 // #pragma mark -
172 
173 
174 /* static  */
175 int32
_InitialDevicesScanThread(void * data)176 DeviceWatcher::_InitialDevicesScanThread(void* data)
177 {
178 	((DeviceWatcher*)data)->_ScanDevices(kDevicesRoot);
179 	return 0;
180 }
181 
182 
183 void
_ScanDevices(const char * path)184 DeviceWatcher::_ScanDevices(const char* path)
185 {
186 	TRACE(("DeviceWatcher::_ScanDevices(\"%s\");\n", path));
187 
188 	BDirectory dir(path);
189 	if (dir.InitCheck() != B_OK)
190 		return;
191 
192 	BEntry entry;
193 	while (dir.GetNextEntry(&entry) == B_OK)  {
194 		BPath name;
195 		entry.GetPath(&name);
196 		if (entry.IsDirectory())
197 			_ScanDevices(name.Path());
198 		else
199            	_AddDevice(name.Path());
200 	}
201 }
202 
203 
204 void
_AddDevice(const char * path)205 DeviceWatcher::_AddDevice(const char* path)
206 {
207 	TRACE(("DeviceWatcher::_AddDevice(\"%s\");\n", path));
208 
209 	if (fDeviceEndpointsMap.ContainsKey(path)) {
210 		// Already known
211 		TRACE(("already known...!\n"));
212 		return;
213 	}
214 
215 	BEntry entry(path);
216 	if (entry.IsDirectory())
217 		// Invalid path!
218 		return;
219 
220 	if (entry.IsSymLink()) {
221 		BEntry symlink(path, true);
222 		if (symlink.IsDirectory()) {
223 			// Invalid path!
224 			return;
225 		}
226 	}
227 
228 	int fd = open(path, O_RDWR | O_EXCL);
229 	if (fd < 0) {
230 		if (errno != EACCES)
231 			return;
232 
233 		// maybe it's a input or output only port?
234 		fd = open(path, O_RDONLY | O_EXCL);
235 		if (fd < 0 && errno == EACCES)
236 			fd = open(path, O_WRONLY | O_EXCL);
237 		if (fd < 0)
238 			return;
239 	}
240 
241 	TRACE(("Doing _AddDevice(\"%s\"); fd=%d\n", path, fd));
242 
243 	MidiPortConsumer* consumer = NULL;
244 	MidiPortProducer* producer = NULL;
245 
246 	int flags = fcntl(fd, F_GETFL);
247 
248 	if ((flags & O_ACCMODE) != O_RDONLY) {
249 		consumer = new MidiPortConsumer(fd, path);
250 		_SetIcons(consumer);
251 		TRACE(("Register %s MidiPortConsumer\n", consumer->Name()));
252 		consumer->Register();
253 	}
254 
255 	if ((flags & O_ACCMODE) != O_WRONLY) {
256 		producer = new MidiPortProducer(fd, path);
257 		_SetIcons(producer);
258 		TRACE(("Register %s MidiPortProducer\n", producer->Name()));
259 		producer->Register();
260 	}
261 
262 	fDeviceEndpointsMap.Put(path, new DeviceEndpoints(fd, consumer, producer));
263 	TRACE(("Done _AddDevice(\"%s\")\n", path));
264 }
265 
266 
267 void
_RemoveDevice(const char * path)268 DeviceWatcher::_RemoveDevice(const char* path)
269 {
270 	TRACE(("DeviceWatcher::_RemoveDevice(\"%s\");\n", path));
271 
272 	DeviceEndpoints* deviceEndpoints = fDeviceEndpointsMap.Get(path);
273 	if (!deviceEndpoints) {
274 		TRACE(("_RemoveDevice(\"%s\") didn't find endpoint in map!!\n", path));
275 		return;
276 	}
277 
278 	TRACE((" _RemoveDevice(\"%s\") unregistering\n", path));
279 	if (deviceEndpoints->fConsumer)
280 		deviceEndpoints->fConsumer->Unregister();
281 	if (deviceEndpoints->fProducer)
282 		deviceEndpoints->fProducer->Unregister();
283 
284 	TRACE((" _RemoveDevice(\"%s\") releasing\n", path));
285 	if (deviceEndpoints->fConsumer)
286 		deviceEndpoints->fConsumer->Release();
287 	if (deviceEndpoints->fProducer)
288 		deviceEndpoints->fProducer->Release();
289 
290 	TRACE((" _RemoveDevice(\"%s\") removing from map\n", path));
291 	fDeviceEndpointsMap.Remove(path);
292 	TRACE(("Done _RemoveDevice(\"%s\")\n", path));
293 }
294 
295 
296 void
_SetIcons(BMidiEndpoint * endpoint)297 DeviceWatcher::_SetIcons(BMidiEndpoint* endpoint)
298 {
299 	BMessage msg;
300 
301 	if (fVectorIconData && fVectorIconDataSize > 0) {
302 		msg.AddData("icon", B_VECTOR_ICON_TYPE, fVectorIconData,
303 			fVectorIconDataSize);
304 	}
305 
306 	if (fLargeIcon) {
307 		msg.AddData("be:large_icon", B_LARGE_ICON_TYPE, fLargeIcon->Bits(),
308 			fLargeIcon->BitsLength());
309 	}
310 
311 	if (fMiniIcon) {
312 		msg.AddData("be:mini_icon", B_MINI_ICON_TYPE, fMiniIcon->Bits(),
313 			fMiniIcon->BitsLength());
314 	}
315 
316 	endpoint->SetProperties(&msg);
317 }
318