Files
RedBear-OS/local/recipes/qt/qtdeclarative/source/src/quicktemplates/qquicknativemenuitem.cpp
T
vasilito f31522130f fix: comprehensive boot warnings and exceptions — fixable silenced, unfixable diagnosed
Build system (5 gaps hardened):
- COOKBOOK_OFFLINE defaults to true (fork-mode)
- normalize_patch handles diff -ruN format
- New 'repo validate-patches' command (25/25 relibc patches)
- 14 patched Qt/Wayland/display recipes added to protected list
- relibc archive regenerated with current patch chain

Boot fixes (fixable):
- Full ISO EFI partition: 16 MiB → 1 MiB (matches mini, BIOS hardcoded 2 MiB offset)
- D-Bus system bus: absolute /usr/bin/dbus-daemon path (was skipped)
- redbear-sessiond: absolute /usr/bin/redbear-sessiond path (was skipped)
- daemon framework: silenced spurious INIT_NOTIFY warnings for oneshot_async services (P0-daemon-silence-init-notify.patch)
- udev-shim: demoted INIT_NOTIFY warning to INFO (expected for oneshot_async)
- relibc: comprehensive named semaphores (sem_open/close/unlink) replacing upstream todo!() stubs
- greeterd: Wayland socket timeout 15s → 30s (compositor DRM wait)
- greeter-ui: built and linked (header guard unification, sem_compat stubs removed)
- mc: un-ignored in both configs, fixed glib/libiconv/pcre2 transitive deps
- greeter config: removed stale keymapd dependency from display/greeter services
- prefix toolchain: relibc headers synced, _RELIBC_STDLIB_H guard unified

Unfixable (diagnosed, upstream):
- i2c-hidd: abort on no-I2C-hardware (QEMU) — process::exit → relibc abort
- kded6/greeter-ui: page fault 0x8 — Qt library null deref
- Thread panics fd != -1 — Rust std library on Redox
- DHCP timeout / eth0 MAC — QEMU user-mode networking
- hwrngd/thermald — no hardware RNG/thermal in VM
- live preload allocation — BIOS memory fragmentation, continues on demand
2026-05-05 20:20:37 +01:00

341 lines
13 KiB
C++

// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include "qquickmenuitem_p.h"
#include <QtCore/qloggingcategory.h>
//#include <QtGui/qicon.h>
//#if QT_CONFIG(shortcut)
//#include <QtGui/qkeysequence.h>
//#endif
#include <QtGui/qpa/qplatformmenu.h>
#include <QtGui/qpa/qplatformtheme.h>
#include <QtGui/private/qguiapplication_p.h>
//#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
#include <QtQuickTemplates2/private/qquickabstractbutton_p_p.h>
#include <QtQuickTemplates2/private/qquickaction_p.h>
#include <QtQuickTemplates2/private/qquickmenu_p_p.h>
#include <QtQuickTemplates2/private/qquickmenuseparator_p.h>
#include <QtQuickTemplates2/private/qquicknativeiconloader_p.h>
#include <QtQuickTemplates2/private/qquicknativemenuitem_p.h>
#include <QtQuickTemplates2/private/qquickshortcutcontext_p_p.h>
QT_BEGIN_NAMESPACE
Q_STATIC_LOGGING_CATEGORY(lcNativeMenuItem, "qt.quick.controls.nativemenuitem")
/*!
\class QQuickNativeMenuItem
\brief A native menu item.
\since 6.7
\internal
Creates a native menu item from an Action/MenuItem/Menu,
and syncs the properties and signals. It can also represent a
MenuSeparator.
\sa Menu, Action
*/
QQuickNativeMenuItem *QQuickNativeMenuItem::createFromNonNativeItem(
QQuickMenu *parentMenu, QQuickItem *nonNativeItem)
{
auto *menuItem = qobject_cast<QQuickMenuItem *>(nonNativeItem);
Type type = Type::Unknown;
if (menuItem) {
if (menuItem->action()) {
type = Type::Action;
} else if (menuItem->subMenu()) {
type = Type::SubMenu;
} else {
// It's a plain MenuItem, rather than a MenuItem created by us for an Action or Menu.
type = Type::MenuItem;
}
} else if (qobject_cast<QQuickMenuSeparator *>(nonNativeItem)) {
type = Type::Separator;
}
std::unique_ptr<QQuickNativeMenuItem> nativeMenuItemPtr(new QQuickNativeMenuItem(
parentMenu, nonNativeItem, type));
if (type == Type::Unknown) {
// It's not a Menu/Action/MenuSeparator that we're dealing with, but we still need
// to create the QQuickNativeMenu item for it so that our container has the same
// indices as the menu's contentModel.
return nativeMenuItemPtr.release();
}
qCDebug(lcNativeMenuItem) << "attemping to create native menu item for"
<< nativeMenuItemPtr->debugText();
auto *parentMenuPrivate = QQuickMenuPrivate::get(parentMenu);
nativeMenuItemPtr->m_handle.reset(parentMenuPrivate->handle->createMenuItem());
if (!nativeMenuItemPtr->m_handle)
nativeMenuItemPtr->m_handle.reset(QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem());
if (!nativeMenuItemPtr->m_handle)
return nullptr;
auto *nativeMenuItem = nativeMenuItemPtr.release();
switch (type) {
case Type::Action:
// Ensure that the action is triggered when the user clicks on a native menu item.
connect(nativeMenuItem->m_handle.get(), &QPlatformMenuItem::activated,
nativeMenuItem->action(), [nativeMenuItem, parentMenu](){
nativeMenuItem->action()->trigger(parentMenu);
});
// Handle programmatic changes in the Action.
connect(nativeMenuItem->action(), &QQuickAction::textChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(nativeMenuItem->action(), &QQuickAction::iconChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(nativeMenuItem->action(), &QQuickAction::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(nativeMenuItem->action(), &QQuickAction::checkedChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(nativeMenuItem->action(), &QQuickAction::checkableChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
break;
case Type::SubMenu:
nativeMenuItem->m_handle->setMenu(QQuickMenuPrivate::get(
nativeMenuItem->subMenu())->handle.get());
// Handle programmatic changes in the Menu.
connect(nativeMenuItem->subMenu(), &QQuickMenu::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(nativeMenuItem->subMenu(), &QQuickMenu::titleChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
break;
case Type::MenuItem:
// Ensure that the MenuItem is clicked when the user clicks on a native menu item.
connect(nativeMenuItem->m_handle.get(), &QPlatformMenuItem::activated,
menuItem, [menuItem](){
if (menuItem->isCheckable()) {
// This changes the checked state, which we need when syncing but also to ensure that
// the user can still use MenuItem's API even though they can't actually interact with it.
menuItem->toggle();
}
// The same applies here: allow users to respond to the MenuItem's clicked signal.
QQuickAbstractButtonPrivate::get(menuItem)->click();
});
// Handle programmatic changes in the MenuItem.
connect(menuItem, &QQuickMenuItem::textChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(menuItem, &QQuickMenuItem::iconChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(menuItem, &QQuickMenuItem::enabledChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(menuItem, &QQuickMenuItem::checkedChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
connect(menuItem, &QQuickMenuItem::checkableChanged, nativeMenuItem, &QQuickNativeMenuItem::sync);
break;
case Type::Separator:
case Type::Unknown:
break;
}
return nativeMenuItem;
}
QQuickNativeMenuItem::QQuickNativeMenuItem(QQuickMenu *parentMenu, QQuickItem *nonNativeItem,
QQuickNativeMenuItem::Type type)
: QObject(parentMenu)
, m_parentMenu(parentMenu)
, m_nonNativeItem(nonNativeItem)
, m_type(type)
{
}
QQuickNativeMenuItem::~QQuickNativeMenuItem()
{
qCDebug(lcNativeMenuItem) << "destroying" << this << debugText();
}
QQuickAction *QQuickNativeMenuItem::action() const
{
return m_type == Type::Action ? qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->action() : nullptr;
}
QQuickMenu *QQuickNativeMenuItem::subMenu() const
{
return m_type == Type::SubMenu ? qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->subMenu() : nullptr;
}
QQuickMenuSeparator *QQuickNativeMenuItem::separator() const
{
return m_type == Type::Separator ? qobject_cast<QQuickMenuSeparator *>(m_nonNativeItem) : nullptr;
}
QPlatformMenuItem *QQuickNativeMenuItem::handle() const
{
return m_handle.get();
}
void QQuickNativeMenuItem::sync()
{
if (m_type == Type::Unknown)
return;
if (m_syncing)
return;
QScopedValueRollback recursionGuard(m_syncing, true);
const auto *action = this->action();
const auto *separator = this->separator();
auto *subMenu = this->subMenu();
auto *menuItem = qobject_cast<QQuickMenuItem *>(m_nonNativeItem);
// Store the values in variables so that we can use it in the debug output below.
const bool enabled = action ? action->isEnabled()
: subMenu ? subMenu->isEnabled()
: menuItem && menuItem->isEnabled();
m_handle->setEnabled(enabled);
// m_handle->setVisible(isVisible());
const bool isSeparator = separator != nullptr;
m_handle->setIsSeparator(isSeparator);
const bool checkable = action ? action->isCheckable() : menuItem && menuItem->isCheckable();
m_handle->setCheckable(checkable);
const bool checked = action ? action->isChecked() : menuItem && menuItem->isChecked();
m_handle->setChecked(checked);
m_handle->setRole(QPlatformMenuItem::TextHeuristicRole);
const QString text = action ? action->text()
: subMenu ? subMenu->title()
: menuItem ? menuItem->text() : QString();
m_handle->setText(text);
// m_handle->setFont(m_font);
// m_handle->setHasExclusiveGroup(m_group && m_group->isExclusive());
m_handle->setHasExclusiveGroup(false);
const QQuickIcon icon = effectiveIcon();
const auto *menuPrivate = QQuickMenuPrivate::get(m_parentMenu);
// We should reload the icon if the window's DPR has changed, regardless if its properties have changed.
// We can't check for ItemDevicePixelRatioHasChanged in QQuickMenu::itemChange,
// because that isn't sent when the menu isn't visible, and will never
// be sent for native menus. We instead store lastDevicePixelRatio in QQuickMenu
// (to avoid storing it for each menu item) and set it whenever it's opened.
bool dprChanged = false;
if (!qGuiApp->topLevelWindows().isEmpty()) {
const auto *window = qGuiApp->topLevelWindows().first();
dprChanged = !qFuzzyCompare(window->devicePixelRatio(), menuPrivate->lastDevicePixelRatio);
}
if (!icon.isEmpty() && (icon != iconLoader()->icon() || dprChanged)) {
// This will load the icon, which will call sync() recursively, hence the m_syncing check.
reloadIcon();
}
if (subMenu) {
// Sync first as dynamically created menus may need to get the handle recreated.
auto *subMenuPrivate = QQuickMenuPrivate::get(subMenu);
subMenuPrivate->syncWithNativeMenu();
if (subMenuPrivate->handle)
m_handle->setMenu(subMenuPrivate->handle.get());
}
#if QT_CONFIG(shortcut)
if (action)
m_handle->setShortcut(action->shortcut());
#endif
if (m_parentMenu) {
auto *menuPrivate = QQuickMenuPrivate::get(m_parentMenu);
if (menuPrivate->handle)
menuPrivate->handle->syncMenuItem(m_handle.get());
}
qCDebug(lcNativeMenuItem) << "sync called on" << debugText() << "handle" << m_handle.get()
<< "enabled:" << enabled << "isSeparator" << isSeparator << "checkable" << checkable
<< "checked" << checked << "text" << text;
}
QQuickIcon QQuickNativeMenuItem::effectiveIcon() const
{
if (const auto *action = this->action())
return action->icon();
if (const auto *subMenu = this->subMenu())
return subMenu->icon();
if (const auto *menuItem = qobject_cast<QQuickMenuItem *>(m_nonNativeItem))
return menuItem->icon();
return {};
}
QQuickNativeIconLoader *QQuickNativeMenuItem::iconLoader() const
{
if (!m_iconLoader) {
QQuickNativeMenuItem *that = const_cast<QQuickNativeMenuItem *>(this);
static int slot = staticMetaObject.indexOfSlot("updateIcon()");
m_iconLoader = new QQuickNativeIconLoader(slot, that);
// Qt Labs Platform's QQuickMenuItem would call m_iconLoader->setEnabled(m_complete) here,
// but since QQuickMenuPrivate::maybeCreateAndInsertNativeItem asserts that the menu's
// completed loading, we can just set it to true.
m_iconLoader->setEnabled(true);
}
return m_iconLoader;
}
void QQuickNativeMenuItem::reloadIcon()
{
iconLoader()->setIcon(effectiveIcon());
m_handle->setIcon(iconLoader()->toQIcon());
}
void QQuickNativeMenuItem::updateIcon()
{
sync();
}
void QQuickNativeMenuItem::addShortcut()
{
#if QT_CONFIG(shortcut)
const auto *action = this->action();
const QKeySequence sequence = action ? action->shortcut() : QKeySequence();
if (!sequence.isEmpty() && action->isEnabled()) {
m_shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, sequence,
Qt::WindowShortcut, QQuickShortcutContext::matcher);
} else {
m_shortcutId = -1;
}
#endif
}
void QQuickNativeMenuItem::removeShortcut()
{
#if QT_CONFIG(shortcut)
if (m_shortcutId == -1)
return;
QKeySequence sequence;
switch (m_type) {
case Type::Action:
sequence = action()->shortcut();
break;
default:
// TODO
break;
}
QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(m_shortcutId, this, sequence);
#endif
}
QString QQuickNativeMenuItem::debugText() const
{
// Prepend below to avoid extra lines from break statements.
QString text = QDebug::toString(m_handle.get());
switch (m_type) {
case Type::Action:
return text.prepend(QString::fromLatin1("Action(text = %1) ").arg(action()->text()));
case Type::SubMenu:
return text.prepend(QString::fromLatin1("Sub-menu(title = %1) ").arg(subMenu()->title()));
case Type::MenuItem:
return text.prepend(QString::fromLatin1("MenuItem(text = %1) ").arg(
qobject_cast<QQuickMenuItem *>(m_nonNativeItem)->text()));
case Type::Separator:
return text.prepend(QStringLiteral("Separator "));
case Type::Unknown:
return text.prepend(QStringLiteral("(Unknown) "));
}
Q_UNREACHABLE();
}
QT_END_NAMESPACE
#include "moc_qquicknativemenuitem_p.cpp"