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