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 Aug 11, 2024
1 parent 816ba27 commit 7dc5dbb
Show file tree
Hide file tree
Showing 6 changed files with 268 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add cxx-qt-lib-extras crate which contains: `QCommandLineOption`, `QCommandLineParser`, `QElapsedTimer`, `QApplication`
- Serde support for `QString` (requires "serde" feature on cxx-qt-lib)
- A new QuickControls module, which exposes `QQuickStyle`. This module is enabled by default and is behind the `qt_quickcontrols` feature.
- Support for `QMessageLogContext` and sending log messages to the Qt message handler.

### Changed

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 @@ -136,6 +136,7 @@ fn main() {
"core/qstringlist",
"core/qt",
"core/qtime",
"core/qtlogging",
"core/qurl",
"core/qvariant/mod",
"core/qvariant/qvariant_bool",
Expand Down Expand Up @@ -254,6 +255,7 @@ fn main() {
"core/qstring",
"core/qstringlist",
"core/qtime",
"core/qtlogging",
"core/qurl",
"core/qvariant/qvariant",
"core/qvector/qvector",
Expand Down
48 changes: 48 additions & 0 deletions crates/cxx-qt-lib/include/core/qtlogging.h
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
#pragma once

#include "rust/cxx.h"
#include <QDebug>
#include <QtCore/qlogging.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
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::{qt_message_output, QMessageLogContext, QtMsgType};

#[cxx::bridge]
mod ffi {
#[namespace = "rust::cxxqtlib1"]
Expand Down
68 changes: 68 additions & 0 deletions crates/cxx-qt-lib/src/core/qtlogging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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;
}
146 changes: 146 additions & 0 deletions crates/cxx-qt-lib/src/core/qtlogging.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// 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 {
/// The level the message is sent to the message handler at.
#[repr(i32)]
enum QtMsgType {
/// A debug message.
QtDebugMsg = 0,
/// An info message.
QtInfoMsg = 4,
/// A warning message.
QtWarningMsg = 1,
/// A fatal message.
QtFatalMsg = 3,
/// A critical message.
QtCriticalMsg = 2,
}

unsafe extern "C++" {
include!("cxx-qt-lib/qstring.h");
type QString = crate::QString;

include!("cxx-qt-lib/qtlogging.h");
type QMessageLogContext<'a> = crate::QMessageLogContext<'a>;
type QtMsgType;

/// Outputs a message in the Qt message handler.
fn qt_message_output(msgType: QtMsgType, context: &QMessageLogContext, string: &QString);

#[cxx_name = "qmessagelogcontext_line"]
#[doc(hidden)]
fn line(context: &QMessageLogContext) -> i32;

#[cxx_name = "qmessagelogcontext_set_line"]
#[doc(hidden)]
fn set_line(context: &mut QMessageLogContext, line: i32);

#[cxx_name = "qmessagelogcontext_file"]
#[doc(hidden)]
unsafe fn file(context: &QMessageLogContext) -> *const c_char;

#[cxx_name = "qmessagelogcontext_set_file"]
#[doc(hidden)]
unsafe fn set_file(context: &mut QMessageLogContext, file: *const c_char);

#[cxx_name = "qmessagelogcontext_function"]
#[doc(hidden)]
unsafe fn function(context: &QMessageLogContext) -> *const c_char;

#[cxx_name = "qmessagelogcontext_set_function"]
#[doc(hidden)]
unsafe fn set_function(context: &mut QMessageLogContext, function: *const c_char);

#[cxx_name = "qmessagelogcontext_category"]
#[doc(hidden)]
unsafe fn category(context: &QMessageLogContext) -> *const c_char;

#[cxx_name = "qmessagelogcontext_set_category"]
#[doc(hidden)]
unsafe fn set_category(context: &mut QMessageLogContext, category: *const c_char);
}

#[namespace = "rust::cxxqtlib1"]
unsafe extern "C++" {
include!("cxx-qt-lib/common.h");

#[doc(hidden)]
#[rust_name = "qmessagelogcontext_default"]
fn construct() -> QMessageLogContext<'static>;
}
}

/// The QMessageLogContext struct defines the context passed to the Qt message handler.
#[repr(C)]
#[derive(Clone, Copy)]
pub struct QMessageLogContext<'a> {
version: i32,
line: i32,
file: *const c_char,
function: *const c_char,
category: *const c_char,
_phantom: PhantomData<&'a c_char>,
}

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

impl<'a> QMessageLogContext<'a> {
pub fn new(
line: i32,
file: &'a *const c_char,
function: &'a *const c_char,
category: &'a *const c_char,
) -> QMessageLogContext<'a> {
let mut context = QMessageLogContext::default();
unsafe {
ffi::set_line(&mut context, line);
ffi::set_file(&mut context, *file);
ffi::set_function(&mut context, *function);
ffi::set_category(&mut context, *category);
}

context
}

/// The line number given to the message handler.
pub fn line(&self) -> i32 {
ffi::line(self)
}

/// The file path given to the message handler.
pub fn file(&self) -> &CStr {
unsafe { CStr::from_ptr(ffi::file(self)) }
}

/// The name of the function given to the message handler.
pub fn function(&self) -> &CStr {
unsafe { CStr::from_ptr(ffi::function(self)) }
}

/// The category given to the message handler.
pub fn category(&self) -> &CStr {
unsafe { CStr::from_ptr(ffi::category(self)) }
}
}

// 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::{qt_message_output, QtMsgType};

0 comments on commit 7dc5dbb

Please sign in to comment.