1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2007 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 *
23 * This file contains the splash screen logic (the `WM_OT_splash` operator).
24 *
25 * - Loads the splash image.
26 * - Displaying version information.
27 * - Lists New Files (application templates).
28 * - Lists Recent files.
29 * - Links to web sites.
30 */
31
32 #include <string.h>
33
34 #include "CLG_log.h"
35
36 #include "DNA_ID.h"
37 #include "DNA_scene_types.h"
38 #include "DNA_screen_types.h"
39 #include "DNA_userdef_types.h"
40 #include "DNA_windowmanager_types.h"
41
42 #include "BLI_blenlib.h"
43 #include "BLI_math.h"
44 #include "BLI_utildefines.h"
45
46 #include "BKE_appdir.h"
47 #include "BKE_blender_version.h"
48 #include "BKE_context.h"
49 #include "BKE_screen.h"
50
51 #include "BLT_translation.h"
52
53 #include "BLF_api.h"
54
55 #include "IMB_imbuf.h"
56 #include "IMB_imbuf_types.h"
57
58 #include "ED_screen.h"
59
60 #include "UI_interface.h"
61 #include "UI_interface_icons.h"
62 #include "UI_resources.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "wm.h"
68
wm_block_close(bContext * C,void * arg_block,void * UNUSED (arg))69 static void wm_block_close(bContext *C, void *arg_block, void *UNUSED(arg))
70 {
71 wmWindow *win = CTX_wm_window(C);
72 UI_popup_block_close(C, win, arg_block);
73 }
74
wm_block_splash_add_label(uiBlock * block,const char * label,int x,int y)75 static void wm_block_splash_add_label(uiBlock *block, const char *label, int x, int y)
76 {
77 if (!(label && label[0])) {
78 return;
79 }
80
81 UI_block_emboss_set(block, UI_EMBOSS_NONE);
82
83 uiBut *but = uiDefBut(
84 block, UI_BTYPE_LABEL, 0, label, 0, y, x, UI_UNIT_Y, NULL, 0, 0, 0, 0, NULL);
85 UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT);
86 UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT);
87
88 /* 1 = UI_SELECT, internal flag to draw in white. */
89 UI_but_flag_enable(but, 1);
90 UI_block_emboss_set(block, UI_EMBOSS);
91 }
92
93 #ifndef WITH_HEADLESS
wm_block_splash_image_roundcorners_add(ImBuf * ibuf)94 static void wm_block_splash_image_roundcorners_add(ImBuf *ibuf)
95 {
96 uchar *rct = (uchar *)ibuf->rect;
97
98 if (rct) {
99 bTheme *btheme = UI_GetTheme();
100 const float roundness = btheme->tui.wcol_menu_back.roundness * U.dpi_fac;
101 const int size = roundness * 20;
102
103 if (size < ibuf->x && size < ibuf->y) {
104 /* Y-axis initial offset. */
105 rct += 4 * (ibuf->y - size) * ibuf->x;
106
107 for (int y = 0; y < size; y++) {
108 for (int x = 0; x < size; x++, rct += 4) {
109 const float pixel = 1.0 / size;
110 const float u = pixel * x;
111 const float v = pixel * y;
112 const float distance = sqrt(u * u + v * v);
113
114 /* Pointer offset to the alpha value of pixel. */
115 /* Note, the left corner is flipped in the X-axis. */
116 const int offset_l = 4 * (size - x - x - 1) + 3;
117 const int offset_r = 4 * (ibuf->x - size) + 3;
118
119 if (distance > 1.0) {
120 rct[offset_l] = 0;
121 rct[offset_r] = 0;
122 }
123 else {
124 /* Create a single pixel wide transition for anti-aliasing.
125 * Invert the distance and map its range [0, 1] to [0, pixel]. */
126 const float fac = (1.0 - distance) * size;
127
128 if (fac > 1.0) {
129 continue;
130 }
131
132 const uchar alpha = unit_float_to_uchar_clamp(fac);
133 rct[offset_l] = alpha;
134 rct[offset_r] = alpha;
135 }
136 }
137
138 /* X-axis offset to the next row. */
139 rct += 4 * (ibuf->x - size);
140 }
141 }
142 }
143 }
144 #endif /* WITH_HEADLESS */
145
wm_block_splash_image(int width,int * r_height)146 static ImBuf *wm_block_splash_image(int width, int *r_height)
147 {
148 #ifndef WITH_HEADLESS
149 extern char datatoc_splash_png[];
150 extern int datatoc_splash_png_size;
151
152 ImBuf *ibuf = NULL;
153 if (U.app_template[0] != '\0') {
154 char splash_filepath[FILE_MAX];
155 char template_directory[FILE_MAX];
156 if (BKE_appdir_app_template_id_search(
157 U.app_template, template_directory, sizeof(template_directory))) {
158 BLI_join_dirfile(splash_filepath, sizeof(splash_filepath), template_directory, "splash.png");
159 ibuf = IMB_loadiffname(splash_filepath, IB_rect, NULL);
160 }
161 }
162
163 if (ibuf == NULL) {
164 const uchar *splash_data = (const uchar *)datatoc_splash_png;
165 size_t splash_data_size = datatoc_splash_png_size;
166 ibuf = IMB_ibImageFromMemory(splash_data, splash_data_size, IB_rect, NULL, "<splash screen>");
167 }
168
169 int height = 0;
170 if (ibuf) {
171 height = (width * ibuf->y) / ibuf->x;
172 if (width != ibuf->x || height != ibuf->y) {
173 IMB_scaleImBuf(ibuf, width, height);
174 }
175
176 wm_block_splash_image_roundcorners_add(ibuf);
177 IMB_premultiply_alpha(ibuf);
178 }
179
180 *r_height = height;
181
182 return ibuf;
183 #else
184 UNUSED_VARS(width, r_height);
185 return NULL;
186 #endif
187 }
188
wm_block_create_splash(bContext * C,ARegion * region,void * UNUSED (arg))189 static uiBlock *wm_block_create_splash(bContext *C, ARegion *region, void *UNUSED(arg))
190 {
191 const uiStyle *style = UI_style_get_dpi();
192
193 uiBlock *block = UI_block_begin(C, region, "splash", UI_EMBOSS);
194
195 /* note on UI_BLOCK_NO_WIN_CLIP, the window size is not always synchronized
196 * with the OS when the splash shows, window clipping in this case gives
197 * ugly results and clipping the splash isn't useful anyway, just disable it T32938. */
198 UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP);
199 UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
200
201 const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
202 int splash_width = text_points_max * 45 * U.dpi_fac;
203 CLAMP_MAX(splash_width, CTX_wm_window(C)->sizex * 0.7f);
204 int splash_height;
205
206 /* Would be nice to support caching this, so it only has to be re-read (and likely resized) on
207 * first draw or if the image changed. */
208 ImBuf *ibuf = wm_block_splash_image(splash_width, &splash_height);
209
210 uiBut *but = uiDefButImage(
211 block, ibuf, 0, 0.5f * U.widget_unit, splash_width, splash_height, NULL);
212
213 UI_but_func_set(but, wm_block_close, block, NULL);
214
215 wm_block_splash_add_label(
216 block, BKE_blender_version_string(), splash_width, splash_height - 13.0 * U.dpi_fac);
217
218 const int layout_margin_x = U.dpi_fac * 26;
219 uiLayout *layout = UI_block_layout(block,
220 UI_LAYOUT_VERTICAL,
221 UI_LAYOUT_PANEL,
222 layout_margin_x,
223 0,
224 splash_width - (layout_margin_x * 2),
225 U.dpi_fac * 110,
226 0,
227 style);
228
229 MenuType *mt = WM_menutype_find("WM_MT_splash", true);
230 if (mt) {
231 UI_menutype_draw(C, mt, layout);
232 }
233
234 UI_block_bounds_set_centered(block, 0);
235
236 return block;
237 }
238
wm_splash_invoke(bContext * C,wmOperator * UNUSED (op),const wmEvent * UNUSED (event))239 static int wm_splash_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
240 {
241 UI_popup_block_invoke(C, wm_block_create_splash, NULL, NULL);
242
243 return OPERATOR_FINISHED;
244 }
245
WM_OT_splash(wmOperatorType * ot)246 void WM_OT_splash(wmOperatorType *ot)
247 {
248 ot->name = "Splash Screen";
249 ot->idname = "WM_OT_splash";
250 ot->description = "Open the splash screen with release info";
251
252 ot->invoke = wm_splash_invoke;
253 ot->poll = WM_operator_winactive;
254 }
255
wm_block_create_about(bContext * C,ARegion * region,void * UNUSED (arg))256 static uiBlock *wm_block_create_about(bContext *C, ARegion *region, void *UNUSED(arg))
257 {
258 const uiStyle *style = UI_style_get_dpi();
259 const short logo_size = 128 * U.dpi_fac;
260 const int text_points_max = MAX2(style->widget.points, style->widgetlabel.points);
261 const int dialog_width = logo_size + (text_points_max * 32 * U.dpi_fac);
262
263 /* Calculate icon column factor. */
264 const float split_factor = (float)logo_size / (float)(dialog_width - style->columnspace);
265
266 uiBlock *block = UI_block_begin(C, region, "about", UI_EMBOSS);
267
268 UI_block_flag_enable(
269 block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_LOOP | UI_BLOCK_NO_WIN_CLIP | UI_BLOCK_NUMSELECT);
270 UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
271 UI_block_emboss_set(block, UI_EMBOSS);
272
273 uiLayout *block_layout = UI_block_layout(
274 block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, dialog_width, 0, 0, style);
275
276 /* Split layout to put Blender logo on left side. */
277 uiLayout *split_block = uiLayoutSplit(block_layout, split_factor, false);
278
279 /* Blender Logo. */
280 uiLayout *layout = uiLayoutColumn(split_block, false);
281 uiDefButAlert(block, ALERT_ICON_BLENDER, 0, 0, 0, logo_size);
282
283 /* The rest of the content on the right. */
284 layout = uiLayoutColumn(split_block, false);
285
286 uiLayoutSetScaleY(layout, 0.7f);
287
288 uiItemS_ex(layout, 1.0f);
289
290 /* Title. */
291 uiItemL_ex(layout, "Blender", ICON_NONE, true, false);
292
293 /* Version. */
294 uiItemL(layout, BKE_blender_version_string(), ICON_NONE);
295
296 uiItemS_ex(layout, 3.0f);
297
298 #ifdef WITH_BUILDINFO
299
300 extern char build_hash[], build_commit_date[], build_commit_time[], build_branch[];
301
302 char str_buf[256] = "\0";
303 BLI_snprintf(str_buf, sizeof(str_buf), "Date: %s %s", build_commit_date, build_commit_time);
304 uiItemL(layout, str_buf, ICON_NONE);
305
306 BLI_snprintf(str_buf, sizeof(str_buf), "Hash: %s", build_hash);
307 uiItemL(layout, str_buf, ICON_NONE);
308
309 BLI_snprintf(str_buf, sizeof(str_buf), "Branch: %s", build_branch);
310 uiItemL(layout, str_buf, ICON_NONE);
311
312 #endif /* WITH_BUILDINFO */
313
314 uiItemS_ex(layout, 1.5f);
315
316 MenuType *mt = WM_menutype_find("WM_MT_splash_about", true);
317 if (mt) {
318 UI_menutype_draw(C, mt, layout);
319 }
320
321 uiItemS_ex(layout, 2.0f);
322
323 UI_block_bounds_set_centered(block, 14 * U.dpi_fac);
324
325 return block;
326 }
327
wm_about_invoke(bContext * C,wmOperator * UNUSED (op),const wmEvent * UNUSED (event))328 static int wm_about_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event))
329 {
330 UI_popup_block_invoke(C, wm_block_create_about, NULL, NULL);
331
332 return OPERATOR_FINISHED;
333 }
334
WM_OT_splash_about(wmOperatorType * ot)335 void WM_OT_splash_about(wmOperatorType *ot)
336 {
337 ot->name = "About Blender";
338 ot->idname = "WM_OT_splash_about";
339 ot->description = "Open a window with information about Blender";
340
341 ot->invoke = wm_about_invoke;
342 ot->poll = WM_operator_winactive;
343 }
344