electronstudio / raylib-python-cffi

Python CFFI bindings for Raylib
http://electronstudio.github.io/raylib-python-cffi
Eclipse Public License 2.0
152 stars 29 forks source link

'out' parameters should throw errors rather than silently convert to pointers #121

Closed deckarep closed 6 months ago

deckarep commented 7 months ago

Hello!

So far this is a great wrapper library but I'm having trouble with something that I thought would be trivial.

When it comes to user interaction with the checkbox: checking or unchecking no matter what I do I can't get the state of the checkbox to stay "checked" and the resulting boolean affected.

I feel like I'm doing something wrong but I've tried everything. One thing that I know works for sure is that if the boolean is toggled logically through code, the state of the checkbox gui element properly reflects that. But not the other way around.

from pyray import *

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

def main():
    init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Foo")
    set_exit_key(KeyboardKey.KEY_ESCAPE)
    set_target_fps(30)

    flag = True

    while not window_should_close():
        begin_drawing()
        clear_background(WHITE)

        gui_check_box(Rectangle(40, 40, 100, 100), "Just a checkbox", flag)

        end_drawing()

main()

Any help is appreciated!

deckarep commented 7 months ago

Ok,

I just discovered a workaround that is not super ideal. If I hand an ffi bool directly to the checkbox then the stickyness (toggle ability) of the checkbox is now correctly tracking.

I think what's happening is that when a regular Python boolean is provided, the makefunc(a) helper method automatically iterates over all arguments and tries to convert them to their ffi ctype equivalent. At least for something like a boolean, when this happens a pointer to a boolean is created to satisfy Raylib's api of wanting a pointer to a boolean for the arg, but the handle is somehow lost. It's this pointer to the boolean that when preserved properly reflects the state of the checkbox being toggled.

Does this mean I need to ensure that I handle this on my end? Or is this a bug?

from pyray import *
from raylib import ffi

SCREEN_WIDTH = 640
SCREEN_HEIGHT = 480

def main():
    init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "Foo")
    set_exit_key(KeyboardKey.KEY_ESCAPE)
    set_target_fps(30)

    flag = ffi.new("bool *", True)

    while not window_should_close():
        begin_drawing()
        clear_background(WHITE)

        gui_check_box(Rectangle(40, 40, 100, 100), "Just a checkbox", flag)

        end_drawing()

main()
electronstudio commented 7 months ago

Yes it's a 'bug', but you're found the workaround and I can't think of a better solution.

We could retain a handle to the created boolean pointer, but:

  1. That would leak memory, if we just did it automatically.
  2. There's no way of passing the handle back to the caller - the original Python boolean that the caller passed will not update when the C boolean does.

Arguably what we should do is refuse to accept Python booleans, throw an exception and tell the user about this workaround.

deckarep commented 7 months ago

Thank you for the prompt reply and explanation. I completely understand the issue and I think it makes more sense to do what you suggest in terms of the API user experience.

With an event loop game or gui app we certainly don't want to leak memory and I think throwing an error is better than accepting an argument which technically will not fully work correctly anyway especially since there is no real way of handling it properly.

Thanks for confirming my understanding and regardless this Python wrapper has been great to work with!

Perhaps I'll contribute a few examples to this repo at least to call out this behavior.

electronstudio commented 6 months ago

I think you're right.

Any examples you could contribute would be gratefully received; we are especially short of Raygui examples.

electronstudio commented 6 months ago

https://github.com/electronstudio/raylib-python-cffi/pull/122