Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4468488

Browse filesBrowse files
authored
Merge pull request #28981 from greglucas/macos-pyos-input-hook
FIX: macos: Use standard NSApp run loop in our input hook
2 parents 6a8a80d + b2eaad1 commit 4468488
Copy full SHA for 4468488

File tree

1 file changed

+56
-54
lines changed
Filter options

1 file changed

+56
-54
lines changed

‎src/_macosx.m

Copy file name to clipboardExpand all lines: src/_macosx.m
+56-54Lines changed: 56 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -40,60 +40,84 @@
4040
static bool keyChangeCapsLock = false;
4141
/* Keep track of the current mouse up/down state for open/closed cursor hand */
4242
static bool leftMouseGrabbing = false;
43-
/* Keep track of whether stdin has been received */
44-
static bool stdin_received = false;
45-
static bool stdin_sigint = false;
4643
// Global variable to store the original SIGINT handler
4744
static PyOS_sighandler_t originalSigintAction = NULL;
4845

49-
// Signal handler for SIGINT, only sets a flag to exit the run loop
46+
// Stop the current app's run loop, sending an event to ensure it actually stops
47+
static void stopWithEvent() {
48+
[NSApp stop: nil];
49+
// Post an event to trigger the actual stopping.
50+
[NSApp postEvent: [NSEvent otherEventWithType: NSEventTypeApplicationDefined
51+
location: NSZeroPoint
52+
modifierFlags: 0
53+
timestamp: 0
54+
windowNumber: 0
55+
context: nil
56+
subtype: 0
57+
data1: 0
58+
data2: 0]
59+
atStart: YES];
60+
}
61+
62+
// Signal handler for SIGINT, only argument matching for stopWithEvent
5063
static void handleSigint(int signal) {
51-
stdin_sigint = true;
64+
stopWithEvent();
65+
}
66+
67+
// Helper function to flush all events.
68+
// This is needed in some instances to ensure e.g. that windows are properly closed.
69+
// It is used in the input hook as well as wrapped in a version callable from Python.
70+
static void flushEvents() {
71+
while (true) {
72+
NSEvent* event = [NSApp nextEventMatchingMask: NSEventMaskAny
73+
untilDate: [NSDate distantPast]
74+
inMode: NSDefaultRunLoopMode
75+
dequeue: YES];
76+
if (!event) {
77+
break;
78+
}
79+
[NSApp sendEvent:event];
80+
}
5281
}
5382

5483
static int wait_for_stdin() {
55-
@autoreleasepool {
56-
stdin_received = false;
57-
stdin_sigint = false;
84+
// Short circuit if no windows are active
85+
// Rely on Python's input handling to manage CPU usage
86+
// This queries the NSApp, rather than using our FigureWindowCount because that is decremented when events still
87+
// need to be processed to properly close the windows.
88+
if (![[NSApp windows] count]) {
89+
flushEvents();
90+
return 1;
91+
}
5892

93+
@autoreleasepool {
5994
// Set up a SIGINT handler to interrupt the event loop if ctrl+c comes in too
6095
originalSigintAction = PyOS_setsig(SIGINT, handleSigint);
6196

6297
// Create an NSFileHandle for standard input
6398
NSFileHandle *stdinHandle = [NSFileHandle fileHandleWithStandardInput];
6499

100+
65101
// Register for data available notifications on standard input
66-
[[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification
67-
object: stdinHandle
68-
queue: [NSOperationQueue mainQueue] // Use the main queue
69-
usingBlock: ^(NSNotification *notification) {
70-
// Mark that input has been received
71-
stdin_received = true;
72-
}
102+
id notificationID = [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification
103+
object: stdinHandle
104+
queue: [NSOperationQueue mainQueue] // Use the main queue
105+
usingBlock: ^(NSNotification *notification) {stopWithEvent();}
73106
];
74107

75108
// Wait in the background for anything that happens to stdin
76109
[stdinHandle waitForDataInBackgroundAndNotify];
77110

78-
// continuously run an event loop until the stdin_received flag is set to exit
79-
while (!stdin_received && !stdin_sigint) {
80-
// This loop is similar to the main event loop and flush_events which have
81-
// Py_[BEGIN|END]_ALLOW_THREADS surrounding the loop.
82-
// This should not be necessary here because PyOS_InputHook releases the GIL for us.
83-
while (true) {
84-
NSEvent *event = [NSApp nextEventMatchingMask: NSEventMaskAny
85-
untilDate: [NSDate distantPast]
86-
inMode: NSDefaultRunLoopMode
87-
dequeue: YES];
88-
if (!event) { break; }
89-
[NSApp sendEvent: event];
90-
}
91-
}
111+
// Run the application's event loop, which will be interrupted on stdin or SIGINT
112+
[NSApp run];
113+
92114
// Remove the input handler as an observer
93-
[[NSNotificationCenter defaultCenter] removeObserver: stdinHandle];
115+
[[NSNotificationCenter defaultCenter] removeObserver: notificationID];
116+
94117

95118
// Restore the original SIGINT handler upon exiting the function
96119
PyOS_setsig(SIGINT, originalSigintAction);
120+
97121
return 1;
98122
}
99123
}
@@ -236,18 +260,7 @@ static void lazy_init(void) {
236260
static PyObject*
237261
stop(PyObject* self)
238262
{
239-
[NSApp stop: nil];
240-
// Post an event to trigger the actual stopping.
241-
[NSApp postEvent: [NSEvent otherEventWithType: NSEventTypeApplicationDefined
242-
location: NSZeroPoint
243-
modifierFlags: 0
244-
timestamp: 0
245-
windowNumber: 0
246-
context: nil
247-
subtype: 0
248-
data1: 0
249-
data2: 0]
250-
atStart: YES];
263+
stopWithEvent();
251264
Py_RETURN_NONE;
252265
}
253266

@@ -402,20 +415,9 @@ bool mpl_check_modifier(bool present, PyObject* list, char const* name)
402415
// We run the app, matching any events that are waiting in the queue
403416
// to process, breaking out of the loop when no events remain and
404417
// displaying the canvas if needed.
405-
NSEvent *event;
406-
407418
Py_BEGIN_ALLOW_THREADS
408419

409-
while (true) {
410-
event = [NSApp nextEventMatchingMask: NSEventMaskAny
411-
untilDate: [NSDate distantPast]
412-
inMode: NSDefaultRunLoopMode
413-
dequeue: YES];
414-
if (!event) {
415-
break;
416-
}
417-
[NSApp sendEvent:event];
418-
}
420+
flushEvents();
419421

420422
Py_END_ALLOW_THREADS
421423

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.