Merge pull request #121722 from Repiteo/4.7.2-cherry-picks-1
[4.7] Cherry-picks for the 4.7 branch (future 4.7.2) - 1st batch
This commit is contained in:
47 files changed
+755
-227
No files matched your search
@@ -12,7 +12,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Restore default cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v6
|
||||
id: cache-ping
|
||||
with:
|
||||
path: ${{ inputs.scons-cache }}
|
||||
@@ -27,7 +27,7 @@ runs:
|
||||
# This is done after pulling the default cache so that PRs can integrate any potential changes
|
||||
# from the default branch without conflicting with whatever local changes were already made.
|
||||
- name: Restore local cache
|
||||
uses: actions/cache/restore@v5
|
||||
uses: actions/cache/restore@v6
|
||||
if: github.ref_name != github.event.repository.default_branch
|
||||
with:
|
||||
path: ${{ inputs.scons-cache }}
|
||||
|
||||
@@ -16,7 +16,7 @@ runs:
|
||||
run: misc/scripts/purge_cache.py ${{ env.CACHE_TIMESTAMP }} "${{ inputs.scons-cache }}"
|
||||
|
||||
- name: Save SCons cache directory
|
||||
uses: actions/cache/save@v5
|
||||
uses: actions/cache/save@v6
|
||||
with:
|
||||
path: ${{ inputs.scons-cache }}
|
||||
key: ${{ inputs.cache-name }}|${{ github.ref_name }}|${{ github.sha }}
|
||||
@@ -22,7 +22,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
repository: godotengine/godot-cpp
|
||||
|
||||
@@ -16,7 +16,7 @@ runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Set up Python 3.x
|
||||
uses: actions/setup-python@v6
|
||||
uses: actions/setup-python@v7
|
||||
with:
|
||||
# Semantic version range syntax or exact version of a Python version.
|
||||
python-version: ${{ inputs.python-version }}
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
sources-changed: ${{ steps.changed-files.outputs.sources_any_changed }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0 # Treeless clone. Slightly less performant than a shallow clone, but makes finding diffs instantaneous.
|
||||
filter: tree:0 # See: https://github.blog/open-source/git/get-up-to-speed-with-partial-clone-and-shallow-clone/
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ int64_t RandomPCG::rand_weighted(const Vector<float> &p_weights) {
|
||||
const float *weights = p_weights.ptr();
|
||||
float weights_sum = 0.0;
|
||||
for (int64_t i = 0; i < weights_size; ++i) {
|
||||
ERR_FAIL_COND_V_MSG(weights[i] < 0.0, -1, vformat("Weight array contains the negative value %f at index %d.", weights[i], i));
|
||||
weights_sum += weights[i];
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,8 @@ void StringName::cleanup() {
|
||||
data.sort_custom<DebugSortReferences>();
|
||||
int unreferenced_stringnames = 0;
|
||||
int rarely_referenced_stringnames = 0;
|
||||
for (int i = 0; i < data.size(); i++) {
|
||||
const int data_size = data.size();
|
||||
for (int i = 0; i < data_size; i++) {
|
||||
print_line(itos(i + 1) + ": " + data[i]->name + " - " + itos(data[i]->debug_references));
|
||||
if (data[i]->debug_references == 0) {
|
||||
unreferenced_stringnames += 1;
|
||||
@@ -81,8 +82,8 @@ void StringName::cleanup() {
|
||||
}
|
||||
}
|
||||
|
||||
print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data.size(), unreferenced_stringnames, unreferenced_stringnames / float(data.size()) * 100));
|
||||
print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data.size(), rarely_referenced_stringnames, rarely_referenced_stringnames / float(data.size()) * 100));
|
||||
print_line(vformat("\nOut of %d StringNames, %d StringNames were never referenced during this run (0 times) (%.2f%%).", data_size, unreferenced_stringnames, unreferenced_stringnames / float(data_size) * 100));
|
||||
print_line(vformat("Out of %d StringNames, %d StringNames were rarely referenced during this run (1-4 times) (%.2f%%).", data_size, rarely_referenced_stringnames, rarely_referenced_stringnames / float(data_size) * 100));
|
||||
}
|
||||
#endif
|
||||
int lost_strings = 0;
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
<return type="int" />
|
||||
<param index="0" name="weights" type="PackedFloat32Array" />
|
||||
<description>
|
||||
Returns a random integer between [code]0[/code] and the size of the array that is passed as a parameter. Each value in the array should be a floating-point number that represents the relative likelihood that it will be returned as an index. A higher value means the value is more likely to be returned as an index, while a value of [code]0[/code] means it will never be returned as an index.
|
||||
Returns a random integer between [code]0[/code] and the size of the array that is passed as a parameter. Each value in the array should be a non-negative floating-point number that represents the relative likelihood that it will be returned as an index. A higher value means the value is more likely to be returned as an index, while a value of [code]0[/code] means it will never be returned as an index.
|
||||
For example, if [code skip-lint][0.5, 1, 1, 2][/code] is passed as a parameter, then the method is twice as likely to return [code]3[/code] (the index of the value [code]2[/code]) and twice as unlikely to return [code]0[/code] (the index of the value [code]0.5[/code]) compared to the indices [code]1[/code] and [code]2[/code].
|
||||
Prints an error and returns [code]-1[/code] if the array is empty.
|
||||
Prints an error and returns [code]-1[/code] if the array is empty or contains any negative values.
|
||||
[codeblocks]
|
||||
[gdscript]
|
||||
var rng = RandomNumberGenerator.new()
|
||||
|
||||
@@ -491,7 +491,9 @@ void EditorVisualProfiler::_graph_tex_draw() {
|
||||
|
||||
if (seeking) {
|
||||
int max_frames = frame_metrics.size();
|
||||
int frame = cursor_metric_edit->get_value() - (frame_metrics[last_metric].frame_number - max_frames + 1);
|
||||
|
||||
int64_t first_visible_frame = static_cast<int64_t>(frame_metrics[last_metric].frame_number) - max_frames + 1;
|
||||
int frame = (cursor_metric_edit->get_value() - first_visible_frame);
|
||||
if (frame < 0) {
|
||||
frame = 0;
|
||||
}
|
||||
|
||||
@@ -1058,10 +1058,11 @@ Ref<Texture2D> CodeTextEditor::_get_completion_icon(const ScriptLanguage::CodeCo
|
||||
Ref<Texture2D> tex;
|
||||
switch (p_option.kind) {
|
||||
case ScriptLanguage::CODE_COMPLETION_KIND_CLASS: {
|
||||
if (has_theme_icon(p_option.display, EditorStringName(EditorIcons))) {
|
||||
tex = get_editor_theme_icon(p_option.display);
|
||||
const String formatted_class_name = p_option.display.unquote();
|
||||
if (has_theme_icon(formatted_class_name, EditorStringName(EditorIcons))) {
|
||||
tex = get_editor_theme_icon(formatted_class_name);
|
||||
} else {
|
||||
tex = EditorNode::get_singleton()->get_class_icon(p_option.display);
|
||||
tex = EditorNode::get_singleton()->get_class_icon(formatted_class_name);
|
||||
if (tex.is_null()) {
|
||||
tex = get_editor_theme_icon(SNAME("Object"));
|
||||
}
|
||||
|
||||
@@ -224,8 +224,9 @@ void GenericTilePolygonEditor::_base_control_draw() {
|
||||
base_control->draw_polygon(polygon, v_color);
|
||||
|
||||
color.a = 0.7;
|
||||
for (int j = 0; j < polygon.size(); j++) {
|
||||
base_control->draw_line(polygon[j], polygon[(j + 1) % polygon.size()], color);
|
||||
const int polygon_size = polygon.size();
|
||||
for (int j = 0; j < polygon_size; j++) {
|
||||
base_control->draw_line(polygon[j], polygon[(j + 1) % polygon_size], color);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,9 +438,10 @@ void GenericTilePolygonEditor::_grab_polygon_segment_point(Vector2 p_pos, const
|
||||
float closest_distance = grab_threshold * 2.0;
|
||||
for (unsigned int i = 0; i < polygons.size(); i++) {
|
||||
const Vector<Vector2> &polygon = polygons[i];
|
||||
for (int j = 0; j < polygon.size(); j++) {
|
||||
const int polygon_size = polygon.size();
|
||||
for (int j = 0; j < polygon_size; j++) {
|
||||
const Vector2 segment_a = polygon[j];
|
||||
const Vector2 segment_b = polygon[(j + 1) % polygon.size()];
|
||||
const Vector2 segment_b = polygon[(j + 1) % polygon_size];
|
||||
Vector2 closest_point = Geometry2D::get_closest_point_to_segment(point, segment_a, segment_b);
|
||||
float distance = closest_point.distance_to(point);
|
||||
if (distance < grab_threshold / editor_zoom_widget->get_zoom() && distance < closest_distance) {
|
||||
@@ -463,7 +465,8 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
|
||||
|
||||
// Snap to polygon vertices.
|
||||
bool snapped = false;
|
||||
for (int i = 0; i < polygon.size(); i++) {
|
||||
const int polygon_size = polygon.size();
|
||||
for (int i = 0; i < polygon_size; i++) {
|
||||
float distance = r_point.distance_to(polygon[i]);
|
||||
if (distance < p_snap_dist && distance < r_current_snapped_dist) {
|
||||
snapped_point = polygon[i];
|
||||
@@ -474,9 +477,9 @@ void GenericTilePolygonEditor::_snap_to_tile_shape(Point2 &r_point, float &r_cur
|
||||
|
||||
// Snap to edges if we did not snap to vertices.
|
||||
if (!snapped) {
|
||||
for (int i = 0; i < polygon.size(); i++) {
|
||||
for (int i = 0; i < polygon_size; i++) {
|
||||
const Vector2 segment_a = polygon[i];
|
||||
const Vector2 segment_b = polygon[(i + 1) % polygon.size()];
|
||||
const Vector2 segment_b = polygon[(i + 1) % polygon_size];
|
||||
Point2 point = Geometry2D::get_closest_point_to_segment(r_point, segment_a, segment_b);
|
||||
float distance = r_point.distance_to(point);
|
||||
if (distance < p_snap_dist && distance < r_current_snapped_dist) {
|
||||
|
||||
@@ -607,6 +607,7 @@ void TileSetAtlasSourceEditor::_update_atlas_source_inspector() {
|
||||
|
||||
void TileSetAtlasSourceEditor::_update_tile_inspector() {
|
||||
// Update visibility.
|
||||
tile_inspector->edit(nullptr);
|
||||
if (tools_button_group->get_pressed_button() == tool_select_button) {
|
||||
if (!selection.is_empty()) {
|
||||
tile_proxy_object.instantiate(this);
|
||||
|
||||
@@ -1808,6 +1808,7 @@ void SpriteFramesEditor::edit(Ref<SpriteFrames> p_frames) {
|
||||
if (p_frames.is_null()) {
|
||||
frames.unref();
|
||||
_remove_sprite_node();
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -3211,6 +3211,8 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
|
||||
HashSet<String> loaded_scripts;
|
||||
List<String> extensions = _get_recognized_extensions();
|
||||
|
||||
_set_script_zoom_factor(p_layout->get_value("ScriptEditor", "zoom_factor", 1.0f));
|
||||
|
||||
for (const Variant &v : scripts) {
|
||||
String path = v;
|
||||
|
||||
@@ -3306,8 +3308,6 @@ void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
|
||||
}
|
||||
}
|
||||
|
||||
_set_script_zoom_factor(p_layout->get_value("ScriptEditor", "zoom_factor", 1.0f));
|
||||
|
||||
restoring_layout = false;
|
||||
|
||||
_update_script_names();
|
||||
|
||||
@@ -502,7 +502,7 @@ void EditorAutoloadSettings::update_autoload() {
|
||||
if (old_info.path == info.path) {
|
||||
// Still the same resource, check status
|
||||
info.node = old_info.node;
|
||||
if (info.node && info.node->is_instance()) {
|
||||
if (info.node && info.node->is_inside_tree()) {
|
||||
Ref<Script> scr = info.node->get_script();
|
||||
info.in_editor = scr.is_valid() && scr->is_tool();
|
||||
if (info.is_singleton == old_info.is_singleton && info.in_editor == old_info.in_editor) {
|
||||
|
||||
@@ -20,7 +20,7 @@ else:
|
||||
deps_folder = os.path.join("bin", "build_deps")
|
||||
|
||||
# AccessKit
|
||||
ac_version = "0.21.2"
|
||||
ac_version = "0.22.3"
|
||||
|
||||
# Create dependencies folder
|
||||
if not os.path.exists(deps_folder):
|
||||
|
||||
@@ -5093,6 +5093,7 @@ NodePath GLTFDocument::_find_material_node_path(Ref<GLTFState> p_state, const Re
|
||||
}
|
||||
|
||||
Ref<GLTFObjectModelProperty> GLTFDocument::import_object_model_property(Ref<GLTFState> p_state, const String &p_json_pointer) {
|
||||
ERR_FAIL_COND_V_MSG(p_state.is_null(), Ref<GLTFObjectModelProperty>(), "Cannot import object model property because GLTFState is null.");
|
||||
if (p_state->object_model_properties.has(p_json_pointer)) {
|
||||
return p_state->object_model_properties[p_json_pointer];
|
||||
}
|
||||
|
||||
@@ -480,6 +480,7 @@ GLTFBufferViewIndex GLTFState::append_data_to_buffers(const Vector<uint8_t> &p_d
|
||||
}
|
||||
|
||||
GLTFNodeIndex GLTFState::append_gltf_node(Ref<GLTFNode> p_gltf_node, Node *p_godot_scene_node, GLTFNodeIndex p_parent_node_index) {
|
||||
ERR_FAIL_COND_V(p_gltf_node.is_null(), GLTFNodeIndex(0));
|
||||
p_gltf_node->set_parent(p_parent_node_index);
|
||||
const GLTFNodeIndex new_index = nodes.size();
|
||||
nodes.append(p_gltf_node);
|
||||
|
||||
@@ -282,7 +282,7 @@ Error SceneReplicationInterface::on_replication_stop(Object *p_obj, Variant p_co
|
||||
for (KeyValue<int, PeerInfo> &E : peers_info) {
|
||||
E.value.sync_nodes.erase(sid);
|
||||
E.value.last_watch_usecs.erase(sid);
|
||||
if (sync->get_net_id()) {
|
||||
if (sync->get_net_id() && uint32_t(E.key) == tobj.remote_peer) {
|
||||
E.value.recv_sync_ids.erase(sync->get_net_id());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ class GodotGLRenderView extends GLSurfaceView implements GodotRenderView {
|
||||
// Pause the renderer
|
||||
godotRenderer.onActivityPaused();
|
||||
});
|
||||
pauseGLThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
@@ -93,6 +93,7 @@ class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderView {
|
||||
// Pause the renderer
|
||||
mRenderer.onVkPause();
|
||||
});
|
||||
pauseRenderThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -93,6 +93,7 @@ internal class VkRenderer {
|
||||
* Called when the rendering thread is resumed.
|
||||
*/
|
||||
fun onVkResume() {
|
||||
Log.v(TAG, "Renderer resumed")
|
||||
GodotLib.onRendererResumed()
|
||||
}
|
||||
|
||||
@@ -100,6 +101,7 @@ internal class VkRenderer {
|
||||
* Called when the rendering thread is paused.
|
||||
*/
|
||||
fun onVkPause() {
|
||||
Log.v(TAG, "Renderer paused")
|
||||
GodotLib.onRendererPaused()
|
||||
}
|
||||
|
||||
@@ -107,7 +109,7 @@ internal class VkRenderer {
|
||||
* Invoked when the render thread is in the process of shutting down.
|
||||
*/
|
||||
fun onRenderThreadExiting() {
|
||||
Log.d(TAG, "Destroying Godot Engine")
|
||||
Log.v(TAG, "Destroying Godot Engine")
|
||||
GodotLib.ondestroy()
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@
|
||||
package org.godotengine.godot.vulkan
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import android.view.SurfaceHolder
|
||||
import android.view.SurfaceView
|
||||
|
||||
@@ -51,6 +52,8 @@ import android.view.SurfaceView
|
||||
*/
|
||||
open internal class VkSurfaceView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {
|
||||
companion object {
|
||||
private val TAG = VkSurfaceView::class.java.simpleName
|
||||
|
||||
fun checkState(expression: Boolean, errorMessage: Any) {
|
||||
check(expression) { errorMessage.toString() }
|
||||
}
|
||||
@@ -129,14 +132,17 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf
|
||||
}
|
||||
|
||||
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
|
||||
Log.v(TAG, "On surface changed [$format]: $width x $height")
|
||||
vkThread.onSurfaceChanged(width, height)
|
||||
}
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder) {
|
||||
Log.v(TAG, "On surface destroyed")
|
||||
vkThread.onSurfaceDestroyed()
|
||||
}
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder) {
|
||||
Log.v(TAG, "On surface created")
|
||||
vkThread.onSurfaceCreated()
|
||||
}
|
||||
}
|
||||
@@ -246,8 +246,11 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_resize(JNIEnv *env, j
|
||||
ANativeWindow *native_window = ANativeWindow_fromSurface(env, p_surface);
|
||||
os_android->set_native_window(native_window);
|
||||
}
|
||||
DisplayServerAndroid::get_singleton()->reset_window();
|
||||
DisplayServerAndroid::get_singleton()->notify_surface_changed(p_width, p_height);
|
||||
|
||||
DisplayServerAndroid *dsa = DisplayServerAndroid::get_singleton();
|
||||
ERR_FAIL_NULL(dsa);
|
||||
dsa->reset_window();
|
||||
dsa->notify_surface_changed(p_width, p_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -288,8 +291,14 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
||||
|
||||
if (step.get() == STEP_SETUP) {
|
||||
// Since Godot is initialized on the UI thread, main_thread_id was set to that thread's id,
|
||||
// but for Godot purposes, the main thread is the one running the game loop
|
||||
Main::setup2(false); // The logo is shown in the next frame otherwise we run into rendering issues
|
||||
// but for Godot purposes, the main thread is the one running the game loop.
|
||||
// The logo is shown in the next frame otherwise we run into rendering issues.
|
||||
if (Main::setup2(false) != OK) {
|
||||
ERR_PRINT("Unable to complete engine setup!");
|
||||
OS::get_singleton()->alert("Unable to complete engine setup!");
|
||||
_terminate(env, false);
|
||||
return false;
|
||||
}
|
||||
input_handler = new AndroidInputHandler();
|
||||
step.increment();
|
||||
return true;
|
||||
@@ -316,7 +325,10 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
||||
|
||||
if (step.get() == STEP_STARTED) {
|
||||
if (Main::start() != EXIT_SUCCESS) {
|
||||
return true; // should exit instead and print the error
|
||||
ERR_PRINT("Unable to start engine!");
|
||||
OS::get_singleton()->alert("Unable to start engine!");
|
||||
_terminate(env, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
godot_java->on_godot_setup_completed(env);
|
||||
@@ -325,10 +337,13 @@ JNIEXPORT jboolean JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env,
|
||||
step.increment();
|
||||
}
|
||||
|
||||
DisplayServerAndroid::get_singleton()->process_accelerometer(accelerometer);
|
||||
DisplayServerAndroid::get_singleton()->process_gravity(gravity);
|
||||
DisplayServerAndroid::get_singleton()->process_magnetometer(magnetometer);
|
||||
DisplayServerAndroid::get_singleton()->process_gyroscope(gyroscope);
|
||||
DisplayServerAndroid *dsa = DisplayServerAndroid::get_singleton();
|
||||
if (dsa) {
|
||||
dsa->process_accelerometer(accelerometer);
|
||||
dsa->process_gravity(gravity);
|
||||
dsa->process_magnetometer(magnetometer);
|
||||
dsa->process_gyroscope(gyroscope);
|
||||
}
|
||||
|
||||
bool should_swap_buffers = false;
|
||||
if (os_android->main_loop_iterate(&should_swap_buffers)) {
|
||||
|
||||
@@ -383,14 +383,17 @@ bool OS_Android::main_loop_iterate(bool *r_should_swap_buffers) {
|
||||
if (!main_loop) {
|
||||
return false;
|
||||
}
|
||||
DisplayServerAndroid::get_singleton()->reset_swap_buffers_flag();
|
||||
DisplayServerAndroid::get_singleton()->process_events();
|
||||
DisplayServerAndroid *dsa = DisplayServerAndroid::get_singleton();
|
||||
ERR_FAIL_NULL_V(dsa, true);
|
||||
dsa->reset_swap_buffers_flag();
|
||||
dsa->process_events();
|
||||
|
||||
uint64_t current_frames_drawn = Engine::get_singleton()->get_frames_drawn();
|
||||
bool exit = Main::iteration();
|
||||
|
||||
if (r_should_swap_buffers) {
|
||||
*r_should_swap_buffers = !is_in_low_processor_usage_mode() ||
|
||||
DisplayServerAndroid::get_singleton()->should_swap_buffers() ||
|
||||
dsa->should_swap_buffers() ||
|
||||
RenderingServer::get_singleton()->has_changed() ||
|
||||
current_frames_drawn != Engine::get_singleton()->get_frames_drawn();
|
||||
}
|
||||
@@ -436,17 +439,25 @@ void OS_Android::_on_distraction_free_mode_changed(bool p_enable) {
|
||||
#endif
|
||||
|
||||
void OS_Android::main_loop_focusout() {
|
||||
DisplayServerAndroid::get_singleton()->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_OUT);
|
||||
DisplayServerAndroid *dsa = DisplayServerAndroid::get_singleton();
|
||||
if (dsa) {
|
||||
dsa->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_OUT);
|
||||
}
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_OUT);
|
||||
}
|
||||
|
||||
// Only pause when we are not in PiP mode.
|
||||
audio_driver_android.set_pause(!DisplayServerAndroid::get_singleton()->is_in_pip_mode());
|
||||
if (dsa) {
|
||||
// Only pause when we are not in PiP mode.
|
||||
audio_driver_android.set_pause(!dsa->is_in_pip_mode());
|
||||
}
|
||||
}
|
||||
|
||||
void OS_Android::main_loop_focusin() {
|
||||
DisplayServerAndroid::get_singleton()->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_IN);
|
||||
DisplayServerAndroid *dsa = DisplayServerAndroid::get_singleton();
|
||||
if (dsa) {
|
||||
dsa->send_window_event(DisplayServerEnums::WINDOW_EVENT_FOCUS_IN);
|
||||
}
|
||||
if (OS::get_singleton()->get_main_loop()) {
|
||||
OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_APPLICATION_FOCUS_IN);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
|
||||
#ifdef VULKAN_ENABLED
|
||||
|
||||
#include "core/variant/variant.h"
|
||||
|
||||
#include <drivers/vulkan/godot_vulkan.h>
|
||||
|
||||
const char *RenderingContextDriverVulkanAndroid::_get_platform_surface_extension() const {
|
||||
@@ -47,7 +49,7 @@ RenderingContextDriver::SurfaceID RenderingContextDriverVulkanAndroid::surface_c
|
||||
|
||||
VkSurfaceKHR vk_surface = VK_NULL_HANDLE;
|
||||
VkResult err = vkCreateAndroidSurfaceKHR(instance_get(), &create_info, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR), &vk_surface);
|
||||
ERR_FAIL_COND_V(err != VK_SUCCESS, SurfaceID());
|
||||
ERR_FAIL_COND_V_MSG(err != VK_SUCCESS, SurfaceID(), vformat("Couldn't create Android Surface (VkResult error %d).", err));
|
||||
|
||||
Surface *surface = memnew(Surface);
|
||||
surface->vk_surface = vk_surface;
|
||||
|
||||
@@ -509,12 +509,6 @@ void DisplayServerWayland::_mouse_update_mode() {
|
||||
|
||||
wayland_thread.pointer_set_constraint(constraint);
|
||||
|
||||
if (wanted_mouse_mode == DisplayServerEnums::MOUSE_MODE_CAPTURED) {
|
||||
WindowData *pointed_win = windows.getptr(wayland_thread.pointer_get_pointed_window_id());
|
||||
ERR_FAIL_NULL(pointed_win);
|
||||
wayland_thread.pointer_set_hint(pointed_win->rect.size / 2);
|
||||
}
|
||||
|
||||
mouse_mode = wanted_mouse_mode;
|
||||
}
|
||||
|
||||
@@ -1929,7 +1923,8 @@ void DisplayServerWayland::try_suspend() {
|
||||
void DisplayServerWayland::process_events() {
|
||||
wayland_thread.mutex.lock();
|
||||
|
||||
wayland_thread.keyboard_echo_keys();
|
||||
// Some behavior depends on the progress of the main thread.
|
||||
wayland_thread.main_loop_callback();
|
||||
|
||||
while (wayland_thread.has_message()) {
|
||||
Ref<WaylandThread::Message> msg = wayland_thread.pop_message();
|
||||
|
||||
@@ -401,7 +401,6 @@ void WaylandThread::_set_current_seat(struct wl_seat *p_seat) {
|
||||
seat_state_unlock_pointer(new_state);
|
||||
|
||||
wl_seat_current = p_seat;
|
||||
pointer_set_constraint(pointer_constraint);
|
||||
}
|
||||
|
||||
void WaylandThread::_window_hover(DisplayServerEnums::WindowID p_window_id) {
|
||||
@@ -2110,6 +2109,19 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
|
||||
}
|
||||
|
||||
if (ws != nullptr) {
|
||||
if (hover_changed && pd.pointed_id != DisplayServerEnums::INVALID_WINDOW_ID) {
|
||||
// We update the constraint only on enter.
|
||||
|
||||
wayland_thread->seat_state_unlock_pointer(ss);
|
||||
if (wayland_thread->pointer_constraint == PointerConstraint::LOCKED) {
|
||||
wayland_thread->seat_state_lock_pointer(ss, ws->wl_surface);
|
||||
// Godot always expects a centered pointer when locked.
|
||||
wayland_thread->seat_state_set_hint(ss, ws->rect.size.x / 2, ws->rect.size.y / 2);
|
||||
} else if (wayland_thread->pointer_constraint == PointerConstraint::CONFINED) {
|
||||
wayland_thread->seat_state_confine_pointer(ss, ws->wl_surface);
|
||||
}
|
||||
}
|
||||
|
||||
double scale = window_state_get_scale_factor(ws);
|
||||
|
||||
wayland_thread->_set_current_seat(ss->wl_seat);
|
||||
@@ -2871,6 +2883,28 @@ void WaylandThread::_wl_data_source_on_dnd_finished(void *data, struct wl_data_s
|
||||
void WaylandThread::_wl_data_source_on_action(void *data, struct wl_data_source *wl_data_source, uint32_t dnd_action) {
|
||||
}
|
||||
|
||||
void WaylandThread::_zwp_locked_pointer_v1_on_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) {
|
||||
SeatState *ss = (SeatState *)data;
|
||||
ERR_FAIL_NULL(ss);
|
||||
|
||||
ss->pointer_locked = true;
|
||||
|
||||
// Maybe by the time we lock the legacy constraint warp has already been
|
||||
// committed. Let's give it a shot.
|
||||
if (ss->constraint_warp_committed) {
|
||||
PointerConstraint old_constraint = ss->wayland_thread->pointer_constraint;
|
||||
ss->wayland_thread->pointer_set_constraint(PointerConstraint::NONE);
|
||||
ss->wayland_thread->pointer_set_constraint(old_constraint);
|
||||
|
||||
ss->pointer_locked = false;
|
||||
ss->constraint_warping = false;
|
||||
ss->constraint_warp_committed = false;
|
||||
}
|
||||
}
|
||||
|
||||
void WaylandThread::_zwp_locked_pointer_v1_on_unlocked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1) {
|
||||
}
|
||||
|
||||
void WaylandThread::_wp_color_manager_on_supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t render_intent) {
|
||||
ColorManagementState *cms = (ColorManagementState *)data;
|
||||
ERR_FAIL_NULL(cms);
|
||||
@@ -4113,10 +4147,13 @@ void WaylandThread::seat_state_unlock_pointer(SeatState *p_ss) {
|
||||
zwp_confined_pointer_v1_destroy(p_ss->wp_confined_pointer);
|
||||
p_ss->wp_confined_pointer = nullptr;
|
||||
}
|
||||
|
||||
p_ss->pointer_locked = false;
|
||||
}
|
||||
|
||||
void WaylandThread::seat_state_lock_pointer(SeatState *p_ss) {
|
||||
void WaylandThread::seat_state_lock_pointer(SeatState *p_ss, struct wl_surface *p_surface) {
|
||||
ERR_FAIL_NULL(p_ss);
|
||||
ERR_FAIL_NULL(p_surface);
|
||||
|
||||
if (p_ss->wl_pointer == nullptr) {
|
||||
WARN_PRINT("Can't lock - no pointer?");
|
||||
@@ -4128,15 +4165,12 @@ void WaylandThread::seat_state_lock_pointer(SeatState *p_ss) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (p_ss->wp_locked_pointer == nullptr) {
|
||||
struct wl_surface *locked_surface = window_get_wl_surface(p_ss->pointer_data.last_pointed_id);
|
||||
if (locked_surface == nullptr) {
|
||||
locked_surface = window_get_wl_surface(DisplayServerEnums::MAIN_WINDOW_ID);
|
||||
}
|
||||
ERR_FAIL_NULL(locked_surface);
|
||||
p_ss->pointer_locked = false;
|
||||
|
||||
p_ss->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(registry.wp_pointer_constraints, locked_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
}
|
||||
// We always set a listener that tracks the locking status. This is useful as
|
||||
// this constraint might be reused for the legacy warp hack.
|
||||
p_ss->wp_locked_pointer = zwp_pointer_constraints_v1_lock_pointer(registry.wp_pointer_constraints, p_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
zwp_locked_pointer_v1_add_listener(p_ss->wp_locked_pointer, &zwp_locked_pointer_v1_listener, p_ss);
|
||||
}
|
||||
|
||||
void WaylandThread::seat_state_set_hint(SeatState *p_ss, int p_x, int p_y) {
|
||||
@@ -4162,8 +4196,9 @@ void WaylandThread::seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y) {
|
||||
wp_pointer_warp_v1_warp_pointer(registry.wp_pointer_warp, surface, p_ss->wl_pointer, wl_fixed_from_int(p_x), wl_fixed_from_int(p_y), p_ss->pointer_enter_serial);
|
||||
}
|
||||
|
||||
void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) {
|
||||
void WaylandThread::seat_state_confine_pointer(SeatState *p_ss, struct wl_surface *p_surface) {
|
||||
ERR_FAIL_NULL(p_ss);
|
||||
ERR_FAIL_NULL(p_surface);
|
||||
|
||||
if (p_ss->wl_pointer == nullptr) {
|
||||
return;
|
||||
@@ -4174,10 +4209,7 @@ void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) {
|
||||
}
|
||||
|
||||
if (p_ss->wp_confined_pointer == nullptr) {
|
||||
struct wl_surface *confined_surface = window_get_wl_surface(p_ss->pointer_data.last_pointed_id);
|
||||
ERR_FAIL_NULL(confined_surface);
|
||||
|
||||
p_ss->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(registry.wp_pointer_constraints, confined_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
p_ss->wp_confined_pointer = zwp_pointer_constraints_v1_confine_pointer(registry.wp_pointer_constraints, p_surface, p_ss->wl_pointer, nullptr, ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5378,19 +5410,22 @@ DisplayServerEnums::WindowID WaylandThread::pointer_get_last_pointed_window_id()
|
||||
}
|
||||
|
||||
void WaylandThread::pointer_set_constraint(PointerConstraint p_constraint) {
|
||||
pointer_constraint = p_constraint;
|
||||
|
||||
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
|
||||
ERR_FAIL_NULL(ss);
|
||||
|
||||
if (ss) {
|
||||
WindowState *ws = window_get_state(ss->pointer_data.pointed_id);
|
||||
if (ws) {
|
||||
seat_state_unlock_pointer(ss);
|
||||
|
||||
if (p_constraint == PointerConstraint::LOCKED) {
|
||||
seat_state_lock_pointer(ss);
|
||||
} else if (p_constraint == PointerConstraint::CONFINED) {
|
||||
seat_state_confine_pointer(ss);
|
||||
if (pointer_constraint == PointerConstraint::LOCKED) {
|
||||
seat_state_lock_pointer(ss, ws->wl_surface);
|
||||
// Godot always expects a centered pointer when locked.
|
||||
seat_state_set_hint(ss, ws->rect.size.x / 2, ws->rect.size.y / 2);
|
||||
} else if (pointer_constraint == PointerConstraint::CONFINED) {
|
||||
seat_state_confine_pointer(ss, ws->wl_surface);
|
||||
}
|
||||
}
|
||||
|
||||
pointer_constraint = p_constraint;
|
||||
}
|
||||
|
||||
void WaylandThread::pointer_set_hint(const Point2i &p_hint) {
|
||||
@@ -5418,19 +5453,6 @@ void WaylandThread::pointer_set_hint(const Point2i &p_hint) {
|
||||
}
|
||||
|
||||
void WaylandThread::pointer_warp(const Point2i &p_to) {
|
||||
// NOTE: This is for compositors that don't support the pointer-warp protocol.
|
||||
// It's hacked together and not guaranteed to work.
|
||||
if (registry.wp_pointer_warp == nullptr) {
|
||||
PointerConstraint old_constraint = pointer_get_constraint();
|
||||
|
||||
pointer_set_constraint(PointerConstraint::LOCKED);
|
||||
pointer_set_hint(p_to);
|
||||
|
||||
pointer_set_constraint(old_constraint);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
|
||||
if (!ss) {
|
||||
return;
|
||||
@@ -5441,6 +5463,35 @@ void WaylandThread::pointer_warp(const Point2i &p_to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: This is for compositors that don't support the pointer-warp protocol.
|
||||
// It's hacked together and not guaranteed to work.
|
||||
if (registry.wp_pointer_warp == nullptr) {
|
||||
if (ss->constraint_warping) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ss->wp_locked_pointer) {
|
||||
seat_state_unlock_pointer(ss);
|
||||
seat_state_lock_pointer(ss, ws->wl_surface);
|
||||
}
|
||||
|
||||
pointer_set_hint(p_to);
|
||||
|
||||
ss->constraint_warping = true;
|
||||
ss->constraint_warp_committed = false;
|
||||
|
||||
if (!ss->wp_locked_pointer) {
|
||||
seat_state_unlock_pointer(ss);
|
||||
seat_state_lock_pointer(ss, ws->wl_surface);
|
||||
}
|
||||
|
||||
pointer_set_hint(p_to);
|
||||
|
||||
// Gotta wait a frame... Not ideal but, y'know.
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// NOTE: It looks like it's not really recommended to convert from
|
||||
// "godot-space" to "wayland-space" and in general I received mixed feelings
|
||||
// discussing about this. I'm not really sure about the maths behind this but,
|
||||
@@ -6133,6 +6184,32 @@ bool WaylandThread::is_fifo_available() const {
|
||||
return registry.wp_fifo_manager_name != 0;
|
||||
}
|
||||
|
||||
void WaylandThread::main_loop_callback() {
|
||||
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
|
||||
|
||||
if (ss) {
|
||||
seat_state_echo_keys(ss);
|
||||
|
||||
// We need to wait a commit before "finalizing" the "legacy warp" hack. This
|
||||
// callback is called *after* a commit, but we don't know when the warp
|
||||
// request has been made, so we first wait one commit, then "finalize" on the
|
||||
// next. Additionally, we also wait for the pointer to be locked, as otherwise
|
||||
// it's not guaranteed for the position hint to even be acknowledged, AFAICT.
|
||||
if (ss->constraint_warping && ss->pointer_locked) {
|
||||
if (ss->constraint_warp_committed) {
|
||||
PointerConstraint old_constraint = pointer_constraint;
|
||||
pointer_set_constraint(PointerConstraint::NONE);
|
||||
pointer_set_constraint(old_constraint);
|
||||
|
||||
ss->constraint_warping = false;
|
||||
ss->constraint_warp_committed = false;
|
||||
} else {
|
||||
ss->constraint_warp_committed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WaylandThread::is_suspended() const {
|
||||
for (const KeyValue<DisplayServerEnums::WindowID, WindowState> &E : windows) {
|
||||
if (!E.value.suspended) {
|
||||
|
||||
@@ -508,6 +508,11 @@ public:
|
||||
struct zwp_locked_pointer_v1 *wp_locked_pointer = nullptr;
|
||||
struct zwp_confined_pointer_v1 *wp_confined_pointer = nullptr;
|
||||
|
||||
bool pointer_locked = false;
|
||||
|
||||
bool constraint_warping = false;
|
||||
bool constraint_warp_committed = false;
|
||||
|
||||
struct zwp_pointer_gesture_pinch_v1 *wp_pointer_gesture_pinch = nullptr;
|
||||
|
||||
// NOTE: According to the wp_pointer_gestures protocol specification, there
|
||||
@@ -824,6 +829,9 @@ private:
|
||||
static void _xdg_popup_on_repositioned(void *data, struct xdg_popup *xdg_popup, uint32_t token);
|
||||
|
||||
// wayland-protocols event handlers.
|
||||
static void _zwp_locked_pointer_v1_on_locked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1);
|
||||
static void _zwp_locked_pointer_v1_on_unlocked(void *data, struct zwp_locked_pointer_v1 *zwp_locked_pointer_v1);
|
||||
|
||||
static void _wp_color_manager_on_supported_intent(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t render_intent);
|
||||
static void _wp_color_manager_on_supported_feature(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t feature);
|
||||
static void _wp_color_manager_on_supported_tf_named(void *data, struct wp_color_manager_v1 *wp_color_manager_v1, uint32_t tf);
|
||||
@@ -1027,6 +1035,11 @@ private:
|
||||
};
|
||||
|
||||
// wayland-protocols event listeners.
|
||||
static constexpr struct zwp_locked_pointer_v1_listener zwp_locked_pointer_v1_listener{
|
||||
.locked = _zwp_locked_pointer_v1_on_locked,
|
||||
.unlocked = _zwp_locked_pointer_v1_on_unlocked,
|
||||
};
|
||||
|
||||
static constexpr struct wp_color_manager_v1_listener wp_color_manager_listener = {
|
||||
.supported_intent = _wp_color_manager_on_supported_intent,
|
||||
.supported_feature = _wp_color_manager_on_supported_feature,
|
||||
@@ -1246,10 +1259,10 @@ public:
|
||||
static EmbeddingCompositorState *godot_embedding_compositor_get_state(struct godot_embedding_compositor *p_compositor);
|
||||
|
||||
void seat_state_unlock_pointer(SeatState *p_ss);
|
||||
void seat_state_lock_pointer(SeatState *p_ss);
|
||||
void seat_state_lock_pointer(SeatState *p_ss, struct wl_surface *p_surface);
|
||||
void seat_state_set_hint(SeatState *p_ss, int p_x, int p_y);
|
||||
void seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y);
|
||||
void seat_state_confine_pointer(SeatState *p_ss);
|
||||
void seat_state_confine_pointer(SeatState *p_ss, struct wl_surface *p_surface);
|
||||
|
||||
static void seat_state_update_cursor(SeatState *p_ss);
|
||||
|
||||
@@ -1369,6 +1382,8 @@ public:
|
||||
bool wait_frame_suspend_ms(int p_timeout);
|
||||
bool is_fifo_available() const;
|
||||
|
||||
void main_loop_callback();
|
||||
|
||||
uint64_t window_get_last_frame_time(DisplayServerEnums::WindowID p_window_id) const;
|
||||
bool window_is_suspended(DisplayServerEnums::WindowID p_window_id) const;
|
||||
bool is_suspended() const;
|
||||
|
||||
@@ -1689,7 +1689,7 @@ DisplayServerEnums::WindowID DisplayServerMacOS::create_sub_window(DisplayServer
|
||||
|
||||
DisplayServerEnums::WindowID id = _create_window(p_mode, p_vsync_mode, p_rect);
|
||||
|
||||
uint32_t set_flags = p_flags & ~(DisplayServerEnums::WINDOW_FLAG_MAX - 1); // Clear the flags that are not supported by the window.
|
||||
uint32_t set_flags = p_flags & ((1 << DisplayServerEnums::WINDOW_FLAG_MAX) - 1); // Clear the flags that are not supported by the window.
|
||||
while (set_flags != 0) {
|
||||
// Find the index of the next set bit.
|
||||
uint32_t index = (uint32_t)__builtin_ctzll(set_flags);
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
#include <shlwapi.h>
|
||||
#include <shobjidl.h>
|
||||
#include <wbemcli.h>
|
||||
#include <windns.h> // For QWORD.
|
||||
|
||||
#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE
|
||||
#define DWMWA_USE_IMMERSIVE_DARK_MODE 20
|
||||
@@ -287,6 +288,37 @@ void DisplayServerWindows::_set_mouse_mode_impl(DisplayServerEnums::MouseMode p_
|
||||
SetCapture(wd.hWnd);
|
||||
|
||||
_register_raw_input_devices(window_id);
|
||||
|
||||
const BitField<WinKeyModifierMask> &mods = _get_mods();
|
||||
|
||||
Ref<InputEventMouseMotion> mm;
|
||||
mm.instantiate();
|
||||
|
||||
mm->set_window_id(window_id);
|
||||
mm->set_pressure(windows[window_id].last_pressure);
|
||||
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
|
||||
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
|
||||
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
|
||||
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
|
||||
mm->set_button_mask(mouse_get_button_state());
|
||||
|
||||
mm->set_position(center);
|
||||
mm->set_global_position(center);
|
||||
mm->set_relative(Vector2(0, 0));
|
||||
mm->set_relative_screen_position(Vector2(0, 0));
|
||||
mm->set_velocity(Vector2(0, 0));
|
||||
mm->set_screen_velocity(Vector2(0, 0));
|
||||
|
||||
mm->set_global_position(center);
|
||||
mm->set_velocity(Vector2(0, 0));
|
||||
mm->set_screen_velocity(Vector2(0, 0));
|
||||
|
||||
if (windows[window_id].window_focused || window_get_active_popup() == window_id) {
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
}
|
||||
|
||||
old_x = center.x;
|
||||
old_y = center.y;
|
||||
}
|
||||
} else {
|
||||
// Mouse is free to move around (not captured or confined).
|
||||
@@ -4221,6 +4253,174 @@ String DisplayServerWindows::keyboard_get_layout_name(int p_index) const {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Vector2 DisplayServerWindows::_get_raw_mouse_motion(const RAWINPUT &p_raw, DisplayServerEnums::WindowID p_window_id) {
|
||||
if (p_raw.data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
|
||||
return Vector2(p_raw.data.mouse.lLastX, p_raw.data.mouse.lLastY);
|
||||
} else if (p_raw.data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
|
||||
int n_screen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
int n_screen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
int n_screen_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
int n_screen_top = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
|
||||
Vector2 abs_pos = Vector2(
|
||||
(double(p_raw.data.mouse.lLastX) - 65536.0 / (n_screen_width)) * n_screen_width / 65536.0 + n_screen_left,
|
||||
(double(p_raw.data.mouse.lLastY) - 65536.0 / (n_screen_height)) * n_screen_height / 65536.0 + n_screen_top);
|
||||
|
||||
POINT coords; // Client coords.
|
||||
coords.x = abs_pos.x;
|
||||
coords.y = abs_pos.y;
|
||||
|
||||
ScreenToClient(windows[p_window_id].hWnd, &coords);
|
||||
|
||||
Vector2 relative(coords.x - old_x, coords.y - old_y);
|
||||
old_x = coords.x;
|
||||
old_y = coords.y;
|
||||
return relative;
|
||||
}
|
||||
return Vector2();
|
||||
}
|
||||
|
||||
void DisplayServerWindows::_process_raw_mouse_motion(const Vector2 &p_relative, bool p_left_button_down, DisplayServerEnums::WindowID p_window_id) {
|
||||
if (p_relative == Vector2()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<InputEventMouseMotion> mm;
|
||||
mm.instantiate();
|
||||
const BitField<WinKeyModifierMask> &mods = _get_mods();
|
||||
|
||||
mm->set_window_id(p_window_id);
|
||||
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
|
||||
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
|
||||
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
|
||||
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
|
||||
|
||||
mm->set_pressure(p_left_button_down ? 1.0f : 0.0f);
|
||||
mm->set_button_mask(mouse_get_button_state());
|
||||
|
||||
Point2i c(windows[p_window_id].width / 2, windows[p_window_id].height / 2);
|
||||
|
||||
// Centering just so it works as before.
|
||||
POINT pos = { (int)c.x, (int)c.y };
|
||||
ClientToScreen(windows[p_window_id].hWnd, &pos);
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
|
||||
mm->set_position(c);
|
||||
mm->set_global_position(c);
|
||||
mm->set_velocity(Vector2(0, 0));
|
||||
mm->set_screen_velocity(Vector2(0, 0));
|
||||
mm->set_relative(p_relative);
|
||||
mm->set_relative_screen_position(p_relative);
|
||||
|
||||
if (windows[p_window_id].window_focused || windows[p_window_id].is_popup) {
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerWindows::_process_raw_input_event(const RAWINPUT &p_raw, DisplayServerEnums::WindowID p_window_id) {
|
||||
if (p_raw.header.dwType == RIM_TYPEKEYBOARD) {
|
||||
if (p_raw.data.keyboard.VKey == VK_SHIFT) {
|
||||
// If multiple Shifts are held down at the same time,
|
||||
// Windows natively only sends a KEYUP for the last one to be released.
|
||||
// Handle all Shift "key up" events here for consistency.
|
||||
if (p_raw.data.keyboard.Flags & RI_KEY_BREAK) {
|
||||
ERR_FAIL_COND(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
|
||||
const BitField<WinKeyModifierMask> &mods = _get_mods();
|
||||
|
||||
KeyEvent ke;
|
||||
ke.shift = false;
|
||||
ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);
|
||||
ke.alt = mods.has_flag(WinKeyModifierMask::ALT);
|
||||
ke.control = mods.has_flag(WinKeyModifierMask::CTRL);
|
||||
ke.meta = mods.has_flag(WinKeyModifierMask::META);
|
||||
ke.uMsg = WM_KEYUP;
|
||||
ke.window_id = p_window_id;
|
||||
|
||||
ke.wParam = VK_SHIFT;
|
||||
// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
|
||||
// Bit 30 -> key was previously down, bit 31 -> key is being released.
|
||||
ke.lParam = p_raw.data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;
|
||||
key_event_buffer[key_event_pos++] = ke;
|
||||
}
|
||||
}
|
||||
} else if (mouse_mode == DisplayServerEnums::MOUSE_MODE_CAPTURED && p_raw.header.dwType == RIM_TYPEMOUSE) {
|
||||
_process_raw_mouse_motion(
|
||||
_get_raw_mouse_motion(p_raw, p_window_id),
|
||||
p_raw.data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN,
|
||||
p_window_id);
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerWindows::process_raw_input() {
|
||||
if (!use_raw_input) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use the same window the mouse is captured for in _set_mouse_mode_impl(),
|
||||
// which is not necessarily the main window.
|
||||
DisplayServerEnums::WindowID window_id = _get_focused_window_or_popup();
|
||||
if (!windows.has(window_id)) {
|
||||
window_id = DisplayServerEnums::MAIN_WINDOW_ID;
|
||||
}
|
||||
|
||||
// Normal 8 kHz frames stay precise; only a larger-sized tail that originates
|
||||
// from a stall is coalesced.
|
||||
constexpr UINT MAX_RAW_MOUSE_EVENTS_PER_FRAME = 512;
|
||||
UINT raw_mouse_events = 0;
|
||||
Vector2 coalesced_raw_mouse_motion;
|
||||
bool coalesced_raw_mouse_left_button_down = false;
|
||||
const bool coalesce_all_raw_mouse_motion = Input::get_singleton()->is_using_accumulated_input();
|
||||
auto flush_coalesced_raw_mouse_motion = [&]() {
|
||||
_process_raw_mouse_motion(coalesced_raw_mouse_motion, coalesced_raw_mouse_left_button_down, window_id);
|
||||
coalesced_raw_mouse_motion = Vector2();
|
||||
coalesced_raw_mouse_left_button_down = false;
|
||||
};
|
||||
|
||||
// Read until the raw queue is empty. A small pass cap can never catch up
|
||||
// after a long stall reaches Windows' 10,000-message queue limit, but a
|
||||
// stall-sized captured mouse tail is coalesced below before it reaches the
|
||||
// rest of the engine.
|
||||
while (true) {
|
||||
UINT n_buffer = 0;
|
||||
// With a null buffer, this reports the byte size of the first pending
|
||||
// message (the minimum required buffer), not a message count.
|
||||
if (GetRawInputBuffer(nullptr, &n_buffer, sizeof(RAWINPUTHEADER)) != 0 || n_buffer == 0) {
|
||||
flush_coalesced_raw_mouse_motion();
|
||||
return;
|
||||
}
|
||||
|
||||
UINT dw_size = n_buffer * sizeof(RAWINPUT);
|
||||
LPBYTE lpb = new BYTE[dw_size];
|
||||
|
||||
// NOTE: Use dw_size, not n_buffer.
|
||||
UINT n_read = GetRawInputBuffer((PRAWINPUT)lpb, &dw_size, sizeof(RAWINPUTHEADER));
|
||||
if (n_read == (UINT)-1 || n_read == 0) {
|
||||
delete[] lpb;
|
||||
flush_coalesced_raw_mouse_motion();
|
||||
return;
|
||||
}
|
||||
|
||||
PRAWINPUT raw = (PRAWINPUT)lpb;
|
||||
for (UINT i = 0; i < n_read; ++i) {
|
||||
if (mouse_mode == DisplayServerEnums::MOUSE_MODE_CAPTURED &&
|
||||
raw->header.dwType == RIM_TYPEMOUSE &&
|
||||
(coalesce_all_raw_mouse_motion || raw_mouse_events >= MAX_RAW_MOUSE_EVENTS_PER_FRAME)) {
|
||||
coalesced_raw_mouse_motion += _get_raw_mouse_motion(*raw, window_id);
|
||||
coalesced_raw_mouse_left_button_down = coalesced_raw_mouse_left_button_down ||
|
||||
(raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN);
|
||||
} else {
|
||||
_process_raw_input_event(*raw, window_id);
|
||||
}
|
||||
if (raw->header.dwType == RIM_TYPEMOUSE) {
|
||||
raw_mouse_events++;
|
||||
}
|
||||
// Move to next RAWINPUT in buffer.
|
||||
raw = NEXTRAWINPUTBLOCK(raw);
|
||||
}
|
||||
delete[] lpb;
|
||||
}
|
||||
}
|
||||
|
||||
void DisplayServerWindows::process_events() {
|
||||
ERR_FAIL_COND(!Thread::is_main_thread());
|
||||
|
||||
@@ -4233,8 +4433,51 @@ void DisplayServerWindows::process_events() {
|
||||
}
|
||||
|
||||
_THREAD_SAFE_LOCK_
|
||||
|
||||
process_raw_input();
|
||||
|
||||
// The pump throttles only what the hardware can flood, and drains the rest.
|
||||
// See <https://ph3at.github.io/posts/Windows-Input/> for more information.
|
||||
//
|
||||
// - WM_INPUT is never dispatched from here: pulling raw input through
|
||||
// PeekMessage() one message at a time is what collapses the pump at high
|
||||
// polling rates (2,000 Hz and above). It stays in the queue for the next
|
||||
// buffered read in process_raw_input().
|
||||
// - WM_MOUSEMOVE and WM_NCMOUSEMOVE are synthesized on demand from the
|
||||
// hardware input stream, so while the mouse is moving, the queue never
|
||||
// reads empty and an unbounded pump spins until the mouse stops. They are
|
||||
// dispatched at most once per frame each. No motion is lost: they carry
|
||||
// the latest (coalesced) cursor position, and the relative motion derived
|
||||
// from successive positions preserves the total delta. Captured mouse
|
||||
// mode gets its sub-frame motion from raw input.
|
||||
// - Everything else (keys, characters, mouse buttons, wheel, system
|
||||
// messages) is discrete and low-rate, and is drained fully. Capping these
|
||||
// would split the WM_CHAR posted by TranslateMessage() from its
|
||||
// WM_KEYDOWN across frames, which breaks the pairing logic in
|
||||
// _process_key_events() and makes keys fire twice.
|
||||
MSG msg = {};
|
||||
while (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
auto peek_discrete = [&] {
|
||||
BOOL ret = PeekMessageW(&msg, nullptr, 0, WM_NCMOUSEMOVE - 1, PM_REMOVE);
|
||||
if (!ret) {
|
||||
ret = PeekMessageW(&msg, nullptr, WM_NCMOUSEMOVE + 1, WM_INPUT - 1, PM_REMOVE);
|
||||
}
|
||||
if (!ret) {
|
||||
ret = PeekMessageW(&msg, nullptr, WM_INPUT + 1, WM_MOUSEMOVE - 1, PM_REMOVE);
|
||||
}
|
||||
if (!ret) {
|
||||
ret = PeekMessageW(&msg, nullptr, WM_MOUSEMOVE + 1, std::numeric_limits<UINT>::max(), PM_REMOVE);
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
while (peek_discrete()) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
if (PeekMessageW(&msg, nullptr, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
if (PeekMessageW(&msg, nullptr, WM_NCMOUSEMOVE, WM_NCMOUSEMOVE, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
}
|
||||
@@ -5629,103 +5872,20 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
break;
|
||||
}
|
||||
|
||||
// The bulk of raw input is drained in batch by process_raw_input()
|
||||
// before the message pump runs. This only sees WM_INPUT messages the
|
||||
// pump picks up afterwards (it peeks the full message range whenever
|
||||
// the app is not focused, and raw input can also arrive while the pump
|
||||
// itself is running). Without this fallback, DefWindowProc() would
|
||||
// free their data unread.
|
||||
UINT dwSize;
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER)) != 0 || dwSize == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, nullptr, &dwSize, sizeof(RAWINPUTHEADER));
|
||||
LPBYTE lpb = new BYTE[dwSize];
|
||||
if (lpb == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) != dwSize) {
|
||||
OutputDebugString(TEXT("GetRawInputData does not return correct size !\n"));
|
||||
}
|
||||
|
||||
RAWINPUT *raw = (RAWINPUT *)lpb;
|
||||
|
||||
const BitField<WinKeyModifierMask> &mods = _get_mods();
|
||||
if (raw->header.dwType == RIM_TYPEKEYBOARD) {
|
||||
if (raw->data.keyboard.VKey == VK_SHIFT) {
|
||||
// If multiple Shifts are held down at the same time,
|
||||
// Windows natively only sends a KEYUP for the last one to be released.
|
||||
if (raw->data.keyboard.Flags & RI_KEY_BREAK) {
|
||||
// Make sure to check the latest key state since
|
||||
// we're in the middle of the message queue.
|
||||
if (GetAsyncKeyState(VK_SHIFT) < 0) {
|
||||
// A Shift is released, but another Shift is still held
|
||||
ERR_BREAK(key_event_pos >= KEY_EVENT_BUFFER_SIZE);
|
||||
|
||||
KeyEvent ke;
|
||||
ke.shift = false;
|
||||
ke.altgr = mods.has_flag(WinKeyModifierMask::ALT_GR);
|
||||
ke.alt = mods.has_flag(WinKeyModifierMask::ALT);
|
||||
ke.control = mods.has_flag(WinKeyModifierMask::CTRL);
|
||||
ke.meta = mods.has_flag(WinKeyModifierMask::META);
|
||||
ke.uMsg = WM_KEYUP;
|
||||
ke.window_id = window_id;
|
||||
|
||||
ke.wParam = VK_SHIFT;
|
||||
// data.keyboard.MakeCode -> 0x2A - left shift, 0x36 - right shift.
|
||||
// Bit 30 -> key was previously down, bit 31 -> key is being released.
|
||||
ke.lParam = raw->data.keyboard.MakeCode << 16 | 1 << 30 | 1 << 31;
|
||||
key_event_buffer[key_event_pos++] = ke;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mouse_mode == DisplayServerEnums::MOUSE_MODE_CAPTURED && raw->header.dwType == RIM_TYPEMOUSE) {
|
||||
Ref<InputEventMouseMotion> mm;
|
||||
mm.instantiate();
|
||||
|
||||
mm->set_window_id(window_id);
|
||||
mm->set_ctrl_pressed(mods.has_flag(WinKeyModifierMask::CTRL));
|
||||
mm->set_shift_pressed(mods.has_flag(WinKeyModifierMask::SHIFT));
|
||||
mm->set_alt_pressed(mods.has_flag(WinKeyModifierMask::ALT));
|
||||
mm->set_meta_pressed(mods.has_flag(WinKeyModifierMask::META));
|
||||
|
||||
mm->set_pressure((raw->data.mouse.ulButtons & RI_MOUSE_LEFT_BUTTON_DOWN) ? 1.0f : 0.0f);
|
||||
|
||||
mm->set_button_mask(mouse_get_button_state());
|
||||
|
||||
Point2i c(windows[window_id].width / 2, windows[window_id].height / 2);
|
||||
|
||||
// Centering just so it works as before.
|
||||
POINT pos = { (int)c.x, (int)c.y };
|
||||
ClientToScreen(windows[window_id].hWnd, &pos);
|
||||
SetCursorPos(pos.x, pos.y);
|
||||
|
||||
mm->set_position(c);
|
||||
mm->set_global_position(c);
|
||||
mm->set_velocity(Vector2(0, 0));
|
||||
mm->set_screen_velocity(Vector2(0, 0));
|
||||
|
||||
if (raw->data.mouse.usFlags == MOUSE_MOVE_RELATIVE) {
|
||||
mm->set_relative(Vector2(raw->data.mouse.lLastX, raw->data.mouse.lLastY));
|
||||
|
||||
} else if (raw->data.mouse.usFlags == MOUSE_MOVE_ABSOLUTE) {
|
||||
int nScreenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
|
||||
int nScreenHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
||||
int nScreenLeft = GetSystemMetrics(SM_XVIRTUALSCREEN);
|
||||
int nScreenTop = GetSystemMetrics(SM_YVIRTUALSCREEN);
|
||||
|
||||
Vector2 abs_pos(
|
||||
(double(raw->data.mouse.lLastX) - 65536.0 / (nScreenWidth)) * nScreenWidth / 65536.0 + nScreenLeft,
|
||||
(double(raw->data.mouse.lLastY) - 65536.0 / (nScreenHeight)) * nScreenHeight / 65536.0 + nScreenTop);
|
||||
|
||||
POINT coords; // Client coords.
|
||||
coords.x = abs_pos.x;
|
||||
coords.y = abs_pos.y;
|
||||
|
||||
ScreenToClient(hWnd, &coords);
|
||||
|
||||
mm->set_relative(Vector2(coords.x - old_x, coords.y - old_y));
|
||||
old_x = coords.x;
|
||||
old_y = coords.y;
|
||||
}
|
||||
mm->set_relative_screen_position(mm->get_relative());
|
||||
|
||||
if ((windows[window_id].window_focused || windows[window_id].is_popup) && mm->get_relative() != Vector2()) {
|
||||
Input::get_singleton()->parse_input_event(mm);
|
||||
}
|
||||
if (GetRawInputData((HRAWINPUT)lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)) == dwSize) {
|
||||
_process_raw_input_event(*(RAWINPUT *)lpb, window_id);
|
||||
}
|
||||
delete[] lpb;
|
||||
} break;
|
||||
@@ -6614,6 +6774,11 @@ LRESULT DisplayServerWindows::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARA
|
||||
} break;
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYUP:
|
||||
// Windows handles shift KEYUP inconsistently, handle with WM_INPUT
|
||||
if (wParam == VK_SHIFT) {
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYDOWN: {
|
||||
if (windows[window_id].ime_suppress_next_keyup && (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP)) {
|
||||
|
||||
@@ -551,6 +551,10 @@ class DisplayServerWindows : public DisplayServer {
|
||||
HWND _find_window_from_process_id(ProcessID p_pid, HWND p_current_hwnd);
|
||||
|
||||
void initialize_tts() const;
|
||||
void process_raw_input();
|
||||
Vector2 _get_raw_mouse_motion(const RAWINPUT &p_raw, DisplayServerEnums::WindowID p_window_id);
|
||||
void _process_raw_mouse_motion(const Vector2 &p_relative, bool p_left_button_down, DisplayServerEnums::WindowID p_window_id);
|
||||
void _process_raw_input_event(const RAWINPUT &p_raw, DisplayServerEnums::WindowID p_window_id);
|
||||
|
||||
struct ScreenHdrData {
|
||||
bool hdr_supported = false;
|
||||
|
||||
@@ -68,6 +68,13 @@ char *wc_to_utf8(const wchar_t *wc) {
|
||||
int widechar_main(int argc, wchar_t **argv) {
|
||||
godot_init_profiler();
|
||||
|
||||
// Prevent Windows from replacing the window with a "ghost" when the main
|
||||
// thread briefly stalls (e.g. during editor startup or focus transitions).
|
||||
// The ghost window intercepts click-backs, so the editor never receives
|
||||
// WM_ACTIVATEAPP and gets permanently stuck at the unfocused throttle.
|
||||
// Must be called before any window is created.
|
||||
DisableProcessWindowsGhosting();
|
||||
|
||||
OS_Windows os(nullptr);
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
@@ -43,9 +43,10 @@ void CollisionPolygon2D::_build_polygon() {
|
||||
collision_object->shape_owner_clear_shapes(owner_id);
|
||||
|
||||
bool solids = build_mode == BUILD_SOLIDS;
|
||||
const int polygon_size = polygon.size();
|
||||
|
||||
if (solids) {
|
||||
if (polygon.size() < 3) {
|
||||
if (polygon_size < 3) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -59,19 +60,19 @@ void CollisionPolygon2D::_build_polygon() {
|
||||
}
|
||||
|
||||
} else {
|
||||
if (polygon.size() < 2) {
|
||||
if (polygon_size < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
Ref<ConcavePolygonShape2D> concave = memnew(ConcavePolygonShape2D);
|
||||
|
||||
Vector<Vector2> segments;
|
||||
segments.resize(polygon.size() * 2);
|
||||
segments.resize(polygon_size * 2);
|
||||
Vector2 *w = segments.ptrw();
|
||||
|
||||
for (int i = 0; i < polygon.size(); i++) {
|
||||
for (int i = 0; i < polygon_size; i++) {
|
||||
w[(i << 1) + 0] = polygon[i];
|
||||
w[(i << 1) + 1] = polygon[(i + 1) % polygon.size()];
|
||||
w[(i << 1) + 1] = polygon[(i + 1) % polygon_size];
|
||||
}
|
||||
|
||||
concave->set_segments(segments);
|
||||
|
||||
@@ -1288,10 +1288,11 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa
|
||||
GenProbesOctree octree;
|
||||
octree.size = subdiv;
|
||||
|
||||
for (int i = 0; i < mesh_data.size(); i++) {
|
||||
const int mesh_data_size = mesh_data.size();
|
||||
for (int i = 0; i < mesh_data_size; i++) {
|
||||
if (p_bake_step) {
|
||||
float p = (float)(i) / mesh_data.size();
|
||||
p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d"), i, mesh_data.size()), p_bake_userdata, false);
|
||||
float p = (float)(i) / mesh_data_size;
|
||||
p_bake_step(0.3 + p * 0.1, vformat(RTR("Creating probes from mesh %d/%d"), i, mesh_data_size), p_bake_userdata, false);
|
||||
}
|
||||
|
||||
for (int j = 0; j < mesh_data[i].points.size(); j += 3) {
|
||||
|
||||
@@ -251,8 +251,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
|
||||
|
||||
if (status.press_attempt && status.pressing_inside) {
|
||||
if (toggle_mode) {
|
||||
bool is_pressed = p_event->is_pressed();
|
||||
if ((is_pressed && action_mode == ACTION_MODE_BUTTON_PRESS) || (!is_pressed && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
|
||||
if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (p_event->is_released() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
|
||||
if (action_mode == ACTION_MODE_BUTTON_PRESS) {
|
||||
status.press_attempt = false;
|
||||
status.pressing_inside = false;
|
||||
@@ -268,7 +267,7 @@ void BaseButton::on_action_event(Ref<InputEvent> p_event) {
|
||||
queue_accessibility_update();
|
||||
}
|
||||
} else {
|
||||
if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (!p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
|
||||
if ((p_event->is_pressed() && action_mode == ACTION_MODE_BUTTON_PRESS) || (p_event->is_released() && action_mode == ACTION_MODE_BUTTON_RELEASE)) {
|
||||
_pressed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1641,7 +1641,7 @@ void ParticlesStorage::update_particles() {
|
||||
particles->instance_motion_vectors_last_change = frame;
|
||||
|
||||
// Copy particles to instance buffer.
|
||||
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY) {
|
||||
if (particles->draw_order != RSE::PARTICLES_DRAW_ORDER_VIEW_DEPTH && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY && particles->transform_align != RSE::PARTICLES_TRANSFORM_ALIGN_LOCAL_BILLBOARD) {
|
||||
//does not need view dependent operation, do copy here
|
||||
ParticlesShader::CopyPushConstant copy_push_constant;
|
||||
|
||||
|
||||
@@ -2358,7 +2358,7 @@ void RendererSceneCull::_light_instance_setup_directional_shadow(int p_shadow_in
|
||||
cull.shadows[p_shadow_index].cascades[i].split = distances[i + 1];
|
||||
cull.shadows[p_shadow_index].cascades[i].shadow_texel_size = radius * 2.0 / texture_size;
|
||||
cull.shadows[p_shadow_index].cascades[i].bias_scale = (z_max - z_min_cam);
|
||||
cull.shadows[p_shadow_index].cascades[i].range_begin = z_max;
|
||||
cull.shadows[p_shadow_index].cascades[i].range_begin = z_max - z_vec.dot(p_cam_transform.origin);
|
||||
cull.shadows[p_shadow_index].cascades[i].uv_scale = uv_scale;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -972,7 +972,7 @@ public:
|
||||
FUNC3(instance_geometry_set_shader_parameter, RID, const StringName &, const Variant &)
|
||||
FUNC2RC(Variant, instance_geometry_get_shader_parameter, RID, const StringName &)
|
||||
FUNC2RC(Variant, instance_geometry_get_shader_parameter_default_value, RID, const StringName &)
|
||||
FUNC2C(instance_geometry_get_shader_parameter_list, RID, List<PropertyInfo> *)
|
||||
FUNC2SC(instance_geometry_get_shader_parameter_list, RID, List<PropertyInfo> *)
|
||||
|
||||
FUNC3R(TypedArray<Image>, bake_render_uv2, RID, const TypedArray<RID> &, const Size2i &)
|
||||
FUNC4R(PackedByteArray, bake_render_area_light_atlas, const TypedArray<RID> &, const TypedArray<Rect2> &, const Size2i &, int)
|
||||
@@ -1059,7 +1059,7 @@ public:
|
||||
FUNC3(canvas_item_set_instance_shader_parameter, RID, const StringName &, const Variant &)
|
||||
FUNC2RC(Variant, canvas_item_get_instance_shader_parameter, RID, const StringName &)
|
||||
FUNC2RC(Variant, canvas_item_get_instance_shader_parameter_default_value, RID, const StringName &)
|
||||
FUNC2C(canvas_item_get_instance_shader_parameter_list, RID, List<PropertyInfo> *)
|
||||
FUNC2SC(canvas_item_get_instance_shader_parameter_list, RID, List<PropertyInfo> *)
|
||||
|
||||
FUNC2(canvas_item_set_use_parent_material, RID, bool)
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -8,7 +8,7 @@ readability.
|
||||
## accesskit
|
||||
|
||||
- Upstream: https://github.com/AccessKit/accesskit-c
|
||||
- Version: 0.21.2 (72460cd52d1b24492393cdf54a6f19d2fd63e258, 2026)
|
||||
- Version: 0.22.3 (826d672661f9453c8b269ab3946dbcbae6300555, 2026)
|
||||
- License: MIT
|
||||
|
||||
Files extracted from upstream source:
|
||||
|
||||
+233
-19
@@ -19,6 +19,44 @@
|
||||
#ifdef __ANDROID__
|
||||
#include <jni.h>
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <TargetConditionals.h>
|
||||
/* Older SDKs may not define all of these; force them to 0 so the
|
||||
value-based checks below are safe. */
|
||||
#ifndef TARGET_OS_OSX
|
||||
#define TARGET_OS_OSX 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_IOS
|
||||
#define TARGET_OS_IOS 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_TV
|
||||
#define TARGET_OS_TV 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_WATCH
|
||||
#define TARGET_OS_WATCH 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_VISION
|
||||
#define TARGET_OS_VISION 0
|
||||
#endif
|
||||
#ifndef TARGET_OS_MACCATALYST
|
||||
#define TARGET_OS_MACCATALYST 0
|
||||
#endif
|
||||
#if TARGET_OS_OSX
|
||||
#define ACCESSKIT_MACOS
|
||||
#endif
|
||||
#if TARGET_OS_IOS || TARGET_OS_MACCATALYST
|
||||
#define ACCESSKIT_IOS
|
||||
#endif
|
||||
#if TARGET_OS_TV
|
||||
#define ACCESSKIT_TVOS
|
||||
#endif
|
||||
#if TARGET_OS_WATCH
|
||||
#define ACCESSKIT_WATCHOS
|
||||
#endif
|
||||
#if TARGET_OS_VISION
|
||||
#define ACCESSKIT_VISIONOS
|
||||
#endif
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
/**
|
||||
* An action to be taken on an accessibility node.
|
||||
@@ -592,15 +630,31 @@ typedef struct accesskit_android_queued_events accesskit_android_queued_events;
|
||||
|
||||
typedef struct accesskit_custom_action accesskit_custom_action;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
typedef struct accesskit_ios_adapter accesskit_ios_adapter;
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
typedef struct accesskit_ios_queued_events accesskit_ios_queued_events;
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
typedef struct accesskit_ios_subclassing_adapter
|
||||
accesskit_ios_subclassing_adapter;
|
||||
#endif
|
||||
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
typedef struct accesskit_macos_adapter accesskit_macos_adapter;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
typedef struct accesskit_macos_queued_events accesskit_macos_queued_events;
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
typedef struct accesskit_macos_subclassing_adapter
|
||||
accesskit_macos_subclassing_adapter;
|
||||
#endif
|
||||
@@ -2561,7 +2615,167 @@ void accesskit_android_injecting_adapter_update_if_active(
|
||||
void *update_factory_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Memory is also freed when calling this function.
|
||||
*/
|
||||
void accesskit_ios_queued_events_raise(
|
||||
struct accesskit_ios_queued_events *events);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* This function must be called on the main thread.
|
||||
* All handlers will always be called on the main thread.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* `view` must be a valid, unreleased pointer to a `UIView`.
|
||||
*/
|
||||
struct accesskit_ios_adapter *accesskit_ios_adapter_new(
|
||||
void *view, accesskit_activation_handler_callback activation_handler,
|
||||
void *activation_handler_userdata,
|
||||
accesskit_action_handler_callback action_handler,
|
||||
void *action_handler_userdata,
|
||||
accesskit_deactivation_handler_callback deactivation_handler,
|
||||
void *deactivation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
void accesskit_ios_adapter_free(struct accesskit_ios_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* You must call `accesskit_ios_queued_events_raise` on the returned pointer. It
|
||||
* can be null if the adapter is not active.
|
||||
*/
|
||||
struct accesskit_ios_queued_events *accesskit_ios_adapter_update_if_active(
|
||||
struct accesskit_ios_adapter *adapter,
|
||||
accesskit_tree_update_factory update_factory,
|
||||
void *update_factory_userdata);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Call this when the host view has just appeared on screen. If an
|
||||
* assistive technology is running, this proactively builds the
|
||||
* accessibility tree.
|
||||
*
|
||||
* You must call `accesskit_ios_queued_events_raise` on the returned pointer. It
|
||||
* can be null if the adapter is not active.
|
||||
*/
|
||||
struct accesskit_ios_queued_events *accesskit_ios_adapter_view_did_appear(
|
||||
struct accesskit_ios_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Returns whether the view itself is an accessibility element.
|
||||
* This corresponds to `isAccessibilityElement`.
|
||||
*/
|
||||
bool accesskit_ios_adapter_is_accessibility_element(
|
||||
struct accesskit_ios_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Returns a pointer to an `NSArray` of accessibility elements
|
||||
* contained in the view. Ownership of the pointer is not transferred.
|
||||
* This corresponds to `accessibilityElements`.
|
||||
*/
|
||||
void *accesskit_ios_adapter_accessibility_elements(
|
||||
struct accesskit_ios_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Returns a pointer to the accessibility element at the specified point,
|
||||
* or null if none. Ownership of the pointer is not transferred.
|
||||
* This corresponds to `accessibilityHitTest:`.
|
||||
*/
|
||||
void *accesskit_ios_adapter_hit_test(struct accesskit_ios_adapter *adapter,
|
||||
double x, double y);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* Caller must call `accesskit_string_free` with the return value.
|
||||
*/
|
||||
char *accesskit_ios_adapter_debug(const struct accesskit_ios_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* All handlers will always be called on the main thread.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* `view` must be a valid, unreleased pointer to a `UIView`.
|
||||
*/
|
||||
struct accesskit_ios_subclassing_adapter *accesskit_ios_subclassing_adapter_new(
|
||||
void *view, accesskit_activation_handler_callback activation_handler,
|
||||
void *activation_handler_userdata,
|
||||
accesskit_action_handler_callback action_handler,
|
||||
void *action_handler_userdata,
|
||||
accesskit_deactivation_handler_callback deactivation_handler,
|
||||
void *deactivation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* All handlers will always be called on the main thread.
|
||||
*
|
||||
* # Safety
|
||||
*
|
||||
* `window` must be a valid, unreleased pointer to a `UIWindow`.
|
||||
*
|
||||
* # Panics
|
||||
*
|
||||
* This function panics if the specified window doesn't currently have
|
||||
* a root view controller with a view.
|
||||
*/
|
||||
struct accesskit_ios_subclassing_adapter *
|
||||
accesskit_ios_subclassing_adapter_for_window(
|
||||
void *window, accesskit_activation_handler_callback activation_handler,
|
||||
void *activation_handler_userdata,
|
||||
accesskit_action_handler_callback action_handler,
|
||||
void *action_handler_userdata,
|
||||
accesskit_deactivation_handler_callback deactivation_handler,
|
||||
void *deactivation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
void accesskit_ios_subclassing_adapter_free(
|
||||
struct accesskit_ios_subclassing_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if (defined(ACCESSKIT_IOS) || defined(ACCESSKIT_TVOS) || \
|
||||
defined(ACCESSKIT_VISIONOS) || defined(ACCESSKIT_WATCHOS))
|
||||
/**
|
||||
* You must call `accesskit_ios_queued_events_raise` on the returned pointer. It
|
||||
* can be null if the adapter is not active.
|
||||
*/
|
||||
struct accesskit_ios_queued_events *
|
||||
accesskit_ios_subclassing_adapter_update_if_active(
|
||||
struct accesskit_ios_subclassing_adapter *adapter,
|
||||
accesskit_tree_update_factory update_factory,
|
||||
void *update_factory_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Memory is also freed when calling this function.
|
||||
*/
|
||||
@@ -2569,7 +2783,7 @@ void accesskit_macos_queued_events_raise(
|
||||
struct accesskit_macos_queued_events *events);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* # Safety
|
||||
*
|
||||
@@ -2581,11 +2795,11 @@ struct accesskit_macos_adapter *accesskit_macos_adapter_new(
|
||||
void *action_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
void accesskit_macos_adapter_free(struct accesskit_macos_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* You must call `accesskit_macos_queued_events_raise` on the returned pointer.
|
||||
* It can be null if the adapter is not active.
|
||||
@@ -2596,7 +2810,7 @@ struct accesskit_macos_queued_events *accesskit_macos_adapter_update_if_active(
|
||||
void *update_factory_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Update the tree state based on whether the window is focused.
|
||||
*
|
||||
@@ -2608,7 +2822,7 @@ accesskit_macos_adapter_update_view_focus_state(
|
||||
struct accesskit_macos_adapter *adapter, bool is_focused);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Returns a pointer to an `NSArray`. Ownership of the pointer is not
|
||||
* transferred.
|
||||
@@ -2619,7 +2833,7 @@ void *accesskit_macos_adapter_view_children(
|
||||
void *activation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Returns a pointer to an `NSObject`. Ownership of the pointer is not
|
||||
* transferred.
|
||||
@@ -2630,7 +2844,7 @@ void *accesskit_macos_adapter_focus(
|
||||
void *activation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Returns a pointer to an `NSObject`. Ownership of the pointer is not
|
||||
* transferred.
|
||||
@@ -2641,7 +2855,7 @@ void *accesskit_macos_adapter_hit_test(
|
||||
void *activation_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Caller must call `accesskit_string_free` with the return value.
|
||||
*/
|
||||
@@ -2649,7 +2863,7 @@ char *accesskit_macos_adapter_debug(
|
||||
const struct accesskit_macos_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* # Safety
|
||||
*
|
||||
@@ -2663,7 +2877,7 @@ accesskit_macos_subclassing_adapter_new(
|
||||
void *action_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* # Safety
|
||||
*
|
||||
@@ -2682,12 +2896,12 @@ accesskit_macos_subclassing_adapter_for_window(
|
||||
void *action_handler_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
void accesskit_macos_subclassing_adapter_free(
|
||||
struct accesskit_macos_subclassing_adapter *adapter);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* You must call `accesskit_macos_queued_events_raise` on the returned pointer.
|
||||
* It can be null if the adapter is not active.
|
||||
@@ -2699,7 +2913,7 @@ accesskit_macos_subclassing_adapter_update_if_active(
|
||||
void *update_factory_userdata);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Update the tree state based on whether the window is focused.
|
||||
*
|
||||
@@ -2711,7 +2925,7 @@ accesskit_macos_subclassing_adapter_update_view_focus_state(
|
||||
struct accesskit_macos_subclassing_adapter *adapter, bool is_focused);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Modifies the specified class, which must be a subclass of `NSWindow`,
|
||||
* to include an `accessibilityFocusedUIElement` method that calls
|
||||
@@ -2732,7 +2946,7 @@ void accesskit_macos_add_focus_forwarder_to_window_class(
|
||||
const char *class_name);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#if defined(ACCESSKIT_MACOS)
|
||||
/**
|
||||
* Modifies the specified class, which must be a subclass of `NSWindow`,
|
||||
* to include an `accessibilityFocusedUIElement` method that calls
|
||||
|
||||
Reference in new issue
Block a user