WSJT-X/qt_helpers.hpp
Bill Somerville 662ed0fa7a
Network interface selection for outgoing UDP multicast datagrams
Default  selection  is  the  loop-back interface.  Users  who  require
interoperation  between  WSJT-X   instances  cooperating  applications
running on different hosts should  select a suitable network interface
and  carefully choose  a multicast  group address,  and TTL,  that has
minimal scope covering the necessary  network(s). Using 224.0.0.1 is a
reasonable    strategy    if   all    hosts    are    on   the    same
subnet. Administratively  scoped multicast group addresses  like those
within 239.255.0.0/16  can cover larger  boundaries, but care  must be
taken if the local subnet has access to a multicast enabled router.

The  IPv4  broadcast  address  (255.255.255.255) may  be  used  as  an
alternative  to multicast  UDP, but  note that  WSJT-X will  only send
broadcast UDP datagrams  on the loop-back interface,  so all recipient
applications must be running on the same host system.

The reference UDP Message protocol  applications are being extended to
be configurable  with a list of  interfaces to join a  multicast group
address on. By default they will only join on the loop-back interface,
which is also  recommended for any applications designed  to take part
in the WSJT-X  UDP Message Protocol. This allows full  user control of
the  scope of  multicast  group membership  with  a very  conservative
default  mode  that will  work  with  all interoperating  applications
running on the same host system.
2020-11-10 20:12:33 +00:00

150 lines
4.4 KiB
C++

#ifndef QT_HELPERS_HPP_
#define QT_HELPERS_HPP_
#include <stdexcept>
#include <QString>
#include <QChar>
#include <QMetaObject>
#include <QHostAddress>
#include <QDataStream>
#include <QMetaType>
#include <QMetaEnum>
#define ENUM_QDATASTREAM_OPS_DECL(CLASS, ENUM) \
QDataStream& operator << (QDataStream&, CLASS::ENUM const&); \
QDataStream& operator >> (QDataStream&, CLASS::ENUM&);
#define ENUM_QDATASTREAM_OPS_IMPL(CLASS, ENUM) \
QDataStream& operator << (QDataStream& os, CLASS::ENUM const& v) \
{ \
auto const& mo = CLASS::staticMetaObject; \
return os << mo.enumerator (mo.indexOfEnumerator (#ENUM)).valueToKey (static_cast<int> (v)); \
} \
\
QDataStream& operator >> (QDataStream& is, CLASS::ENUM& v) \
{ \
char * buffer; \
is >> buffer; \
bool ok {false}; \
auto const& mo = CLASS::staticMetaObject; \
auto const& me = mo.enumerator (mo.indexOfEnumerator (#ENUM)); \
if (buffer) \
{ \
v = static_cast<CLASS::ENUM> (me.keyToValue (buffer, &ok)); \
delete [] buffer; \
} \
if (!ok) \
{ \
v = static_cast<CLASS::ENUM> (me.value (0)); \
} \
return is; \
}
#define ENUM_CONVERSION_OPS_DECL(CLASS, ENUM) \
QString enum_to_qstring (CLASS::ENUM const&);
#define ENUM_CONVERSION_OPS_IMPL(CLASS, ENUM) \
QString enum_to_qstring (CLASS::ENUM const& m) \
{ \
auto const& mo = CLASS::staticMetaObject; \
return QString {mo.enumerator (mo.indexOfEnumerator (#ENUM)).valueToKey (static_cast<int> (m))}; \
}
#if QT_VERSION >= QT_VERSION_CHECK (5, 15, 0)
Qt::SplitBehaviorFlags const SkipEmptyParts = Qt::SkipEmptyParts;
#else
QString::SplitBehavior const SkipEmptyParts = QString::SkipEmptyParts;
#endif
inline
void throw_qstring (QString const& qs)
{
throw std::runtime_error {qs.toLocal8Bit ().constData ()};
}
QString font_as_stylesheet (QFont const&);
// do what is necessary to change a dynamic property and trigger any
// conditional style sheet updates
void update_dynamic_property (QWidget *, char const * property, QVariant const& value);
// round a QDateTime instance to an integral interval of milliseconds
QDateTime qt_round_date_time_to (QDateTime dt, int milliseconds);
// truncate a QDateTime to an integral interval of milliseconds
QDateTime qt_truncate_date_time_to (QDateTime dt, int milliseconds);
template <class T>
class VPtr
{
public:
static T * asPtr (QVariant v)
{
return reinterpret_cast<T *> (v.value<void *> ());
}
static QVariant asQVariant(T * ptr)
{
return QVariant::fromValue (reinterpret_cast<void *> (ptr));
}
};
#if QT_VERSION < QT_VERSION_CHECK (5, 14, 0)
// The Qt devs "fixed" this in 5.14 to specialize to use their own
// qHash(), it doesn't fix the problem we were addressing as qHash()
// returns a uint so is still a poorly distributed 32-bit value on
// 64-bit platforms, but we can't specialize ourselves as Qt already
// has - sigh.
namespace std
{
// std::hash<> specialization for QString based on the dbj2
// algorithm http://www.cse.yorku.ca/~oz/hash.html because qHash()
// is poor on 64-bit platforms due to being a 32-bit hash value
template<>
struct hash<QString>
{
std::size_t operator () (QString const& s) const noexcept
{
std::size_t hash {5381};
for (int i = 0; i < s.size (); ++i)
{
hash = ((hash << 5) + hash) + ((s.at (i).row () << 8) | s.at (i).cell ());
}
return hash;
}
};
}
#endif
inline
bool is_multicast_address (QHostAddress const& host_addr)
{
#if QT_VERSION >= 0x050600
return host_addr.isMulticast ();
#else
bool ok;
return (((host_addr.toIPv4Address (&ok) & 0xf0000000u) == 0xe0000000u) && ok)
|| host_addr.toIPv6Address ()[0] == 0xff;
#endif
}
inline
bool is_MAC_ambiguous_multicast_address (QHostAddress const& host_addr)
{
// sub-ranges 224.128.0.0/24, 225.0.0.0/24, 225.128.0.0/24,
// 226.0.0.0/24, 226.128.0.0/24, ..., 239.0.0.0/24, 239.128.0.0/24
// are not supported as they are inefficient due to ambiguous
// mappings to Ethernet MAC addresses. 224.0.0.0/24 alone is allowed
// from these ranges
bool ok;
auto ipv4 = host_addr.toIPv4Address (&ok);
return ok && !((ipv4 & 0xffffff00u) == 0xe0000000) && (ipv4 & 0xf07fff00) == 0xe0000000;
}
// Register some useful Qt types with QMetaType
Q_DECLARE_METATYPE (QHostAddress);
#endif