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