# pylint: disable=W0611
# coding: utf-8
'''
Window
======

Core class for creating the default Kivy window. Kivy supports only one window
per application: please don't try to create more than one.
'''

__all__ = ('Keyboard', 'WindowBase', 'Window')

from os.path import join, exists
from os import getcwd
from collections import defaultdict

from kivy.core import core_select_lib
from kivy.clock import Clock
from kivy.config import Config
from kivy.logger import Logger
from kivy.base import EventLoop, stopTouchApp
from kivy.modules import Modules
from kivy.event import EventDispatcher
from kivy.properties import ListProperty, ObjectProperty, AliasProperty, \
    NumericProperty, OptionProperty, StringProperty, BooleanProperty, \
    ColorProperty
from kivy.utils import platform, reify, deprecated, pi_version
from kivy.context import get_current_context
from kivy.uix.behaviors import FocusBehavior
from kivy.setupconfig import USE_SDL3
from kivy.graphics.transformation import Matrix
from kivy.graphics.cgl import cgl_get_backend_name
from kivy.graphics.context_instructions import BindTexture
from kivy.core.image import ImageLoader

# late import
VKeyboard = None
android = None
Animation = None


class Keyboard(EventDispatcher):
    '''Keyboard interface that is returned by
    :meth:`WindowBase.request_keyboard`. When you request a keyboard,
    you'll get an instance of this class. Whatever the keyboard input is
    (system or virtual keyboard), you'll receive events through this
    instance.

    :Events:
        `on_key_down`: keycode, text, modifiers
            Fired when a new key is pressed down
        `on_key_up`: keycode
            Fired when a key is released (up)

    Here is an example of how to request a Keyboard in accordance with the
    current configuration:

    .. include:: ../../examples/widgets/keyboardlistener.py
        :literal:

    '''

    # Keycodes mapping, between str <-> int. These keycodes are
    # currently taken from pygame.key. But when a new provider will be
    # used, it must do the translation to these keycodes too.
    keycodes = {
        # specials keys
        'backspace': 8, 'tab': 9, 'enter': 13, 'rshift': 303, 'shift': 304,
        'alt': 308, 'rctrl': 306, 'lctrl': 305,
        'super': 309, 'alt-gr': 307, 'compose': 311, 'pipe': 310,
        'capslock': 301, 'escape': 27, 'spacebar': 32, 'pageup': 280,
        'pagedown': 281, 'end': 279, 'home': 278, 'left': 276, 'up':
        273, 'right': 275, 'down': 274, 'insert': 277, 'delete': 127,
        'numlock': 300, 'print': 144, 'screenlock': 145, 'pause': 19,

        # a-z keys
        'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103,
        'h': 104, 'i': 105, 'j': 106, 'k': 107, 'l': 108, 'm': 109, 'n': 110,
        'o': 111, 'p': 112, 'q': 113, 'r': 114, 's': 115, 't': 116, 'u': 117,
        'v': 118, 'w': 119, 'x': 120, 'y': 121, 'z': 122,

        # 0-9 keys
        '0': 48, '1': 49, '2': 50, '3': 51, '4': 52,
        '5': 53, '6': 54, '7': 55, '8': 56, '9': 57,

        # numpad
        'numpad0': 256, 'numpad1': 257, 'numpad2': 258, 'numpad3': 259,
        'numpad4': 260, 'numpad5': 261, 'numpad6': 262, 'numpad7': 263,
        'numpad8': 264, 'numpad9': 265, 'numpaddecimal': 266,
        'numpaddivide': 267, 'numpadmul': 268, 'numpadsubstract': 269,
        'numpadadd': 270, 'numpadenter': 271,

        # F1-15
        'f1': 282, 'f2': 283, 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287,
        'f7': 288, 'f8': 289, 'f9': 290, 'f10': 291, 'f11': 292, 'f12': 293,
        'f13': 294, 'f14': 295, 'f15': 296,

        # other keys
        '(': 40, ')': 41,
        '[': 91, ']': 93,
        '{': 123, '}': 125,
        ':': 58, ';': 59,
        '=': 61, '+': 43,
        '-': 45, '_': 95,
        '/': 47, '*': 42,
        '?': 47,
        '`': 96, '~': 126,
        '´': 180, '¦': 166,
        '\\': 92, '|': 124,
        '"': 34, "'": 39,
        ',': 44, '.': 46,
        '<': 60, '>': 62,
        '@': 64, '!': 33,
        '#': 35, '$': 36,
        '%': 37, '^': 94,
        '&': 38, '¬': 172,
        '¨': 168, '…': 8230,
        'ù': 249, 'à': 224,
        'é': 233, 'è': 232,
    }

    __events__ = ('on_key_down', 'on_key_up', 'on_textinput')

    def __init__(self, **kwargs):
        super(Keyboard, self).__init__()

        #: Window which the keyboard is attached too
        self.window = kwargs.get('window', None)

        #: Callback that will be called when the keyboard is released
        self.callback = kwargs.get('callback', None)

        #: Target that have requested the keyboard
        self.target = kwargs.get('target', None)

        #: VKeyboard widget, if allowed by the configuration
        self.widget = kwargs.get('widget', None)

    def on_key_down(self, keycode, text, modifiers):
        pass

    def on_key_up(self, keycode):
        pass

    def on_textinput(self, text):
        pass

    def release(self):
        '''Call this method to release the current keyboard.
        This will ensure that the keyboard is no longer attached to your
        callback.'''
        if self.window:
            self.window.release_keyboard(self.target)
            self.target = None

    def _on_window_textinput(self, instance, text):
        return self.dispatch('on_textinput', text)

    def _on_window_key_down(self, instance, keycode, scancode, text,
                            modifiers):
        keycode = (keycode, self.keycode_to_string(keycode))
        if text == '\x04':
            Window.trigger_keyboard_height()
            return
        return self.dispatch('on_key_down', keycode, text, modifiers)

    def _on_window_key_up(self, instance, keycode, *largs):
        keycode = (keycode, self.keycode_to_string(keycode))
        return self.dispatch('on_key_up', keycode)

    def _on_vkeyboard_key_down(self, instance, keycode, text, modifiers):
        if keycode is None:
            keycode = text.lower()
        keycode = (self.string_to_keycode(keycode), keycode)
        return self.dispatch('on_key_down', keycode, text, modifiers)

    def _on_vkeyboard_key_up(self, instance, keycode, text, modifiers):
        if keycode is None:
            keycode = text
        keycode = (self.string_to_keycode(keycode), keycode)
        return self.dispatch('on_key_up', keycode)

    def _on_vkeyboard_textinput(self, instance, text):
        return self.dispatch('on_textinput', text)

    def string_to_keycode(self, value):
        '''Convert a string to a keycode number according to the
        :attr:`Keyboard.keycodes`. If the value is not found in the
        keycodes, it will return -1.
        '''
        return Keyboard.keycodes.get(value, -1)

    def keycode_to_string(self, value):
        '''Convert a keycode number to a string according to the
        :attr:`Keyboard.keycodes`. If the value is not found in the
        keycodes, it will return ''.
        '''
        keycodes = list(Keyboard.keycodes.values())
        if value in keycodes:
            return list(Keyboard.keycodes.keys())[keycodes.index(value)]
        return ''


class WindowBase(EventDispatcher):
    '''WindowBase is an abstract window widget for any window implementation.

    :Parameters:
        `borderless`: str, one of ('0', '1')
            Set the window border state. Check the
            :mod:`~kivy.config` documentation for a
            more detailed explanation on the values.
        `custom_titlebar`: str, one of ('0', '1')
            Set to `'1'` to uses a custom titlebar
        `fullscreen`: str, one of ('0', '1', 'auto', 'fake')
            Make the window fullscreen. Check the
            :mod:`~kivy.config` documentation for a
            more detailed explanation on the values.
        `width`: int
            Width of the window.
        `height`: int
            Height of the window.
        `minimum_width`: int
            Minimum width of the window (only works for sdl3 window provider).
        `minimum_height`: int
            Minimum height of the window (only works for sdl3 window provider).
        `always_on_top`: bool
            When enabled, the window will be brought to the front and will keep
            the window above the rest. If disabled, it will restore the default
            behavior. Only works for the sdl3 window provider.
        `allow_screensaver`: bool
            Allow the device to show a screen saver, or to go to sleep
            on mobile devices. Defaults to True. Only works for sdl3 window
            provider.

    :Events:
        `on_motion`: etype, motionevent
            Fired when a new :class:`~kivy.input.motionevent.MotionEvent` is
            dispatched
        `on_touch_down`:
            Fired when a new touch event is initiated.
        `on_touch_move`:
            Fired when an existing touch event changes location.
        `on_touch_up`:
            Fired when an existing touch event is terminated.
        `on_draw`:
            Fired when the :class:`Window` is being drawn.
        `on_flip`:
            Fired when the :class:`Window` GL surface is being flipped.
        `on_rotate`: rotation
            Fired when the :class:`Window` is being rotated.
        `on_close`:
            Fired when the :class:`Window` is closed.
        `on_request_close`:
            Fired when the event loop wants to close the window, or if the
            escape key is pressed and `exit_on_escape` is `True`. If a function
            bound to this event returns `True`, the window will not be closed.
            If the event is triggered because of the keyboard escape key,
            the keyword argument `source` is dispatched along with a value of
            `keyboard` to the bound functions.

            .. versionadded:: 1.9.0

        `on_cursor_enter`:
            Fired when the cursor enters the window.

            .. versionadded:: 1.9.1

        `on_cursor_leave`:
            Fired when the cursor leaves the window.

            .. versionadded:: 1.9.1

        `on_minimize`:
            Fired when the window is minimized.

            .. versionadded:: 1.10.0

        `on_maximize`:
            Fired when the window is maximized.

            .. versionadded:: 1.10.0

        `on_restore`:
            Fired when the window is restored.

            .. versionadded:: 1.10.0

        `on_hide`:
            Fired when the window is hidden.

            .. versionadded:: 1.10.0

        `on_show`:
            Fired when the window is shown.

            .. versionadded:: 1.10.0

        `on_keyboard`: key, scancode, codepoint, modifier
            Fired when the keyboard is used for input.

            .. versionchanged:: 1.3.0
                The *unicode* parameter has been deprecated in favor of
                codepoint, and will be removed completely in future versions.

        `on_key_down`: key, scancode, codepoint, modifier
            Fired when a key pressed.

            .. versionchanged:: 1.3.0
                The *unicode* parameter has been deprecated in favor of
                codepoint, and will be removed completely in future versions.

        `on_key_up`: key, scancode, codepoint
            Fired when a key is released.

            .. versionchanged:: 1.3.0
                The *unicode* parameter has be deprecated in favor of
                codepoint, and will be removed completely in future versions.

        `on_drop_begin`: x, y, *args
            Fired when text(s) or file(s) drop on the application is about to
            begin.

            .. versionadded:: 2.1.0

        `on_drop_file`: filename (bytes), x, y, *args
            Fired when a file is dropped on the application.

            .. versionadded:: 1.2.0

            .. versionchanged:: 2.1.0
                Renamed from `on_dropfile` to `on_drop_file`.

        `on_drop_text`: text (bytes), x, y, *args
            Fired when a text is dropped on the application.

            .. versionadded:: 2.1.0

        `on_drop_end`: x, y, *args
            Fired when text(s) or file(s) drop on the application has ended.

            .. versionadded:: 2.1.0

        `on_memorywarning`:
            Fired when the platform have memory issue (iOS / Android mostly)
            You can listen to this one, and clean whatever you can.

            .. versionadded:: 1.9.0

        `on_textedit(self, text)`:
            Fired when inputting with IME.
            The string inputting with IME is set as the parameter of
            this event.

            .. versionadded:: 1.10.1
    '''

    __instance = None
    __initialized = False
    _fake_fullscreen = False

    # private properties
    _density = NumericProperty(1.)
    _size = ListProperty([0, 0])
    _modifiers = ListProperty([])
    _rotation = NumericProperty(0)
    _focus = BooleanProperty(True)

    gl_backends_allowed = []
    """
    A list of Kivy gl backend names, which if not empty, will be the
    exclusive list of gl backends that can be used with this window.
    """

    gl_backends_ignored = []
    """
    A list of Kivy gl backend names that may not be used with this window.
    """

    managed_textinput = False
    """
    True if this Window class uses `on_textinput` to insert text, internal.
    """

    children = ListProperty([])
    '''List of the children of this window.

    :attr:`children` is a :class:`~kivy.properties.ListProperty` instance and
    defaults to an empty list.

    Use :meth:`add_widget` and :meth:`remove_widget` to manipulate the list of
    children. Don't manipulate the list directly unless you know what you are
    doing.
    '''

    parent = ObjectProperty(None, allownone=True)
    '''Parent of this window.

    :attr:`parent` is a :class:`~kivy.properties.ObjectProperty` instance and
    defaults to None. When created, the parent is set to the window itself.
    You must take care of it if you are doing a recursive check.
    '''

    icon = StringProperty()
    '''A path to the window icon.

    .. versionadded:: 1.1.2

    :attr:`icon` is a :class:`~kivy.properties.StringProperty`.
    '''

    def _get_modifiers(self):
        return self._modifiers

    modifiers = AliasProperty(_get_modifiers, None, bind=('_modifiers',))
    '''List of keyboard modifiers currently active.

    .. versionadded:: 1.0.9

    :attr:`modifiers` is an :class:`~kivy.properties.AliasProperty`.
    '''

    minimum_width = NumericProperty(0)
    '''The minimum width to restrict the window to.

    .. versionadded:: 1.9.1

    :attr:`minimum_width` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.
    '''

    minimum_height = NumericProperty(0)
    '''The minimum height to restrict the window to.

    .. versionadded:: 1.9.1

    :attr:`minimum_height` is a :class:`~kivy.properties.NumericProperty` and
    defaults to 0.
    '''

    always_on_top = BooleanProperty(False)
    '''When enabled, the window will be brought to the front and will keep
    the window above the rest. If disabled, it will restore the default
    behavior.

    This option can be toggled freely during the window's lifecycle.

    Only works for the sdl3 window provider. Check the :mod:`~kivy.config`
    documentation for a more detailed explanation on the values.

    .. versionadded:: 2.2.0

    :attr:`always_on_top` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to False.
    '''

    allow_screensaver = BooleanProperty(True)
    '''Whether the screen saver is enabled, or on mobile devices whether the
    device is allowed to go to sleep while the app is open.

    .. versionadded:: 1.10.0

    :attr:`allow_screensaver` is a :class:`~kivy.properties.BooleanProperty`
    and defaults to True.
    '''

    def _get_unrotated_size(self):
        w, h = self._size

        w *= self._density
        h *= self._density

        return w, h

    unrotated_size = AliasProperty(
        _get_unrotated_size, None, bind=("_size", "_density")
    )
    '''The unrotated size of the window in drawable pixels. This is the actual
    pixel size of the drawable area of the window, and takes into account the
    window's pixel density.

    The pixel density is the ratio of the physical pixels to the size of the
    window in window coordinates. For example, on a Retina HiDPI display, the
    pixel density is typically 2.0, meaning that the window's drawable size in
    pixels is twice the size in window coordinates.

    .. versionadded:: 3.0.0

    :attr:`unrotated_size` is a
    read-only :class:`~kivy.properties.AliasProperty`.

    '''

    def _get_size(self):
        w, h = self._get_unrotated_size()

        r = self._rotation

        if r not in (0, 180):
            h, w = w, h

        return w, h

    def _set_size(self, size):
        r = self._rotation
        new_w, new_h = size

        if r not in (0, 180):
            new_h, new_w = new_w, new_h

        if (
            new_w / self._density == self._size[0]
            and new_h / self._density == self._size[1]
        ):
            # The size is the same, no need to resize anything
            return

        self._size = (new_w / self._density, new_h / self._density)
        self.dispatch('on_pre_resize', *self._size)

    size = AliasProperty(_get_size, _set_size, bind=('_size', '_rotation'))
    '''The rotated size of the window in drawable pixels. This is the actual
    pixel size of the drawable area of the window, and takes into account the
    window's :attr:`rotation` and pixel density.

    The pixel density is the ratio of the physical pixels to the size of the
    window in window coordinates. For example, on a Retina HiDPI display, the
    pixel density is typically 2.0, meaning that the window's drawable size in
    pixels is twice the size in window coordinates.

    Consider using :attr:`system_size` if you want to programmatically set the
    window size, as is more portable, and will give a nicer UX across different
    OSes and displays.

    This property differs from :attr:`unrotated_size` as it also accounts for
    the window's rotation.

    .. versionadded:: 1.0.9

    :attr:`size` is an :class:`~kivy.properties.AliasProperty`.
    '''

    clearcolor = ColorProperty((0, 0, 0, 1))
    '''Color used to clear the window.

    ::

        from kivy.core.window import Window

        # red background color
        Window.clearcolor = (1, 0, 0, 1)

        # don't clear background at all
        Window.clearcolor = None

    .. versionchanged:: 1.7.2
        The clearcolor default value is now: (0, 0, 0, 1).

    .. versionadded:: 1.0.9

    :attr:`clearcolor` is an :class:`~kivy.properties.ColorProperty` and
    defaults to (0, 0, 0, 1).

    .. versionchanged:: 2.1.0
        Changed from :class:`~kivy.properties.AliasProperty` to
        :class:`~kivy.properties.ColorProperty`.
    '''

    # make some property read-only
    def _get_width(self):
        return self._get_size()[0]

    width = AliasProperty(_get_width, bind=('_rotation', '_size', '_density'))
    '''Rotated window width.

    :attr:`width` is a read-only :class:`~kivy.properties.AliasProperty`.
    '''

    def _get_height(self):
        '''Rotated window height'''
        return self._get_size()[1]

    height = AliasProperty(_get_height,
                           bind=('_rotation', '_size', '_density'))
    '''Rotated window height.

    :attr:`height` is a read-only :class:`~kivy.properties.AliasProperty`.
    '''

    def _get_center(self):
        return self.width / 2., self.height / 2.

    center = AliasProperty(_get_center, bind=('width', 'height'))
    '''Center of the rotated window.

    .. versionadded:: 1.0.9

    :attr:`center` is an :class:`~kivy.properties.AliasProperty`.
    '''

    def _get_rotation(self):
        return self._rotation

    def _set_rotation(self, x):
        x = int(x % 360)
        if x == self._rotation:
            return
        if x not in (0, 90, 180, 270):
            raise ValueError('can rotate only 0, 90, 180, 270 degrees')
        self._rotation = x
        if not self.initialized:
            return
        self.dispatch('on_pre_resize', *self.size)
        self.dispatch('on_rotate', x)

    rotation = AliasProperty(_get_rotation, _set_rotation,
                             bind=('_rotation', ))
    '''Get/set the window content rotation. Can be one of 0, 90, 180, 270
    degrees.

    .. versionadded:: 1.0.9

    :attr:`rotation` is an :class:`~kivy.properties.AliasProperty`.
    '''

    softinput_mode = OptionProperty(
        '', options=('', 'below_target', 'pan', 'scale', 'resize')
    )
    '''This specifies the behavior of window contents on display of the soft
    keyboard on mobile platforms. It can be one of '', 'pan', 'scale',
    'resize' or 'below_target'. Their effects are listed below.

    +----------------+-------------------------------------------------------+
    | Value          | Effect                                                |
    +================+=======================================================+
    | ''             | The main window is left as is, allowing you to use    |
    |                | the :attr:`keyboard_height` to manage the window      |
    |                | contents manually.                                    |
    +----------------+-------------------------------------------------------+
    | 'pan'          | The main window pans, moving the bottom part of the   |
    |                | window to be always on top of the keyboard.           |
    +----------------+-------------------------------------------------------+
    | 'resize'       | The window is resized and the contents scaled to fit  |
    |                | the remaining space.                                  |
    +----------------+-------------------------------------------------------+
    | 'below_target' | The window pans so that the current target TextInput  |
    |                | widget requesting the keyboard is presented just above|
    |                | the soft keyboard.                                    |
    +----------------+-------------------------------------------------------+

    :attr:`softinput_mode` is an :class:`~kivy.properties.OptionProperty` and
    defaults to ''.

    .. note:: The `resize` option does not currently work with SDL3 on Android.

    .. versionadded:: 1.9.0

    .. versionchanged:: 1.9.1
        The 'below_target' option was added.
    '''

    _keyboard_changed = BooleanProperty(False)
    _kheight = NumericProperty(0)
    _kanimation = None

    def _free_kanimation(self, *largs):
        WindowBase._kanimation = None

    def _animate_content(self):
        '''Animate content to IME height.
        '''
        kargs = self.keyboard_anim_args
        global Animation
        if not Animation:
            from kivy.animation import Animation
        if WindowBase._kanimation:
            WindowBase._kanimation.cancel(self)
        WindowBase._kanimation = kanim = Animation(
            _kheight=self.keyboard_height + self.keyboard_padding,
            d=kargs['d'], t=kargs['t'])
        kanim.bind(on_complete=self._free_kanimation)
        kanim.start(self)

    def _upd_kbd_height(self, *kargs):
        self._keyboard_changed = not self._keyboard_changed
        self._animate_content()

    def _get_ios_kheight(self):
        import ios
        return ios.get_kheight()

    def _get_android_kheight(self):
        if USE_SDL3:  # Placeholder until the SDL3 bootstrap supports this
            return 0
        global android
        if not android:
            import android
        return android.get_keyboard_height()

    def _get_kivy_vkheight(self):
        mode = Config.get('kivy', 'keyboard_mode')
        if (
            mode in ['dock', 'systemanddock']
            and self._vkeyboard_cls is not None
        ):
            for w in self.children:
                if isinstance(w, self._vkeyboard_cls):
                    vkeyboard_height = w.height * w.scale
                    if self.softinput_mode == 'pan':
                        return vkeyboard_height
                    elif (
                        self.softinput_mode == 'below_target'
                        and w.target.y < vkeyboard_height
                    ):
                        return vkeyboard_height - w.target.y
        return 0

    def _get_kheight(self):
        if platform == 'android':
            return self._get_android_kheight()
        elif platform == 'ios':
            return self._get_ios_kheight()
        return self._get_kivy_vkheight()

    keyboard_height = AliasProperty(_get_kheight, bind=('_keyboard_changed',))
    '''Returns the height of the softkeyboard/IME on mobile platforms.
    Will return 0 if not on mobile platform or if IME is not active.

    .. note:: This property returns 0 with SDL3 on Android, but setting
              Window.softinput_mode does work.

    .. versionadded:: 1.9.0

    :attr:`keyboard_height` is a read-only
    :class:`~kivy.properties.AliasProperty` and defaults to 0.
    '''

    keyboard_anim_args = {'t': 'in_out_quart', 'd': .5}
    '''The attributes for animating softkeyboard/IME.
    `t` = `transition`, `d` = `duration`. This value will have no effect on
    desktops.

    .. versionadded:: 1.10.0

    :attr:`keyboard_anim_args` is a dict and defaults to
    {'t': 'in_out_quart', 'd': `.5`}.
    '''

    keyboard_padding = NumericProperty(0)
    '''The padding to have between the softkeyboard/IME & target
    or bottom of window. Will have no effect on desktops.

    .. versionadded:: 1.10.0

    :attr:`keyboard_padding` is a
    :class:`~kivy.properties.NumericProperty` and defaults to 0.
    '''

    def _set_system_size(self, size):
        self._size = size

    def _get_system_size(self):
        if self.softinput_mode == 'resize':
            return self._size[0], self._size[1] - self.keyboard_height
        return self._size

    system_size = AliasProperty(_get_system_size, _set_system_size,
                                bind=('_size',))
    '''Size in window coordinates of the window.

    This property is used to set and retrieve the window size in window
    coordinates (which is different from the drawable size).
    The actual :attr:`size` of the window will be adjusted to account
    for the window's rotation and pixel density.

    Setting the window size in window coordinates is more portable and will
    give a nicer UX across different OSes and displays, therefore it is
    recommended to use this property to set the window size instead of
    :attr:`size`.

    .. versionadded:: 1.0.9

    :attr:`system_size` is an :class:`~kivy.properties.AliasProperty`.
    '''

    borderless = BooleanProperty(False)
    '''When set to True, this property removes the window border/decoration.
    Check the :mod:`~kivy.config` documentation for a more detailed
    explanation on the values.

    .. versionadded:: 1.9.0

    :attr:`borderless` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to False.
    '''

    custom_titlebar = BooleanProperty(False)
    '''When set to True, allows the user to set a widget as a titlebar.
    Check the :mod:`~kivy.config` documentation for a more detailed
    explanation on the values.

    .. versionadded:: 2.1.0

    see :meth:`~kivy.core.window.WindowBase.set_custom_titlebar`
    for detailed usage
    :attr:`custom_titlebar` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to False.
    '''

    fullscreen = OptionProperty(False, options=(True, False, 'auto', 'fake'))
    '''This property sets the fullscreen mode of the window. Available options
    are: True, False, 'auto' and 'fake'. Check the :mod:`~kivy.config`
    documentation for more detailed explanations on these values.

    fullscreen is an :class:`~kivy.properties.OptionProperty` and defaults to
    `False`.

    .. versionadded:: 1.2.0

    .. note::
        The 'fake' option has been deprecated, use the :attr:`borderless`
        property instead.

    .. warning::
        On iOS, setting :attr:`fullscreen` to `False` will not automatically
        hide the status bar.

        To achieve this, you must set :attr:`fullscreen` to `False`, and
        then also set :attr:`borderless` to `False`.
    '''

    mouse_pos = ObjectProperty((0, 0))
    '''2d position of the mouse cursor within the window.

    Position is relative to the left/bottom point of the window.

    .. note::
        Cursor position will be scaled by the pixel density if the high density
        mode is supported by the window provider.

    .. versionadded:: 1.2.0

    :attr:`mouse_pos` is an :class:`~kivy.properties.ObjectProperty` and
    defaults to (0, 0).
    '''

    show_cursor = BooleanProperty(True)
    '''Set whether or not the cursor is shown on the window.

    .. versionadded:: 1.9.1

    :attr:`show_cursor` is a :class:`~kivy.properties.BooleanProperty` and
    defaults to True.
    '''

    def _get_focus(self):
        return self._focus

    focus = AliasProperty(_get_focus, None, bind=('_focus',))
    '''Check whether or not the window currently has focus.

    .. versionadded:: 1.9.1

    :attr:`focus` is a read-only :class:`~kivy.properties.AliasProperty` and
    defaults to True.
    '''

    def _set_cursor_state(self, value):
        pass

    def set_system_cursor(self, cursor_name):
        '''Set type of a mouse cursor in the Window.

        It can be one of 'arrow', 'ibeam', 'wait', 'crosshair', 'wait_arrow',
        'size_nwse', 'size_nesw', 'size_we', 'size_ns', 'size_all', 'no', or
        'hand'.

        On some platforms there might not be a specific cursor supported and
        such an option falls back to one of the substitutable alternatives:

        +------------+-----------+------------+-----------+---------------+
        |            | Windows   | MacOS      | Linux X11 | Linux Wayland |
        +============+===========+============+===========+===============+
        | arrow      | arrow     | arrow      | arrow     | arrow         |
        +------------+-----------+------------+-----------+---------------+
        | ibeam      | ibeam     | ibeam      | ibeam     | ibeam         |
        +------------+-----------+------------+-----------+---------------+
        | wait       | wait      | arrow      | wait      | wait          |
        +------------+-----------+------------+-----------+---------------+
        | crosshair  | crosshair | crosshair  | crosshair | hand          |
        +------------+-----------+------------+-----------+---------------+
        | wait_arrow | arrow     | arrow      | wait      | wait          |
        +------------+-----------+------------+-----------+---------------+
        | size_nwse  | size_nwse | size_all   | size_all  | hand          |
        +------------+-----------+------------+-----------+---------------+
        | size_nesw  | size_nesw | size_all   | size_all  | hand          |
        +------------+-----------+------------+-----------+---------------+
        | size_we    | size_we   | size_we    | size_we   | hand          |
        +------------+-----------+------------+-----------+---------------+
        | size_ns    | size_ns   | size_ns    | size_ns   | hand          |
        +------------+-----------+------------+-----------+---------------+
        | size_all   | size_all  | size_all   | size_all  | hand          |
        +------------+-----------+------------+-----------+---------------+
        | no         | no        | no         | no        | ibeam         |
        +------------+-----------+------------+-----------+---------------+
        | hand       | hand      | hand       | hand      | hand          |
        +------------+-----------+------------+-----------+---------------+

        .. versionadded:: 1.10.1

        .. note::
            This feature requires the SDL3 window provider and is currently
            only supported on desktop platforms.
        '''
        pass

    def _get_window_pos(self):
        pass

    def _set_window_pos(self, x, y):
        pass

    def _get_left(self):
        if not self.initialized:
            return self._left
        return self._get_window_pos()[0]

    def _set_left(self, value):
        pos = self._get_window_pos()
        self._set_window_pos(value, pos[1])

    def _get_top(self):
        if not self.initialized:
            return self._top
        return self._get_window_pos()[1]

    def _set_top(self, value):
        pos = self._get_window_pos()
        self._set_window_pos(pos[0], value)

    top = AliasProperty(_get_top, _set_top)
    '''Top position of the window.

