1/**************************************************************************/
2/* viewport.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "viewport.h"
32
33#include "core/config/project_settings.h"
34#include "core/debugger/engine_debugger.h"
35#include "core/object/message_queue.h"
36#include "core/string/translation.h"
37#include "core/templates/pair.h"
38#include "core/templates/sort_array.h"
39#include "scene/2d/audio_listener_2d.h"
40#include "scene/2d/camera_2d.h"
41#include "scene/2d/collision_object_2d.h"
42#ifndef _3D_DISABLED
43#include "scene/3d/audio_listener_3d.h"
44#include "scene/3d/camera_3d.h"
45#include "scene/3d/collision_object_3d.h"
46#include "scene/3d/world_environment.h"
47#endif // _3D_DISABLED
48#include "scene/gui/control.h"
49#include "scene/gui/label.h"
50#include "scene/gui/popup.h"
51#include "scene/gui/popup_menu.h"
52#include "scene/gui/subviewport_container.h"
53#include "scene/main/canvas_layer.h"
54#include "scene/main/window.h"
55#include "scene/resources/mesh.h"
56#include "scene/resources/text_line.h"
57#include "scene/resources/world_2d.h"
58#include "scene/scene_string_names.h"
59#include "servers/audio_server.h"
60#include "servers/rendering/rendering_server_globals.h"
61
62void ViewportTexture::setup_local_to_scene() {
63 // For the same target viewport, setup is only allowed once to prevent multiple free or multiple creations.
64 if (!vp_changed) {
65 return;
66 }
67
68 if (vp_pending) {
69 return;
70 }
71
72 Node *loc_scene = get_local_scene();
73 if (!loc_scene) {
74 return;
75 }
76
77 if (vp) {
78 vp->viewport_textures.erase(this);
79 vp = nullptr;
80 }
81
82 if (loc_scene->is_ready()) {
83 _setup_local_to_scene(loc_scene);
84 } else {
85 loc_scene->connect(SNAME("ready"), callable_mp(this, &ViewportTexture::_setup_local_to_scene).bind(loc_scene), CONNECT_ONE_SHOT);
86 vp_pending = true;
87 }
88}
89
90void ViewportTexture::reset_local_to_scene() {
91 vp_changed = true;
92
93 if (vp) {
94 vp->viewport_textures.erase(this);
95 vp = nullptr;
96 }
97
98 if (proxy.is_valid() && proxy_ph.is_null()) {
99 proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
100 RS::get_singleton()->texture_proxy_update(proxy, proxy_ph);
101 }
102}
103
104void ViewportTexture::set_viewport_path_in_scene(const NodePath &p_path) {
105 if (path == p_path) {
106 return;
107 }
108
109 path = p_path;
110
111 reset_local_to_scene();
112
113 if (get_local_scene() && !path.is_empty()) {
114 setup_local_to_scene();
115 } else {
116 emit_changed();
117 }
118}
119
120NodePath ViewportTexture::get_viewport_path_in_scene() const {
121 return path;
122}
123
124int ViewportTexture::get_width() const {
125 if (!vp) {
126 if (!vp_pending) {
127 ERR_PRINT("Viewport Texture must be set to use it.");
128 }
129 return 0;
130 }
131 return vp->size.width;
132}
133
134int ViewportTexture::get_height() const {
135 if (!vp) {
136 if (!vp_pending) {
137 ERR_PRINT("Viewport Texture must be set to use it.");
138 }
139 return 0;
140 }
141 return vp->size.height;
142}
143
144Size2 ViewportTexture::get_size() const {
145 if (!vp) {
146 if (!vp_pending) {
147 ERR_PRINT("Viewport Texture must be set to use it.");
148 }
149 return Size2();
150 }
151 return vp->size;
152}
153
154RID ViewportTexture::get_rid() const {
155 if (proxy.is_null()) {
156 proxy_ph = RS::get_singleton()->texture_2d_placeholder_create();
157 proxy = RS::get_singleton()->texture_proxy_create(proxy_ph);
158 }
159 return proxy;
160}
161
162bool ViewportTexture::has_alpha() const {
163 return false;
164}
165
166Ref<Image> ViewportTexture::get_image() const {
167 if (!vp) {
168 if (!vp_pending) {
169 ERR_PRINT("Viewport Texture must be set to use it.");
170 }
171 return Ref<Image>();
172 }
173 return RS::get_singleton()->texture_2d_get(vp->texture_rid);
174}
175
176void ViewportTexture::_setup_local_to_scene(const Node *p_loc_scene) {
177 // Always reset this, even if this call fails with an error.
178 vp_pending = false;
179
180 Node *vpn = p_loc_scene->get_node_or_null(path);
181 ERR_FAIL_NULL_MSG(vpn, "Path to node is invalid: '" + path + "'.");
182 vp = Object::cast_to<Viewport>(vpn);
183 ERR_FAIL_NULL_MSG(vp, "Path to node does not point to a viewport: '" + path + "'.");
184
185 vp->viewport_textures.insert(this);
186
187 ERR_FAIL_NULL(RenderingServer::get_singleton());
188 if (proxy_ph.is_valid()) {
189 RS::get_singleton()->texture_proxy_update(proxy, vp->texture_rid);
190 RS::get_singleton()->free(proxy_ph);
191 proxy_ph = RID();
192 } else {
193 ERR_FAIL_COND(proxy.is_valid()); // Should be invalid.
194 proxy = RS::get_singleton()->texture_proxy_create(vp->texture_rid);
195 }
196 vp_changed = false;
197
198 emit_changed();
199}
200
201void ViewportTexture::_bind_methods() {
202 ClassDB::bind_method(D_METHOD("set_viewport_path_in_scene", "path"), &ViewportTexture::set_viewport_path_in_scene);
203 ClassDB::bind_method(D_METHOD("get_viewport_path_in_scene"), &ViewportTexture::get_viewport_path_in_scene);
204
205 ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "viewport_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Viewport", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NODE_PATH_FROM_SCENE_ROOT), "set_viewport_path_in_scene", "get_viewport_path_in_scene");
206}
207
208ViewportTexture::ViewportTexture() {
209 set_local_to_scene(true);
210}
211
212ViewportTexture::~ViewportTexture() {
213 if (vp) {
214 vp->viewport_textures.erase(this);
215 }
216
217 ERR_FAIL_NULL(RenderingServer::get_singleton());
218
219 if (proxy_ph.is_valid()) {
220 RS::get_singleton()->free(proxy_ph);
221 }
222 if (proxy.is_valid()) {
223 RS::get_singleton()->free(proxy);
224 }
225}
226
227void Viewport::_process_dirty_canvas_parent_orders() {
228 for (const ObjectID &id : gui.canvas_parents_with_dirty_order) {
229 Object *obj = ObjectDB::get_instance(id);
230 if (!obj) {
231 continue; // May have been deleted.
232 }
233
234 Node *n = static_cast<Node *>(obj);
235 for (int i = 0; i < n->get_child_count(); i++) {
236 Node *c = n->get_child(i);
237 CanvasItem *ci = Object::cast_to<CanvasItem>(c);
238 if (ci) {
239 ci->update_draw_order();
240 continue;
241 }
242 CanvasLayer *cl = Object::cast_to<CanvasLayer>(c);
243 if (cl) {
244 cl->update_draw_order();
245 }
246 }
247 }
248
249 gui.canvas_parents_with_dirty_order.clear();
250}
251
252void Viewport::_sub_window_update_order() {
253 if (gui.sub_windows.size() < 2) {
254 return;
255 }
256
257 if (!gui.sub_windows[gui.sub_windows.size() - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
258 int index = gui.sub_windows.size() - 1;
259
260 while (index > 0 && gui.sub_windows[index - 1].window->get_flag(Window::FLAG_ALWAYS_ON_TOP)) {
261 --index;
262 }
263
264 if (index != (gui.sub_windows.size() - 1)) {
265 SubWindow sw = gui.sub_windows[gui.sub_windows.size() - 1];
266 gui.sub_windows.remove_at(gui.sub_windows.size() - 1);
267 gui.sub_windows.insert(index, sw);
268 }
269 }
270
271 for (int i = 0; i < gui.sub_windows.size(); i++) {
272 RS::get_singleton()->canvas_item_set_draw_index(gui.sub_windows[i].canvas_item, i);
273 }
274}
275
276void Viewport::_sub_window_register(Window *p_window) {
277 ERR_FAIL_COND(!is_inside_tree());
278 for (int i = 0; i < gui.sub_windows.size(); i++) {
279 ERR_FAIL_COND(gui.sub_windows[i].window == p_window);
280 }
281
282 if (gui.sub_windows.size() == 0) {
283 subwindow_canvas = RS::get_singleton()->canvas_create();
284 RS::get_singleton()->viewport_attach_canvas(viewport, subwindow_canvas);
285 RS::get_singleton()->viewport_set_canvas_stacking(viewport, subwindow_canvas, SUBWINDOW_CANVAS_LAYER, 0);
286 }
287 SubWindow sw;
288 sw.canvas_item = RS::get_singleton()->canvas_item_create();
289 RS::get_singleton()->canvas_item_set_parent(sw.canvas_item, subwindow_canvas);
290 sw.window = p_window;
291 gui.sub_windows.push_back(sw);
292
293 if (gui.subwindow_drag == SUB_WINDOW_DRAG_DISABLED) {
294 if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
295 _sub_window_update_order();
296 } else {
297 _sub_window_grab_focus(p_window);
298 }
299 } else {
300 int index = _sub_window_find(gui.currently_dragged_subwindow);
301 sw = gui.sub_windows[index];
302 gui.sub_windows.remove_at(index);
303 gui.sub_windows.push_back(sw);
304 _sub_window_update_order();
305 }
306
307 RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, viewport);
308}
309
310void Viewport::_sub_window_update(Window *p_window) {
311 int index = _sub_window_find(p_window);
312 ERR_FAIL_COND(index == -1);
313
314 const SubWindow &sw = gui.sub_windows[index];
315
316 Transform2D pos;
317 pos.set_origin(p_window->get_position());
318 RS::get_singleton()->canvas_item_clear(sw.canvas_item);
319 Rect2i r = Rect2i(p_window->get_position(), sw.window->get_size());
320
321 if (!p_window->get_flag(Window::FLAG_BORDERLESS)) {
322 Ref<StyleBox> panel = gui.subwindow_focused == p_window ? p_window->theme_cache.embedded_border : p_window->theme_cache.embedded_unfocused_border;
323 panel->draw(sw.canvas_item, r);
324
325 // Draw the title bar text.
326 Ref<Font> title_font = p_window->theme_cache.title_font;
327 int font_size = p_window->theme_cache.title_font_size;
328 Color title_color = p_window->theme_cache.title_color;
329 int title_height = p_window->theme_cache.title_height;
330 int close_h_ofs = p_window->theme_cache.close_h_offset;
331 int close_v_ofs = p_window->theme_cache.close_v_offset;
332
333 TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size);
334 title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs);
335 title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
336 int x = (r.size.width - title_text.get_size().x) / 2;
337 int y = (-title_height - title_text.get_size().y) / 2;
338
339 Color font_outline_color = p_window->theme_cache.title_outline_modulate;
340 int outline_size = p_window->theme_cache.title_outline_size;
341 if (outline_size > 0 && font_outline_color.a > 0) {
342 title_text.draw_outline(sw.canvas_item, r.position + Point2(x, y), outline_size, font_outline_color);
343 }
344 title_text.draw(sw.canvas_item, r.position + Point2(x, y), title_color);
345
346 bool pressed = gui.subwindow_focused == sw.window && gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE && gui.subwindow_drag_close_inside;
347 Ref<Texture2D> close_icon = pressed ? p_window->theme_cache.close_pressed : p_window->theme_cache.close;
348 close_icon->draw(sw.canvas_item, r.position + Vector2(r.size.width - close_h_ofs, -close_v_ofs));
349 }
350
351 RS::get_singleton()->canvas_item_add_texture_rect(sw.canvas_item, r, sw.window->get_texture()->get_rid());
352}
353
354void Viewport::_sub_window_grab_focus(Window *p_window) {
355 if (p_window == nullptr) {
356 // Release current focus.
357 if (gui.subwindow_focused) {
358 gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
359 gui.subwindow_focused = nullptr;
360 gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
361 }
362
363 Window *this_window = Object::cast_to<Window>(this);
364 if (this_window) {
365 this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
366 }
367
368 return;
369 }
370
371 // The index needs to be update before every usage in case an event callback changed the window list.
372 int index = _sub_window_find(p_window);
373 ERR_FAIL_COND(index == -1);
374
375 if (p_window->get_flag(Window::FLAG_NO_FOCUS)) {
376 // Release current focus.
377 if (gui.subwindow_focused) {
378 gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
379 gui.subwindow_focused = nullptr;
380 gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
381 }
382 // Can only move to foreground, but no focus granted.
383 index = _sub_window_find(p_window);
384 ERR_FAIL_COND(index == -1);
385 SubWindow sw = gui.sub_windows[index];
386 gui.sub_windows.remove_at(index);
387 gui.sub_windows.push_back(sw);
388 _sub_window_update_order();
389 return;
390 }
391
392 if (gui.subwindow_focused) {
393 if (gui.subwindow_focused == p_window) {
394 return; // Nothing to do.
395 }
396 gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
397 gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
398 } else {
399 Window *this_window = Object::cast_to<Window>(this);
400 if (this_window) {
401 this_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
402 }
403 }
404
405 Window *old_focus = gui.subwindow_focused;
406
407 gui.subwindow_focused = p_window;
408
409 gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
410
411 { // Move to foreground.
412 index = _sub_window_find(p_window);
413 ERR_FAIL_COND(index == -1);
414 SubWindow sw = gui.sub_windows[index];
415 gui.sub_windows.remove_at(index);
416 gui.sub_windows.push_back(sw);
417 index = gui.sub_windows.size() - 1;
418 _sub_window_update_order();
419 }
420
421 if (old_focus) {
422 _sub_window_update(old_focus);
423 }
424
425 _sub_window_update(p_window);
426}
427
428void Viewport::_sub_window_remove(Window *p_window) {
429 int index = _sub_window_find(p_window);
430 ERR_FAIL_COND(index == -1);
431
432 ERR_FAIL_NULL(RenderingServer::get_singleton());
433
434 SubWindow sw = gui.sub_windows[index];
435 if (gui.subwindow_over == sw.window) {
436 sw.window->_mouse_leave_viewport();
437 gui.subwindow_over = nullptr;
438 }
439 RS::get_singleton()->free(sw.canvas_item);
440 gui.sub_windows.remove_at(index);
441
442 if (gui.sub_windows.size() == 0) {
443 RS::get_singleton()->free(subwindow_canvas);
444 subwindow_canvas = RID();
445 }
446
447 if (gui.currently_dragged_subwindow == p_window) {
448 gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
449 gui.currently_dragged_subwindow = nullptr;
450 }
451
452 if (gui.subwindow_focused == p_window) {
453 Window *new_focused_window;
454 Window *parent_visible = p_window->get_parent_visible_window();
455
456 gui.subwindow_focused->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_OUT);
457
458 if (parent_visible) {
459 new_focused_window = parent_visible;
460 } else {
461 new_focused_window = Object::cast_to<Window>(this);
462 }
463
464 if (new_focused_window) {
465 int new_focused_index = _sub_window_find(new_focused_window);
466 if (new_focused_index != -1) {
467 gui.subwindow_focused = new_focused_window;
468 } else {
469 gui.subwindow_focused = nullptr;
470 }
471
472 new_focused_window->_event_callback(DisplayServer::WINDOW_EVENT_FOCUS_IN);
473 } else {
474 gui.subwindow_focused = nullptr;
475 }
476 }
477
478 RenderingServer::get_singleton()->viewport_set_parent_viewport(p_window->viewport, p_window->parent ? p_window->parent->viewport : RID());
479}
480
481int Viewport::_sub_window_find(Window *p_window) const {
482 for (int i = 0; i < gui.sub_windows.size(); i++) {
483 if (gui.sub_windows[i].window == p_window) {
484 return i;
485 }
486 }
487
488 return -1;
489}
490
491void Viewport::_update_viewport_path() {
492 if (viewport_textures.is_empty()) {
493 return;
494 }
495
496 Node *scene_root = get_scene_file_path().is_empty() ? get_owner() : this;
497 if (!scene_root && is_inside_tree()) {
498 scene_root = get_tree()->get_edited_scene_root();
499 }
500 if (scene_root && (scene_root == this || scene_root->is_ancestor_of(this))) {
501 NodePath path_in_scene = scene_root->get_path_to(this);
502 for (ViewportTexture *E : viewport_textures) {
503 E->path = path_in_scene;
504 }
505 }
506}
507
508void Viewport::_notification(int p_what) {
509 ERR_MAIN_THREAD_GUARD;
510
511 switch (p_what) {
512 case NOTIFICATION_ENTER_TREE: {
513 _update_viewport_path();
514
515 if (get_parent()) {
516 parent = get_parent()->get_viewport();
517 RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, parent->get_viewport_rid());
518 } else {
519 parent = nullptr;
520 }
521
522 current_canvas = find_world_2d()->get_canvas();
523 RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
524 RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, current_canvas, canvas_transform);
525 RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
526 _update_audio_listener_2d();
527#ifndef _3D_DISABLED
528 RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
529 _update_audio_listener_3d();
530#endif // _3D_DISABLED
531
532 add_to_group("_viewports");
533 if (get_tree()->is_debugging_collisions_hint()) {
534 PhysicsServer2D::get_singleton()->space_set_debug_contacts(find_world_2d()->get_space(), get_tree()->get_collision_debug_contact_count());
535 contact_2d_debug = RenderingServer::get_singleton()->canvas_item_create();
536 RenderingServer::get_singleton()->canvas_item_set_parent(contact_2d_debug, current_canvas);
537#ifndef _3D_DISABLED
538 PhysicsServer3D::get_singleton()->space_set_debug_contacts(find_world_3d()->get_space(), get_tree()->get_collision_debug_contact_count());
539 contact_3d_debug_multimesh = RenderingServer::get_singleton()->multimesh_create();
540 RenderingServer::get_singleton()->multimesh_allocate_data(contact_3d_debug_multimesh, get_tree()->get_collision_debug_contact_count(), RS::MULTIMESH_TRANSFORM_3D, false);
541 RenderingServer::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, 0);
542 RenderingServer::get_singleton()->multimesh_set_mesh(contact_3d_debug_multimesh, get_tree()->get_debug_contact_mesh()->get_rid());
543 contact_3d_debug_instance = RenderingServer::get_singleton()->instance_create();
544 RenderingServer::get_singleton()->instance_set_base(contact_3d_debug_instance, contact_3d_debug_multimesh);
545 RenderingServer::get_singleton()->instance_set_scenario(contact_3d_debug_instance, find_world_3d()->get_scenario());
546 RenderingServer::get_singleton()->instance_geometry_set_flag(contact_3d_debug_instance, RS::INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE, true);
547#endif // _3D_DISABLED
548 set_physics_process_internal(true);
549 }
550 } break;
551
552 case NOTIFICATION_READY: {
553#ifndef _3D_DISABLED
554 if (audio_listener_3d_set.size() && !audio_listener_3d) {
555 AudioListener3D *first = nullptr;
556 for (AudioListener3D *E : audio_listener_3d_set) {
557 if (first == nullptr || first->is_greater_than(E)) {
558 first = E;
559 }
560 }
561
562 if (first) {
563 first->make_current();
564 }
565 }
566
567 if (camera_3d_set.size() && !camera_3d) {
568 // There are cameras but no current camera, pick first in tree and make it current.
569 Camera3D *first = nullptr;
570 for (Camera3D *E : camera_3d_set) {
571 if (first == nullptr || first->is_greater_than(E)) {
572 first = E;
573 }
574 }
575
576 if (first) {
577 first->make_current();
578 }
579 }
580#endif // _3D_DISABLED
581 } break;
582
583 case NOTIFICATION_EXIT_TREE: {
584 _gui_cancel_tooltip();
585
586 RenderingServer::get_singleton()->viewport_set_scenario(viewport, RID());
587 RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
588 if (contact_2d_debug.is_valid()) {
589 RenderingServer::get_singleton()->free(contact_2d_debug);
590 contact_2d_debug = RID();
591 }
592
593 if (contact_3d_debug_multimesh.is_valid()) {
594 RenderingServer::get_singleton()->free(contact_3d_debug_multimesh);
595 RenderingServer::get_singleton()->free(contact_3d_debug_instance);
596 contact_3d_debug_instance = RID();
597 contact_3d_debug_multimesh = RID();
598 }
599
600 remove_from_group("_viewports");
601 set_physics_process_internal(false);
602
603 RS::get_singleton()->viewport_set_active(viewport, false);
604 RenderingServer::get_singleton()->viewport_set_parent_viewport(viewport, RID());
605 } break;
606
607 case NOTIFICATION_PATH_RENAMED: {
608 _update_viewport_path();
609 } break;
610
611 case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
612 if (!get_tree()) {
613 return;
614 }
615
616 if (get_tree()->is_debugging_collisions_hint() && contact_2d_debug.is_valid()) {
617 RenderingServer::get_singleton()->canvas_item_clear(contact_2d_debug);
618 RenderingServer::get_singleton()->canvas_item_set_draw_index(contact_2d_debug, 0xFFFFF); //very high index
619
620 Vector<Vector2> points = PhysicsServer2D::get_singleton()->space_get_contacts(find_world_2d()->get_space());
621 int point_count = PhysicsServer2D::get_singleton()->space_get_contact_count(find_world_2d()->get_space());
622 Color ccol = get_tree()->get_debug_collision_contact_color();
623
624 for (int i = 0; i < point_count; i++) {
625 RenderingServer::get_singleton()->canvas_item_add_rect(contact_2d_debug, Rect2(points[i] - Vector2(2, 2), Vector2(5, 5)), ccol);
626 }
627 }
628#ifndef _3D_DISABLED
629 if (get_tree()->is_debugging_collisions_hint() && contact_3d_debug_multimesh.is_valid()) {
630 Vector<Vector3> points = PhysicsServer3D::get_singleton()->space_get_contacts(find_world_3d()->get_space());
631 int point_count = PhysicsServer3D::get_singleton()->space_get_contact_count(find_world_3d()->get_space());
632
633 RS::get_singleton()->multimesh_set_visible_instances(contact_3d_debug_multimesh, point_count);
634
635 for (int i = 0; i < point_count; i++) {
636 Transform3D point_transform;
637 point_transform.origin = points[i];
638 RS::get_singleton()->multimesh_instance_set_transform(contact_3d_debug_multimesh, i, point_transform);
639 }
640 }
641#endif // _3D_DISABLED
642 } break;
643
644 case NOTIFICATION_VP_MOUSE_ENTER: {
645 gui.mouse_in_viewport = true;
646 } break;
647
648 case NOTIFICATION_VP_MOUSE_EXIT: {
649 gui.mouse_in_viewport = false;
650 _drop_physics_mouseover();
651 // When the mouse exits the viewport, we don't want to end
652 // mouse_focus, because, for example, we want to continue
653 // dragging a scrollbar even if the mouse has left the viewport.
654 } break;
655
656 case NOTIFICATION_WM_WINDOW_FOCUS_OUT: {
657 _gui_cancel_tooltip();
658 _drop_physics_mouseover();
659 if (gui.mouse_focus && !gui.forced_mouse_focus) {
660 _drop_mouse_focus();
661 }
662 // When the window focus changes, we want to end mouse_focus, but
663 // not the mouse_over. Note: The OS will trigger a separate mouse
664 // exit event if the change in focus results in the mouse exiting
665 // the window.
666 } break;
667
668 case NOTIFICATION_PREDELETE: {
669 if (gui_parent) {
670 gui_parent->gui.tooltip_popup = nullptr;
671 gui_parent->gui.tooltip_label = nullptr;
672 }
673 } break;
674 }
675}
676
677void Viewport::_process_picking() {
678 if (!is_inside_tree()) {
679 return;
680 }
681 if (!physics_object_picking) {
682 return;
683 }
684 if (Object::cast_to<Window>(this) && Input::get_singleton()->get_mouse_mode() == Input::MOUSE_MODE_CAPTURED) {
685 return;
686 }
687 if (!gui.mouse_in_viewport) {
688 // Clear picking events if mouse has left viewport.
689 physics_picking_events.clear();
690 return;
691 }
692
693 _drop_physics_mouseover(true);
694
695#ifndef _3D_DISABLED
696 Vector2 last_pos(1e20, 1e20);
697 CollisionObject3D *last_object = nullptr;
698 ObjectID last_id;
699 PhysicsDirectSpaceState3D::RayResult result;
700#endif // _3D_DISABLED
701
702 PhysicsDirectSpaceState2D *ss2d = PhysicsServer2D::get_singleton()->space_get_direct_state(find_world_2d()->get_space());
703
704 SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
705 bool parent_ignore_mouse = (parent_svc && parent_svc->get_mouse_filter() == Control::MOUSE_FILTER_IGNORE);
706 bool create_passive_hover_event = true;
707 if (gui.mouse_over || parent_ignore_mouse) {
708 // When the mouse is over a Control node, passive hovering would cause input events for Colliders, that are behind Control nodes.
709 // When parent SubViewportContainer ignores mouse, that setting should be respected.
710 create_passive_hover_event = false;
711 } else {
712 for (const Ref<InputEvent> &e : physics_picking_events) {
713 Ref<InputEventMouse> m = e;
714 if (m.is_valid()) {
715 // A mouse event exists, so passive hovering isn't necessary.
716 create_passive_hover_event = false;
717 break;
718 }
719 }
720 }
721
722 if (create_passive_hover_event) {
723 // Create a mouse motion event. This is necessary because objects or camera may have moved.
724 // While this extra event is sent, it is checked if both camera and last object and last ID did not move.
725 // If nothing changed, the event is discarded to avoid flooding with unnecessary motion events every frame.
726 Ref<InputEventMouseMotion> mm;
727 mm.instantiate();
728
729 mm->set_device(InputEvent::DEVICE_ID_INTERNAL);
730 mm->set_position(get_mouse_position());
731 mm->set_global_position(mm->get_position());
732 mm->set_alt_pressed(Input::get_singleton()->is_key_pressed(Key::ALT));
733 mm->set_shift_pressed(Input::get_singleton()->is_key_pressed(Key::SHIFT));
734 mm->set_ctrl_pressed(Input::get_singleton()->is_key_pressed(Key::CTRL));
735 mm->set_meta_pressed(Input::get_singleton()->is_key_pressed(Key::META));
736 mm->set_button_mask(Input::get_singleton()->get_mouse_button_mask());
737 physics_picking_events.push_back(mm);
738 }
739
740 while (physics_picking_events.size()) {
741 local_input_handled = false;
742 Ref<InputEvent> ev = physics_picking_events.front()->get();
743 physics_picking_events.pop_front();
744
745 Vector2 pos;
746 bool is_mouse = false;
747
748 Ref<InputEventMouseMotion> mm = ev;
749
750 if (mm.is_valid()) {
751 pos = mm->get_position();
752 is_mouse = true;
753 }
754
755 Ref<InputEventMouseButton> mb = ev;
756
757 if (mb.is_valid()) {
758 pos = mb->get_position();
759 is_mouse = true;
760 }
761
762 Ref<InputEventScreenDrag> sd = ev;
763
764 if (sd.is_valid()) {
765 pos = sd->get_position();
766 }
767
768 Ref<InputEventScreenTouch> st = ev;
769
770 if (st.is_valid()) {
771 pos = st->get_position();
772 }
773
774 if (ss2d) {
775 // Send to 2D.
776
777 uint64_t frame = get_tree()->get_frame();
778
779 PhysicsDirectSpaceState2D::ShapeResult res[64];
780 for (const CanvasLayer *E : canvas_layers) {
781 Transform2D canvas_layer_transform;
782 ObjectID canvas_layer_id;
783 if (E) {
784 // A descendant CanvasLayer.
785 canvas_layer_transform = E->get_final_transform();
786 canvas_layer_id = E->get_instance_id();
787 } else {
788 // This Viewport's builtin canvas.
789 canvas_layer_transform = get_canvas_transform();
790 canvas_layer_id = ObjectID();
791 }
792
793 Vector2 point = canvas_layer_transform.affine_inverse().xform(pos);
794
795 PhysicsDirectSpaceState2D::PointParameters point_params;
796 point_params.position = point;
797 point_params.canvas_instance_id = canvas_layer_id;
798 point_params.collide_with_areas = true;
799 point_params.pick_point = true;
800
801 int rc = ss2d->intersect_point(point_params, res, 64);
802 if (physics_object_picking_sort) {
803 struct ComparatorCollisionObjects {
804 bool operator()(const PhysicsDirectSpaceState2D::ShapeResult &p_a, const PhysicsDirectSpaceState2D::ShapeResult &p_b) const {
805 CollisionObject2D *a = Object::cast_to<CollisionObject2D>(p_a.collider);
806 CollisionObject2D *b = Object::cast_to<CollisionObject2D>(p_b.collider);
807 if (!a || !b) {
808 return false;
809 }
810 int za = a->get_effective_z_index();
811 int zb = b->get_effective_z_index();
812 if (za != zb) {
813 return zb < za;
814 }
815 return a->is_greater_than(b);
816 }
817 };
818 SortArray<PhysicsDirectSpaceState2D::ShapeResult, ComparatorCollisionObjects> sorter;
819 sorter.sort(res, rc);
820 }
821 for (int i = 0; i < rc; i++) {
822 if (res[i].collider_id.is_valid() && res[i].collider) {
823 CollisionObject2D *co = Object::cast_to<CollisionObject2D>(res[i].collider);
824 if (co && co->can_process()) {
825 bool send_event = true;
826 if (is_mouse) {
827 HashMap<ObjectID, uint64_t>::Iterator F = physics_2d_mouseover.find(res[i].collider_id);
828 if (!F) {
829 physics_2d_mouseover.insert(res[i].collider_id, frame);
830 co->_mouse_enter();
831 } else {
832 F->value = frame;
833 // It was already hovered, so don't send the event if it's faked.
834 if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
835 send_event = false;
836 }
837 }
838 HashMap<Pair<ObjectID, int>, uint64_t, PairHash<ObjectID, int>>::Iterator SF = physics_2d_shape_mouseover.find(Pair(res[i].collider_id, res[i].shape));
839 if (!SF) {
840 physics_2d_shape_mouseover.insert(Pair(res[i].collider_id, res[i].shape), frame);
841 co->_mouse_shape_enter(res[i].shape);
842 } else {
843 SF->value = frame;
844 }
845 }
846
847 if (send_event) {
848 co->_input_event_call(this, ev, res[i].shape);
849 }
850 }
851 }
852 }
853 }
854
855 if (is_mouse) {
856 _cleanup_mouseover_colliders(false, false, frame);
857 }
858 }
859
860#ifndef _3D_DISABLED
861 CollisionObject3D *capture_object = nullptr;
862 if (physics_object_capture.is_valid()) {
863 capture_object = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_capture));
864 if (!capture_object || !camera_3d || (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && !mb->is_pressed())) {
865 physics_object_capture = ObjectID();
866 } else {
867 last_id = physics_object_capture;
868 last_object = capture_object;
869 }
870 }
871
872 if (pos == last_pos) {
873 if (last_id.is_valid()) {
874 if (ObjectDB::get_instance(last_id) && last_object) {
875 // Good, exists.
876 _collision_object_3d_input_event(last_object, camera_3d, ev, result.position, result.normal, result.shape);
877 if (last_object->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
878 physics_object_capture = last_id;
879 }
880 }
881 }
882 } else {
883 if (camera_3d) {
884 Vector3 from = camera_3d->project_ray_origin(pos);
885 Vector3 dir = camera_3d->project_ray_normal(pos);
886 real_t far = camera_3d->far;
887
888 PhysicsDirectSpaceState3D *space = PhysicsServer3D::get_singleton()->space_get_direct_state(find_world_3d()->get_space());
889 if (space) {
890 PhysicsDirectSpaceState3D::RayParameters ray_params;
891 ray_params.from = from;
892 ray_params.to = from + dir * far;
893 ray_params.collide_with_areas = true;
894 ray_params.pick_ray = true;
895
896 bool col = space->intersect_ray(ray_params, result);
897 ObjectID new_collider;
898 CollisionObject3D *co = col ? Object::cast_to<CollisionObject3D>(result.collider) : nullptr;
899 if (co && co->can_process()) {
900 new_collider = result.collider_id;
901 if (!capture_object) {
902 last_object = co;
903 last_id = result.collider_id;
904 if (co->get_capture_input_on_drag() && mb.is_valid() && mb->get_button_index() == MouseButton::LEFT && mb->is_pressed()) {
905 physics_object_capture = last_id;
906 }
907 }
908 }
909
910 if (is_mouse && new_collider != physics_object_over) {
911 if (physics_object_over.is_valid()) {
912 CollisionObject3D *previous_co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
913 if (previous_co) {
914 previous_co->_mouse_exit();
915 }
916 }
917
918 if (new_collider.is_valid()) {
919 DEV_ASSERT(co);
920 co->_mouse_enter();
921 }
922
923 physics_object_over = new_collider;
924 }
925 if (capture_object) {
926 _collision_object_3d_input_event(capture_object, camera_3d, ev, result.position, result.normal, result.shape);
927 } else if (new_collider.is_valid()) {
928 _collision_object_3d_input_event(co, camera_3d, ev, result.position, result.normal, result.shape);
929 }
930 }
931
932 last_pos = pos;
933 }
934 }
935#endif // _3D_DISABLED
936 }
937}
938
939RID Viewport::get_viewport_rid() const {
940 ERR_READ_THREAD_GUARD_V(RID());
941 return viewport;
942}
943
944void Viewport::update_canvas_items() {
945 ERR_MAIN_THREAD_GUARD;
946 if (!is_inside_tree()) {
947 return;
948 }
949
950 _update_canvas_items(this);
951}
952
953void Viewport::_set_size(const Size2i &p_size, const Size2i &p_size_2d_override, bool p_allocated) {
954 Transform2D stretch_transform_new = Transform2D();
955 if (is_size_2d_override_stretch_enabled() && p_size_2d_override.width > 0 && p_size_2d_override.height > 0) {
956 Size2 scale = Size2(p_size) / Size2(p_size_2d_override);
957 stretch_transform_new.scale(scale);
958 }
959
960 Size2i new_size = p_size.max(Size2i(2, 2));
961 if (size == new_size && size_allocated == p_allocated && stretch_transform == stretch_transform_new && p_size_2d_override == size_2d_override) {
962 return;
963 }
964
965 size = new_size;
966 size_allocated = p_allocated;
967 size_2d_override = p_size_2d_override;
968 stretch_transform = stretch_transform_new;
969
970#ifndef _3D_DISABLED
971 if (!use_xr) {
972#endif
973
974 if (p_allocated) {
975 RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
976 } else {
977 RS::get_singleton()->viewport_set_size(viewport, 0, 0);
978 }
979
980#ifndef _3D_DISABLED
981 } // if (!use_xr)
982#endif
983
984 _update_global_transform();
985 update_configuration_warnings();
986
987 update_canvas_items();
988
989 for (ViewportTexture *E : viewport_textures) {
990 E->emit_changed();
991 }
992
993 emit_signal(SNAME("size_changed"));
994
995 Rect2i limit = get_visible_rect();
996 for (int i = 0; i < gui.sub_windows.size(); ++i) {
997 Window *sw = gui.sub_windows[i].window;
998 Rect2i rect = Rect2i(sw->position, sw->size);
999 Rect2i new_rect = sw->fit_rect_in_parent(rect, limit);
1000 if (new_rect != rect) {
1001 sw->set_position(new_rect.position);
1002 sw->set_size(new_rect.size);
1003 }
1004 }
1005}
1006
1007Size2i Viewport::_get_size() const {
1008#ifndef _3D_DISABLED
1009 if (use_xr) {
1010 if (XRServer::get_singleton() != nullptr) {
1011 Ref<XRInterface> xr_interface = XRServer::get_singleton()->get_primary_interface();
1012 if (xr_interface.is_valid() && xr_interface->is_initialized()) {
1013 Size2 xr_size = xr_interface->get_render_target_size();
1014 return (Size2i)xr_size;
1015 }
1016 }
1017 return Size2i();
1018 }
1019#endif // _3D_DISABLED
1020
1021 return size;
1022}
1023
1024Size2i Viewport::_get_size_2d_override() const {
1025 return size_2d_override;
1026}
1027
1028bool Viewport::_is_size_allocated() const {
1029 return size_allocated;
1030}
1031
1032Rect2 Viewport::get_visible_rect() const {
1033 ERR_READ_THREAD_GUARD_V(Rect2());
1034 Rect2 r;
1035
1036 if (size == Size2()) {
1037 r = Rect2(Point2(), DisplayServer::get_singleton()->window_get_size());
1038 } else {
1039 r = Rect2(Point2(), size);
1040 }
1041
1042 if (size_2d_override != Size2i()) {
1043 r.size = size_2d_override;
1044 }
1045
1046 return r;
1047}
1048
1049void Viewport::canvas_parent_mark_dirty(Node *p_node) {
1050 ERR_MAIN_THREAD_GUARD;
1051 bool request_update = gui.canvas_parents_with_dirty_order.is_empty();
1052 gui.canvas_parents_with_dirty_order.insert(p_node->get_instance_id());
1053 if (request_update) {
1054 MessageQueue::get_singleton()->push_callable(callable_mp(this, &Viewport::_process_dirty_canvas_parent_orders));
1055 }
1056}
1057
1058void Viewport::_update_audio_listener_2d() {
1059 if (AudioServer::get_singleton()) {
1060 AudioServer::get_singleton()->notify_listener_changed();
1061 }
1062}
1063
1064void Viewport::set_as_audio_listener_2d(bool p_enable) {
1065 ERR_MAIN_THREAD_GUARD;
1066 if (p_enable == is_audio_listener_2d_enabled) {
1067 return;
1068 }
1069
1070 is_audio_listener_2d_enabled = p_enable;
1071 _update_audio_listener_2d();
1072}
1073
1074bool Viewport::is_audio_listener_2d() const {
1075 ERR_READ_THREAD_GUARD_V(false);
1076 return is_audio_listener_2d_enabled;
1077}
1078
1079AudioListener2D *Viewport::get_audio_listener_2d() const {
1080 ERR_READ_THREAD_GUARD_V(nullptr);
1081 return audio_listener_2d;
1082}
1083
1084void Viewport::enable_canvas_transform_override(bool p_enable) {
1085 ERR_MAIN_THREAD_GUARD;
1086 if (override_canvas_transform == p_enable) {
1087 return;
1088 }
1089
1090 override_canvas_transform = p_enable;
1091 if (p_enable) {
1092 RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
1093 } else {
1094 RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
1095 }
1096}
1097
1098bool Viewport::is_canvas_transform_override_enabled() const {
1099 ERR_READ_THREAD_GUARD_V(false);
1100 return override_canvas_transform;
1101}
1102
1103void Viewport::set_canvas_transform_override(const Transform2D &p_transform) {
1104 ERR_MAIN_THREAD_GUARD;
1105 if (canvas_transform_override == p_transform) {
1106 return;
1107 }
1108
1109 canvas_transform_override = p_transform;
1110 if (override_canvas_transform) {
1111 RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform_override);
1112 }
1113}
1114
1115Transform2D Viewport::get_canvas_transform_override() const {
1116 ERR_READ_THREAD_GUARD_V(Transform2D());
1117 return canvas_transform_override;
1118}
1119
1120void Viewport::set_canvas_transform(const Transform2D &p_transform) {
1121 ERR_MAIN_THREAD_GUARD;
1122 canvas_transform = p_transform;
1123
1124 if (!override_canvas_transform) {
1125 RenderingServer::get_singleton()->viewport_set_canvas_transform(viewport, find_world_2d()->get_canvas(), canvas_transform);
1126 }
1127}
1128
1129Transform2D Viewport::get_canvas_transform() const {
1130 ERR_READ_THREAD_GUARD_V(Transform2D());
1131 return canvas_transform;
1132}
1133
1134void Viewport::_update_global_transform() {
1135 Transform2D sxform = stretch_transform * global_canvas_transform;
1136
1137 RenderingServer::get_singleton()->viewport_set_global_canvas_transform(viewport, sxform);
1138}
1139
1140void Viewport::set_global_canvas_transform(const Transform2D &p_transform) {
1141 ERR_MAIN_THREAD_GUARD;
1142 global_canvas_transform = p_transform;
1143
1144 _update_global_transform();
1145}
1146
1147Transform2D Viewport::get_global_canvas_transform() const {
1148 ERR_READ_THREAD_GUARD_V(Transform2D());
1149 return global_canvas_transform;
1150}
1151
1152void Viewport::_camera_2d_set(Camera2D *p_camera_2d) {
1153 camera_2d = p_camera_2d;
1154}
1155
1156void Viewport::_audio_listener_2d_set(AudioListener2D *p_listener) {
1157 if (audio_listener_2d == p_listener) {
1158 return;
1159 } else if (audio_listener_2d) {
1160 audio_listener_2d->clear_current();
1161 }
1162 audio_listener_2d = p_listener;
1163}
1164
1165void Viewport::_audio_listener_2d_remove(AudioListener2D *p_listener) {
1166 if (audio_listener_2d == p_listener) {
1167 audio_listener_2d = nullptr;
1168 }
1169}
1170
1171void Viewport::_canvas_layer_add(CanvasLayer *p_canvas_layer) {
1172 canvas_layers.insert(p_canvas_layer);
1173}
1174
1175void Viewport::_canvas_layer_remove(CanvasLayer *p_canvas_layer) {
1176 canvas_layers.erase(p_canvas_layer);
1177}
1178
1179void Viewport::set_transparent_background(bool p_enable) {
1180 ERR_MAIN_THREAD_GUARD;
1181 transparent_bg = p_enable;
1182 RS::get_singleton()->viewport_set_transparent_background(viewport, p_enable);
1183}
1184
1185bool Viewport::has_transparent_background() const {
1186 ERR_READ_THREAD_GUARD_V(false);
1187 return transparent_bg;
1188}
1189
1190void Viewport::set_use_hdr_2d(bool p_enable) {
1191 ERR_MAIN_THREAD_GUARD;
1192 use_hdr_2d = p_enable;
1193 RS::get_singleton()->viewport_set_use_hdr_2d(viewport, p_enable);
1194}
1195
1196bool Viewport::is_using_hdr_2d() const {
1197 ERR_READ_THREAD_GUARD_V(false);
1198 return use_hdr_2d;
1199}
1200
1201void Viewport::set_world_2d(const Ref<World2D> &p_world_2d) {
1202 ERR_MAIN_THREAD_GUARD;
1203 if (world_2d == p_world_2d) {
1204 return;
1205 }
1206
1207 if (is_inside_tree()) {
1208 RenderingServer::get_singleton()->viewport_remove_canvas(viewport, current_canvas);
1209 }
1210
1211 if (world_2d.is_valid()) {
1212 world_2d->remove_viewport(this);
1213 }
1214
1215 if (p_world_2d.is_valid()) {
1216 bool do_propagate = world_2d.is_valid() && is_inside_tree();
1217 world_2d = p_world_2d;
1218 if (do_propagate) {
1219 _propagate_world_2d_changed(this);
1220 }
1221 } else {
1222 WARN_PRINT("Invalid world_2d");
1223 world_2d = Ref<World2D>(memnew(World2D));
1224 }
1225
1226 world_2d->register_viewport(this);
1227 _update_audio_listener_2d();
1228
1229 if (is_inside_tree()) {
1230 current_canvas = find_world_2d()->get_canvas();
1231 RenderingServer::get_singleton()->viewport_attach_canvas(viewport, current_canvas);
1232 }
1233}
1234
1235Ref<World2D> Viewport::find_world_2d() const {
1236 ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1237 if (world_2d.is_valid()) {
1238 return world_2d;
1239 } else if (parent) {
1240 return parent->find_world_2d();
1241 } else {
1242 return Ref<World2D>();
1243 }
1244}
1245
1246void Viewport::_propagate_viewport_notification(Node *p_node, int p_what) {
1247 p_node->notification(p_what);
1248 for (int i = 0; i < p_node->get_child_count(); i++) {
1249 Node *c = p_node->get_child(i);
1250 if (Object::cast_to<Viewport>(c)) {
1251 continue;
1252 }
1253 _propagate_viewport_notification(c, p_what);
1254 }
1255}
1256
1257Ref<World2D> Viewport::get_world_2d() const {
1258 ERR_READ_THREAD_GUARD_V(Ref<World2D>());
1259 return world_2d;
1260}
1261
1262Camera2D *Viewport::get_camera_2d() const {
1263 ERR_READ_THREAD_GUARD_V(nullptr);
1264 return camera_2d;
1265}
1266
1267Transform2D Viewport::get_final_transform() const {
1268 ERR_READ_THREAD_GUARD_V(Transform2D());
1269 return stretch_transform * global_canvas_transform;
1270}
1271
1272void Viewport::assign_next_enabled_camera_2d(const StringName &p_camera_group) {
1273 ERR_MAIN_THREAD_GUARD;
1274 List<Node *> camera_list;
1275 get_tree()->get_nodes_in_group(p_camera_group, &camera_list);
1276
1277 Camera2D *new_camera = nullptr;
1278 for (Node *E : camera_list) {
1279 Camera2D *cam = Object::cast_to<Camera2D>(E);
1280 if (!cam) {
1281 continue; // Non-camera node (e.g. ParallaxBackground).
1282 }
1283
1284 if (cam->is_enabled()) {
1285 new_camera = cam;
1286 break;
1287 }
1288 }
1289
1290 _camera_2d_set(new_camera);
1291 if (!camera_2d) {
1292 set_canvas_transform(Transform2D());
1293 }
1294}
1295
1296void Viewport::_update_canvas_items(Node *p_node) {
1297 if (p_node != this) {
1298 Window *w = Object::cast_to<Window>(p_node);
1299 if (w && (!w->is_inside_tree() || !w->is_embedded())) {
1300 return;
1301 }
1302
1303 CanvasItem *ci = Object::cast_to<CanvasItem>(p_node);
1304 if (ci) {
1305 ci->queue_redraw();
1306 }
1307 }
1308
1309 int cc = p_node->get_child_count();
1310
1311 for (int i = 0; i < cc; i++) {
1312 _update_canvas_items(p_node->get_child(i));
1313 }
1314}
1315
1316Ref<ViewportTexture> Viewport::get_texture() const {
1317 ERR_READ_THREAD_GUARD_V(Ref<ViewportTexture>());
1318 return default_texture;
1319}
1320
1321void Viewport::set_positional_shadow_atlas_size(int p_size) {
1322 ERR_MAIN_THREAD_GUARD;
1323 positional_shadow_atlas_size = p_size;
1324 RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, p_size, positional_shadow_atlas_16_bits);
1325}
1326
1327int Viewport::get_positional_shadow_atlas_size() const {
1328 ERR_READ_THREAD_GUARD_V(0);
1329 return positional_shadow_atlas_size;
1330}
1331
1332void Viewport::set_positional_shadow_atlas_16_bits(bool p_16_bits) {
1333 ERR_MAIN_THREAD_GUARD;
1334 if (positional_shadow_atlas_16_bits == p_16_bits) {
1335 return;
1336 }
1337
1338 positional_shadow_atlas_16_bits = p_16_bits;
1339 RS::get_singleton()->viewport_set_positional_shadow_atlas_size(viewport, positional_shadow_atlas_size, positional_shadow_atlas_16_bits);
1340}
1341
1342bool Viewport::get_positional_shadow_atlas_16_bits() const {
1343 ERR_READ_THREAD_GUARD_V(false);
1344 return positional_shadow_atlas_16_bits;
1345}
1346void Viewport::set_positional_shadow_atlas_quadrant_subdiv(int p_quadrant, PositionalShadowAtlasQuadrantSubdiv p_subdiv) {
1347 ERR_MAIN_THREAD_GUARD;
1348 ERR_FAIL_INDEX(p_quadrant, 4);
1349 ERR_FAIL_INDEX(p_subdiv, SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
1350
1351 if (positional_shadow_atlas_quadrant_subdiv[p_quadrant] == p_subdiv) {
1352 return;
1353 }
1354
1355 positional_shadow_atlas_quadrant_subdiv[p_quadrant] = p_subdiv;
1356 static const int subdiv[SHADOW_ATLAS_QUADRANT_SUBDIV_MAX] = { 0, 1, 4, 16, 64, 256, 1024 };
1357
1358 RS::get_singleton()->viewport_set_positional_shadow_atlas_quadrant_subdivision(viewport, p_quadrant, subdiv[p_subdiv]);
1359}
1360
1361Viewport::PositionalShadowAtlasQuadrantSubdiv Viewport::get_positional_shadow_atlas_quadrant_subdiv(int p_quadrant) const {
1362 ERR_READ_THREAD_GUARD_V(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1363 ERR_FAIL_INDEX_V(p_quadrant, 4, SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
1364 return positional_shadow_atlas_quadrant_subdiv[p_quadrant];
1365}
1366
1367Ref<InputEvent> Viewport::_make_input_local(const Ref<InputEvent> &ev) {
1368 if (ev.is_null()) {
1369 return ev; // No transformation defined for null event
1370 }
1371
1372 Transform2D ai = get_final_transform().affine_inverse();
1373 return ev->xformed_by(ai);
1374}
1375
1376Vector2 Viewport::get_mouse_position() const {
1377 ERR_READ_THREAD_GUARD_V(Vector2());
1378 if (!is_directly_attached_to_screen()) {
1379 // Rely on the most recent mouse coordinate from an InputEventMouse in push_input.
1380 // In this case get_screen_transform is not applicable, because it is ambiguous.
1381 return gui.last_mouse_pos;
1382 } else if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_MOUSE)) {
1383 Transform2D xform = get_screen_transform_internal(true);
1384 if (xform.determinant() == 0) {
1385 // Screen transform can be non-invertible when the Window is minimized.
1386 return Vector2();
1387 }
1388 return xform.affine_inverse().xform(DisplayServer::get_singleton()->mouse_get_position());
1389 } else {
1390 // Fallback to Input for getting mouse position in case of emulated mouse.
1391 return get_screen_transform_internal().affine_inverse().xform(Input::get_singleton()->get_mouse_position());
1392 }
1393}
1394
1395void Viewport::warp_mouse(const Vector2 &p_position) {
1396 ERR_MAIN_THREAD_GUARD;
1397 Transform2D xform = get_screen_transform_internal();
1398 Vector2 gpos = xform.xform(p_position);
1399 Input::get_singleton()->warp_mouse(gpos);
1400}
1401
1402void Viewport::_gui_sort_roots() {
1403 if (!gui.roots_order_dirty) {
1404 return;
1405 }
1406
1407 gui.roots.sort_custom<Control::CComparator>();
1408
1409 gui.roots_order_dirty = false;
1410}
1411
1412void Viewport::_gui_cancel_tooltip() {
1413 gui.tooltip_control = nullptr;
1414 if (gui.tooltip_timer.is_valid()) {
1415 gui.tooltip_timer->release_connections();
1416 gui.tooltip_timer = Ref<SceneTreeTimer>();
1417 }
1418 if (gui.tooltip_popup) {
1419 gui.tooltip_popup->queue_free();
1420 }
1421}
1422
1423String Viewport::_gui_get_tooltip(Control *p_control, const Vector2 &p_pos, Control **r_tooltip_owner) {
1424 Vector2 pos = p_pos;
1425 String tooltip;
1426
1427 while (p_control) {
1428 tooltip = p_control->get_tooltip(pos);
1429
1430 // Temporary solution for PopupMenus.
1431 PopupMenu *menu = Object::cast_to<PopupMenu>(this);
1432 if (menu) {
1433 tooltip = menu->get_tooltip(pos);
1434 }
1435
1436 if (r_tooltip_owner) {
1437 *r_tooltip_owner = p_control;
1438 }
1439
1440 // If we found a tooltip, we stop here.
1441 if (!tooltip.is_empty()) {
1442 break;
1443 }
1444
1445 // Otherwise, we check parent controls unless some conditions prevent it.
1446
1447 if (p_control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1448 break;
1449 }
1450 if (p_control->is_set_as_top_level()) {
1451 break;
1452 }
1453
1454 // Transform cursor pos for parent control.
1455 pos = p_control->get_transform().xform(pos);
1456
1457 p_control = p_control->get_parent_control();
1458 }
1459
1460 return tooltip;
1461}
1462
1463void Viewport::_gui_show_tooltip() {
1464 if (!gui.tooltip_control) {
1465 return;
1466 }
1467
1468 // Get the Control under cursor and the relevant tooltip text, if any.
1469 Control *tooltip_owner = nullptr;
1470 String tooltip_text = _gui_get_tooltip(
1471 gui.tooltip_control,
1472 gui.tooltip_control->get_global_transform().xform_inv(gui.last_mouse_pos),
1473 &tooltip_owner);
1474 tooltip_text = tooltip_text.strip_edges();
1475 if (tooltip_text.is_empty()) {
1476 return; // Nothing to show.
1477 }
1478
1479 // Remove previous popup if we change something.
1480 if (gui.tooltip_popup) {
1481 memdelete(gui.tooltip_popup);
1482 }
1483
1484 if (!tooltip_owner) {
1485 return;
1486 }
1487
1488 // Popup window which houses the tooltip content.
1489 PopupPanel *panel = memnew(PopupPanel);
1490 panel->set_theme_type_variation(SNAME("TooltipPanel"));
1491
1492 // Ensure no opaque background behind the panel as its StyleBox can be partially transparent (e.g. corners).
1493 panel->set_transparent_background(true);
1494
1495 // Controls can implement `make_custom_tooltip` to provide their own tooltip.
1496 // This should be a Control node which will be added as child to a TooltipPanel.
1497 Control *base_tooltip = tooltip_owner->make_custom_tooltip(tooltip_text);
1498
1499 // If no custom tooltip is given, use a default implementation.
1500 if (!base_tooltip) {
1501 gui.tooltip_label = memnew(Label);
1502 gui.tooltip_label->set_theme_type_variation(SNAME("TooltipLabel"));
1503 gui.tooltip_label->set_auto_translate(gui.tooltip_control->is_auto_translating());
1504 gui.tooltip_label->set_text(tooltip_text);
1505 base_tooltip = gui.tooltip_label;
1506 panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip));
1507 }
1508
1509 base_tooltip->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
1510
1511 panel->set_transient(true);
1512 panel->set_flag(Window::FLAG_NO_FOCUS, true);
1513 panel->set_flag(Window::FLAG_POPUP, false);
1514 panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
1515 panel->set_wrap_controls(true);
1516 panel->add_child(base_tooltip);
1517 panel->gui_parent = this;
1518
1519 gui.tooltip_popup = panel;
1520
1521 tooltip_owner->add_child(gui.tooltip_popup);
1522
1523 Point2 tooltip_offset = GLOBAL_GET("display/mouse_cursor/tooltip_position_offset");
1524 Rect2 r(gui.tooltip_pos + tooltip_offset, gui.tooltip_popup->get_contents_minimum_size());
1525 r.size = r.size.min(panel->get_max_size());
1526
1527 Window *window = gui.tooltip_popup->get_parent_visible_window();
1528 Rect2i vr;
1529 if (gui.tooltip_popup->is_embedded()) {
1530 vr = gui.tooltip_popup->get_embedder()->get_visible_rect();
1531 } else {
1532 vr = window->get_usable_parent_rect();
1533 }
1534
1535 if (r.size.x + r.position.x > vr.size.x + vr.position.x) {
1536 // Place it in the opposite direction. If it fails, just hug the border.
1537 r.position.x = gui.tooltip_pos.x - r.size.x - tooltip_offset.x;
1538
1539 if (r.position.x < vr.position.x) {
1540 r.position.x = vr.position.x + vr.size.x - r.size.x;
1541 }
1542 } else if (r.position.x < vr.position.x) {
1543 r.position.x = vr.position.x;
1544 }
1545
1546 if (r.size.y + r.position.y > vr.size.y + vr.position.y) {
1547 // Same as above.
1548 r.position.y = gui.tooltip_pos.y - r.size.y - tooltip_offset.y;
1549
1550 if (r.position.y < vr.position.y) {
1551 r.position.y = vr.position.y + vr.size.y - r.size.y;
1552 }
1553 } else if (r.position.y < vr.position.y) {
1554 r.position.y = vr.position.y;
1555 }
1556
1557 gui.tooltip_popup->set_position(r.position);
1558 gui.tooltip_popup->set_size(r.size);
1559
1560 DisplayServer::WindowID active_popup = DisplayServer::get_singleton()->window_get_active_popup();
1561 if (active_popup == DisplayServer::INVALID_WINDOW_ID || active_popup == window->get_window_id()) {
1562 gui.tooltip_popup->show();
1563 }
1564 gui.tooltip_popup->child_controls_changed();
1565}
1566
1567bool Viewport::_gui_call_input(Control *p_control, const Ref<InputEvent> &p_input) {
1568 bool stopped = false;
1569 Ref<InputEvent> ev = p_input;
1570
1571 // Returns true if an event should be impacted by a control's mouse filter.
1572 bool is_pointer_event = Ref<InputEventMouse>(p_input).is_valid() || Ref<InputEventScreenDrag>(p_input).is_valid() || Ref<InputEventScreenTouch>(p_input).is_valid();
1573
1574 Ref<InputEventMouseButton> mb = p_input;
1575 bool is_scroll_event = mb.is_valid() &&
1576 (mb->get_button_index() == MouseButton::WHEEL_DOWN ||
1577 mb->get_button_index() == MouseButton::WHEEL_UP ||
1578 mb->get_button_index() == MouseButton::WHEEL_LEFT ||
1579 mb->get_button_index() == MouseButton::WHEEL_RIGHT);
1580
1581 CanvasItem *ci = p_control;
1582 while (ci) {
1583 Control *control = Object::cast_to<Control>(ci);
1584 if (control) {
1585 if (control->data.mouse_filter != Control::MOUSE_FILTER_IGNORE) {
1586 control->_call_gui_input(ev);
1587 }
1588
1589 if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1590 break;
1591 }
1592 if (gui.key_event_accepted) {
1593 stopped = true;
1594 break;
1595 }
1596 if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP && is_pointer_event && !(is_scroll_event && control->data.force_pass_scroll_events)) {
1597 // Mouse, ScreenDrag and ScreenTouch events are stopped by default with MOUSE_FILTER_STOP, unless we have a scroll event and force_pass_scroll_events set to true
1598 stopped = true;
1599 break;
1600 }
1601 }
1602
1603 if (is_input_handled()) {
1604 // Break after Physics Picking in SubViewport.
1605 break;
1606 }
1607
1608 if (ci->is_set_as_top_level()) {
1609 break;
1610 }
1611
1612 ev = ev->xformed_by(ci->get_transform()); // Transform event upwards.
1613 ci = ci->get_parent_item();
1614 }
1615 return stopped;
1616}
1617
1618void Viewport::_gui_call_notification(Control *p_control, int p_what) {
1619 CanvasItem *ci = p_control;
1620 while (ci) {
1621 Control *control = Object::cast_to<Control>(ci);
1622 if (control) {
1623 if (control->data.mouse_filter != Control::MOUSE_FILTER_IGNORE) {
1624 control->notification(p_what);
1625 }
1626
1627 if (!control->is_inside_tree()) {
1628 break;
1629 }
1630
1631 if (!control->is_inside_tree() || control->is_set_as_top_level()) {
1632 break;
1633 }
1634 if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1635 break;
1636 }
1637 }
1638
1639 if (ci->is_set_as_top_level()) {
1640 break;
1641 }
1642
1643 ci = ci->get_parent_item();
1644 }
1645}
1646
1647Control *Viewport::gui_find_control(const Point2 &p_global) {
1648 ERR_MAIN_THREAD_GUARD_V(nullptr);
1649 // Handle subwindows.
1650 _gui_sort_roots();
1651
1652 for (List<Control *>::Element *E = gui.roots.back(); E; E = E->prev()) {
1653 Control *sw = E->get();
1654 if (!sw->is_visible_in_tree()) {
1655 continue;
1656 }
1657
1658 Transform2D xform;
1659 CanvasItem *pci = sw->get_parent_item();
1660 if (pci) {
1661 xform = pci->get_global_transform_with_canvas();
1662 } else {
1663 xform = sw->get_canvas_transform();
1664 }
1665
1666 Control *ret = _gui_find_control_at_pos(sw, p_global, xform);
1667 if (ret) {
1668 return ret;
1669 }
1670 }
1671
1672 return nullptr;
1673}
1674
1675Control *Viewport::_gui_find_control_at_pos(CanvasItem *p_node, const Point2 &p_global, const Transform2D &p_xform) {
1676 if (!p_node->is_visible()) {
1677 return nullptr; // Canvas item hidden, discard.
1678 }
1679
1680 Transform2D matrix = p_xform * p_node->get_transform();
1681 // matrix.determinant() == 0.0f implies that node does not exist on scene
1682 if (matrix.determinant() == 0.0f) {
1683 return nullptr;
1684 }
1685
1686 Control *c = Object::cast_to<Control>(p_node);
1687
1688 if (!c || !c->is_clipping_contents() || c->has_point(matrix.affine_inverse().xform(p_global))) {
1689 for (int i = p_node->get_child_count() - 1; i >= 0; i--) {
1690 CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i));
1691 if (!ci || ci->is_set_as_top_level()) {
1692 continue;
1693 }
1694
1695 Control *ret = _gui_find_control_at_pos(ci, p_global, matrix);
1696 if (ret) {
1697 return ret;
1698 }
1699 }
1700 }
1701
1702 if (!c || c->data.mouse_filter == Control::MOUSE_FILTER_IGNORE) {
1703 return nullptr;
1704 }
1705
1706 matrix.affine_invert();
1707 if (!c->has_point(matrix.xform(p_global))) {
1708 return nullptr;
1709 }
1710
1711 Control *drag_preview = _gui_get_drag_preview();
1712 if (!drag_preview || (c != drag_preview && !drag_preview->is_ancestor_of(c))) {
1713 return c;
1714 }
1715
1716 return nullptr;
1717}
1718
1719bool Viewport::_gui_drop(Control *p_at_control, Point2 p_at_pos, bool p_just_check) {
1720 // Attempt grab, try parent controls too.
1721 CanvasItem *ci = p_at_control;
1722 while (ci) {
1723 Control *control = Object::cast_to<Control>(ci);
1724 if (control) {
1725 if (control->can_drop_data(p_at_pos, gui.drag_data)) {
1726 if (!p_just_check) {
1727 control->drop_data(p_at_pos, gui.drag_data);
1728 }
1729
1730 return true;
1731 }
1732
1733 if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1734 break;
1735 }
1736 }
1737
1738 p_at_pos = ci->get_transform().xform(p_at_pos);
1739
1740 if (ci->is_set_as_top_level()) {
1741 break;
1742 }
1743
1744 ci = ci->get_parent_item();
1745 }
1746
1747 return false;
1748}
1749
1750void Viewport::_gui_input_event(Ref<InputEvent> p_event) {
1751 ERR_FAIL_COND(p_event.is_null());
1752
1753 Ref<InputEventMouseButton> mb = p_event;
1754 if (mb.is_valid()) {
1755 gui.key_event_accepted = false;
1756
1757 Point2 mpos = mb->get_position();
1758 if (mb->is_pressed()) {
1759 if (!gui.mouse_focus_mask.is_empty()) {
1760 // Do not steal mouse focus and stuff while a focus mask exists.
1761 gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
1762 } else {
1763 gui.mouse_focus = gui_find_control(mpos);
1764 gui.last_mouse_focus = gui.mouse_focus;
1765
1766 if (!gui.mouse_focus) {
1767 return;
1768 }
1769
1770 gui.mouse_focus_mask.set_flag(mouse_button_to_mask(mb->get_button_index()));
1771
1772 if (mb->get_button_index() == MouseButton::LEFT) {
1773 gui.drag_accum = Vector2();
1774 gui.drag_attempted = false;
1775 }
1776 }
1777 DEV_ASSERT(gui.mouse_focus);
1778
1779 mb = mb->xformed_by(Transform2D()); // Make a copy of the event.
1780
1781 Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
1782 mb->set_position(pos);
1783
1784#ifdef DEBUG_ENABLED
1785 if (EngineDebugger::get_singleton()) {
1786 Array arr;
1787 arr.push_back(gui.mouse_focus->get_path());
1788 arr.push_back(gui.mouse_focus->get_class());
1789 EngineDebugger::get_singleton()->send_message("scene:click_ctrl", arr);
1790 }
1791#endif
1792
1793 if (mb->get_button_index() == MouseButton::LEFT) { // Assign focus.
1794 CanvasItem *ci = gui.mouse_focus;
1795 while (ci) {
1796 Control *control = Object::cast_to<Control>(ci);
1797 if (control) {
1798 if (control->get_focus_mode() != Control::FOCUS_NONE) {
1799 if (control != gui.key_focus) {
1800 control->grab_focus();
1801 }
1802 break;
1803 }
1804
1805 if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1806 break;
1807 }
1808 }
1809
1810 if (ci->is_set_as_top_level()) {
1811 break;
1812 }
1813
1814 ci = ci->get_parent_item();
1815 }
1816 }
1817
1818 bool stopped = gui.mouse_focus && gui.mouse_focus->can_process() && _gui_call_input(gui.mouse_focus, mb);
1819 if (stopped) {
1820 set_input_as_handled();
1821 }
1822
1823 if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1824 // Alternate drop use (when using force_drag(), as proposed by #5342).
1825 _perform_drop(gui.mouse_focus, pos);
1826 }
1827
1828 _gui_cancel_tooltip();
1829 } else {
1830 if (gui.dragging && mb->get_button_index() == MouseButton::LEFT) {
1831 _perform_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos);
1832 }
1833
1834 gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
1835
1836 if (!gui.mouse_focus) {
1837 // Release event is only sent if a mouse focus (previously pressed button) exists.
1838 return;
1839 }
1840
1841 mb = mb->xformed_by(Transform2D()); // Make a copy.
1842 Point2 pos = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(mpos);
1843 mb->set_position(pos);
1844
1845 Control *mouse_focus = gui.mouse_focus;
1846
1847 // Disable mouse focus if needed before calling input,
1848 // this makes popups on mouse press event work better,
1849 // as the release will never be received otherwise.
1850 if (gui.mouse_focus_mask.is_empty()) {
1851 gui.mouse_focus = nullptr;
1852 gui.forced_mouse_focus = false;
1853 }
1854
1855 bool stopped = mouse_focus && mouse_focus->can_process() && _gui_call_input(mouse_focus, mb);
1856 if (stopped) {
1857 set_input_as_handled();
1858 }
1859 }
1860 }
1861
1862 Ref<InputEventMouseMotion> mm = p_event;
1863 if (mm.is_valid()) {
1864 gui.key_event_accepted = false;
1865 Point2 mpos = mm->get_position();
1866
1867 // Drag & drop.
1868 if (!gui.drag_attempted && gui.mouse_focus && (mm->get_button_mask().has_flag(MouseButtonMask::LEFT))) {
1869 gui.drag_accum += mm->get_relative();
1870 float len = gui.drag_accum.length();
1871 if (len > 10) {
1872 { // Attempt grab, try parent controls too.
1873 CanvasItem *ci = gui.mouse_focus;
1874 while (ci) {
1875 Control *control = Object::cast_to<Control>(ci);
1876 if (control) {
1877 gui.dragging = true;
1878 gui.drag_data = control->get_drag_data(control->get_global_transform_with_canvas().affine_inverse().xform(mpos - gui.drag_accum));
1879 if (gui.drag_data.get_type() != Variant::NIL) {
1880 gui.mouse_focus = nullptr;
1881 gui.forced_mouse_focus = false;
1882 gui.mouse_focus_mask.clear();
1883 break;
1884 } else {
1885 Control *drag_preview = _gui_get_drag_preview();
1886 if (drag_preview) {
1887 ERR_PRINT("Don't set a drag preview and return null data. Preview was deleted and drag request ignored.");
1888 memdelete(drag_preview);
1889 gui.drag_preview_id = ObjectID();
1890 }
1891 gui.dragging = false;
1892 }
1893
1894 if (control->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1895 break;
1896 }
1897 }
1898
1899 if (ci->is_set_as_top_level()) {
1900 break;
1901 }
1902
1903 ci = ci->get_parent_item();
1904 }
1905 }
1906
1907 gui.drag_attempted = true;
1908 if (gui.dragging) {
1909 _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
1910 }
1911 }
1912 }
1913
1914 Control *over = nullptr;
1915 if (gui.mouse_focus) {
1916 over = gui.mouse_focus;
1917 } else if (gui.mouse_in_viewport) {
1918 over = gui_find_control(mpos);
1919 }
1920
1921 DisplayServer::CursorShape ds_cursor_shape = (DisplayServer::CursorShape)Input::get_singleton()->get_default_cursor_shape();
1922
1923 if (over) {
1924 Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
1925 Size2 pos = localizer.xform(mpos);
1926 Vector2 velocity = localizer.basis_xform(mm->get_velocity());
1927 Vector2 rel = localizer.basis_xform(mm->get_relative());
1928
1929 mm = mm->xformed_by(Transform2D()); // Make a copy.
1930
1931 mm->set_global_position(mpos);
1932 mm->set_velocity(velocity);
1933 mm->set_relative(rel);
1934
1935 if (mm->get_button_mask().is_empty()) {
1936 // Nothing pressed.
1937
1938 bool is_tooltip_shown = false;
1939
1940 if (gui.tooltip_popup) {
1941 if (gui.tooltip_control) {
1942 String tooltip = _gui_get_tooltip(over, gui.tooltip_control->get_global_transform().xform_inv(mpos));
1943 tooltip = tooltip.strip_edges();
1944 if (tooltip.length() == 0) {
1945 _gui_cancel_tooltip();
1946 } else if (gui.tooltip_label) {
1947 if (tooltip == gui.tooltip_label->get_text()) {
1948 is_tooltip_shown = true;
1949 }
1950 } else {
1951 is_tooltip_shown = true; // Nothing to compare against, likely using custom control, so if it changes there is nothing we can do.
1952 }
1953 } else {
1954 _gui_cancel_tooltip();
1955 }
1956 }
1957
1958 if (!is_tooltip_shown && over->can_process()) {
1959 if (gui.tooltip_timer.is_valid()) {
1960 gui.tooltip_timer->release_connections();
1961 gui.tooltip_timer = Ref<SceneTreeTimer>();
1962 }
1963 gui.tooltip_control = over;
1964 gui.tooltip_pos = over->get_screen_transform().xform(pos);
1965 gui.tooltip_timer = get_tree()->create_timer(gui.tooltip_delay);
1966 gui.tooltip_timer->set_ignore_time_scale(true);
1967 gui.tooltip_timer->connect("timeout", callable_mp(this, &Viewport::_gui_show_tooltip));
1968 }
1969 }
1970
1971 mm->set_position(pos);
1972
1973 Control::CursorShape cursor_shape = Control::CURSOR_ARROW;
1974 {
1975 Control *c = over;
1976 Vector2 cpos = pos;
1977 while (c) {
1978 if (!gui.mouse_focus_mask.is_empty() || c->has_point(cpos)) {
1979 cursor_shape = c->get_cursor_shape(cpos);
1980 } else {
1981 cursor_shape = Control::CURSOR_ARROW;
1982 }
1983 cpos = c->get_transform().xform(cpos);
1984 if (cursor_shape != Control::CURSOR_ARROW) {
1985 break;
1986 }
1987 if (c->data.mouse_filter == Control::MOUSE_FILTER_STOP) {
1988 break;
1989 }
1990 if (c->is_set_as_top_level()) {
1991 break;
1992 }
1993 c = c->get_parent_control();
1994 }
1995 }
1996
1997 ds_cursor_shape = (DisplayServer::CursorShape)cursor_shape;
1998
1999 bool stopped = over->can_process() && _gui_call_input(over, mm);
2000 if (stopped) {
2001 set_input_as_handled();
2002 }
2003 }
2004
2005 if (gui.dragging) {
2006 // Handle drag & drop.
2007
2008 Control *drag_preview = _gui_get_drag_preview();
2009 if (drag_preview) {
2010 drag_preview->set_position(mpos);
2011 }
2012
2013 gui.drag_mouse_over = over;
2014 gui.drag_mouse_over_pos = Vector2();
2015
2016 // Find the window this is above of.
2017 // See if there is an embedder.
2018 Viewport *embedder = nullptr;
2019 Vector2 viewport_pos;
2020
2021 if (is_embedding_subwindows()) {
2022 embedder = this;
2023 viewport_pos = mpos;
2024 } else {
2025 // Not an embedder, but may be a subwindow of an embedder.
2026 Window *w = Object::cast_to<Window>(this);
2027 if (w) {
2028 if (w->is_embedded()) {
2029 embedder = w->get_embedder();
2030
2031 viewport_pos = get_final_transform().xform(mpos) + w->get_position(); // To parent coords.
2032 }
2033 }
2034 }
2035
2036 Viewport *viewport_under = nullptr;
2037
2038 if (embedder) {
2039 // Use embedder logic.
2040
2041 for (int i = embedder->gui.sub_windows.size() - 1; i >= 0; i--) {
2042 Window *sw = embedder->gui.sub_windows[i].window;
2043 Rect2 swrect = Rect2i(sw->get_position(), sw->get_size());
2044 if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
2045 int title_height = sw->theme_cache.title_height;
2046 swrect.position.y -= title_height;
2047 swrect.size.y += title_height;
2048 }
2049
2050 if (swrect.has_point(viewport_pos)) {
2051 viewport_under = sw;
2052 viewport_pos -= sw->get_position();
2053 }
2054 }
2055
2056 if (!viewport_under) {
2057 // Not in a subwindow, likely in embedder.
2058 viewport_under = embedder;
2059 }
2060 } else {
2061 // Use DisplayServer logic.
2062 Vector2i screen_mouse_pos = DisplayServer::get_singleton()->mouse_get_position();
2063
2064 DisplayServer::WindowID window_id = DisplayServer::get_singleton()->get_window_at_screen_position(screen_mouse_pos);
2065
2066 if (window_id != DisplayServer::INVALID_WINDOW_ID) {
2067 ObjectID object_under = DisplayServer::get_singleton()->window_get_attached_instance_id(window_id);
2068
2069 if (object_under != ObjectID()) { // Fetch window.
2070 Window *w = Object::cast_to<Window>(ObjectDB::get_instance(object_under));
2071 if (w) {
2072 viewport_under = w;
2073 viewport_pos = screen_mouse_pos - w->get_position();
2074 }
2075 }
2076 }
2077 }
2078
2079 if (viewport_under) {
2080 if (viewport_under != this) {
2081 Transform2D ai = viewport_under->get_final_transform().affine_inverse();
2082 viewport_pos = ai.xform(viewport_pos);
2083 }
2084 // Find control under at position.
2085 gui.drag_mouse_over = viewport_under->gui_find_control(viewport_pos);
2086 if (gui.drag_mouse_over) {
2087 Transform2D localizer = gui.drag_mouse_over->get_global_transform_with_canvas().affine_inverse();
2088 gui.drag_mouse_over_pos = localizer.xform(viewport_pos);
2089
2090 bool can_drop = _gui_drop(gui.drag_mouse_over, gui.drag_mouse_over_pos, true);
2091
2092 if (!can_drop) {
2093 ds_cursor_shape = DisplayServer::CURSOR_FORBIDDEN;
2094 } else {
2095 ds_cursor_shape = DisplayServer::CURSOR_CAN_DROP;
2096 }
2097 }
2098
2099 } else {
2100 gui.drag_mouse_over = nullptr;
2101 }
2102 }
2103
2104 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE) && !Object::cast_to<SubViewportContainer>(over)) {
2105 DisplayServer::get_singleton()->cursor_set_shape(ds_cursor_shape);
2106 }
2107 }
2108
2109 Ref<InputEventScreenTouch> touch_event = p_event;
2110 if (touch_event.is_valid()) {
2111 Size2 pos = touch_event->get_position();
2112 const int touch_index = touch_event->get_index();
2113 if (touch_event->is_pressed()) {
2114 Control *over = gui_find_control(pos);
2115 if (over) {
2116 gui.touch_focus[touch_index] = over->get_instance_id();
2117 bool stopped = false;
2118 if (over->can_process()) {
2119 touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2120 pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2121 touch_event->set_position(pos);
2122 stopped = _gui_call_input(over, touch_event);
2123 }
2124 if (stopped) {
2125 set_input_as_handled();
2126 }
2127 return;
2128 }
2129 } else {
2130 bool stopped = false;
2131 ObjectID control_id = gui.touch_focus[touch_index];
2132 Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
2133 if (over && over->can_process()) {
2134 touch_event = touch_event->xformed_by(Transform2D()); // Make a copy.
2135 pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2136 touch_event->set_position(pos);
2137
2138 stopped = _gui_call_input(over, touch_event);
2139 }
2140 if (stopped) {
2141 set_input_as_handled();
2142 }
2143 gui.touch_focus.erase(touch_index);
2144 return;
2145 }
2146 }
2147
2148 Ref<InputEventGesture> gesture_event = p_event;
2149 if (gesture_event.is_valid()) {
2150 gui.key_event_accepted = false;
2151
2152 _gui_cancel_tooltip();
2153
2154 Size2 pos = gesture_event->get_position();
2155
2156 Control *over = gui_find_control(pos);
2157 if (over) {
2158 bool stopped = false;
2159 if (over->can_process()) {
2160 gesture_event = gesture_event->xformed_by(Transform2D()); // Make a copy.
2161 pos = over->get_global_transform_with_canvas().affine_inverse().xform(pos);
2162 gesture_event->set_position(pos);
2163 stopped = _gui_call_input(over, gesture_event);
2164 }
2165 if (stopped) {
2166 set_input_as_handled();
2167 }
2168 return;
2169 }
2170 }
2171
2172 Ref<InputEventScreenDrag> drag_event = p_event;
2173 if (drag_event.is_valid()) {
2174 const int drag_event_index = drag_event->get_index();
2175 ObjectID control_id = gui.touch_focus[drag_event_index];
2176 Control *over = control_id.is_valid() ? Object::cast_to<Control>(ObjectDB::get_instance(control_id)) : nullptr;
2177 if (!over) {
2178 over = gui_find_control(drag_event->get_position());
2179 }
2180 if (over) {
2181 bool stopped = false;
2182 if (over->can_process()) {
2183 Transform2D localizer = over->get_global_transform_with_canvas().affine_inverse();
2184 Size2 pos = localizer.xform(drag_event->get_position());
2185 Vector2 velocity = localizer.basis_xform(drag_event->get_velocity());
2186 Vector2 rel = localizer.basis_xform(drag_event->get_relative());
2187
2188 drag_event = drag_event->xformed_by(Transform2D()); // Make a copy.
2189
2190 drag_event->set_velocity(velocity);
2191 drag_event->set_relative(rel);
2192 drag_event->set_position(pos);
2193
2194 stopped = _gui_call_input(over, drag_event);
2195 }
2196
2197 if (stopped) {
2198 set_input_as_handled();
2199 }
2200 return;
2201 }
2202 }
2203
2204 if (mm.is_null() && mb.is_null() && p_event->is_action_type()) {
2205 if (gui.dragging && p_event->is_action_pressed("ui_cancel") && Input::get_singleton()->is_action_just_pressed("ui_cancel")) {
2206 _perform_drop();
2207 set_input_as_handled();
2208 return;
2209 }
2210
2211 if (p_event->is_action_pressed("ui_cancel")) {
2212 // Cancel tooltip timer or hide tooltip when pressing Escape (this is standard behavior in most applications).
2213 _gui_cancel_tooltip();
2214 if (gui.tooltip_popup) {
2215 // If a tooltip was hidden, prevent other actions associated with `ui_cancel` from occurring.
2216 // For instance, this prevents the node from being deselected when pressing Escape
2217 // to hide a documentation tooltip in the inspector.
2218 set_input_as_handled();
2219 return;
2220 }
2221 }
2222
2223 if (gui.key_focus && !gui.key_focus->is_visible_in_tree()) {
2224 gui.key_focus->release_focus();
2225 }
2226
2227 if (gui.key_focus) {
2228 gui.key_event_accepted = false;
2229 if (gui.key_focus->can_process()) {
2230 gui.key_focus->_call_gui_input(p_event);
2231 }
2232
2233 if (gui.key_event_accepted) {
2234 set_input_as_handled();
2235 return;
2236 }
2237 }
2238
2239 Control *from = gui.key_focus ? gui.key_focus : nullptr;
2240
2241 if (from && p_event->is_pressed()) {
2242 Control *next = nullptr;
2243
2244 Ref<InputEventJoypadMotion> joypadmotion_event = p_event;
2245 if (joypadmotion_event.is_valid()) {
2246 Input *input = Input::get_singleton();
2247
2248 if (p_event->is_action_pressed("ui_focus_next") && input->is_action_just_pressed("ui_focus_next")) {
2249 next = from->find_next_valid_focus();
2250 }
2251
2252 if (p_event->is_action_pressed("ui_focus_prev") && input->is_action_just_pressed("ui_focus_prev")) {
2253 next = from->find_prev_valid_focus();
2254 }
2255
2256 if (p_event->is_action_pressed("ui_up") && input->is_action_just_pressed("ui_up")) {
2257 next = from->_get_focus_neighbor(SIDE_TOP);
2258 }
2259
2260 if (p_event->is_action_pressed("ui_left") && input->is_action_just_pressed("ui_left")) {
2261 next = from->_get_focus_neighbor(SIDE_LEFT);
2262 }
2263
2264 if (p_event->is_action_pressed("ui_right") && input->is_action_just_pressed("ui_right")) {
2265 next = from->_get_focus_neighbor(SIDE_RIGHT);
2266 }
2267
2268 if (p_event->is_action_pressed("ui_down") && input->is_action_just_pressed("ui_down")) {
2269 next = from->_get_focus_neighbor(SIDE_BOTTOM);
2270 }
2271 } else {
2272 if (p_event->is_action_pressed("ui_focus_next", true, true)) {
2273 next = from->find_next_valid_focus();
2274 }
2275
2276 if (p_event->is_action_pressed("ui_focus_prev", true, true)) {
2277 next = from->find_prev_valid_focus();
2278 }
2279
2280 if (p_event->is_action_pressed("ui_up", true, true)) {
2281 next = from->_get_focus_neighbor(SIDE_TOP);
2282 }
2283
2284 if (p_event->is_action_pressed("ui_left", true, true)) {
2285 next = from->_get_focus_neighbor(SIDE_LEFT);
2286 }
2287
2288 if (p_event->is_action_pressed("ui_right", true, true)) {
2289 next = from->_get_focus_neighbor(SIDE_RIGHT);
2290 }
2291
2292 if (p_event->is_action_pressed("ui_down", true, true)) {
2293 next = from->_get_focus_neighbor(SIDE_BOTTOM);
2294 }
2295 }
2296 if (next) {
2297 next->grab_focus();
2298 set_input_as_handled();
2299 }
2300 }
2301 }
2302}
2303
2304void Viewport::_perform_drop(Control *p_control, Point2 p_pos) {
2305 // Without any arguments, simply cancel Drag and Drop.
2306 if (p_control) {
2307 gui.drag_successful = _gui_drop(p_control, p_pos, false);
2308 } else {
2309 gui.drag_successful = false;
2310 }
2311
2312 Control *drag_preview = _gui_get_drag_preview();
2313 if (drag_preview) {
2314 memdelete(drag_preview);
2315 gui.drag_preview_id = ObjectID();
2316 }
2317
2318 gui.drag_data = Variant();
2319 gui.dragging = false;
2320 gui.drag_mouse_over = nullptr;
2321 _propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
2322 // Display the new cursor shape instantly.
2323 update_mouse_cursor_state();
2324}
2325
2326void Viewport::_gui_cleanup_internal_state(Ref<InputEvent> p_event) {
2327 ERR_FAIL_COND(p_event.is_null());
2328
2329 Ref<InputEventMouseButton> mb = p_event;
2330 if (mb.is_valid()) {
2331 if (!mb->is_pressed()) {
2332 gui.mouse_focus_mask.clear_flag(mouse_button_to_mask(mb->get_button_index())); // Remove from mask.
2333 }
2334 }
2335}
2336
2337List<Control *>::Element *Viewport::_gui_add_root_control(Control *p_control) {
2338 gui.roots_order_dirty = true;
2339 return gui.roots.push_back(p_control);
2340}
2341
2342void Viewport::gui_set_root_order_dirty() {
2343 ERR_MAIN_THREAD_GUARD;
2344 gui.roots_order_dirty = true;
2345}
2346
2347void Viewport::_gui_force_drag(Control *p_base, const Variant &p_data, Control *p_control) {
2348 ERR_FAIL_COND_MSG(p_data.get_type() == Variant::NIL, "Drag data must be a value.");
2349
2350 gui.dragging = true;
2351 gui.drag_data = p_data;
2352 gui.mouse_focus = nullptr;
2353
2354 if (p_control) {
2355 _gui_set_drag_preview(p_base, p_control);
2356 }
2357 _propagate_viewport_notification(this, NOTIFICATION_DRAG_BEGIN);
2358}
2359
2360void Viewport::_gui_set_drag_preview(Control *p_base, Control *p_control) {
2361 ERR_FAIL_NULL(p_control);
2362 ERR_FAIL_COND(p_control->is_inside_tree());
2363 ERR_FAIL_COND(p_control->get_parent() != nullptr);
2364
2365 Control *drag_preview = _gui_get_drag_preview();
2366 if (drag_preview) {
2367 memdelete(drag_preview);
2368 }
2369 p_control->set_as_top_level(true);
2370 p_control->set_position(gui.last_mouse_pos);
2371 p_base->get_root_parent_control()->add_child(p_control); // Add as child of viewport.
2372 p_control->move_to_front();
2373
2374 gui.drag_preview_id = p_control->get_instance_id();
2375}
2376
2377Control *Viewport::_gui_get_drag_preview() {
2378 if (gui.drag_preview_id.is_null()) {
2379 return nullptr;
2380 } else {
2381 Control *drag_preview = Object::cast_to<Control>(ObjectDB::get_instance(gui.drag_preview_id));
2382 if (!drag_preview) {
2383 ERR_PRINT("Don't free the control set as drag preview.");
2384 gui.drag_preview_id = ObjectID();
2385 }
2386 return drag_preview;
2387 }
2388}
2389
2390void Viewport::_gui_remove_root_control(List<Control *>::Element *RI) {
2391 gui.roots.erase(RI);
2392}
2393
2394void Viewport::_gui_unfocus_control(Control *p_control) {
2395 if (gui.key_focus == p_control) {
2396 gui.key_focus->release_focus();
2397 }
2398}
2399
2400void Viewport::_gui_hide_control(Control *p_control) {
2401 if (gui.mouse_focus == p_control) {
2402 _drop_mouse_focus();
2403 }
2404
2405 if (gui.key_focus == p_control) {
2406 gui_release_focus();
2407 }
2408 if (gui.mouse_over == p_control) {
2409 _drop_mouse_over();
2410 }
2411 if (gui.drag_mouse_over == p_control) {
2412 gui.drag_mouse_over = nullptr;
2413 }
2414 if (gui.tooltip_control == p_control) {
2415 _gui_cancel_tooltip();
2416 }
2417}
2418
2419void Viewport::_gui_remove_control(Control *p_control) {
2420 if (gui.mouse_focus == p_control) {
2421 gui.mouse_focus = nullptr;
2422 gui.forced_mouse_focus = false;
2423 gui.mouse_focus_mask.clear();
2424 }
2425 if (gui.last_mouse_focus == p_control) {
2426 gui.last_mouse_focus = nullptr;
2427 }
2428 if (gui.key_focus == p_control) {
2429 gui.key_focus = nullptr;
2430 }
2431 if (gui.mouse_over == p_control) {
2432 _drop_mouse_over();
2433 }
2434 if (gui.drag_mouse_over == p_control) {
2435 gui.drag_mouse_over = nullptr;
2436 }
2437 if (gui.tooltip_control == p_control) {
2438 gui.tooltip_control = nullptr;
2439 }
2440}
2441
2442Window *Viewport::get_base_window() const {
2443 ERR_READ_THREAD_GUARD_V(nullptr);
2444 ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
2445
2446 Viewport *v = const_cast<Viewport *>(this);
2447 Window *w = Object::cast_to<Window>(v);
2448 while (!w) {
2449 v = v->get_parent_viewport();
2450 w = Object::cast_to<Window>(v);
2451 }
2452
2453 return w;
2454}
2455
2456void Viewport::_gui_remove_focus_for_window(Node *p_window) {
2457 if (get_base_window() == p_window) {
2458 gui_release_focus();
2459 }
2460}
2461
2462bool Viewport::_gui_control_has_focus(const Control *p_control) {
2463 return gui.key_focus == p_control;
2464}
2465
2466void Viewport::_gui_control_grab_focus(Control *p_control) {
2467 if (gui.key_focus && gui.key_focus == p_control) {
2468 // No need for change.
2469 return;
2470 }
2471 get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window());
2472 if (p_control->is_inside_tree() && p_control->get_viewport() == this) {
2473 gui.key_focus = p_control;
2474 emit_signal(SNAME("gui_focus_changed"), p_control);
2475 p_control->notification(Control::NOTIFICATION_FOCUS_ENTER);
2476 p_control->queue_redraw();
2477 }
2478}
2479
2480void Viewport::_gui_accept_event() {
2481 gui.key_event_accepted = true;
2482 if (is_inside_tree()) {
2483 set_input_as_handled();
2484 }
2485}
2486
2487void Viewport::_drop_mouse_focus() {
2488 Control *c = gui.mouse_focus;
2489 BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2490 gui.mouse_focus = nullptr;
2491 gui.forced_mouse_focus = false;
2492 gui.mouse_focus_mask.clear();
2493
2494 for (int i = 0; i < 3; i++) {
2495 if ((int)mask & (1 << i)) {
2496 Ref<InputEventMouseButton> mb;
2497 mb.instantiate();
2498 mb->set_position(c->get_local_mouse_position());
2499 mb->set_global_position(c->get_local_mouse_position());
2500 mb->set_button_index(MouseButton(i + 1));
2501 mb->set_pressed(false);
2502 mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2503 c->_call_gui_input(mb);
2504 }
2505 }
2506}
2507
2508void Viewport::_drop_physics_mouseover(bool p_paused_only) {
2509 _cleanup_mouseover_colliders(true, p_paused_only);
2510
2511#ifndef _3D_DISABLED
2512 if (physics_object_over.is_valid()) {
2513 CollisionObject3D *co = Object::cast_to<CollisionObject3D>(ObjectDB::get_instance(physics_object_over));
2514 if (co) {
2515 if (!co->is_inside_tree()) {
2516 physics_object_over = ObjectID();
2517 physics_object_capture = ObjectID();
2518 } else if (!(p_paused_only && co->can_process())) {
2519 co->_mouse_exit();
2520 physics_object_over = ObjectID();
2521 physics_object_capture = ObjectID();
2522 }
2523 }
2524 }
2525#endif // _3D_DISABLED
2526}
2527
2528void Viewport::_cleanup_mouseover_colliders(bool p_clean_all_frames, bool p_paused_only, uint64_t p_frame_reference) {
2529 List<ObjectID> to_erase;
2530 List<ObjectID> to_mouse_exit;
2531
2532 for (const KeyValue<ObjectID, uint64_t> &E : physics_2d_mouseover) {
2533 if (!p_clean_all_frames && E.value == p_frame_reference) {
2534 continue;
2535 }
2536
2537 Object *o = ObjectDB::get_instance(E.key);
2538 if (o) {
2539 CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
2540 if (co && co->is_inside_tree()) {
2541 if (p_clean_all_frames && p_paused_only && co->can_process()) {
2542 continue;
2543 }
2544 to_mouse_exit.push_back(E.key);
2545 }
2546 }
2547 to_erase.push_back(E.key);
2548 }
2549
2550 while (to_erase.size()) {
2551 physics_2d_mouseover.erase(to_erase.front()->get());
2552 to_erase.pop_front();
2553 }
2554
2555 // Per-shape.
2556 List<Pair<ObjectID, int>> shapes_to_erase;
2557 List<Pair<ObjectID, int>> shapes_to_mouse_exit;
2558
2559 for (KeyValue<Pair<ObjectID, int>, uint64_t> &E : physics_2d_shape_mouseover) {
2560 if (!p_clean_all_frames && E.value == p_frame_reference) {
2561 continue;
2562 }
2563
2564 Object *o = ObjectDB::get_instance(E.key.first);
2565 if (o) {
2566 CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
2567 if (co && co->is_inside_tree()) {
2568 if (p_clean_all_frames && p_paused_only && co->can_process()) {
2569 continue;
2570 }
2571 shapes_to_mouse_exit.push_back(E.key);
2572 }
2573 }
2574 shapes_to_erase.push_back(E.key);
2575 }
2576
2577 while (shapes_to_erase.size()) {
2578 physics_2d_shape_mouseover.erase(shapes_to_erase.front()->get());
2579 shapes_to_erase.pop_front();
2580 }
2581
2582 while (to_mouse_exit.size()) {
2583 Object *o = ObjectDB::get_instance(to_mouse_exit.front()->get());
2584 CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
2585 co->_mouse_exit();
2586 to_mouse_exit.pop_front();
2587 }
2588
2589 while (shapes_to_mouse_exit.size()) {
2590 Pair<ObjectID, int> e = shapes_to_mouse_exit.front()->get();
2591 Object *o = ObjectDB::get_instance(e.first);
2592 CollisionObject2D *co = Object::cast_to<CollisionObject2D>(o);
2593 co->_mouse_shape_exit(e.second);
2594 shapes_to_mouse_exit.pop_front();
2595 }
2596}
2597
2598void Viewport::_gui_grab_click_focus(Control *p_control) {
2599 gui.mouse_click_grabber = p_control;
2600 call_deferred(SNAME("_post_gui_grab_click_focus"));
2601}
2602
2603void Viewport::_post_gui_grab_click_focus() {
2604 Control *focus_grabber = gui.mouse_click_grabber;
2605 if (!focus_grabber) {
2606 // Redundant grab requests were made.
2607 return;
2608 }
2609 gui.mouse_click_grabber = nullptr;
2610
2611 if (gui.mouse_focus) {
2612 if (gui.mouse_focus == focus_grabber) {
2613 return;
2614 }
2615
2616 BitField<MouseButtonMask> mask = gui.mouse_focus_mask;
2617 Point2 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2618
2619 for (int i = 0; i < 3; i++) {
2620 if ((int)mask & (1 << i)) {
2621 Ref<InputEventMouseButton> mb;
2622 mb.instantiate();
2623
2624 // Send unclick.
2625
2626 mb->set_position(click);
2627 mb->set_button_index(MouseButton(i + 1));
2628 mb->set_pressed(false);
2629 mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2630 gui.mouse_focus->_call_gui_input(mb);
2631 }
2632 }
2633
2634 gui.mouse_focus = focus_grabber;
2635 click = gui.mouse_focus->get_global_transform_with_canvas().affine_inverse().xform(gui.last_mouse_pos);
2636
2637 for (int i = 0; i < 3; i++) {
2638 if ((int)mask & (1 << i)) {
2639 Ref<InputEventMouseButton> mb;
2640 mb.instantiate();
2641
2642 // Send click.
2643
2644 mb->set_position(click);
2645 mb->set_button_index(MouseButton(i + 1));
2646 mb->set_pressed(true);
2647 mb->set_device(InputEvent::DEVICE_ID_INTERNAL);
2648 MessageQueue::get_singleton()->push_callable(callable_mp(gui.mouse_focus, &Control::_call_gui_input), mb);
2649 }
2650 }
2651 }
2652}
2653
2654///////////////////////////////
2655
2656void Viewport::push_text_input(const String &p_text) {
2657 ERR_MAIN_THREAD_GUARD;
2658 if (gui.subwindow_focused) {
2659 gui.subwindow_focused->push_text_input(p_text);
2660 return;
2661 }
2662
2663 if (gui.key_focus) {
2664 gui.key_focus->call("set_text", p_text);
2665 }
2666}
2667
2668Viewport::SubWindowResize Viewport::_sub_window_get_resize_margin(Window *p_subwindow, const Point2 &p_point) {
2669 if (p_subwindow->get_flag(Window::FLAG_BORDERLESS) || p_subwindow->get_flag(Window::FLAG_RESIZE_DISABLED)) {
2670 return SUB_WINDOW_RESIZE_DISABLED;
2671 }
2672
2673 Rect2i r = Rect2i(p_subwindow->get_position(), p_subwindow->get_size());
2674
2675 int title_height = p_subwindow->theme_cache.title_height;
2676
2677 r.position.y -= title_height;
2678 r.size.y += title_height;
2679
2680 if (r.has_point(p_point)) {
2681 return SUB_WINDOW_RESIZE_DISABLED; // It's inside, so no resize.
2682 }
2683
2684 int dist_x = p_point.x < r.position.x ? (p_point.x - r.position.x) : (p_point.x > (r.position.x + r.size.x) ? (p_point.x - (r.position.x + r.size.x)) : 0);
2685 int dist_y = p_point.y < r.position.y ? (p_point.y - r.position.y) : (p_point.y > (r.position.y + r.size.y) ? (p_point.y - (r.position.y + r.size.y)) : 0);
2686
2687 int limit = p_subwindow->theme_cache.resize_margin;
2688
2689 if (ABS(dist_x) > limit) {
2690 return SUB_WINDOW_RESIZE_DISABLED;
2691 }
2692
2693 if (ABS(dist_y) > limit) {
2694 return SUB_WINDOW_RESIZE_DISABLED;
2695 }
2696
2697 if (dist_x < 0 && dist_y < 0) {
2698 return SUB_WINDOW_RESIZE_TOP_LEFT;
2699 }
2700
2701 if (dist_x == 0 && dist_y < 0) {
2702 return SUB_WINDOW_RESIZE_TOP;
2703 }
2704
2705 if (dist_x > 0 && dist_y < 0) {
2706 return SUB_WINDOW_RESIZE_TOP_RIGHT;
2707 }
2708
2709 if (dist_x < 0 && dist_y == 0) {
2710 return SUB_WINDOW_RESIZE_LEFT;
2711 }
2712
2713 if (dist_x > 0 && dist_y == 0) {
2714 return SUB_WINDOW_RESIZE_RIGHT;
2715 }
2716
2717 if (dist_x < 0 && dist_y > 0) {
2718 return SUB_WINDOW_RESIZE_BOTTOM_LEFT;
2719 }
2720
2721 if (dist_x == 0 && dist_y > 0) {
2722 return SUB_WINDOW_RESIZE_BOTTOM;
2723 }
2724
2725 if (dist_x > 0 && dist_y > 0) {
2726 return SUB_WINDOW_RESIZE_BOTTOM_RIGHT;
2727 }
2728
2729 return SUB_WINDOW_RESIZE_DISABLED;
2730}
2731
2732bool Viewport::_sub_windows_forward_input(const Ref<InputEvent> &p_event) {
2733 if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
2734 ERR_FAIL_COND_V(gui.currently_dragged_subwindow == nullptr, false);
2735
2736 Ref<InputEventMouseButton> mb = p_event;
2737 if (mb.is_valid() && !mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2738 if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2739 if (gui.subwindow_drag_close_rect.has_point(mb->get_position())) {
2740 // Close window.
2741 gui.currently_dragged_subwindow->_event_callback(DisplayServer::WINDOW_EVENT_CLOSE_REQUEST);
2742 }
2743 }
2744 gui.subwindow_drag = SUB_WINDOW_DRAG_DISABLED;
2745 if (gui.currently_dragged_subwindow != nullptr) { // May have been erased.
2746 _sub_window_update(gui.currently_dragged_subwindow);
2747 gui.currently_dragged_subwindow = nullptr;
2748 }
2749 }
2750
2751 Ref<InputEventMouseMotion> mm = p_event;
2752 if (mm.is_valid()) {
2753 if (gui.subwindow_drag == SUB_WINDOW_DRAG_MOVE) {
2754 Vector2 diff = mm->get_position() - gui.subwindow_drag_from;
2755 Rect2i new_rect(gui.subwindow_drag_pos + diff, gui.currently_dragged_subwindow->get_size());
2756
2757 if (gui.currently_dragged_subwindow->is_clamped_to_embedder()) {
2758 new_rect = gui.currently_dragged_subwindow->fit_rect_in_parent(new_rect, get_visible_rect());
2759 }
2760
2761 gui.currently_dragged_subwindow->_rect_changed_callback(new_rect);
2762
2763 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
2764 DisplayServer::get_singleton()->cursor_set_shape(DisplayServer::CURSOR_MOVE);
2765 }
2766 }
2767 if (gui.subwindow_drag == SUB_WINDOW_DRAG_CLOSE) {
2768 gui.subwindow_drag_close_inside = gui.subwindow_drag_close_rect.has_point(mm->get_position());
2769 }
2770 if (gui.subwindow_drag == SUB_WINDOW_DRAG_RESIZE) {
2771 Vector2i diff = mm->get_position() - gui.subwindow_drag_from;
2772 Size2i min_size = gui.currently_dragged_subwindow->get_min_size();
2773 Size2i min_size_clamped = gui.currently_dragged_subwindow->get_clamped_minimum_size();
2774
2775 min_size_clamped.x = MAX(min_size_clamped.x, 1);
2776 min_size_clamped.y = MAX(min_size_clamped.y, 1);
2777
2778 Rect2i r = gui.subwindow_resize_from_rect;
2779
2780 Size2i limit = r.size - min_size_clamped;
2781
2782 switch (gui.subwindow_resize_mode) {
2783 case SUB_WINDOW_RESIZE_TOP_LEFT: {
2784 diff.x = MIN(diff.x, limit.x);
2785 diff.y = MIN(diff.y, limit.y);
2786 r.position += diff;
2787 r.size -= diff;
2788 } break;
2789 case SUB_WINDOW_RESIZE_TOP: {
2790 diff.x = 0;
2791 diff.y = MIN(diff.y, limit.y);
2792 r.position += diff;
2793 r.size -= diff;
2794 } break;
2795 case SUB_WINDOW_RESIZE_TOP_RIGHT: {
2796 diff.x = MAX(diff.x, -limit.x);
2797 diff.y = MIN(diff.y, limit.y);
2798 r.position.y += diff.y;
2799 r.size.y -= diff.y;
2800 r.size.x += diff.x;
2801 } break;
2802 case SUB_WINDOW_RESIZE_LEFT: {
2803 diff.x = MIN(diff.x, limit.x);
2804 diff.y = 0;
2805 r.position += diff;
2806 r.size -= diff;
2807
2808 } break;
2809 case SUB_WINDOW_RESIZE_RIGHT: {
2810 diff.x = MAX(diff.x, -limit.x);
2811 r.size.x += diff.x;
2812 } break;
2813 case SUB_WINDOW_RESIZE_BOTTOM_LEFT: {
2814 diff.x = MIN(diff.x, limit.x);
2815 diff.y = MAX(diff.y, -limit.y);
2816 r.position.x += diff.x;
2817 r.size.x -= diff.x;
2818 r.size.y += diff.y;
2819
2820 } break;
2821 case SUB_WINDOW_RESIZE_BOTTOM: {
2822 diff.y = MAX(diff.y, -limit.y);
2823 r.size.y += diff.y;
2824 } break;
2825 case SUB_WINDOW_RESIZE_BOTTOM_RIGHT: {
2826 diff.x = MAX(diff.x, -limit.x);
2827 diff.y = MAX(diff.y, -limit.y);
2828 r.size += diff;
2829
2830 } break;
2831 default: {
2832 }
2833 }
2834
2835 Size2i max_size = gui.currently_dragged_subwindow->get_max_size();
2836 if ((max_size.x > 0 || max_size.y > 0) && (max_size.x >= min_size.x && max_size.y >= min_size.y)) {
2837 max_size.x = MAX(max_size.x, 1);
2838 max_size.y = MAX(max_size.y, 1);
2839
2840 if (r.size.x > max_size.x) {
2841 r.size.x = max_size.x;
2842 }
2843 if (r.size.y > max_size.y) {
2844 r.size.y = max_size.y;
2845 }
2846 }
2847
2848 gui.currently_dragged_subwindow->_rect_changed_callback(r);
2849 }
2850
2851 if (gui.currently_dragged_subwindow) { // May have been erased.
2852 _sub_window_update(gui.currently_dragged_subwindow);
2853 }
2854 }
2855
2856 return true; // Handled.
2857 }
2858 Ref<InputEventMouseButton> mb = p_event;
2859 // If the event is a mouse button, we need to check whether another window was clicked.
2860
2861 if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) {
2862 Window *click_on_window = nullptr;
2863 for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
2864 SubWindow sw = gui.sub_windows.write[i];
2865
2866 // Clicked inside window?
2867
2868 Rect2i r = Rect2i(sw.window->get_position(), sw.window->get_size());
2869
2870 if (!sw.window->get_flag(Window::FLAG_BORDERLESS)) {
2871 // Check top bar.
2872 int title_height = sw.window->theme_cache.title_height;
2873 Rect2i title_bar = r;
2874 title_bar.position.y -= title_height;
2875 title_bar.size.y = title_height;
2876
2877 if (title_bar.size.y > 0 && title_bar.has_point(mb->get_position())) {
2878 click_on_window = sw.window;
2879
2880 int close_h_ofs = sw.window->theme_cache.close_h_offset;
2881 int close_v_ofs = sw.window->theme_cache.close_v_offset;
2882 Ref<Texture2D> close_icon = sw.window->theme_cache.close;
2883
2884 Rect2 close_rect;
2885 close_rect.position = Vector2(r.position.x + r.size.x - close_h_ofs, r.position.y - close_v_ofs);
2886 close_rect.size = close_icon->get_size();
2887
2888 if (gui.subwindow_focused != sw.window) {
2889 // Refocus.
2890 _sub_window_grab_focus(sw.window);
2891 }
2892
2893 if (close_rect.has_point(mb->get_position())) {
2894 gui.subwindow_drag = SUB_WINDOW_DRAG_CLOSE;
2895 gui.subwindow_drag_close_inside = true; // Starts inside.
2896 gui.subwindow_drag_close_rect = close_rect;
2897 } else {
2898 gui.subwindow_drag = SUB_WINDOW_DRAG_MOVE;
2899 }
2900
2901 gui.subwindow_drag_from = mb->get_position();
2902 gui.subwindow_drag_pos = sw.window->get_position();
2903
2904 _sub_window_update(sw.window);
2905 } else {
2906 gui.subwindow_resize_mode = _sub_window_get_resize_margin(sw.window, mb->get_position());
2907 if (gui.subwindow_resize_mode != SUB_WINDOW_RESIZE_DISABLED) {
2908 if (gui.subwindow_focused != sw.window) {
2909 // Refocus.
2910 _sub_window_grab_focus(sw.window);
2911 }
2912
2913 gui.subwindow_resize_from_rect = r;
2914 gui.subwindow_drag_from = mb->get_position();
2915 gui.subwindow_drag = SUB_WINDOW_DRAG_RESIZE;
2916 click_on_window = sw.window;
2917 }
2918 }
2919 }
2920 if (!click_on_window && r.has_point(mb->get_position())) {
2921 // Clicked, see if it needs to fetch focus.
2922 if (gui.subwindow_focused != sw.window) {
2923 // Refocus.
2924 _sub_window_grab_focus(sw.window);
2925 }
2926
2927 click_on_window = sw.window;
2928 }
2929
2930 if (click_on_window) {
2931 break;
2932 }
2933 }
2934
2935 gui.currently_dragged_subwindow = click_on_window;
2936
2937 if (!click_on_window && gui.subwindow_focused) {
2938 // No window found and clicked, remove focus.
2939 _sub_window_grab_focus(nullptr);
2940 }
2941 }
2942
2943 if (gui.subwindow_focused) {
2944 Ref<InputEventMouseMotion> mm = p_event;
2945 if (mm.is_valid()) {
2946 SubWindowResize resize = _sub_window_get_resize_margin(gui.subwindow_focused, mm->get_position());
2947 if (resize != SUB_WINDOW_RESIZE_DISABLED) {
2948 DisplayServer::CursorShape shapes[SUB_WINDOW_RESIZE_MAX] = {
2949 DisplayServer::CURSOR_ARROW,
2950 DisplayServer::CURSOR_FDIAGSIZE,
2951 DisplayServer::CURSOR_VSIZE,
2952 DisplayServer::CURSOR_BDIAGSIZE,
2953 DisplayServer::CURSOR_HSIZE,
2954 DisplayServer::CURSOR_HSIZE,
2955 DisplayServer::CURSOR_BDIAGSIZE,
2956 DisplayServer::CURSOR_VSIZE,
2957 DisplayServer::CURSOR_FDIAGSIZE
2958 };
2959
2960 if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_CURSOR_SHAPE)) {
2961 DisplayServer::get_singleton()->cursor_set_shape(shapes[resize]);
2962 }
2963
2964 return true; // Reserved for showing the resize cursor.
2965 }
2966 }
2967 }
2968
2969 if (gui.subwindow_drag != SUB_WINDOW_DRAG_DISABLED) {
2970 return true; // Dragging, don't pass the event.
2971 }
2972
2973 if (!gui.subwindow_focused) {
2974 return false;
2975 }
2976
2977 Transform2D window_ofs;
2978 window_ofs.set_origin(-gui.subwindow_focused->get_position());
2979
2980 Ref<InputEvent> ev = p_event->xformed_by(window_ofs);
2981
2982 gui.subwindow_focused->_window_input(ev);
2983
2984 return true;
2985}
2986
2987void Viewport::_update_mouse_over() {
2988 // Update gui.mouse_over and gui.subwindow_over in all Viewports.
2989 // Send necessary mouse_enter/mouse_exit signals and the MOUSE_ENTER/MOUSE_EXIT notifications for every Viewport in the SceneTree.
2990
2991 if (is_attached_in_viewport()) {
2992 // Execute this function only, when it is processed by a native Window or a SubViewport, that has no SubViewportContainer as parent.
2993 return;
2994 }
2995
2996 if (get_tree()->get_root()->is_embedding_subwindows() || is_sub_viewport()) {
2997 // Use embedder logic for calculating mouse position.
2998 _update_mouse_over(gui.last_mouse_pos);
2999 } else {
3000 // Native Window: Use DisplayServer logic for calculating mouse position.
3001 Window *receiving_window = get_tree()->get_root()->gui.windowmanager_window_over;
3002 if (!receiving_window) {
3003 return;
3004 }
3005
3006 Vector2 pos = DisplayServer::get_singleton()->mouse_get_position() - receiving_window->get_position();
3007 pos = receiving_window->get_final_transform().affine_inverse().xform(pos);
3008
3009 receiving_window->_update_mouse_over(pos);
3010 }
3011}
3012
3013void Viewport::_update_mouse_over(Vector2 p_pos) {
3014 // Look for embedded windows at mouse position.
3015 if (is_embedding_subwindows()) {
3016 for (int i = gui.sub_windows.size() - 1; i >= 0; i--) {
3017 Window *sw = gui.sub_windows[i].window;
3018 Rect2 swrect = Rect2(sw->get_position(), sw->get_size());
3019 Rect2 swrect_border = swrect;
3020
3021 if (!sw->get_flag(Window::FLAG_BORDERLESS)) {
3022 int title_height = sw->theme_cache.title_height;
3023 int margin = sw->theme_cache.resize_margin;
3024 swrect_border.position.y -= title_height + margin;
3025 swrect_border.size.y += title_height + margin * 2;
3026 swrect_border.position.x -= margin;
3027 swrect_border.size.x += margin * 2;
3028 }
3029
3030 if (swrect_border.has_point(p_pos)) {
3031 if (gui.mouse_over) {
3032 _drop_mouse_over();
3033 } else if (!gui.subwindow_over) {
3034 _drop_physics_mouseover();
3035 }
3036 if (swrect.has_point(p_pos)) {
3037 if (sw != gui.subwindow_over) {
3038 if (gui.subwindow_over) {
3039 gui.subwindow_over->_mouse_leave_viewport();
3040 }
3041 gui.subwindow_over = sw;
3042 if (!sw->is_input_disabled()) {
3043 sw->_propagate_window_notification(sw, NOTIFICATION_WM_MOUSE_ENTER);
3044 }
3045 }
3046 if (!sw->is_input_disabled()) {
3047 sw->_update_mouse_over(sw->get_final_transform().affine_inverse().xform(p_pos - sw->get_position()));
3048 }
3049 } else {
3050 if (gui.subwindow_over) {
3051 gui.subwindow_over->_mouse_leave_viewport();
3052 gui.subwindow_over = nullptr;
3053 }
3054 }
3055 return;
3056 }
3057 }
3058
3059 if (gui.subwindow_over) {
3060 // Take care of moving mouse out of any embedded Window.
3061 gui.subwindow_over->_mouse_leave_viewport();
3062 gui.subwindow_over = nullptr;
3063 }
3064 }
3065
3066 // Look for Controls at mouse position.
3067 Control *over = gui_find_control(p_pos);
3068 bool notify_embedded_viewports = false;
3069 if (over != gui.mouse_over) {
3070 if (gui.mouse_over) {
3071 _drop_mouse_over();
3072 } else {
3073 _drop_physics_mouseover();
3074 }
3075
3076 gui.mouse_over = over;
3077 if (over) {
3078 over->notification(Control::NOTIFICATION_MOUSE_ENTER);
3079 notify_embedded_viewports = true;
3080 }
3081 }
3082
3083 if (over) {
3084 SubViewportContainer *c = Object::cast_to<SubViewportContainer>(over);
3085 if (!c) {
3086 return;
3087 }
3088 Vector2 pos = c->get_global_transform_with_canvas().affine_inverse().xform(p_pos);
3089 if (c->is_stretch_enabled()) {
3090 pos /= c->get_stretch_shrink();
3091 }
3092
3093 for (int i = 0; i < c->get_child_count(); i++) {
3094 SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3095 if (!v || v->is_input_disabled()) {
3096 continue;
3097 }
3098 if (notify_embedded_viewports) {
3099 v->notification(NOTIFICATION_VP_MOUSE_ENTER);
3100 }
3101 v->_update_mouse_over(v->get_final_transform().affine_inverse().xform(pos));
3102 }
3103 }
3104}
3105
3106void Viewport::_mouse_leave_viewport() {
3107 if (!is_inside_tree() || is_input_disabled()) {
3108 return;
3109 }
3110 if (gui.subwindow_over) {
3111 gui.subwindow_over->_mouse_leave_viewport();
3112 gui.subwindow_over = nullptr;
3113 } else if (gui.mouse_over) {
3114 _drop_mouse_over();
3115 }
3116 notification(NOTIFICATION_VP_MOUSE_EXIT);
3117}
3118
3119void Viewport::_drop_mouse_over() {
3120 _gui_cancel_tooltip();
3121 SubViewportContainer *c = Object::cast_to<SubViewportContainer>(gui.mouse_over);
3122 if (c) {
3123 for (int i = 0; i < c->get_child_count(); i++) {
3124 SubViewport *v = Object::cast_to<SubViewport>(c->get_child(i));
3125 if (!v) {
3126 continue;
3127 }
3128 v->_mouse_leave_viewport();
3129 }
3130 }
3131 if (gui.mouse_over->is_inside_tree()) {
3132 gui.mouse_over->notification(Control::NOTIFICATION_MOUSE_EXIT);
3133 }
3134 gui.mouse_over = nullptr;
3135}
3136
3137void Viewport::push_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
3138 ERR_MAIN_THREAD_GUARD;
3139 ERR_FAIL_COND(!is_inside_tree());
3140 ERR_FAIL_COND(p_event.is_null());
3141
3142 if (disable_input) {
3143 return;
3144 }
3145
3146 if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3147 return;
3148 }
3149
3150 local_input_handled = false;
3151
3152 Ref<InputEvent> ev;
3153 if (!p_local_coords) {
3154 ev = _make_input_local(p_event);
3155 } else {
3156 ev = p_event;
3157 }
3158
3159 Ref<InputEventMouse> me = ev;
3160 if (me.is_valid()) {
3161 gui.last_mouse_pos = me->get_position();
3162
3163 _update_mouse_over();
3164 }
3165
3166 if (is_embedding_subwindows() && _sub_windows_forward_input(ev)) {
3167 set_input_as_handled();
3168 return;
3169 }
3170
3171 if (!_can_consume_input_events()) {
3172 return;
3173 }
3174
3175 if (!is_input_handled()) {
3176 ERR_FAIL_COND(!is_inside_tree());
3177 get_tree()->_call_input_pause(input_group, SceneTree::CALL_INPUT_TYPE_INPUT, ev, this); //not a bug, must happen before GUI, order is _input -> gui input -> _unhandled input
3178 }
3179
3180 if (!is_input_handled()) {
3181 ERR_FAIL_COND(!is_inside_tree());
3182 _gui_input_event(ev);
3183 } else {
3184 // Cleanup internal GUI state after accepting event during _input().
3185 _gui_cleanup_internal_state(ev);
3186 }
3187
3188 if (!is_input_handled()) {
3189 _push_unhandled_input_internal(ev);
3190 }
3191
3192 event_count++;
3193}
3194
3195#ifndef DISABLE_DEPRECATED
3196void Viewport::push_unhandled_input(const Ref<InputEvent> &p_event, bool p_local_coords) {
3197 ERR_MAIN_THREAD_GUARD;
3198 WARN_DEPRECATED_MSG(R"*(The "push_unhandled_input()" method is deprecated, use "push_input()" instead.)*");
3199 ERR_FAIL_COND(!is_inside_tree());
3200 ERR_FAIL_COND(p_event.is_null());
3201
3202 local_input_handled = false;
3203
3204 if (disable_input || !_can_consume_input_events()) {
3205 return;
3206 }
3207
3208 if (Engine::get_singleton()->is_editor_hint() && get_tree()->get_edited_scene_root() && get_tree()->get_edited_scene_root()->is_ancestor_of(this)) {
3209 return;
3210 }
3211
3212 Ref<InputEvent> ev;
3213 if (!p_local_coords) {
3214 ev = _make_input_local(p_event);
3215 } else {
3216 ev = p_event;
3217 }
3218
3219 _push_unhandled_input_internal(ev);
3220}
3221#endif // DISABLE_DEPRECATED
3222
3223void Viewport::_push_unhandled_input_internal(const Ref<InputEvent> &p_event) {
3224 // Shortcut Input.
3225 if (Object::cast_to<InputEventKey>(*p_event) != nullptr || Object::cast_to<InputEventShortcut>(*p_event) != nullptr || Object::cast_to<InputEventJoypadButton>(*p_event) != nullptr) {
3226 ERR_FAIL_COND(!is_inside_tree());
3227 get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event, this);
3228 }
3229
3230 // Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
3231 if (!is_input_handled() && (Object::cast_to<InputEventKey>(*p_event) != nullptr)) {
3232 ERR_FAIL_COND(!is_inside_tree());
3233 get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event, this);
3234 }
3235
3236 // Unhandled Input.
3237 if (!is_input_handled()) {
3238 ERR_FAIL_COND(!is_inside_tree());
3239 get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
3240 }
3241
3242 if (physics_object_picking && !is_input_handled()) {
3243 if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED &&
3244 (Object::cast_to<InputEventMouse>(*p_event) ||
3245 Object::cast_to<InputEventScreenDrag>(*p_event) ||
3246 Object::cast_to<InputEventScreenTouch>(*p_event)
3247
3248 )) {
3249 physics_picking_events.push_back(p_event);
3250 set_input_as_handled();
3251 }
3252 }
3253}
3254
3255void Viewport::set_physics_object_picking(bool p_enable) {
3256 ERR_MAIN_THREAD_GUARD;
3257 physics_object_picking = p_enable;
3258 if (physics_object_picking) {
3259 add_to_group("_picking_viewports");
3260 } else {
3261 physics_picking_events.clear();
3262 if (is_in_group("_picking_viewports")) {
3263 remove_from_group("_picking_viewports");
3264 }
3265 }
3266}
3267
3268bool Viewport::get_physics_object_picking() {
3269 ERR_READ_THREAD_GUARD_V(false);
3270 return physics_object_picking;
3271}
3272
3273void Viewport::set_physics_object_picking_sort(bool p_enable) {
3274 ERR_MAIN_THREAD_GUARD;
3275 physics_object_picking_sort = p_enable;
3276}
3277
3278bool Viewport::get_physics_object_picking_sort() {
3279 ERR_READ_THREAD_GUARD_V(false);
3280 return physics_object_picking_sort;
3281}
3282
3283Vector2 Viewport::get_camera_coords(const Vector2 &p_viewport_coords) const {
3284 ERR_READ_THREAD_GUARD_V(Vector2());
3285 Transform2D xf = stretch_transform * global_canvas_transform;
3286 return xf.xform(p_viewport_coords);
3287}
3288
3289Vector2 Viewport::get_camera_rect_size() const {
3290 ERR_READ_THREAD_GUARD_V(Vector2());
3291 return size;
3292}
3293
3294void Viewport::set_disable_input(bool p_disable) {
3295 ERR_MAIN_THREAD_GUARD;
3296 if (p_disable == disable_input) {
3297 return;
3298 }
3299 if (p_disable) {
3300 _drop_mouse_focus();
3301 _mouse_leave_viewport();
3302 _gui_cancel_tooltip();
3303 }
3304 disable_input = p_disable;
3305}
3306
3307bool Viewport::is_input_disabled() const {
3308 ERR_READ_THREAD_GUARD_V(false);
3309 return disable_input;
3310}
3311
3312Variant Viewport::gui_get_drag_data() const {
3313 ERR_READ_THREAD_GUARD_V(Variant());
3314 return gui.drag_data;
3315}
3316
3317PackedStringArray Viewport::get_configuration_warnings() const {
3318 ERR_MAIN_THREAD_GUARD_V(PackedStringArray());
3319 PackedStringArray warnings = Node::get_configuration_warnings();
3320
3321 if (size.x <= 1 || size.y <= 1) {
3322 warnings.push_back(RTR("The Viewport size must be greater than or equal to 2 pixels on both dimensions to render anything."));
3323 }
3324 return warnings;
3325}
3326
3327void Viewport::gui_reset_canvas_sort_index() {
3328 ERR_MAIN_THREAD_GUARD;
3329 gui.canvas_sort_index = 0;
3330}
3331
3332int Viewport::gui_get_canvas_sort_index() {
3333 ERR_MAIN_THREAD_GUARD_V(0);
3334 return gui.canvas_sort_index++;
3335}
3336
3337void Viewport::gui_release_focus() {
3338 ERR_MAIN_THREAD_GUARD;
3339 if (gui.key_focus) {
3340 Control *f = gui.key_focus;
3341 gui.key_focus = nullptr;
3342 f->notification(Control::NOTIFICATION_FOCUS_EXIT, true);
3343 f->queue_redraw();
3344 }
3345}
3346
3347Control *Viewport::gui_get_focus_owner() const {
3348 ERR_READ_THREAD_GUARD_V(nullptr);
3349 return gui.key_focus;
3350}
3351
3352void Viewport::set_msaa_2d(MSAA p_msaa) {
3353 ERR_MAIN_THREAD_GUARD;
3354 ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3355 if (msaa_2d == p_msaa) {
3356 return;
3357 }
3358 msaa_2d = p_msaa;
3359 RS::get_singleton()->viewport_set_msaa_2d(viewport, RS::ViewportMSAA(p_msaa));
3360}
3361
3362Viewport::MSAA Viewport::get_msaa_2d() const {
3363 ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3364 return msaa_2d;
3365}
3366
3367void Viewport::set_msaa_3d(MSAA p_msaa) {
3368 ERR_MAIN_THREAD_GUARD;
3369 ERR_FAIL_INDEX(p_msaa, MSAA_MAX);
3370 if (msaa_3d == p_msaa) {
3371 return;
3372 }
3373 msaa_3d = p_msaa;
3374 RS::get_singleton()->viewport_set_msaa_3d(viewport, RS::ViewportMSAA(p_msaa));
3375}
3376
3377Viewport::MSAA Viewport::get_msaa_3d() const {
3378 ERR_READ_THREAD_GUARD_V(MSAA_DISABLED);
3379 return msaa_3d;
3380}
3381
3382void Viewport::set_screen_space_aa(ScreenSpaceAA p_screen_space_aa) {
3383 ERR_MAIN_THREAD_GUARD;
3384 ERR_FAIL_INDEX(p_screen_space_aa, SCREEN_SPACE_AA_MAX);
3385 if (screen_space_aa == p_screen_space_aa) {
3386 return;
3387 }
3388 screen_space_aa = p_screen_space_aa;
3389 RS::get_singleton()->viewport_set_screen_space_aa(viewport, RS::ViewportScreenSpaceAA(p_screen_space_aa));
3390}
3391
3392Viewport::ScreenSpaceAA Viewport::get_screen_space_aa() const {
3393 ERR_READ_THREAD_GUARD_V(SCREEN_SPACE_AA_DISABLED);
3394 return screen_space_aa;
3395}
3396
3397void Viewport::set_use_taa(bool p_use_taa) {
3398 ERR_MAIN_THREAD_GUARD;
3399 if (use_taa == p_use_taa) {
3400 return;
3401 }
3402 use_taa = p_use_taa;
3403 RS::get_singleton()->viewport_set_use_taa(viewport, p_use_taa);
3404}
3405
3406bool Viewport::is_using_taa() const {
3407 ERR_READ_THREAD_GUARD_V(false);
3408 return use_taa;
3409}
3410
3411void Viewport::set_use_debanding(bool p_use_debanding) {
3412 ERR_MAIN_THREAD_GUARD;
3413 if (use_debanding == p_use_debanding) {
3414 return;
3415 }
3416 use_debanding = p_use_debanding;
3417 RS::get_singleton()->viewport_set_use_debanding(viewport, p_use_debanding);
3418}
3419
3420bool Viewport::is_using_debanding() const {
3421 ERR_READ_THREAD_GUARD_V(false);
3422 return use_debanding;
3423}
3424
3425void Viewport::set_mesh_lod_threshold(float p_pixels) {
3426 ERR_MAIN_THREAD_GUARD;
3427 mesh_lod_threshold = p_pixels;
3428 RS::get_singleton()->viewport_set_mesh_lod_threshold(viewport, mesh_lod_threshold);
3429}
3430
3431float Viewport::get_mesh_lod_threshold() const {
3432 ERR_READ_THREAD_GUARD_V(0);
3433 return mesh_lod_threshold;
3434}
3435
3436void Viewport::set_use_occlusion_culling(bool p_use_occlusion_culling) {
3437 ERR_MAIN_THREAD_GUARD;
3438 if (use_occlusion_culling == p_use_occlusion_culling) {
3439 return;
3440 }
3441
3442 use_occlusion_culling = p_use_occlusion_culling;
3443 RS::get_singleton()->viewport_set_use_occlusion_culling(viewport, p_use_occlusion_culling);
3444
3445 notify_property_list_changed();
3446}
3447
3448bool Viewport::is_using_occlusion_culling() const {
3449 ERR_READ_THREAD_GUARD_V(false);
3450 return use_occlusion_culling;
3451}
3452
3453void Viewport::set_debug_draw(DebugDraw p_debug_draw) {
3454 ERR_MAIN_THREAD_GUARD;
3455 debug_draw = p_debug_draw;
3456 RS::get_singleton()->viewport_set_debug_draw(viewport, RS::ViewportDebugDraw(p_debug_draw));
3457}
3458
3459Viewport::DebugDraw Viewport::get_debug_draw() const {
3460 ERR_READ_THREAD_GUARD_V(DEBUG_DRAW_DISABLED);
3461 return debug_draw;
3462}
3463
3464int Viewport::get_render_info(RenderInfoType p_type, RenderInfo p_info) {
3465 ERR_READ_THREAD_GUARD_V(0);
3466 return RS::get_singleton()->viewport_get_render_info(viewport, RS::ViewportRenderInfoType(p_type), RS::ViewportRenderInfo(p_info));
3467}
3468
3469void Viewport::set_snap_controls_to_pixels(bool p_enable) {
3470 ERR_MAIN_THREAD_GUARD;
3471 snap_controls_to_pixels = p_enable;
3472}
3473
3474bool Viewport::is_snap_controls_to_pixels_enabled() const {
3475 ERR_READ_THREAD_GUARD_V(false);
3476 return snap_controls_to_pixels;
3477}
3478
3479void Viewport::set_snap_2d_transforms_to_pixel(bool p_enable) {
3480 ERR_MAIN_THREAD_GUARD;
3481 snap_2d_transforms_to_pixel = p_enable;
3482 RS::get_singleton()->viewport_set_snap_2d_transforms_to_pixel(viewport, snap_2d_transforms_to_pixel);
3483}
3484
3485bool Viewport::is_snap_2d_transforms_to_pixel_enabled() const {
3486 ERR_READ_THREAD_GUARD_V(false);
3487 return snap_2d_transforms_to_pixel;
3488}
3489
3490void Viewport::set_snap_2d_vertices_to_pixel(bool p_enable) {
3491 ERR_MAIN_THREAD_GUARD;
3492 snap_2d_vertices_to_pixel = p_enable;
3493 RS::get_singleton()->viewport_set_snap_2d_vertices_to_pixel(viewport, snap_2d_vertices_to_pixel);
3494}
3495
3496bool Viewport::is_snap_2d_vertices_to_pixel_enabled() const {
3497 ERR_READ_THREAD_GUARD_V(false);
3498 return snap_2d_vertices_to_pixel;
3499}
3500
3501bool Viewport::gui_is_dragging() const {
3502 ERR_READ_THREAD_GUARD_V(false);
3503 return gui.dragging;
3504}
3505
3506bool Viewport::gui_is_drag_successful() const {
3507 ERR_READ_THREAD_GUARD_V(false);
3508 return gui.drag_successful;
3509}
3510
3511void Viewport::set_input_as_handled() {
3512 ERR_MAIN_THREAD_GUARD;
3513 if (!handle_input_locally) {
3514 ERR_FAIL_COND(!is_inside_tree());
3515 Viewport *vp = this;
3516 while (true) {
3517 if (Object::cast_to<Window>(vp)) {
3518 break;
3519 }
3520 if (!vp->get_parent()) {
3521 break;
3522 }
3523 vp = vp->get_parent()->get_viewport();
3524 }
3525 if (vp != this) {
3526 vp->set_input_as_handled();
3527 return;
3528 }
3529 }
3530
3531 local_input_handled = true;
3532}
3533
3534bool Viewport::is_input_handled() const {
3535 ERR_READ_THREAD_GUARD_V(false);
3536 if (!handle_input_locally) {
3537 ERR_FAIL_COND_V(!is_inside_tree(), false);
3538 const Viewport *vp = this;
3539 while (true) {
3540 if (Object::cast_to<Window>(vp)) {
3541 break;
3542 }
3543 if (!vp->get_parent()) {
3544 break;
3545 }
3546 vp = vp->get_parent()->get_viewport();
3547 }
3548 if (vp != this) {
3549 return vp->is_input_handled();
3550 }
3551 }
3552 return local_input_handled;
3553}
3554
3555void Viewport::set_handle_input_locally(bool p_enable) {
3556 ERR_MAIN_THREAD_GUARD;
3557 handle_input_locally = p_enable;
3558}
3559
3560bool Viewport::is_handling_input_locally() const {
3561 ERR_READ_THREAD_GUARD_V(false);
3562 return handle_input_locally;
3563}
3564
3565void Viewport::set_default_canvas_item_texture_filter(DefaultCanvasItemTextureFilter p_filter) {
3566 ERR_MAIN_THREAD_GUARD;
3567 ERR_FAIL_INDEX(p_filter, DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
3568
3569 if (default_canvas_item_texture_filter == p_filter) {
3570 return;
3571 }
3572 default_canvas_item_texture_filter = p_filter;
3573 switch (default_canvas_item_texture_filter) {
3574 case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST:
3575 RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3576 break;
3577 case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR:
3578 RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
3579 break;
3580 case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS:
3581 RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
3582 break;
3583 case DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS:
3584 RS::get_singleton()->viewport_set_default_canvas_item_texture_filter(viewport, RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
3585 break;
3586 default: {
3587 }
3588 }
3589}
3590
3591Viewport::DefaultCanvasItemTextureFilter Viewport::get_default_canvas_item_texture_filter() const {
3592 ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
3593 return default_canvas_item_texture_filter;
3594}
3595
3596void Viewport::set_default_canvas_item_texture_repeat(DefaultCanvasItemTextureRepeat p_repeat) {
3597 ERR_MAIN_THREAD_GUARD;
3598 ERR_FAIL_INDEX(p_repeat, DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
3599
3600 if (default_canvas_item_texture_repeat == p_repeat) {
3601 return;
3602 }
3603
3604 default_canvas_item_texture_repeat = p_repeat;
3605
3606 switch (default_canvas_item_texture_repeat) {
3607 case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED:
3608 RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
3609 break;
3610 case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED:
3611 RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
3612 break;
3613 case DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR:
3614 RS::get_singleton()->viewport_set_default_canvas_item_texture_repeat(viewport, RS::CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
3615 break;
3616 default: {
3617 }
3618 }
3619}
3620
3621Viewport::DefaultCanvasItemTextureRepeat Viewport::get_default_canvas_item_texture_repeat() const {
3622 ERR_READ_THREAD_GUARD_V(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
3623 return default_canvas_item_texture_repeat;
3624}
3625
3626void Viewport::set_vrs_mode(Viewport::VRSMode p_vrs_mode) {
3627 ERR_MAIN_THREAD_GUARD;
3628 // Note, set this even if not supported on this hardware, it will only be used if it is but we want to save the value as set by the user.
3629 vrs_mode = p_vrs_mode;
3630
3631 switch (p_vrs_mode) {
3632 case VRS_TEXTURE: {
3633 RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_TEXTURE);
3634 } break;
3635 case VRS_XR: {
3636 RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_XR);
3637 } break;
3638 default: {
3639 RS::get_singleton()->viewport_set_vrs_mode(viewport, RS::VIEWPORT_VRS_DISABLED);
3640 } break;
3641 }
3642
3643 notify_property_list_changed();
3644}
3645
3646Viewport::VRSMode Viewport::get_vrs_mode() const {
3647 ERR_READ_THREAD_GUARD_V(VRS_DISABLED);
3648 return vrs_mode;
3649}
3650
3651void Viewport::set_vrs_texture(Ref<Texture2D> p_texture) {
3652 ERR_MAIN_THREAD_GUARD;
3653 vrs_texture = p_texture;
3654
3655 // TODO need to add something here in case the RID changes
3656 RID tex = p_texture.is_valid() ? p_texture->get_rid() : RID();
3657 RS::get_singleton()->viewport_set_vrs_texture(viewport, tex);
3658}
3659
3660Ref<Texture2D> Viewport::get_vrs_texture() const {
3661 ERR_READ_THREAD_GUARD_V(Ref<Texture2D>());
3662 return vrs_texture;
3663}
3664
3665DisplayServer::WindowID Viewport::get_window_id() const {
3666 ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
3667 return DisplayServer::MAIN_WINDOW_ID;
3668}
3669
3670Viewport *Viewport::get_parent_viewport() const {
3671 ERR_READ_THREAD_GUARD_V(nullptr);
3672 ERR_FAIL_COND_V(!is_inside_tree(), nullptr);
3673 if (!get_parent()) {
3674 return nullptr; //root viewport
3675 }
3676
3677 return get_parent()->get_viewport();
3678}
3679
3680void Viewport::set_embedding_subwindows(bool p_embed) {
3681 ERR_THREAD_GUARD;
3682 gui.embed_subwindows_hint = p_embed;
3683}
3684
3685bool Viewport::is_embedding_subwindows() const {
3686 ERR_READ_THREAD_GUARD_V(false);
3687 return gui.embed_subwindows_hint;
3688}
3689
3690TypedArray<Window> Viewport::get_embedded_subwindows() const {
3691 TypedArray<Window> windows;
3692 for (int i = 0; i < gui.sub_windows.size(); i++) {
3693 windows.append(gui.sub_windows[i].window);
3694 }
3695
3696 return windows;
3697}
3698
3699void Viewport::subwindow_set_popup_safe_rect(Window *p_window, const Rect2i &p_rect) {
3700 int index = _sub_window_find(p_window);
3701 ERR_FAIL_COND(index == -1);
3702
3703 gui.sub_windows.write[index].parent_safe_rect = p_rect;
3704}
3705
3706Rect2i Viewport::subwindow_get_popup_safe_rect(Window *p_window) const {
3707 int index = _sub_window_find(p_window);
3708 // FIXME: Re-enable ERR_FAIL_COND after rewriting embedded window popup closing.
3709 // Currently it is expected, that index == -1 can happen.
3710 if (index == -1) {
3711 return Rect2i();
3712 }
3713 // ERR_FAIL_COND_V(index == -1, Rect2i());
3714
3715 return gui.sub_windows[index].parent_safe_rect;
3716}
3717
3718void Viewport::pass_mouse_focus_to(Viewport *p_viewport, Control *p_control) {
3719 ERR_MAIN_THREAD_GUARD;
3720 ERR_FAIL_NULL(p_viewport);
3721 ERR_FAIL_NULL(p_control);
3722
3723 if (gui.mouse_focus) {
3724 p_viewport->gui.mouse_focus = p_control;
3725 p_viewport->gui.mouse_focus_mask = gui.mouse_focus_mask;
3726 p_viewport->gui.key_focus = p_control;
3727 p_viewport->gui.forced_mouse_focus = true;
3728
3729 gui.mouse_focus = nullptr;
3730 gui.forced_mouse_focus = false;
3731 gui.mouse_focus_mask.clear();
3732 }
3733}
3734
3735void Viewport::set_sdf_oversize(SDFOversize p_sdf_oversize) {
3736 ERR_MAIN_THREAD_GUARD;
3737 ERR_FAIL_INDEX(p_sdf_oversize, SDF_OVERSIZE_MAX);
3738 sdf_oversize = p_sdf_oversize;
3739 RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
3740}
3741
3742Viewport::SDFOversize Viewport::get_sdf_oversize() const {
3743 ERR_READ_THREAD_GUARD_V(SDF_OVERSIZE_100_PERCENT);
3744 return sdf_oversize;
3745}
3746
3747void Viewport::set_sdf_scale(SDFScale p_sdf_scale) {
3748 ERR_MAIN_THREAD_GUARD;
3749 ERR_FAIL_INDEX(p_sdf_scale, SDF_SCALE_MAX);
3750 sdf_scale = p_sdf_scale;
3751 RS::get_singleton()->viewport_set_sdf_oversize_and_scale(viewport, RS::ViewportSDFOversize(sdf_oversize), RS::ViewportSDFScale(sdf_scale));
3752}
3753
3754Viewport::SDFScale Viewport::get_sdf_scale() const {
3755 ERR_READ_THREAD_GUARD_V(SDF_SCALE_100_PERCENT);
3756 return sdf_scale;
3757}
3758
3759Transform2D Viewport::get_screen_transform() const {
3760 ERR_READ_THREAD_GUARD_V(Transform2D());
3761 return get_screen_transform_internal();
3762}
3763
3764Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) const {
3765 ERR_READ_THREAD_GUARD_V(Transform2D());
3766 return get_final_transform();
3767}
3768
3769void Viewport::update_mouse_cursor_state() {
3770 // Updates need to happen in Window, because SubViewportContainers might be hidden behind other Controls.
3771 Window *base_window = get_base_window();
3772 if (base_window) {
3773 base_window->update_mouse_cursor_state();
3774 }
3775}
3776
3777void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
3778 ERR_MAIN_THREAD_GUARD;
3779 canvas_cull_mask = p_canvas_cull_mask;
3780 RenderingServer::get_singleton()->viewport_set_canvas_cull_mask(viewport, canvas_cull_mask);
3781}
3782
3783uint32_t Viewport::get_canvas_cull_mask() const {
3784 ERR_READ_THREAD_GUARD_V(0);
3785 return canvas_cull_mask;
3786}
3787
3788void Viewport::set_canvas_cull_mask_bit(uint32_t p_layer, bool p_enable) {
3789 ERR_MAIN_THREAD_GUARD;
3790 ERR_FAIL_UNSIGNED_INDEX(p_layer, 32);
3791 if (p_enable) {
3792 set_canvas_cull_mask(canvas_cull_mask | (1 << p_layer));
3793 } else {
3794 set_canvas_cull_mask(canvas_cull_mask & (~(1 << p_layer)));
3795 }
3796}
3797
3798bool Viewport::get_canvas_cull_mask_bit(uint32_t p_layer) const {
3799 ERR_READ_THREAD_GUARD_V(false);
3800 ERR_FAIL_UNSIGNED_INDEX_V(p_layer, 32, false);
3801 return (canvas_cull_mask & (1 << p_layer));
3802}
3803
3804#ifndef _3D_DISABLED
3805AudioListener3D *Viewport::get_audio_listener_3d() const {
3806 ERR_READ_THREAD_GUARD_V(nullptr);
3807 return audio_listener_3d;
3808}
3809
3810void Viewport::set_as_audio_listener_3d(bool p_enable) {
3811 ERR_MAIN_THREAD_GUARD;
3812 if (p_enable == is_audio_listener_3d_enabled) {
3813 return;
3814 }
3815
3816 is_audio_listener_3d_enabled = p_enable;
3817 _update_audio_listener_3d();
3818}
3819
3820bool Viewport::is_audio_listener_3d() const {
3821 ERR_READ_THREAD_GUARD_V(false);
3822 return is_audio_listener_3d_enabled;
3823}
3824
3825void Viewport::_update_audio_listener_3d() {
3826 if (AudioServer::get_singleton()) {
3827 AudioServer::get_singleton()->notify_listener_changed();
3828 }
3829}
3830
3831void Viewport::_listener_transform_3d_changed_notify() {
3832}
3833
3834void Viewport::_audio_listener_3d_set(AudioListener3D *p_listener) {
3835 if (audio_listener_3d == p_listener) {
3836 return;
3837 }
3838
3839 audio_listener_3d = p_listener;
3840
3841 _update_audio_listener_3d();
3842 _listener_transform_3d_changed_notify();
3843}
3844
3845bool Viewport::_audio_listener_3d_add(AudioListener3D *p_listener) {
3846 audio_listener_3d_set.insert(p_listener);
3847 return audio_listener_3d_set.size() == 1;
3848}
3849
3850void Viewport::_audio_listener_3d_remove(AudioListener3D *p_listener) {
3851 audio_listener_3d_set.erase(p_listener);
3852 if (audio_listener_3d == p_listener) {
3853 audio_listener_3d = nullptr;
3854 }
3855}
3856
3857void Viewport::_audio_listener_3d_make_next_current(AudioListener3D *p_exclude) {
3858 if (audio_listener_3d_set.size() > 0) {
3859 for (AudioListener3D *E : audio_listener_3d_set) {
3860 if (p_exclude == E) {
3861 continue;
3862 }
3863 if (!E->is_inside_tree()) {
3864 continue;
3865 }
3866 if (audio_listener_3d != nullptr) {
3867 return;
3868 }
3869
3870 E->make_current();
3871 }
3872 } else {
3873 // Attempt to reset listener to the camera position.
3874 if (camera_3d != nullptr) {
3875 _update_audio_listener_3d();
3876 _camera_3d_transform_changed_notify();
3877 }
3878 }
3879}
3880
3881void Viewport::_collision_object_3d_input_event(CollisionObject3D *p_object, Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
3882 Transform3D object_transform = p_object->get_global_transform();
3883 Transform3D camera_transform = p_camera->get_global_transform();
3884 ObjectID id = p_object->get_instance_id();
3885
3886 // Avoid sending the fake event unnecessarily if nothing really changed in the context.
3887 if (object_transform == physics_last_object_transform && camera_transform == physics_last_camera_transform && physics_last_id == id) {
3888 Ref<InputEventMouseMotion> mm = p_input_event;
3889 if (mm.is_valid() && mm->get_device() == InputEvent::DEVICE_ID_INTERNAL) {
3890 return; // Discarded.
3891 }
3892 }
3893 p_object->_input_event_call(camera_3d, p_input_event, p_pos, p_normal, p_shape);
3894 physics_last_object_transform = object_transform;
3895 physics_last_camera_transform = camera_transform;
3896 physics_last_id = id;
3897}
3898
3899Camera3D *Viewport::get_camera_3d() const {
3900 ERR_READ_THREAD_GUARD_V(nullptr);
3901 return camera_3d;
3902}
3903
3904void Viewport::_camera_3d_transform_changed_notify() {
3905}
3906
3907void Viewport::_camera_3d_set(Camera3D *p_camera) {
3908 if (camera_3d == p_camera) {
3909 return;
3910 }
3911
3912 if (camera_3d) {
3913 camera_3d->notification(Camera3D::NOTIFICATION_LOST_CURRENT);
3914 }
3915
3916 camera_3d = p_camera;
3917
3918 if (!camera_3d_override) {
3919 if (camera_3d) {
3920 RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
3921 } else {
3922 RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
3923 }
3924 }
3925
3926 if (camera_3d) {
3927 camera_3d->notification(Camera3D::NOTIFICATION_BECAME_CURRENT);
3928 }
3929
3930 _update_audio_listener_3d();
3931 _camera_3d_transform_changed_notify();
3932}
3933
3934bool Viewport::_camera_3d_add(Camera3D *p_camera) {
3935 camera_3d_set.insert(p_camera);
3936 return camera_3d_set.size() == 1;
3937}
3938
3939void Viewport::_camera_3d_remove(Camera3D *p_camera) {
3940 camera_3d_set.erase(p_camera);
3941 if (camera_3d == p_camera) {
3942 camera_3d->notification(Camera3D::NOTIFICATION_LOST_CURRENT);
3943 camera_3d = nullptr;
3944 }
3945}
3946
3947void Viewport::_camera_3d_make_next_current(Camera3D *p_exclude) {
3948 for (Camera3D *E : camera_3d_set) {
3949 if (p_exclude == E) {
3950 continue;
3951 }
3952 if (!E->is_inside_tree()) {
3953 continue;
3954 }
3955 if (camera_3d != nullptr) {
3956 return;
3957 }
3958
3959 E->make_current();
3960 }
3961}
3962
3963void Viewport::enable_camera_3d_override(bool p_enable) {
3964 ERR_MAIN_THREAD_GUARD;
3965 if (p_enable == camera_3d_override) {
3966 return;
3967 }
3968
3969 if (p_enable) {
3970 camera_3d_override.rid = RenderingServer::get_singleton()->camera_create();
3971 } else {
3972 RenderingServer::get_singleton()->free(camera_3d_override.rid);
3973 camera_3d_override.rid = RID();
3974 }
3975
3976 if (p_enable) {
3977 RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d_override.rid);
3978 } else if (camera_3d) {
3979 RenderingServer::get_singleton()->viewport_attach_camera(viewport, camera_3d->get_camera());
3980 } else {
3981 RenderingServer::get_singleton()->viewport_attach_camera(viewport, RID());
3982 }
3983}
3984
3985void Viewport::set_camera_3d_override_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) {
3986 ERR_MAIN_THREAD_GUARD;
3987 if (camera_3d_override) {
3988 if (camera_3d_override.fov == p_fovy_degrees && camera_3d_override.z_near == p_z_near &&
3989 camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_PERSPECTIVE) {
3990 return;
3991 }
3992
3993 camera_3d_override.fov = p_fovy_degrees;
3994 camera_3d_override.z_near = p_z_near;
3995 camera_3d_override.z_far = p_z_far;
3996 camera_3d_override.projection = Camera3DOverrideData::PROJECTION_PERSPECTIVE;
3997
3998 RenderingServer::get_singleton()->camera_set_perspective(camera_3d_override.rid, camera_3d_override.fov, camera_3d_override.z_near, camera_3d_override.z_far);
3999 }
4000}
4001
4002void Viewport::set_camera_3d_override_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) {
4003 ERR_MAIN_THREAD_GUARD;
4004 if (camera_3d_override) {
4005 if (camera_3d_override.size == p_size && camera_3d_override.z_near == p_z_near &&
4006 camera_3d_override.z_far == p_z_far && camera_3d_override.projection == Camera3DOverrideData::PROJECTION_ORTHOGONAL) {
4007 return;
4008 }
4009
4010 camera_3d_override.size = p_size;
4011 camera_3d_override.z_near = p_z_near;
4012 camera_3d_override.z_far = p_z_far;
4013 camera_3d_override.projection = Camera3DOverrideData::PROJECTION_ORTHOGONAL;
4014
4015 RenderingServer::get_singleton()->camera_set_orthogonal(camera_3d_override.rid, camera_3d_override.size, camera_3d_override.z_near, camera_3d_override.z_far);
4016 }
4017}
4018
4019void Viewport::set_disable_3d(bool p_disable) {
4020 ERR_MAIN_THREAD_GUARD;
4021 disable_3d = p_disable;
4022 RenderingServer::get_singleton()->viewport_set_disable_3d(viewport, disable_3d);
4023}
4024
4025bool Viewport::is_3d_disabled() const {
4026 ERR_READ_THREAD_GUARD_V(false);
4027 return disable_3d;
4028}
4029
4030bool Viewport::is_camera_3d_override_enabled() const {
4031 ERR_READ_THREAD_GUARD_V(false);
4032 return camera_3d_override;
4033}
4034
4035void Viewport::set_camera_3d_override_transform(const Transform3D &p_transform) {
4036 ERR_MAIN_THREAD_GUARD;
4037 if (camera_3d_override) {
4038 camera_3d_override.transform = p_transform;
4039 RenderingServer::get_singleton()->camera_set_transform(camera_3d_override.rid, p_transform);
4040 }
4041}
4042
4043Transform3D Viewport::get_camera_3d_override_transform() const {
4044 ERR_READ_THREAD_GUARD_V(Transform3D());
4045 if (camera_3d_override) {
4046 return camera_3d_override.transform;
4047 }
4048
4049 return Transform3D();
4050}
4051
4052Ref<World3D> Viewport::get_world_3d() const {
4053 ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4054 return world_3d;
4055}
4056
4057Ref<World3D> Viewport::find_world_3d() const {
4058 ERR_READ_THREAD_GUARD_V(Ref<World3D>());
4059 if (own_world_3d.is_valid()) {
4060 return own_world_3d;
4061 } else if (world_3d.is_valid()) {
4062 return world_3d;
4063 } else if (parent) {
4064 return parent->find_world_3d();
4065 } else {
4066 return Ref<World3D>();
4067 }
4068}
4069
4070void Viewport::set_world_3d(const Ref<World3D> &p_world_3d) {
4071 ERR_MAIN_THREAD_GUARD;
4072 if (world_3d == p_world_3d) {
4073 return;
4074 }
4075
4076 if (is_inside_tree()) {
4077 _propagate_exit_world_3d(this);
4078 }
4079
4080 if (own_world_3d.is_valid() && world_3d.is_valid()) {
4081 world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4082 }
4083
4084 world_3d = p_world_3d;
4085
4086 if (own_world_3d.is_valid()) {
4087 if (world_3d.is_valid()) {
4088 own_world_3d = world_3d->duplicate();
4089 world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4090 } else {
4091 own_world_3d = Ref<World3D>(memnew(World3D));
4092 }
4093 }
4094
4095 if (is_inside_tree()) {
4096 _propagate_enter_world_3d(this);
4097 }
4098
4099 if (is_inside_tree()) {
4100 RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4101 }
4102
4103 _update_audio_listener_3d();
4104}
4105
4106void Viewport::_own_world_3d_changed() {
4107 ERR_FAIL_COND(world_3d.is_null());
4108 ERR_FAIL_COND(own_world_3d.is_null());
4109
4110 if (is_inside_tree()) {
4111 _propagate_exit_world_3d(this);
4112 }
4113
4114 own_world_3d = world_3d->duplicate();
4115
4116 if (is_inside_tree()) {
4117 _propagate_enter_world_3d(this);
4118 }
4119
4120 if (is_inside_tree()) {
4121 RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4122 }
4123
4124 _update_audio_listener_3d();
4125}
4126
4127void Viewport::set_use_own_world_3d(bool p_use_own_world_3d) {
4128 ERR_MAIN_THREAD_GUARD;
4129 if (p_use_own_world_3d == own_world_3d.is_valid()) {
4130 return;
4131 }
4132
4133 if (is_inside_tree()) {
4134 _propagate_exit_world_3d(this);
4135 }
4136
4137 if (p_use_own_world_3d) {
4138 if (world_3d.is_valid()) {
4139 own_world_3d = world_3d->duplicate();
4140 world_3d->connect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4141 } else {
4142 own_world_3d = Ref<World3D>(memnew(World3D));
4143 }
4144 } else {
4145 own_world_3d = Ref<World3D>();
4146 if (world_3d.is_valid()) {
4147 world_3d->disconnect_changed(callable_mp(this, &Viewport::_own_world_3d_changed));
4148 }
4149 }
4150
4151 if (is_inside_tree()) {
4152 _propagate_enter_world_3d(this);
4153 }
4154
4155 if (is_inside_tree()) {
4156 RenderingServer::get_singleton()->viewport_set_scenario(viewport, find_world_3d()->get_scenario());
4157 }
4158
4159 _update_audio_listener_3d();
4160}
4161
4162bool Viewport::is_using_own_world_3d() const {
4163 ERR_READ_THREAD_GUARD_V(false);
4164 return own_world_3d.is_valid();
4165}
4166
4167void Viewport::_propagate_enter_world_3d(Node *p_node) {
4168 if (p_node != this) {
4169 if (!p_node->is_inside_tree()) { //may not have entered scene yet
4170 return;
4171 }
4172
4173 if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4174 p_node->notification(Node3D::NOTIFICATION_ENTER_WORLD);
4175 } else {
4176 Viewport *v = Object::cast_to<Viewport>(p_node);
4177 if (v) {
4178 if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4179 return;
4180 }
4181 }
4182 }
4183 }
4184
4185 for (int i = 0; i < p_node->get_child_count(); i++) {
4186 _propagate_enter_world_3d(p_node->get_child(i));
4187 }
4188}
4189
4190void Viewport::_propagate_exit_world_3d(Node *p_node) {
4191 if (p_node != this) {
4192 if (!p_node->is_inside_tree()) { //may have exited scene already
4193 return;
4194 }
4195
4196 if (Object::cast_to<Node3D>(p_node) || Object::cast_to<WorldEnvironment>(p_node)) {
4197 p_node->notification(Node3D::NOTIFICATION_EXIT_WORLD);
4198 } else {
4199 Viewport *v = Object::cast_to<Viewport>(p_node);
4200 if (v) {
4201 if (v->world_3d.is_valid() || v->own_world_3d.is_valid()) {
4202 return;
4203 }
4204 }
4205 }
4206 }
4207
4208 for (int i = 0; i < p_node->get_child_count(); i++) {
4209 _propagate_exit_world_3d(p_node->get_child(i));
4210 }
4211}
4212
4213void Viewport::set_use_xr(bool p_use_xr) {
4214 ERR_MAIN_THREAD_GUARD;
4215 if (use_xr != p_use_xr) {
4216 use_xr = p_use_xr;
4217
4218 RS::get_singleton()->viewport_set_use_xr(viewport, use_xr);
4219
4220 if (!use_xr) {
4221 // Set viewport to previous size when exiting XR.
4222 if (size_allocated) {
4223 RS::get_singleton()->viewport_set_size(viewport, size.width, size.height);
4224 } else {
4225 RS::get_singleton()->viewport_set_size(viewport, 0, 0);
4226 }
4227 }
4228 }
4229}
4230
4231bool Viewport::is_using_xr() {
4232 ERR_READ_THREAD_GUARD_V(false);
4233 return use_xr;
4234}
4235
4236void Viewport::set_scaling_3d_mode(Scaling3DMode p_scaling_3d_mode) {
4237 ERR_MAIN_THREAD_GUARD;
4238 if (scaling_3d_mode == p_scaling_3d_mode) {
4239 return;
4240 }
4241
4242 scaling_3d_mode = p_scaling_3d_mode;
4243 RS::get_singleton()->viewport_set_scaling_3d_mode(viewport, (RS::ViewportScaling3DMode)(int)p_scaling_3d_mode);
4244}
4245
4246Viewport::Scaling3DMode Viewport::get_scaling_3d_mode() const {
4247 ERR_READ_THREAD_GUARD_V(SCALING_3D_MODE_BILINEAR);
4248 return scaling_3d_mode;
4249}
4250
4251void Viewport::set_scaling_3d_scale(float p_scaling_3d_scale) {
4252 ERR_MAIN_THREAD_GUARD;
4253 // Clamp to reasonable values that are actually useful.
4254 // Values above 2.0 don't serve a practical purpose since the viewport
4255 // isn't displayed with mipmaps.
4256 scaling_3d_scale = CLAMP(p_scaling_3d_scale, 0.1, 2.0);
4257
4258 RS::get_singleton()->viewport_set_scaling_3d_scale(viewport, scaling_3d_scale);
4259}
4260
4261float Viewport::get_scaling_3d_scale() const {
4262 ERR_READ_THREAD_GUARD_V(0);
4263 return scaling_3d_scale;
4264}
4265
4266void Viewport::set_fsr_sharpness(float p_fsr_sharpness) {
4267 ERR_MAIN_THREAD_GUARD;
4268 if (fsr_sharpness == p_fsr_sharpness) {
4269 return;
4270 }
4271
4272 if (p_fsr_sharpness < 0.0f) {
4273 p_fsr_sharpness = 0.0f;
4274 }
4275
4276 fsr_sharpness = p_fsr_sharpness;
4277 RS::get_singleton()->viewport_set_fsr_sharpness(viewport, p_fsr_sharpness);
4278}
4279
4280float Viewport::get_fsr_sharpness() const {
4281 ERR_READ_THREAD_GUARD_V(0);
4282 return fsr_sharpness;
4283}
4284
4285void Viewport::set_texture_mipmap_bias(float p_texture_mipmap_bias) {
4286 ERR_MAIN_THREAD_GUARD;
4287 if (texture_mipmap_bias == p_texture_mipmap_bias) {
4288 return;
4289 }
4290
4291 texture_mipmap_bias = p_texture_mipmap_bias;
4292 RS::get_singleton()->viewport_set_texture_mipmap_bias(viewport, p_texture_mipmap_bias);
4293}
4294
4295float Viewport::get_texture_mipmap_bias() const {
4296 ERR_READ_THREAD_GUARD_V(0);
4297 return texture_mipmap_bias;
4298}
4299
4300#endif // _3D_DISABLED
4301
4302void Viewport::_propagate_world_2d_changed(Node *p_node) {
4303 if (p_node != this) {
4304 if (Object::cast_to<CanvasItem>(p_node)) {
4305 p_node->notification(CanvasItem::NOTIFICATION_WORLD_2D_CHANGED);
4306 } else {
4307 Viewport *v = Object::cast_to<Viewport>(p_node);
4308 if (v) {
4309 if (v->world_2d.is_valid()) {
4310 return;
4311 }
4312 }
4313 }
4314 }
4315
4316 for (int i = 0; i < p_node->get_child_count(); ++i) {
4317 _propagate_world_2d_changed(p_node->get_child(i));
4318 }
4319}
4320
4321void Viewport::_bind_methods() {
4322 ClassDB::bind_method(D_METHOD("set_world_2d", "world_2d"), &Viewport::set_world_2d);
4323 ClassDB::bind_method(D_METHOD("get_world_2d"), &Viewport::get_world_2d);
4324 ClassDB::bind_method(D_METHOD("find_world_2d"), &Viewport::find_world_2d);
4325
4326 ClassDB::bind_method(D_METHOD("set_canvas_transform", "xform"), &Viewport::set_canvas_transform);
4327 ClassDB::bind_method(D_METHOD("get_canvas_transform"), &Viewport::get_canvas_transform);
4328
4329 ClassDB::bind_method(D_METHOD("set_global_canvas_transform", "xform"), &Viewport::set_global_canvas_transform);
4330 ClassDB::bind_method(D_METHOD("get_global_canvas_transform"), &Viewport::get_global_canvas_transform);
4331 ClassDB::bind_method(D_METHOD("get_final_transform"), &Viewport::get_final_transform);
4332 ClassDB::bind_method(D_METHOD("get_screen_transform"), &Viewport::get_screen_transform);
4333
4334 ClassDB::bind_method(D_METHOD("get_visible_rect"), &Viewport::get_visible_rect);
4335 ClassDB::bind_method(D_METHOD("set_transparent_background", "enable"), &Viewport::set_transparent_background);
4336 ClassDB::bind_method(D_METHOD("has_transparent_background"), &Viewport::has_transparent_background);
4337 ClassDB::bind_method(D_METHOD("set_use_hdr_2d", "enable"), &Viewport::set_use_hdr_2d);
4338 ClassDB::bind_method(D_METHOD("is_using_hdr_2d"), &Viewport::is_using_hdr_2d);
4339
4340 ClassDB::bind_method(D_METHOD("set_msaa_2d", "msaa"), &Viewport::set_msaa_2d);
4341 ClassDB::bind_method(D_METHOD("get_msaa_2d"), &Viewport::get_msaa_2d);
4342
4343 ClassDB::bind_method(D_METHOD("set_msaa_3d", "msaa"), &Viewport::set_msaa_3d);
4344 ClassDB::bind_method(D_METHOD("get_msaa_3d"), &Viewport::get_msaa_3d);
4345
4346 ClassDB::bind_method(D_METHOD("set_screen_space_aa", "screen_space_aa"), &Viewport::set_screen_space_aa);
4347 ClassDB::bind_method(D_METHOD("get_screen_space_aa"), &Viewport::get_screen_space_aa);
4348
4349 ClassDB::bind_method(D_METHOD("set_use_taa", "enable"), &Viewport::set_use_taa);
4350 ClassDB::bind_method(D_METHOD("is_using_taa"), &Viewport::is_using_taa);
4351
4352 ClassDB::bind_method(D_METHOD("set_use_debanding", "enable"), &Viewport::set_use_debanding);
4353 ClassDB::bind_method(D_METHOD("is_using_debanding"), &Viewport::is_using_debanding);
4354
4355 ClassDB::bind_method(D_METHOD("set_use_occlusion_culling", "enable"), &Viewport::set_use_occlusion_culling);
4356 ClassDB::bind_method(D_METHOD("is_using_occlusion_culling"), &Viewport::is_using_occlusion_culling);
4357
4358 ClassDB::bind_method(D_METHOD("set_debug_draw", "debug_draw"), &Viewport::set_debug_draw);
4359 ClassDB::bind_method(D_METHOD("get_debug_draw"), &Viewport::get_debug_draw);
4360
4361 ClassDB::bind_method(D_METHOD("get_render_info", "type", "info"), &Viewport::get_render_info);
4362
4363 ClassDB::bind_method(D_METHOD("get_texture"), &Viewport::get_texture);
4364
4365 ClassDB::bind_method(D_METHOD("set_physics_object_picking", "enable"), &Viewport::set_physics_object_picking);
4366 ClassDB::bind_method(D_METHOD("get_physics_object_picking"), &Viewport::get_physics_object_picking);
4367 ClassDB::bind_method(D_METHOD("set_physics_object_picking_sort", "enable"), &Viewport::set_physics_object_picking_sort);
4368 ClassDB::bind_method(D_METHOD("get_physics_object_picking_sort"), &Viewport::get_physics_object_picking_sort);
4369
4370 ClassDB::bind_method(D_METHOD("get_viewport_rid"), &Viewport::get_viewport_rid);
4371 ClassDB::bind_method(D_METHOD("push_text_input", "text"), &Viewport::push_text_input);
4372 ClassDB::bind_method(D_METHOD("push_input", "event", "in_local_coords"), &Viewport::push_input, DEFVAL(false));
4373#ifndef DISABLE_DEPRECATED
4374 ClassDB::bind_method(D_METHOD("push_unhandled_input", "event", "in_local_coords"), &Viewport::push_unhandled_input, DEFVAL(false));
4375#endif // DISABLE_DEPRECATED
4376
4377 ClassDB::bind_method(D_METHOD("get_camera_2d"), &Viewport::get_camera_2d);
4378 ClassDB::bind_method(D_METHOD("set_as_audio_listener_2d", "enable"), &Viewport::set_as_audio_listener_2d);
4379 ClassDB::bind_method(D_METHOD("is_audio_listener_2d"), &Viewport::is_audio_listener_2d);
4380
4381 ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
4382 ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
4383 ClassDB::bind_method(D_METHOD("update_mouse_cursor_state"), &Viewport::update_mouse_cursor_state);
4384
4385 ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
4386 ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
4387 ClassDB::bind_method(D_METHOD("gui_is_drag_successful"), &Viewport::gui_is_drag_successful);
4388
4389 ClassDB::bind_method(D_METHOD("gui_release_focus"), &Viewport::gui_release_focus);
4390 ClassDB::bind_method(D_METHOD("gui_get_focus_owner"), &Viewport::gui_get_focus_owner);
4391
4392 ClassDB::bind_method(D_METHOD("set_disable_input", "disable"), &Viewport::set_disable_input);
4393 ClassDB::bind_method(D_METHOD("is_input_disabled"), &Viewport::is_input_disabled);
4394
4395 ClassDB::bind_method(D_METHOD("_gui_remove_focus_for_window"), &Viewport::_gui_remove_focus_for_window);
4396 ClassDB::bind_method(D_METHOD("_post_gui_grab_click_focus"), &Viewport::_post_gui_grab_click_focus);
4397
4398 ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_size", "size"), &Viewport::set_positional_shadow_atlas_size);
4399 ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_size"), &Viewport::get_positional_shadow_atlas_size);
4400
4401 ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_16_bits", "enable"), &Viewport::set_positional_shadow_atlas_16_bits);
4402 ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_16_bits"), &Viewport::get_positional_shadow_atlas_16_bits);
4403
4404 ClassDB::bind_method(D_METHOD("set_snap_controls_to_pixels", "enabled"), &Viewport::set_snap_controls_to_pixels);
4405 ClassDB::bind_method(D_METHOD("is_snap_controls_to_pixels_enabled"), &Viewport::is_snap_controls_to_pixels_enabled);
4406
4407 ClassDB::bind_method(D_METHOD("set_snap_2d_transforms_to_pixel", "enabled"), &Viewport::set_snap_2d_transforms_to_pixel);
4408 ClassDB::bind_method(D_METHOD("is_snap_2d_transforms_to_pixel_enabled"), &Viewport::is_snap_2d_transforms_to_pixel_enabled);
4409
4410 ClassDB::bind_method(D_METHOD("set_snap_2d_vertices_to_pixel", "enabled"), &Viewport::set_snap_2d_vertices_to_pixel);
4411 ClassDB::bind_method(D_METHOD("is_snap_2d_vertices_to_pixel_enabled"), &Viewport::is_snap_2d_vertices_to_pixel_enabled);
4412
4413 ClassDB::bind_method(D_METHOD("set_positional_shadow_atlas_quadrant_subdiv", "quadrant", "subdiv"), &Viewport::set_positional_shadow_atlas_quadrant_subdiv);
4414 ClassDB::bind_method(D_METHOD("get_positional_shadow_atlas_quadrant_subdiv", "quadrant"), &Viewport::get_positional_shadow_atlas_quadrant_subdiv);
4415
4416 ClassDB::bind_method(D_METHOD("set_input_as_handled"), &Viewport::set_input_as_handled);
4417 ClassDB::bind_method(D_METHOD("is_input_handled"), &Viewport::is_input_handled);
4418
4419 ClassDB::bind_method(D_METHOD("set_handle_input_locally", "enable"), &Viewport::set_handle_input_locally);
4420 ClassDB::bind_method(D_METHOD("is_handling_input_locally"), &Viewport::is_handling_input_locally);
4421
4422 ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_filter", "mode"), &Viewport::set_default_canvas_item_texture_filter);
4423 ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_filter"), &Viewport::get_default_canvas_item_texture_filter);
4424
4425 ClassDB::bind_method(D_METHOD("set_embedding_subwindows", "enable"), &Viewport::set_embedding_subwindows);
4426 ClassDB::bind_method(D_METHOD("is_embedding_subwindows"), &Viewport::is_embedding_subwindows);
4427 ClassDB::bind_method(D_METHOD("get_embedded_subwindows"), &Viewport::get_embedded_subwindows);
4428
4429 ClassDB::bind_method(D_METHOD("set_canvas_cull_mask", "mask"), &Viewport::set_canvas_cull_mask);
4430 ClassDB::bind_method(D_METHOD("get_canvas_cull_mask"), &Viewport::get_canvas_cull_mask);
4431
4432 ClassDB::bind_method(D_METHOD("set_canvas_cull_mask_bit", "layer", "enable"), &Viewport::set_canvas_cull_mask_bit);
4433 ClassDB::bind_method(D_METHOD("get_canvas_cull_mask_bit", "layer"), &Viewport::get_canvas_cull_mask_bit);
4434
4435 ClassDB::bind_method(D_METHOD("set_default_canvas_item_texture_repeat", "mode"), &Viewport::set_default_canvas_item_texture_repeat);
4436 ClassDB::bind_method(D_METHOD("get_default_canvas_item_texture_repeat"), &Viewport::get_default_canvas_item_texture_repeat);
4437
4438 ClassDB::bind_method(D_METHOD("set_sdf_oversize", "oversize"), &Viewport::set_sdf_oversize);
4439 ClassDB::bind_method(D_METHOD("get_sdf_oversize"), &Viewport::get_sdf_oversize);
4440
4441 ClassDB::bind_method(D_METHOD("set_sdf_scale", "scale"), &Viewport::set_sdf_scale);
4442 ClassDB::bind_method(D_METHOD("get_sdf_scale"), &Viewport::get_sdf_scale);
4443
4444 ClassDB::bind_method(D_METHOD("set_mesh_lod_threshold", "pixels"), &Viewport::set_mesh_lod_threshold);
4445 ClassDB::bind_method(D_METHOD("get_mesh_lod_threshold"), &Viewport::get_mesh_lod_threshold);
4446
4447 ClassDB::bind_method(D_METHOD("_process_picking"), &Viewport::_process_picking);
4448
4449#ifndef _3D_DISABLED
4450 ClassDB::bind_method(D_METHOD("set_world_3d", "world_3d"), &Viewport::set_world_3d);
4451 ClassDB::bind_method(D_METHOD("get_world_3d"), &Viewport::get_world_3d);
4452 ClassDB::bind_method(D_METHOD("find_world_3d"), &Viewport::find_world_3d);
4453
4454 ClassDB::bind_method(D_METHOD("set_use_own_world_3d", "enable"), &Viewport::set_use_own_world_3d);
4455 ClassDB::bind_method(D_METHOD("is_using_own_world_3d"), &Viewport::is_using_own_world_3d);
4456
4457 ClassDB::bind_method(D_METHOD("get_camera_3d"), &Viewport::get_camera_3d);
4458 ClassDB::bind_method(D_METHOD("set_as_audio_listener_3d", "enable"), &Viewport::set_as_audio_listener_3d);
4459 ClassDB::bind_method(D_METHOD("is_audio_listener_3d"), &Viewport::is_audio_listener_3d);
4460
4461 ClassDB::bind_method(D_METHOD("set_disable_3d", "disable"), &Viewport::set_disable_3d);
4462 ClassDB::bind_method(D_METHOD("is_3d_disabled"), &Viewport::is_3d_disabled);
4463
4464 ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &Viewport::set_use_xr);
4465 ClassDB::bind_method(D_METHOD("is_using_xr"), &Viewport::is_using_xr);
4466
4467 ClassDB::bind_method(D_METHOD("set_scaling_3d_mode", "scaling_3d_mode"), &Viewport::set_scaling_3d_mode);
4468 ClassDB::bind_method(D_METHOD("get_scaling_3d_mode"), &Viewport::get_scaling_3d_mode);
4469
4470 ClassDB::bind_method(D_METHOD("set_scaling_3d_scale", "scale"), &Viewport::set_scaling_3d_scale);
4471 ClassDB::bind_method(D_METHOD("get_scaling_3d_scale"), &Viewport::get_scaling_3d_scale);
4472
4473 ClassDB::bind_method(D_METHOD("set_fsr_sharpness", "fsr_sharpness"), &Viewport::set_fsr_sharpness);
4474 ClassDB::bind_method(D_METHOD("get_fsr_sharpness"), &Viewport::get_fsr_sharpness);
4475
4476 ClassDB::bind_method(D_METHOD("set_texture_mipmap_bias", "texture_mipmap_bias"), &Viewport::set_texture_mipmap_bias);
4477 ClassDB::bind_method(D_METHOD("get_texture_mipmap_bias"), &Viewport::get_texture_mipmap_bias);
4478
4479 ClassDB::bind_method(D_METHOD("set_vrs_mode", "mode"), &Viewport::set_vrs_mode);
4480 ClassDB::bind_method(D_METHOD("get_vrs_mode"), &Viewport::get_vrs_mode);
4481
4482 ClassDB::bind_method(D_METHOD("set_vrs_texture", "texture"), &Viewport::set_vrs_texture);
4483 ClassDB::bind_method(D_METHOD("get_vrs_texture"), &Viewport::get_vrs_texture);
4484
4485 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_3d"), "set_disable_3d", "is_3d_disabled");
4486 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_xr"), "set_use_xr", "is_using_xr");
4487 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "own_world_3d"), "set_use_own_world_3d", "is_using_own_world_3d");
4488 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_3d", PROPERTY_HINT_RESOURCE_TYPE, "World3D"), "set_world_3d", "get_world_3d");
4489#endif // _3D_DISABLED
4490 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "world_2d", PROPERTY_HINT_RESOURCE_TYPE, "World2D", PROPERTY_USAGE_NONE), "set_world_2d", "get_world_2d");
4491 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transparent_bg"), "set_transparent_background", "has_transparent_background");
4492 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "handle_input_locally"), "set_handle_input_locally", "is_handling_input_locally");
4493 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_transforms_to_pixel"), "set_snap_2d_transforms_to_pixel", "is_snap_2d_transforms_to_pixel_enabled");
4494 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "snap_2d_vertices_to_pixel"), "set_snap_2d_vertices_to_pixel", "is_snap_2d_vertices_to_pixel_enabled");
4495 ADD_GROUP("Rendering", "");
4496 ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_2d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_2d", "get_msaa_2d");
4497 ADD_PROPERTY(PropertyInfo(Variant::INT, "msaa_3d", PROPERTY_HINT_ENUM, String::utf8("Disabled (Fastest),2× (Average),4× (Slow),8× (Slowest)")), "set_msaa_3d", "get_msaa_3d");
4498 ADD_PROPERTY(PropertyInfo(Variant::INT, "screen_space_aa", PROPERTY_HINT_ENUM, "Disabled (Fastest),FXAA (Fast)"), "set_screen_space_aa", "get_screen_space_aa");
4499 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_taa"), "set_use_taa", "is_using_taa");
4500 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_debanding"), "set_use_debanding", "is_using_debanding");
4501 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_occlusion_culling"), "set_use_occlusion_culling", "is_using_occlusion_culling");
4502 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mesh_lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_mesh_lod_threshold", "get_mesh_lod_threshold");
4503 ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_draw", PROPERTY_HINT_ENUM, "Disabled,Unshaded,Lighting,Overdraw,Wireframe"), "set_debug_draw", "get_debug_draw");
4504 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hdr_2d"), "set_use_hdr_2d", "is_using_hdr_2d");
4505
4506#ifndef _3D_DISABLED
4507 ADD_GROUP("Scaling 3D", "");
4508 ADD_PROPERTY(PropertyInfo(Variant::INT, "scaling_3d_mode", PROPERTY_HINT_ENUM, "Bilinear (Fastest),FSR 1.0 (Fast)"), "set_scaling_3d_mode", "get_scaling_3d_mode");
4509 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "scaling_3d_scale", PROPERTY_HINT_RANGE, "0.25,2.0,0.01"), "set_scaling_3d_scale", "get_scaling_3d_scale");
4510 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texture_mipmap_bias", PROPERTY_HINT_RANGE, "-2,2,0.001"), "set_texture_mipmap_bias", "get_texture_mipmap_bias");
4511 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fsr_sharpness", PROPERTY_HINT_RANGE, "0,2,0.1"), "set_fsr_sharpness", "get_fsr_sharpness");
4512#endif
4513 ADD_GROUP("Variable Rate Shading", "vrs_");
4514 ADD_PROPERTY(PropertyInfo(Variant::INT, "vrs_mode", PROPERTY_HINT_ENUM, "Disabled,Texture,Depth buffer,XR"), "set_vrs_mode", "get_vrs_mode");
4515 ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "vrs_texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_vrs_texture", "get_vrs_texture");
4516 ADD_GROUP("Canvas Items", "canvas_item_");
4517 ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_filter", PROPERTY_HINT_ENUM, "Nearest,Linear,Linear Mipmap,Nearest Mipmap"), "set_default_canvas_item_texture_filter", "get_default_canvas_item_texture_filter");
4518 ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_item_default_texture_repeat", PROPERTY_HINT_ENUM, "Disabled,Enabled,Mirror"), "set_default_canvas_item_texture_repeat", "get_default_canvas_item_texture_repeat");
4519 ADD_GROUP("Audio Listener", "audio_listener_");
4520 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_2d"), "set_as_audio_listener_2d", "is_audio_listener_2d");
4521#ifndef _3D_DISABLED
4522 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_listener_enable_3d"), "set_as_audio_listener_3d", "is_audio_listener_3d");
4523#endif
4524 ADD_GROUP("Physics", "physics_");
4525 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking"), "set_physics_object_picking", "get_physics_object_picking");
4526 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "physics_object_picking_sort"), "set_physics_object_picking_sort", "get_physics_object_picking_sort");
4527 ADD_GROUP("GUI", "gui_");
4528 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_disable_input"), "set_disable_input", "is_input_disabled");
4529 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_snap_controls_to_pixels"), "set_snap_controls_to_pixels", "is_snap_controls_to_pixels_enabled");
4530 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gui_embed_subwindows"), "set_embedding_subwindows", "is_embedding_subwindows");
4531 ADD_GROUP("SDF", "sdf_");
4532 ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_oversize", PROPERTY_HINT_ENUM, "100%,120%,150%,200%"), "set_sdf_oversize", "get_sdf_oversize");
4533 ADD_PROPERTY(PropertyInfo(Variant::INT, "sdf_scale", PROPERTY_HINT_ENUM, "100%,50%,25%"), "set_sdf_scale", "get_sdf_scale");
4534 ADD_GROUP("Positional Shadow Atlas", "positional_shadow_atlas_");
4535 ADD_PROPERTY(PropertyInfo(Variant::INT, "positional_shadow_atlas_size"), "set_positional_shadow_atlas_size", "get_positional_shadow_atlas_size");
4536 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "positional_shadow_atlas_16_bits"), "set_positional_shadow_atlas_16_bits", "get_positional_shadow_atlas_16_bits");
4537 ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_0", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 0);
4538 ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_1", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 1);
4539 ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_2", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 2);
4540 ADD_PROPERTYI(PropertyInfo(Variant::INT, "positional_shadow_atlas_quad_3", PROPERTY_HINT_ENUM, "Disabled,1 Shadow,4 Shadows,16 Shadows,64 Shadows,256 Shadows,1024 Shadows"), "set_positional_shadow_atlas_quadrant_subdiv", "get_positional_shadow_atlas_quadrant_subdiv", 3);
4541 ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_canvas_transform", "get_canvas_transform");
4542 ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "global_canvas_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_canvas_transform", "get_global_canvas_transform");
4543 ADD_PROPERTY(PropertyInfo(Variant::INT, "canvas_cull_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_canvas_cull_mask", "get_canvas_cull_mask");
4544
4545 ADD_SIGNAL(MethodInfo("size_changed"));
4546 ADD_SIGNAL(MethodInfo("gui_focus_changed", PropertyInfo(Variant::OBJECT, "node", PROPERTY_HINT_RESOURCE_TYPE, "Control")));
4547
4548 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_DISABLED);
4549 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1);
4550 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_4);
4551 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_16);
4552 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_64);
4553 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_256);
4554 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_1024);
4555 BIND_ENUM_CONSTANT(SHADOW_ATLAS_QUADRANT_SUBDIV_MAX);
4556
4557 BIND_ENUM_CONSTANT(SCALING_3D_MODE_BILINEAR);
4558 BIND_ENUM_CONSTANT(SCALING_3D_MODE_FSR);
4559 BIND_ENUM_CONSTANT(SCALING_3D_MODE_MAX);
4560
4561 BIND_ENUM_CONSTANT(MSAA_DISABLED);
4562 BIND_ENUM_CONSTANT(MSAA_2X);
4563 BIND_ENUM_CONSTANT(MSAA_4X);
4564 BIND_ENUM_CONSTANT(MSAA_8X);
4565 BIND_ENUM_CONSTANT(MSAA_MAX);
4566
4567 BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_DISABLED);
4568 BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_FXAA);
4569 BIND_ENUM_CONSTANT(SCREEN_SPACE_AA_MAX);
4570
4571 BIND_ENUM_CONSTANT(RENDER_INFO_OBJECTS_IN_FRAME);
4572 BIND_ENUM_CONSTANT(RENDER_INFO_PRIMITIVES_IN_FRAME);
4573 BIND_ENUM_CONSTANT(RENDER_INFO_DRAW_CALLS_IN_FRAME);
4574 BIND_ENUM_CONSTANT(RENDER_INFO_MAX);
4575
4576 BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_VISIBLE);
4577 BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_SHADOW);
4578 BIND_ENUM_CONSTANT(RENDER_INFO_TYPE_MAX);
4579
4580 BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLED);
4581 BIND_ENUM_CONSTANT(DEBUG_DRAW_UNSHADED);
4582 BIND_ENUM_CONSTANT(DEBUG_DRAW_LIGHTING);
4583 BIND_ENUM_CONSTANT(DEBUG_DRAW_OVERDRAW);
4584 BIND_ENUM_CONSTANT(DEBUG_DRAW_WIREFRAME);
4585 BIND_ENUM_CONSTANT(DEBUG_DRAW_NORMAL_BUFFER);
4586 BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_ALBEDO);
4587 BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_LIGHTING);
4588 BIND_ENUM_CONSTANT(DEBUG_DRAW_VOXEL_GI_EMISSION);
4589 BIND_ENUM_CONSTANT(DEBUG_DRAW_SHADOW_ATLAS);
4590 BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
4591 BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
4592 BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
4593 BIND_ENUM_CONSTANT(DEBUG_DRAW_SSIL);
4594 BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
4595 BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
4596 BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
4597 BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES);
4598 BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER);
4599 BIND_ENUM_CONSTANT(DEBUG_DRAW_DISABLE_LOD);
4600 BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_OMNI_LIGHTS);
4601 BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_SPOT_LIGHTS);
4602 BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_DECALS);
4603 BIND_ENUM_CONSTANT(DEBUG_DRAW_CLUSTER_REFLECTION_PROBES);
4604 BIND_ENUM_CONSTANT(DEBUG_DRAW_OCCLUDERS)
4605 BIND_ENUM_CONSTANT(DEBUG_DRAW_MOTION_VECTORS)
4606
4607 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
4608 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR);
4609 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS);
4610 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS);
4611 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_MAX);
4612
4613 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_DISABLED);
4614 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_ENABLED);
4615 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MIRROR);
4616 BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_REPEAT_MAX);
4617
4618 BIND_ENUM_CONSTANT(SDF_OVERSIZE_100_PERCENT);
4619 BIND_ENUM_CONSTANT(SDF_OVERSIZE_120_PERCENT);
4620 BIND_ENUM_CONSTANT(SDF_OVERSIZE_150_PERCENT);
4621 BIND_ENUM_CONSTANT(SDF_OVERSIZE_200_PERCENT);
4622 BIND_ENUM_CONSTANT(SDF_OVERSIZE_MAX);
4623
4624 BIND_ENUM_CONSTANT(SDF_SCALE_100_PERCENT);
4625 BIND_ENUM_CONSTANT(SDF_SCALE_50_PERCENT);
4626 BIND_ENUM_CONSTANT(SDF_SCALE_25_PERCENT);
4627 BIND_ENUM_CONSTANT(SDF_SCALE_MAX);
4628
4629 BIND_ENUM_CONSTANT(VRS_DISABLED);
4630 BIND_ENUM_CONSTANT(VRS_TEXTURE);
4631 BIND_ENUM_CONSTANT(VRS_XR);
4632 BIND_ENUM_CONSTANT(VRS_MAX);
4633}
4634
4635void Viewport::_validate_property(PropertyInfo &p_property) const {
4636 if (vrs_mode != VRS_TEXTURE && (p_property.name == "vrs_texture")) {
4637 p_property.usage = PROPERTY_USAGE_NO_EDITOR;
4638 }
4639}
4640
4641Viewport::Viewport() {
4642 world_2d = Ref<World2D>(memnew(World2D));
4643 world_2d->register_viewport(this);
4644
4645 viewport = RenderingServer::get_singleton()->viewport_create();
4646 texture_rid = RenderingServer::get_singleton()->viewport_get_texture(viewport);
4647
4648 default_texture.instantiate();
4649 default_texture->vp = const_cast<Viewport *>(this);
4650 viewport_textures.insert(default_texture.ptr());
4651 default_texture->proxy = RS::get_singleton()->texture_proxy_create(texture_rid);
4652
4653 canvas_layers.insert(nullptr); // This eases picking code (interpreted as the canvas of the Viewport).
4654
4655 set_positional_shadow_atlas_size(positional_shadow_atlas_size);
4656
4657 for (int i = 0; i < 4; i++) {
4658 positional_shadow_atlas_quadrant_subdiv[i] = SHADOW_ATLAS_QUADRANT_SUBDIV_MAX;
4659 }
4660 set_positional_shadow_atlas_quadrant_subdiv(0, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
4661 set_positional_shadow_atlas_quadrant_subdiv(1, SHADOW_ATLAS_QUADRANT_SUBDIV_4);
4662 set_positional_shadow_atlas_quadrant_subdiv(2, SHADOW_ATLAS_QUADRANT_SUBDIV_16);
4663 set_positional_shadow_atlas_quadrant_subdiv(3, SHADOW_ATLAS_QUADRANT_SUBDIV_64);
4664
4665 set_mesh_lod_threshold(mesh_lod_threshold);
4666
4667 String id = itos(get_instance_id());
4668 input_group = "_vp_input" + id;
4669 gui_input_group = "_vp_gui_input" + id;
4670 unhandled_input_group = "_vp_unhandled_input" + id;
4671 shortcut_input_group = "_vp_shortcut_input" + id;
4672 unhandled_key_input_group = "_vp_unhandled_key_input" + id;
4673
4674 // Window tooltip.
4675 gui.tooltip_delay = GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "gui/timers/tooltip_delay_sec", PROPERTY_HINT_RANGE, "0,5,0.01,or_greater"), 0.5);
4676
4677#ifndef _3D_DISABLED
4678 set_scaling_3d_mode((Viewport::Scaling3DMode)(int)GLOBAL_GET("rendering/scaling_3d/mode"));
4679 set_scaling_3d_scale(GLOBAL_GET("rendering/scaling_3d/scale"));
4680 set_fsr_sharpness((float)GLOBAL_GET("rendering/scaling_3d/fsr_sharpness"));
4681 set_texture_mipmap_bias((float)GLOBAL_GET("rendering/textures/default_filters/texture_mipmap_bias"));
4682#endif // _3D_DISABLED
4683
4684 set_sdf_oversize(sdf_oversize); // Set to server.
4685}
4686
4687Viewport::~Viewport() {
4688 // Erase itself from viewport textures.
4689 for (ViewportTexture *E : viewport_textures) {
4690 E->vp = nullptr;
4691 }
4692 ERR_FAIL_NULL(RenderingServer::get_singleton());
4693 RenderingServer::get_singleton()->free(viewport);
4694}
4695
4696/////////////////////////////////
4697
4698void SubViewport::set_size(const Size2i &p_size) {
4699 ERR_MAIN_THREAD_GUARD;
4700 _internal_set_size(p_size);
4701}
4702
4703void SubViewport::set_size_force(const Size2i &p_size) {
4704 ERR_MAIN_THREAD_GUARD;
4705 // Use only for setting the size from the parent SubViewportContainer with enabled stretch mode.
4706 // Don't expose function to scripting.
4707 _internal_set_size(p_size, true);
4708}
4709
4710void SubViewport::_internal_set_size(const Size2i &p_size, bool p_force) {
4711 SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
4712 if (!p_force && c && c->is_stretch_enabled()) {
4713#ifdef DEBUG_ENABLED
4714 WARN_PRINT("Can't change the size of a `SubViewport` with a `SubViewportContainer` parent that has `stretch` enabled. Set `SubViewportContainer.stretch` to `false` to allow changing the size manually.");
4715#endif // DEBUG_ENABLED
4716 return;
4717 }
4718
4719 _set_size(p_size, _get_size_2d_override(), true);
4720
4721 if (c) {
4722 c->update_minimum_size();
4723 }
4724}
4725
4726Size2i SubViewport::get_size() const {
4727 ERR_READ_THREAD_GUARD_V(Size2());
4728 return _get_size();
4729}
4730
4731void SubViewport::set_size_2d_override(const Size2i &p_size) {
4732 ERR_MAIN_THREAD_GUARD;
4733 _set_size(_get_size(), p_size, true);
4734}
4735
4736Size2i SubViewport::get_size_2d_override() const {
4737 ERR_READ_THREAD_GUARD_V(Size2i());
4738 return _get_size_2d_override();
4739}
4740
4741void SubViewport::set_size_2d_override_stretch(bool p_enable) {
4742 ERR_MAIN_THREAD_GUARD;
4743 if (p_enable == size_2d_override_stretch) {
4744 return;
4745 }
4746
4747 size_2d_override_stretch = p_enable;
4748 _set_size(_get_size(), _get_size_2d_override(), true);
4749}
4750
4751bool SubViewport::is_size_2d_override_stretch_enabled() const {
4752 ERR_READ_THREAD_GUARD_V(false);
4753 return size_2d_override_stretch;
4754}
4755
4756void SubViewport::set_update_mode(UpdateMode p_mode) {
4757 ERR_MAIN_THREAD_GUARD;
4758 update_mode = p_mode;
4759 RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::ViewportUpdateMode(p_mode));
4760}
4761
4762SubViewport::UpdateMode SubViewport::get_update_mode() const {
4763 ERR_READ_THREAD_GUARD_V(UPDATE_DISABLED);
4764 return update_mode;
4765}
4766
4767void SubViewport::set_clear_mode(ClearMode p_mode) {
4768 ERR_MAIN_THREAD_GUARD;
4769 clear_mode = p_mode;
4770 RS::get_singleton()->viewport_set_clear_mode(get_viewport_rid(), RS::ViewportClearMode(p_mode));
4771}
4772
4773SubViewport::ClearMode SubViewport::get_clear_mode() const {
4774 ERR_READ_THREAD_GUARD_V(CLEAR_MODE_ALWAYS);
4775 return clear_mode;
4776}
4777
4778DisplayServer::WindowID SubViewport::get_window_id() const {
4779 ERR_READ_THREAD_GUARD_V(DisplayServer::INVALID_WINDOW_ID);
4780 return DisplayServer::INVALID_WINDOW_ID;
4781}
4782
4783Transform2D SubViewport::get_screen_transform_internal(bool p_absolute_position) const {
4784 ERR_READ_THREAD_GUARD_V(Transform2D());
4785 Transform2D container_transform;
4786 SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
4787 if (c) {
4788 if (c->is_stretch_enabled()) {
4789 container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
4790 }
4791 container_transform = c->get_viewport()->get_screen_transform_internal(p_absolute_position) * c->get_global_transform_with_canvas() * container_transform;
4792 } else {
4793 WARN_PRINT_ONCE("SubViewport is not a child of a SubViewportContainer. get_screen_transform doesn't return the actual screen position.");
4794 }
4795 return container_transform * get_final_transform();
4796}
4797
4798Transform2D SubViewport::get_popup_base_transform() const {
4799 ERR_READ_THREAD_GUARD_V(Transform2D());
4800 if (is_embedding_subwindows()) {
4801 return Transform2D();
4802 }
4803 SubViewportContainer *c = Object::cast_to<SubViewportContainer>(get_parent());
4804 if (!c) {
4805 return get_final_transform();
4806 }
4807 Transform2D container_transform;
4808 if (c->is_stretch_enabled()) {
4809 container_transform.scale(Vector2(c->get_stretch_shrink(), c->get_stretch_shrink()));
4810 }
4811 return c->get_screen_transform() * container_transform * get_final_transform();
4812}
4813
4814bool SubViewport::is_directly_attached_to_screen() const {
4815 // SubViewports, that are used as Textures are not considered to be directly attached to screen.
4816 return Object::cast_to<SubViewportContainer>(get_parent()) && get_parent()->get_viewport() && get_parent()->get_viewport()->is_directly_attached_to_screen();
4817}
4818
4819bool SubViewport::is_attached_in_viewport() const {
4820 return Object::cast_to<SubViewportContainer>(get_parent());
4821}
4822
4823void SubViewport::_notification(int p_what) {
4824 ERR_MAIN_THREAD_GUARD;
4825 switch (p_what) {
4826 case NOTIFICATION_ENTER_TREE: {
4827 RS::get_singleton()->viewport_set_active(get_viewport_rid(), true);
4828
4829 SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
4830 if (parent_svc) {
4831 parent_svc->recalc_force_viewport_sizes();
4832 }
4833 } break;
4834
4835 case NOTIFICATION_EXIT_TREE: {
4836 RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
4837 } break;
4838 }
4839}
4840
4841void SubViewport::_bind_methods() {
4842 ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size);
4843 ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size);
4844
4845 ClassDB::bind_method(D_METHOD("set_size_2d_override", "size"), &SubViewport::set_size_2d_override);
4846 ClassDB::bind_method(D_METHOD("get_size_2d_override"), &SubViewport::get_size_2d_override);
4847
4848 ClassDB::bind_method(D_METHOD("set_size_2d_override_stretch", "enable"), &SubViewport::set_size_2d_override_stretch);
4849 ClassDB::bind_method(D_METHOD("is_size_2d_override_stretch_enabled"), &SubViewport::is_size_2d_override_stretch_enabled);
4850
4851 ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &SubViewport::set_update_mode);
4852 ClassDB::bind_method(D_METHOD("get_update_mode"), &SubViewport::get_update_mode);
4853
4854 ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode);
4855 ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode);
4856
4857 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size", PROPERTY_HINT_NONE, "suffix:px"), "set_size", "get_size");
4858 ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size_2d_override", PROPERTY_HINT_NONE, "suffix:px"), "set_size_2d_override", "get_size_2d_override");
4859 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled");
4860 ADD_GROUP("Render Target", "render_target_");
4861 ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_clear_mode", PROPERTY_HINT_ENUM, "Always,Never,Next Frame"), "set_clear_mode", "get_clear_mode");
4862 ADD_PROPERTY(PropertyInfo(Variant::INT, "render_target_update_mode", PROPERTY_HINT_ENUM, "Disabled,Once,When Visible,When Parent Visible,Always"), "set_update_mode", "get_update_mode");
4863
4864 BIND_ENUM_CONSTANT(CLEAR_MODE_ALWAYS);
4865 BIND_ENUM_CONSTANT(CLEAR_MODE_NEVER);
4866 BIND_ENUM_CONSTANT(CLEAR_MODE_ONCE);
4867
4868 BIND_ENUM_CONSTANT(UPDATE_DISABLED);
4869 BIND_ENUM_CONSTANT(UPDATE_ONCE);
4870 BIND_ENUM_CONSTANT(UPDATE_WHEN_VISIBLE);
4871 BIND_ENUM_CONSTANT(UPDATE_WHEN_PARENT_VISIBLE);
4872 BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
4873}
4874
4875void SubViewport::_validate_property(PropertyInfo &p_property) const {
4876 if (p_property.name == "size") {
4877 SubViewportContainer *parent_svc = Object::cast_to<SubViewportContainer>(get_parent());
4878 if (parent_svc && parent_svc->is_stretch_enabled()) {
4879 p_property.usage = PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY;
4880 } else {
4881 p_property.usage = PROPERTY_USAGE_DEFAULT;
4882 }
4883 }
4884}
4885
4886SubViewport::SubViewport() {
4887 RS::get_singleton()->viewport_set_size(get_viewport_rid(), get_size().width, get_size().height);
4888}
4889
4890SubViewport::~SubViewport() {}
4891