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.

751 lines
20KB

  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_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
  29. pthread_mutex_init (&internal, &atts);
  30. }
  31. CriticalSection::~CriticalSection() throw()
  32. {
  33. pthread_mutex_destroy (&internal);
  34. }
  35. void CriticalSection::enter() const throw()
  36. {
  37. pthread_mutex_lock (&internal);
  38. }
  39. bool CriticalSection::tryEnter() const throw()
  40. {
  41. return pthread_mutex_trylock (&internal) == 0;
  42. }
  43. void CriticalSection::exit() const throw()
  44. {
  45. pthread_mutex_unlock (&internal);
  46. }
  47. //==============================================================================
  48. class WaitableEventImpl
  49. {
  50. public:
  51. WaitableEventImpl (const bool manualReset_)
  52. : triggered (false),
  53. manualReset (manualReset_)
  54. {
  55. pthread_cond_init (&condition, 0);
  56. pthread_mutexattr_t atts;
  57. pthread_mutexattr_init (&atts);
  58. pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT);
  59. pthread_mutex_init (&mutex, &atts);
  60. }
  61. ~WaitableEventImpl()
  62. {
  63. pthread_cond_destroy (&condition);
  64. pthread_mutex_destroy (&mutex);
  65. }
  66. bool wait (const int timeOutMillisecs) throw()
  67. {
  68. pthread_mutex_lock (&mutex);
  69. if (! triggered)
  70. {
  71. if (timeOutMillisecs < 0)
  72. {
  73. do
  74. {
  75. pthread_cond_wait (&condition, &mutex);
  76. }
  77. while (! triggered);
  78. }
  79. else
  80. {
  81. struct timeval now;
  82. gettimeofday (&now, 0);
  83. struct timespec time;
  84. time.tv_sec = now.tv_sec + (timeOutMillisecs / 1000);
  85. time.tv_nsec = (now.tv_usec + ((timeOutMillisecs % 1000) * 1000)) * 1000;
  86. if (time.tv_nsec >= 1000000000)
  87. {
  88. time.tv_nsec -= 1000000000;
  89. time.tv_sec++;
  90. }
  91. do
  92. {
  93. if (pthread_cond_timedwait (&condition, &mutex, &time) == ETIMEDOUT)
  94. {
  95. pthread_mutex_unlock (&mutex);
  96. return false;
  97. }
  98. }
  99. while (! triggered);
  100. }
  101. }
  102. if (! manualReset)
  103. triggered = false;
  104. pthread_mutex_unlock (&mutex);
  105. return true;
  106. }
  107. void signal() throw()
  108. {
  109. pthread_mutex_lock (&mutex);
  110. triggered = true;
  111. pthread_cond_broadcast (&condition);
  112. pthread_mutex_unlock (&mutex);
  113. }
  114. void reset() throw()
  115. {
  116. pthread_mutex_lock (&mutex);
  117. triggered = false;
  118. pthread_mutex_unlock (&mutex);
  119. }
  120. private:
  121. pthread_cond_t condition;
  122. pthread_mutex_t mutex;
  123. bool triggered;
  124. const bool manualReset;
  125. WaitableEventImpl (const WaitableEventImpl&);
  126. WaitableEventImpl& operator= (const WaitableEventImpl&);
  127. };
  128. WaitableEvent::WaitableEvent (const bool manualReset) throw()
  129. : internal (new WaitableEventImpl (manualReset))
  130. {
  131. }
  132. WaitableEvent::~WaitableEvent() throw()
  133. {
  134. delete static_cast <WaitableEventImpl*> (internal);
  135. }
  136. bool WaitableEvent::wait (const int timeOutMillisecs) const throw()
  137. {
  138. return static_cast <WaitableEventImpl*> (internal)->wait (timeOutMillisecs);
  139. }
  140. void WaitableEvent::signal() const throw()
  141. {
  142. static_cast <WaitableEventImpl*> (internal)->signal();
  143. }
  144. void WaitableEvent::reset() const throw()
  145. {
  146. static_cast <WaitableEventImpl*> (internal)->reset();
  147. }
  148. //==============================================================================
  149. void JUCE_CALLTYPE Thread::sleep (int millisecs)
  150. {
  151. struct timespec time;
  152. time.tv_sec = millisecs / 1000;
  153. time.tv_nsec = (millisecs % 1000) * 1000000;
  154. nanosleep (&time, 0);
  155. }
  156. //==============================================================================
  157. const juce_wchar File::separator = '/';
  158. const String File::separatorString ("/");
  159. //==============================================================================
  160. const File File::getCurrentWorkingDirectory()
  161. {
  162. HeapBlock<char> heapBuffer;
  163. char localBuffer [1024];
  164. char* cwd = getcwd (localBuffer, sizeof (localBuffer) - 1);
  165. int bufferSize = 4096;
  166. while (cwd == 0 && errno == ERANGE)
  167. {
  168. heapBuffer.malloc (bufferSize);
  169. cwd = getcwd (heapBuffer, bufferSize - 1);
  170. bufferSize += 1024;
  171. }
  172. return File (String::fromUTF8 (cwd));
  173. }
  174. bool File::setAsCurrentWorkingDirectory() const
  175. {
  176. return chdir (getFullPathName().toUTF8()) == 0;
  177. }
  178. //==============================================================================
  179. #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T
  180. typedef struct stat64 juce_statStruct; // (need to use the 64-bit version to work around a simulator bug)
  181. #else
  182. typedef struct stat juce_statStruct;
  183. #endif
  184. static bool juce_stat (const String& fileName, juce_statStruct& info)
  185. {
  186. return fileName.isNotEmpty()
  187. #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T
  188. && (stat64 (fileName.toUTF8(), &info) == 0);
  189. #else
  190. && (stat (fileName.toUTF8(), &info) == 0);
  191. #endif
  192. }
  193. bool File::isDirectory() const
  194. {
  195. juce_statStruct info;
  196. return fullPath.isEmpty()
  197. || (juce_stat (fullPath, info) && ((info.st_mode & S_IFDIR) != 0));
  198. }
  199. bool File::exists() const
  200. {
  201. juce_statStruct info;
  202. return fullPath.isNotEmpty()
  203. #if JUCE_IOS && ! __DARWIN_ONLY_64_BIT_INO_T
  204. && (lstat64 (fullPath.toUTF8(), &info) == 0);
  205. #else
  206. && (lstat (fullPath.toUTF8(), &info) == 0);
  207. #endif
  208. }
  209. bool File::existsAsFile() const
  210. {
  211. return exists() && ! isDirectory();
  212. }
  213. int64 File::getSize() const
  214. {
  215. juce_statStruct info;
  216. return juce_stat (fullPath, info) ? info.st_size : 0;
  217. }
  218. //==============================================================================
  219. bool File::hasWriteAccess() const
  220. {
  221. if (exists())
  222. return access (fullPath.toUTF8(), W_OK) == 0;
  223. if ((! isDirectory()) && fullPath.containsChar (separator))
  224. return getParentDirectory().hasWriteAccess();
  225. return false;
  226. }
  227. bool File::setFileReadOnlyInternal (const bool shouldBeReadOnly) const
  228. {
  229. juce_statStruct info;
  230. if (! juce_stat (fullPath, info))
  231. return false;
  232. info.st_mode &= 0777; // Just permissions
  233. if (shouldBeReadOnly)
  234. info.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
  235. else
  236. // Give everybody write permission?
  237. info.st_mode |= S_IWUSR | S_IWGRP | S_IWOTH;
  238. return chmod (fullPath.toUTF8(), info.st_mode) == 0;
  239. }
  240. void File::getFileTimesInternal (int64& modificationTime, int64& accessTime, int64& creationTime) const
  241. {
  242. modificationTime = 0;
  243. accessTime = 0;
  244. creationTime = 0;
  245. juce_statStruct info;
  246. if (juce_stat (fullPath, info))
  247. {
  248. modificationTime = (int64) info.st_mtime * 1000;
  249. accessTime = (int64) info.st_atime * 1000;
  250. creationTime = (int64) info.st_ctime * 1000;
  251. }
  252. }
  253. bool File::setFileTimesInternal (int64 modificationTime, int64 accessTime, int64 /*creationTime*/) const
  254. {
  255. struct utimbuf times;
  256. times.actime = (time_t) (accessTime / 1000);
  257. times.modtime = (time_t) (modificationTime / 1000);
  258. return utime (fullPath.toUTF8(), &times) == 0;
  259. }
  260. bool File::deleteFile() const
  261. {
  262. if (! exists())
  263. return true;
  264. else if (isDirectory())
  265. return rmdir (fullPath.toUTF8()) == 0;
  266. else
  267. return remove (fullPath.toUTF8()) == 0;
  268. }
  269. bool File::moveInternal (const File& dest) const
  270. {
  271. if (rename (fullPath.toUTF8(), dest.getFullPathName().toUTF8()) == 0)
  272. return true;
  273. if (hasWriteAccess() && copyInternal (dest))
  274. {
  275. if (deleteFile())
  276. return true;
  277. dest.deleteFile();
  278. }
  279. return false;
  280. }
  281. void File::createDirectoryInternal (const String& fileName) const
  282. {
  283. mkdir (fileName.toUTF8(), 0777);
  284. }
  285. //==============================================================================
  286. int64 juce_fileSetPosition (void* handle, int64 pos)
  287. {
  288. if (handle != 0 && lseek ((int) (pointer_sized_int) handle, pos, SEEK_SET) == pos)
  289. return pos;
  290. return -1;
  291. }
  292. void FileInputStream::openHandle()
  293. {
  294. totalSize = file.getSize();
  295. const int f = open (file.getFullPathName().toUTF8(), O_RDONLY, 00644);
  296. if (f != -1)
  297. fileHandle = (void*) f;
  298. }
  299. void FileInputStream::closeHandle()
  300. {
  301. if (fileHandle != 0)
  302. {
  303. close ((int) (pointer_sized_int) fileHandle);
  304. fileHandle = 0;
  305. }
  306. }
  307. size_t FileInputStream::readInternal (void* const buffer, const size_t numBytes)
  308. {
  309. if (fileHandle != 0)
  310. return jmax ((ssize_t) 0, ::read ((int) (pointer_sized_int) fileHandle, buffer, numBytes));
  311. return 0;
  312. }
  313. //==============================================================================
  314. void FileOutputStream::openHandle()
  315. {
  316. if (file.exists())
  317. {
  318. const int f = open (file.getFullPathName().toUTF8(), O_RDWR, 00644);
  319. if (f != -1)
  320. {
  321. currentPosition = lseek (f, 0, SEEK_END);
  322. if (currentPosition >= 0)
  323. fileHandle = (void*) f;
  324. else
  325. close (f);
  326. }
  327. }
  328. else
  329. {
  330. const int f = open (file.getFullPathName().toUTF8(), O_RDWR + O_CREAT, 00644);
  331. if (f != -1)
  332. fileHandle = (void*) f;
  333. }
  334. }
  335. void FileOutputStream::closeHandle()
  336. {
  337. if (fileHandle != 0)
  338. {
  339. close ((int) (pointer_sized_int) fileHandle);
  340. fileHandle = 0;
  341. }
  342. }
  343. int FileOutputStream::writeInternal (const void* const data, const int numBytes)
  344. {
  345. if (fileHandle != 0)
  346. return (int) ::write ((int) (pointer_sized_int) fileHandle, data, numBytes);
  347. return 0;
  348. }
  349. void FileOutputStream::flushInternal()
  350. {
  351. if (fileHandle != 0)
  352. fsync ((int) (pointer_sized_int) fileHandle);
  353. }
  354. //==============================================================================
  355. const File juce_getExecutableFile()
  356. {
  357. Dl_info exeInfo;
  358. dladdr ((const void*) juce_getExecutableFile, &exeInfo);
  359. return File::getCurrentWorkingDirectory().getChildFile (String::fromUTF8 (exeInfo.dli_fname));
  360. }
  361. //==============================================================================
  362. // if this file doesn't exist, find a parent of it that does..
  363. static bool juce_doStatFS (File f, struct statfs& result)
  364. {
  365. for (int i = 5; --i >= 0;)
  366. {
  367. if (f.exists())
  368. break;
  369. f = f.getParentDirectory();
  370. }
  371. return statfs (f.getFullPathName().toUTF8(), &result) == 0;
  372. }
  373. int64 File::getBytesFreeOnVolume() const
  374. {
  375. struct statfs buf;
  376. if (juce_doStatFS (*this, buf))
  377. return (int64) buf.f_bsize * (int64) buf.f_bavail; // Note: this returns space available to non-super user
  378. return 0;
  379. }
  380. int64 File::getVolumeTotalSize() const
  381. {
  382. struct statfs buf;
  383. if (juce_doStatFS (*this, buf))
  384. return (int64) buf.f_bsize * (int64) buf.f_blocks;
  385. return 0;
  386. }
  387. const String File::getVolumeLabel() const
  388. {
  389. #if JUCE_MAC
  390. struct VolAttrBuf
  391. {
  392. u_int32_t length;
  393. attrreference_t mountPointRef;
  394. char mountPointSpace [MAXPATHLEN];
  395. } attrBuf;
  396. struct attrlist attrList;
  397. zerostruct (attrList);
  398. attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
  399. attrList.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
  400. File f (*this);
  401. for (;;)
  402. {
  403. if (getattrlist (f.getFullPathName().toUTF8(), &attrList, &attrBuf, sizeof (attrBuf), 0) == 0)
  404. return String::fromUTF8 (((const char*) &attrBuf.mountPointRef) + attrBuf.mountPointRef.attr_dataoffset,
  405. (int) attrBuf.mountPointRef.attr_length);
  406. const File parent (f.getParentDirectory());
  407. if (f == parent)
  408. break;
  409. f = parent;
  410. }
  411. #endif
  412. return String::empty;
  413. }
  414. int File::getVolumeSerialNumber() const
  415. {
  416. return 0; // xxx
  417. }
  418. //==============================================================================
  419. void juce_runSystemCommand (const String& command)
  420. {
  421. int result = system (command.toUTF8());
  422. (void) result;
  423. }
  424. const String juce_getOutputFromCommand (const String& command)
  425. {
  426. // slight bodge here, as we just pipe the output into a temp file and read it...
  427. const File tempFile (File::getSpecialLocation (File::tempDirectory)
  428. .getNonexistentChildFile (String::toHexString (Random::getSystemRandom().nextInt()), ".tmp", false));
  429. juce_runSystemCommand (command + " > " + tempFile.getFullPathName());
  430. String result (tempFile.loadFileAsString());
  431. tempFile.deleteFile();
  432. return result;
  433. }
  434. //==============================================================================
  435. class InterProcessLock::Pimpl
  436. {
  437. public:
  438. Pimpl (const String& name, const int timeOutMillisecs)
  439. : handle (0), refCount (1)
  440. {
  441. #if JUCE_MAC
  442. // (don't use getSpecialLocation() to avoid the temp folder being different for each app)
  443. const File temp (File ("~/Library/Caches/Juce").getChildFile (name));
  444. #else
  445. const File temp (File::getSpecialLocation (File::tempDirectory).getChildFile (name));
  446. #endif
  447. temp.create();
  448. handle = open (temp.getFullPathName().toUTF8(), O_RDWR);
  449. if (handle != 0)
  450. {
  451. struct flock fl;
  452. zerostruct (fl);
  453. fl.l_whence = SEEK_SET;
  454. fl.l_type = F_WRLCK;
  455. const int64 endTime = Time::currentTimeMillis() + timeOutMillisecs;
  456. for (;;)
  457. {
  458. const int result = fcntl (handle, F_SETLK, &fl);
  459. if (result >= 0)
  460. return;
  461. if (errno != EINTR)
  462. {
  463. if (timeOutMillisecs == 0
  464. || (timeOutMillisecs > 0 && Time::currentTimeMillis() >= endTime))
  465. break;
  466. Thread::sleep (10);
  467. }
  468. }
  469. }
  470. closeFile();
  471. }
  472. ~Pimpl()
  473. {
  474. closeFile();
  475. }
  476. void closeFile()
  477. {
  478. if (handle != 0)
  479. {
  480. struct flock fl;
  481. zerostruct (fl);
  482. fl.l_whence = SEEK_SET;
  483. fl.l_type = F_UNLCK;
  484. while (! (fcntl (handle, F_SETLKW, &fl) >= 0 || errno != EINTR))
  485. {}
  486. close (handle);
  487. handle = 0;
  488. }
  489. }
  490. int handle, refCount;
  491. };
  492. InterProcessLock::InterProcessLock (const String& name_)
  493. : name (name_)
  494. {
  495. }
  496. InterProcessLock::~InterProcessLock()
  497. {
  498. }
  499. bool InterProcessLock::enter (const int timeOutMillisecs)
  500. {
  501. const ScopedLock sl (lock);
  502. if (pimpl == 0)
  503. {
  504. pimpl = new Pimpl (name, timeOutMillisecs);
  505. if (pimpl->handle == 0)
  506. pimpl = 0;
  507. }
  508. else
  509. {
  510. pimpl->refCount++;
  511. }
  512. return pimpl != 0;
  513. }
  514. void InterProcessLock::exit()
  515. {
  516. const ScopedLock sl (lock);
  517. // Trying to release the lock too many times!
  518. jassert (pimpl != 0);
  519. if (pimpl != 0 && --(pimpl->refCount) == 0)
  520. pimpl = 0;
  521. }
  522. //==============================================================================
  523. void JUCE_API juce_threadEntryPoint (void*);
  524. void* threadEntryProc (void* userData)
  525. {
  526. JUCE_AUTORELEASEPOOL
  527. juce_threadEntryPoint (userData);
  528. return 0;
  529. }
  530. void* juce_createThread (void* userData)
  531. {
  532. pthread_t handle = 0;
  533. if (pthread_create (&handle, 0, threadEntryProc, userData) == 0)
  534. {
  535. pthread_detach (handle);
  536. return (void*) handle;
  537. }
  538. return 0;
  539. }
  540. void juce_killThread (void* handle)
  541. {
  542. if (handle != 0)
  543. pthread_cancel ((pthread_t) handle);
  544. }
  545. void juce_setCurrentThreadName (const String& /*name*/)
  546. {
  547. }
  548. bool juce_setThreadPriority (void* handle, int priority)
  549. {
  550. struct sched_param param;
  551. int policy;
  552. priority = jlimit (0, 10, priority);
  553. if (handle == 0)
  554. handle = (void*) pthread_self();
  555. if (pthread_getschedparam ((pthread_t) handle, &policy, &param) != 0)
  556. return false;
  557. policy = priority == 0 ? SCHED_OTHER : SCHED_RR;
  558. const int minPriority = sched_get_priority_min (policy);
  559. const int maxPriority = sched_get_priority_max (policy);
  560. param.sched_priority = ((maxPriority - minPriority) * priority) / 10 + minPriority;
  561. return pthread_setschedparam ((pthread_t) handle, policy, &param) == 0;
  562. }
  563. Thread::ThreadID Thread::getCurrentThreadId()
  564. {
  565. return (ThreadID) pthread_self();
  566. }
  567. void Thread::yield()
  568. {
  569. sched_yield();
  570. }
  571. //==============================================================================
  572. /* Remove this macro if you're having problems compiling the cpu affinity
  573. calls (the API for these has changed about quite a bit in various Linux
  574. versions, and a lot of distros seem to ship with obsolete versions)
  575. */
  576. #if defined (CPU_ISSET) && ! defined (SUPPORT_AFFINITIES)
  577. #define SUPPORT_AFFINITIES 1
  578. #endif
  579. void Thread::setCurrentThreadAffinityMask (const uint32 affinityMask)
  580. {
  581. #if SUPPORT_AFFINITIES
  582. cpu_set_t affinity;
  583. CPU_ZERO (&affinity);
  584. for (int i = 0; i < 32; ++i)
  585. if ((affinityMask & (1 << i)) != 0)
  586. CPU_SET (i, &affinity);
  587. /*
  588. N.B. If this line causes a compile error, then you've probably not got the latest
  589. version of glibc installed.
  590. If you don't want to update your copy of glibc and don't care about cpu affinities,
  591. then you can just disable all this stuff by setting the SUPPORT_AFFINITIES macro to 0.
  592. */
  593. sched_setaffinity (getpid(), sizeof (cpu_set_t), &affinity);
  594. sched_yield();
  595. #else
  596. /* affinities aren't supported because either the appropriate header files weren't found,
  597. or the SUPPORT_AFFINITIES macro was turned off
  598. */
  599. jassertfalse;
  600. #endif
  601. }