##// END OF EJS Templates
run CFRunLoopRun if NSApp:run finishes on its own...
Min RK -
Show More
@@ -1,133 +1,143 b''
1 1 """Inputhook for OS X
2 2
3 3 Calls NSApp / CoreFoundation APIs via ctypes.
4 4 """
5 5
6 6 # obj-c boilerplate from appnope, used under BSD 2-clause
7 7
8 8 import ctypes
9 9 import ctypes.util
10 from threading import Event
10 11
11 12 objc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('objc'))
12 13
13 14 void_p = ctypes.c_void_p
14 15
15 16 objc.objc_getClass.restype = void_p
16 17 objc.sel_registerName.restype = void_p
17 18 objc.objc_msgSend.restype = void_p
18 19 objc.objc_msgSend.argtypes = [void_p, void_p]
19 20
20 21 msg = objc.objc_msgSend
21 22
22 23 def _utf8(s):
23 24 """ensure utf8 bytes"""
24 25 if not isinstance(s, bytes):
25 26 s = s.encode('utf8')
26 27 return s
27 28
28 29 def n(name):
29 30 """create a selector name (for ObjC methods)"""
30 31 return objc.sel_registerName(_utf8(name))
31 32
32 33 def C(classname):
33 34 """get an ObjC Class by name"""
34 35 return objc.objc_getClass(_utf8(classname))
35 36
36 37 # end obj-c boilerplate from appnope
37 38
38 39 # CoreFoundation C-API calls we will use:
39 40 CoreFoundation = ctypes.cdll.LoadLibrary(ctypes.util.find_library('CoreFoundation'))
40 41
41 42 CFFileDescriptorCreate = CoreFoundation.CFFileDescriptorCreate
42 43 CFFileDescriptorCreate.restype = void_p
43 44 CFFileDescriptorCreate.argtypes = [void_p, ctypes.c_int, ctypes.c_bool, void_p]
44 45
45 46 CFFileDescriptorGetNativeDescriptor = CoreFoundation.CFFileDescriptorGetNativeDescriptor
46 47 CFFileDescriptorGetNativeDescriptor.restype = ctypes.c_int
47 48 CFFileDescriptorGetNativeDescriptor.argtypes = [void_p]
48 49
49 50 CFFileDescriptorEnableCallBacks = CoreFoundation.CFFileDescriptorEnableCallBacks
50 51 CFFileDescriptorEnableCallBacks.restype = None
51 52 CFFileDescriptorEnableCallBacks.argtypes = [void_p, ctypes.c_ulong]
52 53
53 54 CFFileDescriptorCreateRunLoopSource = CoreFoundation.CFFileDescriptorCreateRunLoopSource
54 55 CFFileDescriptorCreateRunLoopSource.restype = void_p
55 56 CFFileDescriptorCreateRunLoopSource.argtypes = [void_p, void_p, void_p]
56 57
57 58 CFRunLoopGetCurrent = CoreFoundation.CFRunLoopGetCurrent
58 59 CFRunLoopGetCurrent.restype = void_p
59 60
60 61 CFRunLoopAddSource = CoreFoundation.CFRunLoopAddSource
61 62 CFRunLoopAddSource.restype = None
62 63 CFRunLoopAddSource.argtypes = [void_p, void_p, void_p]
63 64
64 65 CFRelease = CoreFoundation.CFRelease
65 66 CFRelease.restype = None
66 67 CFRelease.argtypes = [void_p]
67 68
68 69 CFFileDescriptorInvalidate = CoreFoundation.CFFileDescriptorInvalidate
69 70 CFFileDescriptorInvalidate.restype = None
70 71 CFFileDescriptorInvalidate.argtypes = [void_p]
71 72
72 73 # From CFFileDescriptor.h
73 74 kCFFileDescriptorReadCallBack = 1
74 75 kCFRunLoopCommonModes = void_p.in_dll(CoreFoundation, 'kCFRunLoopCommonModes')
75 76
76 77
77 78 def _NSApp():
78 79 """Return the global NSApplication instance (NSApp)"""
79 80 return msg(C('NSApplication'), n('sharedApplication'))
80 81
81 82
82 83 def _wake(NSApp):
83 84 """Wake the Application"""
84 85 event = msg(C('NSEvent'),
85 86 n('otherEventWithType:location:modifierFlags:'
86 87 'timestamp:windowNumber:context:subtype:data1:data2:'),
87 88 15, # Type
88 89 0, # location
89 90 0, # flags
90 91 0, # timestamp
91 92 0, # window
92 93 None, # context
93 94 0, # subtype
94 95 0, # data1
95 96 0, # data2
96 97 )
97 98 msg(NSApp, n('postEvent:atStart:'), void_p(event), True)
98 99
99 100
101 _triggered = Event()
102
100 103 def _input_callback(fdref, flags, info):
101 104 """Callback to fire when there's input to be read"""
105 _triggered.set()
102 106 CFFileDescriptorInvalidate(fdref)
103 107 CFRelease(fdref)
104 108 NSApp = _NSApp()
105 109 msg(NSApp, n('stop:'), NSApp)
106 110 _wake(NSApp)
107 111
108 112 _c_callback_func_type = ctypes.CFUNCTYPE(None, void_p, void_p, void_p)
109 113 _c_input_callback = _c_callback_func_type(_input_callback)
110 114
111 115
112 116 def _stop_on_read(fd):
113 117 """Register callback to stop eventloop when there's data on fd"""
118 _triggered.clear()
114 119 fdref = CFFileDescriptorCreate(None, fd, False, _c_input_callback, None)
115 120 CFFileDescriptorEnableCallBacks(fdref, kCFFileDescriptorReadCallBack)
116 121 source = CFFileDescriptorCreateRunLoopSource(None, fdref, 0)
117 122 loop = CFRunLoopGetCurrent()
118 123 CFRunLoopAddSource(loop, source, kCFRunLoopCommonModes)
119 124 CFRelease(source)
120 125
121 126
122 127 def inputhook(context):
123 128 """Inputhook for Cocoa (NSApp)"""
124 129 NSApp = _NSApp()
125 130 window_count = msg(
126 131 msg(NSApp, n('windows')),
127 132 n('count')
128 133 )
129 134 if not window_count:
130 135 return
131 136 _stop_on_read(context.fileno())
132 137 msg(NSApp, n('run'))
133
138 if not _triggered.is_set():
139 # app closed without firing callback,
140 # probably due to last window being closed.
141 # Run the loop manually in this case,
142 # since there may be events still to process (#9734)
143 CoreFoundation.CFRunLoopRun()
General Comments 0
You need to be logged in to leave comments. Login now