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