Skip to content

Commit 0e8baa5

Browse files
committed
Short circuit input hook when no windows are open
1 parent 18909c7 commit 0e8baa5

File tree

1 file changed

+38
-23
lines changed

1 file changed

+38
-23
lines changed

src/_macosx.m

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -59,21 +59,47 @@ static void stop_with_event() {
5959
atStart: YES];
6060
}
6161

62-
// Signal handler for SIGINT, only sets a flag to exit the run loop
62+
// Signal handler for SIGINT, only argument matching for stop_with_event
6363
static void handleSigint(int signal) {
64-
stop_with_event();
64+
stop_with_event();
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+
}
6581
}
6682

6783
static int wait_for_stdin() {
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+
}
92+
6893
@autoreleasepool {
6994
// Set up a SIGINT handler to interrupt the event loop if ctrl+c comes in too
7095
originalSigintAction = PyOS_setsig(SIGINT, handleSigint);
7196

7297
// Create an NSFileHandle for standard input
7398
NSFileHandle *stdinHandle = [NSFileHandle fileHandleWithStandardInput];
7499

100+
75101
// Register for data available notifications on standard input
76-
[[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification
102+
id notificationID = [[NSNotificationCenter defaultCenter] addObserverForName: NSFileHandleDataAvailableNotification
77103
object: stdinHandle
78104
queue: [NSOperationQueue mainQueue] // Use the main queue
79105
usingBlock: ^(NSNotification *notification) {stop_with_event();}
@@ -82,13 +108,16 @@ static int wait_for_stdin() {
82108
// Wait in the background for anything that happens to stdin
83109
[stdinHandle waitForDataInBackgroundAndNotify];
84110

111+
// Run the application's event loop, which will be interrupted on stdin or SIGINT
85112
[NSApp run];
86113

87114
// Remove the input handler as an observer
88-
[[NSNotificationCenter defaultCenter] removeObserver: stdinHandle];
115+
[[NSNotificationCenter defaultCenter] removeObserver: notificationID];
116+
89117

90118
// Restore the original SIGINT handler upon exiting the function
91119
PyOS_setsig(SIGINT, originalSigintAction);
120+
92121
return 1;
93122
}
94123
}
@@ -366,20 +395,9 @@ static CGFloat _get_device_scale(CGContextRef cr)
366395
// We run the app, matching any events that are waiting in the queue
367396
// to process, breaking out of the loop when no events remain and
368397
// displaying the canvas if needed.
369-
NSEvent *event;
370-
371398
Py_BEGIN_ALLOW_THREADS
372399

373-
while (true) {
374-
event = [NSApp nextEventMatchingMask: NSEventMaskAny
375-
untilDate: [NSDate distantPast]
376-
inMode: NSDefaultRunLoopMode
377-
dequeue: YES];
378-
if (!event) {
379-
break;
380-
}
381-
[NSApp sendEvent:event];
382-
}
400+
flushEvents();
383401

384402
Py_END_ALLOW_THREADS
385403

@@ -1106,13 +1124,10 @@ - (void)close
11061124
{
11071125
[super close];
11081126
--FigureWindowCount;
1109-
if (!FigureWindowCount) {
1110-
/* This is needed for show(), which should exit from [NSApp run]
1111-
* after all windows are closed.
1112-
*/
1113-
PyObject* x = stop(NULL);
1114-
Py_DECREF(x);
1115-
}
1127+
if (!FigureWindowCount) [NSApp stop: self];
1128+
/* This is needed for show(), which should exit from [NSApp run]
1129+
* after all windows are closed.
1130+
*/
11161131
// For each new window, we have incremented the manager reference, so
11171132
// we need to bring that down during close and not just dealloc.
11181133
Py_DECREF(manager);

0 commit comments

Comments
 (0)