Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/windowmanager/intern')
-rw-r--r--source/blender/windowmanager/intern/wm_cursors.c47
-rw-r--r--source/blender/windowmanager/intern/wm_event_system.c32
-rw-r--r--source/blender/windowmanager/intern/wm_init_exit.c5
-rw-r--r--source/blender/windowmanager/intern/wm_operator_props.c10
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c92
-rw-r--r--source/blender/windowmanager/intern/wm_platform_support.c219
-rw-r--r--source/blender/windowmanager/intern/wm_platform_support.h30
-rw-r--r--source/blender/windowmanager/intern/wm_splash_screen.c108
-rw-r--r--source/blender/windowmanager/intern/wm_window.c225
-rw-r--r--source/blender/windowmanager/intern/wm_window_private.h42
10 files changed, 641 insertions, 169 deletions
diff --git a/source/blender/windowmanager/intern/wm_cursors.c b/source/blender/windowmanager/intern/wm_cursors.c
index 4b1abeceebb..8e796a7981a 100644
--- a/source/blender/windowmanager/intern/wm_cursors.c
+++ b/source/blender/windowmanager/intern/wm_cursors.c
@@ -192,6 +192,15 @@ void WM_cursor_set(wmWindow *win, int curs)
GHOST_TStandardCursor ghost_cursor = convert_to_ghost_standard_cursor(curs);
+#if !defined(_WIN32) && !defined(__APPLE__)
+ /* Workaround crosshair cursors with bad visibility in some cursor themes.
+ * Better solution would be to always use custom cursors and support DPI
+ * properly so that the cursors look as good as the native ones. */
+ if (U.curssize && ghost_cursor == GHOST_kStandardCursorCrosshair) {
+ ghost_cursor = GHOST_kStandardCursorCustom;
+ }
+#endif
+
if (ghost_cursor != GHOST_kStandardCursorCustom &&
GHOST_HasCursorShape(win->ghostwin, ghost_cursor)) {
/* Use native GHOST cursor when available. */
@@ -695,38 +704,40 @@ void wm_init_cursor_data(void)
/****************** Normal Cross Cursor ************************/
BEGIN_CURSOR_BLOCK;
static char cross_sbm[] = {
- 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
- 0x01, 0x00, 0x00, 0x3f, 0xfc, 0x3f, 0xfc, 0x00, 0x00, 0x80, 0x01,
- 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01,
+ 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
+ 0x01, 0x00, 0x00, 0x3e, 0x7c, 0x3e, 0x7c, 0x00, 0x00, 0x80, 0x01,
+ 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00,
+
};
static char cross_smsk[] = {
0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0,
- 0x03, 0x7f, 0xfe, 0x3f, 0xfc, 0x3f, 0xfc, 0x7f, 0xfe, 0xc0, 0x03,
+ 0x03, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0xff, 0xff, 0xc0, 0x03,
0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xc0, 0x03,
};
static char cross_lbm[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01,
0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
- 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x40, 0x02, 0x00, 0x00, 0x78, 0x1e, 0x00,
- 0xfc, 0x1f, 0xf8, 0x3f, 0xfc, 0x1f, 0xf8, 0x3f, 0x00, 0x78, 0x1e, 0x00, 0x00, 0x40, 0x02,
- 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
+ 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xfe, 0x3f, 0xfc, 0x7f, 0xfe, 0x3f, 0xfc, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x01,
+ 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+
};
static char cross_lmsk[] = {
- 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01,
- 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
- 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
- 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
- 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01,
- 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80,
- 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00,
- 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
- 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03,
+ 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0,
+ 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
+ 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x7f, 0xfe, 0xff, 0xff, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0xc0, 0x03,
+ 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0,
+ 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00,
+ 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0xc0, 0x03, 0x00,
};
static BCursor CrossCursor = {
diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c
index 6b4327d5f44..a744cfb8c28 100644
--- a/source/blender/windowmanager/intern/wm_event_system.c
+++ b/source/blender/windowmanager/intern/wm_event_system.c
@@ -2352,7 +2352,8 @@ static int wm_handler_fileselect_do(bContext *C,
U.file_space_data.temp_win_sizex * UI_DPI_FAC,
U.file_space_data.temp_win_sizey * UI_DPI_FAC,
SPACE_FILE,
- U.filebrowser_display_type))) {
+ U.filebrowser_display_type,
+ true))) {
ARegion *region_header = BKE_area_find_region_type(area, RGN_TYPE_HEADER);
BLI_assert(area->spacetype == SPACE_FILE);
@@ -2737,7 +2738,10 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
/* Clear the tool-tip whenever a key binding is handled, without this tool-tips
* are kept when a modal operators starts (annoying but otherwise harmless). */
if (action & WM_HANDLER_BREAK) {
- WM_tooltip_clear(C, CTX_wm_window(C));
+ /* Window may be gone after file read. */
+ if (CTX_wm_window(C) != NULL) {
+ WM_tooltip_clear(C, CTX_wm_window(C));
+ }
}
}
else if (handler_base->type == WM_HANDLER_TYPE_UI) {
@@ -4210,19 +4214,31 @@ static void wm_eventemulation(wmEvent *event, bool test_only)
if (U.flag & USER_TWOBUTTONMOUSE) {
if (event->type == LEFTMOUSE) {
- if (event->val == KM_PRESS && event->alt) {
- event->type = MIDDLEMOUSE;
- event->alt = 0;
+ short *mod = (
+#if !defined(WIN32)
+ (U.mouse_emulate_3_button_modifier == USER_EMU_MMB_MOD_OSKEY) ? &event->oskey :
+ &event->alt
+#else
+ /* Disable for WIN32 for now because it accesses the start menu. */
+ &event->alt
+#endif
+ );
- if (!test_only) {
- emulating_event = MIDDLEMOUSE;
+ if (event->val == KM_PRESS) {
+ if (*mod) {
+ *mod = 0;
+ event->type = MIDDLEMOUSE;
+
+ if (!test_only) {
+ emulating_event = MIDDLEMOUSE;
+ }
}
}
else if (event->val == KM_RELEASE) {
/* only send middle-mouse release if emulated */
if (emulating_event == MIDDLEMOUSE) {
event->type = MIDDLEMOUSE;
- event->alt = 0;
+ *mod = 0;
}
if (!test_only) {
diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c
index 70d83153840..c8c35ba1bfc 100644
--- a/source/blender/windowmanager/intern/wm_init_exit.c
+++ b/source/blender/windowmanager/intern/wm_init_exit.c
@@ -99,6 +99,7 @@
#include "wm.h"
#include "wm_files.h"
#include "wm_window.h"
+#include "wm_platform_support.h"
#include "ED_anim_api.h"
#include "ED_armature.h"
@@ -314,6 +315,10 @@ void WM_init(bContext *C, int argc, const char **argv)
#endif
WM_init_opengl(G_MAIN);
+ if (!WM_platform_support_perform_checks()) {
+ exit(-1);
+ }
+
UI_init();
}
diff --git a/source/blender/windowmanager/intern/wm_operator_props.c b/source/blender/windowmanager/intern/wm_operator_props.c
index a8feb22cbf8..21636153904 100644
--- a/source/blender/windowmanager/intern/wm_operator_props.c
+++ b/source/blender/windowmanager/intern/wm_operator_props.c
@@ -396,6 +396,16 @@ void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
+void WM_operator_properties_generic_select(wmOperatorType *ot)
+{
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "wait_to_deselect_others", false, "Wait to Deselect Others", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
+ RNA_def_int(ot->srna, "mouse_x", 0, INT_MIN, INT_MAX, "Mouse X", "", INT_MIN, INT_MAX);
+ RNA_def_int(ot->srna, "mouse_y", 0, INT_MIN, INT_MAX, "Mouse Y", "", INT_MIN, INT_MAX);
+}
+
void WM_operator_properties_gesture_box_zoom(wmOperatorType *ot)
{
WM_operator_properties_border(ot);
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index 285379e90c6..292e27c3cbf 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -700,6 +700,82 @@ void WM_operator_properties_free(PointerRNA *ptr)
/** \name Default Operator Callbacks
* \{ */
+int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ PropertyRNA *wait_to_deselect_prop = RNA_struct_find_property(op->ptr,
+ "wait_to_deselect_others");
+ const short init_event_type = (short)POINTER_AS_INT(op->customdata);
+ int ret_value = 0;
+
+ /* get settings from RNA properties for operator */
+ int mval[2];
+ mval[0] = RNA_int_get(op->ptr, "mouse_x");
+ mval[1] = RNA_int_get(op->ptr, "mouse_y");
+
+ if (init_event_type == 0) {
+ if (event->val == KM_PRESS) {
+ RNA_property_boolean_set(op->ptr, wait_to_deselect_prop, true);
+
+ ret_value = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(ret_value);
+
+ op->customdata = POINTER_FROM_INT((int)event->type);
+ if (ret_value & OPERATOR_RUNNING_MODAL) {
+ WM_event_add_modal_handler(C, op);
+ }
+ return ret_value | OPERATOR_PASS_THROUGH;
+ }
+ else {
+ /* If we are in init phase, and cannot validate init of modal operations,
+ * just fall back to basic exec.
+ */
+ RNA_property_boolean_set(op->ptr, wait_to_deselect_prop, false);
+
+ ret_value = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(ret_value);
+
+ return ret_value | OPERATOR_PASS_THROUGH;
+ }
+ }
+ else if (event->type == init_event_type && event->val == KM_RELEASE) {
+ RNA_property_boolean_set(op->ptr, wait_to_deselect_prop, false);
+
+ ret_value = op->type->exec(C, op);
+ OPERATOR_RETVAL_CHECK(ret_value);
+
+ return ret_value | OPERATOR_PASS_THROUGH;
+ }
+ else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) {
+ const int drag_delta[2] = {
+ mval[0] - event->mval[0],
+ mval[1] - event->mval[1],
+ };
+ /* If user moves mouse more than defined threshold, we consider select operator as
+ * finished. Otherwise, it is still running until we get an 'release' event. In any
+ * case, we pass through event, but select op is not finished yet. */
+ if (WM_event_drag_test_with_delta(event, drag_delta)) {
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+ }
+ else {
+ /* Important not to return anything other than PASS_THROUGH here,
+ * otherwise it prevents underlying tweak detection code to work properly. */
+ return OPERATOR_PASS_THROUGH;
+ }
+ }
+
+ return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
+}
+
+int WM_generic_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ RNA_int_set(op->ptr, "mouse_x", event->mval[0]);
+ RNA_int_set(op->ptr, "mouse_y", event->mval[1]);
+
+ op->customdata = POINTER_FROM_INT(0);
+
+ return op->type->modal(C, op, event);
+}
+
void WM_operator_view3d_unit_defaults(struct bContext *C, struct wmOperator *op)
{
if (op->flag & OP_IS_INVOKE) {
@@ -2621,38 +2697,38 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even
}
else {
delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0];
- delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1];
+ delta[1] = 0.0f;
if (rc->zoom_prop) {
RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
delta[0] /= zoom[0];
- delta[1] /= zoom[1];
}
dist = len_v2(delta);
delta[0] = event->x - rc->slow_mouse[0];
- delta[1] = event->y - rc->slow_mouse[1];
if (rc->zoom_prop) {
delta[0] /= zoom[0];
- delta[1] /= zoom[1];
}
- dist = dist + 0.1f * (delta[0] + delta[1]);
+ dist = dist + 0.1f * (delta[0]);
}
}
else {
delta[0] = rc->initial_mouse[0] - event->x;
delta[1] = rc->initial_mouse[1] - event->y;
-
if (rc->zoom_prop) {
RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom);
delta[0] /= zoom[0];
delta[1] /= zoom[1];
}
-
- dist = len_v2(delta);
+ if (rc->subtype == PROP_ANGLE) {
+ dist = len_v2(delta);
+ }
+ else {
+ dist = clamp_f(-delta[0], 0.0f, FLT_MAX);
+ }
}
/* calculate new value and apply snapping */
diff --git a/source/blender/windowmanager/intern/wm_platform_support.c b/source/blender/windowmanager/intern/wm_platform_support.c
new file mode 100644
index 00000000000..94eceafc59b
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_platform_support.c
@@ -0,0 +1,219 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#include "wm_platform_support.h"
+#include "wm_window_private.h"
+
+#include <string.h>
+
+#include "BLI_sys_types.h"
+#include "BLI_dynstr.h"
+#include "BLI_path_util.h"
+#include "BLI_fileops.h"
+#include "BLI_string.h"
+#include "BLI_linklist.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_appdir.h"
+#include "BKE_global.h"
+
+#include "GPU_platform.h"
+
+#include "GHOST_C-api.h"
+
+#define WM_PLATFORM_SUPPORT_TEXT_SIZE 1024
+
+/* Check if user has already approved the given platform_support_key. */
+static bool wm_platform_support_check_approval(const char *platform_support_key, bool update)
+{
+ const char *const cfgdir = BKE_appdir_folder_id(BLENDER_USER_CONFIG, NULL);
+ bool result = false;
+
+ if (G.factory_startup) {
+ return result;
+ }
+
+ if (cfgdir) {
+ char filepath[FILE_MAX];
+ BLI_make_file_string("/", filepath, cfgdir, BLENDER_PLATFORM_SUPPORT_FILE);
+ LinkNode *lines = BLI_file_read_as_lines(filepath);
+ for (LinkNode *line_node = lines; line_node; line_node = line_node->next) {
+ char *line = line_node->link;
+ if (STREQ(line, platform_support_key)) {
+ result = true;
+ break;
+ }
+ }
+
+ if (!result && update) {
+ FILE *fp = BLI_fopen(filepath, "a");
+ if (fp) {
+ fprintf(fp, "%s\n", platform_support_key);
+ fclose(fp);
+ }
+ }
+
+ BLI_file_free_lines(lines);
+ }
+ return result;
+}
+
+static void wm_platform_support_create_link(char *link)
+{
+ DynStr *ds = BLI_dynstr_new();
+
+ BLI_dynstr_append(ds, "https://docs.blender.org/manual/en/dev/troubleshooting/gpu/");
+#if defined(_WIN32)
+ BLI_dynstr_append(ds, "windows/");
+#elif defined(__APPLE__)
+ BLI_dynstr_append(ds, "apple/");
+#else /* UNIX */
+ BLI_dynstr_append(ds, "linux/");
+#endif
+
+ if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ BLI_dynstr_append(ds, "intel.html");
+ }
+ else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ BLI_dynstr_append(ds, "nvidia.html");
+ }
+ else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
+ BLI_dynstr_append(ds, "amd.html");
+ }
+ else {
+ BLI_dynstr_append(ds, "unknown.html");
+ }
+
+ BLI_assert(BLI_dynstr_get_len(ds) < WM_PLATFORM_SUPPORT_TEXT_SIZE);
+ BLI_dynstr_get_cstring_ex(ds, link);
+ BLI_dynstr_free(ds);
+}
+
+bool WM_platform_support_perform_checks()
+{
+ char title[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+ char message[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+ char link[WM_PLATFORM_SUPPORT_TEXT_SIZE];
+
+ bool result = true;
+
+ eGPUSupportLevel support_level = GPU_platform_support_level();
+ const char *platform_key = GPU_platform_support_level_key();
+
+ /* check if previous check matches the current check. Don't update the approval when running in
+ * `background`. this could have been triggered by installing addons via installers. */
+ if (support_level != GPU_SUPPORT_LEVEL_UNSUPPORTED && !G.factory_startup &&
+ wm_platform_support_check_approval(platform_key, !G.background)) {
+ /* if it matches the user has confirmed and whishes to use it */
+ return result;
+ }
+
+ /* update the message and link based on the found support level */
+ GHOST_DialogOptions dialog_options = 0;
+
+ switch (support_level) {
+ default:
+ case GPU_SUPPORT_LEVEL_SUPPORTED:
+ break;
+
+ case GPU_SUPPORT_LEVEL_LIMITED: {
+ size_t slen = 0;
+ STR_CONCAT(title, slen, "Blender - ");
+ STR_CONCAT(
+ title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Limited Platform Support"));
+ slen = 0;
+ STR_CONCAT(
+ message,
+ slen,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+ "Your graphics card or driver has limited support. It may work, but with "
+ "issues."));
+
+ /* TODO: Extra space is needed for the split function in GHOST_SystemX11. We should change
+ * the behavior in GHOST_SystemX11. */
+ STR_CONCAT(message, slen, "\n \n");
+ STR_CONCAT(
+ message,
+ slen,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+ "Newer graphics drivers may be available to improve Blender support."));
+ STR_CONCAT(message, slen, "\n \n");
+ STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
+ STR_CONCAT(message, slen, GPU_platform_gpu_name());
+
+ dialog_options = GHOST_DialogWarning;
+ break;
+ }
+
+ case GPU_SUPPORT_LEVEL_UNSUPPORTED: {
+ size_t slen = 0;
+ STR_CONCAT(title, slen, "Blender - ");
+ STR_CONCAT(
+ title, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Platform Unsupported"));
+ slen = 0;
+ STR_CONCAT(message,
+ slen,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+ "Your graphics card or driver is not supported."));
+
+ STR_CONCAT(message, slen, "\n \n");
+ STR_CONCAT(
+ message,
+ slen,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER,
+ "Newer graphics drivers may be available to improve Blender support."));
+
+ STR_CONCAT(message, slen, "\n \n");
+ STR_CONCAT(message, slen, CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "Graphics card:\n"));
+ STR_CONCAT(message, slen, GPU_platform_gpu_name());
+ STR_CONCAT(message, slen, "\n \n");
+
+ STR_CONCAT(message,
+ slen,
+ CTX_IFACE_(BLT_I18NCONTEXT_ID_WINDOWMANAGER, "The program will now close."));
+ dialog_options = GHOST_DialogError;
+ result = false;
+ break;
+ }
+ }
+ wm_platform_support_create_link(link);
+
+ bool show_message = ELEM(
+ support_level, GPU_SUPPORT_LEVEL_LIMITED, GPU_SUPPORT_LEVEL_UNSUPPORTED);
+
+ /* We are running in the background print the message in the console. */
+ if ((G.background || G.debug & G_DEBUG) && show_message) {
+ printf("%s\n\n%s\n%s\n", title, message, link);
+ }
+ if (G.background) {
+ /* Don't show the message-box when running in background mode.
+ * Printing to console is enough. */
+ result = true;
+ }
+ else if (show_message) {
+ WM_ghost_show_message_box(
+ title, message, "Find Latest Drivers", "Continue Anyway", link, dialog_options);
+ }
+
+ return result;
+}
diff --git a/source/blender/windowmanager/intern/wm_platform_support.h b/source/blender/windowmanager/intern/wm_platform_support.h
new file mode 100644
index 00000000000..a8e20f6bcdf
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_platform_support.h
@@ -0,0 +1,30 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#ifndef __WM_PLATFORM_SUPPORT_H__
+#define __WM_PLATFORM_SUPPORT_H__
+
+#include "BLI_sys_types.h"
+
+bool WM_platform_support_perform_checks(void);
+
+#endif
diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c
index d3f7661a008..662238b4ae1 100644
--- a/source/blender/windowmanager/intern/wm_splash_screen.c
+++ b/source/blender/windowmanager/intern/wm_splash_screen.c
@@ -178,68 +178,79 @@ static void wm_block_splash_add_labels(uiBlock *block, int x, int y)
#endif /* WITH_BUILDINFO */
}
-static ImBuf *wm_block_splash_image(void)
+static ImBuf *wm_block_splash_image(int r_unit_size[2])
{
#ifndef WITH_HEADLESS
extern char datatoc_splash_png[];
extern int datatoc_splash_png_size;
extern char datatoc_splash_2x_png[];
extern int datatoc_splash_2x_png_size;
+ const bool is_2x = U.dpi_fac > 1.0;
+ const int imb_scale = is_2x ? 2 : 1;
- ImBuf *ibuf = NULL;
+ /* We could allow this to be variable,
+ * for now don't since allowing it might create layout issues.
+ *
+ * Only check width because splashes sometimes change height
+ * and we don't want to break app-templates. */
+ const int x_expect = 501 * imb_scale;
- if (U.dpi_fac > 1.0) {
- ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_2x_png,
- datatoc_splash_2x_png_size,
- IB_rect,
- NULL,
- "<splash screen>");
- }
- else {
- ibuf = IMB_ibImageFromMemory((const uchar *)datatoc_splash_png,
- datatoc_splash_png_size,
- IB_rect,
- NULL,
- "<splash screen>");
- }
+ ImBuf *ibuf = NULL;
- /* overwrite splash with template image */
if (U.app_template[0] != '\0') {
- ImBuf *ibuf_template = NULL;
char splash_filepath[FILE_MAX];
char template_directory[FILE_MAX];
-
if (BKE_appdir_app_template_id_search(
U.app_template, template_directory, sizeof(template_directory))) {
BLI_join_dirfile(splash_filepath,
sizeof(splash_filepath),
template_directory,
- (U.dpi_fac > 1.0) ? "splash_2x.png" : "splash.png");
- ibuf_template = IMB_loadiffname(splash_filepath, IB_rect, NULL);
- if (ibuf_template) {
- const int x_expect = ibuf->x;
- const int y_expect = 250 * (int)U.dpi_fac;
- /* don't cover the header text */
- if (ibuf_template->x == x_expect && ibuf_template->y == y_expect) {
- memcpy(ibuf->rect,
- ibuf_template->rect,
- ibuf_template->x * ibuf_template->y * sizeof(char[4]));
- }
- else {
- CLOG_ERROR(WM_LOG_OPERATORS,
- "Splash expected %dx%d found %dx%d, ignoring: %s\n",
- x_expect,
- y_expect,
- ibuf_template->x,
- ibuf_template->y,
- splash_filepath);
- }
- IMB_freeImBuf(ibuf_template);
+ is_2x ? "splash_2x.png" : "splash.png");
+ ibuf = IMB_loadiffname(splash_filepath, IB_rect, NULL);
+
+ /* We could skip this check, see comment about 'x_expect' above. */
+ if (ibuf->x != x_expect) {
+ CLOG_ERROR(WM_LOG_OPERATORS,
+ "Splash expected %d width found %d, ignoring: %s\n",
+ x_expect,
+ ibuf->x,
+ splash_filepath);
+ IMB_freeImBuf(ibuf);
+ ibuf = NULL;
}
}
}
+
+ if (ibuf == NULL) {
+ const uchar *splash_data;
+ size_t splash_data_size;
+
+ if (is_2x) {
+ splash_data = (const uchar *)datatoc_splash_2x_png;
+ splash_data_size = datatoc_splash_2x_png_size;
+ }
+ else {
+ splash_data = (const uchar *)datatoc_splash_png;
+ splash_data_size = datatoc_splash_png_size;
+ }
+
+ ibuf = IMB_ibImageFromMemory(splash_data, splash_data_size, IB_rect, NULL, "<splash screen>");
+
+ BLI_assert(ibuf->x == x_expect);
+ }
+
+ if (is_2x) {
+ r_unit_size[0] = ibuf->x / 2;
+ r_unit_size[1] = ibuf->y / 2;
+ }
+ else {
+ r_unit_size[0] = ibuf->x;
+ r_unit_size[1] = ibuf->y;
+ }
+
return ibuf;
#else
+ UNUSED_VARS(r_unit_size);
return NULL;
#endif
}
@@ -258,15 +269,17 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
UI_block_flag_enable(block, UI_BLOCK_LOOP | UI_BLOCK_KEEP_OPEN | UI_BLOCK_NO_WIN_CLIP);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
- ImBuf *ibuf = wm_block_splash_image();
+ /* Size before dpi scaling (halved for hi-dpi image). */
+ int ibuf_unit_size[2];
+ ImBuf *ibuf = wm_block_splash_image(ibuf_unit_size);
but = uiDefBut(block,
UI_BTYPE_IMAGE,
0,
"",
0,
0.5f * U.widget_unit,
- U.dpi_fac * 501,
- U.dpi_fac * 250,
+ U.dpi_fac * ibuf_unit_size[0],
+ U.dpi_fac * ibuf_unit_size[1],
/* Button owns the imbuf now. */
ibuf,
0.0,
@@ -277,17 +290,18 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *ar, void *UNUSED(ar
UI_but_func_set(but, wm_block_splash_close, block, NULL);
UI_block_func_set(block, wm_block_splash_refreshmenu, block, NULL);
- int x = U.dpi_fac * 502;
- int y = U.dpi_fac * 237;
+ int x = U.dpi_fac * (ibuf_unit_size[0] + 1);
+ int y = U.dpi_fac * (ibuf_unit_size[1] - 13);
wm_block_splash_add_labels(block, x, y);
+ const int layout_margin_x = U.dpi_fac * 26;
uiLayout *layout = UI_block_layout(block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
- U.dpi_fac * 26,
+ layout_margin_x,
0,
- U.dpi_fac * 450,
+ (U.dpi_fac * ibuf_unit_size[0]) - (layout_margin_x * 2),
U.dpi_fac * 110,
0,
style);
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 2c26a15dce0..fa2320585d7 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -61,7 +61,9 @@
#include "wm.h"
#include "wm_draw.h"
#include "wm_files.h"
+#include "wm_platform_support.h"
#include "wm_window.h"
+#include "wm_window_private.h"
#include "wm_event_system.h"
#include "ED_anim_api.h"
@@ -78,7 +80,7 @@
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
#include "GPU_draw.h"
-#include "GPU_extensions.h"
+#include "GPU_platform.h"
#include "GPU_framebuffer.h"
#include "GPU_init_exit.h"
#include "GPU_immediate.h"
@@ -409,8 +411,10 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win)
/* this is event from ghost, or exit-blender op */
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
{
- /* First check if there is another main window remaining. */
wmWindow *win_other;
+ const bool is_dialog = GHOST_IsDialogWindow(win->ghostwin);
+
+ /* First check if there is another main window remaining. */
for (win_other = wm->windows.first; win_other; win_other = win_other->next) {
if (win_other != win && win_other->parent == NULL && !WM_window_is_temp_screen(win_other)) {
break;
@@ -422,10 +426,15 @@ void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
return;
}
- /* close child windows */
- for (wmWindow *win_child = wm->windows.first; win_child; win_child = win_child->next) {
- if (win_child->parent == win) {
- wm_window_close(C, wm, win_child);
+ /* Close child windows and bring windows back to front that dialogs have pushed behind the main
+ * window. */
+ for (wmWindow *iter_win = wm->windows.first; iter_win; iter_win = iter_win->next) {
+ if (iter_win->parent == win) {
+ wm_window_close(C, wm, iter_win);
+ }
+ else if (is_dialog && iter_win != win && iter_win->parent &&
+ (GHOST_GetWindowState(iter_win->ghostwin) != GHOST_kWindowStateMinimized)) {
+ wm_window_raise(iter_win);
}
}
@@ -547,7 +556,10 @@ static void wm_window_ensure_eventstate(wmWindow *win)
}
/* belongs to below */
-static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wmWindow *win)
+static void wm_window_ghostwindow_add(wmWindowManager *wm,
+ const char *title,
+ wmWindow *win,
+ bool is_dialog)
{
GHOST_WindowHandle ghostwin;
GHOST_GLSettings glSettings = {0};
@@ -569,15 +581,29 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
wmWindow *prev_windrawable = wm->windrawable;
wm_window_clear_drawable(wm);
- ghostwin = GHOST_CreateWindow(g_system,
- title,
- win->posx,
- posy,
- win->sizex,
- win->sizey,
- (GHOST_TWindowState)win->windowstate,
- GHOST_kDrawingContextTypeOpenGL,
- glSettings);
+ if (is_dialog && win->parent) {
+ ghostwin = GHOST_CreateDialogWindow(g_system,
+ win->parent->ghostwin,
+ title,
+ win->posx,
+ posy,
+ win->sizex,
+ win->sizey,
+ (GHOST_TWindowState)win->windowstate,
+ GHOST_kDrawingContextTypeOpenGL,
+ glSettings);
+ }
+ else {
+ ghostwin = GHOST_CreateWindow(g_system,
+ title,
+ win->posx,
+ posy,
+ win->sizex,
+ win->sizey,
+ (GHOST_TWindowState)win->windowstate,
+ GHOST_kDrawingContextTypeOpenGL,
+ glSettings);
+ }
if (ghostwin) {
GHOST_RectangleHandle bounds;
@@ -635,6 +661,68 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
}
}
+static void wm_window_ghostwindow_ensure(wmWindowManager *wm, wmWindow *win, bool is_dialog)
+{
+ wmKeyMap *keymap;
+
+ if (win->ghostwin == NULL) {
+ if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
+ win->posx = wm_init_state.start_x;
+ win->posy = wm_init_state.start_y;
+ win->sizex = wm_init_state.size_x;
+ win->sizey = wm_init_state.size_y;
+
+ if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
+ win->windowstate = GHOST_kWindowStateNormal;
+ wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
+ }
+ else {
+ win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
+ }
+ }
+
+ if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
+ win->windowstate = wm_init_state.windowstate;
+ wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
+ }
+
+ /* without this, cursor restore may fail, T45456 */
+ if (win->cursor == 0) {
+ win->cursor = WM_CURSOR_DEFAULT;
+ }
+
+ wm_window_ghostwindow_add(wm, "Blender", win, is_dialog);
+ }
+
+ if (win->ghostwin != NULL) {
+ /* If we have no ghostwin this is a buggy window that should be removed.
+ * However we still need to initialize it correctly so the screen doesn't hang. */
+
+ /* happens after fileread */
+ wm_window_ensure_eventstate(win);
+ }
+
+ /* add keymap handlers (1 handler for all keys in map!) */
+ keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
+ WM_event_add_keymap_handler(&win->handlers, keymap);
+
+ keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
+ WM_event_add_keymap_handler(&win->handlers, keymap);
+
+ keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
+ WM_event_add_keymap_handler(&win->modalhandlers, keymap);
+
+ /* add drop boxes */
+ {
+ ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
+ WM_event_add_dropbox_handler(&win->handlers, lb);
+ }
+ wm_window_title(wm, win);
+
+ /* add topbar */
+ ED_screen_global_areas_refresh(win);
+}
+
/**
* Initialize #wmWindow without ghostwin, open these and clear.
*
@@ -650,9 +738,6 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, const char *title, wm
*/
void wm_window_ghostwindows_ensure(wmWindowManager *wm)
{
- wmKeyMap *keymap;
- wmWindow *win;
-
BLI_assert(G.background == false);
/* No command-line prefsize? then we set this.
@@ -682,63 +767,8 @@ void wm_window_ghostwindows_ensure(wmWindowManager *wm)
#endif
}
- for (win = wm->windows.first; win; win = win->next) {
- if (win->ghostwin == NULL) {
- if ((win->sizex == 0) || (wm_init_state.override_flag & WIN_OVERRIDE_GEOM)) {
- win->posx = wm_init_state.start_x;
- win->posy = wm_init_state.start_y;
- win->sizex = wm_init_state.size_x;
- win->sizey = wm_init_state.size_y;
-
- if (wm_init_state.override_flag & WIN_OVERRIDE_GEOM) {
- win->windowstate = GHOST_kWindowStateNormal;
- wm_init_state.override_flag &= ~WIN_OVERRIDE_GEOM;
- }
- else {
- win->windowstate = GHOST_WINDOW_STATE_DEFAULT;
- }
- }
-
- if (wm_init_state.override_flag & WIN_OVERRIDE_WINSTATE) {
- win->windowstate = wm_init_state.windowstate;
- wm_init_state.override_flag &= ~WIN_OVERRIDE_WINSTATE;
- }
-
- /* without this, cursor restore may fail, T45456 */
- if (win->cursor == 0) {
- win->cursor = WM_CURSOR_DEFAULT;
- }
-
- wm_window_ghostwindow_add(wm, "Blender", win);
- }
-
- if (win->ghostwin != NULL) {
- /* If we have no ghostwin this is a buggy window that should be removed.
- * However we still need to initialize it correctly so the screen doesn't hang. */
-
- /* happens after fileread */
- wm_window_ensure_eventstate(win);
- }
-
- /* add keymap handlers (1 handler for all keys in map!) */
- keymap = WM_keymap_ensure(wm->defaultconf, "Window", 0, 0);
- WM_event_add_keymap_handler(&win->handlers, keymap);
-
- keymap = WM_keymap_ensure(wm->defaultconf, "Screen", 0, 0);
- WM_event_add_keymap_handler(&win->handlers, keymap);
-
- keymap = WM_keymap_ensure(wm->defaultconf, "Screen Editing", 0, 0);
- WM_event_add_keymap_handler(&win->modalhandlers, keymap);
-
- /* add drop boxes */
- {
- ListBase *lb = WM_dropboxmap_find("Window", 0, 0);
- WM_event_add_dropbox_handler(&win->handlers, lb);
- }
- wm_window_title(wm, win);
-
- /* add topbar */
- ED_screen_global_areas_refresh(win);
+ for (wmWindow *win = wm->windows.first; win; win = win->next) {
+ wm_window_ghostwindow_ensure(wm, win, false);
}
}
@@ -795,10 +825,17 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
* \param space_type: SPACE_VIEW3D, SPACE_INFO, ... (eSpace_Type)
* \return the window or NULL in case of failure.
*/
-wmWindow *WM_window_open_temp(
- bContext *C, const char *title, int x, int y, int sizex, int sizey, int space_type)
+wmWindow *WM_window_open_temp(bContext *C,
+ const char *title,
+ int x,
+ int y,
+ int sizex,
+ int sizey,
+ int space_type,
+ bool dialog)
{
Main *bmain = CTX_data_main(C);
+ wmWindowManager *wm = CTX_wm_manager(C);
wmWindow *win_prev = CTX_wm_window(C);
wmWindow *win;
bScreen *screen;
@@ -823,9 +860,10 @@ wmWindow *WM_window_open_temp(
/* changes rect to fit within desktop */
wm_window_check_position(&rect);
- /* test if we have a temp screen already */
- for (win = CTX_wm_manager(C)->windows.first; win; win = win->next) {
- if (WM_window_is_temp_screen(win)) {
+ /* Reuse temporary or dialog window if one is open (but don't use a dialog for a regular
+ * temporary window, or vice versa). */
+ for (win = wm->windows.first; win; win = win->next) {
+ if (WM_window_is_temp_screen(win) && (dialog == GHOST_IsDialogWindow(win->ghostwin))) {
break;
}
}
@@ -843,11 +881,6 @@ wmWindow *WM_window_open_temp(
win->sizex = BLI_rcti_size_x(&rect);
win->sizey = BLI_rcti_size_y(&rect);
- if (win->ghostwin) {
- wm_window_set_size(win, win->sizex, win->sizey);
- wm_window_raise(win);
- }
-
if (WM_window_get_active_workspace(win) == NULL) {
WorkSpace *workspace = WM_window_get_active_workspace(win_prev);
BKE_workspace_active_set(win->workspace_hook, workspace);
@@ -872,6 +905,9 @@ wmWindow *WM_window_open_temp(
/* make window active, and validate/resize */
CTX_wm_window_set(C, win);
+ if (!win->ghostwin) {
+ wm_window_ghostwindow_ensure(wm, win, dialog);
+ }
WM_check(C);
/* It's possible `win->ghostwin == NULL`.
@@ -887,15 +923,18 @@ wmWindow *WM_window_open_temp(
ED_area_newspace(C, sa, space_type, false);
ED_screen_change(C, screen);
- ED_screen_refresh(CTX_wm_manager(C), win); /* test scale */
+ ED_screen_refresh(wm, win); /* test scale */
if (win->ghostwin) {
+ wm_window_set_size(win, win->sizex, win->sizey);
+ wm_window_raise(win);
+
GHOST_SetTitle(win->ghostwin, title);
return win;
}
else {
/* very unlikely! but opening a new window can fail */
- wm_window_close(C, CTX_wm_manager(C), win);
+ wm_window_close(C, wm, win);
CTX_wm_window_set(C, win_prev);
return NULL;
@@ -2394,4 +2433,14 @@ void WM_opengl_context_release(void *context)
GHOST_ReleaseOpenGLContext((GHOST_ContextHandle)context);
}
+void WM_ghost_show_message_box(const char *title,
+ const char *message,
+ const char *help_label,
+ const char *continue_label,
+ const char *link,
+ GHOST_DialogOptions dialog_options)
+{
+ BLI_assert(g_system);
+ GHOST_ShowMessageBox(g_system, title, message, help_label, continue_label, link, dialog_options);
+}
/** \} */
diff --git a/source/blender/windowmanager/intern/wm_window_private.h b/source/blender/windowmanager/intern/wm_window_private.h
new file mode 100644
index 00000000000..115539861d7
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_window_private.h
@@ -0,0 +1,42 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2019 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup wm
+ */
+#ifndef __WM_WINDOW_PRIVATE_H__
+#define __WM_WINDOW_PRIVATE_H__
+
+#include "BLI_sys_types.h"
+#include "GHOST_Types.h"
+
+/* *************** Message box *************** */
+/* `WM_ghost_show_message_box` is implemented in `wm_windows.c` it is
+ * defined here as it was implemented to be used for showing
+ * a message to the user when the platform is not (fully) supported.
+ *
+ * In all other cases this message box should not be used. */
+void WM_ghost_show_message_box(const char *title,
+ const char *message,
+ const char *help_label,
+ const char *continue_label,
+ const char *link,
+ GHOST_DialogOptions dialog_options);
+
+#endif