Blender V5.0
wm_splash_screen.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2007 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
16
17#include <cstring>
18
19#include "DNA_screen_types.h"
20#include "DNA_userdef_types.h"
22
23#include "BLI_listbase.h"
24#include "BLI_math_base.h"
25#include "BLI_path_utils.hh"
26#include "BLI_utildefines.h"
27
28#include "BKE_appdir.hh"
29#include "BKE_blender_version.h"
30#include "BKE_context.hh"
31#include "BKE_preferences.h"
32
33#include "BLT_translation.hh"
34
35#include "IMB_imbuf.hh"
36#include "IMB_imbuf_types.hh"
37
38#include "ED_datafiles.h"
39#include "ED_screen.hh"
40
41#include "RNA_access.hh"
42
43#include "UI_interface.hh"
44#include "UI_interface_icons.hh"
46#include "UI_resources.hh"
47
48#include "WM_api.hh"
49#include "WM_types.hh"
50
51#include "wm.hh"
52
53/* -------------------------------------------------------------------- */
56
57static void wm_block_splash_close(bContext *C, void *arg_block, void * /*arg*/)
58{
59 wmWindow *win = CTX_wm_window(C);
60 UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
61}
62
63static void wm_block_splash_add_label(uiBlock *block, const char *label, int x, int y)
64{
65 if (!(label && label[0])) {
66 return;
67 }
68
70
71 uiBut *but = uiDefBut(
72 block, ButType::Label, 0, label, 0, y, x, UI_UNIT_Y, nullptr, 0, 0, std::nullopt);
75
76 /* Regardless of theme, this text should always be bright white. */
77 uchar color[4] = {255, 255, 255, 255};
78 UI_but_color_set(but, color);
79
81}
82
83#ifndef WITH_HEADLESS
85{
86 uchar *rct = ibuf->byte_buffer.data;
87 if (!rct) {
88 return;
89 }
90
91 bTheme *btheme = UI_GetTheme();
92 const float roundness = btheme->tui.wcol_menu_back.roundness * UI_SCALE_FAC;
93 const int size = roundness * 20;
94
96 /* Y-axis initial offset. */
97 rct += 4 * (ibuf->y - size) * ibuf->x;
98
99 for (int y = 0; y < size; y++) {
100 for (int x = 0; x < size; x++, rct += 4) {
101 const float pixel = 1.0 / size;
102 const float u = pixel * x;
103 const float v = pixel * y;
104 const float distance = sqrt(u * u + v * v);
105
106 /* Pointer offset to the alpha value of pixel. */
107 /* NOTE: the left corner is flipped in the X-axis. */
108 const int offset_l = 4 * (size - x - x - 1) + 3;
109 const int offset_r = 4 * (ibuf->x - size) + 3;
110
111 if (distance > 1.0) {
112 rct[offset_l] = 0;
113 rct[offset_r] = 0;
114 }
115 else {
116 /* Create a single pixel wide transition for anti-aliasing.
117 * Invert the distance and map its range [0, 1] to [0, pixel]. */
118 const float fac = (1.0 - distance) * size;
119
120 if (fac > 1.0) {
121 continue;
122 }
123
124 const uchar alpha = unit_float_to_uchar_clamp(fac);
125 rct[offset_l] = alpha;
126 rct[offset_r] = alpha;
127 }
128 }
129
130 /* X-axis offset to the next row. */
131 rct += 4 * (ibuf->x - size);
132 }
133 }
134}
135#endif /* !WITH_HEADLESS */
136
137static ImBuf *wm_block_splash_image(int width, int *r_height)
138{
139 ImBuf *ibuf = nullptr;
140 int height = 0;
141#ifndef WITH_HEADLESS
142 if (U.app_template[0] != '\0') {
143 char splash_filepath[FILE_MAX];
144 char template_directory[FILE_MAX];
146 U.app_template, template_directory, sizeof(template_directory)))
147 {
148 BLI_path_join(splash_filepath, sizeof(splash_filepath), template_directory, "splash.png");
149 ibuf = IMB_load_image_from_filepath(splash_filepath, IB_byte_data);
150 }
151 }
152
153 if (ibuf == nullptr) {
154 const char *custom_splash_path = BLI_getenv("BLENDER_CUSTOM_SPLASH");
155 if (custom_splash_path) {
156 ibuf = IMB_load_image_from_filepath(custom_splash_path, IB_byte_data);
157 }
158 }
159
160 if (ibuf == nullptr) {
161 const uchar *splash_data = (const uchar *)datatoc_splash_png;
162 size_t splash_data_size = datatoc_splash_png_size;
164 splash_data, splash_data_size, IB_byte_data, "<splash screen>");
165 }
166
167 if (ibuf) {
168 ibuf->planes = 32; /* The image might not have an alpha channel. */
169 height = (width * ibuf->y) / ibuf->x;
170 if (width != ibuf->x || height != ibuf->y) {
171 IMB_scale(ibuf, width, height, IMBScaleFilter::Box, false);
172 }
173
176 }
177
178#else
179 UNUSED_VARS(width);
180#endif
181 *r_height = height;
182 return ibuf;
183}
184
186 int *r_height,
187 int max_width,
188 int max_height)
189{
190 ImBuf *ibuf = nullptr;
191 int height = 0;
192 int width = max_width;
193#ifndef WITH_HEADLESS
194
195 const char *custom_splash_path = BLI_getenv("BLENDER_CUSTOM_SPLASH_BANNER");
196 if (custom_splash_path) {
197 ibuf = IMB_load_image_from_filepath(custom_splash_path, IB_byte_data);
198 }
199
200 if (!ibuf) {
201 return nullptr;
202 }
203
204 ibuf->planes = 32; /* The image might not have an alpha channel. */
205
206 width = ibuf->x;
207 height = ibuf->y;
208 if (width > 0 && height > 0 && (width > max_width || height > max_height)) {
209 const float splash_ratio = max_width / float(max_height);
210 const float banner_ratio = ibuf->x / float(ibuf->y);
211
212 if (banner_ratio > splash_ratio) {
213 /* The banner is wider than the splash image. */
214 width = max_width;
215 height = max_width / banner_ratio;
216 }
217 else if (banner_ratio < splash_ratio) {
218 /* The banner is taller than the splash image. */
219 height = max_height;
220 width = max_height * banner_ratio;
221 }
222 else {
223 width = max_width;
224 height = max_height;
225 }
226 if (width != ibuf->x || height != ibuf->y) {
227 IMB_scale(ibuf, width, height, IMBScaleFilter::Box, false);
228 }
229 }
230
232
233#else
234 UNUSED_VARS(max_height);
235#endif
236 *r_height = height;
237 *r_width = width;
238 return ibuf;
239}
240
244static void wm_block_splash_close_on_fileselect(bContext *C, void *arg1, void * /*arg2*/)
245{
246 wmWindow *win = CTX_wm_window(C);
247 if (!win) {
248 return;
249 }
250
251 /* Check for the event as this will run before the new window/area has been created. */
252 bool has_fileselect = false;
253 LISTBASE_FOREACH (const wmEvent *, event, &win->runtime->event_queue) {
254 if (event->type == EVT_FILESELECT) {
255 has_fileselect = true;
256 break;
257 }
258 }
259
260 if (has_fileselect) {
261 wm_block_splash_close(C, arg1, nullptr);
262 }
263}
264
265#if defined(__APPLE__)
266/* Check if Blender is running under Rosetta for the purpose of displaying a splash screen warning.
267 * From Apple's WWDC 2020 Session - Explore the new system architecture of Apple Silicon Macs.
268 * Time code: 14:31 - https://developer.apple.com/videos/play/wwdc2020/10686/ */
269
270# include <sys/sysctl.h>
271
272static int is_using_macos_rosetta()
273{
274 int ret = 0;
275 size_t size = sizeof(ret);
276
277 if (sysctlbyname("sysctl.proc_translated", &ret, &size, nullptr, 0) != -1) {
278 return ret;
279 }
280 /* If "sysctl.proc_translated" is not present then must be native. */
281 if (errno == ENOENT) {
282 return 0;
283 }
284 return -1;
285}
286#endif /* __APPLE__ */
287
288static uiBlock *wm_block_splash_create(bContext *C, ARegion *region, void * /*arg*/)
289{
290 const uiStyle *style = UI_style_get_dpi();
291
292 uiBlock *block = UI_block_begin(C, region, "splash", blender::ui::EmbossType::Emboss);
293
294 /* Note on #UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized
295 * with the OS when the splash shows, window clipping in this case gives
296 * ugly results and clipping the splash isn't useful anyway, just disable it #32938. */
299
300 int splash_width = style->widget.points * 45 * UI_SCALE_FAC;
301 CLAMP_MAX(splash_width, WM_window_native_pixel_x(CTX_wm_window(C)) * 0.7f);
302 int splash_height;
303
304 /* Would be nice to support caching this, so it only has to be re-read (and likely resized) on
305 * first draw or if the image changed. */
306 ImBuf *ibuf = wm_block_splash_image(splash_width, &splash_height);
307 /* This should never happen, if it does - don't crash. */
308 if (LIKELY(ibuf)) {
309 uiBut *but = uiDefButImage(
310 block, ibuf, 0, 0.5f * U.widget_unit, splash_width, splash_height, nullptr);
311
312 UI_but_func_set(but, wm_block_splash_close, block, nullptr);
313
316 splash_width - 8.0 * UI_SCALE_FAC,
317 splash_height - 13.0 * UI_SCALE_FAC);
318 }
319
320 /* Banner image passed through the environment, to overlay on the splash and
321 * indicate a custom Blender version. Transparency can be used. To replace the
322 * full splash screen, see BLENDER_CUSTOM_SPLASH. */
323 int banner_width = 0;
324 int banner_height = 0;
326 &banner_width, &banner_height, splash_width, splash_height);
327 if (bannerbuf) {
328 uiBut *banner_but = uiDefButImage(
329 block, bannerbuf, 0, 0.5f * U.widget_unit, banner_width, banner_height, nullptr);
330
331 UI_but_func_set(banner_but, wm_block_splash_close, block, nullptr);
332 }
333
334 const int layout_margin_x = UI_SCALE_FAC * 26;
335 uiLayout &layout = blender::ui::block_layout(block,
338 layout_margin_x,
339 0,
340 splash_width - (layout_margin_x * 2),
341 UI_SCALE_FAC * 110,
342 0,
343 style);
344
345 MenuType *mt;
346
347 /* Draw setup screen if no preferences have been saved yet. */
349 mt = WM_menutype_find("WM_MT_splash_quick_setup", true);
350
351 /* The #UI_BLOCK_QUICK_SETUP flag prevents the button text from being left-aligned,
352 * as it is for all menus due to the #UI_BLOCK_LOOP flag, see in #ui_def_but. */
354 }
355 else {
356 mt = WM_menutype_find("WM_MT_splash", true);
357 }
358
360
361 if (mt) {
362 UI_menutype_draw(C, mt, &layout);
363 }
364
365/* Displays a warning if blender is being emulated via Rosetta (macOS) or XTA (Windows) */
366#if defined(__APPLE__) || defined(_M_X64)
367# if defined(__APPLE__)
368 if (is_using_macos_rosetta() > 0)
369# elif defined(_M_X64)
370 const char *proc_id = BLI_getenv("PROCESSOR_IDENTIFIER");
371 if (proc_id && strncmp(proc_id, "ARM", 3) == 0)
372# endif
373 {
375
376 uiLayout *split = &layout.split(0.725, true);
377 uiLayout *row1 = &split->row(true);
378 uiLayout *row2 = &split->row(true);
379
380 row1->label(RPT_("Intel binary detected. Expect reduced performance."), ICON_ERROR);
381
382 PointerRNA op_ptr = row2->op("WM_OT_url_open",
384 ICON_URL,
387# if defined(__APPLE__)
389 &op_ptr,
390 "url",
391 "https://docs.blender.org/manual/en/latest/getting_started/installing/macos.html");
392# elif defined(_M_X64)
394 &op_ptr,
395 "url",
396 "https://docs.blender.org/manual/en/latest/getting_started/installing/windows.html");
397# endif
398
399 layout.separator();
400 }
401#endif
402
404
405 return block;
406}
407
409 wmOperator * /*op*/,
410 const wmEvent * /*event*/)
411{
413
414 return OPERATOR_FINISHED;
415}
416
418{
419 ot->name = "Splash Screen";
420 ot->idname = "WM_OT_splash";
421 ot->description = "Open the splash screen with release info";
422
423 ot->invoke = wm_splash_invoke;
425}
426
428
429/* -------------------------------------------------------------------- */
432
433static uiBlock *wm_block_about_create(bContext *C, ARegion *region, void * /*arg*/)
434{
435 const uiStyle *style = UI_style_get_dpi();
436 const int dialog_width = style->widget.points * 42 * UI_SCALE_FAC;
437
438 uiBlock *block = UI_block_begin(C, region, "about", blender::ui::EmbossType::Emboss);
439
442
443 uiLayout &layout = blender::ui::block_layout(block,
446 0,
447 0,
448 dialog_width,
449 0,
450 0,
451 style);
452
453/* Blender logo. */
454#ifndef WITH_HEADLESS
455 constexpr bool show_color = false;
456 const float size = 0.2f * dialog_width;
457
458 ImBuf *ibuf = UI_svg_icon_bitmap(ICON_BLENDER_LOGO_LARGE, size, show_color);
459
460 if (ibuf) {
461 bTheme *btheme = UI_GetTheme();
462 const uchar *color = btheme->tui.wcol_menu_back.text_sel;
463
464 /* The top margin. */
465 uiLayout *row = &layout.row(false);
466 row->separator(0.2f);
467
468 /* The logo image. */
469 row = &layout.row(false);
471 uiDefButImage(block, ibuf, 0, U.widget_unit, ibuf->x, ibuf->y, show_color ? nullptr : color);
472
473 /* Padding below the logo. */
474 row = &layout.row(false);
475 row->separator(2.7f);
476 }
477#endif /* !WITH_HEADLESS */
478
479 uiLayout *col = &layout.column(true);
480
481 uiItemL_ex(col, IFACE_("Blender"), ICON_NONE, true, false);
482
483 MenuType *mt = WM_menutype_find("WM_MT_splash_about", true);
484 if (mt) {
485 UI_menutype_draw(C, mt, col);
486 }
487
489
490 return block;
491}
492
494 wmOperator * /*op*/,
495 const wmEvent * /*event*/)
496{
498
499 return OPERATOR_FINISHED;
500}
501
503{
504 ot->name = "About Blender";
505 ot->idname = "WM_OT_splash_about";
506 ot->description = "Open a window with information about Blender";
507
508 ot->invoke = wm_splash_about_invoke;
510}
511
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy) ATTR_NONNULL(1)
Definition appdir.cc:1082
const char * BKE_blender_version_string(void)
Definition blender.cc:146
wmWindow * CTX_wm_window(const bContext *C)
#define LISTBASE_FOREACH(type, var, list)
#define FILE_MAX
#define BLI_path_join(...)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
unsigned char uchar
#define CLAMP_MAX(a, c)
#define UNUSED_VARS(...)
#define LIKELY(x)
#define RPT_(msgid)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
#define IFACE_(msgid)
#define UI_SCALE_FAC
@ OPERATOR_FINISHED
int datatoc_splash_png_size
const char datatoc_splash_png[]
static void split(const char *text, const char *seps, char ***str, int *count)
void IMB_premultiply_alpha(ImBuf *ibuf)
Definition filter.cc:381
ImBuf * IMB_load_image_from_filepath(const char *filepath, const int flags, char r_colorspace[IM_MAX_SPACE]=nullptr)
Definition readimage.cc:189
ImBuf * IMB_load_image_from_memory(const unsigned char *mem, const size_t size, const int flags, const char *descr, const char *filepath=nullptr, char r_colorspace[IM_MAX_SPACE]=nullptr)
Definition readimage.cc:121
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
Definition scaling.cc:465
@ IB_byte_data
#define C
Definition RandGen.cpp:29
void UI_but_func_set(uiBut *but, std::function< void(bContext &)> func)
#define UI_UNIT_Y
void UI_block_emboss_set(uiBlock *block, blender::ui::EmbossType emboss)
@ UI_BLOCK_LOOP
@ UI_BLOCK_KEEP_OPEN
@ UI_BLOCK_QUICK_SETUP
@ UI_BLOCK_NO_WIN_CLIP
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_but_color_set(uiBut *but, const uchar color[4])
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
const uiStyle * UI_style_get_dpi()
void UI_but_drawflag_enable(uiBut *but, int flag)
void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
void UI_block_func_set(uiBlock *block, uiButHandleFunc func, void *arg1, void *arg2)
void UI_but_drawflag_disable(uiBut *but, int flag)
uiBut * uiDefButImage(uiBlock *block, void *imbuf, int x, int y, short width, short height, const uchar color[4])
void UI_block_bounds_set_centered(uiBlock *block, int addval)
Definition interface.cc:679
@ UI_BLOCK_THEME_STYLE_POPUP
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BUT_TEXT_RIGHT
@ UI_BUT_TEXT_LEFT
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
ImBuf * UI_svg_icon_bitmap(uint icon_id, float size, bool multicolor=false)
#define UI_ITEM_NONE
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
uiBut * uiItemL_ex(uiLayout *layout, blender::StringRef name, int icon, bool highlight, bool redalert)
bTheme * UI_GetTheme()
#define U
ATTR_WARN_UNUSED_RESULT const BMVert * v
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
nullptr float
uint col
#define sqrt
float distance(VecOp< float, D >, VecOp< float, D >) RET
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
uiLayout & block_layout(uiBlock *block, LayoutDirection direction, LayoutType type, int x, int y, int size, int em, int padding, const uiStyle *style)
return ret
void RNA_string_set(PointerRNA *ptr, const char *name, const char *value)
ImBufByteBuffer byte_buffer
unsigned char planes
uiWidgetColors wcol_menu_back
ThemeUI tui
void alignment_set(blender::ui::LayoutAlign alignment)
void label(blender::StringRef name, int icon)
uiLayout & column(bool align)
void separator(float factor=1.0f, LayoutSeparatorType type=LayoutSeparatorType::Auto)
uiLayout & row(bool align)
uiLayout & split(float percentage, bool align)
PointerRNA op(wmOperatorType *ot, std::optional< blender::StringRef > name, int icon, blender::wm::OpCallContext context, eUI_Item_Flag flag)
uiFontStyle widget
unsigned char text_sel[4]
WindowRuntimeHandle * runtime
@ EVT_FILESELECT
wmOperatorType * ot
Definition wm_files.cc:4237
MenuType * WM_menutype_find(const StringRef idname, bool quiet)
bool WM_operator_winactive(bContext *C)
void WM_OT_splash_about(wmOperatorType *ot)
static ImBuf * wm_block_splash_image(int width, int *r_height)
static void wm_block_splash_add_label(uiBlock *block, const char *label, int x, int y)
static void wm_block_splash_close(bContext *C, void *arg_block, void *)
static uiBlock * wm_block_about_create(bContext *C, ARegion *region, void *)
static ImBuf * wm_block_splash_banner_image(int *r_width, int *r_height, int max_width, int max_height)
static void wm_block_splash_image_roundcorners_add(ImBuf *ibuf)
static wmOperatorStatus wm_splash_about_invoke(bContext *C, wmOperator *, const wmEvent *)
static wmOperatorStatus wm_splash_invoke(bContext *C, wmOperator *, const wmEvent *)
static void wm_block_splash_close_on_fileselect(bContext *C, void *arg1, void *)
static uiBlock * wm_block_splash_create(bContext *C, ARegion *region, void *)
void WM_OT_splash(wmOperatorType *ot)
int WM_window_native_pixel_x(const wmWindow *win)