diff options
author | Juan Linietsky <juan@godotengine.org> | 2020-03-01 19:14:37 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2020-03-26 15:49:32 +0100 |
commit | 4396e98834f159da59ec790f2ff64fb65dacd9ce (patch) | |
tree | d618e96d7a7b856260fd96e5e727f38a26de0724 | |
parent | a2da99f40cf2123c0906c734a2eb01e9b65a45a2 (diff) |
Refactored Input, create DisplayServer and DisplayServerX11
95 files changed, 4820 insertions, 693 deletions
diff --git a/core/SCsub b/core/SCsub index b4974196bd..ca003ce931 100644 --- a/core/SCsub +++ b/core/SCsub @@ -166,6 +166,7 @@ SConscript('math/SCsub') SConscript('crypto/SCsub') SConscript('io/SCsub') SConscript('debugger/SCsub') +SConscript('input/SCsub') SConscript('bind/SCsub') diff --git a/core/debugger/remote_debugger.cpp b/core/debugger/remote_debugger.cpp index 5f7ffb115c..09c60e222c 100644 --- a/core/debugger/remote_debugger.cpp +++ b/core/debugger/remote_debugger.cpp @@ -33,7 +33,7 @@ #include "core/debugger/debugger_marshalls.h" #include "core/debugger/engine_debugger.h" #include "core/debugger/script_debugger.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "core/project_settings.h" #include "core/script_language.h" diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 0945240c1f..8706a38453 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -30,8 +30,8 @@ #include "global_constants.h" +#include "core/input/input_event.h" #include "core/object.h" -#include "core/os/input_event.h" #include "core/os/keyboard.h" #include "core/variant.h" diff --git a/core/input/SCsub b/core/input/SCsub new file mode 100644 index 0000000000..f1660932e5 --- /dev/null +++ b/core/input/SCsub @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +Import('env') + +from platform_methods import run_in_subprocess +import input_builders + + +# Order matters here. Higher index controller database files write on top of lower index database files. +controller_databases = ["#core/input/gamecontrollerdb_204.txt", "#core/input/gamecontrollerdb_205.txt", "#core/input/gamecontrollerdb.txt", "#core/input/godotcontrollerdb.txt"] + +env.Depends("#core/input/default_controller_mappings.gen.cpp", controller_databases) +env.CommandNoCache("#core/input/default_controller_mappings.gen.cpp", controller_databases, run_in_subprocess(input_builders.make_default_controller_mappings)) + +env.add_source_files(env.core_sources, "*.cpp") + +# Don't warn about duplicate entry here, we need it registered manually for first build, +# even if later builds will pick it up twice due to above *.cpp globbing. +env.add_source_files(env.core_sources, "#core/input/default_controller_mappings.gen.cpp", warn_duplicates=False) + diff --git a/main/default_controller_mappings.h b/core/input/default_controller_mappings.h index 9e2a69acec..9e2a69acec 100644 --- a/main/default_controller_mappings.h +++ b/core/input/default_controller_mappings.h diff --git a/main/gamecontrollerdb.txt b/core/input/gamecontrollerdb.txt index 90d309c1c8..90d309c1c8 100644 --- a/main/gamecontrollerdb.txt +++ b/core/input/gamecontrollerdb.txt diff --git a/main/gamecontrollerdb_204.txt b/core/input/gamecontrollerdb_204.txt index 7fbe925b25..7fbe925b25 100644 --- a/main/gamecontrollerdb_204.txt +++ b/core/input/gamecontrollerdb_204.txt diff --git a/main/gamecontrollerdb_205.txt b/core/input/gamecontrollerdb_205.txt index 55c45eb148..55c45eb148 100644 --- a/main/gamecontrollerdb_205.txt +++ b/core/input/gamecontrollerdb_205.txt diff --git a/main/godotcontrollerdb.txt b/core/input/godotcontrollerdb.txt index 472b01947b..472b01947b 100644 --- a/main/godotcontrollerdb.txt +++ b/core/input/godotcontrollerdb.txt diff --git a/main/input_default.cpp b/core/input/input.cpp index aa9e772a38..2bede44d19 100644 --- a/main/input_default.cpp +++ b/core/input/input.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* input_default.cpp */ +/* input.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,15 +28,129 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "input_default.h" +#include "input.h" -#include "core/input_map.h" +#include "core/input/default_controller_mappings.h" +#include "core/input/input_map.h" #include "core/os/os.h" -#include "main/default_controller_mappings.h" -#include "scene/resources/texture.h" -#include "servers/visual_server.h" +#include "core/project_settings.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#endif + +Input *Input::singleton = NULL; + +Input *Input::get_singleton() { + + return singleton; +} + +void Input::set_mouse_mode(MouseMode p_mode) { + ERR_FAIL_INDEX((int)p_mode, 4); + OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode); +} + +Input::MouseMode Input::get_mouse_mode() const { + + return (MouseMode)OS::get_singleton()->get_mouse_mode(); +} + +void Input::_bind_methods() { + + ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); + ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); + ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); + ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); + ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); + ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); + ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); + ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); + ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed); + ClassDB::bind_method(D_METHOD("is_joy_known", "device"), &Input::is_joy_known); + ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); + ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); + ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); + ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); + ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); + ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); + ClassDB::bind_method(D_METHOD("get_joy_button_string", "button_index"), &Input::get_joy_button_string); + ClassDB::bind_method(D_METHOD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string); + ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string); + ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); + ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); + ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); + ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); + ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); + ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); + ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); + ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); + //ClassDB::bind_method(D_METHOD("get_mouse_position"),&Input::get_mouse_position); - this is not the function you want + ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &Input::get_last_mouse_speed); + ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask); + ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); + ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode); + ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position); + ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); + ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); + ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); + ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); + ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); + ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); + ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); + + BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); + BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); + BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); + BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); + + BIND_ENUM_CONSTANT(CURSOR_ARROW); + BIND_ENUM_CONSTANT(CURSOR_IBEAM); + BIND_ENUM_CONSTANT(CURSOR_POINTING_HAND); + BIND_ENUM_CONSTANT(CURSOR_CROSS); + BIND_ENUM_CONSTANT(CURSOR_WAIT); + BIND_ENUM_CONSTANT(CURSOR_BUSY); + BIND_ENUM_CONSTANT(CURSOR_DRAG); + BIND_ENUM_CONSTANT(CURSOR_CAN_DROP); + BIND_ENUM_CONSTANT(CURSOR_FORBIDDEN); + BIND_ENUM_CONSTANT(CURSOR_VSIZE); + BIND_ENUM_CONSTANT(CURSOR_HSIZE); + BIND_ENUM_CONSTANT(CURSOR_BDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_FDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_MOVE); + BIND_ENUM_CONSTANT(CURSOR_VSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HELP); + + ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); +} + +void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { +#ifdef TOOLS_ENABLED + + const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; + + String pf = p_function; + if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { + + List<PropertyInfo> pinfo; + ProjectSettings::get_singleton()->get_property_list(&pinfo); + + for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { + const PropertyInfo &pi = E->get(); + + if (!pi.name.begins_with("input/")) + continue; -void InputDefault::SpeedTrack::update(const Vector2 &p_delta_p) { + String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); + r_options->push_back(quote_style + name + quote_style); + } + } +#endif +} + +void Input::SpeedTrack::update(const Vector2 &p_delta_p) { uint64_t tick = OS::get_singleton()->get_ticks_usec(); uint32_t tdiff = tick - last_tick; @@ -60,26 +174,26 @@ void InputDefault::SpeedTrack::update(const Vector2 &p_delta_p) { } } -void InputDefault::SpeedTrack::reset() { +void Input::SpeedTrack::reset() { last_tick = OS::get_singleton()->get_ticks_usec(); speed = Vector2(); accum_t = 0; } -InputDefault::SpeedTrack::SpeedTrack() { +Input::SpeedTrack::SpeedTrack() { min_ref_frame = 0.1; max_ref_frame = 0.3; reset(); } -bool InputDefault::is_key_pressed(int p_keycode) const { +bool Input::is_key_pressed(int p_keycode) const { _THREAD_SAFE_METHOD_ return keys_pressed.has(p_keycode); } -bool InputDefault::is_mouse_button_pressed(int p_button) const { +bool Input::is_mouse_button_pressed(int p_button) const { _THREAD_SAFE_METHOD_ return (mouse_button_mask & (1 << (p_button - 1))) != 0; @@ -90,18 +204,18 @@ static int _combine_device(int p_value, int p_device) { return p_value | (p_device << 20); } -bool InputDefault::is_joy_button_pressed(int p_device, int p_button) const { +bool Input::is_joy_button_pressed(int p_device, int p_button) const { _THREAD_SAFE_METHOD_ return joy_buttons_pressed.has(_combine_device(p_button, p_device)); } -bool InputDefault::is_action_pressed(const StringName &p_action) const { +bool Input::is_action_pressed(const StringName &p_action) const { return action_state.has(p_action) && action_state[p_action].pressed; } -bool InputDefault::is_action_just_pressed(const StringName &p_action) const { +bool Input::is_action_just_pressed(const StringName &p_action) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) @@ -114,7 +228,7 @@ bool InputDefault::is_action_just_pressed(const StringName &p_action) const { } } -bool InputDefault::is_action_just_released(const StringName &p_action) const { +bool Input::is_action_just_released(const StringName &p_action) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) @@ -127,7 +241,7 @@ bool InputDefault::is_action_just_released(const StringName &p_action) const { } } -float InputDefault::get_action_strength(const StringName &p_action) const { +float Input::get_action_strength(const StringName &p_action) const { const Map<StringName, Action>::Element *E = action_state.find(p_action); if (!E) return 0.0f; @@ -135,7 +249,7 @@ float InputDefault::get_action_strength(const StringName &p_action) const { return E->get().strength; } -float InputDefault::get_joy_axis(int p_device, int p_axis) const { +float Input::get_joy_axis(int p_device, int p_axis) const { _THREAD_SAFE_METHOD_ int c = _combine_device(p_axis, p_device); @@ -146,13 +260,13 @@ float InputDefault::get_joy_axis(int p_device, int p_axis) const { } } -String InputDefault::get_joy_name(int p_idx) { +String Input::get_joy_name(int p_idx) { _THREAD_SAFE_METHOD_ return joy_names[p_idx].name; }; -Vector2 InputDefault::get_joy_vibration_strength(int p_device) { +Vector2 Input::get_joy_vibration_strength(int p_device) { if (joy_vibration.has(p_device)) { return Vector2(joy_vibration[p_device].weak_magnitude, joy_vibration[p_device].strong_magnitude); } else { @@ -160,7 +274,7 @@ Vector2 InputDefault::get_joy_vibration_strength(int p_device) { } } -uint64_t InputDefault::get_joy_vibration_timestamp(int p_device) { +uint64_t Input::get_joy_vibration_timestamp(int p_device) { if (joy_vibration.has(p_device)) { return joy_vibration[p_device].timestamp; } else { @@ -168,7 +282,7 @@ uint64_t InputDefault::get_joy_vibration_timestamp(int p_device) { } } -float InputDefault::get_joy_vibration_duration(int p_device) { +float Input::get_joy_vibration_duration(int p_device) { if (joy_vibration.has(p_device)) { return joy_vibration[p_device].duration; } else { @@ -188,7 +302,7 @@ static String _hex_str(uint8_t p_byte) { return ret; }; -void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { +void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) { _THREAD_SAFE_METHOD_ Joypad js; @@ -230,36 +344,47 @@ void InputDefault::joy_connection_changed(int p_idx, bool p_connected, String p_ emit_signal("joy_connection_changed", p_idx, p_connected); }; -Vector3 InputDefault::get_gravity() const { +Vector3 Input::get_gravity() const { _THREAD_SAFE_METHOD_ return gravity; } -Vector3 InputDefault::get_accelerometer() const { +Vector3 Input::get_accelerometer() const { _THREAD_SAFE_METHOD_ return accelerometer; } -Vector3 InputDefault::get_magnetometer() const { +Vector3 Input::get_magnetometer() const { _THREAD_SAFE_METHOD_ return magnetometer; } -Vector3 InputDefault::get_gyroscope() const { +Vector3 Input::get_gyroscope() const { _THREAD_SAFE_METHOD_ return gyroscope; } -void InputDefault::parse_input_event(const Ref<InputEvent> &p_event) { +void Input::parse_drop_files(const Vector<String> &p_files) { + if (main_loop) { + main_loop->drop_files(p_files); + } +} +void Input::parse_notification(int p_notification) { + if (main_loop) { + main_loop->notification(p_notification); + } +} + +void Input::parse_input_event(const Ref<InputEvent> &p_event) { _parse_input_event_impl(p_event, false); } -void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { +void Input::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool p_is_emulated) { // Notes on mouse-touch emulation: // - Emulated mouse events are parsed, that is, re-routed to this method, so they make the same effects @@ -442,14 +567,14 @@ void InputDefault::_parse_input_event_impl(const Ref<InputEvent> &p_event, bool main_loop->input_event(p_event); } -void InputDefault::set_joy_axis(int p_device, int p_axis, float p_value) { +void Input::set_joy_axis(int p_device, int p_axis, float p_value) { _THREAD_SAFE_METHOD_ int c = _combine_device(p_axis, p_device); _joy_axis[c] = p_value; } -void InputDefault::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { +void Input::start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration) { _THREAD_SAFE_METHOD_ if (p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { return; @@ -462,7 +587,7 @@ void InputDefault::start_joy_vibration(int p_device, float p_weak_magnitude, flo joy_vibration[p_device] = vibration; } -void InputDefault::stop_joy_vibration(int p_device) { +void Input::stop_joy_vibration(int p_device) { _THREAD_SAFE_METHOD_ VibrationInfo vibration; vibration.weak_magnitude = 0; @@ -472,68 +597,68 @@ void InputDefault::stop_joy_vibration(int p_device) { joy_vibration[p_device] = vibration; } -void InputDefault::vibrate_handheld(int p_duration_ms) { +void Input::vibrate_handheld(int p_duration_ms) { OS::get_singleton()->vibrate_handheld(p_duration_ms); } -void InputDefault::set_gravity(const Vector3 &p_gravity) { +void Input::set_gravity(const Vector3 &p_gravity) { _THREAD_SAFE_METHOD_ gravity = p_gravity; } -void InputDefault::set_accelerometer(const Vector3 &p_accel) { +void Input::set_accelerometer(const Vector3 &p_accel) { _THREAD_SAFE_METHOD_ accelerometer = p_accel; } -void InputDefault::set_magnetometer(const Vector3 &p_magnetometer) { +void Input::set_magnetometer(const Vector3 &p_magnetometer) { _THREAD_SAFE_METHOD_ magnetometer = p_magnetometer; } -void InputDefault::set_gyroscope(const Vector3 &p_gyroscope) { +void Input::set_gyroscope(const Vector3 &p_gyroscope) { _THREAD_SAFE_METHOD_ gyroscope = p_gyroscope; } -void InputDefault::set_main_loop(MainLoop *p_main_loop) { +void Input::set_main_loop(MainLoop *p_main_loop) { main_loop = p_main_loop; } -void InputDefault::set_mouse_position(const Point2 &p_posf) { +void Input::set_mouse_position(const Point2 &p_posf) { mouse_speed_track.update(p_posf - mouse_pos); mouse_pos = p_posf; } -Point2 InputDefault::get_mouse_position() const { +Point2 Input::get_mouse_position() const { return mouse_pos; } -Point2 InputDefault::get_last_mouse_speed() const { +Point2 Input::get_last_mouse_speed() const { return mouse_speed_track.speed; } -int InputDefault::get_mouse_button_mask() const { +int Input::get_mouse_button_mask() const { return mouse_button_mask; // do not trust OS implementation, should remove it - OS::get_singleton()->get_mouse_button_state(); } -void InputDefault::warp_mouse_position(const Vector2 &p_to) { +void Input::warp_mouse_position(const Vector2 &p_to) { OS::get_singleton()->warp_mouse_position(p_to); } -Point2i InputDefault::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect) { +Point2i Input::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect) { // The relative distance reported for the next event after a warp is in the boundaries of the // size of the rect on that axis, but it may be greater, in which case there's not problem as fmod() @@ -559,10 +684,10 @@ Point2i InputDefault::warp_mouse_motion(const Ref<InputEventMouseMotion> &p_moti return rel_warped; } -void InputDefault::iteration(float p_step) { +void Input::iteration(float p_step) { } -void InputDefault::action_press(const StringName &p_action, float p_strength) { +void Input::action_press(const StringName &p_action, float p_strength) { Action action; @@ -574,7 +699,7 @@ void InputDefault::action_press(const StringName &p_action, float p_strength) { action_state[p_action] = action; } -void InputDefault::action_release(const StringName &p_action) { +void Input::action_release(const StringName &p_action) { Action action; @@ -586,19 +711,19 @@ void InputDefault::action_release(const StringName &p_action) { action_state[p_action] = action; } -void InputDefault::set_emulate_touch_from_mouse(bool p_emulate) { +void Input::set_emulate_touch_from_mouse(bool p_emulate) { emulate_touch_from_mouse = p_emulate; } -bool InputDefault::is_emulating_touch_from_mouse() const { +bool Input::is_emulating_touch_from_mouse() const { return emulate_touch_from_mouse; } // Calling this whenever the game window is focused helps unstucking the "touch mouse" // if the OS or its abstraction class hasn't properly reported that touch pointers raised -void InputDefault::ensure_touch_mouse_raised() { +void Input::ensure_touch_mouse_raised() { if (mouse_from_touch_index != -1) { mouse_from_touch_index = -1; @@ -617,22 +742,22 @@ void InputDefault::ensure_touch_mouse_raised() { } } -void InputDefault::set_emulate_mouse_from_touch(bool p_emulate) { +void Input::set_emulate_mouse_from_touch(bool p_emulate) { emulate_mouse_from_touch = p_emulate; } -bool InputDefault::is_emulating_mouse_from_touch() const { +bool Input::is_emulating_mouse_from_touch() const { return emulate_mouse_from_touch; } -Input::CursorShape InputDefault::get_default_cursor_shape() const { +Input::CursorShape Input::get_default_cursor_shape() const { return default_shape; } -void InputDefault::set_default_cursor_shape(CursorShape p_shape) { +void Input::set_default_cursor_shape(CursorShape p_shape) { if (default_shape == p_shape) return; @@ -647,19 +772,19 @@ void InputDefault::set_default_cursor_shape(CursorShape p_shape) { parse_input_event(mm); } -Input::CursorShape InputDefault::get_current_cursor_shape() const { +Input::CursorShape Input::get_current_cursor_shape() const { return (Input::CursorShape)OS::get_singleton()->get_cursor_shape(); } -void InputDefault::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { +void Input::set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { if (Engine::get_singleton()->is_editor_hint()) return; OS::get_singleton()->set_custom_mouse_cursor(p_cursor, (OS::CursorShape)p_shape, p_hotspot); } -void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) { +void Input::accumulate_input_event(const Ref<InputEvent> &p_event) { ERR_FAIL_COND(p_event.is_null()); if (!use_accumulated_input) { @@ -672,7 +797,7 @@ void InputDefault::accumulate_input_event(const Ref<InputEvent> &p_event) { accumulated_events.push_back(p_event); } -void InputDefault::flush_accumulated_events() { +void Input::flush_accumulated_events() { while (accumulated_events.front()) { parse_input_event(accumulated_events.front()->get()); @@ -680,12 +805,12 @@ void InputDefault::flush_accumulated_events() { } } -void InputDefault::set_use_accumulated_input(bool p_enable) { +void Input::set_use_accumulated_input(bool p_enable) { use_accumulated_input = p_enable; } -void InputDefault::release_pressed_events() { +void Input::release_pressed_events() { flush_accumulated_events(); // this is needed to release actions strengths @@ -693,61 +818,13 @@ void InputDefault::release_pressed_events() { joy_buttons_pressed.clear(); _joy_axis.clear(); - for (Map<StringName, InputDefault::Action>::Element *E = action_state.front(); E; E = E->next()) { + for (Map<StringName, Input::Action>::Element *E = action_state.front(); E; E = E->next()) { if (E->get().pressed) action_release(E->key()); } } -InputDefault::InputDefault() { - - use_accumulated_input = true; - mouse_button_mask = 0; - emulate_touch_from_mouse = false; - emulate_mouse_from_touch = false; - mouse_from_touch_index = -1; - main_loop = NULL; - default_shape = CURSOR_ARROW; - - hat_map_default[HAT_UP].type = TYPE_BUTTON; - hat_map_default[HAT_UP].index = JOY_DPAD_UP; - hat_map_default[HAT_UP].value = 0; - - hat_map_default[HAT_RIGHT].type = TYPE_BUTTON; - hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT; - hat_map_default[HAT_RIGHT].value = 0; - - hat_map_default[HAT_DOWN].type = TYPE_BUTTON; - hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN; - hat_map_default[HAT_DOWN].value = 0; - - hat_map_default[HAT_LEFT].type = TYPE_BUTTON; - hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT; - hat_map_default[HAT_LEFT].value = 0; - - fallback_mapping = -1; - - // Parse default mappings. - { - int i = 0; - while (DefaultControllerMappings::mappings[i]) { - parse_mapping(DefaultControllerMappings::mappings[i++]); - } - } - - // If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides. - String env_mapping = OS::get_singleton()->get_environment("SDL_GAMECONTROLLERCONFIG"); - if (env_mapping != "") { - Vector<String> entries = env_mapping.split("\n"); - for (int i = 0; i < entries.size(); i++) { - if (entries[i] == "") - continue; - parse_mapping(entries[i]); - } - } -} - -void InputDefault::joy_button(int p_device, int p_button, bool p_pressed) { +void Input::joy_button(int p_device, int p_button, bool p_pressed) { _THREAD_SAFE_METHOD_; Joypad &joy = joy_names[p_device]; @@ -786,7 +863,7 @@ void InputDefault::joy_button(int p_device, int p_button, bool p_pressed) { // no event? } -void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { +void Input::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { _THREAD_SAFE_METHOD_; @@ -901,7 +978,7 @@ void InputDefault::joy_axis(int p_device, int p_axis, const JoyAxis &p_value) { //printf("invalid mapping\n"); } -void InputDefault::joy_hat(int p_device, int p_val) { +void Input::joy_hat(int p_device, int p_val) { _THREAD_SAFE_METHOD_; const Joypad &joy = joy_names[p_device]; @@ -933,7 +1010,7 @@ void InputDefault::joy_hat(int p_device, int p_val) { joy_names[p_device].hat_current = p_val; } -void InputDefault::_button_event(int p_device, int p_index, bool p_pressed) { +void Input::_button_event(int p_device, int p_index, bool p_pressed) { Ref<InputEventJoypadButton> ievent; ievent.instance(); @@ -944,7 +1021,7 @@ void InputDefault::_button_event(int p_device, int p_index, bool p_pressed) { parse_input_event(ievent); } -void InputDefault::_axis_event(int p_device, int p_axis, float p_value) { +void Input::_axis_event(int p_device, int p_axis, float p_value) { Ref<InputEventJoypadMotion> ievent; ievent.instance(); @@ -955,7 +1032,7 @@ void InputDefault::_axis_event(int p_device, int p_axis, float p_value) { parse_input_event(ievent); }; -InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) { +Input::JoyEvent Input::_find_to_event(String p_to) { // string names of the SDL buttons in the same order as input_event.h godot buttons static const char *buttons[] = { "a", "b", "x", "y", "leftshoulder", "rightshoulder", "lefttrigger", "righttrigger", "leftstick", "rightstick", "back", "start", "dpup", "dpdown", "dpleft", "dpright", "guide", NULL }; @@ -993,7 +1070,7 @@ InputDefault::JoyEvent InputDefault::_find_to_event(String p_to) { return ret; }; -void InputDefault::parse_mapping(String p_mapping) { +void Input::parse_mapping(String p_mapping) { _THREAD_SAFE_METHOD_; JoyDeviceMapping mapping; @@ -1058,7 +1135,7 @@ void InputDefault::parse_mapping(String p_mapping) { //printf("added mapping with uuid %ls\n", mapping.uid.c_str()); }; -void InputDefault::add_joy_mapping(String p_mapping, bool p_update_existing) { +void Input::add_joy_mapping(String p_mapping, bool p_update_existing) { parse_mapping(p_mapping); if (p_update_existing) { Vector<String> entry = p_mapping.split(","); @@ -1071,7 +1148,7 @@ void InputDefault::add_joy_mapping(String p_mapping, bool p_update_existing) { } } -void InputDefault::remove_joy_mapping(String p_guid) { +void Input::remove_joy_mapping(String p_guid) { for (int i = map_db.size() - 1; i >= 0; i--) { if (p_guid == map_db[i].uid) { map_db.remove(i); @@ -1084,7 +1161,7 @@ void InputDefault::remove_joy_mapping(String p_guid) { } } -void InputDefault::set_fallback_mapping(String p_guid) { +void Input::set_fallback_mapping(String p_guid) { for (int i = 0; i < map_db.size(); i++) { if (map_db[i].uid == p_guid) { @@ -1095,27 +1172,27 @@ void InputDefault::set_fallback_mapping(String p_guid) { } //Defaults to simple implementation for platforms with a fixed gamepad layout, like consoles. -bool InputDefault::is_joy_known(int p_device) { +bool Input::is_joy_known(int p_device) { return OS::get_singleton()->is_joy_known(p_device); } -String InputDefault::get_joy_guid(int p_device) const { +String Input::get_joy_guid(int p_device) const { return OS::get_singleton()->get_joy_guid(p_device); } //platforms that use the remapping system can override and call to these ones -bool InputDefault::is_joy_mapped(int p_device) { +bool Input::is_joy_mapped(int p_device) { int mapping = joy_names[p_device].mapping; return mapping != -1 ? (mapping != fallback_mapping) : false; } -String InputDefault::get_joy_guid_remapped(int p_device) const { +String Input::get_joy_guid_remapped(int p_device) const { ERR_FAIL_COND_V(!joy_names.has(p_device), ""); return joy_names[p_device].uid; } -Array InputDefault::get_connected_joypads() { +Array Input::get_connected_joypads() { Array ret; Map<int, Joypad>::Element *elem = joy_names.front(); while (elem) { @@ -1159,12 +1236,12 @@ static const char *_axes[JOY_AXIS_MAX] = { "" }; -String InputDefault::get_joy_button_string(int p_button) { +String Input::get_joy_button_string(int p_button) { ERR_FAIL_INDEX_V(p_button, JOY_BUTTON_MAX, ""); return _buttons[p_button]; } -int InputDefault::get_joy_button_index_from_string(String p_button) { +int Input::get_joy_button_index_from_string(String p_button) { for (int i = 0; i < JOY_BUTTON_MAX; i++) { if (p_button == _buttons[i]) { return i; @@ -1173,7 +1250,7 @@ int InputDefault::get_joy_button_index_from_string(String p_button) { ERR_FAIL_V(-1); } -int InputDefault::get_unused_joy_id() { +int Input::get_unused_joy_id() { for (int i = 0; i < JOYPADS_MAX; i++) { if (!joy_names.has(i) || !joy_names[i].connected) { return i; @@ -1182,12 +1259,12 @@ int InputDefault::get_unused_joy_id() { return -1; } -String InputDefault::get_joy_axis_string(int p_axis) { +String Input::get_joy_axis_string(int p_axis) { ERR_FAIL_INDEX_V(p_axis, JOY_AXIS_MAX, ""); return _axes[p_axis]; } -int InputDefault::get_joy_axis_index_from_string(String p_axis) { +int Input::get_joy_axis_index_from_string(String p_axis) { for (int i = 0; i < JOY_AXIS_MAX; i++) { if (p_axis == _axes[i]) { return i; @@ -1195,3 +1272,55 @@ int InputDefault::get_joy_axis_index_from_string(String p_axis) { } ERR_FAIL_V(-1); } + +Input::Input() { + + singleton = this; + use_accumulated_input = true; + mouse_button_mask = 0; + mouse_window = 0; + emulate_touch_from_mouse = false; + emulate_mouse_from_touch = false; + mouse_from_touch_index = -1; + main_loop = NULL; + default_shape = CURSOR_ARROW; + + hat_map_default[HAT_UP].type = TYPE_BUTTON; + hat_map_default[HAT_UP].index = JOY_DPAD_UP; + hat_map_default[HAT_UP].value = 0; + + hat_map_default[HAT_RIGHT].type = TYPE_BUTTON; + hat_map_default[HAT_RIGHT].index = JOY_DPAD_RIGHT; + hat_map_default[HAT_RIGHT].value = 0; + + hat_map_default[HAT_DOWN].type = TYPE_BUTTON; + hat_map_default[HAT_DOWN].index = JOY_DPAD_DOWN; + hat_map_default[HAT_DOWN].value = 0; + + hat_map_default[HAT_LEFT].type = TYPE_BUTTON; + hat_map_default[HAT_LEFT].index = JOY_DPAD_LEFT; + hat_map_default[HAT_LEFT].value = 0; + + fallback_mapping = -1; + + // Parse default mappings. + { + int i = 0; + while (DefaultControllerMappings::mappings[i]) { + parse_mapping(DefaultControllerMappings::mappings[i++]); + } + } + + // If defined, parse SDL_GAMECONTROLLERCONFIG for possible new mappings/overrides. + String env_mapping = OS::get_singleton()->get_environment("SDL_GAMECONTROLLERCONFIG"); + if (env_mapping != "") { + Vector<String> entries = env_mapping.split("\n"); + for (int i = 0; i < entries.size(); i++) { + if (entries[i] == "") + continue; + parse_mapping(entries[i]); + } + } +} + +////////////////////////////////////////////////////////// diff --git a/main/input_default.h b/core/input/input.h index 0ec31d417b..eb2880e953 100644 --- a/main/input_default.h +++ b/core/input/input.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* input_default.h */ +/* input.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -28,16 +28,76 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef INPUT_DEFAULT_H -#define INPUT_DEFAULT_H +#ifndef INPUT_H +#define INPUT_H -#include "core/os/input.h" +#include "core/object.h" +#include "core/os/main_loop.h" +#include "core/os/thread_safe.h" -class InputDefault : public Input { +class Input : public Object { - GDCLASS(InputDefault, Input); + GDCLASS(Input, Object); _THREAD_SAFE_CLASS_ + static Input *singleton; + +public: + enum MouseMode { + MOUSE_MODE_VISIBLE, + MOUSE_MODE_HIDDEN, + MOUSE_MODE_CAPTURED, + MOUSE_MODE_CONFINED + }; + +#undef CursorShape + enum CursorShape { + CURSOR_ARROW, + CURSOR_IBEAM, + CURSOR_POINTING_HAND, + CURSOR_CROSS, + CURSOR_WAIT, + CURSOR_BUSY, + CURSOR_DRAG, + CURSOR_CAN_DROP, + CURSOR_FORBIDDEN, + CURSOR_VSIZE, + CURSOR_HSIZE, + CURSOR_BDIAGSIZE, + CURSOR_FDIAGSIZE, + CURSOR_MOVE, + CURSOR_VSPLIT, + CURSOR_HSPLIT, + CURSOR_HELP, + CURSOR_MAX + }; + + enum HatMask { + HAT_MASK_CENTER = 0, + HAT_MASK_UP = 1, + HAT_MASK_RIGHT = 2, + HAT_MASK_DOWN = 4, + HAT_MASK_LEFT = 8, + }; + + enum HatDir { + HAT_UP, + HAT_RIGHT, + HAT_DOWN, + HAT_LEFT, + HAT_MAX, + }; + + enum { + JOYPADS_MAX = 16, + }; + + struct JoyAxis { + int min; + float value; + }; + +private: int mouse_button_mask; Set<int> keys_pressed; @@ -49,6 +109,7 @@ class InputDefault : public Input { Vector3 magnetometer; Vector3 gyroscope; Vector2 mouse_pos; + int64_t mouse_window; MainLoop *main_loop; struct Action { @@ -123,33 +184,6 @@ class InputDefault : public Input { CursorShape default_shape; -public: - enum HatMask { - HAT_MASK_CENTER = 0, - HAT_MASK_UP = 1, - HAT_MASK_RIGHT = 2, - HAT_MASK_DOWN = 4, - HAT_MASK_LEFT = 8, - }; - - enum HatDir { - HAT_UP, - HAT_RIGHT, - HAT_DOWN, - HAT_LEFT, - HAT_MAX, - }; - - enum { - JOYPADS_MAX = 16, - }; - - struct JoyAxis { - int min; - float value; - }; - -private: enum JoyType { TYPE_BUTTON, TYPE_AXIS, @@ -186,37 +220,48 @@ private: List<Ref<InputEvent>> accumulated_events; bool use_accumulated_input; +protected: + static void _bind_methods(); + public: - virtual bool is_key_pressed(int p_keycode) const; - virtual bool is_mouse_button_pressed(int p_button) const; - virtual bool is_joy_button_pressed(int p_device, int p_button) const; - virtual bool is_action_pressed(const StringName &p_action) const; - virtual bool is_action_just_pressed(const StringName &p_action) const; - virtual bool is_action_just_released(const StringName &p_action) const; - virtual float get_action_strength(const StringName &p_action) const; - - virtual float get_joy_axis(int p_device, int p_axis) const; + void set_mouse_mode(MouseMode p_mode); + MouseMode get_mouse_mode() const; + void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; + + static Input *get_singleton(); + + bool is_key_pressed(int p_keycode) const; + bool is_mouse_button_pressed(int p_button) const; + bool is_joy_button_pressed(int p_device, int p_button) const; + bool is_action_pressed(const StringName &p_action) const; + bool is_action_just_pressed(const StringName &p_action) const; + bool is_action_just_released(const StringName &p_action) const; + float get_action_strength(const StringName &p_action) const; + + float get_joy_axis(int p_device, int p_axis) const; String get_joy_name(int p_idx); - virtual Array get_connected_joypads(); - virtual Vector2 get_joy_vibration_strength(int p_device); - virtual float get_joy_vibration_duration(int p_device); - virtual uint64_t get_joy_vibration_timestamp(int p_device); + Array get_connected_joypads(); + Vector2 get_joy_vibration_strength(int p_device); + float get_joy_vibration_duration(int p_device); + uint64_t get_joy_vibration_timestamp(int p_device); void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = ""); void parse_joypad_mapping(String p_mapping, bool p_update_existing); - virtual Vector3 get_gravity() const; - virtual Vector3 get_accelerometer() const; - virtual Vector3 get_magnetometer() const; - virtual Vector3 get_gyroscope() const; + Vector3 get_gravity() const; + Vector3 get_accelerometer() const; + Vector3 get_magnetometer() const; + Vector3 get_gyroscope() const; - virtual Point2 get_mouse_position() const; - virtual Point2 get_last_mouse_speed() const; - virtual int get_mouse_button_mask() const; + Point2 get_mouse_position() const; + Point2 get_last_mouse_speed() const; + int get_mouse_button_mask() const; - virtual void warp_mouse_position(const Vector2 &p_to); - virtual Point2i warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect); + void warp_mouse_position(const Vector2 &p_to); + Point2i warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect); - virtual void parse_input_event(const Ref<InputEvent> &p_event); + void parse_drop_files(const Vector<String> &p_files); + void parse_notification(int p_notification); + void parse_input_event(const Ref<InputEvent> &p_event); void set_gravity(const Vector3 &p_gravity); void set_accelerometer(const Vector3 &p_accel); @@ -224,9 +269,9 @@ public: void set_gyroscope(const Vector3 &p_gyroscope); void set_joy_axis(int p_device, int p_axis, float p_value); - virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); - virtual void stop_joy_vibration(int p_device); - virtual void vibrate_handheld(int p_duration_ms = 500); + void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0); + void stop_joy_vibration(int p_device); + void vibrate_handheld(int p_duration_ms = 500); void set_main_loop(MainLoop *p_main_loop); void set_mouse_position(const Point2 &p_posf); @@ -237,31 +282,31 @@ public: void iteration(float p_step); void set_emulate_touch_from_mouse(bool p_emulate); - virtual bool is_emulating_touch_from_mouse() const; + bool is_emulating_touch_from_mouse() const; void ensure_touch_mouse_raised(); void set_emulate_mouse_from_touch(bool p_emulate); - virtual bool is_emulating_mouse_from_touch() const; + bool is_emulating_mouse_from_touch() const; - virtual CursorShape get_default_cursor_shape() const; - virtual void set_default_cursor_shape(CursorShape p_shape); - virtual CursorShape get_current_cursor_shape() const; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); + CursorShape get_default_cursor_shape() const; + void set_default_cursor_shape(CursorShape p_shape); + CursorShape get_current_cursor_shape() const; + void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = Input::CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()); void parse_mapping(String p_mapping); void joy_button(int p_device, int p_button, bool p_pressed); void joy_axis(int p_device, int p_axis, const JoyAxis &p_value); void joy_hat(int p_device, int p_val); - virtual void add_joy_mapping(String p_mapping, bool p_update_existing = false); - virtual void remove_joy_mapping(String p_guid); - virtual bool is_joy_known(int p_device); - virtual String get_joy_guid(int p_device) const; + void add_joy_mapping(String p_mapping, bool p_update_existing = false); + void remove_joy_mapping(String p_guid); + bool is_joy_known(int p_device); + String get_joy_guid(int p_device) const; - virtual String get_joy_button_string(int p_button); - virtual String get_joy_axis_string(int p_axis); - virtual int get_joy_axis_index_from_string(String p_axis); - virtual int get_joy_button_index_from_string(String p_button); + String get_joy_button_string(int p_button); + String get_joy_axis_string(int p_axis); + int get_joy_axis_index_from_string(String p_axis); + int get_joy_button_index_from_string(String p_button); int get_unused_joy_id(); @@ -269,12 +314,16 @@ public: String get_joy_guid_remapped(int p_device) const; void set_fallback_mapping(String p_guid); - virtual void accumulate_input_event(const Ref<InputEvent> &p_event); - virtual void flush_accumulated_events(); - virtual void set_use_accumulated_input(bool p_enable); + void accumulate_input_event(const Ref<InputEvent> &p_event); + void flush_accumulated_events(); + void set_use_accumulated_input(bool p_enable); - virtual void release_pressed_events(); - InputDefault(); + void release_pressed_events(); + + Input(); }; -#endif // INPUT_DEFAULT_H +VARIANT_ENUM_CAST(Input::MouseMode); +VARIANT_ENUM_CAST(Input::CursorShape); + +#endif // INPUT_H diff --git a/core/input/input_builders.py b/core/input/input_builders.py new file mode 100644 index 0000000000..ca142c0c80 --- /dev/null +++ b/core/input/input_builders.py @@ -0,0 +1,73 @@ +"""Functions used to generate source files during build time + +All such functions are invoked in a subprocess on Windows to prevent build flakiness. +""" + +from platform_methods import subprocess_main +from collections import OrderedDict + + +def make_default_controller_mappings(target, source, env): + dst = target[0] + g = open(dst, "w") + + g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") + g.write("#include \"core/typedefs.h\"\n") + g.write("#include \"core/input/default_controller_mappings.h\"\n") + + # ensure mappings have a consistent order + platform_mappings = OrderedDict() + for src_path in source: + with open(src_path, "r") as f: + # read mapping file and skip header + mapping_file_lines = f.readlines()[2:] + + current_platform = None + for line in mapping_file_lines: + if not line: + continue + line = line.strip() + if len(line) == 0: + continue + if line[0] == "#": + current_platform = line[1:].strip() + if current_platform not in platform_mappings: + platform_mappings[current_platform] = {} + elif current_platform: + line_parts = line.split(",") + guid = line_parts[0] + if guid in platform_mappings[current_platform]: + g.write("// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(src_path, current_platform, platform_mappings[current_platform][guid])) + valid_mapping = True + for input_map in line_parts[2:]: + if "+" in input_map or "-" in input_map or "~" in input_map: + g.write("// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format(src_path, current_platform, line)) + valid_mapping = False + break + if valid_mapping: + platform_mappings[current_platform][guid] = line + + platform_variables = { + "Linux": "#if X11_ENABLED", + "Windows": "#ifdef WINDOWS_ENABLED", + "Mac OS X": "#ifdef OSX_ENABLED", + "Android": "#if defined(__ANDROID__)", + "iOS": "#ifdef IPHONE_ENABLED", + "Javascript": "#ifdef JAVASCRIPT_ENABLED", + "UWP": "#ifdef UWP_ENABLED", + } + + g.write("const char* DefaultControllerMappings::mappings[] = {\n") + for platform, mappings in platform_mappings.items(): + variable = platform_variables[platform] + g.write("{}\n".format(variable)) + for mapping in mappings.values(): + g.write("\t\"{}\",\n".format(mapping)) + g.write("#endif\n") + + g.write("\tNULL\n};\n") + g.close() + + +if __name__ == '__main__': + subprocess_main(globals()) diff --git a/core/os/input_event.cpp b/core/input/input_event.cpp index 204a36bf56..088d185b8d 100644 --- a/core/os/input_event.cpp +++ b/core/input/input_event.cpp @@ -30,7 +30,7 @@ #include "input_event.h" -#include "core/input_map.h" +#include "core/input/input_map.h" #include "core/os/keyboard.h" const int InputEvent::DEVICE_ID_TOUCH_MOUSE = -1; @@ -136,6 +136,25 @@ InputEvent::InputEvent() { device = 0; } +//////////////// + +void InputEventFromWindow::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_window_id", "id"), &InputEventFromWindow::set_window_id); + ClassDB::bind_method(D_METHOD("get_window_id"), &InputEventFromWindow::get_window_id); + ADD_PROPERTY(PropertyInfo(Variant::INT, "window_id"), "set_window_id", "get_window_id"); +} + +void InputEventFromWindow::set_window_id(int64_t p_id) { + window_id = p_id; +} +int64_t InputEventFromWindow::get_window_id() const { + return window_id; +} + +InputEventFromWindow::InputEventFromWindow() { + window_id = 0; +} ////////////////// @@ -499,7 +518,7 @@ Ref<InputEvent> InputEventMouseButton::xformed_by(const Transform2D &p_xform, co mb.instance(); mb->set_device(get_device()); - + mb->set_window_id(get_window_id()); mb->set_modifiers_from_event(this); mb->set_position(l); @@ -650,6 +669,7 @@ Ref<InputEvent> InputEventMouseMotion::xformed_by(const Transform2D &p_xform, co mm.instance(); mm->set_device(get_device()); + mm->set_window_id(get_window_id()); mm->set_modifiers_from_event(this); @@ -697,6 +717,10 @@ bool InputEventMouseMotion::accumulate(const Ref<InputEvent> &p_event) { if (motion.is_null()) return false; + if (get_window_id() != motion->get_window_id()) { + return false; + } + if (is_pressed() != motion->is_pressed()) { return false; } @@ -948,6 +972,7 @@ Ref<InputEvent> InputEventScreenTouch::xformed_by(const Transform2D &p_xform, co Ref<InputEventScreenTouch> st; st.instance(); st->set_device(get_device()); + st->set_window_id(get_window_id()); st->set_index(index); st->set_position(p_xform.xform(pos + p_local_ofs)); st->set_pressed(pressed); @@ -1028,6 +1053,7 @@ Ref<InputEvent> InputEventScreenDrag::xformed_by(const Transform2D &p_xform, con sd.instance(); sd->set_device(get_device()); + sd->set_window_id(get_window_id()); sd->set_index(index); sd->set_position(p_xform.xform(pos + p_local_ofs)); @@ -1186,6 +1212,8 @@ Ref<InputEvent> InputEventMagnifyGesture::xformed_by(const Transform2D &p_xform, ev.instance(); ev->set_device(get_device()); + ev->set_window_id(get_window_id()); + ev->set_modifiers_from_event(this); ev->set_position(p_xform.xform(get_position() + p_local_ofs)); @@ -1228,6 +1256,8 @@ Ref<InputEvent> InputEventPanGesture::xformed_by(const Transform2D &p_xform, con ev.instance(); ev->set_device(get_device()); + ev->set_window_id(get_window_id()); + ev->set_modifiers_from_event(this); ev->set_position(p_xform.xform(get_position() + p_local_ofs)); diff --git a/core/os/input_event.h b/core/input/input_event.h index 21549d811f..2fdcdd0319 100644 --- a/core/os/input_event.h +++ b/core/input/input_event.h @@ -205,8 +205,24 @@ public: InputEvent(); }; -class InputEventWithModifiers : public InputEvent { - GDCLASS(InputEventWithModifiers, InputEvent); +class InputEventFromWindow : public InputEvent { + + GDCLASS(InputEventFromWindow, InputEvent); + + int64_t window_id; + +protected: + static void _bind_methods(); + +public: + void set_window_id(int64_t p_id); + int64_t get_window_id() const; + + InputEventFromWindow(); +}; + +class InputEventWithModifiers : public InputEventFromWindow { + GDCLASS(InputEventWithModifiers, InputEventFromWindow); bool shift; bool alt; @@ -440,8 +456,8 @@ public: InputEventJoypadButton(); }; -class InputEventScreenTouch : public InputEvent { - GDCLASS(InputEventScreenTouch, InputEvent); +class InputEventScreenTouch : public InputEventFromWindow { + GDCLASS(InputEventScreenTouch, InputEventFromWindow); int index; Vector2 pos; bool pressed; @@ -465,9 +481,9 @@ public: InputEventScreenTouch(); }; -class InputEventScreenDrag : public InputEvent { +class InputEventScreenDrag : public InputEventFromWindow { - GDCLASS(InputEventScreenDrag, InputEvent); + GDCLASS(InputEventScreenDrag, InputEventFromWindow); int index; Vector2 pos; Vector2 relative; diff --git a/core/input_map.cpp b/core/input/input_map.cpp index 8f18c082d6..8f18c082d6 100644 --- a/core/input_map.cpp +++ b/core/input/input_map.cpp diff --git a/core/input_map.h b/core/input/input_map.h index 6039d915c9..de6f57b0bf 100644 --- a/core/input_map.h +++ b/core/input/input_map.h @@ -31,8 +31,8 @@ #ifndef INPUT_MAP_H #define INPUT_MAP_H +#include "core/input/input_event.h" #include "core/object.h" -#include "core/os/input_event.h" class InputMap : public Object { diff --git a/core/method_ptrcall.h b/core/method_ptrcall.h index d09242e2f1..7ae0a788bd 100644 --- a/core/method_ptrcall.h +++ b/core/method_ptrcall.h @@ -118,6 +118,9 @@ MAKE_PTRARG(String); MAKE_PTRARG(Vector2); MAKE_PTRARG(Rect2); MAKE_PTRARG_BY_REFERENCE(Vector3); +MAKE_PTRARG(Vector2i); +MAKE_PTRARG(Rect2i); +MAKE_PTRARG_BY_REFERENCE(Vector3i); MAKE_PTRARG(Transform2D); MAKE_PTRARG_BY_REFERENCE(Plane); MAKE_PTRARG(Quat); diff --git a/core/os/input.cpp b/core/os/input.cpp deleted file mode 100644 index 1768b851df..0000000000 --- a/core/os/input.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/*************************************************************************/ -/* input.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "input.h" - -#include "core/input_map.h" -#include "core/os/os.h" -#include "core/project_settings.h" - -#ifdef TOOLS_ENABLED -#include "editor/editor_settings.h" -#endif - -Input *Input::singleton = NULL; - -Input *Input::get_singleton() { - - return singleton; -} - -void Input::set_mouse_mode(MouseMode p_mode) { - ERR_FAIL_INDEX((int)p_mode, 4); - OS::get_singleton()->set_mouse_mode((OS::MouseMode)p_mode); -} - -Input::MouseMode Input::get_mouse_mode() const { - - return (MouseMode)OS::get_singleton()->get_mouse_mode(); -} - -void Input::_bind_methods() { - - ClassDB::bind_method(D_METHOD("is_key_pressed", "keycode"), &Input::is_key_pressed); - ClassDB::bind_method(D_METHOD("is_mouse_button_pressed", "button"), &Input::is_mouse_button_pressed); - ClassDB::bind_method(D_METHOD("is_joy_button_pressed", "device", "button"), &Input::is_joy_button_pressed); - ClassDB::bind_method(D_METHOD("is_action_pressed", "action"), &Input::is_action_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_pressed", "action"), &Input::is_action_just_pressed); - ClassDB::bind_method(D_METHOD("is_action_just_released", "action"), &Input::is_action_just_released); - ClassDB::bind_method(D_METHOD("get_action_strength", "action"), &Input::get_action_strength); - ClassDB::bind_method(D_METHOD("add_joy_mapping", "mapping", "update_existing"), &Input::add_joy_mapping, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("remove_joy_mapping", "guid"), &Input::remove_joy_mapping); - ClassDB::bind_method(D_METHOD("joy_connection_changed", "device", "connected", "name", "guid"), &Input::joy_connection_changed); - ClassDB::bind_method(D_METHOD("is_joy_known", "device"), &Input::is_joy_known); - ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis); - ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name); - ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid); - ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads); - ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength); - ClassDB::bind_method(D_METHOD("get_joy_vibration_duration", "device"), &Input::get_joy_vibration_duration); - ClassDB::bind_method(D_METHOD("get_joy_button_string", "button_index"), &Input::get_joy_button_string); - ClassDB::bind_method(D_METHOD("get_joy_button_index_from_string", "button"), &Input::get_joy_button_index_from_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_string", "axis_index"), &Input::get_joy_axis_string); - ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string); - ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0)); - ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration); - ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500)); - ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity); - ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer); - ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer); - ClassDB::bind_method(D_METHOD("get_gyroscope"), &Input::get_gyroscope); - //ClassDB::bind_method(D_METHOD("get_mouse_position"),&Input::get_mouse_position); - this is not the function you want - ClassDB::bind_method(D_METHOD("get_last_mouse_speed"), &Input::get_last_mouse_speed); - ClassDB::bind_method(D_METHOD("get_mouse_button_mask"), &Input::get_mouse_button_mask); - ClassDB::bind_method(D_METHOD("set_mouse_mode", "mode"), &Input::set_mouse_mode); - ClassDB::bind_method(D_METHOD("get_mouse_mode"), &Input::get_mouse_mode); - ClassDB::bind_method(D_METHOD("warp_mouse_position", "to"), &Input::warp_mouse_position); - ClassDB::bind_method(D_METHOD("action_press", "action", "strength"), &Input::action_press, DEFVAL(1.f)); - ClassDB::bind_method(D_METHOD("action_release", "action"), &Input::action_release); - ClassDB::bind_method(D_METHOD("set_default_cursor_shape", "shape"), &Input::set_default_cursor_shape, DEFVAL(CURSOR_ARROW)); - ClassDB::bind_method(D_METHOD("get_current_cursor_shape"), &Input::get_current_cursor_shape); - ClassDB::bind_method(D_METHOD("set_custom_mouse_cursor", "image", "shape", "hotspot"), &Input::set_custom_mouse_cursor, DEFVAL(CURSOR_ARROW), DEFVAL(Vector2())); - ClassDB::bind_method(D_METHOD("parse_input_event", "event"), &Input::parse_input_event); - ClassDB::bind_method(D_METHOD("set_use_accumulated_input", "enable"), &Input::set_use_accumulated_input); - - BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); - BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); - BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); - BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); - - BIND_ENUM_CONSTANT(CURSOR_ARROW); - BIND_ENUM_CONSTANT(CURSOR_IBEAM); - BIND_ENUM_CONSTANT(CURSOR_POINTING_HAND); - BIND_ENUM_CONSTANT(CURSOR_CROSS); - BIND_ENUM_CONSTANT(CURSOR_WAIT); - BIND_ENUM_CONSTANT(CURSOR_BUSY); - BIND_ENUM_CONSTANT(CURSOR_DRAG); - BIND_ENUM_CONSTANT(CURSOR_CAN_DROP); - BIND_ENUM_CONSTANT(CURSOR_FORBIDDEN); - BIND_ENUM_CONSTANT(CURSOR_VSIZE); - BIND_ENUM_CONSTANT(CURSOR_HSIZE); - BIND_ENUM_CONSTANT(CURSOR_BDIAGSIZE); - BIND_ENUM_CONSTANT(CURSOR_FDIAGSIZE); - BIND_ENUM_CONSTANT(CURSOR_MOVE); - BIND_ENUM_CONSTANT(CURSOR_VSPLIT); - BIND_ENUM_CONSTANT(CURSOR_HSPLIT); - BIND_ENUM_CONSTANT(CURSOR_HELP); - - ADD_SIGNAL(MethodInfo("joy_connection_changed", PropertyInfo(Variant::INT, "device"), PropertyInfo(Variant::BOOL, "connected"))); -} - -void Input::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { -#ifdef TOOLS_ENABLED - - const String quote_style = EDITOR_DEF("text_editor/completion/use_single_quotes", 0) ? "'" : "\""; - - String pf = p_function; - if (p_idx == 0 && (pf == "is_action_pressed" || pf == "action_press" || pf == "action_release" || pf == "is_action_just_pressed" || pf == "is_action_just_released" || pf == "get_action_strength")) { - - List<PropertyInfo> pinfo; - ProjectSettings::get_singleton()->get_property_list(&pinfo); - - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - const PropertyInfo &pi = E->get(); - - if (!pi.name.begins_with("input/")) - continue; - - String name = pi.name.substr(pi.name.find("/") + 1, pi.name.length()); - r_options->push_back(quote_style + name + quote_style); - } - } -#endif -} - -Input::Input() { - - singleton = this; -} - -////////////////////////////////////////////////////////// diff --git a/core/os/input.h b/core/os/input.h deleted file mode 100644 index 55e0511080..0000000000 --- a/core/os/input.h +++ /dev/null @@ -1,146 +0,0 @@ -/*************************************************************************/ -/* input.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef INPUT_H -#define INPUT_H - -#include "core/object.h" -#include "core/os/main_loop.h" -#include "core/os/thread_safe.h" - -class Input : public Object { - - GDCLASS(Input, Object); - - static Input *singleton; - -protected: - static void _bind_methods(); - -public: - enum MouseMode { - MOUSE_MODE_VISIBLE, - MOUSE_MODE_HIDDEN, - MOUSE_MODE_CAPTURED, - MOUSE_MODE_CONFINED - }; - -#undef CursorShape - enum CursorShape { - CURSOR_ARROW, - CURSOR_IBEAM, - CURSOR_POINTING_HAND, - CURSOR_CROSS, - CURSOR_WAIT, - CURSOR_BUSY, - CURSOR_DRAG, - CURSOR_CAN_DROP, - CURSOR_FORBIDDEN, - CURSOR_VSIZE, - CURSOR_HSIZE, - CURSOR_BDIAGSIZE, - CURSOR_FDIAGSIZE, - CURSOR_MOVE, - CURSOR_VSPLIT, - CURSOR_HSPLIT, - CURSOR_HELP, - CURSOR_MAX - }; - - void set_mouse_mode(MouseMode p_mode); - MouseMode get_mouse_mode() const; - - static Input *get_singleton(); - - virtual bool is_key_pressed(int p_keycode) const = 0; - virtual bool is_mouse_button_pressed(int p_button) const = 0; - virtual bool is_joy_button_pressed(int p_device, int p_button) const = 0; - virtual bool is_action_pressed(const StringName &p_action) const = 0; - virtual bool is_action_just_pressed(const StringName &p_action) const = 0; - virtual bool is_action_just_released(const StringName &p_action) const = 0; - virtual float get_action_strength(const StringName &p_action) const = 0; - - virtual float get_joy_axis(int p_device, int p_axis) const = 0; - virtual String get_joy_name(int p_idx) = 0; - virtual Array get_connected_joypads() = 0; - virtual void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) = 0; - virtual void add_joy_mapping(String p_mapping, bool p_update_existing = false) = 0; - virtual void remove_joy_mapping(String p_guid) = 0; - virtual bool is_joy_known(int p_device) = 0; - virtual String get_joy_guid(int p_device) const = 0; - virtual Vector2 get_joy_vibration_strength(int p_device) = 0; - virtual float get_joy_vibration_duration(int p_device) = 0; - virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0; - virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0; - virtual void stop_joy_vibration(int p_device) = 0; - virtual void vibrate_handheld(int p_duration_ms = 500) = 0; - - virtual Point2 get_mouse_position() const = 0; - virtual Point2 get_last_mouse_speed() const = 0; - virtual int get_mouse_button_mask() const = 0; - - virtual void warp_mouse_position(const Vector2 &p_to) = 0; - virtual Point2i warp_mouse_motion(const Ref<InputEventMouseMotion> &p_motion, const Rect2 &p_rect) = 0; - - virtual Vector3 get_gravity() const = 0; - virtual Vector3 get_accelerometer() const = 0; - virtual Vector3 get_magnetometer() const = 0; - virtual Vector3 get_gyroscope() const = 0; - - virtual void action_press(const StringName &p_action, float p_strength = 1.f) = 0; - virtual void action_release(const StringName &p_action) = 0; - - void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const; - - virtual bool is_emulating_touch_from_mouse() const = 0; - virtual bool is_emulating_mouse_from_touch() const = 0; - - virtual CursorShape get_default_cursor_shape() const = 0; - virtual void set_default_cursor_shape(CursorShape p_shape) = 0; - virtual CursorShape get_current_cursor_shape() const = 0; - virtual void set_custom_mouse_cursor(const RES &p_cursor, CursorShape p_shape = CURSOR_ARROW, const Vector2 &p_hotspot = Vector2()) = 0; - - virtual String get_joy_button_string(int p_button) = 0; - virtual String get_joy_axis_string(int p_axis) = 0; - virtual int get_joy_button_index_from_string(String p_button) = 0; - virtual int get_joy_axis_index_from_string(String p_axis) = 0; - - virtual void parse_input_event(const Ref<InputEvent> &p_event) = 0; - virtual void accumulate_input_event(const Ref<InputEvent> &p_event) = 0; - virtual void flush_accumulated_events() = 0; - virtual void set_use_accumulated_input(bool p_enable) = 0; - - Input(); -}; - -VARIANT_ENUM_CAST(Input::MouseMode); -VARIANT_ENUM_CAST(Input::CursorShape); - -#endif // INPUT_H diff --git a/core/os/main_loop.h b/core/os/main_loop.h index 3fdfb77af2..2b7ec8fc5d 100644 --- a/core/os/main_loop.h +++ b/core/os/main_loop.h @@ -31,7 +31,7 @@ #ifndef MAIN_LOOP_H #define MAIN_LOOP_H -#include "core/os/input_event.h" +#include "core/input/input_event.h" #include "core/reference.h" #include "core/script_language.h" diff --git a/core/os/midi_driver.cpp b/core/os/midi_driver.cpp index 6ebec50ff0..68c3bf3912 100644 --- a/core/os/midi_driver.cpp +++ b/core/os/midi_driver.cpp @@ -30,8 +30,8 @@ #include "midi_driver.h" +#include "core/input/input.h" #include "core/os/os.h" -#include "main/input_default.h" uint8_t MIDIDriver::last_received_message = 0x00; MIDIDriver *MIDIDriver::singleton = NULL; @@ -117,7 +117,7 @@ void MIDIDriver::receive_input_packet(uint64_t timestamp, uint8_t *data, uint32_ break; } - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + Input *id = Input::get_singleton(); id->parse_input_event(event); } diff --git a/core/os/os.cpp b/core/os/os.cpp index 9a65d537ac..b9c0040627 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -30,9 +30,9 @@ #include "os.h" +#include "core/input/input.h" #include "core/os/dir_access.h" #include "core/os/file_access.h" -#include "core/os/input.h" #include "core/os/midi_driver.h" #include "core/project_settings.h" #include "core/version_generated.gen.h" diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index b0ba8ed194..929df0686e 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -38,7 +38,8 @@ #include "core/crypto/hashing_context.h" #include "core/engine.h" #include "core/func_ref.h" -#include "core/input_map.h" +#include "core/input/input.h" +#include "core/input/input_map.h" #include "core/io/config_file.h" #include "core/io/dtls_server.h" #include "core/io/http_client.h" @@ -62,7 +63,6 @@ #include "core/math/geometry.h" #include "core/math/random_number_generator.h" #include "core/math/triangle_mesh.h" -#include "core/os/input.h" #include "core/os/main_loop.h" #include "core/packed_data_container.h" #include "core/path_remap.h" diff --git a/core/type_info.h b/core/type_info.h index 7e4b192b27..816d0d9381 100644 --- a/core/type_info.h +++ b/core/type_info.h @@ -144,6 +144,9 @@ MAKE_TYPE_INFO(String, Variant::STRING) MAKE_TYPE_INFO(Vector2, Variant::VECTOR2) MAKE_TYPE_INFO(Rect2, Variant::RECT2) MAKE_TYPE_INFO(Vector3, Variant::VECTOR3) +MAKE_TYPE_INFO(Vector2i, Variant::VECTOR2I) +MAKE_TYPE_INFO(Rect2i, Variant::RECT2I) +MAKE_TYPE_INFO(Vector3i, Variant::VECTOR3I) MAKE_TYPE_INFO(Transform2D, Variant::TRANSFORM2D) MAKE_TYPE_INFO(Plane, Variant::PLANE) MAKE_TYPE_INFO(Quat, Variant::QUAT) diff --git a/core/variant_parser.cpp b/core/variant_parser.cpp index d2ee0b71c9..0a578faf78 100644 --- a/core/variant_parser.cpp +++ b/core/variant_parser.cpp @@ -30,8 +30,8 @@ #include "variant_parser.h" +#include "core/input/input_event.h" #include "core/io/resource_loader.h" -#include "core/os/input_event.h" #include "core/os/keyboard.h" #include "core/string_buffer.h" diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index fb427afe24..d3205ff147 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -31,7 +31,7 @@ #include "animation_track_editor.h" #include "animation_track_editor_plugins.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/animation_bezier_editor.h" #include "editor/plugins/animation_player_editor_plugin.h" diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index ddd702fc6c..39e9288204 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -30,7 +30,7 @@ #include "code_editor.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "core/string_builder.h" #include "editor/editor_scale.h" diff --git a/editor/editor_audio_buses.cpp b/editor/editor_audio_buses.cpp index d77216697e..0c69061766 100644 --- a/editor/editor_audio_buses.cpp +++ b/editor/editor_audio_buses.cpp @@ -30,8 +30,8 @@ #include "editor_audio_buses.h" +#include "core/input/input.h" #include "core/io/resource_saver.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "editor_node.h" #include "editor_scale.h" diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 874f984844..c2ad4f87ff 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -30,7 +30,7 @@ #include "editor_help.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "doc_data_compressed.gen.h" #include "editor/plugins/script_editor_plugin.h" diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f40762586e..3ac383daad 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -32,6 +32,7 @@ #include "core/bind/core_bind.h" #include "core/class_db.h" +#include "core/input/input.h" #include "core/io/config_file.h" #include "core/io/image_loader.h" #include "core/io/resource_loader.h" @@ -39,7 +40,6 @@ #include "core/io/stream_peer_ssl.h" #include "core/message_queue.h" #include "core/os/file_access.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/path_remap.h" @@ -47,7 +47,6 @@ #include "core/project_settings.h" #include "core/translation.h" #include "core/version.h" -#include "main/input_default.h" #include "main/main.h" #include "scene/gui/center_container.h" #include "scene/gui/control.h" @@ -5482,7 +5481,7 @@ EditorNode::EditorNode() { ResourceLoader::clear_translation_remaps(); //no remaps using during editor ResourceLoader::clear_path_remaps(); - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + Input *id = Input::get_singleton(); if (id) { diff --git a/editor/editor_spin_slider.cpp b/editor/editor_spin_slider.cpp index 0ede0a3b7a..42d0b14899 100644 --- a/editor/editor_spin_slider.cpp +++ b/editor/editor_spin_slider.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "editor_spin_slider.h" +#include "core/input/input.h" #include "core/math/expression.h" -#include "core/os/input.h" #include "editor_node.h" #include "editor_scale.h" diff --git a/editor/export_template_manager.cpp b/editor/export_template_manager.cpp index 9328a5e04d..d3bcfe3d38 100644 --- a/editor/export_template_manager.cpp +++ b/editor/export_template_manager.cpp @@ -30,10 +30,10 @@ #include "export_template_manager.h" +#include "core/input/input.h" #include "core/io/json.h" #include "core/io/zip_io.h" #include "core/os/dir_access.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/version.h" #include "editor_node.h" diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 663f2dde05..dc8f7f3724 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -30,9 +30,9 @@ #include "animation_blend_space_2d_editor.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/math/delaunay.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index e79909fae1..931bce65d7 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "animation_blend_tree_editor_plugin.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_inspector.h" diff --git a/editor/plugins/animation_player_editor_plugin.cpp b/editor/plugins/animation_player_editor_plugin.cpp index af12335a27..10d13f0188 100644 --- a/editor/plugins/animation_player_editor_plugin.cpp +++ b/editor/plugins/animation_player_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "animation_player_editor_plugin.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/io/resource_saver.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/animation_track_editor.h" diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index cae959e1f4..1fe57665eb 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -30,9 +30,9 @@ #include "animation_state_machine_editor.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/math/delaunay.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index c9706a7f68..cf99d2ef78 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -34,9 +34,9 @@ #include "animation_blend_space_2d_editor.h" #include "animation_blend_tree_editor_plugin.h" #include "animation_state_machine_editor.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/math/delaunay.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/editor_scale.h" diff --git a/editor/plugins/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp index 80b7e6ffc8..04f6efd436 100644 --- a/editor/plugins/asset_library_editor_plugin.cpp +++ b/editor/plugins/asset_library_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "asset_library_editor_plugin.h" +#include "core/input/input.h" #include "core/io/json.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/version.h" #include "editor/editor_node.h" diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 9f66672f4a..13f4979ba2 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -30,7 +30,7 @@ #include "canvas_item_editor_plugin.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" diff --git a/editor/plugins/collision_polygon_editor_plugin.cpp b/editor/plugins/collision_polygon_editor_plugin.cpp index 1562286073..05c4772284 100644 --- a/editor/plugins/collision_polygon_editor_plugin.cpp +++ b/editor/plugins/collision_polygon_editor_plugin.cpp @@ -31,8 +31,8 @@ #include "collision_polygon_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/input/input.h" #include "core/os/file_access.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "editor/editor_settings.h" #include "scene/3d/camera.h" diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 878787231d..cbfc37d1e1 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -32,7 +32,7 @@ #include "canvas_item_editor_plugin.h" #include "core/core_string_names.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" diff --git a/editor/plugins/polygon_2d_editor_plugin.cpp b/editor/plugins/polygon_2d_editor_plugin.cpp index 8c115586a4..0611a3fe7e 100644 --- a/editor/plugins/polygon_2d_editor_plugin.cpp +++ b/editor/plugins/polygon_2d_editor_plugin.cpp @@ -31,8 +31,8 @@ #include "polygon_2d_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/input/input.h" #include "core/os/file_access.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index b3f78b5406..2245781703 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "script_editor_plugin.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/os/file_access.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.h" diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 2f1274a99a..0a8c18a7bc 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -30,8 +30,8 @@ #include "spatial_editor_plugin.h" +#include "core/input/input.h" #include "core/math/camera_matrix.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/print_string.h" #include "core/project_settings.h" diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index 2262e12f5d..72ee2260be 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -31,7 +31,7 @@ #include "texture_region_editor_plugin.h" #include "core/core_string_names.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "scene/gui/check_box.h" diff --git a/editor/plugins/tile_map_editor_plugin.cpp b/editor/plugins/tile_map_editor_plugin.cpp index 017f986469..1c77c13962 100644 --- a/editor/plugins/tile_map_editor_plugin.cpp +++ b/editor/plugins/tile_map_editor_plugin.cpp @@ -31,8 +31,8 @@ #include "tile_map_editor_plugin.h" #include "canvas_item_editor_plugin.h" +#include "core/input/input.h" #include "core/math/math_funcs.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" diff --git a/editor/plugins/tile_set_editor_plugin.cpp b/editor/plugins/tile_set_editor_plugin.cpp index a5a667802e..b063ddcde0 100644 --- a/editor/plugins/tile_set_editor_plugin.cpp +++ b/editor/plugins/tile_set_editor_plugin.cpp @@ -30,7 +30,7 @@ #include "tile_set_editor_plugin.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "editor/editor_scale.h" #include "editor/plugins/canvas_item_editor_plugin.h" diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp index a10e1c1d2c..c7c3ba8198 100644 --- a/editor/plugins/visual_shader_editor_plugin.cpp +++ b/editor/plugins/visual_shader_editor_plugin.cpp @@ -30,9 +30,9 @@ #include "visual_shader_editor_plugin.h" +#include "core/input/input.h" #include "core/io/resource_loader.h" #include "core/math/math_defs.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "core/version.h" diff --git a/editor/project_settings_editor.cpp b/editor/project_settings_editor.cpp index 4fb61929a3..47dbbed780 100644 --- a/editor/project_settings_editor.cpp +++ b/editor/project_settings_editor.cpp @@ -31,7 +31,7 @@ #include "project_settings_editor.h" #include "core/global_constants.h" -#include "core/input_map.h" +#include "core/input/input_map.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "core/translation.h" diff --git a/editor/property_editor.cpp b/editor/property_editor.cpp index 6913b0253b..feef274d33 100644 --- a/editor/property_editor.cpp +++ b/editor/property_editor.cpp @@ -31,11 +31,11 @@ #include "property_editor.h" #include "core/class_db.h" +#include "core/input/input.h" #include "core/io/image_loader.h" #include "core/io/marshalls.h" #include "core/io/resource_loader.h" #include "core/math/expression.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/pair.h" #include "core/print_string.h" diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 7845af7f79..bdc5f0a3e8 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -30,8 +30,8 @@ #include "scene_tree_dock.h" +#include "core/input/input.h" #include "core/io/resource_saver.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/project_settings.h" #include "editor/debugger/editor_debugger_node.h" diff --git a/main/SCsub b/main/SCsub index e1efc7e6ed..0262701d79 100644 --- a/main/SCsub +++ b/main/SCsub @@ -9,15 +9,6 @@ env.main_sources = [] env.add_source_files(env.main_sources, "*.cpp") -# Order matters here. Higher index controller database files write on top of lower index database files. -controller_databases = ["#main/gamecontrollerdb_204.txt", "#main/gamecontrollerdb_205.txt", "#main/gamecontrollerdb.txt", "#main/godotcontrollerdb.txt"] - -env.Depends("#main/default_controller_mappings.gen.cpp", controller_databases) -env.CommandNoCache("#main/default_controller_mappings.gen.cpp", controller_databases, run_in_subprocess(main_builders.make_default_controller_mappings)) - -# Don't warn about duplicate entry here, we need it registered manually for first build, -# even if later builds will pick it up twice due to above *.cpp globbing. -env.add_source_files(env.main_sources, "#main/default_controller_mappings.gen.cpp", warn_duplicates=False) env.Depends("#main/splash.gen.h", "#main/splash.png") env.CommandNoCache("#main/splash.gen.h", "#main/splash.png", run_in_subprocess(main_builders.make_splash)) diff --git a/main/main.cpp b/main/main.cpp index 0047d05771..b95ba54576 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -32,7 +32,8 @@ #include "core/crypto/crypto.h" #include "core/debugger/engine_debugger.h" -#include "core/input_map.h" +#include "core/input/input.h" +#include "core/input/input_map.h" #include "core/io/file_access_network.h" #include "core/io/file_access_pack.h" #include "core/io/file_access_zip.h" @@ -49,7 +50,6 @@ #include "core/version_hash.gen.h" #include "drivers/register_driver_types.h" #include "main/app_icon.gen.h" -#include "main/input_default.h" #include "main/main_timer_sync.h" #include "main/performance.h" #include "main/splash.gen.h" @@ -1294,7 +1294,7 @@ Error Main::setup2(Thread::ID p_main_tid_override) { GLOBAL_DEF("application/config/windows_native_icon", String()); ProjectSettings::get_singleton()->set_custom_property_info("application/config/windows_native_icon", PropertyInfo(Variant::STRING, "application/config/windows_native_icon", PROPERTY_HINT_FILE, "*.ico")); - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + Input *id = Input::get_singleton(); if (id) { if (bool(GLOBAL_DEF("input_devices/pointing/emulate_touch_from_mouse", false)) && !(editor || project_manager)) { if (!OS::get_singleton()->has_touchscreen_ui_hint()) { diff --git a/main/main_builders.py b/main/main_builders.py index e24070ccc3..aebac2b022 100644 --- a/main/main_builders.py +++ b/main/main_builders.py @@ -63,67 +63,5 @@ def make_app_icon(target, source, env): g.write("#endif") -def make_default_controller_mappings(target, source, env): - dst = target[0] - g = open(dst, "w") - - g.write("/* THIS FILE IS GENERATED DO NOT EDIT */\n") - g.write("#include \"core/typedefs.h\"\n") - g.write("#include \"main/default_controller_mappings.h\"\n") - - # ensure mappings have a consistent order - platform_mappings = OrderedDict() - for src_path in source: - with open(src_path, "r") as f: - # read mapping file and skip header - mapping_file_lines = f.readlines()[2:] - - current_platform = None - for line in mapping_file_lines: - if not line: - continue - line = line.strip() - if len(line) == 0: - continue - if line[0] == "#": - current_platform = line[1:].strip() - if current_platform not in platform_mappings: - platform_mappings[current_platform] = {} - elif current_platform: - line_parts = line.split(",") - guid = line_parts[0] - if guid in platform_mappings[current_platform]: - g.write("// WARNING - DATABASE {} OVERWROTE PRIOR MAPPING: {} {}\n".format(src_path, current_platform, platform_mappings[current_platform][guid])) - valid_mapping = True - for input_map in line_parts[2:]: - if "+" in input_map or "-" in input_map or "~" in input_map: - g.write("// WARNING - DISCARDED UNSUPPORTED MAPPING TYPE FROM DATABASE {}: {} {}\n".format(src_path, current_platform, line)) - valid_mapping = False - break - if valid_mapping: - platform_mappings[current_platform][guid] = line - - platform_variables = { - "Linux": "#if X11_ENABLED", - "Windows": "#ifdef WINDOWS_ENABLED", - "Mac OS X": "#ifdef OSX_ENABLED", - "Android": "#if defined(__ANDROID__)", - "iOS": "#ifdef IPHONE_ENABLED", - "Javascript": "#ifdef JAVASCRIPT_ENABLED", - "UWP": "#ifdef UWP_ENABLED", - } - - g.write("const char* DefaultControllerMappings::mappings[] = {\n") - for platform, mappings in platform_mappings.items(): - variable = platform_variables[platform] - g.write("{}\n".format(variable)) - for mapping in mappings.values(): - g.write("\t\"{}\",\n".format(mapping)) - g.write("#endif\n") - - g.write("\tNULL\n};\n") - g.close() - - if __name__ == '__main__': subprocess_main(globals()) diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm index 39447e8dab..a0cb79d264 100644 --- a/modules/arkit/arkit_interface.mm +++ b/modules/arkit/arkit_interface.mm @@ -28,7 +28,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "scene/resources/surface_tool.h" #include "servers/visual/visual_server_globals.h" diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp index a033c2b7f9..f6912276f5 100644 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ b/modules/gdnative/arvr/arvr_interface_gdnative.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "arvr_interface_gdnative.h" -#include "main/input_default.h" +#include "core/input/input.h" #include "servers/arvr/arvr_positional_tracker.h" #include "servers/visual/visual_server_globals.h" @@ -317,7 +317,7 @@ godot_int GDAPI godot_arvr_add_controller(char *p_device_name, godot_int p_hand, ARVRServer *arvr_server = ARVRServer::get_singleton(); ERR_FAIL_NULL_V(arvr_server, 0); - InputDefault *input = (InputDefault *)Input::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL_V(input, 0); ARVRPositionalTracker *new_tracker = memnew(ARVRPositionalTracker); @@ -356,7 +356,7 @@ void GDAPI godot_arvr_remove_controller(godot_int p_controller_id) { ARVRServer *arvr_server = ARVRServer::get_singleton(); ERR_FAIL_NULL(arvr_server); - InputDefault *input = (InputDefault *)Input::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); ARVRPositionalTracker *remove_tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); @@ -394,7 +394,7 @@ void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int ARVRServer *arvr_server = ARVRServer::get_singleton(); ERR_FAIL_NULL(arvr_server); - InputDefault *input = (InputDefault *)Input::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); @@ -410,14 +410,14 @@ void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p ARVRServer *arvr_server = ARVRServer::get_singleton(); ERR_FAIL_NULL(arvr_server); - InputDefault *input = (InputDefault *)Input::get_singleton(); + Input *input = Input::get_singleton(); ERR_FAIL_NULL(input); ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); if (tracker != NULL) { int joyid = tracker->get_joy_id(); if (joyid != -1) { - InputDefault::JoyAxis jx; + Input::JoyAxis jx; jx.min = p_can_be_negative ? -1 : 0; jx.value = p_value; input->joy_axis(joyid, p_axis, jx); diff --git a/modules/gridmap/grid_map_editor_plugin.cpp b/modules/gridmap/grid_map_editor_plugin.cpp index a656ee8b63..aa470105e0 100644 --- a/modules/gridmap/grid_map_editor_plugin.cpp +++ b/modules/gridmap/grid_map_editor_plugin.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "grid_map_editor_plugin.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/plugins/spatial_editor_plugin.h" diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 2f1d95cd42..af429eadd5 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "mobile_vr_interface.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "servers/visual/visual_server_globals.h" diff --git a/modules/visual_script/visual_script_editor.cpp b/modules/visual_script/visual_script_editor.cpp index ca255ebf82..14c37bd375 100644 --- a/modules/visual_script/visual_script_editor.cpp +++ b/modules/visual_script/visual_script_editor.cpp @@ -30,8 +30,8 @@ #include "visual_script_editor.h" +#include "core/input/input.h" #include "core/object.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/script_language.h" #include "core/variant.h" diff --git a/modules/visual_script/visual_script_nodes.cpp b/modules/visual_script/visual_script_nodes.cpp index 41c1ea4ca2..12da9647e4 100644 --- a/modules/visual_script/visual_script_nodes.cpp +++ b/modules/visual_script/visual_script_nodes.cpp @@ -32,7 +32,7 @@ #include "core/engine.h" #include "core/global_constants.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/main/node.h" diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 8bbf41d82d..e6ab272e7f 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -37,12 +37,12 @@ #include "api/java_class_wrapper.h" #include "audio_driver_jandroid.h" #include "core/engine.h" +#include "core/input/input.h" #include "core/project_settings.h" #include "dir_access_jandroid.h" #include "file_access_android.h" #include "file_access_jandroid.h" #include "jni_utils.h" -#include "main/input_default.h" #include "main/main.h" #include "net_socket_android.h" #include "os_android.h" diff --git a/platform/android/os_android.h b/platform/android/os_android.h index ec6ffe5438..745a28f6df 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -33,10 +33,9 @@ #include "audio_driver_jandroid.h" #include "audio_driver_opensl.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/main_loop.h" #include "drivers/unix/os_unix.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" diff --git a/platform/haiku/haiku_direct_window.h b/platform/haiku/haiku_direct_window.h index ccc9542f0e..4817abbb7a 100644 --- a/platform/haiku/haiku_direct_window.h +++ b/platform/haiku/haiku_direct_window.h @@ -35,8 +35,8 @@ #include <DirectWindow.h> +#include "core/input/input.h" #include "core/os/os.h" -#include "main/input_default.h" #include "haiku_gl_view.h" diff --git a/platform/haiku/os_haiku.h b/platform/haiku/os_haiku.h index fc8cb77a91..6804ae5f82 100644 --- a/platform/haiku/os_haiku.h +++ b/platform/haiku/os_haiku.h @@ -33,10 +33,10 @@ #include "audio_driver_media_kit.h" #include "context_gl_haiku.h" +#include "core/input/input.h" #include "drivers/unix/os_unix.h" #include "haiku_application.h" #include "haiku_direct_window.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual_server.h" diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index f42679e754..3a61a40b26 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -33,15 +33,15 @@ #ifndef OS_IPHONE_H #define OS_IPHONE_H -#include "core/os/input.h" +#include "core/input/input.h" #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/unix/os_unix.h" +#include "core/input/input.h" #include "game_center.h" #include "icloud.h" #include "in_app_store.h" #include "ios.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" diff --git a/platform/javascript/os_javascript.h b/platform/javascript/os_javascript.h index ed59477914..c4ef33f7b1 100644 --- a/platform/javascript/os_javascript.h +++ b/platform/javascript/os_javascript.h @@ -32,8 +32,8 @@ #define OS_JAVASCRIPT_H #include "audio_driver_javascript.h" +#include "core/input/input.h" #include "drivers/unix/os_unix.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" diff --git a/platform/osx/joypad_osx.h b/platform/osx/joypad_osx.h index 2c076b3680..287cf63cc8 100644 --- a/platform/osx/joypad_osx.h +++ b/platform/osx/joypad_osx.h @@ -40,7 +40,7 @@ #include <ForceFeedback/ForceFeedbackConstants.h> #include <IOKit/hid/IOHIDLib.h> -#include "main/input_default.h" +#include "core/input/input.h" struct rec_element { IOHIDElementRef ref; diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index e865c3078f..b98c279f92 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -33,13 +33,12 @@ #define BitMap _QDBitMap // Suppress deprecated QuickDraw definition. -#include "core/os/input.h" +#include "core/input/input.h" #include "crash_handler_osx.h" #include "drivers/coreaudio/audio_driver_coreaudio.h" #include "drivers/coremidi/midi_driver_coremidi.h" #include "drivers/unix/os_unix.h" #include "joypad_osx.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual/visual_server_wrap_mt.h" diff --git a/platform/server/os_server.h b/platform/server/os_server.h index 7584293722..63afd68ee8 100644 --- a/platform/server/os_server.h +++ b/platform/server/os_server.h @@ -31,9 +31,9 @@ #ifndef OS_SERVER_H #define OS_SERVER_H +#include "core/input/input.h" #include "drivers/dummy/texture_loader_dummy.h" #include "drivers/unix/os_unix.h" -#include "main/input_default.h" #ifdef __APPLE__ #include "platform/osx/crash_handler_osx.h" #include "platform/osx/semaphore_osx.h" diff --git a/platform/uwp/joypad_uwp.h b/platform/uwp/joypad_uwp.h index f2a721f3dd..69431052e5 100644 --- a/platform/uwp/joypad_uwp.h +++ b/platform/uwp/joypad_uwp.h @@ -31,7 +31,7 @@ #ifndef JOYPAD_UWP_H #define JOYPAD_UWP_H -#include "main/input_default.h" +#include "core/input/input.h" ref class JoypadUWP sealed { diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index ac6e0f3dd5..29fd975274 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -32,13 +32,12 @@ #define OS_UWP_H #include "context_egl_uwp.h" +#include "core/input/input.h" #include "core/math/transform_2d.h" -#include "core/os/input.h" #include "core/os/os.h" #include "core/ustring.h" #include "drivers/xaudio2/audio_driver_xaudio2.h" #include "joypad_uwp.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h index 8506aa7b20..a7b31fa85f 100644 --- a/platform/windows/os_windows.h +++ b/platform/windows/os_windows.h @@ -31,7 +31,7 @@ #ifndef OS_WINDOWS_H #define OS_WINDOWS_H -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "core/project_settings.h" #include "crash_handler_windows.h" @@ -39,7 +39,6 @@ #include "drivers/wasapi/audio_driver_wasapi.h" #include "drivers/winmidi/midi_driver_winmidi.h" #include "key_mapping_windows.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" diff --git a/platform/x11/SCsub b/platform/x11/SCsub index 2268e4cc3d..ff4e667b87 100644 --- a/platform/x11/SCsub +++ b/platform/x11/SCsub @@ -11,6 +11,7 @@ common_x11 = [ "crash_handler_x11.cpp", "os_x11.cpp", "key_mapping_x11.cpp", + "display_server_x11.cpp", "joypad_linux.cpp", "detect_prime.cpp" ] diff --git a/platform/x11/display_server_x11.cpp b/platform/x11/display_server_x11.cpp new file mode 100644 index 0000000000..b4bd5658b8 --- /dev/null +++ b/platform/x11/display_server_x11.cpp @@ -0,0 +1,3216 @@ +/*************************************************************************/ +/* display_server_x11.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "display_server_x11.h" + +#ifdef X11_ENABLED + +#include "detect_prime.h" + +#include "core/os/dir_access.h" +#include "core/print_string.h" +#include "errno.h" +#include "key_mapping_x11.h" + +#if defined(OPENGL_ENABLED) +#include "drivers/gles2/rasterizer_gles2.h" +#endif + +#if defined(VULKAN_ENABLED) +#include "servers/visual/rasterizer_rd/rasterizer_rd.h" +#endif + +#include "servers/visual/visual_server_raster.h" +#include "servers/visual/visual_server_wrap_mt.h" + +#ifdef HAVE_MNTENT +#include <mntent.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "X11/Xutil.h" + +#include "X11/Xatom.h" +#include "X11/extensions/Xinerama.h" +// ICCCM +#define WM_NormalState 1L // window normal state +#define WM_IconicState 3L // window minimized +// EWMH +#define _NET_WM_STATE_REMOVE 0L // remove/unset property +#define _NET_WM_STATE_ADD 1L // add/set property +#define _NET_WM_STATE_TOGGLE 2L // toggle property + +#include "main/main.h" + +#include <dlfcn.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +//stupid linux.h +#ifdef KEY_TAB +#undef KEY_TAB +#endif + +#include <X11/Xatom.h> + +#undef CursorShape + +#include <X11/XKBlib.h> + +// 2.2 is the first release with multitouch +#define XINPUT_CLIENT_VERSION_MAJOR 2 +#define XINPUT_CLIENT_VERSION_MINOR 2 + +#define VALUATOR_ABSX 0 +#define VALUATOR_ABSY 1 +#define VALUATOR_PRESSURE 2 +#define VALUATOR_TILTX 3 +#define VALUATOR_TILTY 4 + +static const double abs_resolution_mult = 10000.0; +static const double abs_resolution_range_mult = 10.0; + +bool DisplayServerX11::has_feature(Feature p_feature) const { + switch (p_feature) { + case FEATURE_SUBWINDOWS: +#ifdef TOUCH_ENABLED + case FEATURE_TOUCHSCREEN: +#endif + case FEATURE_MOUSE: + case FEATURE_MOUSE_WARP: + case FEATURE_CLIPBOARD: + case FEATURE_CURSOR_SHAPE: + case FEATURE_CUSTOM_CURSOR_SHAPE: + case FEATURE_IME: + case FEATURE_WINDOW_TRANSPARENCY: + //case FEATURE_HIDPI: + case FEATURE_ICON: + case FEATURE_NATIVE_ICON: + case FEATURE_SWAP_BUFFERS: + return true; + default: { + } + } + + return false; +} +String DisplayServerX11::get_name() const { + return "X11"; +} + +void DisplayServerX11::alert(const String &p_alert, const String &p_title) { + const char *message_programs[] = { "zenity", "kdialog", "Xdialog", "xmessage" }; + + String path = OS::get_singleton()->get_environment("PATH"); + Vector<String> path_elems = path.split(":", false); + String program; + + for (int i = 0; i < path_elems.size(); i++) { + for (uint64_t k = 0; k < sizeof(message_programs) / sizeof(char *); k++) { + String tested_path = path_elems[i].plus_file(message_programs[k]); + + if (FileAccess::exists(tested_path)) { + program = tested_path; + break; + } + } + + if (program.length()) + break; + } + + List<String> args; + + if (program.ends_with("zenity")) { + args.push_back("--error"); + args.push_back("--width"); + args.push_back("500"); + args.push_back("--title"); + args.push_back(p_title); + args.push_back("--text"); + args.push_back(p_alert); + } + + if (program.ends_with("kdialog")) { + args.push_back("--error"); + args.push_back(p_alert); + args.push_back("--title"); + args.push_back(p_title); + } + + if (program.ends_with("Xdialog")) { + args.push_back("--title"); + args.push_back(p_title); + args.push_back("--msgbox"); + args.push_back(p_alert); + args.push_back("0"); + args.push_back("0"); + } + + if (program.ends_with("xmessage")) { + args.push_back("-center"); + args.push_back("-title"); + args.push_back(p_title); + args.push_back(p_alert); + } + + if (program.length()) { + OS::get_singleton()->execute(program, args, true); + } else { + print_line(p_alert); + } +} + +void DisplayServerX11::_update_real_mouse_position(const WindowData &wd) { + Window root_return, child_return; + int root_x, root_y, win_x, win_y; + unsigned int mask_return; + + Bool xquerypointer_result = XQueryPointer(x11_display, wd.x11_window, &root_return, &child_return, &root_x, &root_y, + &win_x, &win_y, &mask_return); + + if (xquerypointer_result) { + if (win_x > 0 && win_y > 0 && win_x <= wd.size.width && win_y <= wd.size.height) { + + last_mouse_pos.x = win_x; + last_mouse_pos.y = win_y; + last_mouse_pos_valid = true; + Input::get_singleton()->set_mouse_position(last_mouse_pos); + } + } +} + +bool DisplayServerX11::_refresh_device_info() { + int event_base, error_base; + + print_verbose("XInput: Refreshing devices."); + + if (!XQueryExtension(x11_display, "XInputExtension", &xi.opcode, &event_base, &error_base)) { + print_verbose("XInput extension not available. Please upgrade your distribution."); + return false; + } + + int xi_major_query = XINPUT_CLIENT_VERSION_MAJOR; + int xi_minor_query = XINPUT_CLIENT_VERSION_MINOR; + + if (XIQueryVersion(x11_display, &xi_major_query, &xi_minor_query) != Success) { + print_verbose(vformat("XInput 2 not available (server supports %d.%d).", xi_major_query, xi_minor_query)); + xi.opcode = 0; + return false; + } + + if (xi_major_query < XINPUT_CLIENT_VERSION_MAJOR || (xi_major_query == XINPUT_CLIENT_VERSION_MAJOR && xi_minor_query < XINPUT_CLIENT_VERSION_MINOR)) { + print_verbose(vformat("XInput %d.%d not available (server supports %d.%d). Touch input unavailable.", + XINPUT_CLIENT_VERSION_MAJOR, XINPUT_CLIENT_VERSION_MINOR, xi_major_query, xi_minor_query)); + } + + xi.absolute_devices.clear(); + xi.touch_devices.clear(); + + int dev_count; + XIDeviceInfo *info = XIQueryDevice(x11_display, XIAllDevices, &dev_count); + + for (int i = 0; i < dev_count; i++) { + XIDeviceInfo *dev = &info[i]; + if (!dev->enabled) + continue; + if (!(dev->use == XIMasterPointer || dev->use == XIFloatingSlave)) + continue; + + bool direct_touch = false; + bool absolute_mode = false; + int resolution_x = 0; + int resolution_y = 0; + int range_min_x = 0; + int range_min_y = 0; + int range_max_x = 0; + int range_max_y = 0; + int pressure_resolution = 0; + int tilt_resolution_x = 0; + int tilt_resolution_y = 0; + for (int j = 0; j < dev->num_classes; j++) { +#ifdef TOUCH_ENABLED + if (dev->classes[j]->type == XITouchClass && ((XITouchClassInfo *)dev->classes[j])->mode == XIDirectTouch) { + direct_touch = true; + } +#endif + if (dev->classes[j]->type == XIValuatorClass) { + XIValuatorClassInfo *class_info = (XIValuatorClassInfo *)dev->classes[j]; + + if (class_info->number == VALUATOR_ABSX && class_info->mode == XIModeAbsolute) { + resolution_x = class_info->resolution; + range_min_x = class_info->min; + range_max_x = class_info->max; + absolute_mode = true; + } else if (class_info->number == VALUATOR_ABSY && class_info->mode == XIModeAbsolute) { + resolution_y = class_info->resolution; + range_min_y = class_info->min; + range_max_y = class_info->max; + absolute_mode = true; + } else if (class_info->number == VALUATOR_PRESSURE && class_info->mode == XIModeAbsolute) { + pressure_resolution = (class_info->max - class_info->min); + if (pressure_resolution == 0) pressure_resolution = 1; + } else if (class_info->number == VALUATOR_TILTX && class_info->mode == XIModeAbsolute) { + tilt_resolution_x = (class_info->max - class_info->min); + if (tilt_resolution_x == 0) tilt_resolution_x = 1; + } else if (class_info->number == VALUATOR_TILTY && class_info->mode == XIModeAbsolute) { + tilt_resolution_y = (class_info->max - class_info->min); + if (tilt_resolution_y == 0) tilt_resolution_y = 1; + } + } + } + if (direct_touch) { + xi.touch_devices.push_back(dev->deviceid); + print_verbose("XInput: Using touch device: " + String(dev->name)); + } + if (absolute_mode) { + // If no resolution was reported, use the min/max ranges. + if (resolution_x <= 0) { + resolution_x = (range_max_x - range_min_x) * abs_resolution_range_mult; + } + if (resolution_y <= 0) { + resolution_y = (range_max_y - range_min_y) * abs_resolution_range_mult; + } + + xi.absolute_devices[dev->deviceid] = Vector2(abs_resolution_mult / resolution_x, abs_resolution_mult / resolution_y); + print_verbose("XInput: Absolute pointing device: " + String(dev->name)); + } + + xi.pressure = 0; + xi.pen_devices[dev->deviceid] = Vector3(pressure_resolution, tilt_resolution_x, tilt_resolution_y); + } + + XIFreeDeviceInfo(info); +#ifdef TOUCH_ENABLED + if (!xi.touch_devices.size()) { + print_verbose("XInput: No touch devices found."); + } +#endif + + return true; +} + +void DisplayServerX11::_flush_mouse_motion() { + while (true) { + if (XPending(x11_display) > 0) { + XEvent event; + XPeekEvent(x11_display, &event); + + if (XGetEventData(x11_display, &event.xcookie) && event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) { + XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; + + if (event_data->evtype == XI_RawMotion) { + XNextEvent(x11_display, &event); + } else { + break; + } + } else { + break; + } + } else { + break; + } + } + + xi.relative_motion.x = 0; + xi.relative_motion.y = 0; +} + +void DisplayServerX11::mouse_set_mode(MouseMode p_mode) { + if (p_mode == mouse_mode) + return; + + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) + XUngrabPointer(x11_display, CurrentTime); + + // The only modes that show a cursor are VISIBLE and CONFINED + bool showCursor = (p_mode == MOUSE_MODE_VISIBLE || p_mode == MOUSE_MODE_CONFINED); + + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + + if (showCursor) { + XDefineCursor(x11_display, E->get().x11_window, cursors[current_cursor]); // show cursor + } else { + XDefineCursor(x11_display, E->get().x11_window, null_cursor); // hide cursor + } + } + mouse_mode = p_mode; + + if (mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED) { + + //flush pending motion events + _flush_mouse_motion(); + WindowData &main_window = windows[MAIN_WINDOW_ID]; + + if (XGrabPointer( + x11_display, main_window.x11_window, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, windows[MAIN_WINDOW_ID].x11_window, None, CurrentTime) != GrabSuccess) { + ERR_PRINT("NO GRAB"); + } + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + center.x = main_window.size.width / 2; + center.y = main_window.size.height / 2; + + XWarpPointer(x11_display, None, main_window.x11_window, + 0, 0, 0, 0, (int)center.x, (int)center.y); + + Input::get_singleton()->set_mouse_position(center); + } + } else { + do_mouse_warp = false; + } + + XFlush(x11_display); +} +DisplayServerX11::MouseMode DisplayServerX11::mouse_get_mode() const { + return mouse_mode; +} + +void DisplayServerX11::mouse_warp_to_position(const Point2i &p_to) { + if (mouse_mode == MOUSE_MODE_CAPTURED) { + + last_mouse_pos = p_to; + } else { + + /*XWindowAttributes xwa; + XGetWindowAttributes(x11_display, x11_window, &xwa); + printf("%d %d\n", xwa.x, xwa.y); needed? */ + + XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window, + 0, 0, 0, 0, (int)p_to.x, (int)p_to.y); + } +} +Point2i DisplayServerX11::mouse_get_position() const { + return last_mouse_pos; +} +int DisplayServerX11::mouse_get_button_state() const { + return last_button_state; +} + +void DisplayServerX11::clipboard_set(const String &p_text) { + + internal_clipboard = p_text; + XSetSelectionOwner(x11_display, XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, CurrentTime); + XSetSelectionOwner(x11_display, XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, CurrentTime); +} + +static String _clipboard_get_impl(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard, Atom target) { + + String ret; + + Atom type; + Atom selection = XA_PRIMARY; + int format, result; + unsigned long len, bytes_left, dummy; + unsigned char *data; + Window Sown = XGetSelectionOwner(x11_display, p_source); + + if (Sown == x11_window) { + + return p_internal_clipboard; + }; + + if (Sown != None) { + XConvertSelection(x11_display, p_source, target, selection, + x11_window, CurrentTime); + XFlush(x11_display); + while (true) { + XEvent event; + XNextEvent(x11_display, &event); + if (event.type == SelectionNotify && event.xselection.requestor == x11_window) { + break; + }; + }; + + // + // Do not get any data, see how much data is there + // + XGetWindowProperty(x11_display, x11_window, + selection, // Tricky.. + 0, 0, // offset - len + 0, // Delete 0==FALSE + AnyPropertyType, //flag + &type, // return type + &format, // return format + &len, &bytes_left, //that + &data); + // DATA is There + if (bytes_left > 0) { + result = XGetWindowProperty(x11_display, x11_window, + selection, 0, bytes_left, 0, + AnyPropertyType, &type, &format, + &len, &dummy, &data); + if (result == Success) { + ret.parse_utf8((const char *)data); + } else + printf("FAIL\n"); + XFree(data); + } + } + + return ret; +} + +static String _clipboard_get(Atom p_source, Window x11_window, ::Display *x11_display, String p_internal_clipboard) { + String ret; + Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True); + if (utf8_atom != None) { + ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, utf8_atom); + } + if (ret == "") { + ret = _clipboard_get_impl(p_source, x11_window, x11_display, p_internal_clipboard, XA_STRING); + } + return ret; +} + +String DisplayServerX11::clipboard_get() const { + + String ret; + ret = _clipboard_get(XInternAtom(x11_display, "CLIPBOARD", 0), windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard); + + if (ret == "") { + ret = _clipboard_get(XA_PRIMARY, windows[MAIN_WINDOW_ID].x11_window, x11_display, internal_clipboard); + }; + + return ret; +} + +int DisplayServerX11::get_screen_count() const { + // Using Xinerama Extension + int event_base, error_base; + const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); + if (!ext_okay) return 0; + + int count; + XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); + XFree(xsi); + return count; +} +Point2i DisplayServerX11::screen_get_position(int p_screen) const { + if (p_screen == SCREEN_OF_MAIN_WINDOW) { + p_screen = window_get_current_screen(); + } + + // Using Xinerama Extension + int event_base, error_base; + const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); + if (!ext_okay) { + return Point2i(0, 0); + } + + int count; + XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); + if (p_screen >= count) { + return Point2i(0, 0); + } + + Point2i position = Point2i(xsi[p_screen].x_org, xsi[p_screen].y_org); + + XFree(xsi); + + return position; +} +Size2i DisplayServerX11::screen_get_size(int p_screen) const { + + if (p_screen == SCREEN_OF_MAIN_WINDOW) { + p_screen = window_get_current_screen(); + } + + // Using Xinerama Extension + int event_base, error_base; + const Bool ext_okay = XineramaQueryExtension(x11_display, &event_base, &error_base); + if (!ext_okay) return Size2i(0, 0); + + int count; + XineramaScreenInfo *xsi = XineramaQueryScreens(x11_display, &count); + if (p_screen >= count) return Size2i(0, 0); + + Size2i size = Point2i(xsi[p_screen].width, xsi[p_screen].height); + XFree(xsi); + return size; +} +int DisplayServerX11::screen_get_dpi(int p_screen) const { + if (p_screen == SCREEN_OF_MAIN_WINDOW) { + p_screen = window_get_current_screen(); + } + + //invalid screen? + ERR_FAIL_INDEX_V(p_screen, get_screen_count(), 0); + + //Get physical monitor Dimensions through XRandR and calculate dpi + Size2i sc = screen_get_size(p_screen); + if (xrandr_ext_ok) { + int count = 0; + if (xrr_get_monitors) { + xrr_monitor_info *monitors = xrr_get_monitors(x11_display, windows[MAIN_WINDOW_ID].x11_window, true, &count); + if (p_screen < count) { + double xdpi = sc.width / (double)monitors[p_screen].mwidth * 25.4; + double ydpi = sc.height / (double)monitors[p_screen].mheight * 25.4; + xrr_free_monitors(monitors); + return (xdpi + ydpi) / 2; + } + xrr_free_monitors(monitors); + } else if (p_screen == 0) { + XRRScreenSize *sizes = XRRSizes(x11_display, 0, &count); + if (sizes) { + double xdpi = sc.width / (double)sizes[0].mwidth * 25.4; + double ydpi = sc.height / (double)sizes[0].mheight * 25.4; + return (xdpi + ydpi) / 2; + } + } + } + + int width_mm = DisplayWidthMM(x11_display, p_screen); + int height_mm = DisplayHeightMM(x11_display, p_screen); + double xdpi = (width_mm ? sc.width / (double)width_mm * 25.4 : 0); + double ydpi = (height_mm ? sc.height / (double)height_mm * 25.4 : 0); + if (xdpi || ydpi) + return (xdpi + ydpi) / (xdpi && ydpi ? 2 : 1); + + //could not get dpi + return 96; +} +bool DisplayServerX11::screen_is_touchscreen(int p_screen) const { +#ifndef _MSC_VER +#warning Need to get from proper window +#endif + return false; //? +} + +Vector<DisplayServer::WindowID> DisplayServerX11::get_window_list() const { + Vector<int> ret; + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + ret.push_back(E->key()); + } + return ret; +} + +DisplayServer::WindowID DisplayServerX11::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect) { + + WindowID id = _create_window(p_mode, p_rect.size); + window_set_position(p_rect.position, id); + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { + if (p_flags & (1 << i)) { + window_set_flag(WindowFlags(i), true, id); + } + } + + return id; +} + +void DisplayServerX11::delete_sub_window(WindowID p_id) { + ERR_FAIL_COND(windows.has(p_id)); + ERR_FAIL_COND_MSG(p_id == MAIN_WINDOW_ID, "Main window can't be deleted"); //ma + + WindowData &wd = windows[p_id]; + + XUnmapWindow(x11_display, wd.x11_window); + XDestroyWindow(x11_display, wd.x11_window); + if (wd.xic) { + XDestroyIC(wd.xic); + } + + windows.erase(p_id); +} + +void DisplayServerX11::window_set_title(const String &p_title, WindowID p_window) { + + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + XStoreName(x11_display, wd.x11_window, p_title.utf8().get_data()); + + Atom _net_wm_name = XInternAtom(x11_display, "_NET_WM_NAME", false); + Atom utf8_string = XInternAtom(x11_display, "UTF8_STRING", false); + XChangeProperty(x11_display, wd.x11_window, _net_wm_name, utf8_string, 8, PropModeReplace, (unsigned char *)p_title.utf8().get_data(), p_title.utf8().length()); +} + +int DisplayServerX11::window_get_current_screen(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), -1); + const WindowData &wd = windows[p_window]; + + int x, y; + Window child; + XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child); + + int count = get_screen_count(); + for (int i = 0; i < count; i++) { + Point2i pos = screen_get_position(i); + Size2i size = screen_get_size(i); + if ((x >= pos.x && x < pos.x + size.width) && (y >= pos.y && y < pos.y + size.height)) + return i; + } + return 0; +} +void DisplayServerX11::window_set_current_screen(int p_screen, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + int count = get_screen_count(); + if (p_screen >= count) return; + + if (window_get_mode(p_window) == WINDOW_MODE_FULLSCREEN) { + Point2i position = screen_get_position(p_screen); + Size2i size = screen_get_size(p_screen); + + XMoveResizeWindow(x11_display, wd.x11_window, position.x, position.y, size.x, size.y); + } else { + if (p_screen != window_get_current_screen(p_window)) { + Point2i position = screen_get_position(p_screen); + XMoveWindow(x11_display, wd.x11_window, position.x, position.y); + } + } +} + +Point2i DisplayServerX11::window_get_position(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), Point2i()); + const WindowData &wd = windows[p_window]; + int x, y; + Window child; + XTranslateCoordinates(x11_display, wd.x11_window, DefaultRootWindow(x11_display), 0, 0, &x, &y, &child); + return Point2i(x, y); +} +void DisplayServerX11::window_set_position(const Point2i &p_position, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + int x = 0; + int y = 0; + if (!window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) { + //exclude window decorations + XSync(x11_display, False); + Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True); + if (prop != None) { + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + if (format == 32 && len == 4) { + long *extents = (long *)data; + x = extents[0]; + y = extents[2]; + } + XFree(data); + } + } + } + XMoveWindow(x11_display, wd.x11_window, p_position.x - x, p_position.y - y); + _update_real_mouse_position(wd); +} + +void DisplayServerX11::window_set_max_size(const Size2i p_size, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if ((p_size != Size2i()) && ((p_size.x < wd.min_size.x) || (p_size.y < wd.min_size.y))) { + ERR_PRINT("Maximum window size can't be smaller than minimum window size!"); + return; + } + wd.max_size = p_size; + + if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { + XSizeHints *xsh; + xsh = XAllocSizeHints(); + xsh->flags = 0L; + if (wd.min_size != Size2i()) { + xsh->flags |= PMinSize; + xsh->min_width = wd.min_size.x; + xsh->min_height = wd.min_size.y; + } + if (wd.max_size != Size2i()) { + xsh->flags |= PMaxSize; + xsh->max_width = wd.max_size.x; + xsh->max_height = wd.max_size.y; + } + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + + XFlush(x11_display); + } +} +Size2i DisplayServerX11::window_get_max_size(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + + return wd.max_size; +} + +void DisplayServerX11::window_set_min_size(const Size2i p_size, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if ((p_size != Size2i()) && (wd.max_size != Size2i()) && ((p_size.x > wd.max_size.x) || (p_size.y > wd.max_size.y))) { + ERR_PRINT("Minimum window size can't be larger than maximum window size!"); + return; + } + wd.min_size = p_size; + + if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { + XSizeHints *xsh; + xsh = XAllocSizeHints(); + xsh->flags = 0L; + if (wd.min_size != Size2i()) { + xsh->flags |= PMinSize; + xsh->min_width = wd.min_size.x; + xsh->min_height = wd.min_size.y; + } + if (wd.max_size != Size2i()) { + xsh->flags |= PMaxSize; + xsh->max_width = wd.max_size.x; + xsh->max_height = wd.max_size.y; + } + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + + XFlush(x11_display); + } +} +Size2i DisplayServerX11::window_get_min_size(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + + return wd.min_size; +} + +void DisplayServerX11::window_set_size(const Size2i p_size, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if (wd.size.width == p_size.width && wd.size.height == p_size.height) + return; + + XWindowAttributes xwa; + XSync(x11_display, False); + XGetWindowAttributes(x11_display, wd.x11_window, &xwa); + int old_w = xwa.width; + int old_h = xwa.height; + + // If window resizable is disabled we need to update the attributes first + XSizeHints *xsh; + xsh = XAllocSizeHints(); + if (!window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { + xsh->flags = PMinSize | PMaxSize; + xsh->min_width = p_size.x; + xsh->max_width = p_size.x; + xsh->min_height = p_size.y; + xsh->max_height = p_size.y; + } else { + xsh->flags = 0L; + if (wd.min_size != Size2i()) { + xsh->flags |= PMinSize; + xsh->min_width = wd.min_size.x; + xsh->min_height = wd.min_size.y; + } + if (wd.max_size != Size2i()) { + xsh->flags |= PMaxSize; + xsh->max_width = wd.max_size.x; + xsh->max_height = wd.max_size.y; + } + } + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + + // Resize the window + XResizeWindow(x11_display, wd.x11_window, p_size.x, p_size.y); + + // Update our videomode width and height + wd.size = p_size; + + for (int timeout = 0; timeout < 50; ++timeout) { + XSync(x11_display, False); + XGetWindowAttributes(x11_display, wd.x11_window, &xwa); + + if (old_w != xwa.width || old_h != xwa.height) + break; + + usleep(10000); + } +} +Size2i DisplayServerX11::window_get_size(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + return wd.size; +} +Size2i DisplayServerX11::window_get_real_size(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), Size2i()); + const WindowData &wd = windows[p_window]; + + XWindowAttributes xwa; + XSync(x11_display, False); + XGetWindowAttributes(x11_display, wd.x11_window, &xwa); + int w = xwa.width; + int h = xwa.height; + Atom prop = XInternAtom(x11_display, "_NET_FRAME_EXTENTS", True); + if (prop != None) { + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, 4, False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + if (format == 32 && len == 4) { + long *extents = (long *)data; + w += extents[0] + extents[1]; // left, right + h += extents[2] + extents[3]; // top, bottom + } + XFree(data); + } + } + return Size2(w, h); +} + +bool DisplayServerX11::window_is_maximize_allowed(WindowID p_window) const { + + ERR_FAIL_COND_V(windows.has(p_window), false); + const WindowData &wd = windows[p_window]; + + Atom property = XInternAtom(x11_display, "_NET_WM_ALLOWED_ACTIONS", False); + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 1024, + False, + XA_ATOM, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success) { + Atom *atoms = (Atom *)data; + Atom wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_HORZ", False); + Atom wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_ACTION_MAXIMIZE_VERT", False); + bool found_wm_act_max_horz = false; + bool found_wm_act_max_vert = false; + + for (uint64_t i = 0; i < len; i++) { + if (atoms[i] == wm_act_max_horz) + found_wm_act_max_horz = true; + if (atoms[i] == wm_act_max_vert) + found_wm_act_max_vert = true; + + if (found_wm_act_max_horz || found_wm_act_max_vert) + return true; + } + XFree(atoms); + } + + return false; +} + +void DisplayServerX11::_set_wm_maximized(WindowID p_window, bool p_enabled) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + // Using EWMH -- Extended Window Manager Hints + XEvent xev; + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = wm_max_horz; + xev.xclient.data.l[2] = wm_max_vert; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + if (p_enabled && window_is_maximize_allowed(p_window)) { + // Wait for effective resizing (so the GLX context is too). + // Give up after 0.5s, it's not going to happen on this WM. + // https://github.com/godotengine/godot/issues/19978 + for (int attempt = 0; window_get_mode(p_window) != WINDOW_MODE_MAXIMIZED && attempt < 50; attempt++) { + usleep(10000); + } + } +} + +void DisplayServerX11::_set_wm_fullscreen(WindowID p_window, bool p_enabled) { + + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + if (p_enabled && !window_get_flag(WINDOW_FLAG_BORDERLESS, p_window)) { + // remove decorations if the window is not already borderless + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = 0; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } + + if (p_enabled && window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { + // Set the window as resizable to prevent window managers to ignore the fullscreen state flag. + XSizeHints *xsh; + + xsh = XAllocSizeHints(); + xsh->flags = 0L; + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + } + + // Using EWMH -- Extended Window Manager Hints + XEvent xev; + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_fullscreen = XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = wm_fullscreen; + xev.xclient.data.l[2] = 0; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + // set bypass compositor hint + Atom bypass_compositor = XInternAtom(x11_display, "_NET_WM_BYPASS_COMPOSITOR", False); + unsigned long compositing_disable_on = p_enabled ? 1 : 0; + XChangeProperty(x11_display, wd.x11_window, bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&compositing_disable_on, 1); + + XFlush(x11_display); + + if (!p_enabled) { + // Reset the non-resizable flags if we un-set these before. + Size2 size = window_get_size(p_window); + XSizeHints *xsh; + xsh = XAllocSizeHints(); + if (window_get_flag(WINDOW_FLAG_RESIZE_DISABLED, p_window)) { + xsh->flags = PMinSize | PMaxSize; + xsh->min_width = size.x; + xsh->max_width = size.x; + xsh->min_height = size.y; + xsh->max_height = size.y; + } else { + xsh->flags = 0L; + if (wd.min_size != Size2()) { + xsh->flags |= PMinSize; + xsh->min_width = wd.min_size.x; + xsh->min_height = wd.min_size.y; + } + if (wd.max_size != Size2()) { + xsh->flags |= PMaxSize; + xsh->max_width = wd.max_size.x; + xsh->max_height = wd.max_size.y; + } + } + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + + // put back or remove decorations according to the last set borderless state + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = window_get_flag(WINDOW_FLAG_BORDERLESS, p_window) ? 0 : 1; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + } +} + +void DisplayServerX11::window_set_mode(WindowMode p_mode, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + WindowMode old_mode = window_get_mode(p_window); + if (old_mode == p_mode) { + return; // do nothing + } + //remove all "extra" modes + + switch (old_mode) { + case WINDOW_MODE_WINDOWED: { + //do nothing + } break; + case WINDOW_MODE_MINIMIZED: { + //Un-Minimize + // Using ICCCM -- Inter-Client Communication Conventions Manual + XEvent xev; + Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_change; + xev.xclient.format = 32; + xev.xclient.data.l[0] = WM_NormalState; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = wm_hidden; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + } break; + case WINDOW_MODE_FULLSCREEN: { + //Remove full-screen + _set_wm_fullscreen(p_window, false); + + //un-maximize required for always on top + bool on_top = window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window); + + wd.fullscreen = false; + + window_set_position(wd.last_position_before_fs, p_window); + + if (on_top) { + _set_wm_maximized(p_window, false); + } + + } break; + case WINDOW_MODE_MAXIMIZED: { + + _set_wm_maximized(p_window, false); + } break; + } + + switch (p_mode) { + case WINDOW_MODE_WINDOWED: { + //do nothing + } break; + case WINDOW_MODE_MINIMIZED: { + // Using ICCCM -- Inter-Client Communication Conventions Manual + XEvent xev; + Atom wm_change = XInternAtom(x11_display, "WM_CHANGE_STATE", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_change; + xev.xclient.format = 32; + xev.xclient.data.l[0] = WM_IconicState; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_hidden = XInternAtom(x11_display, "_NET_WM_STATE_HIDDEN", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = wm_hidden; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + } break; + case WINDOW_MODE_FULLSCREEN: { + wd.last_position_before_fs = window_get_position(p_window); + if (window_get_flag(WINDOW_FLAG_ALWAYS_ON_TOP, p_window)) { + _set_wm_maximized(p_window, true); + } + _set_wm_fullscreen(p_window, true); + wd.fullscreen = true; + } break; + case WINDOW_MODE_MAXIMIZED: { + + _set_wm_maximized(p_window, true); + + } break; + } +} + +DisplayServer::WindowMode DisplayServerX11::window_get_mode(WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), WINDOW_MODE_WINDOWED); + const WindowData &wd = windows[p_window]; + + if (wd.fullscreen) { //if fullscreen, it's not in another mode + return WINDOW_MODE_FULLSCREEN; + } + { //test maximized + // Using EWMH -- Extended Window Manager Hints + Atom property = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + bool retval = false; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 1024, + False, + XA_ATOM, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success) { + Atom *atoms = (Atom *)data; + Atom wm_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False); + Atom wm_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False); + bool found_wm_max_horz = false; + bool found_wm_max_vert = false; + + for (uint64_t i = 0; i < len; i++) { + if (atoms[i] == wm_max_horz) + found_wm_max_horz = true; + if (atoms[i] == wm_max_vert) + found_wm_max_vert = true; + + if (found_wm_max_horz && found_wm_max_vert) { + retval = true; + break; + } + } + } + + XFree(data); + if (retval) { + return WINDOW_MODE_MAXIMIZED; + } + } + + { // test minimzed + // Using ICCCM -- Inter-Client Communication Conventions Manual + Atom property = XInternAtom(x11_display, "WM_STATE", True); + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + + int result = XGetWindowProperty( + x11_display, + wd.x11_window, + property, + 0, + 32, + False, + AnyPropertyType, + &type, + &format, + &len, + &remaining, + &data); + + if (result == Success) { + long *state = (long *)data; + if (state[0] == WM_IconicState) + return WINDOW_MODE_MINIMIZED; + } + } + + // all other discarded, return windowed. + + return WINDOW_MODE_WINDOWED; +} + +void DisplayServerX11::window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + switch (p_flag) { + case WINDOW_FLAG_RESIZE_DISABLED: { + XSizeHints *xsh; + xsh = XAllocSizeHints(); + if (p_enabled) { + Size2 size = window_get_size(p_window); + + xsh->flags = PMinSize | PMaxSize; + xsh->min_width = size.x; + xsh->max_width = size.x; + xsh->min_height = size.y; + xsh->max_height = size.y; + } else { + xsh->flags = 0L; + if (wd.min_size != Size2()) { + xsh->flags |= PMinSize; + xsh->min_width = wd.min_size.x; + xsh->min_height = wd.min_size.y; + } + if (wd.max_size != Size2()) { + xsh->flags |= PMaxSize; + xsh->max_width = wd.max_size.x; + xsh->max_height = wd.max_size.y; + } + } + + XSetWMNormalHints(x11_display, wd.x11_window, xsh); + XFree(xsh); + + wd.resize_disabled = p_enabled; + + XFlush(x11_display); + + } break; + case WINDOW_FLAG_BORDERLESS: { + + Hints hints; + Atom property; + hints.flags = 2; + hints.decorations = p_enabled ? 0 : 1; + property = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + XChangeProperty(x11_display, wd.x11_window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5); + + // Preserve window size + window_set_size(window_get_size(p_window), p_window); + + wd.borderless = p_enabled; + } break; + case WINDOW_FLAG_ALWAYS_ON_TOP: { + + if (p_enabled && wd.fullscreen) { + _set_wm_maximized(p_window, true); + } + + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_above = XInternAtom(x11_display, "_NET_WM_STATE_ABOVE", False); + + XClientMessageEvent xev; + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.window = wd.x11_window; + xev.message_type = wm_state; + xev.format = 32; + xev.data.l[0] = p_enabled ? _NET_WM_STATE_ADD : _NET_WM_STATE_REMOVE; + xev.data.l[1] = wm_above; + xev.data.l[3] = 1; + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *)&xev); + + if (!p_enabled && !wd.fullscreen) { + _set_wm_maximized(p_window, false); + } + wd.on_top = p_enabled; + + } break; + case WINDOW_FLAG_TRANSPARENT: { + //todo reimplement + } break; + default: { + } + } +} +bool DisplayServerX11::window_get_flag(WindowFlags p_flag, WindowID p_window) const { + ERR_FAIL_COND_V(windows.has(p_window), false); + const WindowData &wd = windows[p_window]; + + switch (p_flag) { + case WINDOW_FLAG_RESIZE_DISABLED: { + + return wd.resize_disabled; + } break; + case WINDOW_FLAG_BORDERLESS: { + + bool borderless = wd.borderless; + Atom prop = XInternAtom(x11_display, "_MOTIF_WM_HINTS", True); + if (prop != None) { + + Atom type; + int format; + unsigned long len; + unsigned long remaining; + unsigned char *data = NULL; + if (XGetWindowProperty(x11_display, wd.x11_window, prop, 0, sizeof(Hints), False, AnyPropertyType, &type, &format, &len, &remaining, &data) == Success) { + if (data && (format == 32) && (len >= 5)) { + borderless = !((Hints *)data)->decorations; + } + XFree(data); + } + } + return borderless; + } break; + case WINDOW_FLAG_ALWAYS_ON_TOP: { + + return wd.on_top; + } break; + case WINDOW_FLAG_TRANSPARENT: { + //todo reimplement + } break; + default: { + } + } + + return false; +} + +void DisplayServerX11::window_request_attention(WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + // Using EWMH -- Extended Window Manager Hints + // + // Sets the _NET_WM_STATE_DEMANDS_ATTENTION atom for WM_STATE + // Will be unset by the window manager after user react on the request for attention + + XEvent xev; + Atom wm_state = XInternAtom(x11_display, "_NET_WM_STATE", False); + Atom wm_attention = XInternAtom(x11_display, "_NET_WM_STATE_DEMANDS_ATTENTION", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = wm_state; + xev.xclient.format = 32; + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = wm_attention; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(x11_display); +} + +void DisplayServerX11::window_move_to_foreground(WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + XEvent xev; + Atom net_active_window = XInternAtom(x11_display, "_NET_ACTIVE_WINDOW", False); + + memset(&xev, 0, sizeof(xev)); + xev.type = ClientMessage; + xev.xclient.window = wd.x11_window; + xev.xclient.message_type = net_active_window; + xev.xclient.format = 32; + xev.xclient.data.l[0] = 1; + xev.xclient.data.l[1] = CurrentTime; + + XSendEvent(x11_display, DefaultRootWindow(x11_display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + XFlush(x11_display); +} + +bool DisplayServerX11::window_can_draw(WindowID p_window) const { + //this seems to be all that is provided by X11 + return window_get_mode(p_window) != WINDOW_MODE_MINIMIZED; +} + +void DisplayServerX11::window_set_ime_active(const bool p_active, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + wd.im_active = p_active; + + if (!wd.xic) + return; + + if (p_active) { + XSetICFocus(wd.xic); + window_set_ime_position(wd.im_position, p_window); + } else { + XUnsetICFocus(wd.xic); + } +} +void DisplayServerX11::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { + ERR_FAIL_COND(windows.has(p_window)); + WindowData &wd = windows[p_window]; + + wd.im_position = p_pos; + + if (!wd.xic) + return; + + ::XPoint spot; + spot.x = short(p_pos.x); + spot.y = short(p_pos.y); + XVaNestedList preedit_attr = XVaCreateNestedList(0, XNSpotLocation, &spot, NULL); + XSetICValues(wd.xic, XNPreeditAttributes, preedit_attr, NULL); + XFree(preedit_attr); +} + +void DisplayServerX11::cursor_set_shape(CursorShape p_shape) { + ERR_FAIL_INDEX(p_shape, CURSOR_MAX); + + if (p_shape == current_cursor) { + return; + } + + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + if (cursors[p_shape] != None) { + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]); + } + } else if (cursors[CURSOR_ARROW] != None) { + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + XDefineCursor(x11_display, E->get().x11_window, cursors[CURSOR_ARROW]); + } + } + } + + current_cursor = p_shape; +} +DisplayServerX11::CursorShape DisplayServerX11::cursor_get_shape() const { + return current_cursor; +} +void DisplayServerX11::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + + if (p_cursor.is_valid()) { + + Map<CursorShape, Vector<Variant>>::Element *cursor_c = cursors_cache.find(p_shape); + + if (cursor_c) { + if (cursor_c->get()[0] == p_cursor && cursor_c->get()[1] == p_hotspot) { + cursor_set_shape(p_shape); + return; + } + + cursors_cache.erase(p_shape); + } + + Ref<Texture2D> texture = p_cursor; + Ref<AtlasTexture> atlas_texture = p_cursor; + Ref<Image> image; + Size2 texture_size; + Rect2 atlas_rect; + + if (texture.is_valid()) { + image = texture->get_data(); + } + + if (!image.is_valid() && atlas_texture.is_valid()) { + texture = atlas_texture->get_atlas(); + + atlas_rect.size.width = texture->get_width(); + atlas_rect.size.height = texture->get_height(); + atlas_rect.position.x = atlas_texture->get_region().position.x; + atlas_rect.position.y = atlas_texture->get_region().position.y; + + texture_size.width = atlas_texture->get_region().size.x; + texture_size.height = atlas_texture->get_region().size.y; + } else if (image.is_valid()) { + texture_size.width = texture->get_width(); + texture_size.height = texture->get_height(); + } + + ERR_FAIL_COND(!texture.is_valid()); + ERR_FAIL_COND(p_hotspot.x < 0 || p_hotspot.y < 0); + ERR_FAIL_COND(texture_size.width > 256 || texture_size.height > 256); + ERR_FAIL_COND(p_hotspot.x > texture_size.width || p_hotspot.y > texture_size.height); + + image = texture->get_data(); + + ERR_FAIL_COND(!image.is_valid()); + + // Create the cursor structure + XcursorImage *cursor_image = XcursorImageCreate(texture_size.width, texture_size.height); + XcursorUInt image_size = texture_size.width * texture_size.height; + XcursorDim size = sizeof(XcursorPixel) * image_size; + + cursor_image->version = 1; + cursor_image->size = size; + cursor_image->xhot = p_hotspot.x; + cursor_image->yhot = p_hotspot.y; + + // allocate memory to contain the whole file + cursor_image->pixels = (XcursorPixel *)memalloc(size); + + for (XcursorPixel index = 0; index < image_size; index++) { + int row_index = floor(index / texture_size.width) + atlas_rect.position.y; + int column_index = (index % int(texture_size.width)) + atlas_rect.position.x; + + if (atlas_texture.is_valid()) { + column_index = MIN(column_index, atlas_rect.size.width - 1); + row_index = MIN(row_index, atlas_rect.size.height - 1); + } + + *(cursor_image->pixels + index) = image->get_pixel(column_index, row_index).to_argb32(); + } + + ERR_FAIL_COND(cursor_image->pixels == NULL); + + // Save it for a further usage + cursors[p_shape] = XcursorImageLoadCursor(x11_display, cursor_image); + + Vector<Variant> params; + params.push_back(p_cursor); + params.push_back(p_hotspot); + cursors_cache.insert(p_shape, params); + + if (p_shape == current_cursor) { + if (mouse_mode == MOUSE_MODE_VISIBLE || mouse_mode == MOUSE_MODE_CONFINED) { + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + XDefineCursor(x11_display, E->get().x11_window, cursors[p_shape]); + } + } + } + + memfree(cursor_image->pixels); + XcursorImageDestroy(cursor_image); + } else { + // Reset to default system cursor + if (img[p_shape]) { + cursors[p_shape] = XcursorImageLoadCursor(x11_display, img[p_shape]); + } + + CursorShape c = current_cursor; + current_cursor = CURSOR_MAX; + cursor_set_shape(c); + + cursors_cache.erase(p_shape); + } +} + +DisplayServerX11::LatinKeyboardVariant DisplayServerX11::get_latin_keyboard_variant() const { + XkbDescRec *xkbdesc = XkbAllocKeyboard(); + ERR_FAIL_COND_V(!xkbdesc, LATIN_KEYBOARD_QWERTY); + + XkbGetNames(x11_display, XkbSymbolsNameMask, xkbdesc); + ERR_FAIL_COND_V(!xkbdesc->names, LATIN_KEYBOARD_QWERTY); + ERR_FAIL_COND_V(!xkbdesc->names->symbols, LATIN_KEYBOARD_QWERTY); + + char *layout = XGetAtomName(x11_display, xkbdesc->names->symbols); + ERR_FAIL_COND_V(!layout, LATIN_KEYBOARD_QWERTY); + + Vector<String> info = String(layout).split("+"); + ERR_FAIL_INDEX_V(1, info.size(), LATIN_KEYBOARD_QWERTY); + + if (info[1].find("colemak") != -1) { + return LATIN_KEYBOARD_COLEMAK; + } else if (info[1].find("qwertz") != -1) { + return LATIN_KEYBOARD_QWERTZ; + } else if (info[1].find("azerty") != -1) { + return LATIN_KEYBOARD_AZERTY; + } else if (info[1].find("qzerty") != -1) { + return LATIN_KEYBOARD_QZERTY; + } else if (info[1].find("dvorak") != -1) { + return LATIN_KEYBOARD_DVORAK; + } else if (info[1].find("neo") != -1) { + return LATIN_KEYBOARD_NEO; + } + + return LATIN_KEYBOARD_QWERTY; +} + +DisplayServerX11::Property DisplayServerX11::_read_property(Display *p_display, Window p_window, Atom p_property) { + + Atom actual_type; + int actual_format; + unsigned long nitems; + unsigned long bytes_after; + unsigned char *ret = 0; + + int read_bytes = 1024; + + //Keep trying to read the property until there are no + //bytes unread. + do { + if (ret != 0) + XFree(ret); + + XGetWindowProperty(p_display, p_window, p_property, 0, read_bytes, False, AnyPropertyType, + &actual_type, &actual_format, &nitems, &bytes_after, + &ret); + + read_bytes *= 2; + + } while (bytes_after != 0); + + Property p = { ret, actual_format, (int)nitems, actual_type }; + + return p; +} + +static Atom pick_target_from_list(Display *p_display, Atom *p_list, int p_count) { + + static const char *target_type = "text/uri-list"; + + for (int i = 0; i < p_count; i++) { + + Atom atom = p_list[i]; + + if (atom != None && String(XGetAtomName(p_display, atom)) == target_type) + return atom; + } + return None; +} + +static Atom pick_target_from_atoms(Display *p_disp, Atom p_t1, Atom p_t2, Atom p_t3) { + + static const char *target_type = "text/uri-list"; + if (p_t1 != None && String(XGetAtomName(p_disp, p_t1)) == target_type) + return p_t1; + + if (p_t2 != None && String(XGetAtomName(p_disp, p_t2)) == target_type) + return p_t2; + + if (p_t3 != None && String(XGetAtomName(p_disp, p_t3)) == target_type) + return p_t3; + + return None; +} + +void DisplayServerX11::_get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state) { + + state->set_shift((p_x11_state & ShiftMask)); + state->set_control((p_x11_state & ControlMask)); + state->set_alt((p_x11_state & Mod1Mask /*|| p_x11_state&Mod5Mask*/)); //altgr should not count as alt + state->set_metakey((p_x11_state & Mod4Mask)); +} + +unsigned int DisplayServerX11::_get_mouse_button_state(unsigned int p_x11_button, int p_x11_type) { + + unsigned int mask = 1 << (p_x11_button - 1); + + if (p_x11_type == ButtonPress) { + last_button_state |= mask; + } else { + last_button_state &= ~mask; + } + + return last_button_state; +} + +void DisplayServerX11::_handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo) { + + WindowData wd = windows[p_window]; + // X11 functions don't know what const is + XKeyEvent *xkeyevent = p_event; + + // This code was pretty difficult to write. + // The docs stink and every toolkit seems to + // do it in a different way. + + /* Phase 1, obtain a proper keysym */ + + // This was also very difficult to figure out. + // You'd expect you could just use Keysym provided by + // XKeycodeToKeysym to obtain internationalized + // input.. WRONG!! + // you must use XLookupString (???) which not only wastes + // cycles generating an unnecessary string, but also + // still works in half the cases. (won't handle deadkeys) + // For more complex input methods (deadkeys and more advanced) + // you have to use XmbLookupString (??). + // So.. then you have to chosse which of both results + // you want to keep. + // This is a real bizarreness and cpu waster. + + KeySym keysym_keycode = 0; // keysym used to find a keycode + KeySym keysym_unicode = 0; // keysym used to find unicode + + // XLookupString returns keysyms usable as nice keycodes. + char str[256 + 1]; + XKeyEvent xkeyevent_no_mod = *xkeyevent; + xkeyevent_no_mod.state &= ~ShiftMask; + xkeyevent_no_mod.state &= ~ControlMask; + XLookupString(xkeyevent, str, 256, &keysym_unicode, NULL); + XLookupString(&xkeyevent_no_mod, NULL, 0, &keysym_keycode, NULL); + + // Meanwhile, XLookupString returns keysyms useful for unicode. + + if (!xmbstring) { + // keep a temporary buffer for the string + xmbstring = (char *)memalloc(sizeof(char) * 8); + xmblen = 8; + } + + if (xkeyevent->type == KeyPress && wd.xic) { + + Status status; +#ifdef X_HAVE_UTF8_STRING + int utf8len = 8; + char *utf8string = (char *)memalloc(sizeof(char) * utf8len); + int utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string, + utf8len - 1, &keysym_unicode, &status); + if (status == XBufferOverflow) { + utf8len = utf8bytes + 1; + utf8string = (char *)memrealloc(utf8string, utf8len); + utf8bytes = Xutf8LookupString(wd.xic, xkeyevent, utf8string, + utf8len - 1, &keysym_unicode, &status); + } + utf8string[utf8bytes] = '\0'; + + if (status == XLookupChars) { + bool keypress = xkeyevent->type == KeyPress; + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode); + + if (keycode >= 'a' && keycode <= 'z') + keycode -= 'a' - 'A'; + + String tmp; + tmp.parse_utf8(utf8string, utf8bytes); + for (int i = 0; i < tmp.length(); i++) { + Ref<InputEventKey> k; + k.instance(); + if (physical_keycode == 0 && keycode == 0 && tmp[i] == 0) { + continue; + } + + if (keycode == 0) { + keycode = physical_keycode; + } + + _get_key_modifier_state(xkeyevent->state, k); + + k->set_unicode(tmp[i]); + + k->set_pressed(keypress); + + k->set_keycode(keycode); + + k->set_physical_keycode(physical_keycode); + + k->set_echo(false); + + if (k->get_keycode() == KEY_BACKTAB) { + //make it consistent across platforms. + k->set_keycode(KEY_TAB); + k->set_physical_keycode(KEY_TAB); + k->set_shift(true); + } + + Input::get_singleton()->accumulate_input_event(k); + } + memfree(utf8string); + return; + } + memfree(utf8string); +#else + do { + + int mnbytes = XmbLookupString(xic, xkeyevent, xmbstring, xmblen - 1, &keysym_unicode, &status); + xmbstring[mnbytes] = '\0'; + + if (status == XBufferOverflow) { + xmblen = mnbytes + 1; + xmbstring = (char *)memrealloc(xmbstring, xmblen); + } + } while (status == XBufferOverflow); +#endif + } + + /* Phase 2, obtain a Godot keycode from the keysym */ + + // KeyMappingX11 just translated the X11 keysym to a PIGUI + // keysym, so it works in all platforms the same. + + unsigned int keycode = KeyMappingX11::get_keycode(keysym_keycode); + unsigned int physical_keycode = KeyMappingX11::get_scancode(xkeyevent->keycode); + + /* Phase 3, obtain a unicode character from the keysym */ + + // KeyMappingX11 also translates keysym to unicode. + // It does a binary search on a table to translate + // most properly. + unsigned int unicode = keysym_unicode > 0 ? KeyMappingX11::get_unicode_from_keysym(keysym_unicode) : 0; + + /* Phase 4, determine if event must be filtered */ + + // This seems to be a side-effect of using XIM. + // XEventFilter looks like a core X11 function, + // but it's actually just used to see if we must + // ignore a deadkey, or events XIM determines + // must not reach the actual gui. + // Guess it was a design problem of the extension + + bool keypress = xkeyevent->type == KeyPress; + + if (physical_keycode == 0 && keycode == 0 && unicode == 0) { + return; + } + + if (keycode == 0) { + keycode = physical_keycode; + } + + /* Phase 5, determine modifier mask */ + + // No problems here, except I had no way to + // know Mod1 was ALT and Mod4 was META (applekey/winkey) + // just tried Mods until i found them. + + //print_verbose("mod1: "+itos(xkeyevent->state&Mod1Mask)+" mod 5: "+itos(xkeyevent->state&Mod5Mask)); + + Ref<InputEventKey> k; + k.instance(); + + _get_key_modifier_state(xkeyevent->state, k); + + /* Phase 6, determine echo character */ + + // Echo characters in X11 are a keyrelease and a keypress + // one after the other with the (almot) same timestamp. + // To detect them, i use XPeekEvent and check that their + // difference in time is below a threshold. + + if (xkeyevent->type != KeyPress) { + + p_echo = false; + + // make sure there are events pending, + // so this call won't block. + if (XPending(x11_display) > 0) { + XEvent peek_event; + XPeekEvent(x11_display, &peek_event); + + // I'm using a threshold of 5 msecs, + // since sometimes there seems to be a little + // jitter. I'm still not convinced that all this approach + // is correct, but the xorg developers are + // not very helpful today. + +#define ABSDIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + ::Time threshold = ABSDIFF(peek_event.xkey.time, xkeyevent->time); +#undef ABSDIFF + if (peek_event.type == KeyPress && threshold < 5) { + KeySym rk; + XLookupString((XKeyEvent *)&peek_event, str, 256, &rk, NULL); + if (rk == keysym_keycode) { + XEvent event; + XNextEvent(x11_display, &event); //erase next event + _handle_key_event(p_window, (XKeyEvent *)&event, true); + return; //ignore current, echo next + } + } + + // use the time from peek_event so it always works + } + + // save the time to check for echo when keypress happens + } + + /* Phase 7, send event to Window */ + + k->set_pressed(keypress); + + if (keycode >= 'a' && keycode <= 'z') + keycode -= 'a' - 'A'; + + k->set_keycode(keycode); + k->set_physical_keycode(physical_keycode); + k->set_unicode(unicode); + k->set_echo(p_echo); + + if (k->get_keycode() == KEY_BACKTAB) { + //make it consistent across platforms. + k->set_keycode(KEY_TAB); + k->set_physical_keycode(KEY_TAB); + k->set_shift(true); + } + + //don't set mod state if modifier keys are released by themselves + //else event.is_action() will not work correctly here + if (!k->is_pressed()) { + if (k->get_keycode() == KEY_SHIFT) + k->set_shift(false); + else if (k->get_keycode() == KEY_CONTROL) + k->set_control(false); + else if (k->get_keycode() == KEY_ALT) + k->set_alt(false); + else if (k->get_keycode() == KEY_META) + k->set_metakey(false); + } + + bool last_is_pressed = Input::get_singleton()->is_key_pressed(k->get_keycode()); + if (k->is_pressed()) { + if (last_is_pressed) { + k->set_echo(true); + } + } + + Input::get_singleton()->accumulate_input_event(k); +} + +void DisplayServerX11::_xim_destroy_callback(::XIM im, ::XPointer client_data, + ::XPointer call_data) { + + WARN_PRINT("Input method stopped"); + DisplayServerX11 *ds = reinterpret_cast<DisplayServerX11 *>(client_data); + ds->xim = NULL; + + for (Map<WindowID, WindowData>::Element *E = ds->windows.front(); E; E = E->next()) { + E->get().xic = NULL; + } +} + +void DisplayServerX11::_window_changed(XEvent *event) { + + WindowID window_id = MAIN_WINDOW_ID; + + //assign the event to the relevant window + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + if (event->xany.window == E->get().x11_window) { + window_id = E->key(); + break; + } + } + + WindowData &wd = windows[window_id]; + + if (wd.xic) { + // Not portable. + window_set_ime_position(Point2(0, 1)); + } + if ((event->xconfigure.width == wd.size.width) && + (event->xconfigure.height == wd.size.height)) + return; + + wd.size.width = event->xconfigure.width; + wd.size.height = event->xconfigure.height; + +#if defined(VULKAN_ENABLED) + if (video_driver == "vulkan") { + context_vulkan->window_resize(wd.vulkan_window, wd.size.width, wd.size.height); + } +#endif +} + +void DisplayServerX11::process_events() { + do_mouse_warp = false; + + // Is the current mouse mode one where it needs to be grabbed. + bool mouse_mode_grab = mouse_mode == MOUSE_MODE_CAPTURED || mouse_mode == MOUSE_MODE_CONFINED; + + while (XPending(x11_display) > 0) { + XEvent event; + XNextEvent(x11_display, &event); + + WindowID window_id = MAIN_WINDOW_ID; + + //assign the event to the relevant window + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + if (event.xany.window == E->get().x11_window) { + window_id = E->key(); + break; + } + } + + if (XFilterEvent(&event, None)) { + continue; + } + + if (XGetEventData(x11_display, &event.xcookie)) { + + if (event.xcookie.type == GenericEvent && event.xcookie.extension == xi.opcode) { + + XIDeviceEvent *event_data = (XIDeviceEvent *)event.xcookie.data; + int index = event_data->detail; + Vector2 pos = Vector2(event_data->event_x, event_data->event_y); + + switch (event_data->evtype) { + case XI_HierarchyChanged: + case XI_DeviceChanged: { + _refresh_device_info(); + } break; + case XI_RawMotion: { + XIRawEvent *raw_event = (XIRawEvent *)event_data; + int device_id = raw_event->deviceid; + + // Determine the axis used (called valuators in XInput for some forsaken reason) + // Mask is a bitmask indicating which axes are involved. + // We are interested in the values of axes 0 and 1. + if (raw_event->valuators.mask_len <= 0) { + break; + } + + const double *values = raw_event->raw_values; + + double rel_x = 0.0; + double rel_y = 0.0; + double pressure = 0.0; + double tilt_x = 0.0; + double tilt_y = 0.0; + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSX)) { + rel_x = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_ABSY)) { + rel_y = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_PRESSURE)) { + pressure = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTX)) { + tilt_x = *values; + values++; + } + + if (XIMaskIsSet(raw_event->valuators.mask, VALUATOR_TILTY)) { + tilt_y = *values; + } + + Map<int, Vector3>::Element *pen_info = xi.pen_devices.find(device_id); + if (pen_info) { + Vector3 mult = pen_info->value(); + if (mult.x != 0.0) xi.pressure = pressure / mult.x; + if ((mult.y != 0.0) && (mult.z != 0.0)) xi.tilt = Vector2(tilt_x / mult.y, tilt_y / mult.z); + } + + // https://bugs.freedesktop.org/show_bug.cgi?id=71609 + // http://lists.libsdl.org/pipermail/commits-libsdl.org/2015-June/000282.html + if (raw_event->time == xi.last_relative_time && rel_x == xi.relative_motion.x && rel_y == xi.relative_motion.y) { + break; // Flush duplicate to avoid overly fast motion + } + + xi.old_raw_pos.x = xi.raw_pos.x; + xi.old_raw_pos.y = xi.raw_pos.y; + xi.raw_pos.x = rel_x; + xi.raw_pos.y = rel_y; + + Map<int, Vector2>::Element *abs_info = xi.absolute_devices.find(device_id); + + if (abs_info) { + // Absolute mode device + Vector2 mult = abs_info->value(); + + xi.relative_motion.x += (xi.raw_pos.x - xi.old_raw_pos.x) * mult.x; + xi.relative_motion.y += (xi.raw_pos.y - xi.old_raw_pos.y) * mult.y; + } else { + // Relative mode device + xi.relative_motion.x = xi.raw_pos.x; + xi.relative_motion.y = xi.raw_pos.y; + } + + xi.last_relative_time = raw_event->time; + } break; +#ifdef TOUCH_ENABLED + case XI_TouchBegin: // Fall-through + // Disabled hand-in-hand with the grabbing + //XIAllowTouchEvents(x11_display, event_data->deviceid, event_data->detail, x11_window, XIAcceptTouch); + + case XI_TouchEnd: { + + bool is_begin = event_data->evtype == XI_TouchBegin; + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_window_id(window_id); + st->set_index(index); + st->set_position(pos); + st->set_pressed(is_begin); + + if (is_begin) { + if (xi.state.has(index)) // Defensive + break; + xi.state[index] = pos; + if (xi.state.size() == 1) { + // X11 may send a motion event when a touch gesture begins, that would result + // in a spurious mouse motion event being sent to Godot; remember it to be able to filter it out + xi.mouse_pos_to_filter = pos; + } + Input::get_singleton()->accumulate_input_event(st); + } else { + if (!xi.state.has(index)) // Defensive + break; + xi.state.erase(index); + Input::get_singleton()->accumulate_input_event(st); + } + } break; + + case XI_TouchUpdate: { + + Map<int, Vector2>::Element *curr_pos_elem = xi.state.find(index); + if (!curr_pos_elem) { // Defensive + break; + } + + if (curr_pos_elem->value() != pos) { + + Ref<InputEventScreenDrag> sd; + sd.instance(); + sd->set_window_id(window_id); + sd->set_index(index); + sd->set_position(pos); + sd->set_relative(pos - curr_pos_elem->value()); + Input::get_singleton()->accumulate_input_event(sd); + + curr_pos_elem->value() = pos; + } + } break; +#endif + } + } + } + XFreeEventData(x11_display, &event.xcookie); + + switch (event.type) { + case Expose: + Main::force_redraw(); + break; + + case NoExpose: + minimized = true; + break; + + case VisibilityNotify: { + XVisibilityEvent *visibility = (XVisibilityEvent *)&event; + minimized = (visibility->state == VisibilityFullyObscured); + } break; + case LeaveNotify: { + if (!mouse_mode_grab) + Input::get_singleton()->parse_notification(MainLoop::NOTIFICATION_WM_MOUSE_EXIT); + + } break; + case EnterNotify: { + if (!mouse_mode_grab) + Input::get_singleton()->parse_notification(MainLoop::NOTIFICATION_WM_MOUSE_ENTER); + } break; + case FocusIn: + minimized = false; + window_has_focus = true; + Input::get_singleton()->parse_notification(MainLoop::NOTIFICATION_WM_FOCUS_IN); + window_focused = true; + + if (mouse_mode_grab) { + // Show and update the cursor if confined and the window regained focus. + + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + + if (mouse_mode == MOUSE_MODE_CONFINED) + XUndefineCursor(x11_display, E->get().x11_window); + else if (mouse_mode == MOUSE_MODE_CAPTURED) // or re-hide it in captured mode + XDefineCursor(x11_display, E->get().x11_window, null_cursor); + + XGrabPointer( + x11_display, E->get().x11_window, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, E->get().x11_window, None, CurrentTime); + } + } +#ifdef TOUCH_ENABLED + // Grab touch devices to avoid OS gesture interference + /*for (int i = 0; i < xi.touch_devices.size(); ++i) { + XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask); + }*/ +#endif + if (windows[window_id].xic) { + XSetICFocus(windows[window_id].xic); + } + break; + + case FocusOut: + window_has_focus = false; + Input::get_singleton()->release_pressed_events(); + Input::get_singleton()->parse_notification(MainLoop::NOTIFICATION_WM_FOCUS_OUT); + window_focused = false; + + if (mouse_mode_grab) { + for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) { + + //dear X11, I try, I really try, but you never work, you do whathever you want. + if (mouse_mode == MOUSE_MODE_CAPTURED) { + // Show the cursor if we're in captured mode so it doesn't look weird. + XUndefineCursor(x11_display, E->get().x11_window); + } + } + XUngrabPointer(x11_display, CurrentTime); + } +#ifdef TOUCH_ENABLED + // Ungrab touch devices so input works as usual while we are unfocused + /*for (int i = 0; i < xi.touch_devices.size(); ++i) { + XIUngrabDevice(x11_display, xi.touch_devices[i], CurrentTime); + }*/ + + // Release every pointer to avoid sticky points + for (Map<int, Vector2>::Element *E = xi.state.front(); E; E = E->next()) { + + Ref<InputEventScreenTouch> st; + st.instance(); + st->set_index(E->key()); + st->set_window_id(window_id); + st->set_position(E->get()); + Input::get_singleton()->accumulate_input_event(st); + } + xi.state.clear(); +#endif + if (windows[window_id].xic) { + XSetICFocus(windows[window_id].xic); + } + break; + + case ConfigureNotify: + _window_changed(&event); + break; + case ButtonPress: + case ButtonRelease: { + + /* exit in case of a mouse button press */ + last_timestamp = event.xbutton.time; + if (mouse_mode == MOUSE_MODE_CAPTURED) { + event.xbutton.x = last_mouse_pos.x; + event.xbutton.y = last_mouse_pos.y; + } + + Ref<InputEventMouseButton> mb; + mb.instance(); + + mb->set_window_id(window_id); + _get_key_modifier_state(event.xbutton.state, mb); + mb->set_button_index(event.xbutton.button); + if (mb->get_button_index() == 2) + mb->set_button_index(3); + else if (mb->get_button_index() == 3) + mb->set_button_index(2); + mb->set_button_mask(_get_mouse_button_state(mb->get_button_index(), event.xbutton.type)); + mb->set_position(Vector2(event.xbutton.x, event.xbutton.y)); + mb->set_global_position(mb->get_position()); + + mb->set_pressed((event.type == ButtonPress)); + + if (event.type == ButtonPress) { + + uint64_t diff = OS::get_singleton()->get_ticks_usec() / 1000 - last_click_ms; + + if (mb->get_button_index() == last_click_button_index) { + + if (diff < 400 && Vector2(last_click_pos).distance_to(Vector2(event.xbutton.x, event.xbutton.y)) < 5) { + + last_click_ms = 0; + last_click_pos = Point2i(-100, -100); + last_click_button_index = -1; + mb->set_doubleclick(true); + } + + } else if (mb->get_button_index() < 4 || mb->get_button_index() > 7) { + last_click_button_index = mb->get_button_index(); + } + + if (!mb->is_doubleclick()) { + last_click_ms += diff; + last_click_pos = Point2i(event.xbutton.x, event.xbutton.y); + } + } + + Input::get_singleton()->accumulate_input_event(mb); + + } break; + case MotionNotify: { + + // The X11 API requires filtering one-by-one through the motion + // notify events, in order to figure out which event is the one + // generated by warping the mouse pointer. + + while (true) { + if (mouse_mode == MOUSE_MODE_CAPTURED && event.xmotion.x == windows[MAIN_WINDOW_ID].size.width / 2 && event.xmotion.y == windows[MAIN_WINDOW_ID].size.height / 2) { + //this is likely the warp event since it was warped here + center = Vector2(event.xmotion.x, event.xmotion.y); + break; + } + + if (XPending(x11_display) > 0) { + XEvent tevent; + XPeekEvent(x11_display, &tevent); + if (tevent.type == MotionNotify) { + XNextEvent(x11_display, &event); + } else { + break; + } + } else { + break; + } + } + + last_timestamp = event.xmotion.time; + + // Motion is also simple. + // A little hack is in order + // to be able to send relative motion events. + Point2i pos(event.xmotion.x, event.xmotion.y); + + // Avoidance of spurious mouse motion (see handling of touch) + bool filter = false; + // Adding some tolerance to match better Point2i to Vector2 + if (xi.state.size() && Vector2(pos).distance_squared_to(xi.mouse_pos_to_filter) < 2) { + filter = true; + } + // Invalidate to avoid filtering a possible legitimate similar event coming later + xi.mouse_pos_to_filter = Vector2(1e10, 1e10); + if (filter) { + break; + } + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + if (xi.relative_motion.x == 0 && xi.relative_motion.y == 0) { + break; + } + + Point2i new_center = pos; + pos = last_mouse_pos + xi.relative_motion; + center = new_center; + do_mouse_warp = window_has_focus; // warp the cursor if we're focused in + } + + if (!last_mouse_pos_valid) { + + last_mouse_pos = pos; + last_mouse_pos_valid = true; + } + + // Hackish but relative mouse motion is already handled in the RawMotion event. + // RawMotion does not provide the absolute mouse position (whereas MotionNotify does). + // Therefore, RawMotion cannot be the authority on absolute mouse position. + // RawMotion provides more precision than MotionNotify, which doesn't sense subpixel motion. + // Therefore, MotionNotify cannot be the authority on relative mouse motion. + // This means we need to take a combined approach... + Point2i rel; + + // Only use raw input if in capture mode. Otherwise use the classic behavior. + if (mouse_mode == MOUSE_MODE_CAPTURED) { + rel = xi.relative_motion; + } else { + rel = pos - last_mouse_pos; + } + + // Reset to prevent lingering motion + xi.relative_motion.x = 0; + xi.relative_motion.y = 0; + + if (mouse_mode == MOUSE_MODE_CAPTURED) { + pos = Point2i(windows[MAIN_WINDOW_ID].size.width / 2, windows[MAIN_WINDOW_ID].size.height / 2); + } + + Ref<InputEventMouseMotion> mm; + mm.instance(); + + mm->set_window_id(window_id); + mm->set_pressure(xi.pressure); + mm->set_tilt(xi.tilt); + + // Make the absolute position integral so it doesn't look _too_ weird :) + Point2i posi(pos); + + _get_key_modifier_state(event.xmotion.state, mm); + mm->set_button_mask(mouse_get_button_state()); + mm->set_position(posi); + mm->set_global_position(posi); + Input::get_singleton()->set_mouse_position(posi); + mm->set_speed(Input::get_singleton()->get_last_mouse_speed()); + + mm->set_relative(rel); + + last_mouse_pos = pos; + + // printf("rel: %d,%d\n", rel.x, rel.y ); + // Don't propagate the motion event unless we have focus + // this is so that the relative motion doesn't get messed up + // after we regain focus. + if (window_has_focus || !mouse_mode_grab) + Input::get_singleton()->accumulate_input_event(mm); + + } break; + case KeyPress: + case KeyRelease: { + + last_timestamp = event.xkey.time; + + // key event is a little complex, so + // it will be handled in its own function. + _handle_key_event(window_id, (XKeyEvent *)&event); + } break; + case SelectionRequest: { + + XSelectionRequestEvent *req; + XEvent e, respond; + e = event; + + req = &(e.xselectionrequest); + if (req->target == XInternAtom(x11_display, "UTF8_STRING", 0) || + req->target == XInternAtom(x11_display, "COMPOUND_TEXT", 0) || + req->target == XInternAtom(x11_display, "TEXT", 0) || + req->target == XA_STRING || + req->target == XInternAtom(x11_display, "text/plain;charset=utf-8", 0) || + req->target == XInternAtom(x11_display, "text/plain", 0)) { + CharString clip = clipboard_get().utf8(); + XChangeProperty(x11_display, + req->requestor, + req->property, + req->target, + 8, + PropModeReplace, + (unsigned char *)clip.get_data(), + clip.length()); + respond.xselection.property = req->property; + } else if (req->target == XInternAtom(x11_display, "TARGETS", 0)) { + + Atom data[7]; + data[0] = XInternAtom(x11_display, "TARGETS", 0); + data[1] = XInternAtom(x11_display, "UTF8_STRING", 0); + data[2] = XInternAtom(x11_display, "COMPOUND_TEXT", 0); + data[3] = XInternAtom(x11_display, "TEXT", 0); + data[4] = XA_STRING; + data[5] = XInternAtom(x11_display, "text/plain;charset=utf-8", 0); + data[6] = XInternAtom(x11_display, "text/plain", 0); + + XChangeProperty(x11_display, + req->requestor, + req->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char *)&data, + sizeof(data) / sizeof(data[0])); + respond.xselection.property = req->property; + + } else { + char *targetname = XGetAtomName(x11_display, req->target); + printf("No Target '%s'\n", targetname); + if (targetname) + XFree(targetname); + respond.xselection.property = None; + } + + respond.xselection.type = SelectionNotify; + respond.xselection.display = req->display; + respond.xselection.requestor = req->requestor; + respond.xselection.selection = req->selection; + respond.xselection.target = req->target; + respond.xselection.time = req->time; + XSendEvent(x11_display, req->requestor, True, NoEventMask, &respond); + XFlush(x11_display); + } break; + + case SelectionNotify: + + if (event.xselection.target == requested) { + + Property p = _read_property(x11_display, windows[window_id].x11_window, XInternAtom(x11_display, "PRIMARY", 0)); + + Vector<String> files = String((char *)p.data).split("\n", false); + for (int i = 0; i < files.size(); i++) { + files.write[i] = files[i].replace("file://", "").http_unescape().strip_edges(); + } + Input::get_singleton()->parse_drop_files(files); + + //Reply that all is well. + XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = x11_display; + m.window = xdnd_source_window; + m.message_type = xdnd_finished; + m.format = 32; + m.data.l[0] = windows[window_id].x11_window; + m.data.l[1] = 1; + m.data.l[2] = xdnd_action_copy; //We only ever copy. + + XSendEvent(x11_display, xdnd_source_window, False, NoEventMask, (XEvent *)&m); + } + break; + + case ClientMessage: + + if ((unsigned int)event.xclient.data.l[0] == (unsigned int)wm_delete) + Input::get_singleton()->parse_notification(MainLoop::NOTIFICATION_WM_QUIT_REQUEST); + + else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_enter) { + + //File(s) have been dragged over the window, check for supported target (text/uri-list) + xdnd_version = (event.xclient.data.l[1] >> 24); + Window source = event.xclient.data.l[0]; + bool more_than_3 = event.xclient.data.l[1] & 1; + if (more_than_3) { + Property p = _read_property(x11_display, source, XInternAtom(x11_display, "XdndTypeList", False)); + requested = pick_target_from_list(x11_display, (Atom *)p.data, p.nitems); + } else + requested = pick_target_from_atoms(x11_display, event.xclient.data.l[2], event.xclient.data.l[3], event.xclient.data.l[4]); + } else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_position) { + + //xdnd position event, reply with an XDND status message + //just depending on type of data for now + XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = event.xclient.data.l[0]; + m.message_type = xdnd_status; + m.format = 32; + m.data.l[0] = windows[window_id].x11_window; + m.data.l[1] = (requested != None); + m.data.l[2] = 0; //empty rectangle + m.data.l[3] = 0; + m.data.l[4] = xdnd_action_copy; + + XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m); + XFlush(x11_display); + } else if ((unsigned int)event.xclient.message_type == (unsigned int)xdnd_drop) { + + if (requested != None) { + xdnd_source_window = event.xclient.data.l[0]; + if (xdnd_version >= 1) + XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, event.xclient.data.l[2]); + else + XConvertSelection(x11_display, xdnd_selection, requested, XInternAtom(x11_display, "PRIMARY", 0), windows[window_id].x11_window, CurrentTime); + } else { + //Reply that we're not interested. + XClientMessageEvent m; + memset(&m, 0, sizeof(m)); + m.type = ClientMessage; + m.display = event.xclient.display; + m.window = event.xclient.data.l[0]; + m.message_type = xdnd_finished; + m.format = 32; + m.data.l[0] = windows[window_id].x11_window; + m.data.l[1] = 0; + m.data.l[2] = None; //Failed. + XSendEvent(x11_display, event.xclient.data.l[0], False, NoEventMask, (XEvent *)&m); + } + } + break; + default: + break; + } + } + + XFlush(x11_display); + + if (do_mouse_warp) { + + XWarpPointer(x11_display, None, windows[MAIN_WINDOW_ID].x11_window, + 0, 0, 0, 0, (int)windows[MAIN_WINDOW_ID].size.width / 2, (int)windows[MAIN_WINDOW_ID].size.height / 2); + + /* + Window root, child; + int root_x, root_y; + int win_x, win_y; + unsigned int mask; + XQueryPointer( x11_display, x11_window, &root, &child, &root_x, &root_y, &win_x, &win_y, &mask ); + + printf("Root: %d,%d\n", root_x, root_y); + printf("Win: %d,%d\n", win_x, win_y); + */ + } + + Input::get_singleton()->flush_accumulated_events(); +} + +void DisplayServerX11::release_rendering_thread() { + WARN_PRINT("Rendering thread not supported by this display server."); +} +void DisplayServerX11::make_rendering_thread() { + WARN_PRINT("Rendering thread not supported by this display server."); +} +void DisplayServerX11::swap_buffers() { + WARN_PRINT("Swap buffers not supported by this display server."); +} + +void DisplayServerX11::set_native_icon(const String &p_filename) { + WARN_PRINT("Native icon not supported by this display server."); +} +void DisplayServerX11::set_icon(const Ref<Image> &p_icon) { + WARN_PRINT("Icon not supported by this display server."); +} + +DisplayServer *DisplayServerX11::create_func(const String &p_video_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + + return memnew(DisplayServerX11(p_video_driver, p_mode, p_flags, p_resolution, r_error)); +} + +DisplayServerX11::WindowID DisplayServerX11::_create_window(WindowMode p_mode, const Vector2i &p_resolution) { + + //Create window + + long visualMask = VisualScreenMask; + int numberOfVisuals; + XVisualInfo vInfoTemplate = {}; + vInfoTemplate.screen = DefaultScreen(x11_display); + XVisualInfo *visualInfo = XGetVisualInfo(x11_display, visualMask, &vInfoTemplate, &numberOfVisuals); + + Colormap colormap = XCreateColormap(x11_display, RootWindow(x11_display, vInfoTemplate.screen), visualInfo->visual, AllocNone); + + XSetWindowAttributes windowAttributes = {}; + windowAttributes.colormap = colormap; + windowAttributes.background_pixel = 0xFFFFFFFF; + windowAttributes.border_pixel = 0; + windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask; + + unsigned long valuemask = CWBorderPixel | CWColormap | CWEventMask; + + WindowID id; + { + WindowData wd; + wd.x11_window = XCreateWindow(x11_display, RootWindow(x11_display, visualInfo->screen), 0, 0, OS::get_singleton()->get_video_mode().width, OS::get_singleton()->get_video_mode().height, 0, visualInfo->depth, InputOutput, visualInfo->visual, valuemask, &windowAttributes); + + //set_class_hint(x11_display, wd.x11_window); + XMapWindow(x11_display, wd.x11_window); + XFlush(x11_display); + + XSync(x11_display, False); + //XSetErrorHandler(oldHandler); + + XFree(visualInfo); + +#if defined(VULKAN_ENABLED) + if (context_vulkan) { + wd.vulkan_window = context_vulkan->window_create(wd.x11_window, x11_display, p_resolution.width, p_resolution.height); + ERR_FAIL_COND_V_MSG(wd.vulkan_window == -1, INVALID_WINDOW_ID, "Can't create a Vulkan window"); + } +#endif + + //associate PID + + // make PID known to X11 + { + const long pid = OS::get_singleton()->get_process_id(); + Atom net_wm_pid = XInternAtom(x11_display, "_NET_WM_PID", False); + XChangeProperty(x11_display, wd.x11_window, net_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); + } + + long im_event_mask = 0; + + { + XIEventMask all_event_mask; + XSetWindowAttributes new_attr; + + new_attr.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | EnterWindowMask | + LeaveWindowMask | PointerMotionMask | + Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonMotionMask | KeymapStateMask | + ExposureMask | VisibilityChangeMask | + StructureNotifyMask | + SubstructureNotifyMask | SubstructureRedirectMask | + FocusChangeMask | PropertyChangeMask | + ColormapChangeMask | OwnerGrabButtonMask | + im_event_mask; + + XChangeWindowAttributes(x11_display, wd.x11_window, CWEventMask, &new_attr); + + static unsigned char all_mask_data[XIMaskLen(XI_LASTEVENT)] = {}; + + all_event_mask.deviceid = XIAllDevices; + all_event_mask.mask_len = sizeof(all_mask_data); + all_event_mask.mask = all_mask_data; + + XISetMask(all_event_mask.mask, XI_HierarchyChanged); + +#ifdef TOUCH_ENABLED + if (xi.touch_devices.size()) { + XISetMask(all_event_mask.mask, XI_TouchBegin); + XISetMask(all_event_mask.mask, XI_TouchUpdate); + XISetMask(all_event_mask.mask, XI_TouchEnd); + XISetMask(all_event_mask.mask, XI_TouchOwnership); + } +#endif + + XISelectEvents(x11_display, wd.x11_window, &all_event_mask, 1); + } + + /* set the titlebar name */ + XStoreName(x11_display, wd.x11_window, "Godot"); + XSetWMProtocols(x11_display, wd.x11_window, &wm_delete, 1); + XChangeProperty(x11_display, wd.x11_window, xdnd_aware, XA_ATOM, 32, PropModeReplace, (unsigned char *)&xdnd_version, 1); + + if (xim && xim_style) { + + wd.xic = XCreateIC(xim, XNInputStyle, xim_style, XNClientWindow, wd.x11_window, XNFocusWindow, wd.x11_window, (char *)NULL); + if (XGetICValues(wd.xic, XNFilterEvents, &im_event_mask, NULL) != NULL) { + WARN_PRINT("XGetICValues couldn't obtain XNFilterEvents value"); + XDestroyIC(wd.xic); + wd.xic = NULL; + } + if (wd.xic) { + XUnsetICFocus(wd.xic); + } else { + WARN_PRINT("XCreateIC couldn't create wd.xic"); + } + } else { + + wd.xic = NULL; + WARN_PRINT("XCreateIC couldn't create wd.xic"); + } + + id = window_id_counter++; + + windows[id] = wd; + } + + WindowData &wd = windows[id]; + + window_set_mode(p_mode, id); + + //set cursor + if (cursors[current_cursor] != None) { + + XDefineCursor(x11_display, wd.x11_window, cursors[current_cursor]); + } + return id; +} + +DisplayServerX11::DisplayServerX11(const String &p_video_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error) { + + r_error = OK; + + last_button_state = 0; + + xmbstring = NULL; + + last_click_ms = 0; + last_click_button_index = -1; + last_click_pos = Point2i(-100, -100); + + last_timestamp = 0; + last_mouse_pos_valid = false; + last_keyrelease_time = 0; + + if (OS::get_singleton()->get_render_thread_mode() == OS::RENDER_SEPARATE_THREAD) { + XInitThreads(); + } + + /** XLIB INITIALIZATION **/ + x11_display = XOpenDisplay(NULL); + + if (!x11_display) { + ERR_PRINT("X11 Display is not available"); + r_error = ERR_UNAVAILABLE; + return; + } + + char *modifiers = NULL; + Bool xkb_dar = False; + XAutoRepeatOn(x11_display); + xkb_dar = XkbSetDetectableAutoRepeat(x11_display, True, NULL); + + // Try to support IME if detectable auto-repeat is supported + if (xkb_dar == True) { + +#ifdef X_HAVE_UTF8_STRING + // Xutf8LookupString will be used later instead of XmbLookupString before + // the multibyte sequences can be converted to unicode string. + modifiers = XSetLocaleModifiers(""); +#endif + } + + if (modifiers == NULL) { + if (OS::get_singleton()->is_stdout_verbose()) { + WARN_PRINT("IME is disabled"); + } + XSetLocaleModifiers("@im=none"); + WARN_PRINT("Error setting locale modifiers"); + } + + const char *err; + xrr_get_monitors = NULL; + xrr_free_monitors = NULL; + int xrandr_major = 0; + int xrandr_minor = 0; + int event_base, error_base; + xrandr_ext_ok = XRRQueryExtension(x11_display, &event_base, &error_base); + xrandr_handle = dlopen("libXrandr.so.2", RTLD_LAZY); + if (!xrandr_handle) { + err = dlerror(); + fprintf(stderr, "could not load libXrandr.so.2, Error: %s\n", err); + } else { + XRRQueryVersion(x11_display, &xrandr_major, &xrandr_minor); + if (((xrandr_major << 8) | xrandr_minor) >= 0x0105) { + xrr_get_monitors = (xrr_get_monitors_t)dlsym(xrandr_handle, "XRRGetMonitors"); + if (!xrr_get_monitors) { + err = dlerror(); + fprintf(stderr, "could not find symbol XRRGetMonitors\nError: %s\n", err); + } else { + xrr_free_monitors = (xrr_free_monitors_t)dlsym(xrandr_handle, "XRRFreeMonitors"); + if (!xrr_free_monitors) { + err = dlerror(); + fprintf(stderr, "could not find XRRFreeMonitors\nError: %s\n", err); + xrr_get_monitors = NULL; + } + } + } + } + + if (!_refresh_device_info()) { + OS::get_singleton()->alert("Your system does not support XInput 2.\n" + "Please upgrade your distribution.", + "Unable to initialize XInput"); + r_error = ERR_UNAVAILABLE; + return; + } + + xim = XOpenIM(x11_display, NULL, NULL, NULL); + + if (xim == NULL) { + WARN_PRINT("XOpenIM failed"); + xim_style = 0L; + } else { + ::XIMCallback im_destroy_callback; + im_destroy_callback.client_data = (::XPointer)(this); + im_destroy_callback.callback = (::XIMProc)(_xim_destroy_callback); + if (XSetIMValues(xim, XNDestroyCallback, &im_destroy_callback, + NULL) != NULL) { + WARN_PRINT("Error setting XIM destroy callback"); + } + + ::XIMStyles *xim_styles = NULL; + xim_style = 0L; + char *imvalret = XGetIMValues(xim, XNQueryInputStyle, &xim_styles, NULL); + if (imvalret != NULL || xim_styles == NULL) { + fprintf(stderr, "Input method doesn't support any styles\n"); + } + + if (xim_styles) { + xim_style = 0L; + for (int i = 0; i < xim_styles->count_styles; i++) { + + if (xim_styles->supported_styles[i] == + (XIMPreeditNothing | XIMStatusNothing)) { + + xim_style = xim_styles->supported_styles[i]; + break; + } + } + + XFree(xim_styles); + } + XFree(imvalret); + } + + /* Atorm internment */ + wm_delete = XInternAtom(x11_display, "WM_DELETE_WINDOW", true); + //Set Xdnd (drag & drop) support + xdnd_aware = XInternAtom(x11_display, "XdndAware", False); + xdnd_version = 5; + xdnd_enter = XInternAtom(x11_display, "XdndEnter", False); + xdnd_position = XInternAtom(x11_display, "XdndPosition", False); + xdnd_status = XInternAtom(x11_display, "XdndStatus", False); + xdnd_action_copy = XInternAtom(x11_display, "XdndActionCopy", False); + xdnd_drop = XInternAtom(x11_display, "XdndDrop", False); + xdnd_finished = XInternAtom(x11_display, "XdndFinished", False); + xdnd_selection = XInternAtom(x11_display, "XdndSelection", False); + + //!!!!!!!!!!!!!!!!!!!!!!!!!! + //TODO - do Vulkan and GLES2 support checks, driver selection and fallback + video_driver = p_video_driver; + +#ifndef _MSC_VER +#warning Forcing vulkan video driver because OpenGL not implemented yet +#endif + video_driver = "vulkan"; + +#if defined(VULKAN_ENABLED) + if (video_driver == "vulkan") { + + context_vulkan = memnew(VulkanContextX11); + if (context_vulkan->initialize() != OK) { + memdelete(context_vulkan); + context_vulkan = NULL; + r_error = ERR_CANT_CREATE; + ERR_FAIL_MSG("Could not initialize Vulkan"); + } + + //temporary + rendering_device_vulkan = memnew(RenderingDeviceVulkan); + rendering_device_vulkan->initialize(context_vulkan); + + RasterizerRD::make_current(); + } +#endif + // Init context and rendering device +#if defined(OPENGL_ENABLED) + if (video_driver == "opengl_es") { + if (getenv("DRI_PRIME") == NULL) { + int use_prime = -1; + + if (getenv("PRIMUS_DISPLAY") || + getenv("PRIMUS_libGLd") || + getenv("PRIMUS_libGLa") || + getenv("PRIMUS_libGL") || + getenv("PRIMUS_LOAD_GLOBAL") || + getenv("BUMBLEBEE_SOCKET")) { + + print_verbose("Optirun/primusrun detected. Skipping GPU detection"); + use_prime = 0; + } + + if (getenv("LD_LIBRARY_PATH")) { + String ld_library_path(getenv("LD_LIBRARY_PATH")); + Vector<String> libraries = ld_library_path.split(":"); + + for (int i = 0; i < libraries.size(); ++i) { + if (FileAccess::exists(libraries[i] + "/libGL.so.1") || + FileAccess::exists(libraries[i] + "/libGL.so")) { + + print_verbose("Custom libGL override detected. Skipping GPU detection"); + use_prime = 0; + } + } + } + + if (use_prime == -1) { + print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic."); + use_prime = detect_prime(); + } + + if (use_prime) { + print_line("Found discrete GPU, setting DRI_PRIME=1 to use it."); + print_line("Note: Set DRI_PRIME=0 in the environment to disable Godot from using the discrete GPU."); + setenv("DRI_PRIME", "1", 1); + } + } + + ContextGL_X11::ContextType opengl_api_type = ContextGL_X11::GLES_2_0_COMPATIBLE; + + context_gles2 = memnew(ContextGL_X11(x11_display, x11_window, current_videomode, opengl_api_type)); + + if (context_gles2->initialize() != OK) { + memdelete(context_gles2); + context_gles2 = NULL; + ERR_FAIL_V(ERR_UNAVAILABLE); + } + + context_gles2->set_use_vsync(current_videomode.use_vsync); + + if (RasterizerGLES2::is_viable() == OK) { + RasterizerGLES2::register_config(); + RasterizerGLES2::make_current(); + } else { + memdelete(context_gles2); + context_gles2 = NULL; + ERR_FAIL_V(ERR_UNAVAILABLE); + } + } +#endif + + WindowID main_window = _create_window(p_mode, p_resolution); + for (int i = 0; i < WINDOW_FLAG_MAX; i++) { + if (p_flags & (1 << i)) { + window_set_flag(WindowFlags(i), true, main_window); + } + } + + /* + visual_server = memnew(VisualServerRaster); + if (get_render_thread_mode() != RENDER_THREAD_UNSAFE) { + visual_server = memnew(VisualServerWrapMT(visual_server, get_render_thread_mode() == RENDER_SEPARATE_THREAD)); + } + */ + + { + //set all event master mask + XIEventMask all_master_event_mask; + static unsigned char all_master_mask_data[XIMaskLen(XI_LASTEVENT)] = {}; + all_master_event_mask.deviceid = XIAllMasterDevices; + all_master_event_mask.mask_len = sizeof(all_master_mask_data); + all_master_event_mask.mask = all_master_mask_data; + XISetMask(all_master_event_mask.mask, XI_DeviceChanged); + XISetMask(all_master_event_mask.mask, XI_RawMotion); + XISelectEvents(x11_display, DefaultRootWindow(x11_display), &all_master_event_mask, 1); + } + + // Disabled by now since grabbing also blocks mouse events + // (they are received as extended events instead of standard events) + /*XIClearMask(xi.touch_event_mask.mask, XI_TouchOwnership); + + // Grab touch devices to avoid OS gesture interference + for (int i = 0; i < xi.touch_devices.size(); ++i) { + XIGrabDevice(x11_display, xi.touch_devices[i], x11_window, CurrentTime, None, XIGrabModeAsync, XIGrabModeAsync, False, &xi.touch_event_mask); + }*/ + + cursor_size = XcursorGetDefaultSize(x11_display); + cursor_theme = XcursorGetTheme(x11_display); + + if (!cursor_theme) { + print_verbose("XcursorGetTheme could not get cursor theme"); + cursor_theme = "default"; + } + + for (int i = 0; i < CURSOR_MAX; i++) { + + cursors[i] = None; + img[i] = NULL; + } + + current_cursor = CURSOR_ARROW; + + for (int i = 0; i < CURSOR_MAX; i++) { + + static const char *cursor_file[] = { + "left_ptr", + "xterm", + "hand2", + "cross", + "watch", + "left_ptr_watch", + "fleur", + "dnd-move", + "crossed_circle", + "v_double_arrow", + "h_double_arrow", + "size_bdiag", + "size_fdiag", + "move", + "row_resize", + "col_resize", + "question_arrow" + }; + + img[i] = XcursorLibraryLoadImage(cursor_file[i], cursor_theme, cursor_size); + if (!img[i]) { + const char *fallback = NULL; + + switch (i) { + case CURSOR_POINTING_HAND: + fallback = "pointer"; + break; + case CURSOR_CROSS: + fallback = "crosshair"; + break; + case CURSOR_WAIT: + fallback = "wait"; + break; + case CURSOR_BUSY: + fallback = "progress"; + break; + case CURSOR_DRAG: + fallback = "grabbing"; + break; + case CURSOR_CAN_DROP: + fallback = "hand1"; + break; + case CURSOR_FORBIDDEN: + fallback = "forbidden"; + break; + case CURSOR_VSIZE: + fallback = "ns-resize"; + break; + case CURSOR_HSIZE: + fallback = "ew-resize"; + break; + case CURSOR_BDIAGSIZE: + fallback = "fd_double_arrow"; + break; + case CURSOR_FDIAGSIZE: + fallback = "bd_double_arrow"; + break; + case CURSOR_MOVE: + img[i] = img[CURSOR_DRAG]; + break; + case CURSOR_VSPLIT: + fallback = "sb_v_double_arrow"; + break; + case CURSOR_HSPLIT: + fallback = "sb_h_double_arrow"; + break; + case CURSOR_HELP: + fallback = "help"; + break; + } + if (fallback != NULL) { + img[i] = XcursorLibraryLoadImage(fallback, cursor_theme, cursor_size); + } + } + if (img[i]) { + cursors[i] = XcursorImageLoadCursor(x11_display, img[i]); + } else { + print_verbose("Failed loading custom cursor: " + String(cursor_file[i])); + } + } + + { + // Creating an empty/transparent cursor + + // Create 1x1 bitmap + Pixmap cursormask = XCreatePixmap(x11_display, + RootWindow(x11_display, DefaultScreen(x11_display)), 1, 1, 1); + + // Fill with zero + XGCValues xgc; + xgc.function = GXclear; + GC gc = XCreateGC(x11_display, cursormask, GCFunction, &xgc); + XFillRectangle(x11_display, cursormask, gc, 0, 0, 1, 1); + + // Color value doesn't matter. Mask zero means no foreground or background will be drawn + XColor col = {}; + + Cursor cursor = XCreatePixmapCursor(x11_display, + cursormask, // source (using cursor mask as placeholder, since it'll all be ignored) + cursormask, // mask + &col, &col, 0, 0); + + XFreePixmap(x11_display, cursormask); + XFreeGC(x11_display, gc); + + if (cursor == None) { + ERR_PRINT("FAILED CREATING CURSOR"); + } + + null_cursor = cursor; + } + cursor_set_shape(CURSOR_BUSY); + + requested = None; + + visual_server->init(); + + window_has_focus = true; // Set focus to true at init + + /*if (p_desired.layered) { + set_window_per_pixel_transparency_enabled(true); + }*/ + + XEvent xevent; + while (XPending(x11_display) > 0) { + XNextEvent(x11_display, &xevent); + if (xevent.type == ConfigureNotify) { + _window_changed(&xevent); + } + } + + _update_real_mouse_position(windows[MAIN_WINDOW_ID]); + + r_error = OK; +} +DisplayServerX11::~DisplayServerX11() { +} + +#endif // X11 enabled diff --git a/platform/x11/display_server_x11.h b/platform/x11/display_server_x11.h new file mode 100644 index 0000000000..bc6f1bf2a1 --- /dev/null +++ b/platform/x11/display_server_x11.h @@ -0,0 +1,315 @@ +/*************************************************************************/ +/* display_server_x11.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DISPLAY_SERVER_X11_H +#define DISPLAY_SERVER_X11_H + +#ifdef X11_ENABLED + +#include "servers/display_server.h" + +#include "core/input/input.h" +#include "crash_handler_x11.h" +#include "drivers/alsa/audio_driver_alsa.h" +#include "drivers/alsamidi/midi_driver_alsamidi.h" +#include "drivers/pulseaudio/audio_driver_pulseaudio.h" +#include "drivers/unix/os_unix.h" +#include "joypad_linux.h" +#include "servers/audio_server.h" +#include "servers/visual/rasterizer.h" +#include "servers/visual_server.h" + +#if defined(OPENGL_ENABLED) +#include "context_gl_x11.h" +#endif + +#if defined(VULKAN_ENABLED) +#include "drivers/vulkan/rendering_device_vulkan.h" +#include "platform/x11/vulkan_context_x11.h" +#endif + +#include <X11/Xcursor/Xcursor.h> +#include <X11/Xlib.h> +#include <X11/extensions/XInput2.h> +#include <X11/extensions/Xrandr.h> +#include <X11/keysym.h> + +// Hints for X11 fullscreen +typedef struct { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long inputMode; + unsigned long status; +} Hints; + +typedef struct _xrr_monitor_info { + Atom name; + Bool primary; + Bool automatic; + int noutput; + int x; + int y; + int width; + int height; + int mwidth; + int mheight; + RROutput *outputs; +} xrr_monitor_info; + +#undef CursorShape + +class DisplayServerX11 : public DisplayServer { + //No need to register, it's platform-specific and nothing is added + //GDCLASS(DisplayServerX11, DisplayServer) + + Atom wm_delete; + Atom xdnd_enter; + Atom xdnd_position; + Atom xdnd_status; + Atom xdnd_action_copy; + Atom xdnd_drop; + Atom xdnd_finished; + Atom xdnd_selection; + Atom xdnd_aware; + Atom requested; + int xdnd_version; + +#if defined(OPENGL_ENABLED) + ContextGL_X11 *context_gles2; +#endif +#if defined(VULKAN_ENABLED) + VulkanContextX11 *context_vulkan; + RenderingDeviceVulkan *rendering_device_vulkan; +#endif + + //Rasterizer *rasterizer; + VisualServer *visual_server; + + struct WindowData { + Window x11_window; + ::XIC xic; +#if defined(VULKAN_ENABLED) + int vulkan_window; +#endif + Size2i min_size; + Size2i max_size; + Size2i size; + Size2i im_position; + bool im_active = false; + + //better to guess on the fly, given WM can change it + //WindowMode mode; + bool fullscreen = false; //OS can't exit from this mode + bool on_top = false; + bool borderless = false; + bool resize_disabled = false; + Vector2i last_position_before_fs; + }; + + Map<WindowID, WindowData> windows; + + WindowID window_id_counter = MAIN_WINDOW_ID; + WindowID _create_window(WindowMode p_mode, const Vector2i &p_resolution); + + String internal_clipboard; + Window xdnd_source_window; + ::Display *x11_display; + char *xmbstring; + int xmblen; + unsigned long last_timestamp; + ::Time last_keyrelease_time; + ::XIM xim; + ::XIMStyle xim_style; + static void _xim_destroy_callback(::XIM im, ::XPointer client_data, + ::XPointer call_data); + + Point2i last_mouse_pos; + bool last_mouse_pos_valid; + Point2i last_click_pos; + uint64_t last_click_ms; + int last_click_button_index; + uint32_t last_button_state; + + struct { + int opcode; + Vector<int> touch_devices; + Map<int, Vector2> absolute_devices; + Map<int, Vector3> pen_devices; + XIEventMask all_event_mask; + Map<int, Vector2> state; + double pressure; + Vector2 tilt; + Vector2 mouse_pos_to_filter; + Vector2 relative_motion; + Vector2 raw_pos; + Vector2 old_raw_pos; + ::Time last_relative_time; + } xi; + + bool _refresh_device_info(); + + unsigned int _get_mouse_button_state(unsigned int p_x11_button, int p_x11_type); + void _get_key_modifier_state(unsigned int p_x11_state, Ref<InputEventWithModifiers> state); + void _flush_mouse_motion(); + + MouseMode mouse_mode; + Point2i center; + + void _handle_key_event(WindowID p_window, XKeyEvent *p_event, bool p_echo = false); + + bool force_quit; + bool minimized; + bool window_has_focus; + bool do_mouse_warp; + + const char *cursor_theme; + int cursor_size; + XcursorImage *img[CURSOR_MAX]; + Cursor cursors[CURSOR_MAX]; + Cursor null_cursor; + CursorShape current_cursor; + Map<CursorShape, Vector<Variant>> cursors_cache; + + bool layered_window; + + String video_driver; + bool window_focused; + //void set_wm_border(bool p_enabled); + void set_wm_fullscreen(bool p_enabled); + void set_wm_above(bool p_enabled); + + typedef xrr_monitor_info *(*xrr_get_monitors_t)(Display *dpy, Window window, Bool get_active, int *nmonitors); + typedef void (*xrr_free_monitors_t)(xrr_monitor_info *monitors); + xrr_get_monitors_t xrr_get_monitors; + xrr_free_monitors_t xrr_free_monitors; + void *xrandr_handle; + Bool xrandr_ext_ok; + + struct Property { + unsigned char *data; + int format, nitems; + Atom type; + }; + static Property _read_property(Display *p_display, Window p_window, Atom p_property); + + void _update_real_mouse_position(const WindowData &wd); + void _set_wm_fullscreen(WindowID p_window, bool p_enabled); + void _set_wm_maximized(WindowID p_window, bool p_enabled); + +protected: + void _window_changed(XEvent *event); + +public: + virtual bool has_feature(Feature p_feature) const; + virtual String get_name() const; + + virtual void alert(const String &p_alert, const String &p_title = "ALERT!"); + + virtual void mouse_set_mode(MouseMode p_mode); + virtual MouseMode mouse_get_mode() const; + + virtual void mouse_warp_to_position(const Point2i &p_to); + virtual Point2i mouse_get_position() const; + virtual int mouse_get_button_state() const; + + virtual void clipboard_set(const String &p_text); + virtual String clipboard_get() const; + + virtual int get_screen_count() const; + virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + virtual Vector<int> get_window_list() const; + + virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i &p_rect = Rect2i()); + virtual void delete_sub_window(WindowID p_id); + + virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID); + + virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID); + + virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const; + virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID); + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const; + virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID); + virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID); + virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID); + + virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID); + + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const; + + virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID); + + virtual void cursor_set_shape(CursorShape p_shape); + virtual CursorShape cursor_get_shape() const; + virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + + virtual void process_events(); + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + virtual void set_native_icon(const String &p_filename); + virtual void set_icon(const Ref<Image> &p_icon); + + static DisplayServer *create_func(const String &p_video_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + + DisplayServerX11(const String &p_video_driver, WindowMode p_mode, uint32_t p_flags, const Vector2i &p_resolution, Error &r_error); + ~DisplayServerX11(); +}; + +#endif // X11 enabled + +#endif // DISPLAY_SERVER_X11_H diff --git a/platform/x11/joypad_linux.cpp b/platform/x11/joypad_linux.cpp index 1aacc6a250..7531b1160f 100644 --- a/platform/x11/joypad_linux.cpp +++ b/platform/x11/joypad_linux.cpp @@ -71,7 +71,7 @@ void JoypadLinux::Joypad::reset() { dpad = 0; fd = -1; - InputDefault::JoyAxis jx; + Input::JoyAxis jx; jx.min = -1; jx.value = 0.0f; for (int i = 0; i < MAX_ABS; i++) { @@ -80,7 +80,7 @@ void JoypadLinux::Joypad::reset() { } } -JoypadLinux::JoypadLinux(InputDefault *in) { +JoypadLinux::JoypadLinux(Input *in) { exit_udev = false; input = in; joy_thread = Thread::create(joy_thread_func, this); @@ -436,11 +436,11 @@ void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) { joy.ff_effect_timestamp = p_timestamp; } -InputDefault::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { +Input::JoyAxis JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { int min = p_abs->minimum; int max = p_abs->maximum; - InputDefault::JoyAxis jx; + Input::JoyAxis jx; if (min < 0) { jx.min = -1; @@ -492,11 +492,11 @@ void JoypadLinux::process_joypads() { case ABS_HAT0X: if (ev.value != 0) { if (ev.value < 0) - joy->dpad |= InputDefault::HAT_MASK_LEFT; + joy->dpad |= Input::HAT_MASK_LEFT; else - joy->dpad |= InputDefault::HAT_MASK_RIGHT; + joy->dpad |= Input::HAT_MASK_RIGHT; } else - joy->dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT); + joy->dpad &= ~(Input::HAT_MASK_LEFT | Input::HAT_MASK_RIGHT); input->joy_hat(i, joy->dpad); break; @@ -504,11 +504,11 @@ void JoypadLinux::process_joypads() { case ABS_HAT0Y: if (ev.value != 0) { if (ev.value < 0) - joy->dpad |= InputDefault::HAT_MASK_UP; + joy->dpad |= Input::HAT_MASK_UP; else - joy->dpad |= InputDefault::HAT_MASK_DOWN; + joy->dpad |= Input::HAT_MASK_DOWN; } else - joy->dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN); + joy->dpad &= ~(Input::HAT_MASK_UP | Input::HAT_MASK_DOWN); input->joy_hat(i, joy->dpad); break; @@ -517,7 +517,7 @@ void JoypadLinux::process_joypads() { if (ev.code >= MAX_ABS) return; if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) { - InputDefault::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value); + Input::JoyAxis value = axis_correct(joy->abs_info[ev.code], ev.value); joy->curr_axis[joy->abs_map[ev.code]] = value; } break; diff --git a/platform/x11/joypad_linux.h b/platform/x11/joypad_linux.h index d5719b6dbe..0d175193a5 100644 --- a/platform/x11/joypad_linux.h +++ b/platform/x11/joypad_linux.h @@ -33,15 +33,15 @@ #define JOYPAD_LINUX_H #ifdef JOYDEV_ENABLED +#include "core/input/input.h" #include "core/os/mutex.h" #include "core/os/thread.h" -#include "main/input_default.h" struct input_absinfo; class JoypadLinux { public: - JoypadLinux(InputDefault *in); + JoypadLinux(Input *in); ~JoypadLinux(); void process_joypads(); @@ -53,7 +53,7 @@ private: }; struct Joypad { - InputDefault::JoyAxis curr_axis[MAX_ABS]; + Input::JoyAxis curr_axis[MAX_ABS]; int key_map[MAX_KEY]; int abs_map[MAX_ABS]; int dpad; @@ -74,7 +74,7 @@ private: bool exit_udev; Mutex joy_mutex; Thread *joy_thread; - InputDefault *input; + Input *input; Joypad joypads[JOYPADS_MAX]; Vector<String> attached_devices; @@ -95,7 +95,7 @@ private: void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp); void joypad_vibration_stop(int p_id, uint64_t p_timestamp); - InputDefault::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const; + Input::JoyAxis axis_correct(const input_absinfo *p_abs, int p_value) const; }; #endif diff --git a/platform/x11/os_x11.cpp b/platform/x11/os_x11.cpp index 772913980b..8b92af9dda 100644 --- a/platform/x11/os_x11.cpp +++ b/platform/x11/os_x11.cpp @@ -651,7 +651,7 @@ Error OS_X11::initialize(const VideoMode &p_desired, int p_video_driver, int p_a AudioDriverManager::initialize(p_audio_driver); - input = memnew(InputDefault); + input = memnew(Input); window_has_focus = true; // Set focus to true at init #ifdef JOYDEV_ENABLED diff --git a/platform/x11/os_x11.h b/platform/x11/os_x11.h index 997a6cc053..4cdf5445ce 100644 --- a/platform/x11/os_x11.h +++ b/platform/x11/os_x11.h @@ -31,14 +31,13 @@ #ifndef OS_X11_H #define OS_X11_H -#include "core/os/input.h" +#include "core/input/input.h" #include "crash_handler_x11.h" #include "drivers/alsa/audio_driver_alsa.h" #include "drivers/alsamidi/midi_driver_alsamidi.h" #include "drivers/pulseaudio/audio_driver_pulseaudio.h" #include "drivers/unix/os_unix.h" #include "joypad_linux.h" -#include "main/input_default.h" #include "servers/audio_server.h" #include "servers/visual/rasterizer.h" #include "servers/visual_server.h" @@ -181,7 +180,7 @@ class OS_X11 : public OS_Unix { CursorShape current_cursor; Map<CursorShape, Vector<Variant>> cursors_cache; - InputDefault *input; + Input *input; #ifdef JOYDEV_ENABLED JoypadLinux *joypad; diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index ef7aa9ba01..0f93a61b09 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -29,9 +29,10 @@ /*************************************************************************/ #include "canvas_item.h" + +#include "core/input/input.h" #include "core/message_queue.h" #include "core/method_bind_ext.gen.inc" -#include "core/os/input.h" #include "scene/main/canvas_layer.h" #include "scene/main/viewport.h" #include "scene/resources/font.h" diff --git a/scene/2d/touch_screen_button.cpp b/scene/2d/touch_screen_button.cpp index 1cca45b422..20d73113dc 100644 --- a/scene/2d/touch_screen_button.cpp +++ b/scene/2d/touch_screen_button.cpp @@ -30,8 +30,8 @@ #include "touch_screen_button.h" -#include "core/input_map.h" -#include "core/os/input.h" +#include "core/input/input.h" +#include "core/input/input_map.h" #include "core/os/os.h" void TouchScreenButton::set_texture(const Ref<Texture2D> &p_texture) { diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp index d23e5ffa08..4600b9e73f 100644 --- a/scene/3d/arvr_nodes.cpp +++ b/scene/3d/arvr_nodes.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "arvr_nodes.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "servers/arvr/arvr_interface.h" #include "servers/arvr_server.h" diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index a8b5ac6909..29900def80 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -30,7 +30,7 @@ #include "color_picker.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index 4bc33b220e..bae82923b2 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -30,7 +30,7 @@ #include "graph_edit.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "scene/gui/box_container.h" diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index e75dadd5e0..9b24498740 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -30,7 +30,7 @@ #include "popup_menu.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/print_string.h" diff --git a/scene/gui/shortcut.h b/scene/gui/shortcut.h index 59d3245db5..b22c3441c5 100644 --- a/scene/gui/shortcut.h +++ b/scene/gui/shortcut.h @@ -31,7 +31,7 @@ #ifndef SHORTCUT_H #define SHORTCUT_H -#include "core/os/input_event.h" +#include "core/input/input_event.h" #include "core/resource.h" class ShortCut : public Resource { diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 576de98a4f..c5e8da9978 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -29,8 +29,8 @@ /*************************************************************************/ #include "spin_box.h" +#include "core/input/input.h" #include "core/math/expression.h" -#include "core/os/input.h" Size2 SpinBox::get_minimum_size() const { diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index cdc2430eb5..46f6d002cd 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -30,8 +30,8 @@ #include "text_edit.h" +#include "core/input/input.h" #include "core/message_queue.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/project_settings.h" diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index 12b3d56938..d9bfffffe9 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -30,8 +30,8 @@ #include "tree.h" +#include "core/input/input.h" #include "core/math/math_funcs.h" -#include "core/os/input.h" #include "core/os/keyboard.h" #include "core/os/os.h" #include "core/print_string.h" diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index f472de220b..b7b2f190b3 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -31,6 +31,7 @@ #include "scene_tree.h" #include "core/debugger/engine_debugger.h" +#include "core/input/input.h" #include "core/io/marshalls.h" #include "core/io/resource_loader.h" #include "core/message_queue.h" @@ -39,7 +40,6 @@ #include "core/os/os.h" #include "core/print_string.h" #include "core/project_settings.h" -#include "main/input_default.h" #include "node.h" #include "scene/debugger/scene_debugger.h" #include "scene/resources/dynamic_font.h" @@ -668,7 +668,7 @@ void SceneTree::_notification(int p_notification) { case NOTIFICATION_WM_FOCUS_IN: { - InputDefault *id = Object::cast_to<InputDefault>(Input::get_singleton()); + Input *id = Input::get_singleton(); if (id) { id->ensure_touch_mouse_raised(); } diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 8ef3bdd04e..adfcc8153b 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -32,7 +32,7 @@ #include "core/core_string_names.h" #include "core/debugger/engine_debugger.h" -#include "core/os/input.h" +#include "core/input/input.h" #include "core/os/os.h" #include "core/project_settings.h" #include "scene/2d/collision_object_2d.h" diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp index a64e36b6a2..69d7c7a2d9 100644 --- a/servers/arvr/arvr_positional_tracker.cpp +++ b/servers/arvr/arvr_positional_tracker.cpp @@ -29,7 +29,7 @@ /*************************************************************************/ #include "arvr_positional_tracker.h" -#include "core/os/input.h" +#include "core/input/input.h" void ARVRPositionalTracker::_bind_methods() { BIND_ENUM_CONSTANT(TRACKER_HAND_UNKNOWN); diff --git a/servers/display_server.cpp b/servers/display_server.cpp new file mode 100644 index 0000000000..c3ecf302c3 --- /dev/null +++ b/servers/display_server.cpp @@ -0,0 +1,374 @@ +/*************************************************************************/ +/* display_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "display_server.h" + +DisplayServer *DisplayServer::singleton = nullptr; + +void DisplayServer::global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta) { + WARN_PRINT("Global menus not supported by this display server."); +} +void DisplayServer::global_menu_add_separator(const String &p_menu) { + WARN_PRINT("Global menus not supported by this display server."); +} +void DisplayServer::global_menu_remove_item(const String &p_menu, int p_idx) { + WARN_PRINT("Global menus not supported by this display server."); +} +void DisplayServer::global_menu_clear(const String &p_menu) { + WARN_PRINT("Global menus not supported by this display server."); +} + +void DisplayServer::mouse_set_mode(MouseMode p_mode) { + WARN_PRINT("Mouse is not supported by this display server."); +} +DisplayServer::MouseMode DisplayServer::mouse_get_mode() const { + return MOUSE_MODE_VISIBLE; +} + +void DisplayServer::mouse_warp_to_position(const Point2i &p_to) { + WARN_PRINT("Mouse warping is not supported by this display server."); +} +Point2i DisplayServer::mouse_get_position() const { + ERR_FAIL_V_MSG(Point2i(), "Mouse is not supported by this display server."); +} +int DisplayServer::mouse_get_button_state() const { + ERR_FAIL_V_MSG(0, "Mouse is not supported by this display server."); +} + +void DisplayServer::clipboard_set(const String &p_text) { + WARN_PRINT("Clipboard is not supported by this display server."); +} +String DisplayServer::clipboard_get() const { + ERR_FAIL_V_MSG(String(), "Clipboard is not supported by this display server."); +} + +void DisplayServer::screen_set_orientation(ScreenOrientation p_orientation, int p_screen) { + WARN_PRINT("Orientation not supported by this display server."); +} +DisplayServer::ScreenOrientation DisplayServer::screen_get_orientation(int p_screen) const { + return SCREEN_LANDSCAPE; +} + +DisplayServer::WindowID DisplayServer::create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i) { + ERR_FAIL_V_MSG(INVALID_WINDOW_ID, "Sub-windows not supported by this display server."); +} +void DisplayServer::delete_sub_window(WindowID p_id) { + ERR_FAIL_MSG("Sub-windows not supported by this display server."); +} + +void DisplayServer::window_set_ime_active(const bool p_active, WindowID p_window) { + WARN_PRINT("IME not supported by this display server."); +} +void DisplayServer::window_set_ime_position(const Point2i &p_pos, WindowID p_window) { + WARN_PRINT("IME not supported by this display server."); +} + +Point2i DisplayServer::ime_get_selection() const { + ERR_FAIL_V_MSG(Point2i(), "IME or NOTIFICATION_WM_IME_UPDATE not supported by this display server."); +} +String DisplayServer::ime_get_text() const { + ERR_FAIL_V_MSG(String(), "IME or NOTIFICATION_WM_IME_UPDATEnot supported by this display server."); +} + +void DisplayServer::console_set_visible(bool p_enabled) { + WARN_PRINT("Console window not supported by this display server."); +} +bool DisplayServer::is_console_visible() const { + return false; +} + +void DisplayServer::virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect) { + WARN_PRINT("Virtual keyboard not supported by this display server."); +} +void DisplayServer::virtual_keyboard_hide() { + WARN_PRINT("Virtual keyboard not supported by this display server."); +} + +// returns height of the currently shown keyboard (0 if keyboard is hidden) +int DisplayServer::virtual_keyboard_get_height() const { + ERR_FAIL_V_MSG(0, "Virtual keyboad not supported by this display server."); +} + +void DisplayServer::cursor_set_shape(CursorShape p_shape) { + WARN_PRINT("Cursor shape not supported by this display server."); +} +DisplayServer::CursorShape DisplayServer::cursor_get_shape() const { + return CURSOR_ARROW; +} +void DisplayServer::cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot) { + WARN_PRINT("Custom cursor shape not supported by this display server."); +} + +bool DisplayServer::get_swap_ok_cancel() { + return false; +} + +void DisplayServer::enable_for_stealing_focus(OS::ProcessID pid) { +} + +//plays video natively, in fullscreen, only implemented in mobile for now, likely not possible to implement on linux also. +Error DisplayServer::native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen) { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Native video not supported by this display server."); +} +bool DisplayServer::native_video_is_playing() const { + return false; +} +void DisplayServer::native_video_pause() { + WARN_PRINT("Native video not supported by this display server."); +} +void DisplayServer::native_video_unpause() { + WARN_PRINT("Native video not supported by this display server."); +} +void DisplayServer::native_video_stop() { + WARN_PRINT("Native video not supported by this display server."); +} + +Error DisplayServer::dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback) { + WARN_PRINT("Native dialogs not supported by this display server."); + return OK; +} +Error DisplayServer::dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback) { + WARN_PRINT("Native dialogs not supported by this display server."); + return OK; +} + +DisplayServer::LatinKeyboardVariant DisplayServer::get_latin_keyboard_variant() const { + return LATIN_KEYBOARD_QWERTY; +} + +void DisplayServer::force_process_and_drop_events() { +} + +void DisplayServer::release_rendering_thread() { + WARN_PRINT("Rendering thread not supported by this display server."); +} +void DisplayServer::make_rendering_thread() { + WARN_PRINT("Rendering thread not supported by this display server."); +} +void DisplayServer::swap_buffers() { + WARN_PRINT("Swap buffers not supported by this display server."); +} + +void DisplayServer::set_native_icon(const String &p_filename) { + WARN_PRINT("Native icon not supported by this display server."); +} +void DisplayServer::set_icon(const Ref<Image> &p_icon) { + WARN_PRINT("Iconnot supported by this display server."); +} + +void DisplayServer::_bind_methods() { + + ClassDB::bind_method(D_METHOD("has_feature", "feature"), &DisplayServer::has_feature); + ClassDB::bind_method(D_METHOD("get_name"), &DisplayServer::get_name); + + ClassDB::bind_method(D_METHOD("global_menu_add_item", "menu", "label", "id", "meta"), &DisplayServer::global_menu_add_item); + ClassDB::bind_method(D_METHOD("global_menu_add_separator", "menu"), &DisplayServer::global_menu_add_separator); + ClassDB::bind_method(D_METHOD("global_menu_remove_item", "menu", "idx"), &DisplayServer::global_menu_remove_item); + ClassDB::bind_method(D_METHOD("global_menu_clear", "menu"), &DisplayServer::global_menu_clear); + + ClassDB::bind_method(D_METHOD("alert", "text", "title"), &DisplayServer::alert, DEFVAL("Alert!")); + + ClassDB::bind_method(D_METHOD("mouse_set_mode", "mouse_mode"), &DisplayServer::mouse_set_mode); + ClassDB::bind_method(D_METHOD("mouse_get_mode"), &DisplayServer::mouse_get_mode); + + ClassDB::bind_method(D_METHOD("mouse_warp_to_position", "position"), &DisplayServer::mouse_warp_to_position); + ClassDB::bind_method(D_METHOD("mouse_get_position"), &DisplayServer::mouse_get_position); + ClassDB::bind_method(D_METHOD("mouse_get_button_state"), &DisplayServer::mouse_get_button_state); + + ClassDB::bind_method(D_METHOD("clipboard_set", "clipboard"), &DisplayServer::clipboard_set); + ClassDB::bind_method(D_METHOD("clipboard_get"), &DisplayServer::clipboard_get); + + ClassDB::bind_method(D_METHOD("get_screen_count"), &DisplayServer::get_screen_count); + ClassDB::bind_method(D_METHOD("screen_get_position", "screen"), &DisplayServer::screen_get_position, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + ClassDB::bind_method(D_METHOD("screen_get_size", "screen"), &DisplayServer::screen_get_size, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + ClassDB::bind_method(D_METHOD("screen_get_dpi", "screen"), &DisplayServer::screen_get_dpi, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + ClassDB::bind_method(D_METHOD("screen_is_touchscreen", "screen"), &DisplayServer::screen_is_touchscreen, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + + ClassDB::bind_method(D_METHOD("screen_set_orientation", "orientation", "screen"), &DisplayServer::screen_set_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + ClassDB::bind_method(D_METHOD("screen_get_orientation", "screen"), &DisplayServer::screen_get_orientation, DEFVAL(SCREEN_OF_MAIN_WINDOW)); + + ClassDB::bind_method(D_METHOD("get_window_list"), &DisplayServer::get_window_list); + + ClassDB::bind_method(D_METHOD("create_sub_window", "mode", "rect"), &DisplayServer::create_sub_window, DEFVAL(Rect2i())); + ClassDB::bind_method(D_METHOD("delete_sub_window", "window_id"), &DisplayServer::delete_sub_window); + + ClassDB::bind_method(D_METHOD("window_set_title", "title", "window_id"), &DisplayServer::window_set_title, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_current_screen", "window_id"), &DisplayServer::window_get_current_screen, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_current_screen", "screen", "window_id"), &DisplayServer::window_set_current_screen, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_position", "window_id"), &DisplayServer::window_get_position, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_position", "position", "window_id"), &DisplayServer::window_set_position, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_size", "window_id"), &DisplayServer::window_get_size, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_size", "size", "window_id"), &DisplayServer::window_set_size, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_max_size", "window_id"), &DisplayServer::window_get_max_size, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_max_size", "max_size", "window_id"), &DisplayServer::window_set_max_size, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_min_size", "window_id"), &DisplayServer::window_get_min_size, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_min_size", "min_size", "window_id"), &DisplayServer::window_set_min_size, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_real_size", "window_id"), &DisplayServer::window_get_real_size, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_get_mode", "window_id"), &DisplayServer::window_get_mode, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_mode", "mode", "window_id"), &DisplayServer::window_set_mode, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_set_flag", "flag", "enabled", "window_id"), &DisplayServer::window_set_flag, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_get_flag", "flag", "window_id"), &DisplayServer::window_get_flag, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_request_attention", "window_id"), &DisplayServer::window_request_attention, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_move_to_foreground", "window_id"), &DisplayServer::window_move_to_foreground, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_can_draw", "window_id"), &DisplayServer::window_can_draw, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("window_set_ime_active", "active", "window_id"), &DisplayServer::window_set_ime_active, DEFVAL(MAIN_WINDOW_ID)); + ClassDB::bind_method(D_METHOD("window_set_ime_position", "position", "window_id"), &DisplayServer::window_set_ime_position, DEFVAL(MAIN_WINDOW_ID)); + + ClassDB::bind_method(D_METHOD("ime_get_selection"), &DisplayServer::ime_get_selection); + ClassDB::bind_method(D_METHOD("ime_get_text"), &DisplayServer::ime_get_text); + + ClassDB::bind_method(D_METHOD("console_set_visible", "console_visible"), &DisplayServer::console_set_visible); + ClassDB::bind_method(D_METHOD("is_console_visible"), &DisplayServer::is_console_visible); + + ClassDB::bind_method(D_METHOD("virtual_keyboard_show", "existing_text", "position"), &DisplayServer::virtual_keyboard_show, DEFVAL(Rect2i())); + ClassDB::bind_method(D_METHOD("virtual_keyboard_hide"), &DisplayServer::virtual_keyboard_hide); + + ClassDB::bind_method(D_METHOD("virtual_keyboard_get_height"), &DisplayServer::virtual_keyboard_get_height); + + ClassDB::bind_method(D_METHOD("cursor_set_shape", "shape"), &DisplayServer::cursor_set_shape); + ClassDB::bind_method(D_METHOD("cursor_get_shape"), &DisplayServer::cursor_get_shape); + ClassDB::bind_method(D_METHOD("cursor_set_custom_image", "cursor", "shape", "hotspot"), &DisplayServer::cursor_set_custom_image); + + ClassDB::bind_method(D_METHOD("get_swap_ok_cancel"), &DisplayServer::get_swap_ok_cancel); + + ClassDB::bind_method(D_METHOD("enable_for_stealing_focus", "process_id"), &DisplayServer::enable_for_stealing_focus); + + ClassDB::bind_method(D_METHOD("native_video_play", "path", "volume", "audio_track", "subtitle_track"), &DisplayServer::native_video_play); + ClassDB::bind_method(D_METHOD("native_video_is_playing"), &DisplayServer::native_video_is_playing); + ClassDB::bind_method(D_METHOD("native_video_stop"), &DisplayServer::native_video_stop); + ClassDB::bind_method(D_METHOD("native_video_pause"), &DisplayServer::native_video_pause); + ClassDB::bind_method(D_METHOD("native_video_unpause"), &DisplayServer::native_video_unpause); + + ClassDB::bind_method(D_METHOD("dialog_show", "title", "description", "buttons", "callback"), &DisplayServer::dialog_show); + ClassDB::bind_method(D_METHOD("dialog_input_text", "title", "description", "existing_text", "callback"), &DisplayServer::dialog_show); + + ClassDB::bind_method(D_METHOD("get_latin_keyboard_variant"), &DisplayServer::get_latin_keyboard_variant); + + ClassDB::bind_method(D_METHOD("process_events"), &DisplayServer::process_events); + ClassDB::bind_method(D_METHOD("force_process_and_drop_events"), &DisplayServer::force_process_and_drop_events); + + ClassDB::bind_method(D_METHOD("set_native_icon", "filename"), &DisplayServer::set_native_icon); + ClassDB::bind_method(D_METHOD("set_icon", "image"), &DisplayServer::set_icon); + + BIND_ENUM_CONSTANT(FEATURE_GLOBAL_MENU); + BIND_ENUM_CONSTANT(FEATURE_SUBWINDOWS); + BIND_ENUM_CONSTANT(FEATURE_TOUCHSCREEN); + BIND_ENUM_CONSTANT(FEATURE_MOUSE); + BIND_ENUM_CONSTANT(FEATURE_MOUSE_WARP); + BIND_ENUM_CONSTANT(FEATURE_CLIPBOARD); + BIND_ENUM_CONSTANT(FEATURE_VIRTUAL_KEYBOARD); + BIND_ENUM_CONSTANT(FEATURE_CURSOR_SHAPE); + BIND_ENUM_CONSTANT(FEATURE_CUSTOM_CURSOR_SHAPE); + BIND_ENUM_CONSTANT(FEATURE_NATIVE_VIDEO); + BIND_ENUM_CONSTANT(FEATURE_NATIVE_DIALOG); + BIND_ENUM_CONSTANT(FEATURE_CONSOLE_WINDOW); + BIND_ENUM_CONSTANT(FEATURE_IME); + BIND_ENUM_CONSTANT(FEATURE_WINDOW_TRANSPARENCY); + BIND_ENUM_CONSTANT(FEATURE_HIDPI); + BIND_ENUM_CONSTANT(FEATURE_ICON); + BIND_ENUM_CONSTANT(FEATURE_NATIVE_ICON); + BIND_ENUM_CONSTANT(FEATURE_ORIENTATION); + BIND_ENUM_CONSTANT(FEATURE_SWAP_BUFFERS); + + BIND_ENUM_CONSTANT(MOUSE_MODE_VISIBLE); + BIND_ENUM_CONSTANT(MOUSE_MODE_HIDDEN); + BIND_ENUM_CONSTANT(MOUSE_MODE_CAPTURED); + BIND_ENUM_CONSTANT(MOUSE_MODE_CONFINED); + + BIND_CONSTANT(SCREEN_OF_MAIN_WINDOW); + BIND_CONSTANT(MAIN_WINDOW_ID); + BIND_CONSTANT(INVALID_WINDOW_ID); + + BIND_ENUM_CONSTANT(SCREEN_LANDSCAPE); + BIND_ENUM_CONSTANT(SCREEN_PORTRAIT); + BIND_ENUM_CONSTANT(SCREEN_REVERSE_LANDSCAPE); + BIND_ENUM_CONSTANT(SCREEN_REVERSE_PORTRAIT); + BIND_ENUM_CONSTANT(SCREEN_SENSOR_LANDSCAPE); + BIND_ENUM_CONSTANT(SCREEN_SENSOR_PORTRAIT); + BIND_ENUM_CONSTANT(SCREEN_SENSOR); + + BIND_ENUM_CONSTANT(CURSOR_ARROW); + BIND_ENUM_CONSTANT(CURSOR_IBEAM); + BIND_ENUM_CONSTANT(CURSOR_POINTING_HAND); + BIND_ENUM_CONSTANT(CURSOR_CROSS); + BIND_ENUM_CONSTANT(CURSOR_WAIT); + BIND_ENUM_CONSTANT(CURSOR_BUSY); + BIND_ENUM_CONSTANT(CURSOR_DRAG); + BIND_ENUM_CONSTANT(CURSOR_CAN_DROP); + BIND_ENUM_CONSTANT(CURSOR_FORBIDDEN); + BIND_ENUM_CONSTANT(CURSOR_VSIZE); + BIND_ENUM_CONSTANT(CURSOR_HSIZE); + BIND_ENUM_CONSTANT(CURSOR_BDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_FDIAGSIZE); + BIND_ENUM_CONSTANT(CURSOR_MOVE); + BIND_ENUM_CONSTANT(CURSOR_VSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HSPLIT); + BIND_ENUM_CONSTANT(CURSOR_HELP); + BIND_ENUM_CONSTANT(CURSOR_MAX); + + BIND_ENUM_CONSTANT(WINDOW_MODE_WINDOWED); + BIND_ENUM_CONSTANT(WINDOW_MODE_MINIMIZED); + BIND_ENUM_CONSTANT(WINDOW_MODE_MAXIMIZED); + BIND_ENUM_CONSTANT(WINDOW_MODE_FULLSCREEN); + + BIND_ENUM_CONSTANT(WINDOW_FLAG_RESIZE_DISABLED); + BIND_ENUM_CONSTANT(WINDOW_FLAG_BORDERLESS); + BIND_ENUM_CONSTANT(WINDOW_FLAG_ALWAYS_ON_TOP); + BIND_ENUM_CONSTANT(WINDOW_FLAG_TRANSPARENT); + BIND_ENUM_CONSTANT(WINDOW_FLAG_MAX); + BIND_ENUM_CONSTANT(WINDOW_FLAG_RESIZE_DISABLED_BIT); + BIND_ENUM_CONSTANT(WINDOW_FLAG_BORDERLESS_BIT); + BIND_ENUM_CONSTANT(WINDOW_FLAG_ALWAYS_ON_TOP_BIT); + BIND_ENUM_CONSTANT(WINDOW_FLAG_TRANSPARENT_BIT); + + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_QWERTY); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_QWERTZ); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_AZERTY); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_QZERTY); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_DVORAK); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_NEO); + BIND_ENUM_CONSTANT(LATIN_KEYBOARD_COLEMAK); +} +DisplayServer::DisplayServer() { + singleton = this; +} +DisplayServer::~DisplayServer() { +} diff --git a/servers/display_server.h b/servers/display_server.h new file mode 100644 index 0000000000..c05a7fadbd --- /dev/null +++ b/servers/display_server.h @@ -0,0 +1,273 @@ +/*************************************************************************/ +/* display_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef DISPLAY_SERVER_H +#define DISPLAY_SERVER_H + +#include "core/os/os.h" +#include "core/resource.h" + +class DisplayServer : public Object { + GDCLASS(DisplayServer, Object) + + static DisplayServer *singleton; + +protected: + static void _bind_methods(); + +public: + enum Feature { + FEATURE_GLOBAL_MENU, + FEATURE_SUBWINDOWS, + FEATURE_TOUCHSCREEN, + FEATURE_MOUSE, + FEATURE_MOUSE_WARP, + FEATURE_CLIPBOARD, + FEATURE_VIRTUAL_KEYBOARD, + FEATURE_CURSOR_SHAPE, + FEATURE_CUSTOM_CURSOR_SHAPE, + FEATURE_NATIVE_VIDEO, + FEATURE_NATIVE_DIALOG, + FEATURE_CONSOLE_WINDOW, + FEATURE_IME, + FEATURE_WINDOW_TRANSPARENCY, + FEATURE_HIDPI, + FEATURE_ICON, + FEATURE_NATIVE_ICON, + FEATURE_ORIENTATION, + FEATURE_SWAP_BUFFERS + + }; + + virtual bool has_feature(Feature p_feature) const = 0; + virtual String get_name() const = 0; + + virtual void global_menu_add_item(const String &p_menu, const String &p_label, const Variant &p_signal, const Variant &p_meta); + virtual void global_menu_add_separator(const String &p_menu); + virtual void global_menu_remove_item(const String &p_menu, int p_idx); + virtual void global_menu_clear(const String &p_menu); + + virtual void alert(const String &p_alert, const String &p_title = "ALERT!") = 0; + + enum MouseMode { + MOUSE_MODE_VISIBLE, + MOUSE_MODE_HIDDEN, + MOUSE_MODE_CAPTURED, + MOUSE_MODE_CONFINED + }; + + virtual void mouse_set_mode(MouseMode p_mode); + virtual MouseMode mouse_get_mode() const; + + virtual void mouse_warp_to_position(const Point2i &p_to); + virtual Point2i mouse_get_position() const; + virtual int mouse_get_button_state() const; + + virtual void clipboard_set(const String &p_text); + virtual String clipboard_get() const; + + enum { + SCREEN_OF_MAIN_WINDOW = -1 + }; + + virtual int get_screen_count() const = 0; + virtual Point2i screen_get_position(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; + virtual Size2i screen_get_size(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; + virtual int screen_get_dpi(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; + virtual bool screen_is_touchscreen(int p_screen = SCREEN_OF_MAIN_WINDOW) const = 0; + + enum ScreenOrientation { + + SCREEN_LANDSCAPE, + SCREEN_PORTRAIT, + SCREEN_REVERSE_LANDSCAPE, + SCREEN_REVERSE_PORTRAIT, + SCREEN_SENSOR_LANDSCAPE, + SCREEN_SENSOR_PORTRAIT, + SCREEN_SENSOR, + }; + + virtual void screen_set_orientation(ScreenOrientation p_orientation, int p_screen = SCREEN_OF_MAIN_WINDOW); + ScreenOrientation screen_get_orientation(int p_screen = SCREEN_OF_MAIN_WINDOW) const; + + enum { + MAIN_WINDOW_ID = 0, + INVALID_WINDOW_ID = -1 + }; + + typedef int WindowID; + + virtual Vector<int> get_window_list() const = 0; + + enum WindowMode { + WINDOW_MODE_WINDOWED, + WINDOW_MODE_MINIMIZED, + WINDOW_MODE_MAXIMIZED, + WINDOW_MODE_FULLSCREEN + }; + + enum WindowFlags { + WINDOW_FLAG_RESIZE_DISABLED, + WINDOW_FLAG_BORDERLESS, + WINDOW_FLAG_ALWAYS_ON_TOP, + WINDOW_FLAG_TRANSPARENT, + WINDOW_FLAG_MAX, + WINDOW_FLAG_RESIZE_DISABLED_BIT = (1 << WINDOW_FLAG_RESIZE_DISABLED), + WINDOW_FLAG_BORDERLESS_BIT = (1 << WINDOW_FLAG_BORDERLESS), + WINDOW_FLAG_ALWAYS_ON_TOP_BIT = (1 << WINDOW_FLAG_ALWAYS_ON_TOP), + WINDOW_FLAG_TRANSPARENT_BIT = (1 << WINDOW_FLAG_TRANSPARENT) + }; + + virtual WindowID create_sub_window(WindowMode p_mode, uint32_t p_flags, const Rect2i = Rect2i()); + virtual void delete_sub_window(WindowID p_id); + + virtual void window_set_title(const String &p_title, WindowID p_window = MAIN_WINDOW_ID) = 0; + + virtual int window_get_current_screen(WindowID p_window = MAIN_WINDOW_ID) const = 0; + virtual void window_set_current_screen(int p_screen, WindowID p_window = MAIN_WINDOW_ID) = 0; + + virtual Point2i window_get_position(WindowID p_window = MAIN_WINDOW_ID) const = 0; + virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) = 0; + + virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual void window_set_min_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual Size2i window_get_min_size(WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual void window_set_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual Size2i window_get_size(WindowID p_window = MAIN_WINDOW_ID) const = 0; + virtual Size2i window_get_real_size(WindowID p_window = MAIN_WINDOW_ID) const = 0; // FIXME: Find clearer name for this. + + virtual void window_set_mode(WindowMode p_mode, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual WindowMode window_get_mode(WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual bool window_is_maximize_allowed(WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual void window_set_flag(WindowFlags p_flag, bool p_enabled, WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual bool window_get_flag(WindowFlags p_flag, WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual void window_request_attention(WindowID p_window = MAIN_WINDOW_ID) = 0; + virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) = 0; + + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const = 0; + + virtual void window_set_ime_active(const bool p_active, WindowID p_window = MAIN_WINDOW_ID); + virtual void window_set_ime_position(const Point2i &p_pos, WindowID p_window = MAIN_WINDOW_ID); + + virtual Point2i ime_get_selection() const; + virtual String ime_get_text() const; + + virtual void console_set_visible(bool p_enabled); + virtual bool is_console_visible() const; + + virtual void virtual_keyboard_show(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2()); + virtual void virtual_keyboard_hide(); + + // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) + virtual int virtual_keyboard_get_height() const; + + enum CursorShape { + CURSOR_ARROW, + CURSOR_IBEAM, + CURSOR_POINTING_HAND, + CURSOR_CROSS, + CURSOR_WAIT, + CURSOR_BUSY, + CURSOR_DRAG, + CURSOR_CAN_DROP, + CURSOR_FORBIDDEN, + CURSOR_VSIZE, + CURSOR_HSIZE, + CURSOR_BDIAGSIZE, + CURSOR_FDIAGSIZE, + CURSOR_MOVE, + CURSOR_VSPLIT, + CURSOR_HSPLIT, + CURSOR_HELP, + CURSOR_MAX + }; + virtual void cursor_set_shape(CursorShape p_shape); + virtual CursorShape cursor_get_shape() const; + virtual void cursor_set_custom_image(const RES &p_cursor, CursorShape p_shape, const Vector2 &p_hotspot); + + virtual bool get_swap_ok_cancel(); + + virtual void enable_for_stealing_focus(OS::ProcessID pid); + + //plays video natively, in fullscreen, only implemented in mobile for now, likely not possible to implement on linux also. + virtual Error native_video_play(String p_path, float p_volume, String p_audio_track, String p_subtitle_track, int p_screen = SCREEN_OF_MAIN_WINDOW); + virtual bool native_video_is_playing() const; + virtual void native_video_pause(); + virtual void native_video_unpause(); + virtual void native_video_stop(); + + virtual Error dialog_show(String p_title, String p_description, Vector<String> p_buttons, const Callable &p_callback); + virtual Error dialog_input_text(String p_title, String p_description, String p_partial, const Callable &p_callback); + + enum LatinKeyboardVariant { + LATIN_KEYBOARD_QWERTY, + LATIN_KEYBOARD_QWERTZ, + LATIN_KEYBOARD_AZERTY, + LATIN_KEYBOARD_QZERTY, + LATIN_KEYBOARD_DVORAK, + LATIN_KEYBOARD_NEO, + LATIN_KEYBOARD_COLEMAK, + }; + + virtual LatinKeyboardVariant get_latin_keyboard_variant() const; + + virtual void process_events() = 0; + + virtual void force_process_and_drop_events(); + + virtual void release_rendering_thread(); + virtual void make_rendering_thread(); + virtual void swap_buffers(); + + virtual void set_native_icon(const String &p_filename); + virtual void set_icon(const Ref<Image> &p_icon); + + typedef Vector<String> *(*GetSupportedVideoDriversFunction)(); + typedef DisplayServer *(*CreateFunction)(const String &, WindowMode, uint32_t, const Size2i &, Error &r_error); //video driver, window mode, resolution + + DisplayServer(); + ~DisplayServer(); +}; + +VARIANT_ENUM_CAST(DisplayServer::Feature) +VARIANT_ENUM_CAST(DisplayServer::MouseMode) +VARIANT_ENUM_CAST(DisplayServer::ScreenOrientation) +VARIANT_ENUM_CAST(DisplayServer::WindowMode) +VARIANT_ENUM_CAST(DisplayServer::WindowFlags) +VARIANT_ENUM_CAST(DisplayServer::CursorShape) +VARIANT_ENUM_CAST(DisplayServer::LatinKeyboardVariant) + +#endif // DISPLAY_SERVER_H diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index fd65f57380..574b373c30 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "register_server_types.h" + #include "core/engine.h" #include "core/project_settings.h" @@ -56,6 +57,7 @@ #include "audio_server.h" #include "camera/camera_feed.h" #include "camera_server.h" +#include "display_server.h" #include "navigation_2d_server.h" #include "navigation_server.h" #include "physics/physics_server_sw.h" @@ -95,6 +97,7 @@ void register_server_types() { OS::get_singleton()->set_has_server_feature_callback(has_server_feature_callback); + ClassDB::register_virtual_class<DisplayServer>(); ClassDB::register_virtual_class<VisualServer>(); ClassDB::register_class<AudioServer>(); ClassDB::register_virtual_class<PhysicsServer>(); |