Files
test/source/blender/blenkernel/intern/blender.cc
Julian Eisel a6ebfb05ad UI: Option to store enabled asset shelf catalogs in preferences
Adds a new asset shelf option (`STORE_ENABLED_CATALOGS_IN_PREFERENCES`
option in RNA) to use the Preferences for storing the enabled catalogs.
This way asset shelf types can decide if for their use-case, they want
to synchronize the enabled catalogs over Blender sessions and files, or
keep the stored locally in the file.

This is important because for example on one hand, it would be annoying
if for brush assets you'd have to enable the visible catalog tabs for
every 3D View and every file, while on the other hand you need that
level of control for the pose library where the catalogs the rigger/
animator cares about varies from project to project, character to
character and shot to shot.

Conceptually this also makes some sense: The new brush assets workflow
synchronizes brush assets and their catalogs across Blender sessions
and files, basically making them globally accessible independent of
the current file/project, so treating the enabled catalogs the same
is consistent.

Previously reviewed in #120264

Pull Request: https://projects.blender.org/blender/blender/pulls/121363
2024-05-03 10:20:01 -04:00

486 lines
11 KiB
C++

/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*
* Application level startup/shutdown functionality.
*/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "IMB_imbuf.hh"
#include "IMB_moviecache.hh"
#include "BKE_addon.h"
#include "BKE_asset.hh"
#include "BKE_blender.hh" /* own include */
#include "BKE_blender_user_menu.hh" /* own include */
#include "BKE_blender_version.h" /* own include */
#include "BKE_brush.hh"
#include "BKE_cachefile.hh"
#include "BKE_callbacks.hh"
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "BKE_studiolight.h"
#include "BKE_writeffmpeg.hh"
#include "DEG_depsgraph.hh"
#include "RE_texture.h"
#include "BLF_api.hh"
Global G;
UserDef U;
/* -------------------------------------------------------------------- */
/** \name Blender Free on Exit
* \{ */
void BKE_blender_free()
{
/* samples are in a global list..., also sets G_MAIN->sound->sample nullptr */
/* Needs to run before main free as window-manager is still referenced for icons preview jobs. */
BKE_studiolight_free();
BKE_blender_globals_clear();
if (G.log.file != nullptr) {
fclose(static_cast<FILE *>(G.log.file));
}
BKE_spacetypes_free(); /* after free main, it uses space callbacks */
IMB_exit();
BKE_cachefiles_exit();
DEG_free_node_types();
BKE_brush_system_exit();
RE_texture_rng_exit();
BKE_callback_global_finalize();
IMB_moviecache_destruct();
#ifdef WITH_FFMPEG
BKE_ffmpeg_exit();
#endif
BKE_node_system_exit();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Version Access
* \{ */
static char blender_version_string[48] = "";
/* Only includes patch if non-zero. */
static char blender_version_string_compact[48] = "";
static void blender_version_init()
{
const char *version_cycle = "";
if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha")) {
version_cycle = " Alpha";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "beta")) {
version_cycle = " Beta";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "rc")) {
version_cycle = " Release Candidate";
}
else if (STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "release")) {
version_cycle = "";
}
else {
BLI_assert_msg(0, "Invalid Blender version cycle");
}
SNPRINTF(blender_version_string,
"%d.%01d.%d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
BLENDER_VERSION_PATCH,
version_cycle);
SNPRINTF(blender_version_string_compact,
"%d.%01d%s",
BLENDER_VERSION / 100,
BLENDER_VERSION % 100,
version_cycle);
}
const char *BKE_blender_version_string()
{
return blender_version_string;
}
const char *BKE_blender_version_string_compact()
{
return blender_version_string_compact;
}
void BKE_blender_version_blendfile_string_from_values(char *str_buff,
const size_t str_buff_maxncpy,
const short file_version,
const short file_subversion)
{
const short file_version_major = file_version / 100;
const short file_version_minor = file_version % 100;
if (file_subversion >= 0) {
BLI_snprintf(str_buff,
str_buff_maxncpy,
"%d.%d (sub %d)",
file_version_major,
file_version_minor,
file_subversion);
}
else {
BLI_snprintf(str_buff, str_buff_maxncpy, "%d.%d", file_version_major, file_version_minor);
}
}
bool BKE_blender_version_is_alpha()
{
bool is_alpha = STREQ(STRINGIFY(BLENDER_VERSION_CYCLE), "alpha");
return is_alpha;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender #Global Initialize/Clear
* \{ */
void BKE_blender_globals_init()
{
blender_version_init();
memset(&G, 0, sizeof(Global));
U.savetime = 1;
BKE_blender_globals_main_replace(BKE_main_new());
STRNCPY(G.filepath_last_image, "//");
#ifndef WITH_PYTHON_SECURITY /* default */
G.f |= G_FLAG_SCRIPT_AUTOEXEC;
#else
G.f &= ~G_FLAG_SCRIPT_AUTOEXEC;
#endif
G.log.level = 1;
}
void BKE_blender_globals_clear()
{
if (G_MAIN == nullptr) {
return;
}
BLI_assert(G_MAIN->is_global_main);
BKE_main_free(G_MAIN); /* free all lib data */
G_MAIN = nullptr;
}
void BKE_blender_globals_main_replace(Main *bmain)
{
BLI_assert(!bmain->is_global_main);
BKE_blender_globals_clear();
bmain->is_global_main = true;
G_MAIN = bmain;
}
Main *BKE_blender_globals_main_swap(Main *new_gmain)
{
Main *old_gmain = G_MAIN;
BLI_assert(old_gmain->is_global_main);
BLI_assert(!new_gmain->is_global_main);
new_gmain->is_global_main = true;
G_MAIN = new_gmain;
old_gmain->is_global_main = false;
return old_gmain;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Preferences
* \{ */
static void keymap_item_free(wmKeyMapItem *kmi)
{
if (kmi->properties) {
IDP_FreeProperty(kmi->properties);
}
if (kmi->ptr) {
MEM_freeN(kmi->ptr);
}
}
void BKE_blender_userdef_data_swap(UserDef *userdef_a, UserDef *userdef_b)
{
blender::dna::shallow_swap(*userdef_a, *userdef_b);
}
void BKE_blender_userdef_data_set(UserDef *userdef)
{
BKE_blender_userdef_data_swap(&U, userdef);
BKE_blender_userdef_data_free(userdef, true);
}
void BKE_blender_userdef_data_set_and_free(UserDef *userdef)
{
BKE_blender_userdef_data_set(userdef);
MEM_freeN(userdef);
}
static void userdef_free_keymaps(UserDef *userdef)
{
for (wmKeyMap *km = static_cast<wmKeyMap *>(userdef->user_keymaps.first), *km_next; km;
km = km_next)
{
km_next = km->next;
LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &km->diff_items) {
if (kmdi->add_item) {
keymap_item_free(kmdi->add_item);
MEM_freeN(kmdi->add_item);
}
if (kmdi->remove_item) {
keymap_item_free(kmdi->remove_item);
MEM_freeN(kmdi->remove_item);
}
}
LISTBASE_FOREACH (wmKeyMapItem *, kmi, &km->items) {
keymap_item_free(kmi);
}
BLI_freelistN(&km->diff_items);
BLI_freelistN(&km->items);
MEM_freeN(km);
}
BLI_listbase_clear(&userdef->user_keymaps);
}
static void userdef_free_keyconfig_prefs(UserDef *userdef)
{
for (wmKeyConfigPref *kpt = static_cast<wmKeyConfigPref *>(userdef->user_keyconfig_prefs.first),
*kpt_next;
kpt;
kpt = kpt_next)
{
kpt_next = kpt->next;
IDP_FreeProperty(kpt->prop);
MEM_freeN(kpt);
}
BLI_listbase_clear(&userdef->user_keyconfig_prefs);
}
static void userdef_free_user_menus(UserDef *userdef)
{
for (bUserMenu *um = static_cast<bUserMenu *>(userdef->user_menus.first), *um_next; um;
um = um_next)
{
um_next = um->next;
BKE_blender_user_menu_item_free_list(&um->items);
MEM_freeN(um);
}
}
static void userdef_free_addons(UserDef *userdef)
{
for (bAddon *addon = static_cast<bAddon *>(userdef->addons.first), *addon_next; addon;
addon = addon_next)
{
addon_next = addon->next;
BKE_addon_free(addon);
}
BLI_listbase_clear(&userdef->addons);
}
void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts)
{
#define U BLI_STATIC_ASSERT(false, "Global 'U' not allowed, only use arguments passed in!")
#ifdef U
/* Quiet warning. */
#endif
userdef_free_keymaps(userdef);
userdef_free_keyconfig_prefs(userdef);
userdef_free_user_menus(userdef);
userdef_free_addons(userdef);
if (clear_fonts) {
LISTBASE_FOREACH (uiFont *, font, &userdef->uifonts) {
BLF_unload_id(font->blf_id);
}
BLF_default_set(-1);
}
BLI_freelistN(&userdef->autoexec_paths);
BLI_freelistN(&userdef->script_directories);
BLI_freelistN(&userdef->asset_libraries);
BLI_freelistN(&userdef->extension_repos);
LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings)
{
BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths);
MEM_freeN(settings);
}
BLI_listbase_clear(&userdef->asset_shelves_settings);
BLI_freelistN(&userdef->uistyles);
BLI_freelistN(&userdef->uifonts);
BLI_freelistN(&userdef->themes);
#undef U
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender Preferences (Application Templates)
* \{ */
void BKE_blender_userdef_app_template_data_swap(UserDef *userdef_a, UserDef *userdef_b)
{
/* TODO:
* - various minor settings (add as needed).
*/
#define VALUE_SWAP(id) \
{ \
std::swap(userdef_a->id, userdef_b->id); \
}
#define DATA_SWAP(id) \
{ \
UserDef userdef_tmp; \
memcpy(&(userdef_tmp.id), &(userdef_a->id), sizeof(userdef_tmp.id)); \
memcpy(&(userdef_a->id), &(userdef_b->id), sizeof(userdef_tmp.id)); \
memcpy(&(userdef_b->id), &(userdef_tmp.id), sizeof(userdef_tmp.id)); \
} \
((void)0)
#define FLAG_SWAP(id, ty, flags) \
{ \
CHECK_TYPE(&(userdef_a->id), ty *); \
const ty f = flags; \
const ty a = userdef_a->id; \
const ty b = userdef_b->id; \
userdef_a->id = (userdef_a->id & ~f) | (b & f); \
userdef_b->id = (userdef_b->id & ~f) | (a & f); \
} \
((void)0)
VALUE_SWAP(uistyles);
VALUE_SWAP(uifonts);
VALUE_SWAP(themes);
VALUE_SWAP(addons);
VALUE_SWAP(user_keymaps);
VALUE_SWAP(user_keyconfig_prefs);
DATA_SWAP(font_path_ui);
DATA_SWAP(font_path_ui_mono);
DATA_SWAP(keyconfigstr);
DATA_SWAP(gizmo_flag);
DATA_SWAP(app_flag);
/* We could add others. */
FLAG_SWAP(uiflag, int, USER_SAVE_PROMPT | USER_SPLASH_DISABLE | USER_SHOW_GIZMO_NAVIGATE);
DATA_SWAP(ui_scale);
#undef VALUE_SWAP
#undef DATA_SWAP
#undef FLAG_SWAP
}
void BKE_blender_userdef_app_template_data_set(UserDef *userdef)
{
BKE_blender_userdef_app_template_data_swap(&U, userdef);
BKE_blender_userdef_data_free(userdef, true);
}
void BKE_blender_userdef_app_template_data_set_and_free(UserDef *userdef)
{
BKE_blender_userdef_app_template_data_set(userdef);
MEM_freeN(userdef);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Blender's AtExit
*
* \note Don't use MEM_mallocN so functions can be registered at any time.
* \{ */
static struct AtExitData {
AtExitData *next;
void (*func)(void *user_data);
void *user_data;
} *g_atexit = nullptr;
void BKE_blender_atexit_register(void (*func)(void *user_data), void *user_data)
{
AtExitData *ae = static_cast<AtExitData *>(malloc(sizeof(*ae)));
ae->next = g_atexit;
ae->func = func;
ae->user_data = user_data;
g_atexit = ae;
}
void BKE_blender_atexit_unregister(void (*func)(void *user_data), const void *user_data)
{
AtExitData *ae = g_atexit;
AtExitData **ae_p = &g_atexit;
while (ae) {
if ((ae->func == func) && (ae->user_data == user_data)) {
*ae_p = ae->next;
free(ae);
return;
}
ae_p = &ae->next;
ae = ae->next;
}
}
void BKE_blender_atexit()
{
AtExitData *ae = g_atexit, *ae_next;
while (ae) {
ae_next = ae->next;
ae->func(ae->user_data);
free(ae);
ae = ae_next;
}
g_atexit = nullptr;
}
/** \} */