diff options
Diffstat (limited to 'platform')
28 files changed, 577 insertions, 458 deletions
diff --git a/platform/SCsub b/platform/SCsub index e624f8e90f..2019a30be7 100644 --- a/platform/SCsub +++ b/platform/SCsub @@ -18,11 +18,10 @@ for platform in env.platform_apis: reg_apis_inc += '\n' reg_apis += '}\n\n' unreg_apis += '}\n' -f = open_utf8('register_platform_apis.gen.cpp', 'w') -f.write(reg_apis_inc) -f.write(reg_apis) -f.write(unreg_apis) -f.close() +with open_utf8('register_platform_apis.gen.cpp', 'w') as f: + f.write(reg_apis_inc) + f.write(reg_apis) + f.write(unreg_apis) platform_sources.append('register_platform_apis.gen.cpp') lib = env.add_library('platform', platform_sources) diff --git a/platform/android/AndroidManifest.xml.template b/platform/android/AndroidManifest.xml.template index a42ceb3c21..3e42b7a3cd 100644 --- a/platform/android/AndroidManifest.xml.template +++ b/platform/android/AndroidManifest.xml.template @@ -16,7 +16,8 @@ android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:screenOrientation="landscape" - android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize"> + android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize" + android:resizeableActivity="false"> <intent-filter> <action android:name="android.intent.action.MAIN" /> diff --git a/platform/android/SCsub b/platform/android/SCsub index d2285a82dd..8c08289932 100644 --- a/platform/android/SCsub +++ b/platform/android/SCsub @@ -41,10 +41,8 @@ prog = None abspath = env.Dir(".").abspath -gradle_basein = open_utf8(abspath + "/build.gradle.template", "r") -gradle_baseout = open_utf8(abspath + "/java/build.gradle", "w") - -gradle_text = gradle_basein.read() +with open_utf8(abspath + "/build.gradle.template", "r") as gradle_basein: + gradle_text = gradle_basein.read() gradle_maven_flat_text = "" if len(env.android_flat_dirs) > 0: @@ -131,17 +129,19 @@ gradle_text = gradle_text.replace("$$GRADLE_DEFAULT_CONFIG$$", gradle_default_co gradle_text = gradle_text.replace("$$GRADLE_PLUGINS$$", gradle_plugins) gradle_text = gradle_text.replace("$$GRADLE_CLASSPATH$$", gradle_classpath) -gradle_baseout.write(gradle_text) -gradle_baseout.close() +with open_utf8(abspath + "/java/build.gradle", "w") as gradle_baseout: + gradle_baseout.write(gradle_text) + +with open_utf8(abspath + "/AndroidManifest.xml.template", "r") as pp_basein: + manifest = pp_basein.read() -pp_basein = open_utf8(abspath + "/AndroidManifest.xml.template", "r") -pp_baseout = open_utf8(abspath + "/java/AndroidManifest.xml", "w") -manifest = pp_basein.read() manifest = manifest.replace("$$ADD_APPLICATION_CHUNKS$$", env.android_manifest_chunk) manifest = manifest.replace("$$ADD_PERMISSION_CHUNKS$$", env.android_permission_chunk) manifest = manifest.replace("$$ADD_APPATTRIBUTE_CHUNKS$$", env.android_appattributes_chunk) -pp_baseout.write(manifest) + +with open_utf8(abspath + "/java/AndroidManifest.xml", "w") as pp_baseout: + pp_baseout.write(manifest) lib = env_android.add_shared_library("#bin/libgodot", [android_objects], SHLIBSUFFIX=env["SHLIBSUFFIX"]) diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 6b4d0ff8c4..6d9f0a7e9a 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -301,8 +301,7 @@ class EditorExportAndroid : public EditorExportPlatform { args.push_back("-s"); args.push_back(d.id); args.push_back("shell"); - args.push_back("cat"); - args.push_back("/system/build.prop"); + args.push_back("getprop"); int ec; String dp; @@ -315,7 +314,14 @@ class EditorExportAndroid : public EditorExportPlatform { d.api_level = 0; for (int j = 0; j < props.size(); j++) { + // got information by `shell cat /system/build.prop` before and its format is "property=value" + // it's now changed to use `shell getporp` because of permission issue with Android 8.0 and above + // its format is "[property]: [value]" so changed it as like build.prop String p = props[j]; + p = p.replace("]: ", "="); + p = p.replace("[", ""); + p = p.replace("]", ""); + if (p.begins_with("ro.product.model=")) { device = p.get_slice("=", 1).strip_edges(); } else if (p.begins_with("ro.product.brand=")) { diff --git a/platform/android/java_glue.cpp b/platform/android/java_glue.cpp index 9baf58c3eb..579c06f76b 100644 --- a/platform/android/java_glue.cpp +++ b/platform/android/java_glue.cpp @@ -936,6 +936,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_newcontext(JNIEnv *en } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_back(JNIEnv *env, jobject obj) { + if (step == 0) + return; + os_android->main_loop_request_go_back(); } @@ -976,6 +979,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, job JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_touch(JNIEnv *env, jobject obj, jint ev, jint pointer, jint count, jintArray positions) { + if (step == 0) + return; + Vector<OS_Android::TouchPos> points; for (int i = 0; i < count; i++) { @@ -1250,6 +1256,8 @@ static unsigned int android_get_keysym(unsigned int p_code) { } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env, jobject obj, jint p_device, jint p_button, jboolean p_pressed) { + if (step == 0) + return; OS_Android::JoypadEvent jevent; jevent.device = p_device; @@ -1261,6 +1269,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joybutton(JNIEnv *env } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, jobject obj, jint p_device, jint p_axis, jfloat p_value) { + if (step == 0) + return; OS_Android::JoypadEvent jevent; jevent.device = p_device; @@ -1272,6 +1282,9 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyaxis(JNIEnv *env, } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyhat(JNIEnv *env, jobject obj, jint p_device, jint p_hat_x, jint p_hat_y) { + if (step == 0) + return; + OS_Android::JoypadEvent jevent; jevent.device = p_device; jevent.type = OS_Android::JOY_EVENT_HAT; @@ -1301,6 +1314,8 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_joyconnectionchanged( } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_key(JNIEnv *env, jobject obj, jint p_scancode, jint p_unicode_char, jboolean p_pressed) { + if (step == 0) + return; Ref<InputEventKey> ievent; ievent.instance(); @@ -1344,14 +1359,18 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_gyroscope(JNIEnv *env JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusin(JNIEnv *env, jobject obj) { - if (os_android && step > 0) - os_android->main_loop_focusin(); + if (step == 0) + return; + + os_android->main_loop_focusin(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env, jobject obj) { - if (os_android && step > 0) - os_android->main_loop_focusout(); + if (step == 0) + return; + + os_android->main_loop_focusout(); } JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jobject obj) { diff --git a/platform/iphone/SCsub b/platform/iphone/SCsub index 6b5f30dc41..b96bec16b4 100644 --- a/platform/iphone/SCsub +++ b/platform/iphone/SCsub @@ -1,4 +1,5 @@ #!/usr/bin/env python +import os Import('env') @@ -21,6 +22,10 @@ ios_lib = env_ios.add_library('iphone', iphone_lib) def combine_libs(target=None, source=None, env=None): lib_path = target[0].srcnode().abspath - env.Execute('$IPHONEPATH/usr/bin/libtool -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source])) + if ("OSXCROSS_IOS" in os.environ): + libtool = '$IPHONEPATH/usr/bin/${ios_triple}libtool' + else: + libtool = "$IPHONEPATH/usr/bin/libtool" + env.Execute(libtool + ' -static -o "' + lib_path + '" ' + ' '.join([('"' + lib.srcnode().abspath + '"') for lib in source])) combine_command = env_ios.Command('#bin/libgodot' + env_ios['LIBSUFFIX'], [ios_lib] + env_ios['LIBS'], combine_libs) diff --git a/platform/iphone/platform_config.h b/platform/iphone/platform_config.h index d205c7da83..d9fd61fb6e 100644 --- a/platform/iphone/platform_config.h +++ b/platform/iphone/platform_config.h @@ -30,6 +30,7 @@ #include <alloca.h> +#define GLES2_INCLUDE_H <ES2/gl.h> #define GLES3_INCLUDE_H <ES3/gl.h> #define PLATFORM_REFCOUNT diff --git a/platform/iphone/power_iphone.cpp b/platform/iphone/power_iphone.cpp index 95a9aa9705..7f9dadc363 100644 --- a/platform/iphone/power_iphone.cpp +++ b/platform/iphone/power_iphone.cpp @@ -30,7 +30,7 @@ #include "power_iphone.h" -bool OS::PowerState::UpdatePowerInfo() { +bool PowerIphone::UpdatePowerInfo() { return false; } diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 66a8a8d93c..5991075e29 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -3,38 +3,35 @@ Import('env') javascript_files = [ - "os_javascript.cpp", - "audio_driver_javascript.cpp", - "javascript_main.cpp", - "power_javascript.cpp", - "http_client_javascript.cpp", - "javascript_eval.cpp", + 'audio_driver_javascript.cpp', + 'http_client_javascript.cpp', + 'javascript_eval.cpp', + 'javascript_main.cpp', + 'os_javascript.cpp', ] -env_javascript = env.Clone() -if env['target'] == "profile": - env_javascript.Append(CPPFLAGS=['-DPROFILER_ENABLED']) - -javascript_objects = [] -for x in javascript_files: - javascript_objects.append(env_javascript.Object(x)) - -env.Append(LINKFLAGS=["-s", "EXPORTED_FUNCTIONS=\"['_main','_main_after_fs_sync','_send_notification']\""]) - -target_dir = env.Dir("#bin") -build = env.add_program(['#bin/godot', target_dir.File('godot' + env['PROGSUFFIX'] + '.wasm')], javascript_objects, PROGSUFFIX=env['PROGSUFFIX'] + '.js'); +build = env.add_program(['#bin/godot${PROGSUFFIX}.js', '#bin/godot${PROGSUFFIX}.wasm'], javascript_files); js, wasm = build -js_libraries = [] -js_libraries.append(env.File('http_request.js')) +js_libraries = [ + 'http_request.js', +] for lib in js_libraries: - env.Append(LINKFLAGS=['--js-library', lib.path]) + env.Append(LINKFLAGS=['--js-library', env.File(lib).path]) env.Depends(build, js_libraries) wrapper_start = env.File('pre.js') wrapper_end = env.File('engine.js') -js_final = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX=env['PROGSUFFIX'] + '.wrapped.js') - -zip_dir = target_dir.Dir('.javascript_zip') -zip_files = env.InstallAs([zip_dir.File('godot.js'), zip_dir.File('godot.wasm'), zip_dir.File('godot.html')], [js_final, wasm, '#misc/dist/html/default.html']) -Zip('#bin/godot', zip_files, ZIPSUFFIX=env['PROGSUFFIX'] + env['ZIPSUFFIX'], ZIPROOT=zip_dir, ZIPCOMSTR="Archving $SOURCES as $TARGET") +js_wrapped = env.Textfile('#bin/godot', [wrapper_start, js, wrapper_end], TEXTFILESUFFIX='${PROGSUFFIX}.wrapped.js') + +zip_dir = env.Dir('#bin/.javascript_zip') +zip_files = env.InstallAs([ + zip_dir.File('godot.js'), + zip_dir.File('godot.wasm'), + zip_dir.File('godot.html') +], [ + js_wrapped, + wasm, + '#misc/dist/html/default.html' +]) +env.Zip('#bin/godot', zip_files, ZIPROOT=zip_dir, ZIPSUFFIX='${PROGSUFFIX}${ZIPSUFFIX}', ZIPCOMSTR='Archving $SOURCES as $TARGET') diff --git a/platform/javascript/detect.py b/platform/javascript/detect.py index 7e6a1518ed..a48cb872ee 100644 --- a/platform/javascript/detect.py +++ b/platform/javascript/detect.py @@ -8,23 +8,22 @@ def is_active(): def get_name(): - return "JavaScript" + return 'JavaScript' def can_build(): - - return ("EMSCRIPTEN_ROOT" in os.environ or "EMSCRIPTEN" in os.environ) + return 'EM_CONFIG' in os.environ or os.path.exists(os.path.expanduser('~/.emscripten')) def get_opts(): from SCons.Variables import BoolVariable return [ + # eval() can be a security concern, so it can be disabled. BoolVariable('javascript_eval', 'Enable JavaScript eval interface', True), ] def get_flags(): - return [ ('tools', False), ('module_theora_enabled', False), @@ -36,24 +35,11 @@ def get_flags(): ] -def create(env): - - # remove Windows' .exe suffix - return env.Clone(tools=['textfile', 'zip'], PROGSUFFIX='') - - -def escape_sources_backslashes(target, source, env, for_signature): - return [path.replace('\\','\\\\') for path in env.GetBuildPath(source)] - -def escape_target_backslashes(target, source, env, for_signature): - return env.GetBuildPath(target[0]).replace('\\','\\\\') - - def configure(env): ## Build type - if (env["target"] == "release"): + if env['target'] == 'release' or env['target'] == 'profile': # Use -Os to prioritize optimizing for reduced file size. This is # particularly valuable for the web platform because it directly # decreases download time. @@ -62,65 +48,102 @@ def configure(env): # run-time performance. env.Append(CCFLAGS=['-Os']) env.Append(LINKFLAGS=['-Os']) - - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['-O2', '-s', 'ASSERTIONS=1']) - # retain function names at the cost of file size, for backtraces and profiling + if env['target'] == 'profile': + env.Append(LINKFLAGS=['--profiling-funcs']) + + elif env['target'] == 'release_debug': + env.Append(CPPDEFINES=['DEBUG_ENABLED']) + env.Append(CCFLAGS=['-O2']) + env.Append(LINKFLAGS=['-O2']) + # Retain function names for backtraces at the cost of file size. env.Append(LINKFLAGS=['--profiling-funcs']) - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-O1', '-D_DEBUG', '-g', '-DDEBUG_ENABLED']) + elif env['target'] == 'debug': + env.Append(CPPDEFINES=['DEBUG_ENABLED']) + env.Append(CCFLAGS=['-O1', '-g']) env.Append(LINKFLAGS=['-O1', '-g']) + env.Append(LINKFLAGS=['-s', 'ASSERTIONS=1']) ## Compiler configuration env['ENV'] = os.environ - if ("EMSCRIPTEN_ROOT" in os.environ): - env.PrependENVPath('PATH', os.environ['EMSCRIPTEN_ROOT']) - elif ("EMSCRIPTEN" in os.environ): - env.PrependENVPath('PATH', os.environ['EMSCRIPTEN']) - env['CC'] = 'emcc' - env['CXX'] = 'em++' - env['LINK'] = 'emcc' - env['RANLIB'] = 'emranlib' - # Emscripten's ar has issues with duplicate file names, so use cc - env['AR'] = 'emcc' - env['ARFLAGS'] = '-o' - - if (os.name == 'nt'): - # use TempFileMunge on Windows since some commands get too long for - # cmd.exe even with spawn_fix - # need to escape backslashes for this - env['ESCAPED_SOURCES'] = escape_sources_backslashes - env['ESCAPED_TARGET'] = escape_target_backslashes - env['ARCOM'] = '${TEMPFILE("%s")}' % env['ARCOM'].replace('$SOURCES', '$ESCAPED_SOURCES').replace('$TARGET', '$ESCAPED_TARGET') + em_config_file = os.getenv('EM_CONFIG') or os.path.expanduser('~/.emscripten') + if not os.path.exists(em_config_file): + raise RuntimeError("Emscripten configuration file '%s' does not exist" % em_config_file) + with open(em_config_file) as f: + em_config = {} + try: + # Emscripten configuration file is a Python file with simple assignments. + exec(f.read(), em_config) + except StandardError as e: + raise RuntimeError("Emscripten configuration file '%s' is invalid:\n%s" % (em_config_file, e)) + if 'EMSCRIPTEN_ROOT' not in em_config: + raise RuntimeError("'EMSCRIPTEN_ROOT' missing in Emscripten configuration file '%s'" % em_config_file) + env.PrependENVPath('PATH', em_config['EMSCRIPTEN_ROOT']) + + env['CC'] = 'emcc' + env['CXX'] = 'em++' + env['LINK'] = 'emcc' + + # Emscripten's ar has issues with duplicate file names, so use cc. + env['AR'] = 'emcc' + env['ARFLAGS'] = '-o' + # emranlib is a noop, so it's safe to use with AR=emcc. + env['RANLIB'] = 'emranlib' + + # Use TempFileMunge since some AR invocations are too long for cmd.exe. + # Use POSIX-style paths, required with TempFileMunge. + env['ARCOM_POSIX'] = env['ARCOM'].replace( + '$TARGET', '$TARGET.posix').replace( + '$SOURCES', '$SOURCES.posix') + env['ARCOM'] = '${TEMPFILE(ARCOM_POSIX)}' + + # All intermediate files are just LLVM bitcode. + env['OBJPREFIX'] = '' env['OBJSUFFIX'] = '.bc' + env['PROGPREFIX'] = '' + # Program() output consists of multiple files, so specify suffixes manually at builder. + env['PROGSUFFIX'] = '' + env['LIBPREFIX'] = 'lib' env['LIBSUFFIX'] = '.bc' + env['LIBPREFIXES'] = ['$LIBPREFIX'] + env['LIBSUFFIXES'] = ['$LIBSUFFIX'] ## Compile flags env.Append(CPPPATH=['#platform/javascript']) - env.Append(CPPFLAGS=['-DJAVASCRIPT_ENABLED', '-DUNIX_ENABLED', '-DPTHREAD_NO_RENAME', '-DTYPED_METHOD_BIND', '-DNO_THREADS']) - env.Append(CPPFLAGS=['-DGLES3_ENABLED']) + env.Append(CPPDEFINES=['JAVASCRIPT_ENABLED', 'UNIX_ENABLED']) - # These flags help keep the file size down - env.Append(CPPFLAGS=["-fno-exceptions", '-DNO_SAFE_CAST', '-fno-rtti']) + # No multi-threading (SharedArrayBuffer) available yet, + # once feasible also consider memory buffer size issues. + env.Append(CPPDEFINES=['NO_THREADS']) + + # These flags help keep the file size down. + env.Append(CCFLAGS=['-fno-exceptions', '-fno-rtti']) + # Don't use dynamic_cast, necessary with no-rtti. + env.Append(CPPDEFINES=['NO_SAFE_CAST']) if env['javascript_eval']: - env.Append(CPPFLAGS=['-DJAVASCRIPT_EVAL_ENABLED']) + env.Append(CPPDEFINES=['JAVASCRIPT_EVAL_ENABLED']) ## Link flags env.Append(LINKFLAGS=['-s', 'BINARYEN=1']) + + # Allow increasing memory buffer size during runtime. This is efficient + # when using WebAssembly (in comparison to asm.js) and works well for + # us since we don't know requirements at compile-time. env.Append(LINKFLAGS=['-s', 'ALLOW_MEMORY_GROWTH=1']) + + # This setting just makes WebGL 2 APIs available, it does NOT disable WebGL 1. env.Append(LINKFLAGS=['-s', 'USE_WEBGL2=1']) - env.Append(LINKFLAGS=['-s', 'EXTRA_EXPORTED_RUNTIME_METHODS="[\'FS\']"']) env.Append(LINKFLAGS=['-s', 'INVOKE_RUN=0']) + + # TODO: Reevaluate usage of this setting now that engine.js manages engine runtime. env.Append(LINKFLAGS=['-s', 'NO_EXIT_RUNTIME=1']) - # TODO: Move that to opus module's config + # TODO: Move that to opus module's config. if 'module_opus_enabled' in env and env['module_opus_enabled']: - env.opus_fixed_point = "yes" + env.opus_fixed_point = 'yes' diff --git a/platform/javascript/engine.js b/platform/javascript/engine.js index bca1851f40..e4839af433 100644 --- a/platform/javascript/engine.js +++ b/platform/javascript/engine.js @@ -1,3 +1,5 @@ + exposedLibs['PATH'] = PATH; + exposedLibs['FS'] = FS; return Module; }, }; @@ -12,6 +14,13 @@ var loadingFiles = {}; + function getPathLeaf(path) { + + while (path.endsWith('/')) + path = path.slice(0, -1); + return path.slice(path.lastIndexOf('/') + 1); + } + function getBasePath(path) { if (path.endsWith('/')) @@ -23,14 +32,15 @@ function getBaseName(path) { - path = getBasePath(path); - return path.slice(path.lastIndexOf('/') + 1); + return getPathLeaf(getBasePath(path)); } Engine = function Engine() { this.rtenv = null; + var LIBS = {}; + var initPromise = null; var unloadAfterInit = true; @@ -80,11 +90,11 @@ return new Promise(function(resolve, reject) { rtenvProps.onRuntimeInitialized = resolve; rtenvProps.onAbort = reject; - rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps); + rtenvProps.engine.rtenv = Engine.RuntimeEnvironment(rtenvProps, LIBS); }); } - this.preloadFile = function(pathOrBuffer, bufferFilename) { + this.preloadFile = function(pathOrBuffer, destPath) { if (pathOrBuffer instanceof ArrayBuffer) { pathOrBuffer = new Uint8Array(pathOrBuffer); @@ -93,14 +103,14 @@ } if (pathOrBuffer instanceof Uint8Array) { preloadedFiles.push({ - name: bufferFilename, + path: destPath, buffer: pathOrBuffer }); return Promise.resolve(); } else if (typeof pathOrBuffer === 'string') { return loadPromise(pathOrBuffer, preloadProgressTracker).then(function(xhr) { preloadedFiles.push({ - name: pathOrBuffer, + path: destPath || pathOrBuffer, buffer: xhr.response }); }); @@ -119,7 +129,12 @@ this.startGame = function(mainPack) { executableName = getBaseName(mainPack); - return Promise.all([this.init(getBasePath(mainPack)), this.preloadFile(mainPack)]).then( + return Promise.all([ + // Load from directory, + this.init(getBasePath(mainPack)), + // ...but write to root where the engine expects it. + this.preloadFile(mainPack, getPathLeaf(mainPack)) + ]).then( Function.prototype.apply.bind(synchronousStart, this, []) ); }; @@ -138,18 +153,6 @@ } var actualCanvas = this.rtenv.canvas; - var testContext = false; - var testCanvas; - try { - testCanvas = document.createElement('canvas'); - testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2'); - } catch (e) {} - if (!testContext) { - throw new Error("WebGL 2 not available"); - } - testCanvas = null; - testContext = null; - // canvas can grab focus on click if (actualCanvas.tabIndex < 0) { actualCanvas.tabIndex = 0; @@ -175,7 +178,16 @@ this.rtenv.thisProgram = executableName || getBaseName(basePath); preloadedFiles.forEach(function(file) { - this.rtenv.FS.createDataFile('/', file.name, new Uint8Array(file.buffer), true, true, true); + var dir = LIBS.PATH.dirname(file.path); + try { + LIBS.FS.stat(dir); + } catch (e) { + if (e.code !== 'ENOENT') { + throw e; + } + LIBS.FS.mkdirTree(dir); + } + LIBS.FS.createDataFile('/', file.path, new Uint8Array(file.buffer), true, true, true); }, this); preloadedFiles = null; @@ -273,6 +285,20 @@ Engine.RuntimeEnvironment = engine.RuntimeEnvironment; + Engine.isWebGLAvailable = function(majorVersion = 1) { + + var testContext = false; + try { + var testCanvas = document.createElement('canvas'); + if (majorVersion === 1) { + testContext = testCanvas.getContext('webgl') || testCanvas.getContext('experimental-webgl'); + } else if (majorVersion === 2) { + testContext = testCanvas.getContext('webgl2') || testCanvas.getContext('experimental-webgl2'); + } + } catch (e) {} + return !!testContext; + }; + Engine.load = function(newBasePath) { if (newBasePath !== undefined) basePath = getBasePath(newBasePath); diff --git a/platform/javascript/http_client_javascript.cpp b/platform/javascript/http_client_javascript.cpp index 118a77835e..8d90e01ae1 100644 --- a/platform/javascript/http_client_javascript.cpp +++ b/platform/javascript/http_client_javascript.cpp @@ -88,7 +88,7 @@ Error HTTPClient::prepare_request(Method p_method, const String &p_url, const Ve ERR_FAIL_COND_V(port < 0, ERR_UNCONFIGURED); ERR_FAIL_COND_V(!p_url.begins_with("/"), ERR_INVALID_PARAMETER); - String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + "/" + p_url; + String url = (use_tls ? "https://" : "http://") + host + ":" + itos(port) + p_url; godot_xhr_reset(xhr_id); godot_xhr_open(xhr_id, _methods[p_method], url.utf8().get_data(), username.empty() ? NULL : username.utf8().get_data(), diff --git a/platform/javascript/javascript_main.cpp b/platform/javascript/javascript_main.cpp index e85fe0800f..54d4755bd7 100644 --- a/platform/javascript/javascript_main.cpp +++ b/platform/javascript/javascript_main.cpp @@ -40,7 +40,7 @@ static void main_loop() { os->main_loop_iterate(); } -extern "C" void main_after_fs_sync(char *p_idbfs_err) { +extern "C" EMSCRIPTEN_KEEPALIVE void main_after_fs_sync(char *p_idbfs_err) { String idbfs_err = String::utf8(p_idbfs_err); if (!idbfs_err.empty()) { diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp index ace0bdad60..a275fb7929 100644 --- a/platform/javascript/os_javascript.cpp +++ b/platform/javascript/os_javascript.cpp @@ -413,14 +413,13 @@ static EM_BOOL joy_callback_func(int p_type, const EmscriptenGamepadEvent *p_eve return false; } -extern "C" { -void send_notification(int notif) { +extern "C" EMSCRIPTEN_KEEPALIVE void send_notification(int notif) { + if (notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || notif == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) { _cursor_inside_canvas = notif == MainLoop::NOTIFICATION_WM_MOUSE_ENTER; } OS_JavaScript::get_singleton()->get_main_loop()->notification(notif); } -} Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, int p_audio_driver) { @@ -444,6 +443,7 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, break; } EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(NULL, &attributes); + ERR_EXPLAIN("WebGL " + itos(attributes.majorVersion) + ".0 not available"); ERR_FAIL_COND_V(emscripten_webgl_make_context_current(ctx) != EMSCRIPTEN_RESULT_SUCCESS, ERR_UNAVAILABLE); video_mode = p_desired; @@ -479,8 +479,6 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver, input = memnew(InputDefault); _input = input; - power_manager = memnew(PowerJavascript); - #define EM_CHECK(ev) \ if (result != EMSCRIPTEN_RESULT_SUCCESS) \ ERR_PRINTS("Error while setting " #ev " callback: Code " + itos(result)) @@ -967,15 +965,21 @@ String OS_JavaScript::get_joy_guid(int p_device) const { } OS::PowerState OS_JavaScript::get_power_state() { - return power_manager->get_power_state(); + + WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to POWERSTATE_UNKNOWN"); + return OS::POWERSTATE_UNKNOWN; } int OS_JavaScript::get_power_seconds_left() { - return power_manager->get_power_seconds_left(); + + WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to -1"); + return -1; } int OS_JavaScript::get_power_percent_left() { - return power_manager->get_power_percent_left(); + + WARN_PRINT("Power management is not supported for the HTML5 platform, defaulting to -1"); + return -1; } bool OS_JavaScript::_check_internal_feature_support(const String &p_feature) { diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index f0ba9422e8..46eb1b3f13 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -36,7 +36,6 @@ #include "main/input_default.h" #include "os/input.h" #include "os/main_loop.h" -#include "power_javascript.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" @@ -64,8 +63,6 @@ class OS_JavaScript : public OS_Unix { GetUserDataDirFunc get_user_data_dir_func; - PowerJavascript *power_manager; - static void _close_notification_funcs(const String &p_file, int p_flags); void process_joypads(); diff --git a/platform/javascript/power_javascript.cpp b/platform/javascript/power_javascript.cpp deleted file mode 100644 index 5241644dbc..0000000000 --- a/platform/javascript/power_javascript.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/*************************************************************************/ -/* power_javascript.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "power_javascript.h" -#include "error_macros.h" - -bool PowerJavascript::UpdatePowerInfo() { - // TODO Javascript implementation - return false; -} - -OS::PowerState PowerJavascript::get_power_state() { - if (UpdatePowerInfo()) { - return power_state; - } else { - WARN_PRINT("Power management is not implemented on this platform, defaulting to POWERSTATE_UNKNOWN"); - return OS::POWERSTATE_UNKNOWN; - } -} - -int PowerJavascript::get_power_seconds_left() { - if (UpdatePowerInfo()) { - return nsecs_left; - } else { - WARN_PRINT("Power management is not implemented on this platform, defaulting to -1"); - return -1; - } -} - -int PowerJavascript::get_power_percent_left() { - if (UpdatePowerInfo()) { - return percent_left; - } else { - WARN_PRINT("Power management is not implemented on this platform, defaulting to -1"); - return -1; - } -} - -PowerJavascript::PowerJavascript() : - nsecs_left(-1), - percent_left(-1), - power_state(OS::POWERSTATE_UNKNOWN) { -} - -PowerJavascript::~PowerJavascript() { -} diff --git a/platform/javascript/power_javascript.h b/platform/javascript/power_javascript.h deleted file mode 100644 index c0c564aa60..0000000000 --- a/platform/javascript/power_javascript.h +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************/ -/* power_javascript.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ -#define PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ - -#include "os/os.h" - -class PowerJavascript { -private: - int nsecs_left; - int percent_left; - OS::PowerState power_state; - - bool UpdatePowerInfo(); - -public: - PowerJavascript(); - virtual ~PowerJavascript(); - - OS::PowerState get_power_state(); - int get_power_seconds_left(); - int get_power_percent_left(); -}; - -#endif /* PLATFORM_JAVASCRIPT_POWER_JAVASCRIPT_H_ */ diff --git a/platform/javascript/pre.js b/platform/javascript/pre.js index 311aa44fda..02194bc75e 100644 --- a/platform/javascript/pre.js +++ b/platform/javascript/pre.js @@ -1,2 +1,2 @@ var Engine = { - RuntimeEnvironment: function(Module) { + RuntimeEnvironment: function(Module, exposedLibs) { diff --git a/platform/osx/detect.py b/platform/osx/detect.py index e51a8082f7..1e9631fae0 100644 --- a/platform/osx/detect.py +++ b/platform/osx/detect.py @@ -93,6 +93,7 @@ def configure(env): env['AR'] = basecmd + "ar" env['RANLIB'] = basecmd + "ranlib" env['AS'] = basecmd + "as" + env.Append(CCFLAGS=['-D__MACPORTS__']) #hack to fix libvpx MM256_BROADCASTSI128_SI256 define if (env["CXX"] == "clang++"): env.Append(CPPFLAGS=['-DTYPED_METHOD_BIND']) diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index edd093afbb..db265812fa 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -496,7 +496,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (use_dmg()) { String pack_path = tmp_app_path_name + "/Contents/Resources/" + pkg_name + ".pck"; Vector<SharedObject> shared_objects; - Error err = save_pack(p_preset, pack_path, &shared_objects); + err = save_pack(p_preset, pack_path, &shared_objects); // see if we can code sign our new package String identity = p_preset->get("codesign/identity"); @@ -504,7 +504,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p if (err == OK) { DirAccess *da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < shared_objects.size(); i++) { - da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); + err = da->copy(shared_objects[i].path, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); if (err == OK && identity != "") { err = _code_sign(p_preset, tmp_app_path_name + "/Contents/Frameworks/" + shared_objects[i].path.get_file()); } @@ -549,7 +549,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p String pack_path = EditorSettings::get_singleton()->get_cache_dir().plus_file(pkg_name + ".pck"); Vector<SharedObject> shared_objects; - Error err = save_pack(p_preset, pack_path, &shared_objects); + err = save_pack(p_preset, pack_path, &shared_objects); if (err == OK) { zipOpenNewFileInZip(dst_pkg_zip, @@ -581,7 +581,9 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p } else { err = ERR_CANT_OPEN; } + } + if (err == OK) { //add shared objects for (int i = 0; i < shared_objects.size(); i++) { Vector<uint8_t> file = FileAccess::get_file_as_array(shared_objects[i].path); @@ -609,7 +611,7 @@ Error EditorExportPlatformOSX::export_project(const Ref<EditorExportPreset> &p_p zipClose(dst_pkg_zip, NULL); } - return OK; + return err; } bool EditorExportPlatformOSX::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const { diff --git a/platform/osx/godot_main_osx.mm b/platform/osx/godot_main_osx.mm index 6ccbaf896b..64116fa1e0 100644 --- a/platform/osx/godot_main_osx.mm +++ b/platform/osx/godot_main_osx.mm @@ -101,5 +101,5 @@ int main(int argc, char **argv) { Main::cleanup(); - return 0; + return os.get_exit_code(); }; diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index 486d7af1c1..fee25e98cb 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -100,7 +100,7 @@ public: id context; CursorShape cursor_shape; - NSCursor *cursors[CURSOR_MAX] = { NULL }; + NSCursor *cursors[CURSOR_MAX]; MouseMode mouse_mode; String title; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index a811ff585d..0c5524dd08 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -192,6 +192,7 @@ static Vector2 get_mouse_pos(NSEvent *event) { // Note: called before main loop init! char *utfs = strdup([filename UTF8String]); OS_OSX::singleton->open_with_filename.parse_utf8(utfs); + free(utfs); return YES; } @@ -263,6 +264,7 @@ static Vector2 get_mouse_pos(NSEvent *event) { NSWindow *window = (NSWindow *)[notification object]; CGFloat newBackingScaleFactor = [window backingScaleFactor]; CGFloat oldBackingScaleFactor = [[[notification userInfo] objectForKey:@"NSBackingPropertyOldScaleFactorKey"] doubleValue]; + [OS_OSX::singleton->window_view setWantsBestResolutionOpenGLSurface:YES]; if (newBackingScaleFactor != oldBackingScaleFactor) { //Set new display scale and window size @@ -837,6 +839,108 @@ static int translateKey(unsigned int key) { return table[key]; } +struct _KeyCodeMap { + UniChar kchar; + int kcode; +}; + +static const _KeyCodeMap _keycodes[55] = { + { '`', KEY_QUOTELEFT }, + { '~', KEY_ASCIITILDE }, + { '0', KEY_KP_0 }, + { '1', KEY_KP_1 }, + { '2', KEY_KP_2 }, + { '3', KEY_KP_3 }, + { '4', KEY_KP_4 }, + { '5', KEY_KP_5 }, + { '6', KEY_KP_6 }, + { '7', KEY_KP_7 }, + { '8', KEY_KP_8 }, + { '9', KEY_KP_9 }, + { '-', KEY_MINUS }, + { '_', KEY_UNDERSCORE }, + { '=', KEY_EQUAL }, + { '+', KEY_PLUS }, + { 'q', KEY_Q }, + { 'w', KEY_W }, + { 'e', KEY_E }, + { 'r', KEY_R }, + { 't', KEY_T }, + { 'y', KEY_Y }, + { 'u', KEY_U }, + { 'i', KEY_I }, + { 'o', KEY_O }, + { 'p', KEY_P }, + { '[', KEY_BRACERIGHT }, + { ']', KEY_BRACELEFT }, + { '{', KEY_BRACERIGHT }, + { '}', KEY_BRACELEFT }, + { 'a', KEY_A }, + { 's', KEY_S }, + { 'd', KEY_D }, + { 'f', KEY_F }, + { 'g', KEY_G }, + { 'h', KEY_H }, + { 'j', KEY_J }, + { 'k', KEY_K }, + { 'l', KEY_L }, + { ';', KEY_SEMICOLON }, + { ':', KEY_COLON }, + { '\'', KEY_APOSTROPHE }, + { '\"', KEY_QUOTEDBL }, + { '\\', KEY_BACKSLASH }, + { '#', KEY_NUMBERSIGN }, + { 'z', KEY_Z }, + { 'x', KEY_X }, + { 'c', KEY_C }, + { 'v', KEY_V }, + { 'b', KEY_B }, + { 'n', KEY_N }, + { 'm', KEY_M }, + { ',', KEY_COMMA }, + { '.', KEY_PERIOD }, + { '/', KEY_SLASH } +}; + +static int remapKey(unsigned int key) { + + TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); + if (!currentKeyboard) + return translateKey(key); + + CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData); + if (!layoutData) + return nil; + + const UCKeyboardLayout *keyboardLayout = (const UCKeyboardLayout *)CFDataGetBytePtr(layoutData); + + UInt32 keysDown = 0; + UniChar chars[4]; + UniCharCount realLength; + + OSStatus err = UCKeyTranslate(keyboardLayout, + key, + kUCKeyActionDisplay, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, + &keysDown, + sizeof(chars) / sizeof(chars[0]), + &realLength, + chars); + + if (err != noErr) { + return translateKey(key); + } + + for (unsigned int i = 0; i < 55; i++) { + if (_keycodes[i].kchar == chars[0]) { + return _keycodes[i].kcode; + } + } + return translateKey(key); +} + - (void)keyDown:(NSEvent *)event { //disable raw input in IME mode @@ -846,7 +950,7 @@ static int translateKey(unsigned int key) { ke.osx_state = [event modifierFlags]; ke.pressed = true; ke.echo = [event isARepeat]; - ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.scancode = remapKey([event keyCode]); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -899,7 +1003,7 @@ static int translateKey(unsigned int key) { } ke.osx_state = mod; - ke.scancode = latin_keyboard_keycode_convert(translateKey(key)); + ke.scancode = remapKey(key); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -915,7 +1019,7 @@ static int translateKey(unsigned int key) { ke.osx_state = [event modifierFlags]; ke.pressed = false; ke.echo = false; - ke.scancode = latin_keyboard_keycode_convert(translateKey([event keyCode])); + ke.scancode = remapKey([event keyCode]); ke.unicode = 0; push_to_key_event_buffer(ke); @@ -1403,9 +1507,7 @@ void OS_OSX::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - int image_size = 32 * 32; - - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); NSBitmapImageRep *imgrep = [[[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL @@ -2320,6 +2422,7 @@ OS_OSX *OS_OSX::singleton = NULL; OS_OSX::OS_OSX() { + memset(cursors, 0, sizeof(cursors)); key_event_pos = 0; mouse_mode = OS::MOUSE_MODE_VISIBLE; main_loop = NULL; diff --git a/platform/uwp/detect.py b/platform/uwp/detect.py index f2c5c5d42e..0e7b125dc5 100644 --- a/platform/uwp/detect.py +++ b/platform/uwp/detect.py @@ -25,8 +25,11 @@ def can_build(): def get_opts(): + from SCons.Variables import BoolVariable return [ + ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), + BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), ] @@ -40,6 +43,8 @@ def get_flags(): def configure(env): + env.msvc = True + if (env["bits"] != "default"): print("Error: bits argument is disabled for MSVC") print(""" diff --git a/platform/windows/detect.py b/platform/windows/detect.py index b8ee2cd19f..2ce55d98be 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -12,23 +12,17 @@ def get_name(): def can_build(): - if (os.name == "nt"): # Building natively on Windows - if (os.getenv("VCINSTALLDIR")): # MSVC + # If VCINSTALLDIR is set in the OS environ, use traditional Godot logic to set up MSVC + if (os.getenv("VCINSTALLDIR")): # MSVC, manual setup return True - print("MSVC not detected (no VCINSTALLDIR environment variable), attempting MinGW.") - mingw32 = "" - mingw64 = "" - if (os.getenv("MINGW32_PREFIX")): - mingw32 = os.getenv("MINGW32_PREFIX") - if (os.getenv("MINGW64_PREFIX")): - mingw64 = os.getenv("MINGW64_PREFIX") - - test = "gcc --version > NUL 2>&1" - if (os.system(test) == 0 or os.system(mingw32 + test) == 0 or os.system(mingw64 + test) == 0): - return True + # Otherwise, let SCons find MSVC if installed, or else Mingw. + # Since we're just returning True here, if there's no compiler + # installed, we'll get errors when it tries to build with the + # null compiler. + return True if (os.name == "posix"): # Cross-compiling with MinGW-w64 (old MinGW32 is not supported) @@ -70,6 +64,8 @@ def get_opts(): ('target_win_version', 'Targeted Windows version, >= 0x0601 (Windows 7)', '0x0601'), EnumVariable('debug_symbols', 'Add debug symbols to release version', 'yes', ('yes', 'no', 'full')), BoolVariable('separate_debug_symbols', 'Create a separate file with the debug symbols', False), + ('msvc_version', 'MSVC version to use. Ignored if VCINSTALLDIR is set in shell env.', None), + BoolVariable('use_mingw', 'Use the Mingw compiler, even if MSVC is installed. Only used on Windows.', False), ] @@ -98,192 +94,253 @@ def build_res_file(target, source, env): return 0 -def configure(env): - - env.Append(CPPPATH=['#platform/windows']) - - if (os.name == "nt" and os.getenv("VCINSTALLDIR")): # MSVC +def setup_msvc_manual(env): + """Set up env to use MSVC manually, using VCINSTALLDIR""" + if (env["bits"] != "default"): + print(""" + Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console + (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits + argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. + """) + raise SCons.Errors.UserError("Bits argument should not be used when using VCINSTALLDIR") + + # Force bits arg + # (Actually msys2 mingw can support 64-bit, we could detect that) + env["bits"] = "32" + env["x86_libtheora_opt_vc"] = True + + # find compiler manually + compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) + print("Found MSVC compiler: " + compiler_version_str) + + # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm + if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): + env["bits"] = "64" + env["x86_libtheora_opt_vc"] = False + print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") + elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): + print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") + else: + print("Failed to manually detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup, or avoid setting VCINSTALLDIR.") + +def setup_msvc_auto(env): + """Set up MSVC using SCons's auto-detection logic""" + + # If MSVC_VERSION is set by SCons, we know MSVC is installed. + # But we may want a different version or target arch. + + # The env may have already been set up with default MSVC tools, so + # reset a few things so we can set it up with the tools we want. + # (Ideally we'd decide on the tool config before configuring any + # environment, and just set the env up once, but this function runs + # on an existing env so this is the simplest way.) + env['MSVC_SETUP_RUN'] = False # Need to set this to re-run the tool + env['MSVS_VERSION'] = None + env['MSVC_VERSION'] = None + env['TARGET_ARCH'] = None + if env['bits'] != 'default': + env['TARGET_ARCH'] = {'32': 'x86', '64': 'x86_64'}[env['bits']] + if env.has_key('msvc_version'): + env['MSVC_VERSION'] = env['msvc_version'] + env.Tool('msvc') + env.Tool('mssdk') # we want the MS SDK + # Note: actual compiler version can be found in env['MSVC_VERSION'], e.g. "14.1" for VS2015 + # Get actual target arch into bits (it may be "default" at this point): + if env['TARGET_ARCH'] in ('amd64', 'x86_64'): + env['bits'] = 64 + else: + env['bits'] = 32 + print(" Found MSVC version %s, arch %s, bits=%s" % (env['MSVC_VERSION'], env['TARGET_ARCH'], env['bits'])) + if env['TARGET_ARCH'] in ('amd64', 'x86_64'): + env["x86_libtheora_opt_vc"] = False + +def setup_mingw(env): + """Set up env for use with mingw""" + # Nothing to do here + print("Using Mingw") + pass + +def configure_msvc(env, manual_msvc_config): + """Configure env to work with MSVC""" + + # Build type + + if (env["target"] == "release"): + env.Append(CCFLAGS=['/O2']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) + env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) + + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['/O2']) + env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + + elif (env["target"] == "debug_release"): + env.Append(CCFLAGS=['/Z7', '/Od']) + env.Append(LINKFLAGS=['/DEBUG']) + env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) + env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) + + elif (env["target"] == "debug"): + env.AppendUnique(CCFLAGS=['/Z7', '/Od', '/EHsc']) + env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED', 'DEBUG_MEMORY_ENABLED', + 'D3D_DEBUG_INFO']) + env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + env.Append(LINKFLAGS=['/DEBUG']) + + ## Compile/link flags + + env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) + env.AppendUnique(CXXFLAGS=['/TP']) # assume all sources are C++ + if manual_msvc_config: # should be automatic if SCons found it + env.Append(CPPPATH=[os.getenv("WindowsSdkDir") + "/Include"]) + + env.AppendUnique(CPPDEFINES = ['WINDOWS_ENABLED', 'OPENGL_ENABLED', + 'RTAUDIO_ENABLED', 'WASAPI_ENABLED', + 'TYPED_METHOD_BIND', 'WIN32', 'MSVC', + {'WINVER' : '$target_win_version', + '_WIN32_WINNT': '$target_win_version'}]) + if env["bits"] == "64": + env.AppendUnique(CPPDEFINES=['_WIN64']) + + ## Libs + + LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', + 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', + 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt'] + env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) + + if manual_msvc_config: + env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) - env['ENV']['TMP'] = os.environ['TMP'] + ## LTO - ## Build type - - if (env["target"] == "release"): - env.Append(CCFLAGS=['/O2']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) - - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['/O2', '/DDEBUG_ENABLED']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - - elif (env["target"] == "debug_release"): - env.Append(CCFLAGS=['/Z7', '/Od']) - env.Append(LINKFLAGS=['/DEBUG']) - env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) - env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) - - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['/Z7', '/DDEBUG_ENABLED', '/DDEBUG_MEMORY_ENABLED', '/DD3D_DEBUG_INFO', '/Od', '/EHsc']) - env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) - env.Append(LINKFLAGS=['/DEBUG']) - - ## Architecture - - # Note: this detection/override code from here onward should be here instead of in SConstruct because it's platform and compiler specific (MSVC/Windows) - if (env["bits"] != "default"): - print("Error: bits argument is disabled for MSVC") - print(""" - Bits argument is not supported for MSVC compilation. Architecture depends on the Native/Cross Compile Tools Prompt/Developer Console - (or Visual Studio settings) that is being used to run SCons. As a consequence, bits argument is disabled. Run scons again without bits - argument (example: scons p=windows) and SCons will attempt to detect what MSVC compiler will be executed and inform you. - """) - sys.exit() - - # Forcing bits argument because MSVC does not have a flag to set this through SCons... it's different compilers (cl.exe's) called from the proper command prompt - # that decide the architecture that is build for. Scons can only detect the os.getenviron (because vsvarsall.bat sets a lot of stuff for cl.exe to work with) - env["bits"] = "32" - env["x86_libtheora_opt_vc"] = True - - ## Compiler configuration - - env['ENV'] = os.environ - # This detection function needs the tools env (that is env['ENV'], not SCons's env), and that is why it's this far below in the code - compiler_version_str = methods.detect_visual_c_compiler_version(env['ENV']) - - print("Detected MSVC compiler: " + compiler_version_str) - # If building for 64bit architecture, disable assembly optimisations for 32 bit builds (theora as of writing)... vc compiler for 64bit can not compile _asm - if(compiler_version_str == "amd64" or compiler_version_str == "x86_amd64"): - env["bits"] = "64" - env["x86_libtheora_opt_vc"] = False - print("Compiled program architecture will be a 64 bit executable (forcing bits=64).") - elif (compiler_version_str == "x86" or compiler_version_str == "amd64_x86"): - print("Compiled program architecture will be a 32 bit executable. (forcing bits=32).") + if (env["use_lto"]): + env.AppendUnique(CCFLAGS=['/GL']) + env.AppendUnique(ARFLAGS=['/LTCG']) + if env["progress"]: + env.AppendUnique(LINKFLAGS=['/LTCG:STATUS']) else: - print("Failed to detect MSVC compiler architecture version... Defaulting to 32bit executable settings (forcing bits=32). Compilation attempt will continue, but SCons can not detect for what architecture this build is compiled for. You should check your settings/compilation setup.") + env.AppendUnique(LINKFLAGS=['/LTCG']) - ## Compile flags + if manual_msvc_config: + env.Append(CPPPATH=[p for p in os.getenv("INCLUDE").split(";")]) + env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) - env.Append(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) - env.Append(CXXFLAGS=['/TP']) - env.Append(CPPFLAGS=['/DMSVC', '/GR', ]) - env.Append(CCFLAGS=['/I' + os.getenv("WindowsSdkDir") + "/Include"]) + # Incremental linking fix + env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] + env['BUILDERS']['Program'] = methods.precious_program - env.Append(CCFLAGS=['/DWINDOWS_ENABLED']) - env.Append(CCFLAGS=['/DOPENGL_ENABLED']) - env.Append(CCFLAGS=['/DRTAUDIO_ENABLED']) - env.Append(CCFLAGS=['/DWASAPI_ENABLED']) - env.Append(CCFLAGS=['/DTYPED_METHOD_BIND']) - env.Append(CCFLAGS=['/DWIN32']) - env.Append(CCFLAGS=['/DWINVER=%s' % env['target_win_version'], '/D_WIN32_WINNT=%s' % env['target_win_version']]) - if env["bits"] == "64": - env.Append(CCFLAGS=['/D_WIN64']) +def configure_mingw(env): + # Workaround for MinGW. See: + # http://www.scons.org/wiki/LongCmdLinesOnWin32 + env.use_windows_spawn_fix() - LIBS = ['winmm', 'opengl32', 'dsound', 'kernel32', 'ole32', 'oleaut32', 'user32', 'gdi32', 'IPHLPAPI', 'Shlwapi', 'wsock32', 'Ws2_32', 'shell32', 'advapi32', 'dinput8', 'dxguid', 'imm32', 'bcrypt'] - env.Append(LINKFLAGS=[p + env["LIBSUFFIX"] for p in LIBS]) + ## Build type - env.Append(LIBPATH=[os.getenv("WindowsSdkDir") + "/Lib"]) + if (env["target"] == "release"): + env.Append(CCFLAGS=['-msse2']) - if (os.getenv("VCINSTALLDIR")): - VC_PATH = os.getenv("VCINSTALLDIR") + if (env["bits"] == "64"): + env.Append(CCFLAGS=['-O3']) else: - VC_PATH = "" + env.Append(CCFLAGS=['-O2']) - if (env["use_lto"]): - env.Append(CCFLAGS=['/GL']) - env.Append(ARFLAGS=['/LTCG']) - if env["progress"]: - env.Append(LINKFLAGS=['/LTCG:STATUS']) - else: - env.Append(LINKFLAGS=['/LTCG']) + env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) - env.Append(CCFLAGS=["/I" + p for p in os.getenv("INCLUDE").split(";")]) - env.Append(LIBPATH=[p for p in os.getenv("LIB").split(";")]) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) - # Incremental linking fix - env['BUILDERS']['ProgramOriginal'] = env['BUILDERS']['Program'] - env['BUILDERS']['Program'] = methods.precious_program + elif (env["target"] == "release_debug"): + env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) + if (env["debug_symbols"] == "yes"): + env.Prepend(CCFLAGS=['-g1']) + if (env["debug_symbols"] == "full"): + env.Prepend(CCFLAGS=['-g2']) - else: # MinGW - - # Workaround for MinGW. See: - # http://www.scons.org/wiki/LongCmdLinesOnWin32 - env.use_windows_spawn_fix() - - ## Build type + elif (env["target"] == "debug"): + env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) - if (env["target"] == "release"): - env.Append(CCFLAGS=['-msse2']) + ## Compiler configuration - if (env["bits"] == "64"): - env.Append(CCFLAGS=['-O3']) - else: - env.Append(CCFLAGS=['-O2']) + if (os.name == "nt"): + env['ENV']['TMP'] = os.environ['TMP'] # way to go scons, you can be so stupid sometimes + else: + env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - env.Append(LINKFLAGS=['-Wl,--subsystem,windows']) + if (env["bits"] == "default"): + if (os.name == "nt"): + env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" + else: # default to 64-bit on Linux + env["bits"] = "64" - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) + mingw_prefix = "" - elif (env["target"] == "release_debug"): - env.Append(CCFLAGS=['-O2', '-DDEBUG_ENABLED']) - if (env["debug_symbols"] == "yes"): - env.Prepend(CCFLAGS=['-g1']) - if (env["debug_symbols"] == "full"): - env.Prepend(CCFLAGS=['-g2']) + if (env["bits"] == "32"): + env.Append(LINKFLAGS=['-static']) + env.Append(LINKFLAGS=['-static-libgcc']) + env.Append(LINKFLAGS=['-static-libstdc++']) + mingw_prefix = env["mingw_prefix_32"] + else: + env.Append(LINKFLAGS=['-static']) + mingw_prefix = env["mingw_prefix_64"] - elif (env["target"] == "debug"): - env.Append(CCFLAGS=['-g3', '-DDEBUG_ENABLED', '-DDEBUG_MEMORY_ENABLED']) + env["CC"] = mingw_prefix + "gcc" + env['AS'] = mingw_prefix + "as" + env['CXX'] = mingw_prefix + "g++" + env['AR'] = mingw_prefix + "gcc-ar" + env['RANLIB'] = mingw_prefix + "gcc-ranlib" + env['LINK'] = mingw_prefix + "g++" + env["x86_libtheora_opt_gcc"] = True - ## Compiler configuration + if env['use_lto']: + env.Append(CCFLAGS=['-flto']) + env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) - if (os.name == "nt"): - env['ENV']['TMP'] = os.environ['TMP'] # way to go scons, you can be so stupid sometimes - else: - env["PROGSUFFIX"] = env["PROGSUFFIX"] + ".exe" # for linux cross-compilation - if (env["bits"] == "default"): - if (os.name == "nt"): - env["bits"] = "64" if "PROGRAMFILES(X86)" in os.environ else "32" - else: # default to 64-bit on Linux - env["bits"] = "64" + ## Compile flags - mingw_prefix = "" + env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) + env.Append(CCFLAGS=['-DOPENGL_ENABLED']) + env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) + env.Append(CCFLAGS=['-DWASAPI_ENABLED']) + env.Append(CCFLAGS=['-DWINVER=%s' % env['target_win_version'], '-D_WIN32_WINNT=%s' % env['target_win_version']]) + env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt']) - if (env["bits"] == "32"): - env.Append(LINKFLAGS=['-static']) - env.Append(LINKFLAGS=['-static-libgcc']) - env.Append(LINKFLAGS=['-static-libstdc++']) - mingw_prefix = env["mingw_prefix_32"] - else: - env.Append(LINKFLAGS=['-static']) - mingw_prefix = env["mingw_prefix_64"] + env.Append(CPPFLAGS=['-DMINGW_ENABLED']) - env["CC"] = mingw_prefix + "gcc" - env['AS'] = mingw_prefix + "as" - env['CXX'] = mingw_prefix + "g++" - env['AR'] = mingw_prefix + "gcc-ar" - env['RANLIB'] = mingw_prefix + "gcc-ranlib" - env['LINK'] = mingw_prefix + "g++" - env["x86_libtheora_opt_gcc"] = True + # resrc + env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) - if env['use_lto']: - env.Append(CCFLAGS=['-flto']) - env.Append(LINKFLAGS=['-flto=' + str(env.GetOption("num_jobs"))]) +def configure(env): + # At this point the env has been set up with basic tools/compilers. + env.Append(CPPPATH=['#platform/windows']) + print("Configuring for Windows: target=%s, bits=%s" % (env['target'], env['bits'])) - ## Compile flags + if (os.name == "nt"): + env['ENV'] = os.environ # this makes build less repeatable, but simplifies some things + env['ENV']['TMP'] = os.environ['TMP'] - env.Append(CCFLAGS=['-DWINDOWS_ENABLED', '-mwindows']) - env.Append(CCFLAGS=['-DOPENGL_ENABLED']) - env.Append(CCFLAGS=['-DRTAUDIO_ENABLED']) - env.Append(CCFLAGS=['-DWASAPI_ENABLED']) - env.Append(CCFLAGS=['-DWINVER=%s' % env['target_win_version'], '-D_WIN32_WINNT=%s' % env['target_win_version']]) - env.Append(LIBS=['mingw32', 'opengl32', 'dsound', 'ole32', 'd3d9', 'winmm', 'gdi32', 'iphlpapi', 'shlwapi', 'wsock32', 'ws2_32', 'kernel32', 'oleaut32', 'dinput8', 'dxguid', 'ksuser', 'imm32', 'bcrypt']) + # First figure out which compiler, version, and target arch we're using + if os.getenv("VCINSTALLDIR"): + # Manual setup of MSVC + setup_msvc_manual(env) + env.msvc = True + manual_msvc_config = True + elif env.get('MSVC_VERSION', ''): + setup_msvc_auto(env) + env.msvc = True + manual_msvc_config = False + else: + setup_mingw(env) + env.msvc = False - env.Append(CPPFLAGS=['-DMINGW_ENABLED']) + # Now set compiler/linker flags + if env.msvc: + configure_msvc(env, manual_msvc_config) - # resrc - env.Append(BUILDERS={'RES': env.Builder(action=build_res_file, suffix='.o', src_suffix='.rc')}) + else: # MinGW + configure_mingw(env) diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index 13fe781ff3..f51d969c16 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -1896,26 +1896,25 @@ void OS_Windows::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shap Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - UINT image_size = 32 * 32; + UINT image_size = texture->get_width() * texture->get_height(); UINT size = sizeof(UINT) * image_size; - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); // Create the BITMAP with alpha channel COLORREF *buffer = (COLORREF *)malloc(sizeof(COLORREF) * image_size); image->lock(); for (UINT index = 0; index < image_size; index++) { - int column_index = floor(index / 32); - int row_index = index % 32; + int row_index = floor(index / texture->get_width()); + int column_index = index % texture->get_width(); - Color pcColor = image->get_pixel(row_index, column_index); - *(buffer + index) = image->get_pixel(row_index, column_index).to_argb32(); + *(buffer + index) = image->get_pixel(column_index, row_index).to_argb32(); } image->unlock(); // Using 4 channels, so 4 * 8 bits - HBITMAP bitmap = CreateBitmap(32, 32, 1, 4 * 8, buffer); + HBITMAP bitmap = CreateBitmap(texture->get_width(), texture->get_height(), 1, 4 * 8, buffer); COLORREF clrTransparent = -1; // Create the AND and XOR masks for the bitmap diff --git a/platform/x11/detect.py b/platform/x11/detect.py index da2b0701b6..5820a926e9 100644 --- a/platform/x11/detect.py +++ b/platform/x11/detect.py @@ -234,10 +234,10 @@ def configure(env): print("ALSA libraries not found, disabling driver") if env['pulseaudio']: - if (os.system("pkg-config --exists libpulse-simple") == 0): # 0 means found + if (os.system("pkg-config --exists libpulse") == 0): # 0 means found print("Enabling PulseAudio") env.Append(CPPFLAGS=["-DPULSEAUDIO_ENABLED"]) - env.ParseConfig('pkg-config --cflags --libs libpulse-simple') + env.ParseConfig('pkg-config --cflags --libs libpulse') else: print("PulseAudio development libraries not found, disabling driver") diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index c06c7516d0..338d13410f 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -2375,11 +2375,11 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c Ref<Texture> texture = p_cursor; Ref<Image> image = texture->get_data(); - ERR_FAIL_COND(texture->get_width() != 32 || texture->get_height() != 32); + ERR_FAIL_COND(texture->get_width() > 256 || texture->get_height() > 256); // Create the cursor structure XcursorImage *cursor_image = XcursorImageCreate(texture->get_width(), texture->get_height()); - XcursorUInt image_size = 32 * 32; + XcursorUInt image_size = texture->get_width() * texture->get_height(); XcursorDim size = sizeof(XcursorPixel) * image_size; cursor_image->version = 1; @@ -2393,10 +2393,10 @@ void OS_X11::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, c image->lock(); for (XcursorPixel index = 0; index < image_size; index++) { - int column_index = floor(index / 32); - int row_index = index % 32; + int row_index = floor(index / texture->get_width()); + int column_index = index % texture->get_width(); - *(cursor_image->pixels + index) = image->get_pixel(row_index, column_index).to_argb32(); + *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); } image->unlock(); |