xref: /haiku/src/servers/midi/DeviceWatcher.cpp (revision e36a1b58e6daf3efeec46621114691ef499faafc)
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 
12 #include "debug.h"
13 #include "DeviceWatcher.h"
14 #include "PortDrivers.h"
15 
16 #include <stdio.h>
17 #include <new>
18 
19 #include <Application.h>
20 #include <Bitmap.h>
21 #include <Directory.h>
22 #include <Entry.h>
23 #include <File.h>
24 #include <Path.h>
25 #include <PathMonitor.h>
26 #include <Resources.h>
27 #include <Roster.h>
28 
29 using std::nothrow;
30 
31 using namespace BPrivate;
32 using BPrivate::HashMap;
33 using BPrivate::HashString;
34 
35 const char *kDevicesRoot = "/dev/midi";
36 // const char *kDevicesRoot = "/Data/tmp";
37 
38 
39 class DeviceEndpoints {
40 public:
41 	DeviceEndpoints(int fd, MidiPortConsumer* consumer, MidiPortProducer* producer)
42 		:  fFD(fd), fConsumer(consumer), fProducer(producer)
43 	{
44 	}
45 
46 	int 				fFD;
47 	MidiPortConsumer*	fConsumer;
48 	MidiPortProducer*	fProducer;
49 };
50 
51 
52 
53 DeviceWatcher::DeviceWatcher()
54 	: BLooper("MIDI devices watcher"),
55 	fDeviceEndpointsMap()
56 {
57 	// TODO: add support for vector icons
58 
59 	fLargeIcon = new BBitmap(BRect(0, 0, 31, 31), B_CMAP8);
60 	fMiniIcon  = new BBitmap(BRect(0, 0, 15, 15), B_CMAP8);
61 
62 	app_info info;
63 	be_app->GetAppInfo(&info);
64 	BFile file(&info.ref, B_READ_ONLY);
65 
66 	BResources res;
67 	if (res.SetTo(&file) == B_OK) {
68 		size_t size;
69 		const void* bits;
70 
71 		bits = res.LoadResource(B_LARGE_ICON_TYPE, 10, &size);
72 		fLargeIcon->SetBits(bits, size, 0, B_CMAP8);
73 
74 		bits = res.LoadResource(B_MINI_ICON_TYPE, 11, &size);
75 		fMiniIcon->SetBits(bits, size, 0, B_CMAP8);
76 	}
77 
78 	Start();
79 }
80 
81 
82 DeviceWatcher::~DeviceWatcher()
83 {
84 	Stop();
85 
86 	delete fLargeIcon;
87 	delete fMiniIcon;
88 }
89 
90 
91 status_t
92 DeviceWatcher::Start()
93 {
94 	// Do an initial scan
95 
96 	// We need to do this from a separate thread, otherwise we will deadlock.
97 	// The reason is that we instantiate a BMidiRoster object, which sends a
98 	// message to the midi_server to register itself, and blocks until it gets
99 	// a response. But since we _are_ the midi_server we will never be able to
100 	// send that response if our main thread is already blocking.
101 
102     resume_thread(spawn_thread(_InitialDevicesScanThread,
103 		"Initial devices scan", B_NORMAL_PRIORITY, this));
104 
105 	// And watch for any change
106 	return BPathMonitor::StartWatching(kDevicesRoot, B_ENTRY_CREATED
107 		| B_ENTRY_REMOVED | B_ENTRY_MOVED | B_WATCH_FILES_ONLY
108 		| B_WATCH_RECURSIVELY, this);
109 }
110 
111 
112 status_t
113 DeviceWatcher::Stop()
114 {
115 	return BPathMonitor::StopWatching(kDevicesRoot, this);
116 }
117 
118 
119 void
120 DeviceWatcher::MessageReceived(BMessage* message)
121 {
122 	if (message->what != B_PATH_MONITOR)
123 		return;
124 
125 	int32 opcode;
126 	if (message->FindInt32("opcode", &opcode) != B_OK)
127 		return;
128 
129 	// message->PrintToStream();
130 
131 	const char* path;
132 	if (message->FindString("path", &path) != B_OK)
133 		return;
134 
135 	switch (opcode) {
136 		case B_ENTRY_CREATED: {
137 			_AddDevice(path);
138 			break;
139 		}
140 		case B_ENTRY_REMOVED: {
141 			_RemoveDevice(path);
142 			break;
143 		}
144 	}
145 }
146 
147 
148 // #pragma mark -
149 
150 
151 /* static  */
152 int32
153 DeviceWatcher::_InitialDevicesScanThread(void* data)
154 {
155 	((DeviceWatcher*)data)->_ScanDevices(kDevicesRoot);
156 	return 0;
157 }
158 
159 
160 void
161 DeviceWatcher::_ScanDevices(const char* path)
162 {
163 	TRACE(("DeviceWatcher::_ScanDevices(\"%s\");\n", path));
164 
165 	BDirectory dir(path);
166 	if (dir.InitCheck() != B_OK)
167 		return;
168 
169 	BEntry entry;
170 	while (dir.GetNextEntry(&entry) == B_OK)  {
171 		BPath name;
172 		entry.GetPath(&name);
173 		if (entry.IsDirectory())
174 			_ScanDevices(name.Path());
175 		else
176            	_AddDevice(name.Path());
177 	}
178 }
179 
180 
181 void
182 DeviceWatcher::_AddDevice(const char* path)
183 {
184 	TRACE(("DeviceWatcher::_AddDevice(\"%s\");\n", path));
185 
186 	if (fDeviceEndpointsMap.ContainsKey(path)) {
187 		// Already known
188 		return;
189 	}
190 
191 	BEntry entry(path);
192 	if (entry.IsDirectory())
193 		// Invalid path !
194 		return;
195 
196 	if (entry.IsSymLink()) {
197 		BEntry symlink(path, true);
198 		if (symlink.IsDirectory()) {
199 			// Invalid path!
200 			return;
201 		}
202 	}
203 
204 	int fd = open(path, O_RDWR | O_EXCL);
205 	if (fd < 0)
206 		return;
207 
208 	MidiPortConsumer* consumer = new MidiPortConsumer(fd, path);
209 	_SetIcons(consumer);
210 	TRACE(("Register %s MidiPortConsumer\n", consumer->Name()));
211 	consumer->Register();
212 
213 	MidiPortProducer* producer = new MidiPortProducer(fd, path);
214 	_SetIcons(producer);
215 	TRACE(("Register %s MidiPortProducer\n", producer->Name()));
216 	producer->Register();
217 
218 	DeviceEndpoints* deviceEndpoints = new DeviceEndpoints(fd, consumer, producer);
219 	fDeviceEndpointsMap.Put(path, deviceEndpoints);
220 }
221 
222 
223 void
224 DeviceWatcher::_RemoveDevice(const char* path)
225 {
226 	TRACE(("DeviceWatcher::_RemoveDevice(\"%s\");\n", path));
227 
228 	DeviceEndpoints* deviceEndpoints = fDeviceEndpointsMap.Get(path);
229 	if (!deviceEndpoints)
230 		return;
231 
232 	close(deviceEndpoints->fFD);
233 
234 	deviceEndpoints->fConsumer->Unregister();
235 	deviceEndpoints->fProducer->Unregister();
236 
237 	deviceEndpoints->fConsumer->Release();
238 	deviceEndpoints->fProducer->Release();
239 
240 	fDeviceEndpointsMap.Remove(path);
241 }
242 
243 
244 void
245 DeviceWatcher::_SetIcons(BMidiEndpoint* endpoint)
246 {
247 	BMessage msg;
248 
249 	// TODO: handle Haiku vector icon type
250 
251 	msg.AddData("be:large_icon", B_LARGE_ICON_TYPE, fLargeIcon->Bits(),
252 		fLargeIcon->BitsLength());
253 
254 	msg.AddData("be:mini_icon", B_MINI_ICON_TYPE, fMiniIcon->Bits(),
255 		fMiniIcon->BitsLength());
256 
257 	endpoint->SetProperties(&msg);
258 }
259