work
This commit is contained in:
parent
cc11c41f32
commit
20f8d81f13
@ -528,7 +528,7 @@ void FormMain::switchVisibility(bool force_hide) {
|
||||
QSystemTrayIcon::MessageIcon::Warning});
|
||||
}
|
||||
else {
|
||||
close();
|
||||
hide();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1078,9 +1078,15 @@ void FormMain::changeEvent(QEvent* event) {
|
||||
}
|
||||
|
||||
void FormMain::closeEvent(QCloseEvent* event) {
|
||||
QMainWindow::closeEvent(event);
|
||||
if (qApp->quitOnLastWindowClosed()) {
|
||||
QMainWindow::closeEvent(event);
|
||||
}
|
||||
else /*if (!event->spontaneous())*/ {
|
||||
event->ignore();
|
||||
hide();
|
||||
}
|
||||
|
||||
qDebugNN << LOGSEC_GUI << "Main window's close event";
|
||||
qDebugNN << LOGSEC_GUI << "Main window close event";
|
||||
}
|
||||
|
||||
void FormMain::showEvent(QShowEvent* event) {
|
||||
@ -1092,7 +1098,7 @@ void FormMain::showEvent(QShowEvent* event) {
|
||||
void FormMain::hideEvent(QHideEvent* event) {
|
||||
QMainWindow::hideEvent(event);
|
||||
|
||||
qDebugNN << LOGSEC_GUI << "Main window's hide event";
|
||||
qDebugNN << LOGSEC_GUI << "Main window hide event";
|
||||
}
|
||||
|
||||
void FormMain::showDocs() {
|
||||
|
@ -33,13 +33,14 @@ LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||
}
|
||||
|
||||
// Create a video child window. Force Qt to create a native window, and
|
||||
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa
|
||||
// pass the window ID to the mpv wid option. Works on: X11, win32, Cocoa.
|
||||
m_mpvContainer->setAttribute(Qt::WidgetAttribute::WA_DontCreateNativeAncestors);
|
||||
m_mpvContainer->setAttribute(Qt::WidgetAttribute::WA_NativeWindow);
|
||||
|
||||
layout()->addWidget(m_mpvContainer);
|
||||
|
||||
auto raw_wid = m_mpvContainer->winId();
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// Truncate to 32-bit, as all Windows handles are. This also ensures
|
||||
// it doesn't go negative.
|
||||
@ -49,10 +50,19 @@ LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||
#endif
|
||||
|
||||
mpv_set_option(m_mpvHandle, "wid", MPV_FORMAT_INT64, &wid);
|
||||
|
||||
mpv_set_option_string(m_mpvHandle, "input-default-bindings", "yes");
|
||||
mpv_set_option_string(m_mpvHandle, "msg-level", "all=v");
|
||||
mpv::qt::set_option_variant(m_mpvHandle, "hwdec", "auto");
|
||||
mpv_set_option_string(m_mpvHandle, "terminal", "yes");
|
||||
mpv_set_option_string(m_mpvHandle, "keep-open", "yes");
|
||||
// mpv_set_option_string(m_mpvHandle, "input-terminal", "yes");
|
||||
mpv_set_option_string(m_mpvHandle, "hwdec", "auto");
|
||||
mpv_set_option_string(m_mpvHandle, "osd-playing-msg", "${media-title}");
|
||||
mpv_set_option_string(m_mpvHandle, "osc", "yes");
|
||||
mpv_set_option_string(m_mpvHandle, "input-cursor", "yes");
|
||||
|
||||
// mpv_set_option_string(m_mpvHandle, "osd-on-seek", "msg-bar");
|
||||
|
||||
// mpv::qt::set_option_variant(m_mpvHandle, "hwdec", "auto");
|
||||
|
||||
// Enable keyboard input on the X11 window. For the messy details, see
|
||||
// --input-vo-keyboard on the manpage.
|
||||
@ -62,6 +72,8 @@ LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||
mpv_observe_property(m_mpvHandle, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
||||
mpv_observe_property(m_mpvHandle, 0, "track-list", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(m_mpvHandle, 0, "chapter-list", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(m_mpvHandle, 0, "duration", MPV_FORMAT_NODE);
|
||||
mpv_observe_property(m_mpvHandle, 0, "volume", MPV_FORMAT_NODE);
|
||||
|
||||
// From this point on, the wakeup function will be called. The callback
|
||||
// can come from any thread, so we use the QueuedConnection mechanism to
|
||||
@ -71,6 +83,7 @@ LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||
this,
|
||||
&LibMpvBackend::onMpvEvents,
|
||||
Qt::ConnectionType::QueuedConnection);
|
||||
|
||||
mpv_set_wakeup_callback(m_mpvHandle, wakeup, this);
|
||||
|
||||
if (mpv_initialize(m_mpvHandle) < 0) {
|
||||
@ -88,25 +101,8 @@ void LibMpvBackend::handleMpvEvent(mpv_event* event) {
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
mpv_event_property* prop = (mpv_event_property*)event->data;
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double*)prop->data;
|
||||
std::stringstream ss;
|
||||
ss << "At: " << time;
|
||||
}
|
||||
else if (prop->format == MPV_FORMAT_NONE) {
|
||||
}
|
||||
}
|
||||
else if (strcmp(prop->name, "chapter-list") == 0 || strcmp(prop->name, "track-list") == 0) {
|
||||
// Dump the properties as JSON for demo purposes.
|
||||
if (prop->format == MPV_FORMAT_NODE) {
|
||||
QVariant v = mpv::qt::node_to_variant((mpv_node*)prop->data);
|
||||
// Abuse JSON support for easily printing the mpv_node contents.
|
||||
QJsonDocument d = QJsonDocument::fromVariant(v);
|
||||
appendLog("Change property " + QString(prop->name) + ":\n");
|
||||
appendLog(d.toJson().data());
|
||||
}
|
||||
}
|
||||
|
||||
processPropertyChange(prop);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -128,16 +124,14 @@ void LibMpvBackend::handleMpvEvent(mpv_event* event) {
|
||||
}
|
||||
|
||||
case MPV_EVENT_LOG_MESSAGE: {
|
||||
struct mpv_event_log_message* msg = (struct mpv_event_log_message*)event->data;
|
||||
std::stringstream ss;
|
||||
ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
|
||||
qDebugNN << LOGSEC_MPV << QString::fromStdString(ss.str());
|
||||
break;
|
||||
mpv_event_log_message* msg = (mpv_event_log_message*)event->data;
|
||||
|
||||
processLogMessage(msg);
|
||||
}
|
||||
|
||||
case MPV_EVENT_SHUTDOWN: {
|
||||
mpv_terminate_destroy(m_mpvHandle);
|
||||
m_mpvHandle = NULL;
|
||||
m_mpvHandle = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -159,6 +153,35 @@ void LibMpvBackend::onMpvEvents() {
|
||||
}
|
||||
}
|
||||
|
||||
void LibMpvBackend::processPropertyChange(mpv_event_property* prop) {
|
||||
if (strcmp(prop->name, "time-pos") == 0) {
|
||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||
double time = *(double*)prop->data;
|
||||
std::stringstream ss;
|
||||
ss << "At: " << time;
|
||||
}
|
||||
else if (prop->format == MPV_FORMAT_NONE) {
|
||||
}
|
||||
}
|
||||
else if (strcmp(prop->name, "chapter-list") == 0 || strcmp(prop->name, "track-list") == 0) {
|
||||
// Dump the properties as JSON for demo purposes.
|
||||
if (prop->format == MPV_FORMAT_NODE) {
|
||||
QVariant v = mpv::qt::node_to_variant((mpv_node*)prop->data);
|
||||
// Abuse JSON support for easily printing the mpv_node contents.
|
||||
QJsonDocument d = QJsonDocument::fromVariant(v);
|
||||
appendLog("Change property " + QString(prop->name) + ":\n");
|
||||
appendLog(d.toJson().data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LibMpvBackend::processLogMessage(mpv_event_log_message* msg) {
|
||||
std::stringstream ss;
|
||||
ss << "[" << msg->prefix << "] " << msg->level << ": " << msg->text;
|
||||
|
||||
appendLog(QString::fromStdString(ss.str()));
|
||||
}
|
||||
|
||||
void LibMpvBackend::appendLog(const QString& text) {
|
||||
qDebugNN << LOGSEC_MPV << text;
|
||||
}
|
||||
@ -166,16 +189,27 @@ void LibMpvBackend::appendLog(const QString& text) {
|
||||
bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) {
|
||||
Q_UNUSED(watched)
|
||||
|
||||
if (event->type() == QEvent::Type::ShortcutOverride) {
|
||||
// NOTE: If user presses key which is application-wide assigned to some
|
||||
// action, do not propagate the shortcut to application.
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (event->type() == QEvent::Type::KeyPress) {
|
||||
char txt = (char)dynamic_cast<QKeyEvent*>(event)->key();
|
||||
char str[2];
|
||||
if (m_mpvHandle != nullptr) {
|
||||
char txt = (char)dynamic_cast<QKeyEvent*>(event)->key();
|
||||
char str[2];
|
||||
|
||||
str[0] = txt;
|
||||
str[1] = '\0';
|
||||
str[0] = txt;
|
||||
str[1] = '\0';
|
||||
|
||||
const char* args[] = {"keypress", str, NULL};
|
||||
const char* args[] = {"keypress", str, nullptr};
|
||||
|
||||
mpv_command_async(m_mpvHandle, 0, args);
|
||||
mpv_command_async(m_mpvHandle, 0, args);
|
||||
}
|
||||
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -185,7 +219,7 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) {
|
||||
void LibMpvBackend::playUrl(const QUrl& url) {
|
||||
auto eb = url.toString().toLocal8Bit();
|
||||
const char* css = eb.data();
|
||||
const char* args[] = {"loadfile", css, NULL};
|
||||
const char* args[] = {"loadfile", css, nullptr};
|
||||
mpv_command_async(m_mpvHandle, 0, args);
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@
|
||||
|
||||
struct mpv_handle;
|
||||
struct mpv_event;
|
||||
struct mpv_event_property;
|
||||
struct mpv_event_log_message;
|
||||
|
||||
class LibMpvBackend : public PlayerBackend {
|
||||
Q_OBJECT
|
||||
@ -38,6 +40,8 @@ class LibMpvBackend : public PlayerBackend {
|
||||
void launchMpvEvents();
|
||||
|
||||
private:
|
||||
void processPropertyChange(mpv_event_property* prop);
|
||||
void processLogMessage(mpv_event_log_message* msg);
|
||||
void appendLog(const QString& text);
|
||||
void createPlayer();
|
||||
void handleMpvEvent(mpv_event* event);
|
||||
|
@ -1,3 +1,5 @@
|
||||
// For license of this file, see <project-root-folder>/LICENSE.md.
|
||||
|
||||
#ifndef LIBMPV_QTHELPER_H_
|
||||
#define LIBMPV_QTHELPER_H_
|
||||
|
||||
@ -14,7 +16,6 @@
|
||||
|
||||
namespace mpv {
|
||||
namespace qt {
|
||||
|
||||
// Wrapper around mpv_handle. Does refcounting under the hood.
|
||||
class Handle {
|
||||
struct container {
|
||||
@ -51,25 +52,34 @@ namespace mpv {
|
||||
switch (node->format) {
|
||||
case MPV_FORMAT_STRING:
|
||||
return QVariant(QString::fromUtf8(node->u.string));
|
||||
|
||||
case MPV_FORMAT_FLAG:
|
||||
return QVariant(static_cast<bool>(node->u.flag));
|
||||
|
||||
case MPV_FORMAT_INT64:
|
||||
return QVariant(static_cast<qlonglong>(node->u.int64));
|
||||
|
||||
case MPV_FORMAT_DOUBLE:
|
||||
return QVariant(node->u.double_);
|
||||
|
||||
case MPV_FORMAT_NODE_ARRAY: {
|
||||
mpv_node_list* list = node->u.list;
|
||||
QVariantList qlist;
|
||||
for (int n = 0; n < list->num; n++)
|
||||
|
||||
for (int n = 0; n < list->num; n++) {
|
||||
qlist.append(node_to_variant(&list->values[n]));
|
||||
}
|
||||
|
||||
return QVariant(qlist);
|
||||
}
|
||||
case MPV_FORMAT_NODE_MAP: {
|
||||
mpv_node_list* list = node->u.list;
|
||||
QVariantMap qmap;
|
||||
|
||||
for (int n = 0; n < list->num; n++) {
|
||||
qmap.insert(QString::fromUtf8(list->keys[n]), node_to_variant(&list->values[n]));
|
||||
}
|
||||
|
||||
return QVariant(qmap);
|
||||
}
|
||||
default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions)
|
||||
@ -81,9 +91,11 @@ namespace mpv {
|
||||
node_builder(const QVariant& v) {
|
||||
set(&node_, v);
|
||||
}
|
||||
|
||||
~node_builder() {
|
||||
free_node(&node_);
|
||||
}
|
||||
|
||||
mpv_node* node() {
|
||||
return &node_;
|
||||
}
|
||||
@ -91,45 +103,63 @@ namespace mpv {
|
||||
private:
|
||||
Q_DISABLE_COPY(node_builder)
|
||||
mpv_node node_;
|
||||
|
||||
mpv_node_list* create_list(mpv_node* dst, bool is_map, int num) {
|
||||
dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY;
|
||||
mpv_node_list* list = new mpv_node_list();
|
||||
dst->u.list = list;
|
||||
if (!list)
|
||||
|
||||
if (!list) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
list->values = new mpv_node[num]();
|
||||
if (!list->values)
|
||||
|
||||
if (!list->values) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (is_map) {
|
||||
list->keys = new char*[num]();
|
||||
if (!list->keys)
|
||||
|
||||
if (!list->keys) {
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
|
||||
err:
|
||||
free_node(dst);
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* dup_qstring(const QString& s) {
|
||||
QByteArray b = s.toUtf8();
|
||||
char* r = new char[b.size() + 1];
|
||||
if (r)
|
||||
|
||||
if (r) {
|
||||
std::memcpy(r, b.data(), b.size() + 1);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
bool test_type(const QVariant& v, QMetaType::Type t) {
|
||||
// The Qt docs say: "Although this function is declared as returning
|
||||
// "QVariant::Type(obsolete), the return value should be interpreted
|
||||
// as QMetaType::Type."
|
||||
// So a cast really seems to be needed to avoid warnings (urgh).
|
||||
return static_cast<int>(v.type()) == static_cast<int>(t);
|
||||
return v.typeId() == static_cast<int>(t);
|
||||
}
|
||||
void set(mpv_node* dst, const QVariant& src) {
|
||||
if (test_type(src, QMetaType::QString)) {
|
||||
dst->format = MPV_FORMAT_STRING;
|
||||
dst->u.string = dup_qstring(src.toString());
|
||||
if (!dst->u.string)
|
||||
|
||||
if (!dst->u.string) {
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else if (test_type(src, QMetaType::Bool)) {
|
||||
dst->format = MPV_FORMAT_FLAG;
|
||||
@ -147,57 +177,80 @@ namespace mpv {
|
||||
else if (src.canConvert<QVariantList>()) {
|
||||
QVariantList qlist = src.toList();
|
||||
mpv_node_list* list = create_list(dst, false, qlist.size());
|
||||
if (!list)
|
||||
|
||||
if (!list) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
list->num = qlist.size();
|
||||
for (int n = 0; n < qlist.size(); n++)
|
||||
|
||||
for (int n = 0; n < qlist.size(); n++) {
|
||||
set(&list->values[n], qlist[n]);
|
||||
}
|
||||
}
|
||||
else if (src.canConvert<QVariantMap>()) {
|
||||
QVariantMap qmap = src.toMap();
|
||||
mpv_node_list* list = create_list(dst, true, qmap.size());
|
||||
if (!list)
|
||||
|
||||
if (!list) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
list->num = qmap.size();
|
||||
|
||||
for (int n = 0; n < qmap.size(); n++) {
|
||||
list->keys[n] = dup_qstring(qmap.keys()[n]);
|
||||
|
||||
if (!list->keys[n]) {
|
||||
free_node(dst);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
set(&list->values[n], qmap.values()[n]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
fail:
|
||||
dst->format = MPV_FORMAT_NONE;
|
||||
}
|
||||
|
||||
void free_node(mpv_node* dst) {
|
||||
switch (dst->format) {
|
||||
case MPV_FORMAT_STRING:
|
||||
delete[] dst->u.string;
|
||||
break;
|
||||
|
||||
case MPV_FORMAT_NODE_ARRAY:
|
||||
case MPV_FORMAT_NODE_MAP: {
|
||||
mpv_node_list* list = dst->u.list;
|
||||
|
||||
if (list) {
|
||||
for (int n = 0; n < list->num; n++) {
|
||||
if (list->keys)
|
||||
if (list->keys) {
|
||||
delete[] list->keys[n];
|
||||
if (list->values)
|
||||
}
|
||||
|
||||
if (list->values) {
|
||||
free_node(&list->values[n]);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] list->keys;
|
||||
delete[] list->values;
|
||||
}
|
||||
|
||||
delete list;
|
||||
break;
|
||||
}
|
||||
|
||||
default:;
|
||||
}
|
||||
|
||||
dst->format = MPV_FORMAT_NONE;
|
||||
}
|
||||
};
|
||||
@ -207,7 +260,9 @@ namespace mpv {
|
||||
*/
|
||||
struct node_autofree {
|
||||
mpv_node* ptr;
|
||||
|
||||
node_autofree(mpv_node* a_ptr) : ptr(a_ptr) {}
|
||||
|
||||
~node_autofree() {
|
||||
mpv_free_node_contents(ptr);
|
||||
}
|
||||
@ -223,8 +278,11 @@ namespace mpv {
|
||||
*/
|
||||
static inline QVariant get_property_variant(mpv_handle* ctx, const QString& name) {
|
||||
mpv_node node;
|
||||
if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0)
|
||||
|
||||
if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
node_autofree f(&node);
|
||||
return node_to_variant(&node);
|
||||
}
|
||||
@ -236,6 +294,7 @@ namespace mpv {
|
||||
*/
|
||||
static inline int set_property_variant(mpv_handle* ctx, const QString& name, const QVariant& v) {
|
||||
node_builder node(v);
|
||||
|
||||
return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
|
||||
}
|
||||
|
||||
@ -246,6 +305,7 @@ namespace mpv {
|
||||
*/
|
||||
static inline int set_option_variant(mpv_handle* ctx, const QString& name, const QVariant& v) {
|
||||
node_builder node(v);
|
||||
|
||||
return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node());
|
||||
}
|
||||
|
||||
@ -258,8 +318,11 @@ namespace mpv {
|
||||
static inline QVariant command_variant(mpv_handle* ctx, const QVariant& args) {
|
||||
node_builder node(args);
|
||||
mpv_node res;
|
||||
if (mpv_command_node(ctx, node.node(), &res) < 0)
|
||||
|
||||
if (mpv_command_node(ctx, node.node(), &res) < 0) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
node_autofree f(&res);
|
||||
return node_to_variant(&res);
|
||||
}
|
||||
@ -288,8 +351,10 @@ namespace mpv {
|
||||
* @return error code (<0) or success (>=0)
|
||||
*/
|
||||
static inline int get_error(const QVariant& v) {
|
||||
if (!v.canConvert<ErrorReturn>())
|
||||
if (!v.canConvert<ErrorReturn>()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return v.value<ErrorReturn>().error;
|
||||
}
|
||||
|
||||
@ -310,8 +375,11 @@ namespace mpv {
|
||||
static inline QVariant get_property(mpv_handle* ctx, const QString& name) {
|
||||
mpv_node node;
|
||||
int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node);
|
||||
if (err < 0)
|
||||
|
||||
if (err < 0) {
|
||||
return QVariant::fromValue(ErrorReturn(err));
|
||||
}
|
||||
|
||||
node_autofree f(&node);
|
||||
return node_to_variant(&node);
|
||||
}
|
||||
@ -336,8 +404,11 @@ namespace mpv {
|
||||
node_builder node(args);
|
||||
mpv_node res;
|
||||
int err = mpv_command_node(ctx, node.node(), &res);
|
||||
if (err < 0)
|
||||
|
||||
if (err < 0) {
|
||||
return QVariant::fromValue(ErrorReturn(err));
|
||||
}
|
||||
|
||||
node_autofree f(&res);
|
||||
return node_to_variant(&res);
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ class ServiceEntryPoint {
|
||||
// into the model. This method can for example display
|
||||
// some kind of first-time configuration dialog inside itself
|
||||
// before returning the root item.
|
||||
// Returns NULL if initialization of new root cannot be done.
|
||||
// Returns nullptr if initialization of new root cannot be done.
|
||||
virtual ServiceRoot* createNewRoot() const = 0;
|
||||
|
||||
// Performs initialization of all service accounts created using this entry
|
||||
|
Loading…
x
Reference in New Issue
Block a user