/*************************************************************************/
/*  library_pandemonium_display.js                                             */
/*************************************************************************/
/*                       This file is part of:                           */
/*                           GODOT ENGINE                                */
/*                      https://godotengine.org                          */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur.                 */
/* Copyright (c) 2014-2022 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.                */
/*************************************************************************/

const PandemoniumDisplayVK = {

    $PandemoniumDisplayVK__deps: ['$PandemoniumRuntime', '$PandemoniumConfig', '$PandemoniumEventListeners'],
    $PandemoniumDisplayVK__postset: 'PandemoniumOS.atexit(function(resolve, reject) { PandemoniumDisplayVK.clear(); resolve(); });',
    $PandemoniumDisplayVK: {
        textinput: null,
        textarea: null,

        available: function() {
            return PandemoniumConfig.virtual_keyboard && 'ontouchstart' in window;
        },

        init: function(input_cb) {
            function create(what) {
                const elem = document.createElement(what);
                elem.style.display = 'none';
                elem.style.position = 'absolute';
                elem.style.zIndex = '-1';
                elem.style.background = 'transparent';
                elem.style.padding = '0px';
                elem.style.margin = '0px';
                elem.style.overflow = 'hidden';
                elem.style.width = '0px';
                elem.style.height = '0px';
                elem.style.border = '0px';
                elem.style.outline = 'none';
                elem.readonly = true;
                elem.disabled = true;
                PandemoniumEventListeners.add(elem, 'input', function(evt) {
                    const c_str = PandemoniumRuntime.allocString(elem.value);
                    input_cb(c_str, elem.selectionEnd);
                    PandemoniumRuntime.free(c_str);
                }, false);
                PandemoniumEventListeners.add(elem, 'blur', function(evt) {
                    elem.style.display = 'none';
                    elem.readonly = true;
                    elem.disabled = true;
                }, false);
                PandemoniumConfig.canvas.insertAdjacentElement('beforebegin', elem);
                return elem;
            }
            PandemoniumDisplayVK.textinput = create('input');
            PandemoniumDisplayVK.textarea = create('textarea');
            PandemoniumDisplayVK.updateSize();
        },
        show: function(text, type, start, end) {
            if (!PandemoniumDisplayVK.textinput || !PandemoniumDisplayVK.textarea) {
                return;
            }
            if (PandemoniumDisplayVK.textinput.style.display !== '' || PandemoniumDisplayVK.textarea.style.display !== '') {
                PandemoniumDisplayVK.hide();
            }
            PandemoniumDisplayVK.updateSize();
            let elem = GodotDisplayVK.textinput;
            switch (type) {
                case 0: // KEYBOARD_TYPE_DEFAULT
                    elem.type = 'text';
                    elem.inputmode = '';
                    break;
                case 1: // KEYBOARD_TYPE_MULTILINE
                    elem = GodotDisplayVK.textarea;
                    break;
                case 2: // KEYBOARD_TYPE_NUMBER
                    elem.type = 'text';
                    elem.inputmode = 'numeric';
                    break;
                case 3: // KEYBOARD_TYPE_NUMBER_DECIMAL
                    elem.type = 'text';
                    elem.inputmode = 'decimal';
                    break;
                case 4: // KEYBOARD_TYPE_PHONE
                    elem.type = 'tel';
                    elem.inputmode = '';
                    break;
                case 5: // KEYBOARD_TYPE_EMAIL_ADDRESS
                    elem.type = 'email';
                    elem.inputmode = '';
                    break;
                case 6: // KEYBOARD_TYPE_PASSWORD
                    elem.type = 'password';
                    elem.inputmode = '';
                    break;
                case 7: // KEYBOARD_TYPE_URL
                    elem.type = 'url';
                    elem.inputmode = '';
                    break;
                default:
                    elem.type = 'text';
                    elem.inputmode = '';
                    break;
            }
            elem.readonly = false;
            elem.disabled = false;
            elem.value = text;
            elem.style.display = 'block';
            elem.focus();
            elem.setSelectionRange(start, end);
        },
        hide: function() {
            if (!PandemoniumDisplayVK.textinput || !PandemoniumDisplayVK.textarea) {
                return;
            }
            [PandemoniumDisplayVK.textinput, PandemoniumDisplayVK.textarea].forEach(function(elem) {
                elem.blur();
                elem.style.display = 'none';
                elem.value = '';
            });
        },
        updateSize: function() {
            if (!PandemoniumDisplayVK.textinput || !PandemoniumDisplayVK.textarea) {
                return;
            }
            const rect = PandemoniumConfig.canvas.getBoundingClientRect();

            function update(elem) {
                elem.style.left = `${rect.left}px`;
                elem.style.top = `${rect.top}px`;
                elem.style.width = `${rect.width}px`;
                elem.style.height = `${rect.height}px`;
            }
            update(PandemoniumDisplayVK.textinput);
            update(PandemoniumDisplayVK.textarea);
        },
        clear: function() {
            if (PandemoniumDisplayVK.textinput) {
                PandemoniumDisplayVK.textinput.remove();
                PandemoniumDisplayVK.textinput = null;
            }
            if (PandemoniumDisplayVK.textarea) {
                PandemoniumDisplayVK.textarea.remove();
                PandemoniumDisplayVK.textarea = null;
            }
        },
    },
};
mergeInto(LibraryManager.library, PandemoniumDisplayVK);

/*
 * Display server cursor helper.
 * Keeps track of cursor status and custom shapes.
 */
const PandemoniumDisplayCursor = {
    $PandemoniumDisplayCursor__deps: ['$PandemoniumOS', '$PandemoniumConfig'],
    $PandemoniumDisplayCursor__postset: 'PandemoniumOS.atexit(function(resolve, reject) { PandemoniumDisplayCursor.clear(); resolve(); });',
    $PandemoniumDisplayCursor: {
        shape: 'default',
        visible: true,
        cursors: {},
        set_style: function(style) {
            PandemoniumConfig.canvas.style.cursor = style;
        },
        set_shape: function(shape) {
            PandemoniumDisplayCursor.shape = shape;
            let css = shape;
            if (shape in PandemoniumDisplayCursor.cursors) {
                const c = PandemoniumDisplayCursor.cursors[shape];
                css = `url("${c.url}") ${c.x} ${c.y}, default`;
            }
            if (PandemoniumDisplayCursor.visible) {
                PandemoniumDisplayCursor.set_style(css);
            }
        },
        clear: function() {
            PandemoniumDisplayCursor.set_style('');
            PandemoniumDisplayCursor.shape = 'default';
            PandemoniumDisplayCursor.visible = true;
            Object.keys(PandemoniumDisplayCursor.cursors).forEach(function(key) {
                URL.revokeObjectURL(PandemoniumDisplayCursor.cursors[key]);
                delete PandemoniumDisplayCursor.cursors[key];
            });
        },
        lockPointer: function() {
            const canvas = PandemoniumConfig.canvas;
            if (canvas.requestPointerLock) {
                canvas.requestPointerLock();
            }
        },
        releasePointer: function() {
            if (document.exitPointerLock) {
                document.exitPointerLock();
            }
        },
        isPointerLocked: function() {
            return document.pointerLockElement === PandemoniumConfig.canvas;
        },
    },
};
mergeInto(LibraryManager.library, PandemoniumDisplayCursor);

const PandemoniumDisplayScreen = {
    $PandemoniumDisplayScreen__deps: ['$PandemoniumConfig', '$PandemoniumOS', '$GL', 'emscripten_webgl_get_current_context'],
    $PandemoniumDisplayScreen: {
        desired_size: [0, 0],
        hidpi: true,
        getPixelRatio: function() {
            return PandemoniumDisplayScreen.hidpi ? window.devicePixelRatio || 1 : 1;
        },
        isFullscreen: function() {
            const elem = document.fullscreenElement || document.mozFullscreenElement ||
                document.webkitFullscreenElement || document.msFullscreenElement;
            if (elem) {
                return elem === PandemoniumConfig.canvas;
            }
            // But maybe knowing the element is not supported.
            return document.fullscreen || document.mozFullScreen ||
                document.webkitIsFullscreen;
        },
        hasFullscreen: function() {
            return document.fullscreenEnabled || document.mozFullScreenEnabled ||
                document.webkitFullscreenEnabled;
        },
        requestFullscreen: function() {
            if (!PandemoniumDisplayScreen.hasFullscreen()) {
                return 1;
            }
            const canvas = PandemoniumConfig.canvas;
            try {
                const promise = (canvas.requestFullscreen || canvas.msRequestFullscreen ||
                    canvas.mozRequestFullScreen || canvas.mozRequestFullscreen ||
                    canvas.webkitRequestFullscreen
                ).call(canvas);
                // Some browsers (Safari) return undefined.
                // For the standard ones, we need to catch it.
                if (promise) {
                    promise.catch(function() {
                        // nothing to do.
                    });
                }
            } catch (e) {
                return 1;
            }
            return 0;
        },
        exitFullscreen: function() {
            if (!PandemoniumDisplayScreen.isFullscreen()) {
                return 0;
            }
            try {
                const promise = document.exitFullscreen();
                if (promise) {
                    promise.catch(function() {
                        // nothing to do.
                    });
                }
            } catch (e) {
                return 1;
            }
            return 0;
        },
        _updateGL: function() {
            const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
            const gl = GL.getContext(gl_context_handle);
            if (gl) {
                GL.resizeOffscreenFramebuffer(gl);
            }
        },
        updateSize: function() {
            const isFullscreen = PandemoniumDisplayScreen.isFullscreen();
            const wantsFullWindow = PandemoniumConfig.canvas_resize_policy === 2;
            const noResize = PandemoniumConfig.canvas_resize_policy === 0;
            const wwidth = PandemoniumDisplayScreen.desired_size[0];
            const wheight = PandemoniumDisplayScreen.desired_size[1];
            const canvas = PandemoniumConfig.canvas;
            let width = wwidth;
            let height = wheight;
            if (noResize) {
                // Don't resize canvas, just update GL if needed.
                if (canvas.width !== width || canvas.height !== height) {
                    PandemoniumDisplayScreen.desired_size = [canvas.width, canvas.height];
                    PandemoniumDisplayScreen._updateGL();
                    return 1;
                }
                return 0;
            }
            const scale = PandemoniumDisplayScreen.getPixelRatio();
            if (isFullscreen || wantsFullWindow) {
                // We need to match screen size.
                width = window.innerWidth * scale;
                height = window.innerHeight * scale;
            }
            const csw = `${width / scale}px`;
            const csh = `${height / scale}px`;
            if (canvas.style.width !== csw || canvas.style.height !== csh || canvas.width !== width || canvas.height !== height) {
                // Size doesn't match.
                // Resize canvas, set correct CSS pixel size, update GL.
                canvas.width = width;
                canvas.height = height;
                canvas.style.width = csw;
                canvas.style.height = csh;
                PandemoniumDisplayScreen._updateGL();
                return 1;
            }
            return 0;
        },
    },
};
mergeInto(LibraryManager.library, PandemoniumDisplayScreen);

/**
 * Display server interface.
 *
 * Exposes all the functions needed by DisplayServer implementation.
 */
const PandemoniumDisplay = {
    $PandemoniumDisplay__deps: ['$PandemoniumConfig', '$PandemoniumRuntime', '$PandemoniumDisplayCursor', '$PandemoniumEventListeners', '$PandemoniumDisplayScreen', '$PandemoniumDisplayVK'],
    $PandemoniumDisplay: {
        window_icon: '',
        getDPI: function() {
            // devicePixelRatio is given in dppx
            // https://drafts.csswg.org/css-values/#resolution
            // > due to the 1:96 fixed ratio of CSS *in* to CSS *px*, 1dppx is equivalent to 96dpi.
            const dpi = Math.round(window.devicePixelRatio * 96);
            return dpi >= 96 ? dpi : 96;
        },
    },

    // This is implemented as "glGetBufferSubData" in new emscripten versions.
    // Since we have to support older (pre 2.0.17) emscripten versions, we add this wrapper function instead.
    pandemonium_js_display_glGetBufferSubData__sig: 'viiii',
    pandemonium_js_display_glGetBufferSubData__deps: ['$GL', 'emscripten_webgl_get_current_context'],
    pandemonium_js_display_glGetBufferSubData: function(target, offset, size, data) {
        const gl_context_handle = _emscripten_webgl_get_current_context(); // eslint-disable-line no-undef
        const gl = GL.getContext(gl_context_handle);
        if (gl) {
            gl.GLctx['getBufferSubData'](target, offset, HEAPU8, data, size);
        }
    },

    pandemonium_js_display_is_swap_ok_cancel__sig: 'i',
    pandemonium_js_display_is_swap_ok_cancel: function() {
        const win = (['Windows', 'Win64', 'Win32', 'WinCE']);
        const plat = navigator.platform || '';
        if (win.indexOf(plat) !== -1) {
            return 1;
        }
        return 0;
    },

    pandemonium_js_display_alert__sig: 'vi',
    pandemonium_js_display_alert: function(p_text) {
        window.alert(PandemoniumRuntime.parseString(p_text)); // eslint-disable-line no-alert
    },

    pandemonium_js_display_screen_dpi_get__sig: 'i',
    pandemonium_js_display_screen_dpi_get: function() {
        return PandemoniumDisplay.getDPI();
    },

    pandemonium_js_display_pixel_ratio_get__sig: 'f',
    pandemonium_js_display_pixel_ratio_get: function() {
        return PandemoniumDisplayScreen.getPixelRatio();
    },

    pandemonium_js_display_fullscreen_request__sig: 'i',
    pandemonium_js_display_fullscreen_request: function() {
        return PandemoniumDisplayScreen.requestFullscreen();
    },

    pandemonium_js_display_fullscreen_exit__sig: 'i',
    pandemonium_js_display_fullscreen_exit: function() {
        return PandemoniumDisplayScreen.exitFullscreen();
    },

    pandemonium_js_display_desired_size_set__sig: 'vii',
    pandemonium_js_display_desired_size_set: function(width, height) {
        PandemoniumDisplayScreen.desired_size = [width, height];
        PandemoniumDisplayScreen.updateSize();
    },

    pandemonium_js_display_size_update__sig: 'i',
    pandemonium_js_display_size_update: function() {
        const updated = PandemoniumDisplayScreen.updateSize();
        if (updated) {
            PandemoniumDisplayVK.updateSize();
        }
        return updated;
    },

    pandemonium_js_display_screen_size_get__sig: 'vii',
    pandemonium_js_display_screen_size_get: function(width, height) {
        const scale = PandemoniumDisplayScreen.getPixelRatio();
        PandemoniumRuntime.setHeapValue(width, window.screen.width * scale, 'i32');
        PandemoniumRuntime.setHeapValue(height, window.screen.height * scale, 'i32');
    },

    pandemonium_js_display_window_size_get__sig: 'vii',
    pandemonium_js_display_window_size_get: function(p_width, p_height) {
        PandemoniumRuntime.setHeapValue(p_width, PandemoniumConfig.canvas.width, 'i32');
        PandemoniumRuntime.setHeapValue(p_height, PandemoniumConfig.canvas.height, 'i32');
    },

    pandemonium_js_display_has_webgl__sig: 'ii',
    pandemonium_js_display_has_webgl: function(p_version) {
        if (p_version !== 1 && p_version !== 2) {
            return false;
        }
        try {
            return !!document.createElement('canvas').getContext(p_version === 2 ? 'webgl2' : 'webgl');
        } catch (e) {
            /* Not available */
        }
        return false;
    },

    /*
     * Canvas
     */
    pandemonium_js_display_canvas_focus__sig: 'v',
    pandemonium_js_display_canvas_focus: function() {
        PandemoniumConfig.canvas.focus();
    },

    pandemonium_js_display_canvas_is_focused__sig: 'i',
    pandemonium_js_display_canvas_is_focused: function() {
        return document.activeElement === PandemoniumConfig.canvas;
    },

    /*
     * Touchscreen
     */
    pandemonium_js_display_touchscreen_is_available__sig: 'i',
    pandemonium_js_display_touchscreen_is_available: function() {
        return 'ontouchstart' in window;
    },

    /*
     * Clipboard
     */
    pandemonium_js_display_clipboard_set__sig: 'ii',
    pandemonium_js_display_clipboard_set: function(p_text) {
        const text = PandemoniumRuntime.parseString(p_text);
        if (!navigator.clipboard || !navigator.clipboard.writeText) {
            return 1;
        }
        navigator.clipboard.writeText(text).catch(function(e) {
            // Setting OS clipboard is only possible from an input callback.
            PandemoniumRuntime.error('Setting OS clipboard is only possible from an input callback for the HTML5 plafrom. Exception:', e);
        });
        return 0;
    },

    pandemonium_js_display_clipboard_get__sig: 'ii',
    pandemonium_js_display_clipboard_get: function(callback) {
        const func = PandemoniumRuntime.get_func(callback);
        try {
            navigator.clipboard.readText().then(function(result) {
                const ptr = PandemoniumRuntime.allocString(result);
                func(ptr);
                PandemoniumRuntime.free(ptr);
            }).catch(function(e) {
                // Fail graciously.
            });
        } catch (e) {
            // Fail graciously.
        }
    },

    /*
     * Window
     */
    pandemonium_js_display_window_title_set__sig: 'vi',
    pandemonium_js_display_window_title_set: function(p_data) {
        document.title = PandemoniumRuntime.parseString(p_data);
    },

    pandemonium_js_display_window_icon_set__sig: 'vii',
    pandemonium_js_display_window_icon_set: function(p_ptr, p_len) {
        let link = document.getElementById('-gd-engine-icon');
        if (link === null) {
            link = document.createElement('link');
            link.rel = 'icon';
            link.id = '-gd-engine-icon';
            document.head.appendChild(link);
        }
        const old_icon = PandemoniumDisplay.window_icon;
        const png = new Blob([PandemoniumRuntime.heapSlice(HEAPU8, p_ptr, p_len)], {
            type: 'image/png'
        });
        PandemoniumDisplay.window_icon = URL.createObjectURL(png);
        link.href = PandemoniumDisplay.window_icon;
        if (old_icon) {
            URL.revokeObjectURL(old_icon);
        }
    },

    /*
     * Cursor
     */
    pandemonium_js_display_cursor_set_visible__sig: 'vi',
    pandemonium_js_display_cursor_set_visible: function(p_visible) {
        const visible = p_visible !== 0;
        if (visible === PandemoniumDisplayCursor.visible) {
            return;
        }
        PandemoniumDisplayCursor.visible = visible;
        if (visible) {
            PandemoniumDisplayCursor.set_shape(PandemoniumDisplayCursor.shape);
        } else {
            PandemoniumDisplayCursor.set_style('none');
        }
    },

    pandemonium_js_display_cursor_is_hidden__sig: 'i',
    pandemonium_js_display_cursor_is_hidden: function() {
        return !PandemoniumDisplayCursor.visible;
    },

    pandemonium_js_display_cursor_set_shape__sig: 'vi',
    pandemonium_js_display_cursor_set_shape: function(p_string) {
        PandemoniumDisplayCursor.set_shape(PandemoniumRuntime.parseString(p_string));
    },

    pandemonium_js_display_cursor_set_custom_shape__sig: 'viiiii',
    pandemonium_js_display_cursor_set_custom_shape: function(p_shape, p_ptr, p_len, p_hotspot_x, p_hotspot_y) {
        const shape = PandemoniumRuntime.parseString(p_shape);
        const old_shape = PandemoniumDisplayCursor.cursors[shape];
        if (p_len > 0) {
            const png = new Blob([PandemoniumRuntime.heapSlice(HEAPU8, p_ptr, p_len)], {
                type: 'image/png'
            });
            const url = URL.createObjectURL(png);
            PandemoniumDisplayCursor.cursors[shape] = {
                url: url,
                x: p_hotspot_x,
                y: p_hotspot_y,
            };
        } else {
            delete PandemoniumDisplayCursor.cursors[shape];
        }
        if (shape === PandemoniumDisplayCursor.shape) {
            PandemoniumDisplayCursor.set_shape(PandemoniumDisplayCursor.shape);
        }
        if (old_shape) {
            URL.revokeObjectURL(old_shape.url);
        }
    },

    pandemonium_js_display_cursor_lock_set__sig: 'vi',
    pandemonium_js_display_cursor_lock_set: function(p_lock) {
        if (p_lock) {
            PandemoniumDisplayCursor.lockPointer();
        } else {
            PandemoniumDisplayCursor.releasePointer();
        }
    },

    pandemonium_js_display_cursor_is_locked__sig: 'i',
    pandemonium_js_display_cursor_is_locked: function() {
        return PandemoniumDisplayCursor.isPointerLocked() ? 1 : 0;
    },

    /*
     * Listeners
     */
    pandemonium_js_display_fullscreen_cb__sig: 'vi',
    pandemonium_js_display_fullscreen_cb: function(callback) {
        const canvas = PandemoniumConfig.canvas;
        const func = PandemoniumRuntime.get_func(callback);

        function change_cb(evt) {
            if (evt.target === canvas) {
                func(PandemoniumDisplayScreen.isFullscreen());
            }
        }
        PandemoniumEventListeners.add(document, 'fullscreenchange', change_cb, false);
        PandemoniumEventListeners.add(document, 'mozfullscreenchange', change_cb, false);
        PandemoniumEventListeners.add(document, 'webkitfullscreenchange', change_cb, false);
    },

    pandemonium_js_display_window_blur_cb__sig: 'vi',
    pandemonium_js_display_window_blur_cb: function(callback) {
        const func = PandemoniumRuntime.get_func(callback);
        PandemoniumEventListeners.add(window, 'blur', function() {
            func();
        }, false);
    },

    pandemonium_js_display_notification_cb__sig: 'viiiii',
    pandemonium_js_display_notification_cb: function(callback, p_enter, p_exit, p_in, p_out) {
        const canvas = PandemoniumConfig.canvas;
        const func = PandemoniumRuntime.get_func(callback);
        const notif = [p_enter, p_exit, p_in, p_out];
        ['mouseover', 'mouseleave', 'focus', 'blur'].forEach(function(evt_name, idx) {
            PandemoniumEventListeners.add(canvas, evt_name, function() {
                func(notif[idx]);
            }, true);
        });
    },

    pandemonium_js_display_setup_canvas__sig: 'viiii',
    pandemonium_js_display_setup_canvas: function(p_width, p_height, p_fullscreen, p_hidpi) {
        const canvas = PandemoniumConfig.canvas;
        PandemoniumEventListeners.add(canvas, 'contextmenu', function(ev) {
            ev.preventDefault();
        }, false);
        PandemoniumEventListeners.add(canvas, 'webglcontextlost', function(ev) {
            alert('WebGL context lost, please reload the page'); // eslint-disable-line no-alert
            ev.preventDefault();
        }, false);
        PandemoniumDisplayScreen.hidpi = !!p_hidpi;
        switch (PandemoniumConfig.canvas_resize_policy) {
            case 0: // None
                PandemoniumDisplayScreen.desired_size = [canvas.width, canvas.height];
                break;
            case 1: // Project
                PandemoniumDisplayScreen.desired_size = [p_width, p_height];
                break;
            default: // Full window
                // Ensure we display in the right place, the size will be handled by updateSize
                canvas.style.position = 'absolute';
                canvas.style.top = 0;
                canvas.style.left = 0;
                break;
        }
        PandemoniumDisplayScreen.updateSize();
        if (p_fullscreen) {
            PandemoniumDisplayScreen.requestFullscreen();
        }
    },

    /*
     * Virtual Keyboard
     */
    pandemonium_js_display_vk_show__sig: 'viiii',
    pandemonium_js_display_vk_show: function(p_text, p_type, p_start, p_end) {
        const text = PandemoniumRuntime.parseString(p_text);
        const start = p_start > 0 ? p_start : 0;
        const end = p_end > 0 ? p_end : start;
        PandemoniumDisplayVK.show(text, p_type, start, end);
    },

    pandemonium_js_display_vk_hide__sig: 'v',
    pandemonium_js_display_vk_hide: function() {
        PandemoniumDisplayVK.hide();
    },

    pandemonium_js_display_vk_available__sig: 'i',
    pandemonium_js_display_vk_available: function() {
        return PandemoniumDisplayVK.available();
    },

    pandemonium_js_display_vk_cb__sig: 'vi',
    pandemonium_js_display_vk_cb: function(p_input_cb) {
        const input_cb = PandemoniumRuntime.get_func(p_input_cb);
        if (PandemoniumDisplayVK.available()) {
            PandemoniumDisplayVK.init(input_cb);
        }
    },
};

autoAddDeps(PandemoniumDisplay, '$PandemoniumDisplay');
mergeInto(LibraryManager.library, PandemoniumDisplay);