From 5b47aee7acae1bb042fcdbcae8c6a244fb8fb142 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 17 Jan 2024 14:07:40 -0500 Subject: [PATCH] Add bindings for QtMsgType, QMessageLogContext and qt_message_output This allows Rust and CXX-Qt applications to send messages to the Qt logger. --- .../include/core/qtlogging.h | 54 +++++++ crates/cxx-qt-lib-headers/src/lib.rs | 1 + crates/cxx-qt-lib/build.rs | 1 + crates/cxx-qt-lib/src/core/mod.rs | 3 + crates/cxx-qt-lib/src/core/qtlogging.cpp | 7 + crates/cxx-qt-lib/src/core/qtlogging.rs | 138 ++++++++++++++++++ examples/cargo_without_cmake/src/main.rs | 12 ++ 7 files changed, 216 insertions(+) create mode 100644 crates/cxx-qt-lib-headers/include/core/qtlogging.h create mode 100644 crates/cxx-qt-lib/src/core/qtlogging.cpp create mode 100644 crates/cxx-qt-lib/src/core/qtlogging.rs diff --git a/crates/cxx-qt-lib-headers/include/core/qtlogging.h b/crates/cxx-qt-lib-headers/include/core/qtlogging.h new file mode 100644 index 000000000..98eefa642 --- /dev/null +++ b/crates/cxx-qt-lib-headers/include/core/qtlogging.h @@ -0,0 +1,54 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Joshua Goins +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#pragma once + +#include +#include +#include "rust/cxx.h" + +int qmessagelogcontext_line(const QMessageLogContext &context) { + return context.line; +} + +void qmessagelogcontext_set_line(QMessageLogContext &context, const int line) { + context.line = line; +} + +const char *qmessagelogcontext_file(const QMessageLogContext &context) { + return context.file; +} + +void qmessagelogcontext_set_file(QMessageLogContext &context, const char *file) { + context.file = file; +} + +const char *qmessagelogcontext_function(const QMessageLogContext &context) { + return context.function; +} + +void qmessagelogcontext_set_function(QMessageLogContext &context, const char *function) { + context.function = function; +} + +const char *qmessagelogcontext_category(const QMessageLogContext &context) { + return context.category; +} + +void qmessagelogcontext_set_category(QMessageLogContext &context, const char *category) { + context.category = category; +} + +// Define namespace otherwise we hit a GCC bug +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480 +namespace rust { + +template<> +struct IsRelocatable : ::std::true_type +{ +}; + +} // namespace rust \ No newline at end of file diff --git a/crates/cxx-qt-lib-headers/src/lib.rs b/crates/cxx-qt-lib-headers/src/lib.rs index 844cc9ec1..ae7f29e46 100644 --- a/crates/cxx-qt-lib-headers/src/lib.rs +++ b/crates/cxx-qt-lib-headers/src/lib.rs @@ -64,6 +64,7 @@ pub fn write_headers(directory: impl AsRef) { (include_str!("../include/core/qt.h"), "qt.h"), (include_str!("../include/core/qtime.h"), "qtime.h"), (include_str!("../include/core/qtimezone.h"), "qtimezone.h"), + (include_str!("../include/core/qtlogging.h"), "qtlogging.h"), (include_str!("../include/core/qurl.h"), "qurl.h"), (include_str!("../include/core/qvariant.h"), "qvariant.h"), (include_str!("../include/core/qvector.h"), "qvector.h"), diff --git a/crates/cxx-qt-lib/build.rs b/crates/cxx-qt-lib/build.rs index ff06b7f77..d20793d16 100644 --- a/crates/cxx-qt-lib/build.rs +++ b/crates/cxx-qt-lib/build.rs @@ -98,6 +98,7 @@ fn main() { "core/qstringlist", "core/qt", "core/qtime", + "core/qtlogging", "core/qurl", "core/qvariant/mod", "core/qvariant/qvariant_bool", diff --git a/crates/cxx-qt-lib/src/core/mod.rs b/crates/cxx-qt-lib/src/core/mod.rs index cd2d2e315..7c561584d 100644 --- a/crates/cxx-qt-lib/src/core/mod.rs +++ b/crates/cxx-qt-lib/src/core/mod.rs @@ -78,6 +78,9 @@ pub use qt::{ mod qtime; pub use qtime::QTime; +mod qtlogging; +pub use qtlogging::{QMessageLogContext, QtMsgType, qt_message_output}; + #[cfg(not(target_os = "emscripten"))] mod qtimezone; #[cfg(not(target_os = "emscripten"))] diff --git a/crates/cxx-qt-lib/src/core/qtlogging.cpp b/crates/cxx-qt-lib/src/core/qtlogging.cpp new file mode 100644 index 000000000..1bdd22d9e --- /dev/null +++ b/crates/cxx-qt-lib/src/core/qtlogging.cpp @@ -0,0 +1,7 @@ +// clang-format off +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// clang-format on +// SPDX-FileContributor: Joshua Goins +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +#include "cxx-qt-lib/qtlogging.h" diff --git a/crates/cxx-qt-lib/src/core/qtlogging.rs b/crates/cxx-qt-lib/src/core/qtlogging.rs new file mode 100644 index 000000000..b1b81ad24 --- /dev/null +++ b/crates/cxx-qt-lib/src/core/qtlogging.rs @@ -0,0 +1,138 @@ +// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company +// SPDX-FileContributor: Joshua Goins +// +// SPDX-License-Identifier: MIT OR Apache-2.0 +use cxx::{type_id, ExternType}; +use std::ffi::c_char; +use std::ffi::CStr; + +#[cxx::bridge] +mod ffi { + #[repr(i32)] + enum QtMsgType { + QtDebugMsg = 0, + QtInfoMsg = 4, + QtWarningMsg = 1, + QtFatalMsg = 3, + QtCriticalMsg = 2, + QtSystemMsg = 2 + } + + unsafe extern "C++" { + include!("cxx-qt-lib/common.h"); + + include!("cxx-qt-lib/qstring.h"); + type QString = crate::QString; + + include!("cxx-qt-lib/qtlogging.h"); + type QMessageLogContext = crate::QMessageLogContext; + type QtMsgType; + + fn qt_message_output(msgType: QtMsgType, context: &QMessageLogContext, string: &QString); + + #[cxx_name = "qmessagelogcontext_line"] + fn line(context: &QMessageLogContext) -> i32; + + #[cxx_name = "qmessagelogcontext_set_line"] + fn set_line(context: &mut QMessageLogContext, line: i32); + + #[cxx_name = "qmessagelogcontext_file"] + unsafe fn file(context: &QMessageLogContext) -> *const c_char; + + #[cxx_name = "qmessagelogcontext_set_file"] + unsafe fn set_file(context: &mut QMessageLogContext, file: *const c_char); + + #[cxx_name = "qmessagelogcontext_function"] + unsafe fn function(context: &QMessageLogContext) -> *const c_char; + + #[cxx_name = "qmessagelogcontext_set_function"] + unsafe fn set_function(context: &mut QMessageLogContext, function: *const c_char); + + #[cxx_name = "qmessagelogcontext_category"] + unsafe fn category(context: &QMessageLogContext) -> *const c_char; + + #[cxx_name = "qmessagelogcontext_set_category"] + unsafe fn set_category(context: &mut QMessageLogContext, category: *const c_char); + } + + #[namespace = "rust::cxxqtlib1"] + unsafe extern "C++" { + #[doc(hidden)] + #[rust_name = "qmessagelogcontext_default"] + fn construct() -> QMessageLogContext; + } +} + +#[repr(C)] +pub struct QMessageLogContext { + version: i32, + line: i32, + file: *const c_char, + function: *const c_char, + category: *const c_char +} + +impl Default for QMessageLogContext { + fn default() -> Self { + ffi::qmessagelogcontext_default() + } +} + +impl QMessageLogContext { + pub fn line(&self) -> i32 { + ffi::line(&self) + } + + pub fn set_line(&mut self, line: i32) { + ffi::set_line(self, line); + } + + pub fn file(&self) -> &CStr { + unsafe { + CStr::from_ptr(ffi::file(self)) + } + } + + pub fn set_file(&mut self, file: &CStr) { + unsafe { + ffi::set_file(self, file.as_ptr()); + } + } + + pub fn function(&self) -> &CStr { + unsafe { + CStr::from_ptr(ffi::function(self)) + } + } + + pub fn set_function(&mut self, function: &CStr) { + unsafe { + ffi::set_function(self, function.as_ptr()); + } + } + + pub fn category(&self) -> &CStr { + unsafe { + CStr::from_ptr(ffi::category(self)) + } + } + + pub fn set_category(&mut self, category: &CStr) { + unsafe { + ffi::set_category(self, category.as_ptr()); + } + } +} + +// Safety: +// +// Static checks on the C++ side ensure that QMessageLogContext is trivial. +unsafe impl ExternType for QMessageLogContext { + type Id = type_id!("QMessageLogContext"); + type Kind = cxx::kind::Trivial; +} + +pub use ffi::{ + QtMsgType, qt_message_output +}; + diff --git a/examples/cargo_without_cmake/src/main.rs b/examples/cargo_without_cmake/src/main.rs index 0c7ebd1ba..63e2b2b0e 100644 --- a/examples/cargo_without_cmake/src/main.rs +++ b/examples/cargo_without_cmake/src/main.rs @@ -15,6 +15,10 @@ pub mod cxxqt_object; use cxx_qt_lib::{QGuiApplication, QQmlApplicationEngine, QUrl}; +use cxx_qt_lib::{QMessageLogContext, QtMsgType, qt_message_output}; +use cxx_qt_lib::QString; +use std::ffi::CString; + // ANCHOR_END: book_cargo_imports // ANCHOR: book_cargo_rust_main @@ -28,6 +32,14 @@ fn main() { engine.load(&QUrl::from("qrc:/qt/qml/com/kdab/cxx_qt/demo/qml/main.qml")); } + let str = CString::new("test").unwrap(); + + let mut context: QMessageLogContext = QMessageLogContext::default(); + context.set_line(5); + context.set_file(&str); + + qt_message_output(QtMsgType::QtInfoMsg, &context, &QString::from("Test!")); + // Start the app if let Some(app) = app.as_mut() { app.exec();