# Copyright 2004-2026 Tom Rothamel <pytom@bishoujo.us>
#
# 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.

init -1500 python:

    class MouseDisplayable(renpy.Displayable):
        """
        :doc: mouse_displayable

        A displayable that wraps a mouse cursor displayable, and causes
        it to move across the screen when the player moves their mouse.

        `cursor`
            A displayable that is used to draw the mouse. If this is None,
            the hardware cursor is used.

        `x`, `y`
            The coordinates of the hotspot, relative to the upper left
            corner of the mouse, in virtual pixels.
        """

        cursor = None

        def __init__(self, cursor, x=0, y=0):

            super(MouseDisplayable, self).__init__()

            if cursor:
                self.cursors = { 'default': ( renpy.displayable(cursor), x, y) }
            else:
                self.cursors = { 'default': None }


            self.last_cursor = "_default_"
            self.last_cursor_st = 0

        def add(self, name, cursor, x=0, y=0):
            """
            :doc: mouse_displayable

            This adds a second cursor, that is used when the `name`
            mouse is displayed. This returns the MouseDisplayable,
            so that calls to this method can be chained.
            """

            if cursor is None:
                self.cursors[name] = None
                return

            self.cursors[name] = ( renpy.displayable(cursor), x, y )
            return self

        def get_cursor_name(self):
            """
            Return the name of the cursor that will be used.
            """

            # Determine the name of the mouse to use.
            for name in renpy.get_mouse_names():
                if name in self.cursors:
                    return name

            return "default"

        def _has_mouse_cursor(self):
            """
            Returns True if this displayable will draw the mouse cursor,
            False if it won't.
            """

            name = self.get_cursor_name()
            return self.cursors.get(name, None) is not None

        def render(self, width, height, st, at):


            name = self.get_cursor_name()

            # Render this displayable.
            rv = renpy.Render(width, height)

            if self.cursors[name] is None:
                return rv

            # Adjust st when the cursor changes.
            if (name != self.last_cursor) or (self.cursor is None):
                self.last_cursor = name
                self.last_cursor_st = st
                self.cursor = self.cursors[name][0]._duplicate(None)

            st = st - self.last_cursor_st

            # If the user is on the screen,
            x, y = renpy.get_mouse_pos()
            _, xo, yo = self.cursors[name]
            d = self.cursor

            if (0 <= x < width) and (0 <= y < height) and renpy.is_mouse_visible():

                cr = renpy.render(d, width, height, st, at)
                rv.subpixel_blit(cr, (x - xo, y - yo))

                # This helps the mouse hide work.
                renpy.redraw(self, 1.0)

            return rv

        def visit(self):
            return [ i[0] for i in self.cursors.values() if i ]
