diff options
Diffstat (limited to 'source/blender/windowmanager/intern')
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 |