Skip to content

Commit

Permalink
Add bindings for QtMsgType, QMessageLogContext and qt_message_output
Browse files Browse the repository at this point in the history
This allows Rust and CXX-Qt applications to send messages to the Qt
logger.
  • Loading branch information
redstrate committed Feb 11, 2024
1 parent 4978415 commit c8364f2
Show file tree
Hide file tree
Showing 8 changed files with 244 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `internal_pointer_mut()` function on `QModelIndex`
- `c_void` in CXX-Qt-lib for easy access to `void *`
- `CxxQtThread` is now marked as `Sync` so that it can be used by reference
- Support for `QMessageLogContext` and sending log messages to the Qt message handler.

### Changed

Expand Down
38 changes: 38 additions & 0 deletions crates/cxx-qt-lib-headers/include/core/qtlogging.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// clang-format off
// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Joshua Goins <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once

#include <QtCore/QtLogging>
#include <QDebug>
#include "rust/cxx.h"

int qmessagelogcontext_line(const QMessageLogContext &context);

void qmessagelogcontext_set_line(QMessageLogContext &context, const int line);

const char *qmessagelogcontext_file(const QMessageLogContext &context);

void qmessagelogcontext_set_file(QMessageLogContext &context, const char *file);

const char *qmessagelogcontext_function(const QMessageLogContext &context);

void qmessagelogcontext_set_function(QMessageLogContext &context, const char *function);

const char *qmessagelogcontext_category(const QMessageLogContext &context);

void qmessagelogcontext_set_category(QMessageLogContext &context, const char *category);

// Define namespace otherwise we hit a GCC bug
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56480
namespace rust {

template<>
struct IsRelocatable<QMessageLogContext> : ::std::true_type
{
};

} // namespace rust
1 change: 1 addition & 0 deletions crates/cxx-qt-lib-headers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pub fn write_headers(directory: impl AsRef<Path>) {
(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"),
Expand Down
2 changes: 2 additions & 0 deletions crates/cxx-qt-lib/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ fn main() {
"core/qstringlist",
"core/qt",
"core/qtime",
"core/qtlogging",
"core/qurl",
"core/qvariant/mod",
"core/qvariant/qvariant_bool",
Expand Down Expand Up @@ -227,6 +228,7 @@ fn main() {
"core/qstring",
"core/qstringlist",
"core/qtime",
"core/qtlogging",
"core/qurl",
"core/qvariant/qvariant",
"core/qvector/qvector",
Expand Down
3 changes: 3 additions & 0 deletions crates/cxx-qt-lib/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ pub use qvariant::{QVariant, QVariantValue};
mod qvector;
pub use qvector::{QVector, QVectorElement};

mod qtlogging;
pub use qtlogging::{QMessageLogContext, QtMsgType, qt_message_output};

#[cxx::bridge]
mod ffi {
#[namespace = "rust::cxxqtlib1"]
Expand Down
48 changes: 48 additions & 0 deletions crates/cxx-qt-lib/src/core/qtlogging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// clang-format off
// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// clang-format on
// SPDX-FileContributor: Joshua Goins <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
#include "cxx-qt-lib/qtlogging.h"

#include "../assertion_utils.h"

// QMessageLogContext has three "const char*" members for line, category, etc
// https://codebrowser.dev/qt5/qtbase/src/corelib/global/qlogging.h.html#QMessageLogContext
assert_alignment_and_size(QMessageLogContext, alignof(intptr_t), sizeof(intptr_t) * 4);

static_assert(::std::is_trivially_copyable<QMessageLogContext>::value,
"QMessageLogContext must be trivially copyable");

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;
}
139 changes: 139 additions & 0 deletions crates/cxx-qt-lib/src/core/qtlogging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
// SPDX-FileCopyrightText: 2024 Klarälvdalens Datakonsult AB, a KDAB Group company <[email protected]>
// SPDX-FileContributor: Joshua Goins <[email protected]>
//
// SPDX-License-Identifier: MIT OR Apache-2.0
use cxx::{type_id, ExternType};
use std::ffi::c_char;
use std::ffi::CStr;
use std::marker::PhantomData;

#[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<'a> = crate::QMessageLogContext<'a>;
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<'static>;
}
}

#[repr(C)]
pub struct QMessageLogContext<'a> {
version: i32,
line: i32,
file: &'a *const c_char,
function: &'a *const c_char,
category: &'a *const c_char
}

impl Default for QMessageLogContext<'_> {
fn default() -> Self {
ffi::qmessagelogcontext_default()
}
}

impl<'a> QMessageLogContext<'a> {
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: &'a 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: &'a 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: &'a 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
};

12 changes: 12 additions & 0 deletions examples/cargo_without_cmake/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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();
Expand Down

0 comments on commit c8364f2

Please sign in to comment.