release v0.21.23

-----BEGIN PGP SIGNATURE-----
 
 iQJEBAABCgAuFiEEA5IzWngIOJSkMBxDI26KWMbbRRIFAl6huEwQHG1heEBtdXNp
 Y3BkLm9yZwAKCRAjbopYxttFEgcrD/9jLUkiszNc+QWbUGj+RiTaD6fMxA1M8itn
 nB7MKI/g/3ggryWMWNhG51O+8wFNs/4PmJJPGxKoU6i6wmS3YFhTAcs0ryilDGAU
 1FumnMg/2qIyi2E6K2BMbi0YwZiJhBPWwD4JSt1KGvwPes/qQPFgHAi7ZWhP3tar
 fHmgmjwDZ/Kgpl5/Bn7nVNgVuzk/MpBOSwxX9tFtRxqE9wdpm9idve7SVDT7MTvk
 vdONWbAe8jXl/A8JHWaUsws0l7fyK5ZKSOXvdeSbzd67I8Rz3aqJMqUh2k/rDuv/
 GrDyeEtLV5cXZsL4B3/34kCTKac2ZJmRbSh+buKeDc1Gf0clWnvRMdsMbSoRBY4F
 lTWJbjndfq2+iHHBRfaqRjombv52R11yLT+O0aMLEm6l7xPm/rHZXJIcYSmCafd7
 FR1qMaVKP5s+M+MqGePxzCUJSWJ+1bjZwjLaHrYXYPUoXSg3mSaeDE5g7BjQhm1E
 2Hcfui9lvqR55UNo0NvDBjRT5FBGBUdjF6DjYplUGApw7xFtdahXlEvG7yfyg3ae
 pZ3FQ1MZ4dESAw7EhTEBwajsVRQ9DhGQenYTxxCnGVdCucZRPQ9Abhas0U4iFHUA
 wGj7j4WKPi+OUSyiT0j4nGuwEVtCkBFv34DqPMLjx8jqtJ8YgCt4iJD4dFwhk1zz
 uoQBhq27Gg==
 =sR2r
 -----END PGP SIGNATURE-----

Merge tag 'v0.21.23'

release v0.21.23
This commit is contained in:
Max Kellermann 2020-04-23 17:54:22 +02:00
commit 0b3acc3eec
20 changed files with 238 additions and 60 deletions

18
NEWS
View File

@ -36,6 +36,24 @@ ver 0.22 (not yet released)
* switch to C++17 * switch to C++17
- GCC 7 or clang 4 (or newer) recommended - GCC 7 or clang 4 (or newer) recommended
ver 0.21.23 (2020/04/23)
* protocol
- add tag fallback for AlbumSort
* storage
- curl: fix corrupt "href" values in the presence of XML entities
- curl: unescape "href" values
* input
- nfs: fix crash bug
- nfs: fix freeze bug on reconnect
* decoder
- gme: adapt to API change in the upcoming version 0.7.0
* output
- alsa: implement channel mapping for 5.0 and 7.0
* player
- drain outputs at end of song in "single" mode
* Windows
- fix case insensitive search
ver 0.21.22 (2020/04/02) ver 0.21.22 (2020/04/02)
* database * database
- simple: optimize startup - simple: optimize startup

View File

@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.musicpd" package="org.musicpd"
android:installLocation="auto" android:installLocation="auto"
android:versionCode="45" android:versionCode="46"
android:versionName="0.21.22"> android:versionName="0.21.23">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>

View File

@ -187,7 +187,11 @@ gme_file_decode(DecoderClient &client, Path path_fs)
LogWarning(gme_domain, gme_err); LogWarning(gme_domain, gme_err);
if (length > 0) if (length > 0)
gme_set_fade(emu, length); gme_set_fade(emu, length
#if GME_VERSION >= 0x000700
, 8000
#endif
);
/* play */ /* play */
DecoderCommand cmd; DecoderCommand cmd;

View File

@ -23,8 +23,8 @@
#include "PollGroupWinSelect.hxx" #include "PollGroupWinSelect.hxx"
constexpr int EVENT_READ = 0; static constexpr int EVENT_READ = 0;
constexpr int EVENT_WRITE = 1; static constexpr int EVENT_WRITE = 1;
static constexpr static constexpr
bool HasEvent(unsigned events, int event_id) noexcept bool HasEvent(unsigned events, int event_id) noexcept

View File

@ -23,6 +23,10 @@
#include <cassert> #include <cassert>
#include <utility> #include <utility>
#ifdef USE_EPOLL
#include <cerrno>
#endif
void void
SocketMonitor::Dispatch(unsigned flags) noexcept SocketMonitor::Dispatch(unsigned flags) noexcept
{ {
@ -81,6 +85,21 @@ SocketMonitor::Schedule(unsigned flags) noexcept
if (success) if (success)
scheduled_flags = flags; scheduled_flags = flags;
#ifdef USE_EPOLL
else if (errno == EBADF || errno == ENOENT)
/* the socket was probably closed by somebody else
(EBADF) or a new file descriptor with the same
number was created but not registered already
(ENOENT) - we can assume that there are no
scheduled events */
/* note that when this happens, we're actually lucky
that it has failed - imagine another thread may
meanwhile have created something on the same file
descriptor number, and has registered it; the
epoll_ctl() call above would then have succeeded,
but broke the other thread's epoll registration */
scheduled_flags = 0;
#endif
return success; return success;
} }

View File

@ -108,7 +108,7 @@ public:
} }
bool ScheduleRead() noexcept { bool ScheduleRead() noexcept {
return Schedule(GetScheduledFlags() | READ | HANGUP | ERROR); return Schedule(GetScheduledFlags() | READ);
} }
bool ScheduleWrite() noexcept { bool ScheduleWrite() noexcept {
@ -116,7 +116,7 @@ public:
} }
void CancelRead() noexcept { void CancelRead() noexcept {
Schedule(GetScheduledFlags() & ~(READ|HANGUP|ERROR)); Schedule(GetScheduledFlags() & ~READ);
} }
void CancelWrite() noexcept { void CancelWrite() noexcept {

View File

@ -90,6 +90,11 @@ public:
constexpr constexpr
#endif #endif
operator Path() const noexcept { operator Path() const noexcept {
#ifdef _UNICODE
if (value.IsNull())
return nullptr;
#endif
return value; return value;
} }
}; };

View File

@ -34,12 +34,6 @@
#include <algorithm> #include <algorithm>
#endif #endif
#ifdef _WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#include <cassert>
#include <memory> #include <memory>
#include <string.h> #include <string.h>
@ -65,27 +59,6 @@ try {
folded.SetSize(folded_length); folded.SetSize(folded_length);
return UCharToUTF8({folded.begin(), folded.size()}); return UCharToUTF8({folded.begin(), folded.size()});
#elif defined(_WIN32)
const auto u = MultiByteToWideChar(CP_UTF8, src);
const int size = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, nullptr, 0,
nullptr, nullptr, 0);
if (size <= 0)
return AllocatedString<>::Duplicate(src);
std::unique_ptr<wchar_t[]> buffer(new wchar_t[size]);
int result = LCMapStringEx(LOCALE_NAME_INVARIANT,
LCMAP_SORTKEY|LINGUISTIC_IGNORECASE,
u.c_str(), -1, buffer.get(), size,
nullptr, nullptr, 0);
if (result <= 0)
return AllocatedString<>::Duplicate(src);
return WideCharToMultiByte(CP_UTF8,
{buffer.get(), size_t(result - 1)});
#else #else
#error not implemented #error not implemented
#endif #endif

View File

@ -22,7 +22,7 @@
#include "config.h" #include "config.h"
#if defined(HAVE_ICU) || defined(_WIN32) #ifdef HAVE_ICU
#define HAVE_ICU_CASE_FOLD #define HAVE_ICU_CASE_FOLD
#include <string_view> #include <string_view>

View File

@ -108,7 +108,7 @@ IcuCollate(std::string_view a, std::string_view b) noexcept
} }
auto result = CompareStringEx(LOCALE_NAME_INVARIANT, auto result = CompareStringEx(LOCALE_NAME_INVARIANT,
LINGUISTIC_IGNORECASE, NORM_IGNORECASE,
wa.c_str(), -1, wa.c_str(), -1,
wb.c_str(), -1, wb.c_str(), -1,
nullptr, nullptr, 0); nullptr, nullptr, 0);

View File

@ -22,11 +22,27 @@
#include "util/StringAPI.hxx" #include "util/StringAPI.hxx"
#include "config.h" #include "config.h"
#ifdef _WIN32
#include "Win32.hxx"
#include <windows.h>
#endif
#ifdef HAVE_ICU_CASE_FOLD #ifdef HAVE_ICU_CASE_FOLD
IcuCompare::IcuCompare(std::string_view _needle) noexcept IcuCompare::IcuCompare(std::string_view _needle) noexcept
:needle(IcuCaseFold(_needle)) {} :needle(IcuCaseFold(_needle)) {}
#elif defined(_WIN32)
IcuCompare::IcuCompare(std::string_view _needle) noexcept
:needle(nullptr)
{
try {
needle = MultiByteToWideChar(CP_UTF8, _needle);
} catch (...) {
}
}
#else #else
IcuCompare::IcuCompare(std::string_view _needle) noexcept IcuCompare::IcuCompare(std::string_view _needle) noexcept
@ -39,6 +55,22 @@ IcuCompare::operator==(const char *haystack) const noexcept
{ {
#ifdef HAVE_ICU_CASE_FOLD #ifdef HAVE_ICU_CASE_FOLD
return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str()); return StringIsEqual(IcuCaseFold(haystack).c_str(), needle.c_str());
#elif defined(_WIN32)
if (needle.IsNull())
/* the MultiByteToWideChar() call in the constructor
has failed, so let's always fail the comparison */
return false;
try {
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
return CompareStringEx(LOCALE_NAME_INVARIANT,
NORM_IGNORECASE,
w_haystack.c_str(), -1,
needle.c_str(), -1,
nullptr, nullptr, 0) == CSTR_EQUAL;
} catch (...) {
return false;
}
#else #else
return StringIsEqualIgnoreCase(haystack, needle.c_str()); return StringIsEqualIgnoreCase(haystack, needle.c_str());
#endif #endif
@ -50,6 +82,24 @@ IcuCompare::IsIn(const char *haystack) const noexcept
#ifdef HAVE_ICU_CASE_FOLD #ifdef HAVE_ICU_CASE_FOLD
return StringFind(IcuCaseFold(haystack).c_str(), return StringFind(IcuCaseFold(haystack).c_str(),
needle.c_str()) != nullptr; needle.c_str()) != nullptr;
#elif defined(_WIN32)
if (needle.IsNull())
/* the MultiByteToWideChar() call in the constructor
has failed, so let's always fail the comparison */
return false;
try {
auto w_haystack = MultiByteToWideChar(CP_UTF8, haystack);
return FindNLSStringEx(LOCALE_NAME_INVARIANT,
FIND_FROMSTART|NORM_IGNORECASE,
w_haystack.c_str(), -1,
needle.c_str(), -1,
nullptr,
nullptr, nullptr, 0) >= 0;
} catch (...) {
/* MultiByteToWideChar() has failed */
return false;
}
#elif defined(HAVE_STRCASESTR) #elif defined(HAVE_STRCASESTR)
return strcasestr(haystack, needle.c_str()) != nullptr; return strcasestr(haystack, needle.c_str()) != nullptr;
#else #else

View File

@ -25,13 +25,23 @@
#include <string_view> #include <string_view>
#ifdef _WIN32
#include <wchar.h>
#endif
/** /**
* This class can compare one string ("needle") with lots of other * This class can compare one string ("needle") with lots of other
* strings ("haystacks") efficiently, ignoring case. With some * strings ("haystacks") efficiently, ignoring case. With some
* configurations, it can prepare a case-folded version of the needle. * configurations, it can prepare a case-folded version of the needle.
*/ */
class IcuCompare { class IcuCompare {
#ifdef _WIN32
/* Windows API functions work with wchar_t strings, so let's
cache the MultiByteToWideChar() result for performance */
AllocatedString<wchar_t> needle;
#else
AllocatedString<> needle; AllocatedString<> needle;
#endif
public: public:
IcuCompare():needle(nullptr) {} IcuCompare():needle(nullptr) {}
@ -40,12 +50,12 @@ public:
IcuCompare(const IcuCompare &src) noexcept IcuCompare(const IcuCompare &src) noexcept
:needle(src :needle(src
? AllocatedString<>::Duplicate(src.needle) ? src.needle.Clone()
: nullptr) {} : nullptr) {}
IcuCompare &operator=(const IcuCompare &src) noexcept { IcuCompare &operator=(const IcuCompare &src) noexcept {
needle = src needle = src
? AllocatedString<>::Duplicate(src.needle) ? src.needle.Clone()
: nullptr; : nullptr;
return *this; return *this;
} }

View File

@ -191,7 +191,9 @@ static constexpr int
events_to_libnfs(unsigned i) noexcept events_to_libnfs(unsigned i) noexcept
{ {
return ((i & SocketMonitor::READ) ? POLLIN : 0) | return ((i & SocketMonitor::READ) ? POLLIN : 0) |
((i & SocketMonitor::WRITE) ? POLLOUT : 0); ((i & SocketMonitor::WRITE) ? POLLOUT : 0) |
((i & SocketMonitor::HANGUP) ? POLLHUP : 0) |
((i & SocketMonitor::ERROR) ? POLLERR : 0);
} }
NfsConnection::~NfsConnection() noexcept NfsConnection::~NfsConnection() noexcept
@ -450,8 +452,7 @@ NfsConnection::ScheduleSocket() noexcept
SocketMonitor::Open(_fd); SocketMonitor::Open(_fd);
} }
SocketMonitor::Schedule(libnfs_to_events(which_events) SocketMonitor::Schedule(libnfs_to_events(which_events));
| SocketMonitor::HANGUP);
} }
inline int inline int

View File

@ -180,7 +180,6 @@ NfsFileReader::OnNfsConnectionDisconnected(std::exception_ptr e) noexcept
inline void inline void
NfsFileReader::OpenCallback(nfsfh *_fh) noexcept NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
{ {
assert(state == State::OPEN);
assert(connection != nullptr); assert(connection != nullptr);
assert(_fh != nullptr); assert(_fh != nullptr);
@ -197,27 +196,33 @@ NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
} }
inline void inline void
NfsFileReader::StatCallback(const struct stat *st) noexcept NfsFileReader::StatCallback(const struct stat *_st) noexcept
{ {
assert(state == State::STAT);
assert(connection != nullptr); assert(connection != nullptr);
assert(fh != nullptr); assert(fh != nullptr);
assert(st != nullptr); assert(_st != nullptr);
#if defined(_WIN32) && !defined(_WIN64)
/* on 32-bit Windows, libnfs enables -D_FILE_OFFSET_BITS=64,
but MPD (Meson) doesn't - to work around this mismatch, we
cast explicitly to "struct stat64" */
const auto *st = (const struct stat64 *)_st;
#else
const auto *st = _st;
#endif
if (!S_ISREG(st->st_mode)) { if (!S_ISREG(st->st_mode)) {
OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file"))); OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
return; return;
} }
state = State::IDLE;
OnNfsFileOpen(st->st_size); OnNfsFileOpen(st->st_size);
} }
void void
NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
{ {
switch (state) { switch (std::exchange(state, State::IDLE)) {
case State::INITIAL: case State::INITIAL:
case State::DEFER: case State::DEFER:
case State::MOUNT: case State::MOUNT:
@ -234,7 +239,6 @@ NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
break; break;
case State::READ: case State::READ:
state = State::IDLE;
OnNfsFileRead(data, status); OnNfsFileRead(data, status);
break; break;
} }

View File

@ -21,6 +21,28 @@
#include "Buffer.hxx" #include "Buffer.hxx"
#include "util/ConstBuffer.hxx" #include "util/ConstBuffer.hxx"
/*
* According to:
* - https://xiph.org/flac/format.html#frame_header
* - https://github.com/nu774/qaac/wiki/Multichannel--handling
* the source channel order (after decoding, e.g., flac, alac) is for
* - 1ch: mono
* - 2ch: left, right
* - 3ch: left, right, center
* - 4ch: front left, front right, back left, back right
* - 5ch: front left, front right, front center, back/surround left, back/surround right
* - 6ch (aka 5.1): front left, front right, front center, LFE, back/surround left, back/surround right
* - 7ch: front left, front right, front center, LFE, back center, side left, side right
* - 8ch: (aka 7.1): front left, front right, front center, LFE, back left, back right, side left, side right
*
* The ALSA default channel map is (see /usr/share/alsa/pcm/surround71.conf):
* - front left, front right, back left, back right, front center, LFE, side left, side right
*
* Hence, in case of the following source channel orders 3ch, 5ch, 6ch (aka
* 5.1), 7ch and 8ch the channel order has to be adapted
*/
template<typename V> template<typename V>
struct TwoPointers { struct TwoPointers {
V *dest; V *dest;
@ -44,17 +66,57 @@ struct TwoPointers {
return *this; return *this;
} }
TwoPointers<V> &ToAlsa50() noexcept {
*dest++ = src[0]; // front left
*dest++ = src[1]; // front right
*dest++ = src[3]; // surround left
*dest++ = src[4]; // surround right
*dest++ = src[2]; // front center
src += 5;
return *this;
}
TwoPointers<V> &ToAlsa51() noexcept { TwoPointers<V> &ToAlsa51() noexcept {
return CopyTwo() // left+right return CopyTwo() // left+right
.SwapTwoPairs(); // center, LFE, surround left+right .SwapTwoPairs(); // center, LFE, surround left+right
} }
TwoPointers<V> &ToAlsa70() noexcept {
*dest++ = src[0]; // front left
*dest++ = src[1]; // front right
*dest++ = src[5]; // side left
*dest++ = src[6]; // side right
*dest++ = src[2]; // front center
*dest++ = src[3]; // LFE
*dest++ = src[4]; // back center
src += 7;
return *this;
}
TwoPointers<V> &ToAlsa71() noexcept { TwoPointers<V> &ToAlsa71() noexcept {
return ToAlsa51() return ToAlsa51()
.CopyTwo(); // side left+right .CopyTwo(); // side left+right
} }
}; };
template<typename V>
static void
ToAlsaChannelOrder50(V *dest, const V *src, size_t n) noexcept
{
TwoPointers<V> p{dest, src};
for (size_t i = 0; i != n; ++i)
p.ToAlsa50();
}
template<typename V>
static inline ConstBuffer<V>
ToAlsaChannelOrder50(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
{
auto dest = buffer.GetT<V>(src.size);
ToAlsaChannelOrder50(dest, src.data, src.size / 5);
return { dest, src.size };
}
template<typename V> template<typename V>
static void static void
ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept ToAlsaChannelOrder51(V *dest, const V *src, size_t n) noexcept
@ -73,6 +135,24 @@ ToAlsaChannelOrder51(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
return { dest, src.size }; return { dest, src.size };
} }
template<typename V>
static void
ToAlsaChannelOrder70(V *dest, const V *src, size_t n) noexcept
{
TwoPointers<V> p{dest, src};
for (size_t i = 0; i != n; ++i)
p.ToAlsa70();
}
template<typename V>
static inline ConstBuffer<V>
ToAlsaChannelOrder70(PcmBuffer &buffer, ConstBuffer<V> src) noexcept
{
auto dest = buffer.GetT<V>(src.size);
ToAlsaChannelOrder70(dest, src.data, src.size / 7);
return { dest, src.size };
}
template<typename V> template<typename V>
static void static void
ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept ToAlsaChannelOrder71(V *dest, const V *src, size_t n) noexcept
@ -97,9 +177,15 @@ ToAlsaChannelOrderT(PcmBuffer &buffer, ConstBuffer<V> src,
unsigned channels) noexcept unsigned channels) noexcept
{ {
switch (channels) { switch (channels) {
case 5: // 5.0
return ToAlsaChannelOrder50(buffer, src);
case 6: // 5.1 case 6: // 5.1
return ToAlsaChannelOrder51(buffer, src); return ToAlsaChannelOrder51(buffer, src);
case 7: // 7.0
return ToAlsaChannelOrder70(buffer, src);
case 8: // 7.1 case 8: // 7.1
return ToAlsaChannelOrder71(buffer, src); return ToAlsaChannelOrder71(buffer, src);

View File

@ -62,7 +62,7 @@ mixramp_interpolate(const char *ramp_list, float required_db) noexcept
++ramp_list; ++ramp_list;
/* Check for exact match. */ /* Check for exact match. */
if (db == required_db) { if (db >= required_db) {
return duration; return duration;
} }

View File

@ -967,6 +967,12 @@ Player::SongBorder() noexcept
if (border_pause) { if (border_pause) {
paused = true; paused = true;
pc.listener.OnBorderPause(); pc.listener.OnBorderPause();
/* drain all outputs to guarantee the current song is
really being played to the end; without this, the
Pause() call would drop all ring buffers */
pc.outputs.Drain();
pc.outputs.Pause(); pc.outputs.Pause();
idle_add(IDLE_PLAYER); idle_add(IDLE_PLAYER);
} }

View File

@ -394,7 +394,7 @@ private:
break; break;
case State::HREF: case State::HREF:
response.href.assign(s, len); response.href.append(s, len);
break; break;
case State::STATUS: case State::STATUS:
@ -474,7 +474,7 @@ class HttpListDirectoryOperation final : public PropfindOperation {
public: public:
HttpListDirectoryOperation(CurlGlobal &curl, const char *uri) HttpListDirectoryOperation(CurlGlobal &curl, const char *uri)
:PropfindOperation(curl, uri, 1), :PropfindOperation(curl, uri, 1),
base_path(UriPathOrSlash(uri)) {} base_path(CurlUnescape(GetEasy(), UriPathOrSlash(uri))) {}
std::unique_ptr<StorageDirectoryReader> Perform() { std::unique_ptr<StorageDirectoryReader> Perform() {
DeferStart(); DeferStart();
@ -499,8 +499,7 @@ private:
/* kludge: ignoring case in this comparison to avoid /* kludge: ignoring case in this comparison to avoid
false negatives if the web server uses a different false negatives if the web server uses a different
case in hex digits in escaped characters; TODO: case */
implement properly */
path = StringAfterPrefixIgnoreCase(path, base_path.c_str()); path = StringAfterPrefixIgnoreCase(path, base_path.c_str());
if (path == nullptr || path.empty()) if (path == nullptr || path.empty())
return nullptr; return nullptr;
@ -523,11 +522,12 @@ protected:
if (r.status != 200) if (r.status != 200)
return; return;
const auto escaped_name = HrefToEscapedName(r.href.c_str()); std::string href = CurlUnescape(GetEasy(), r.href.c_str());
if (escaped_name.IsNull()) const auto name = HrefToEscapedName(href.c_str());
if (name.IsNull())
return; return;
entries.emplace_front(CurlUnescape(GetEasy(), escaped_name)); entries.emplace_front(std::string(name.data, name.size));
auto &info = entries.front().info; auto &info = entries.front().info;
info = StorageFileInfo(r.collection info = StorageFileInfo(r.collection

View File

@ -45,6 +45,10 @@ ApplyTagFallback(TagType type, F &&f) noexcept
"AlbumArtist"/"ArtistSort" was found */ "AlbumArtist"/"ArtistSort" was found */
return f(TAG_ARTIST); return f(TAG_ARTIST);
if (type == TAG_ALBUM_SORT)
/* fall back to "Album" if no "AlbumSort" was found */
return f(TAG_ALBUM);
return false; return false;
} }

View File

@ -26,9 +26,7 @@ static unsigned
FromAvahiWatchEvent(AvahiWatchEvent e) FromAvahiWatchEvent(AvahiWatchEvent e)
{ {
return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) | return (e & AVAHI_WATCH_IN ? SocketMonitor::READ : 0) |
(e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0) | (e & AVAHI_WATCH_OUT ? SocketMonitor::WRITE : 0);
(e & AVAHI_WATCH_ERR ? SocketMonitor::ERROR : 0) |
(e & AVAHI_WATCH_HUP ? SocketMonitor::HANGUP : 0);
} }
static AvahiWatchEvent static AvahiWatchEvent