"""
Core option definitions, values, categories, and internationalization.
Corresponds to the ``retro_core_option_*`` and ``retro_variable`` types
in ``libretro.h``. Defines option definitions, values, categories,
and internationalization wrappers for core configuration.
.. seealso:: :mod:`libretro.drivers.options`
"""
from copy import deepcopy
from ctypes import POINTER, Array, Structure, c_bool, c_char_p, pointer
from dataclasses import dataclass
from libretro.api._utils import MemoDict, deepcopy_array
from libretro.ctypes import TypedArray, TypedFunctionPointer, TypedPointer
RETRO_NUM_CORE_OPTION_VALUES_MAX = 128
"""Maximum number of values a single core option can have."""
retro_core_options_update_display_callback_t = TypedFunctionPointer[c_bool, []]
"""
Recompute the visibility of dynamic core options.
Registered by the :term:`core` and called by the :term:`frontend`
to ask the core to update which options should be shown or hidden,
typically by calling :data:`~libretro.api.environment.RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY` for each one.
All core options are visible by default.
:return: :obj:`True` if any core option's visibility was adjusted
since the last call to this function,
:obj:`False` otherwise.
Corresponds to :c:type:`retro_core_options_update_display_callback_t` in ``libretro.h``.
"""
[docs]
@dataclass(init=False, slots=True)
class retro_variable(Structure):
"""
Corresponds to :c:type:`retro_variable` in ``libretro.h``.
A key/value pair used for legacy core options (v0).
>>> from libretro.api import retro_variable
>>> v = retro_variable()
>>> v.key is None
True
"""
key: bytes | None
"""The option's unique key."""
value: bytes | None
"""The option's current value, or a pipe-delimited list of possible values."""
_fields_ = (
("key", c_char_p),
("value", c_char_p),
)
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object, including all strings.
Intended for use with :func:`copy.deepcopy`.
>>> import copy
>>> from libretro.api import retro_variable
>>> copy.deepcopy(retro_variable()).key is None
True
"""
return retro_variable(self.key, self.value)
[docs]
@dataclass(init=False, slots=True)
class retro_core_option_display(Structure):
"""
Corresponds to :c:type:`retro_core_option_display` in ``libretro.h``.
Controls whether a core option is visible in the frontend UI.
>>> from libretro.api import retro_core_option_display
>>> d = retro_core_option_display()
>>> d.visible
False
"""
key: bytes | None
"""The unique key of the option to show or hide."""
visible: bool
"""Whether the option should be visible to the player."""
_fields_ = (
("key", c_char_p),
("visible", c_bool),
)
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object, including all strings.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_option_display(self.key, self.visible)
[docs]
@dataclass(init=False, slots=True)
class retro_core_option_value(Structure):
"""
Corresponds to :c:type:`retro_core_option_value` in ``libretro.h``.
A single selectable value for a core option.
>>> from libretro.api import retro_core_option_value
>>> v = retro_core_option_value()
>>> v.value is None
True
"""
value: bytes | None
"""Internal value string."""
label: bytes | None
"""Human-readable label, or ``None`` to display :attr:`value` as-is."""
_fields_ = (
("value", c_char_p),
("label", c_char_p),
)
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object, including all strings.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_option_value(self.value, self.label)
NUM_CORE_OPTION_VALUES_MAX = RETRO_NUM_CORE_OPTION_VALUES_MAX
"""Alias for :data:`RETRO_NUM_CORE_OPTION_VALUES_MAX`."""
CoreOptionArray = retro_core_option_value * RETRO_NUM_CORE_OPTION_VALUES_MAX
"""Fixed-size array type for :class:`retro_core_option_value` entries."""
[docs]
@dataclass(init=False, slots=True)
class retro_core_option_definition(Structure):
"""
Corresponds to :c:type:`retro_core_option_definition` in ``libretro.h``.
Defines a single core option with a key, description, info text,
possible values, and a default value (v1 options API).
>>> from libretro.api import retro_core_option_definition
>>> d = retro_core_option_definition()
>>> d.key is None
True
"""
key: bytes | None
"""Unique key for this option."""
desc: bytes | None
"""Human-readable description shown to the player."""
info: bytes | None
"""Extended information or tooltip text."""
values: Array[retro_core_option_value]
"""Array of possible values for this option."""
default_value: bytes | None
"""Default value if not previously set."""
_fields_ = (
("key", c_char_p),
("desc", c_char_p),
("info", c_char_p),
("values", CoreOptionArray),
("default_value", c_char_p),
)
[docs]
def __deepcopy__(self, memo: MemoDict = None):
"""
Return a copy of this object, including all strings and subobjects.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_option_definition(
self.key,
self.desc,
self.info,
deepcopy_array(self.values, memo),
self.default_value,
)
[docs]
@dataclass(init=False, slots=True)
class retro_core_options_intl(Structure):
"""
Corresponds to :c:type:`retro_core_options_intl` in ``libretro.h``.
Wraps US English and localized option definitions for v1 options.
>>> from libretro.api import retro_core_options_intl
>>> intl = retro_core_options_intl()
>>> intl.us is None
True
"""
us: TypedPointer[retro_core_option_definition] | None
"""US English option definitions."""
local: TypedPointer[retro_core_option_definition] | None
"""Localized option definitions."""
_fields_ = (
("us", POINTER(retro_core_option_definition)),
("local", POINTER(retro_core_option_definition)),
)
[docs]
def __deepcopy__(self, memo: MemoDict = None):
"""
Return a copy of this object, including all strings and subobjects.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_options_intl(
pointer(deepcopy(self.us[0], memo)) if self.us else None,
pointer(deepcopy(self.local[0], memo)) if self.local else None,
)
[docs]
@dataclass(init=False, slots=True)
class retro_core_option_v2_category(Structure):
"""
Corresponds to :c:type:`retro_core_option_v2_category` in ``libretro.h``.
Groups related options under a named category (v2 options API).
>>> from libretro.api import retro_core_option_v2_category
>>> cat = retro_core_option_v2_category()
>>> cat.key is None
True
"""
key: bytes | None
"""Unique key for this category."""
desc: bytes | None
"""Human-readable description shown to the player."""
info: bytes | None
"""Extended information about this category."""
_fields_ = (
("key", c_char_p),
("desc", c_char_p),
("info", c_char_p),
)
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object, including all strings.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_option_v2_category(self.key, self.desc, self.info)
[docs]
@dataclass(init=False, slots=True)
class retro_core_option_v2_definition(Structure):
"""
Corresponds to :c:type:`retro_core_option_v2_definition` in ``libretro.h``.
Defines a single core option with category support (v2 options API).
>>> from libretro.api import retro_core_option_v2_definition
>>> d = retro_core_option_v2_definition()
>>> d.key is None
True
"""
key: bytes | None
"""Unique key for this option."""
desc: bytes | None
"""Human-readable description shown to the player."""
desc_categorized: bytes | None
"""Shorter description for display under its category."""
info: bytes | None
"""Extended information or tooltip text."""
info_categorized: bytes | None
"""Shorter tooltip text for display under its category."""
category_key: bytes | None
"""Key of the category this option belongs to, or ``None``."""
values: TypedArray[retro_core_option_value]
"""Array of possible values for this option."""
default_value: bytes | None
"""Default value if not previously set."""
_fields_ = (
("key", c_char_p),
("desc", c_char_p),
("desc_categorized", c_char_p),
("info", c_char_p),
("info_categorized", c_char_p),
("category_key", c_char_p),
("values", CoreOptionArray),
("default_value", c_char_p),
)
[docs]
def __deepcopy__(self, memo: MemoDict = None):
"""
Return a copy of this object, including all strings and subobjects.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_option_v2_definition(
self.key,
self.desc,
self.desc_categorized,
self.info,
self.info_categorized,
self.category_key,
deepcopy_array(self.values, memo),
self.default_value,
)
[docs]
@dataclass(init=False, slots=True)
class retro_core_options_v2(Structure):
"""
Corresponds to :c:type:`retro_core_options_v2` in ``libretro.h``.
Top-level container for v2 option categories and definitions.
>>> from libretro.api import retro_core_options_v2
>>> v2 = retro_core_options_v2()
>>> v2.categories is None
True
"""
categories: TypedPointer[retro_core_option_v2_category] | None
"""Array of option categories, terminated by a zeroed-out entry."""
definitions: TypedPointer[retro_core_option_v2_definition] | None
"""Array of option definitions, terminated by a zeroed-out entry."""
_fields_ = (
("categories", POINTER(retro_core_option_v2_category)),
("definitions", POINTER(retro_core_option_v2_definition)),
)
[docs]
def __deepcopy__(self, memo: MemoDict = None):
"""
Return a copy of this object, including all strings and subobjects.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_options_v2(
pointer(deepcopy(self.categories[0], memo)) if self.categories else None,
pointer(deepcopy(self.definitions[0], memo)) if self.definitions else None,
)
[docs]
@dataclass(init=False, slots=True)
class retro_core_options_v2_intl(Structure):
"""
Corresponds to :c:type:`retro_core_options_v2_intl` in ``libretro.h``.
Wraps US English and localized v2 option sets.
>>> from libretro.api import retro_core_options_v2_intl
>>> intl = retro_core_options_v2_intl()
>>> intl.us is None
True
"""
us: TypedPointer[retro_core_options_v2] | None
"""US English option definitions and categories."""
local: TypedPointer[retro_core_options_v2] | None
"""Localized option definitions and categories."""
_fields_ = (
("us", POINTER(retro_core_options_v2)),
("local", POINTER(retro_core_options_v2)),
)
[docs]
def __deepcopy__(self, memo: MemoDict = None):
"""
Return a copy of this object, including all strings and subobjects.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_options_v2_intl(
pointer(deepcopy(self.us[0], memo)) if self.us else None,
pointer(deepcopy(self.local[0], memo)) if self.local else None,
)
[docs]
@dataclass(init=False, slots=True)
class retro_core_options_update_display_callback(Structure):
"""
Corresponds to :c:type:`retro_core_options_update_display_callback` in ``libretro.h``.
Wraps a callback that the frontend calls to determine
whether to refresh option visibility.
>>> from libretro.api import retro_core_options_update_display_callback
>>> cb = retro_core_options_update_display_callback()
>>> cb.callback is None
True
"""
callback: retro_core_options_update_display_callback_t | None
"""Called by the frontend to request a visibility update for core options."""
_fields_ = (("callback", retro_core_options_update_display_callback_t),)
[docs]
def __call__(self) -> bool:
"""
Invoke the callback.
:raises ValueError: If no callback has been set.
:returns: ``True`` if the display should be updated.
"""
if not self.callback:
raise ValueError("No callback has been set")
return bool(self.callback())
[docs]
def __deepcopy__(self, _):
"""
Return a copy of this object.
Intended for use with :func:`copy.deepcopy`.
"""
return retro_core_options_update_display_callback(self.callback)
__all__ = [
"RETRO_NUM_CORE_OPTION_VALUES_MAX",
"retro_variable",
"retro_core_option_display",
"retro_core_option_value",
"CoreOptionArray",
"retro_core_option_definition",
"retro_core_options_intl",
"retro_core_option_v2_category",
"retro_core_option_v2_definition",
"retro_core_options_v2",
"retro_core_options_v2_intl",
"retro_core_options_update_display_callback",
"retro_core_options_update_display_callback_t",
"NUM_CORE_OPTION_VALUES_MAX",
]