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