Merge pull request #91610 from bruvzg/web_ime_3
[Web, 3.x] Add IME input support.
This commit is contained in:
commit
ece8b38dd1
4 changed files with 314 additions and 15 deletions
|
@ -60,6 +60,12 @@ extern void godot_js_input_touch_cb(void (*p_callback)(int p_type, int p_count),
|
||||||
extern void godot_js_input_key_cb(void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]);
|
extern void godot_js_input_key_cb(void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]);
|
||||||
extern void godot_js_input_vibrate_handheld(int p_duration_ms);
|
extern void godot_js_input_vibrate_handheld(int p_duration_ms);
|
||||||
|
|
||||||
|
// IME
|
||||||
|
extern void godot_js_set_ime_active(int p_active);
|
||||||
|
extern void godot_js_set_ime_position(int p_x, int p_y);
|
||||||
|
extern void godot_js_set_ime_cb(void (*p_input)(int p_type, const char *p_text), void (*p_callback)(int p_type, int p_repeat, int p_modifiers), char r_code[32], char r_key[32]);
|
||||||
|
extern int godot_js_is_ime_focused();
|
||||||
|
|
||||||
// Input gamepad
|
// Input gamepad
|
||||||
extern void godot_js_input_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
|
extern void godot_js_input_gamepad_cb(void (*p_on_change)(int p_index, int p_connected, const char *p_id, const char *p_guid));
|
||||||
extern int godot_js_input_gamepad_sample();
|
extern int godot_js_input_gamepad_sample();
|
||||||
|
|
|
@ -28,6 +28,119 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IME API helper.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const GodotIME = {
|
||||||
|
$GodotIME__deps: ['$GodotRuntime', '$GodotEventListeners'],
|
||||||
|
$GodotIME__postset: 'GodotOS.atexit(function(resolve, reject) { GodotIME.clear(); resolve(); });',
|
||||||
|
$GodotIME: {
|
||||||
|
ime: null,
|
||||||
|
active: false,
|
||||||
|
|
||||||
|
getModifiers: function (evt) {
|
||||||
|
return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
|
||||||
|
},
|
||||||
|
|
||||||
|
ime_active: function (active) {
|
||||||
|
function focus_timer() {
|
||||||
|
GodotIME.active = true;
|
||||||
|
GodotIME.ime.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GodotIME.ime) {
|
||||||
|
if (active) {
|
||||||
|
GodotIME.ime.style.display = 'block';
|
||||||
|
setInterval(focus_timer, 100);
|
||||||
|
} else {
|
||||||
|
GodotIME.ime.style.display = 'none';
|
||||||
|
GodotConfig.canvas.focus();
|
||||||
|
GodotIME.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ime_position: function (x, y) {
|
||||||
|
if (GodotIME.ime) {
|
||||||
|
const canvas = GodotConfig.canvas;
|
||||||
|
const rect = canvas.getBoundingClientRect();
|
||||||
|
const rw = canvas.width / rect.width;
|
||||||
|
const rh = canvas.height / rect.height;
|
||||||
|
const clx = (x / rw) + rect.x;
|
||||||
|
const cly = (y / rh) + rect.y;
|
||||||
|
|
||||||
|
GodotIME.ime.style.left = `${clx}px`;
|
||||||
|
GodotIME.ime.style.top = `${cly}px`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function (ime_cb, key_cb, code, key) {
|
||||||
|
function key_event_cb(pressed, evt) {
|
||||||
|
const modifiers = GodotIME.getModifiers(evt);
|
||||||
|
GodotRuntime.stringToHeap(evt.code, code, 32);
|
||||||
|
GodotRuntime.stringToHeap(evt.key, key, 32);
|
||||||
|
key_cb(pressed, evt.repeat, modifiers);
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
|
function ime_event_cb(event) {
|
||||||
|
if (GodotIME.ime) {
|
||||||
|
if (event.type === 'compositionstart') {
|
||||||
|
ime_cb(0, null);
|
||||||
|
GodotIME.ime.innerHTML = '';
|
||||||
|
} else if (event.type === 'compositionupdate') {
|
||||||
|
const ptr = GodotRuntime.allocString(event.data);
|
||||||
|
ime_cb(1, ptr);
|
||||||
|
GodotRuntime.free(ptr);
|
||||||
|
} else if (event.type === 'compositionend') {
|
||||||
|
const ptr = GodotRuntime.allocString(event.data);
|
||||||
|
ime_cb(2, ptr);
|
||||||
|
GodotRuntime.free(ptr);
|
||||||
|
GodotIME.ime.innerHTML = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const ime = document.createElement('div');
|
||||||
|
ime.className = 'ime';
|
||||||
|
ime.style.background = 'none';
|
||||||
|
ime.style.opacity = 0.0;
|
||||||
|
ime.style.position = 'fixed';
|
||||||
|
ime.style.textAlign = 'left';
|
||||||
|
ime.style.fontSize = '1px';
|
||||||
|
ime.style.left = '0px';
|
||||||
|
ime.style.top = '0px';
|
||||||
|
ime.style.width = '100%';
|
||||||
|
ime.style.height = '40px';
|
||||||
|
ime.style.display = 'none';
|
||||||
|
ime.contentEditable = 'true';
|
||||||
|
|
||||||
|
GodotEventListeners.add(ime, 'compositionstart', ime_event_cb, false);
|
||||||
|
GodotEventListeners.add(ime, 'compositionupdate', ime_event_cb, false);
|
||||||
|
GodotEventListeners.add(ime, 'compositionend', ime_event_cb, false);
|
||||||
|
GodotEventListeners.add(ime, 'keydown', key_event_cb.bind(null, 1), false);
|
||||||
|
GodotEventListeners.add(ime, 'keyup', key_event_cb.bind(null, 0), false);
|
||||||
|
|
||||||
|
ime.onblur = function () {
|
||||||
|
this.style.display = 'none';
|
||||||
|
GodotConfig.canvas.focus();
|
||||||
|
GodotIME.active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
GodotConfig.canvas.parentElement.appendChild(ime);
|
||||||
|
GodotIME.ime = ime;
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function () {
|
||||||
|
if (GodotIME.ime) {
|
||||||
|
GodotIME.ime.remove();
|
||||||
|
GodotIME.ime = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mergeInto(LibraryManager.library, GodotIME);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gamepad API helper.
|
* Gamepad API helper.
|
||||||
*/
|
*/
|
||||||
|
@ -338,7 +451,7 @@ mergeInto(LibraryManager.library, GodotInputDragDrop);
|
||||||
* Godot exposed input functions.
|
* Godot exposed input functions.
|
||||||
*/
|
*/
|
||||||
const GodotInput = {
|
const GodotInput = {
|
||||||
$GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop'],
|
$GodotInput__deps: ['$GodotRuntime', '$GodotConfig', '$GodotEventListeners', '$GodotInputGamepads', '$GodotInputDragDrop', '$GodotIME'],
|
||||||
$GodotInput: {
|
$GodotInput: {
|
||||||
getModifiers: function (evt) {
|
getModifiers: function (evt) {
|
||||||
return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
|
return (evt.shiftKey + 0) + ((evt.altKey + 0) << 1) + ((evt.ctrlKey + 0) << 2) + ((evt.metaKey + 0) << 3);
|
||||||
|
@ -456,6 +569,35 @@ const GodotInput = {
|
||||||
GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false);
|
GodotEventListeners.add(GodotConfig.canvas, 'keyup', key_cb.bind(null, 0), false);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IME API
|
||||||
|
*/
|
||||||
|
godot_js_set_ime_active__proxy: 'sync',
|
||||||
|
godot_js_set_ime_active__sig: 'vi',
|
||||||
|
godot_js_set_ime_active: function (p_active) {
|
||||||
|
GodotIME.ime_active(p_active);
|
||||||
|
},
|
||||||
|
|
||||||
|
godot_js_set_ime_position__proxy: 'sync',
|
||||||
|
godot_js_set_ime_position__sig: 'vii',
|
||||||
|
godot_js_set_ime_position: function (p_x, p_y) {
|
||||||
|
GodotIME.ime_position(p_x, p_y);
|
||||||
|
},
|
||||||
|
|
||||||
|
godot_js_set_ime_cb__proxy: 'sync',
|
||||||
|
godot_js_set_ime_cb__sig: 'viiii',
|
||||||
|
godot_js_set_ime_cb: function (p_ime_cb, p_key_cb, code, key) {
|
||||||
|
const ime_cb = GodotRuntime.get_func(p_ime_cb);
|
||||||
|
const key_cb = GodotRuntime.get_func(p_key_cb);
|
||||||
|
GodotIME.init(ime_cb, key_cb, code, key);
|
||||||
|
},
|
||||||
|
|
||||||
|
godot_js_is_ime_focused__proxy: 'sync',
|
||||||
|
godot_js_is_ime_focused__sig: 'i',
|
||||||
|
godot_js_is_ime_focused: function () {
|
||||||
|
return GodotIME.active;
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Gamepad API
|
* Gamepad API
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -177,6 +177,9 @@ void OS_JavaScript::send_notification_callback(int p_notification) {
|
||||||
if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
|
if (p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER || p_notification == MainLoop::NOTIFICATION_WM_MOUSE_EXIT) {
|
||||||
os->cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
|
os->cursor_inside_canvas = p_notification == MainLoop::NOTIFICATION_WM_MOUSE_ENTER;
|
||||||
}
|
}
|
||||||
|
if (godot_js_is_ime_focused() && (p_notification == MainLoop::NOTIFICATION_WM_FOCUS_IN || p_notification == MainLoop::NOTIFICATION_WM_FOCUS_OUT)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
MainLoop *loop = os->get_main_loop();
|
MainLoop *loop = os->get_main_loop();
|
||||||
if (loop) {
|
if (loop) {
|
||||||
loop->notification(p_notification);
|
loop->notification(p_notification);
|
||||||
|
@ -283,22 +286,39 @@ static void dom2godot_mod(Ref<InputEventWithModifiers> ev, int p_mod) {
|
||||||
void OS_JavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) {
|
void OS_JavaScript::key_callback(int p_pressed, int p_repeat, int p_modifiers) {
|
||||||
OS_JavaScript *os = get_singleton();
|
OS_JavaScript *os = get_singleton();
|
||||||
JSKeyEvent &key_event = os->key_event;
|
JSKeyEvent &key_event = os->key_event;
|
||||||
|
|
||||||
|
const String code = String::utf8(key_event.code);
|
||||||
|
const String key = String::utf8(key_event.key);
|
||||||
|
|
||||||
// Resume audio context after input in case autoplay was denied.
|
// Resume audio context after input in case autoplay was denied.
|
||||||
os->resume_audio();
|
os->resume_audio();
|
||||||
|
|
||||||
Ref<InputEventKey> ev;
|
if (os->ime_started) {
|
||||||
ev.instance();
|
return;
|
||||||
ev->set_echo(p_repeat);
|
|
||||||
ev->set_scancode(dom_code2godot_scancode(key_event.code, key_event.key, false));
|
|
||||||
ev->set_physical_scancode(dom_code2godot_scancode(key_event.code, key_event.key, true));
|
|
||||||
ev->set_pressed(p_pressed);
|
|
||||||
dom2godot_mod(ev, p_modifiers);
|
|
||||||
|
|
||||||
String unicode = String::utf8(key_event.key);
|
|
||||||
if (unicode.length() == 1) {
|
|
||||||
ev->set_unicode(unicode[0]);
|
|
||||||
}
|
}
|
||||||
os->input->parse_input_event(ev);
|
|
||||||
|
wchar_t c = 0x00;
|
||||||
|
String unicode = key;
|
||||||
|
if (unicode.length() == 1) {
|
||||||
|
c = unicode[0];
|
||||||
|
}
|
||||||
|
uint32_t keycode = dom_code2godot_scancode(code.utf8().get_data(), key.utf8().get_data(), false);
|
||||||
|
uint32_t scancode = dom_code2godot_scancode(code.utf8().get_data(), key.utf8().get_data(), true);
|
||||||
|
|
||||||
|
OS_JavaScript::KeyEvent ke;
|
||||||
|
|
||||||
|
ke.pressed = p_pressed;
|
||||||
|
ke.echo = p_repeat;
|
||||||
|
ke.raw = true;
|
||||||
|
ke.keycode = keycode;
|
||||||
|
ke.physical_keycode = scancode;
|
||||||
|
ke.unicode = c;
|
||||||
|
ke.mod = p_modifiers;
|
||||||
|
|
||||||
|
if (os->key_event_pos >= os->key_event_buffer.size()) {
|
||||||
|
os->key_event_buffer.resize(1 + os->key_event_pos);
|
||||||
|
}
|
||||||
|
os->key_event_buffer.write[os->key_event_pos++] = ke;
|
||||||
|
|
||||||
// Make sure to flush all events so we can call restricted APIs inside the event.
|
// Make sure to flush all events so we can call restricted APIs inside the event.
|
||||||
os->input->flush_buffered_events();
|
os->input->flush_buffered_events();
|
||||||
|
@ -579,7 +599,7 @@ OS::MouseMode OS_JavaScript::get_mouse_mode() const {
|
||||||
int OS_JavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
|
int OS_JavaScript::mouse_wheel_callback(double p_delta_x, double p_delta_y) {
|
||||||
OS_JavaScript *os = get_singleton();
|
OS_JavaScript *os = get_singleton();
|
||||||
|
|
||||||
if (!godot_js_display_canvas_is_focused()) {
|
if (!godot_js_display_canvas_is_focused() && !godot_js_is_ime_focused()) {
|
||||||
if (os->cursor_inside_canvas) {
|
if (os->cursor_inside_canvas) {
|
||||||
godot_js_display_canvas_focus();
|
godot_js_display_canvas_focus();
|
||||||
} else {
|
} else {
|
||||||
|
@ -670,6 +690,90 @@ void OS_JavaScript::touch_callback(int p_type, int p_count) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IME.
|
||||||
|
void OS_JavaScript::ime_callback(int p_type, const char *p_text) {
|
||||||
|
OS_JavaScript *os = get_singleton();
|
||||||
|
|
||||||
|
// Resume audio context after input in case autoplay was denied.
|
||||||
|
os->resume_audio();
|
||||||
|
|
||||||
|
switch (p_type) {
|
||||||
|
case 0: {
|
||||||
|
// IME start.
|
||||||
|
os->ime_text = String();
|
||||||
|
os->ime_selection = Vector2i();
|
||||||
|
for (int i = os->key_event_pos - 1; i >= 0; i--) {
|
||||||
|
// Delete last raw keydown event from query.
|
||||||
|
if (os->key_event_buffer[i].pressed && os->key_event_buffer[i].raw) {
|
||||||
|
os->key_event_buffer.remove(i);
|
||||||
|
os->key_event_pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
|
||||||
|
os->ime_started = true;
|
||||||
|
} break;
|
||||||
|
case 1: {
|
||||||
|
// IME update.
|
||||||
|
if (os->ime_active && os->ime_started) {
|
||||||
|
os->ime_text = String::utf8(p_text);
|
||||||
|
os->ime_selection = Vector2i(os->ime_text.length(), os->ime_text.length());
|
||||||
|
os->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case 2: {
|
||||||
|
// IME commit.
|
||||||
|
if (os->ime_active && os->ime_started) {
|
||||||
|
os->ime_started = false;
|
||||||
|
|
||||||
|
os->ime_text = String();
|
||||||
|
os->ime_selection = Vector2i();
|
||||||
|
os->get_main_loop()->notification(MainLoop::NOTIFICATION_OS_IME_UPDATE);
|
||||||
|
|
||||||
|
String text = String::utf8(p_text);
|
||||||
|
for (int i = 0; i < text.length(); i++) {
|
||||||
|
OS_JavaScript::KeyEvent ke;
|
||||||
|
|
||||||
|
ke.pressed = true;
|
||||||
|
ke.echo = false;
|
||||||
|
ke.raw = false;
|
||||||
|
ke.keycode = 0;
|
||||||
|
ke.physical_keycode = 0;
|
||||||
|
ke.unicode = text[i];
|
||||||
|
ke.mod = 0;
|
||||||
|
|
||||||
|
if (os->key_event_pos >= os->key_event_buffer.size()) {
|
||||||
|
os->key_event_buffer.resize(1 + os->key_event_pos);
|
||||||
|
}
|
||||||
|
os->key_event_buffer.write[os->key_event_pos++] = ke;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
os->process_keys();
|
||||||
|
os->input->flush_buffered_events();
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_JavaScript::set_ime_active(const bool p_active) {
|
||||||
|
ime_active = p_active;
|
||||||
|
godot_js_set_ime_active(p_active);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OS_JavaScript::set_ime_position(const Point2 &p_pos) {
|
||||||
|
godot_js_set_ime_position(p_pos.x, p_pos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2 OS_JavaScript::get_ime_selection() const {
|
||||||
|
return ime_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
String OS_JavaScript::get_ime_text() const {
|
||||||
|
return ime_text;
|
||||||
|
}
|
||||||
|
|
||||||
// Gamepad
|
// Gamepad
|
||||||
void OS_JavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
|
void OS_JavaScript::gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid) {
|
||||||
InputDefault *input = get_singleton()->input;
|
InputDefault *input = get_singleton()->input;
|
||||||
|
@ -707,6 +811,26 @@ void OS_JavaScript::process_joypads() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OS_JavaScript::process_keys() {
|
||||||
|
for (int i = 0; i < key_event_pos; i++) {
|
||||||
|
const OS_JavaScript::KeyEvent &ke = key_event_buffer[i];
|
||||||
|
|
||||||
|
Ref<InputEventKey> ev;
|
||||||
|
ev.instance();
|
||||||
|
ev->set_pressed(ke.pressed);
|
||||||
|
ev->set_echo(ke.echo);
|
||||||
|
ev->set_scancode(ke.keycode);
|
||||||
|
ev->set_physical_scancode(ke.physical_keycode);
|
||||||
|
ev->set_unicode(ke.unicode);
|
||||||
|
if (ke.raw) {
|
||||||
|
dom2godot_mod(ev, ke.mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
input->parse_input_event(ev);
|
||||||
|
}
|
||||||
|
key_event_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool OS_JavaScript::is_joy_known(int p_device) {
|
bool OS_JavaScript::is_joy_known(int p_device) {
|
||||||
return input->is_joy_mapped(p_device);
|
return input->is_joy_mapped(p_device);
|
||||||
}
|
}
|
||||||
|
@ -859,6 +983,7 @@ Error OS_JavaScript::initialize(const VideoMode &p_desired, int p_video_driver,
|
||||||
godot_js_input_gamepad_cb(&OS_JavaScript::gamepad_callback);
|
godot_js_input_gamepad_cb(&OS_JavaScript::gamepad_callback);
|
||||||
godot_js_input_paste_cb(&OS_JavaScript::update_clipboard_callback);
|
godot_js_input_paste_cb(&OS_JavaScript::update_clipboard_callback);
|
||||||
godot_js_input_drop_files_cb(&OS_JavaScript::drop_files_callback);
|
godot_js_input_drop_files_cb(&OS_JavaScript::drop_files_callback);
|
||||||
|
godot_js_set_ime_cb(&OS_JavaScript::ime_callback, &OS_JavaScript::key_callback, key_event.code, key_event.key);
|
||||||
|
|
||||||
// JS Display interface (js/libs/library_godot_display.js)
|
// JS Display interface (js/libs/library_godot_display.js)
|
||||||
godot_js_display_fullscreen_cb(&OS_JavaScript::fullscreen_change_callback);
|
godot_js_display_fullscreen_cb(&OS_JavaScript::fullscreen_change_callback);
|
||||||
|
@ -940,8 +1065,8 @@ bool OS_JavaScript::main_loop_iterate() {
|
||||||
godot_js_os_fs_sync(&OS_JavaScript::fs_sync_callback);
|
godot_js_os_fs_sync(&OS_JavaScript::fs_sync_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
process_keys();
|
||||||
input->flush_buffered_events();
|
input->flush_buffered_events();
|
||||||
|
|
||||||
if (godot_js_input_gamepad_sample() == OK) {
|
if (godot_js_input_gamepad_sample() == OK) {
|
||||||
process_joypads();
|
process_joypads();
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,24 @@ private:
|
||||||
};
|
};
|
||||||
JSKeyEvent key_event;
|
JSKeyEvent key_event;
|
||||||
|
|
||||||
|
bool ime_active = false;
|
||||||
|
bool ime_started = false;
|
||||||
|
String ime_text;
|
||||||
|
Vector2 ime_selection;
|
||||||
|
|
||||||
|
struct KeyEvent {
|
||||||
|
bool pressed = false;
|
||||||
|
bool echo = false;
|
||||||
|
bool raw = false;
|
||||||
|
uint32_t keycode = 0;
|
||||||
|
uint32_t physical_keycode = 0;
|
||||||
|
uint32_t unicode = 0;
|
||||||
|
int mod = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
Vector<KeyEvent> key_event_buffer;
|
||||||
|
int key_event_pos = 0;
|
||||||
|
|
||||||
VideoMode video_mode;
|
VideoMode video_mode;
|
||||||
bool transparency_enabled;
|
bool transparency_enabled;
|
||||||
|
|
||||||
|
@ -94,6 +112,7 @@ private:
|
||||||
static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
|
static void gamepad_callback(int p_index, int p_connected, const char *p_id, const char *p_guid);
|
||||||
static void input_text_callback(const char *p_text, int p_cursor);
|
static void input_text_callback(const char *p_text, int p_cursor);
|
||||||
void process_joypads();
|
void process_joypads();
|
||||||
|
void process_keys();
|
||||||
|
|
||||||
static void file_access_close_callback(const String &p_file, int p_flags);
|
static void file_access_close_callback(const String &p_file, int p_flags);
|
||||||
|
|
||||||
|
@ -105,6 +124,7 @@ private:
|
||||||
static void update_clipboard_callback(const char *p_text);
|
static void update_clipboard_callback(const char *p_text);
|
||||||
static void update_pwa_state_callback();
|
static void update_pwa_state_callback();
|
||||||
static void _js_utterance_callback(int p_event, int p_id, int p_pos);
|
static void _js_utterance_callback(int p_event, int p_id, int p_pos);
|
||||||
|
static void ime_callback(int p_type, const char *p_text);
|
||||||
static void update_voices_callback(int p_size, const char **p_voice);
|
static void update_voices_callback(int p_size, const char **p_voice);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -161,6 +181,12 @@ public:
|
||||||
virtual float get_screen_scale(int p_screen = -1) const;
|
virtual float get_screen_scale(int p_screen = -1) const;
|
||||||
virtual float get_screen_max_scale() const;
|
virtual float get_screen_max_scale() const;
|
||||||
|
|
||||||
|
virtual void set_ime_active(const bool p_active);
|
||||||
|
virtual void set_ime_position(const Point2 &p_pos);
|
||||||
|
|
||||||
|
virtual Point2 get_ime_selection() const;
|
||||||
|
virtual String get_ime_text() const;
|
||||||
|
|
||||||
virtual Point2 get_mouse_position() const;
|
virtual Point2 get_mouse_position() const;
|
||||||
virtual int get_mouse_button_state() const;
|
virtual int get_mouse_button_state() const;
|
||||||
virtual void set_cursor_shape(CursorShape p_shape);
|
virtual void set_cursor_shape(CursorShape p_shape);
|
||||||
|
|
Loading…
Reference in a new issue