|
40 | 40 | staticbool keyChangeCapsLock =false; |
41 | 41 | /* Keep track of the current mouse up/down state for open/closed cursor hand*/ |
42 | 42 | staticbool leftMouseGrabbing =false; |
43 | | -/* Keep track of whether stdin has been received*/ |
44 | | -staticbool stdin_received =false; |
45 | | -staticbool stdin_sigint =false; |
46 | 43 | // Global variable to store the original SIGINT handler |
47 | 44 | static PyOS_sighandler_t originalSigintAction =NULL; |
48 | 45 |
|
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 | +staticvoidstopWithEvent() { |
| 48 | + [NSAppstop:nil]; |
| 49 | +// Post an event to trigger the actual stopping. |
| 50 | + [NSApppostEvent: [NSEventotherEventWithType: 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 |
50 | 63 | staticvoidhandleSigint(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 | +staticvoidflushEvents() { |
| 71 | +while (true) { |
| 72 | +NSEvent* event = [NSAppnextEventMatchingMask: NSEventMaskAny |
| 73 | +untilDate: [NSDatedistantPast] |
| 74 | +inMode:NSDefaultRunLoopMode |
| 75 | +dequeue:YES]; |
| 76 | +if (!event) { |
| 77 | +break; |
| 78 | + } |
| 79 | + [NSAppsendEvent:event]; |
| 80 | + } |
52 | 81 | } |
53 | 82 |
|
54 | 83 | staticintwait_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 (![[NSAppwindows]count]) { |
| 89 | +flushEvents(); |
| 90 | +return1; |
| 91 | + } |
58 | 92 |
|
| 93 | + @autoreleasepool { |
59 | 94 | // Set up a SIGINT handler to interrupt the event loop if ctrl+c comes in too |
60 | 95 | originalSigintAction =PyOS_setsig(SIGINT, handleSigint); |
61 | 96 |
|
62 | 97 | // Create an NSFileHandle for standard input |
63 | 98 | NSFileHandle *stdinHandle = [NSFileHandlefileHandleWithStandardInput]; |
64 | 99 |
|
| 100 | + |
65 | 101 | // Register for data available notifications on standard input |
66 | | - [[NSNotificationCenterdefaultCenter]addObserverForName:NSFileHandleDataAvailableNotification |
67 | | -object: stdinHandle |
68 | | -queue: [NSOperationQueuemainQueue]// Use the main queue |
69 | | -usingBlock: ^(NSNotification *notification) { |
70 | | -// Mark that input has been received |
71 | | - stdin_received =true; |
72 | | - } |
| 102 | +id notificationID = [[NSNotificationCenterdefaultCenter]addObserverForName:NSFileHandleDataAvailableNotification |
| 103 | +object: stdinHandle |
| 104 | +queue: [NSOperationQueuemainQueue]// Use the main queue |
| 105 | +usingBlock: ^(NSNotification *notification) {stopWithEvent();} |
73 | 106 | ]; |
74 | 107 |
|
75 | 108 | // Wait in the background for anything that happens to stdin |
76 | 109 | [stdinHandlewaitForDataInBackgroundAndNotify]; |
77 | 110 |
|
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 = [NSAppnextEventMatchingMask: NSEventMaskAny |
85 | | -untilDate: [NSDatedistantPast] |
86 | | -inMode:NSDefaultRunLoopMode |
87 | | -dequeue:YES]; |
88 | | -if (!event) {break; } |
89 | | - [NSAppsendEvent: event]; |
90 | | - } |
91 | | - } |
| 111 | +// Run the application's event loop, which will be interrupted on stdin or SIGINT |
| 112 | + [NSApprun]; |
| 113 | + |
92 | 114 | // Remove the input handler as an observer |
93 | | - [[NSNotificationCenterdefaultCenter]removeObserver: stdinHandle]; |
| 115 | + [[NSNotificationCenterdefaultCenter]removeObserver: notificationID]; |
| 116 | + |
94 | 117 |
|
95 | 118 | // Restore the original SIGINT handler upon exiting the function |
96 | 119 | PyOS_setsig(SIGINT, originalSigintAction); |
| 120 | + |
97 | 121 | return1; |
98 | 122 | } |
99 | 123 | } |
@@ -236,18 +260,7 @@ static void lazy_init(void) { |
236 | 260 | static PyObject* |
237 | 261 | stop(PyObject* self) |
238 | 262 | { |
239 | | - [NSAppstop:nil]; |
240 | | -// Post an event to trigger the actual stopping. |
241 | | - [NSApppostEvent: [NSEventotherEventWithType: 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(); |
251 | 264 | Py_RETURN_NONE; |
252 | 265 | } |
253 | 266 |
|
@@ -382,20 +395,9 @@ static CGFloat _get_device_scale(CGContextRef cr) |
382 | 395 | // We run the app, matching any events that are waiting in the queue |
383 | 396 | // to process, breaking out of the loop when no events remain and |
384 | 397 | // displaying the canvas if needed. |
385 | | -NSEvent *event; |
386 | | - |
387 | 398 | Py_BEGIN_ALLOW_THREADS |
388 | 399 |
|
389 | | -while (true) { |
390 | | - event = [NSAppnextEventMatchingMask: NSEventMaskAny |
391 | | -untilDate: [NSDatedistantPast] |
392 | | -inMode:NSDefaultRunLoopMode |
393 | | -dequeue:YES]; |
394 | | -if (!event) { |
395 | | -break; |
396 | | - } |
397 | | - [NSAppsendEvent:event]; |
398 | | - } |
| 400 | +flushEvents(); |
399 | 401 |
|
400 | 402 | Py_END_ALLOW_THREADS |
401 | 403 |
|
|