The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

613 lines
16KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-10 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. /*
  19. This file contains posix routines that are common to both the Linux and Mac builds.
  20. It gets included directly in the cpp files for these platforms.
  21. */
  22. //==============================================================================
  23. CriticalSection::CriticalSection() throw()
  24. {
  25. pthread_mutexattr_t atts;
  26. pthread_mutexattr_init (&atts);
  27. pthread_mutexattr_settype (&atts, PTHREAD_MUTEX_RECURSIVE);
  28. pthread_mutex_init (&internal, &atts);
  29. }
  30. CriticalSection::~CriticalSection() throw()
  31. {
  32. pthread_mutex_destroy (&internal);
  33. }
  34. void CriticalSection::enter() const throw()
  35. {
  36. pthread_mutex_lock (&internal);
  37. }
  38. bool CriticalSection::tryEnter() const throw()
  39. {
  40. return pthread_mutex_trylock (&internal) == 0;
  41. }
  42. void CriticalSection::exit() const throw()
  43. {
  44. pthread_mutex_unlock (&internal);
  45. }
  46. //==============================================================================
  47. class WaitableEventImpl
  48. {
  49. public:
  50. WaitableEventImpl (const bool manualReset_)
  51. : triggered (false),
  52. manualReset (manualReset_)
  53. {
  54. pthread_cond_init (&condition, 0);
  55. pthread_mutex_init (&mutex, 0);
  56. }
  57. ~WaitableEventImpl()
  58. {
  59. pthread_cond_destroy (&condition);
  60. pthread_mutex_destroy (&mutex);
  61. }
  62. bool wait (const int timeOutMillisecs) throw()
  63. {
  64. pthread_mutex_lock (&mutex);
  65. if (! triggered)
  66. {
  67. if (timeOutMillisecs < 0)
  68. {
  69. do
  70. {
  71. pthread_cond_wait (&condition, &mutex);
  72. }
  73. while (! triggered);
  74. }
  75. else
  76. {
  77. struct timeval now;
  78. gettimeofday (&now, 0);
  79. struct timespec time;
  80. time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000);
  81. time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000;
  82. if (time.tv_nsec >= 1000000000)
  83. {
  84. time.tv_nsec -= 1000000000;
  85. time.tv_sec++;
  86. }
  87. do
  88. {
  89. if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT)
  90. {
  91. pthread_mutex_unlock (&mutex);
  92. return false;
  93. }
  94. }
  95. while (! triggered);
  96. }
  97. }
  98. if (! manualReset)
  99. triggered = false;
  100. pthread_mutex_unlock (&mutex);
  101. return true;
  102. }
  103. void signal() throw()
  104. {
  105. pthread_mutex_lock (&mutex);
  106. triggered = true;
  107. pthread_cond_broadcast (&condition);
  108. pthread_mutex_unlock (&mutex);
  109. }
  110. void reset() throw()
  111. {
  112. pthread_mutex_lock (&mutex);
  113. triggered = false;
  114. pthread_mutex_unlock (&mutex);
  115. }
  116. private:
  117. pthread_cond_t condition;
  118. pthread_mutex_t mutex;
  119. bool triggered;
  120. const bool manualReset;
  121. WaitableEventImpl (const WaitableEventImpl&);
  122. WaitableEventImpl& operator= (const WaitableEventImpl&);
  123. };
  124. WaitableEvent::WaitableEvent (const bool manualReset) throw()
  125. : internal (new WaitableEventImpl (manualReset))
  126. {
  127. }
  128. WaitableEvent::~WaitableEvent() throw()
  129. {
  130. delete static_cast <WaitableEventImpl*> (internal);
  131. }
  132. bool WaitableEvent::wait (const int timeOutMillisecs) const throw()
  133. {
  134. return static_cast <WaitableEventImpl*> (internal)->wait (timeOutMillisecs);
  135. }
  136. void WaitableEvent::signal() const throw()
  137. {
  138. static_cast <WaitableEventImpl*> (internal)->signal();
  139. }
  140. void WaitableEvent::reset() const throw()
  141. {
  142. static_cast <WaitableEventImpl*> (internal)->reset();
  143. }
  144. //==============================================================================
  145. void JUCE_CALLTYPE Thread::sleep (int millisecs)
  146. {
  147. struct timespec time;
  148. time.tv_sec = millisecs / 1000;
  149. time.tv_nsec = (millisecs % 1000) * 1000000;
  150. nanosleep (&time, 0);
  151. }
  152. //==============================================================================
  153. const juce_wchar File::separator = '/';
  154. const String File::separatorString ("/");
  155. //==============================================================================
  156. const File File::getCurrentWorkingDirectory()
  157. {
  158. HeapBlock<char> heapBuffer;
  159. char localBuffer [1024];
  160. char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
  161. int bufferSize = 4096;
  162. while (cwd == 0 && errno == ERANGE)
  163. {
  164. heapBuffer.malloc (bufferSize);
  165. cwd = getcwd (heapBuffer, bufferSize - 1);
  166. bufferSize += 1024;
  167. }
  168. return File (String::fromUTF8 (cwd));
  169. }
  170. bool File::setAsCurrentWorkingDirectory() const
  171. {
  172. return chdir (getFullPathName().toUTF8()) == 0;
  173. }
  174. //==============================================================================
  175. static bool juce_stat (const String& fileName, struct stat& info)
  176. {
  177. return fileName.isNotEmpty()
  178. && (stat (fileName.toUTF8(), &info) == 0);
  179. }
  180. bool File::isDirectory() const
  181. {
  182. struct stat info;
  183. return fullPath.isEmpty()
  184. || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0));
  185. }
  186. bool File::exists() const
  187. {
  188. return fullPath.isNotEmpty()
  189. && access (fullPath.toUTF8(), F_OK) == 0;
  190. }
  191. bool File::existsAsFile() const
  192. {
  193. return exists() && ! isDirectory();
  194. }
  195. int64 File::getSize() const
  196. {
  197. struct stat info;
  198. return juce_stat (fullPath, info) ? info.st_size : 0;
  199. }
  200. //==============================================================================
  201. bool File::hasWriteAccess() const
  202. {
  203. if (exists())
  204. return access (fullPath.toUTF8(), W_OK) == 0;
  205. if ((! isDirectory()) && fullPath.containsChar (separator))
  206. return getParentDirectory().hasWriteAccess();
  207. return false;
  208. }
  209. bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const
  210. {
  211. struct stat info;
  212. const int res = stat (fullPath.toUTF8(), &info);
  213. if (res != 0)
  214. return false;
  215. info.st_mode &= 0777; // Just permissions
  216. if (shouldBeReadOnly)
  217. info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
  218. else
  219. // Give everybody write permission?
  220. info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
  221. return chmod (fullPath.toUTF8(), info.st_mode) == 0;
  222. }
  223. void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const
  224. {
  225. modificationTime = 0;
  226. accessTime = 0;
  227. creationTime = 0;
  228. struct stat info;
  229. const int res = stat (fullPath.toUTF8(), &info);
  230. if (res == 0)
  231. {
  232. modificationTime = (int64) info.st_mtime * 1000;
  233. accessTime = (int64) info.st_atime * 1000;
  234. creationTime = (int64) info.st_ctime * 1000;
  235. }
  236. }
  237. bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const
  238. {
  239. struct utimbuf times;
  240. times.actime = (time_t) (accessTime / 1000);
  241. times.modtime = (time_t) (modificationTime / 1000);
  242. return utime (fullPath.toUTF8(), &times) == 0;
  243. }
  244. bool File::deleteFile() const
  245. {
  246. if (! exists())
  247. return true;
  248. else if (isDirectory())
  249. return rmdir (fullPath.toUTF8()) == 0;
  250. else
  251. return remove (fullPath.toUTF8()) == 0;
  252. }
  253. bool File::moveInternal (const File& dest) const
  254. {
  255. if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0)
  256. return true;
  257. if (hasWriteAccess() && copyInternal (dest))
  258. {
  259. if (deleteFile())
  260. return true;
  261. dest.deleteFile();
  262. }
  263. return false;
  264. }
  265. void File::createDirectoryInternal (const String& fileName) const
  266. {
  267. mkdir (fileName.toUTF8(), 0777);
  268. }
  269. void* juce_fileOpen (const File& file, bool forWriting)
  270. {
  271. int flags = O_RDONLY;
  272. if (forWriting)
  273. {
  274. if (file.exists())
  275. {
  276. const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644);
  277. if (f != -1)
  278. lseek (f, 0, SEEK_END);
  279. return (void*) f;
  280. }
  281. else
  282. {
  283. flags = O_RDWR + O_CREAT;
  284. }
  285. }
  286. return (void*) open (file.getFullPathName().toUTF8(), flags, 00644);
  287. }
  288. void juce_fileClose (void* handle)
  289. {
  290. if (handle != 0)
  291. close ((int) (pointer_sized_int) handle);
  292. }
  293. int juce_fileRead (void* handle, void* buffer, int size)
  294. {
  295. if (handle != 0)
  296. return jmax (0, (int) read ((int) (pointer_sized_int) handle, buffer, size));
  297. return 0;
  298. }
  299. int juce_fileWrite (void* handle, const void* buffer, int size)
  300. {
  301. if (handle != 0)
  302. return (int) write ((int) (pointer_sized_int) handle, buffer, size);
  303. return 0;
  304. }
  305. int64 juce_fileSetPosition (void* handle, int64 pos)
  306. {
  307. if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos)
  308. return pos;
  309. return -1;
  310. }
  311. int64 FileOutputStream::getPositionInternal() const
  312. {
  313. if (fileHandle != 0)
  314. return lseek ((int) (pointer_sized_int) fileHandle, 0, SEEK_CUR);
  315. return -1;
  316. }
  317. void FileOutputStream::flushInternal()
  318. {
  319. if (fileHandle != 0)
  320. fsync ((int) (pointer_sized_int) fileHandle);
  321. }
  322. const File juce_getExecutableFile()
  323. {
  324. Dl_info exeInfo;
  325. dladdr ((const void*) juce_getExecutableFile, &exeInfo);
  326. return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname));
  327. }
  328. //==============================================================================
  329. // if this file doesn't exist, find a parent of it that does..
  330. static bool juce_doStatFS (File f, struct statfs& result)
  331. {
  332. for (int i = 5; --i >= 0;)
  333. {
  334. if (f.exists())
  335. break;
  336. f = f.getParentDirectory();
  337. }
  338. return statfs (f.getFullPathName().toUTF8(), &result) == 0;
  339. }
  340. int64 File::getBytesFreeOnVolume() const
  341. {
  342. struct statfs buf;
  343. if (juce_doStatFS (*this, buf))
  344. return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user
  345. return 0;
  346. }
  347. int64 File::getVolumeTotalSize() const
  348. {
  349. struct statfs buf;
  350. if (juce_doStatFS (*this, buf))
  351. return (int64) buf.f_bsize * (int64) buf.f_blocks;
  352. return 0;
  353. }
  354. const String File::getVolumeLabel() const
  355. {
  356. #if JUCE_MAC
  357. struct VolAttrBuf
  358. {
  359. u_int32_t length;
  360. attrreference_t mountPointRef;
  361. char mountPointSpace [MAXPATHLEN];
  362. } attrBuf;
  363. struct attrlist attrList;
  364. zerostruct (attrList);
  365. attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
  366. attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
  367. File f (*this);
  368. for (;;)
  369. {
  370. if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0)
  371. return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset,
  372. (int) attrBuf.mountPointRef.attr_length);
  373. const File parent (f.getParentDirectory());
  374. if (f == parent)
  375. break;
  376. f = parent;
  377. }
  378. #endif
  379. return String::empty;
  380. }
  381. int File::getVolumeSerialNumber() const
  382. {
  383. return 0; // xxx
  384. }
  385. //==============================================================================
  386. void juce_runSystemCommand (const String& command)
  387. {
  388. int result = system (command.toUTF8());
  389. (void) result;
  390. }
  391. const String juce_getOutputFromCommand (const String& command)
  392. {
  393. // slight bodge here, as we just pipe the output into a temp file and read it...
  394. const File tempFile (File::getSpecialLocation (File::tempDirectory)
  395. .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false));
  396. juce_runSystemCommand (command + " > " + tempFile.getFullPathName());
  397. String result (tempFile.loadFileAsString());
  398. tempFile.deleteFile();
  399. return result;
  400. }
  401. //==============================================================================
  402. class InterProcessLock::Pimpl
  403. {
  404. public:
  405. Pimpl (const String& name, const int timeOutMillisecs)
  406. : handle (0), refCount (1)
  407. {
  408. #if JUCE_MAC
  409. // (don't use getSpecialLocation() to avoid the temp folder being different for each app)
  410. const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
  411. #else
  412. const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
  413. #endif
  414. temp.create();
  415. handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
  416. if (handle != 0)
  417. {
  418. struct flock fl;
  419. zerostruct (fl);
  420. fl.l_whence = SEEK_SET;
  421. fl.l_type = F_WRLCK;
  422. const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs;
  423. for (;;)
  424. {
  425. const int result = fcntl (handle, F_SETLK, &fl);
  426. if (result >= 0)
  427. return;
  428. if (errno != EINTR)
  429. {
  430. if (timeOutMillisecs == 0
  431. || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime))
  432. break;
  433. Thread::sleep (10);
  434. }
  435. }
  436. }
  437. closeFile();
  438. }
  439. ~Pimpl()
  440. {
  441. closeFile();
  442. }
  443. void closeFile()
  444. {
  445. if (handle != 0)
  446. {
  447. struct flock fl;
  448. zerostruct (fl);
  449. fl.l_whence = SEEK_SET;
  450. fl.l_type = F_UNLCK;
  451. while (! (fcntl (handle, F_SETLKW, &fl) >= 0 || errno != EINTR))
  452. {}
  453. close (handle);
  454. handle = 0;
  455. }
  456. }
  457. int handle, refCount;
  458. };
  459. InterProcessLock::InterProcessLock (const String& name_)
  460. : name (name_)
  461. {
  462. }
  463. InterProcessLock::~InterProcessLock()
  464. {
  465. }
  466. bool InterProcessLock::enter (const int timeOutMillisecs)
  467. {
  468. const ScopedLock sl (lock);
  469. if (pimpl == 0)
  470. {
  471. pimpl = new Pimpl (name, timeOutMillisecs);
  472. if (pimpl->handle == 0)
  473. pimpl = 0;
  474. }
  475. else
  476. {
  477. pimpl->refCount++;
  478. }
  479. return pimpl != 0;
  480. }
  481. void InterProcessLock::exit()
  482. {
  483. const ScopedLock sl (lock);
  484. // Trying to release the lock too many times!
  485. jassert (pimpl != 0);
  486. if (pimpl != 0 && --(pimpl->refCount) == 0)
  487. pimpl = 0;
  488. }