Move shared code to a new crate winit-common

This commit is contained in:
Mads Marquart 2025-05-25 13:41:28 +02:00 committed by GitHub
parent 3b986f5583
commit 0adc0898f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 131 additions and 68 deletions

6
.github/CODEOWNERS vendored
View file

@ -5,9 +5,11 @@
/src/platform/ios.rs @madsmtm
/src/platform/macos.rs @madsmtm
/src/platform_impl/apple @madsmtm
/winit-common/src/core_foundation @madsmtm
/winit-common/src/event_handler.rs @madsmtm
# Unix
/src/platform_impl/linux/mod.rs @kchibisov
# XKB
/winit-common/src/xkb @kchibisov
# Wayland
/src/platform/wayland.rs @kchibisov

View file

@ -184,7 +184,18 @@ jobs:
- name: Test winit Android
if: contains(matrix.platform.target, 'android')
run: cargo $CMD test -p winit-android --target=${{ matrix.platform.target }} --features native-activity --no-run
run: cargo $CMD test -p winit-android --features native-activity --no-run
- name: Test winit Common (EventHandler)
run: cargo $CMD test -p winit-common --features event-handler --no-run
- name: Test winit Common (CF)
if: contains(matrix.platform.target, 'apple')
run: cargo $CMD test -p winit-common --features core-foundation --no-run
- name: Test winit Common (XKB)
if: contains(matrix.platform.target, 'linux')
run: cargo $CMD test -p winit-common --features xkb,x11,wayland --no-run
- name: Test winit Orbital
if: contains(matrix.platform.target, 'redox')

View file

@ -13,6 +13,7 @@ rust-version = "1.80"
# `winit` has no version here to allow using it in dev deps for docs.
winit = { path = "." }
winit-android = { version = "0.0.0", path = "winit-android" }
winit-common = { version = "0.0.0", path = "winit-common" }
winit-core = { version = "0.0.0", path = "winit-core" }
winit-orbital = { version = "0.0.0", path = "winit-orbital" }
winit-win32 = { version = "0.0.0", path = "winit-win32" }
@ -172,12 +173,13 @@ wayland = [
"sctk",
"ahash",
"memmap2",
"winit-common/wayland",
]
wayland-csd-adwaita = ["sctk-adwaita", "sctk-adwaita/ab_glyph"]
wayland-csd-adwaita-crossfont = ["sctk-adwaita", "sctk-adwaita/crossfont"]
wayland-csd-adwaita-notitle = ["sctk-adwaita"]
wayland-dlopen = ["wayland-backend/dlopen"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb"]
x11 = ["x11-dl", "bytemuck", "percent-encoding", "xkbcommon-dl/x11", "x11rb", "winit-common/x11"]
[build-dependencies]
cfg_aliases.workspace = true
@ -208,6 +210,7 @@ winit-android.workspace = true
block2.workspace = true
dispatch2.workspace = true
objc2.workspace = true
winit-common = { workspace = true, features = ["core-foundation", "event-handler"] }
# AppKit
[target.'cfg(target_os = "macos")'.dependencies]
@ -358,6 +361,7 @@ wayland-backend = { workspace = true, optional = true }
wayland-client = { workspace = true, optional = true }
wayland-protocols = { workspace = true, optional = true }
wayland-protocols-plasma = { workspace = true, optional = true }
winit-common = { workspace = true, features = ["xkb"] }
x11-dl = { workspace = true, optional = true }
x11rb = { workspace = true, optional = true, features = [
"allow-unsafe-code",

View file

@ -8,13 +8,13 @@ use dispatch2::MainThreadBound;
use objc2::MainThreadMarker;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSRunningApplication};
use objc2_foundation::NSNotification;
use winit_common::core_foundation::EventLoopProxy;
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
use winit_core::event::{StartCause, WindowEvent};
use winit_core::event_loop::ControlFlow;
use winit_core::window::WindowId;
use super::super::event_handler::EventHandler;
use super::super::event_loop_proxy::EventLoopProxy;
use super::event_loop::{notify_windows_of_exit, stop_app_immediately, ActiveEventLoop};
use super::menu;
use super::observer::{EventLoopWaker, RunLoop};

View file

@ -22,12 +22,12 @@ use winit_core::event_loop::{
use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
use winit_core::window::Theme;
use super::super::notification_center::create_observer;
use super::app::override_send_event;
use super::app_state::AppState;
use super::cursor::CustomCursor;
use super::event::dummy_event;
use super::monitor;
use super::notification_center::create_observer;
use super::observer::setup_control_flow_observers;
use crate::platform::macos::ActivationPolicy;
use crate::platform_impl::Window;

View file

@ -9,6 +9,7 @@ mod event_loop;
mod ffi;
mod menu;
mod monitor;
mod notification_center;
mod observer;
mod view;
mod window;

View file

@ -1,3 +1,4 @@
// NOTE: This is symlinked to be contained in both the AppKit and UIKit implementations.
use std::ptr::NonNull;
use block2::RcBlock;
@ -11,7 +12,7 @@ use objc2_foundation::{
///
/// This is used in Winit as an alternative to declaring an application delegate, as we want to
/// give the user full control over those.
pub fn create_observer(
pub(crate) fn create_observer(
center: &NSNotificationCenter,
name: &NSNotificationName,
handler: impl Fn(&NSNotification) + 'static,

View file

@ -1,10 +1,5 @@
//! Apple/Darwin-specific implementations
#[cfg(target_os = "macos")]
mod appkit;
mod event_handler;
mod event_loop_proxy;
mod notification_center;
#[cfg(not(target_os = "macos"))]
mod uikit;

View file

@ -16,13 +16,13 @@ use objc2_core_foundation::{
CGSize,
};
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView};
use winit_common::core_foundation::EventLoopProxy;
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
use winit_core::event::{StartCause, SurfaceSizeWriter, WindowEvent};
use winit_core::event_loop::ControlFlow;
use winit_core::window::WindowId;
use super::super::event_handler::EventHandler;
use super::super::event_loop_proxy::EventLoopProxy;
use super::window::WinitUIWindow;
use super::ActiveEventLoop;

View file

@ -26,8 +26,8 @@ use winit_core::event_loop::{
use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
use winit_core::window::{Theme, Window as CoreWindow};
use super::super::notification_center::create_observer;
use super::app_state::{send_occluded_event_for_all_windows, AppState};
use super::notification_center::create_observer;
use super::{app_state, monitor, MonitorHandle};
use crate::platform_impl::Window;

View file

@ -3,6 +3,7 @@
mod app_state;
mod event_loop;
mod monitor;
mod notification_center;
mod view;
mod view_controller;
mod window;

View file

@ -0,0 +1 @@
../appkit/notification_center.rs

View file

@ -1 +0,0 @@
pub mod xkb;

View file

@ -9,17 +9,16 @@ use std::time::Duration;
#[cfg(x11_platform)]
use dpi::Size;
pub(crate) use winit_common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
use winit_core::application::ApplicationHandler;
use winit_core::error::{EventLoopError, NotSupportedError};
use winit_core::event_loop::pump_events::PumpStatus;
use winit_core::event_loop::ActiveEventLoop;
use winit_core::window::ActivationToken;
pub(crate) use self::common::xkb::{physicalkey_to_scancode, scancode_to_physicalkey};
#[cfg(x11_platform)]
use crate::platform::x11::WindowType as XWindowType;
pub(crate) mod common;
#[cfg(wayland_platform)]
pub(crate) mod wayland;
#[cfg(x11_platform)]

View file

@ -11,10 +11,10 @@ use sctk::reexports::client::protocol::wl_keyboard::{
use sctk::reexports::client::protocol::wl_seat::WlSeat;
use sctk::reexports::client::{Connection, Dispatch, Proxy, QueueHandle, WEnum};
use tracing::warn;
use winit_common::xkb::Context;
use winit_core::event::{ElementState, WindowEvent};
use winit_core::keyboard::ModifiersState;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::wayland::event_loop::sink::EventSink;
use crate::platform_impl::wayland::state::WinitState;
use crate::platform_impl::wayland::{self, WindowId};

View file

@ -311,7 +311,7 @@ impl CoreWindow for Window {
}
fn reset_dead_keys(&self) {
crate::platform_impl::common::xkb::reset_dead_keys()
winit_common::xkb::reset_dead_keys()
}
fn surface_position(&self) -> PhysicalPosition<i32> {

View file

@ -5,6 +5,7 @@ use std::slice;
use std::sync::{Arc, Mutex};
use dpi::{PhysicalPosition, PhysicalSize};
use winit_common::xkb::{self, Context, XkbState};
use winit_core::application::ApplicationHandler;
use winit_core::event::{
ButtonSource, DeviceEvent, DeviceId, ElementState, FingerId, Ime, MouseButton,
@ -28,8 +29,6 @@ use x11rb::protocol::xproto::{self, ConnectionExt as _, ModMask};
use x11rb::x11_utils::{ExtensionInformation, Serialize};
use xkbcommon_dl::xkb_mod_mask_t;
use crate::platform_impl::common::xkb::{self, XkbState};
use crate::platform_impl::platform::common::xkb::Context;
use crate::platform_impl::platform::x11::ime::{ImeEvent, ImeEventReceiver, ImeRequest};
use crate::platform_impl::platform::x11::ActiveEventLoop;
use crate::platform_impl::x11::atoms::*;

View file

@ -15,6 +15,7 @@ use calloop::ping::Ping;
use calloop::{EventLoop as Loop, Readiness};
use libc::{setlocale, LC_CTYPE};
use tracing::warn;
use winit_common::xkb::Context;
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, RequestError};
@ -35,7 +36,6 @@ use x11rb::x11_utils::X11Error as LogicalError;
use x11rb::xcb_ffi::ReplyOrIdError;
use crate::platform::x11::XlibErrorHook;
use crate::platform_impl::common::xkb::Context;
use crate::platform_impl::platform::min_timeout;
use crate::platform_impl::x11::window::Window;

View file

@ -37,7 +37,6 @@ use super::{
XConnection,
};
use crate::platform::x11::{WindowAttributesX11, WindowType};
use crate::platform_impl::common;
use crate::platform_impl::x11::atoms::*;
use crate::platform_impl::x11::util::rgba_to_cardinals;
use crate::platform_impl::x11::{
@ -85,7 +84,7 @@ impl CoreWindow for Window {
}
fn reset_dead_keys(&self) {
common::xkb::reset_dead_keys();
winit_common::xkb::reset_dead_keys();
}
fn surface_position(&self) -> PhysicalPosition<i32> {

42
winit-common/Cargo.toml Normal file
View file

@ -0,0 +1,42 @@
[package]
description = "Winit implementation helpers"
documentation = "https://docs.rs/winit-common"
edition.workspace = true
license.workspace = true
name = "winit-common"
repository.workspace = true
rust-version.workspace = true
version = "0.0.0"
[features]
# Event Handler
event-handler = []
# XKB
wayland = ["dep:memmap2"]
x11 = ["xkbcommon-dl?/x11", "dep:x11-dl"]
xkb = ["dep:xkbcommon-dl", "dep:smol_str"]
# CoreFoundation
core-foundation = ["dep:objc2", "dep:objc2-core-foundation"]
[dependencies]
smol_str = { workspace = true, optional = true }
tracing.workspace = true
winit-core.workspace = true
# XKB
memmap2 = { workspace = true, optional = true }
x11-dl = { workspace = true, optional = true }
xkbcommon-dl = { workspace = true, optional = true }
# CoreFoundation
objc2 = { workspace = true, optional = true }
objc2-core-foundation = { workspace = true, optional = true, features = [
"std",
"CFRunLoop",
"CFString",
] }
[package.metadata.docs.rs]
all-features = true

1
winit-common/README.md Symbolic link
View file

@ -0,0 +1 @@
../README.md

View file

@ -14,7 +14,7 @@ use winit_core::event_loop::EventLoopProxyProvider;
///
/// See <https://developer.apple.com/documentation/corefoundation/cfrunloopsource?language=objc>.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub(crate) struct EventLoopProxy {
pub struct EventLoopProxy {
source: CFRetained<CFRunLoopSource>,
/// Cached value of `CFRunLoopGetMain`.
main_loop: CFRetained<CFRunLoop>,
@ -28,7 +28,7 @@ impl EventLoopProxy {
/// Create a new proxy, registering it to be performed on the main thread.
///
/// The provided closure should call `proxy_wake_up` on the application.
pub(crate) fn new<F: Fn() + 'static>(mtm: MainThreadMarker, signaller: F) -> Self {
pub fn new<F: Fn() + 'static>(mtm: MainThreadMarker, signaller: F) -> Self {
// We use an `Arc` here to make sure that the reference-counting of the signal container is
// atomic (`Retained`/`CFRetained` would be valid alternatives too).
let signaller = Arc::new(signaller);
@ -99,8 +99,7 @@ impl EventLoopProxy {
// FIXME(madsmtm): Use this on macOS too.
// More difficult there, since the user can re-start the event loop.
#[cfg_attr(target_os = "macos", allow(dead_code))]
pub(crate) fn invalidate(&self) {
pub fn invalidate(&self) {
// NOTE: We do NOT fire this on `Drop`, since we want the proxy to be cloneable, such that
// we only need to register a single source even if there's multiple proxies in use.
self.source.invalidate();

View file

@ -0,0 +1,3 @@
mod event_loop_proxy;
pub use event_loop_proxy::*;

View file

@ -6,7 +6,7 @@ use winit_core::application::ApplicationHandler;
/// A helper type for storing a reference to `ApplicationHandler`, allowing interior mutable access
/// to it within the execution of a closure.
#[derive(Default)]
pub(crate) struct EventHandler {
pub struct EventHandler {
/// This can be in the following states:
/// - Not registered by the event loop, or terminated (None).
/// - Present (Some(handler)).
@ -26,7 +26,7 @@ impl fmt::Debug for EventHandler {
}
impl EventHandler {
pub(crate) fn new() -> Self {
pub fn new() -> Self {
Self { inner: RefCell::new(None) }
}
@ -35,7 +35,7 @@ impl EventHandler {
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
/// to store the handler in a thread local, such that it can be accessed
/// from within the closure.
pub(crate) fn set<'handler, R>(
pub fn set<'handler, R>(
&self,
app: Box<dyn ApplicationHandler + 'handler>,
closure: impl FnOnce() -> R,
@ -104,16 +104,15 @@ impl EventHandler {
// soundness.
}
#[cfg(target_os = "macos")]
pub(crate) fn in_use(&self) -> bool {
pub fn in_use(&self) -> bool {
self.inner.try_borrow().is_err()
}
pub(crate) fn ready(&self) -> bool {
pub fn ready(&self) -> bool {
matches!(self.inner.try_borrow().as_deref(), Ok(Some(_)))
}
pub(crate) fn handle(&self, callback: impl FnOnce(&mut (dyn ApplicationHandler + '_))) {
pub fn handle(&self, callback: impl FnOnce(&mut (dyn ApplicationHandler + '_))) {
match self.inner.try_borrow_mut().as_deref_mut() {
Ok(Some(ref mut user_app)) => {
// It is important that we keep the reference borrowed here,
@ -137,7 +136,7 @@ impl EventHandler {
}
}
pub(crate) fn terminate(&self) {
pub fn terminate(&self) {
match self.inner.try_borrow_mut().as_deref_mut() {
Ok(data @ Some(_)) => {
let handler = data.take();

8
winit-common/src/lib.rs Normal file
View file

@ -0,0 +1,8 @@
//! Winit implementation helpers.
#[cfg(feature = "core-foundation")]
pub mod core_foundation;
#[cfg(feature = "event-handler")]
pub mod event_handler;
#[cfg(feature = "xkb")]
pub mod xkb;

View file

@ -7,19 +7,19 @@ use std::ptr::{self, NonNull};
use winit_core::keyboard::{
Key, KeyCode, KeyLocation, NamedKey, NativeKey, NativeKeyCode, PhysicalKey,
};
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
use x11_dl::xlib_xcb::xcb_connection_t;
use xkb::XKB_MOD_INVALID;
use xkbcommon_dl::{
self as xkb, xkb_keycode_t, xkb_keymap, xkb_keymap_compile_flags, xkb_keysym_t,
xkb_layout_index_t, xkb_mod_index_t,
};
#[cfg(wayland_platform)]
#[cfg(feature = "wayland")]
use {memmap2::MmapOptions, std::os::unix::io::OwnedFd};
#[cfg(x11_platform)]
use crate::platform_impl::common::xkb::XKBXH;
use crate::platform_impl::common::xkb::{XkbContext, XKBH};
#[cfg(feature = "x11")]
use super::XKBXH;
use super::{XkbContext, XKBH};
/// Map the raw X11-style keycode to the `KeyCode` enum.
///
@ -947,7 +947,7 @@ pub struct XkbKeymap {
}
impl XkbKeymap {
#[cfg(wayland_platform)]
#[cfg(feature = "wayland")]
pub fn from_fd(context: &XkbContext, fd: OwnedFd, size: usize) -> Option<Self> {
let map = unsafe { MmapOptions::new().len(size).map_copy_read_only(&fd).ok()? };
@ -964,7 +964,7 @@ impl XkbKeymap {
Some(Self::new_inner(keymap, 0))
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn from_x11_keymap(
context: &XkbContext,
xcb: *mut xcb_connection_t,
@ -997,7 +997,7 @@ impl XkbKeymap {
Self { keymap, _mods_indices: mods_indices, _core_keyboard_id }
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn mods_indices(&self) -> ModsIndices {
self._mods_indices
}
@ -1049,7 +1049,7 @@ impl Deref for XkbKeymap {
}
/// Modifier index in the keymap.
#[cfg_attr(not(x11_platform), allow(dead_code))]
#[cfg_attr(not(feature = "x11"), allow(dead_code))]
#[derive(Default, Debug, Clone, Copy)]
pub struct ModsIndices {
pub shift: Option<xkb_mod_index_t>,

View file

@ -1,20 +1,19 @@
use std::ops::Deref;
use std::os::raw::c_char;
#[cfg(wayland_platform)]
#[cfg(feature = "wayland")]
use std::os::unix::io::OwnedFd;
use std::ptr::{self, NonNull};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::LazyLock;
use smol_str::SmolStr;
use tracing::warn;
use winit_core::event::{ElementState, KeyEvent};
use winit_core::keyboard::{Key, KeyLocation};
use xkbcommon_dl::{
self as xkb, xkb_compose_status, xkb_context, xkb_context_flags, xkbcommon_compose_handle,
xkbcommon_handle, XkbCommon, XkbCommonCompose,
};
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
use {x11_dl::xlib_xcb::xcb_connection_t, xkbcommon_dl::x11::xkbcommon_x11_handle};
mod compose;
@ -22,7 +21,7 @@ mod keymap;
mod state;
use compose::{ComposeStatus, XkbComposeState, XkbComposeTable};
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub use keymap::raw_keycode_to_physicalkey;
use keymap::XkbKeymap;
pub use keymap::{physicalkey_to_scancode, scancode_to_physicalkey};
@ -50,7 +49,7 @@ pub enum Error {
#[derive(Debug)]
pub struct Context {
// NOTE: field order matters.
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub core_keyboard_id: i32,
state: Option<XkbState>,
keymap: Option<XkbKeymap>,
@ -84,7 +83,7 @@ impl Context {
keymap: None,
compose_state1,
compose_state2,
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
core_keyboard_id: 0,
_compose_table: compose_table,
context,
@ -125,23 +124,23 @@ impl Context {
self.keymap.as_mut()
}
#[cfg(wayland_platform)]
#[cfg(feature = "wayland")]
pub fn set_keymap_from_fd(&mut self, fd: OwnedFd, size: usize) {
let keymap = XkbKeymap::from_fd(&self.context, fd, size);
let state = keymap.as_ref().and_then(XkbState::new_wayland);
if keymap.is_none() || state.is_none() {
warn!("failed to update xkb keymap");
tracing::warn!("failed to update xkb keymap");
}
self.state = state;
self.keymap = keymap;
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn set_keymap_from_x11(&mut self, xcb: *mut xcb_connection_t) {
let keymap = XkbKeymap::from_x11_keymap(&self.context, xcb, self.core_keyboard_id);
let state = keymap.as_ref().and_then(|keymap| XkbState::new_x11(xcb, keymap));
if keymap.is_none() || state.is_none() {
warn!("failed to update xkb keymap");
tracing::warn!("failed to update xkb keymap");
}
self.state = state;
self.keymap = keymap;
@ -160,7 +159,7 @@ impl Context {
/// Key builder context with the user provided xkb state.
///
/// Should be used when the original context must not be altered.
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn key_context_with_state<'a>(
&'a mut self,
state: &'a mut XkbState,

View file

@ -4,16 +4,16 @@ use std::os::raw::c_char;
use std::ptr::NonNull;
use smol_str::SmolStr;
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
use x11_dl::xlib_xcb::xcb_connection_t;
use xkbcommon_dl::{
self as xkb, xkb_keycode_t, xkb_keysym_t, xkb_layout_index_t, xkb_state, xkb_state_component,
};
use crate::platform_impl::common::xkb::keymap::XkbKeymap;
#[cfg(x11_platform)]
use crate::platform_impl::common::xkb::XKBXH;
use crate::platform_impl::common::xkb::{make_string_with, XKBH};
use super::keymap::XkbKeymap;
#[cfg(feature = "x11")]
use super::XKBXH;
use super::{make_string_with, XKBH};
#[derive(Debug)]
pub struct XkbState {
@ -22,13 +22,13 @@ pub struct XkbState {
}
impl XkbState {
#[cfg(wayland_platform)]
#[cfg(feature = "wayland")]
pub fn new_wayland(keymap: &XkbKeymap) -> Option<Self> {
let state = NonNull::new(unsafe { (XKBH.xkb_state_new)(keymap.as_ptr()) })?;
Some(Self::new_inner(state))
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn new_x11(xcb: *mut xcb_connection_t, keymap: &XkbKeymap) -> Option<Self> {
let state = unsafe {
(XKBXH.xkb_x11_state_new_from_device)(keymap.as_ptr(), xcb, keymap._core_keyboard_id)
@ -52,7 +52,7 @@ impl XkbState {
unsafe { (XKBH.xkb_state_key_get_layout)(self.state.as_ptr(), key) }
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn depressed_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(
@ -62,7 +62,7 @@ impl XkbState {
}
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn latched_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(
@ -72,7 +72,7 @@ impl XkbState {
}
}
#[cfg(x11_platform)]
#[cfg(feature = "x11")]
pub fn locked_modifiers(&mut self) -> xkb::xkb_mod_mask_t {
unsafe {
(XKBH.xkb_state_serialize_mods)(