pyglet: Window buttons on mac cause python/pyglet to crash

Description: The window buttons (close, minimize, full screen) cause python to crash in various ways. (Segmentation fault, Recursion Error, Abort Trap, Illegal Instruction).

Pyglet version(s): 1.4.1

Python version(s): 3.7

OS: OS X 10.14.5 (Seems to only affect newer Macbooks)

Related tickets: bitbucket #199 bitbucket #229

Minimal example:

import pyglet

pyglet.window.Window(resizable=True)

pyglet.app.run()

Output 1 (close):

$ python3 example.py 
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 1120, in objc_method
    args = convert_method_arguments(encoding, args)
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 1000, in convert_method_arguments
    new_args.append(ObjCInstance(a))
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 921, in __new__
    if not isinstance(object_ptr, c_void_p):
RecursionError: maximum recursion depth exceeded while calling a Python object
Segmentation fault: 11
$ 

Output 2 (minimize):

$ python3 example.py 
Traceback (most recent call last):
  File "_ctypes/callbacks.c", line 232, in 'calling callback function'
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 1120, in objc_method
    args = convert_method_arguments(encoding, args)
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 1000, in convert_method_arguments
    new_args.append(ObjCInstance(a))
  File "/usr/local/lib/python3.7/site-packages/pyglet/libs/darwin/cocoapy/runtime.py", line 921, in __new__
    if not isinstance(object_ptr, c_void_p):
RecursionError: maximum recursion depth exceeded while calling a Python object
Illegal instruction: 4
$ 

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 23 (21 by maintainers)

Commits related to this issue

Most upvoted comments

I forked cocoa-python here:

https://github.com/olehermanse/cocoa-python

And reproduced the issue, with a quite small example:

# Example of using ctypes with Cocoa to create an NSWindow with
# an application menu item for quitting.

import sys
from cocoapy import *

NSWindow = ObjCClass('NSWindow')
NSApplication = ObjCClass('NSApplication')
NSMenu = ObjCClass('NSMenu')
NSMenuItem = ObjCClass('NSMenuItem')
NSAutoreleasePool = ObjCClass('NSAutoreleasePool')

class MyWindow_Implementation(object):
    MyWindow = ObjCSubclass('NSWindow', 'MyWindow')

    @MyWindow.method(b'@'+NSUIntegerEncoding+b'@@B')
    def nextEventMatchingMask_untilDate_inMode_dequeue_(self, mask, date, mode, dequeue):

        print(f"nextEventMatchingMask({mask}, {date}, {mode}, {dequeue})")

        event = send_super(self, 'nextEventMatchingMask:untilDate:inMode:dequeue:',
                           mask, date, mode, dequeue, argtypes=[c_uint, c_void_p, c_void_p, c_bool])
        print("send_super returned")

        if event.value is None:
            ret = 0
        else:
            ret = event.value
        print("return value: " + str(ret))
        return ret

MyWindow = ObjCClass('MyWindow')

def create_window():
    print('creating window')
    frame = NSMakeRect(100, 100, 300, 300)
    window = MyWindow.alloc().initWithContentRect_styleMask_backing_defer_(
        frame,
        NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask,
        NSBackingStoreBuffered,
        0)
    window.setTitle_(get_NSString("My Awesome Window"))
    window.makeKeyAndOrderFront_(None)
    return window

def create_autorelease_pool():
    pool = NSAutoreleasePool.alloc().init()
    return pool

def application_run():
    app = NSApplication.sharedApplication()
    create_autorelease_pool()
    create_window()
    app.run()  # never returns

######################################################################

if __name__ == '__main__':
    application_run()

https://github.com/olehermanse/cocoa-python/commit/dc536717f0dcf9f008108a882ccb0955135160eb

So, there are many other examples where send_super() from cocoa-python works, however, in this example it doesn’t. When you call send_super, it seems to call MyWindow.nextEventMatchingMaskUntilDate instead of NSWindow.nextEventMatchingMaskUntilDate.

To reproduce:

$ git clone https://github.com/olehermanse/cocoa-python.git
$ cd cocoa-python
$ pip3 uninstall -y cocoapy ; pip3 install . && python3 examples/osx_crash.py 

Note: I thought setFrameSize had the same problem as nextEventMatchingMaskUntilDate, but that is just because it calls nextEventMatchingMaskUntilDate internally.

We had a pretty long session last night trying to find the root cause. The newer macbooks are indeed sending different data in some events and the current cocoa wrapper is not equipped to deal with it. @olehermanse is testing out PyCocoa now (more recent project).

I have no idea how Cocoa or ObjC works, but here’s a dirty hack of cocoapy that makes window operations work:

def send_super(receiver, selName, *args, **kwargs):
    if hasattr(receiver, '_as_parameter_'):
        receiver = receiver._as_parameter_
    superclass = get_superclass_of_object(receiver)
    superduperclass = c_void_p(objc.class_getSuperclass(superclass))
    if superduperclass.value is not None:
        superclass = superduperclass
    super_struct = OBJC_SUPER(receiver, superclass)

I was able to reproduce this issue on my 2018 Macbook Pro 15 inch w/ touchbar. Got the same output as @olehermanse when minimizing and closing. Also, manually resizing the window by dragging causes a segfault with the same output as closing.

Pyglet version(s): 1.4.1 Python version(s): 3.7.3 OS: macOS 10.14.5