diff mbox series

[v2,5/7] ui/cocoa: Don't call NSApp sendEvent directly from handleEvent

Message ID 20190214102816.3393-6-peter.maydell@linaro.org
State Superseded
Headers show
Series ui/cocoa: Use OSX's main loop | expand

Commit Message

Peter Maydell Feb. 14, 2019, 10:28 a.m. UTC
Currently the handleEvent method will directly call the NSApp
sendEvent method for any events that we want to let OSX deal
with. When we rearrange the event handling code, the way that
we say "let OSX have this event" is going to change. Prepare
for that by refactoring so that handleEvent returns a flag
indicating whether it consumed the event.

Suggested-by: BALATON Zoltan <balaton@eik.bme.hu>
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

---
New patch in v2
---
 ui/cocoa.m | 49 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 34 insertions(+), 15 deletions(-)

-- 
2.17.2 (Apple Git-113)

Comments

BALATON Zoltan Feb. 14, 2019, 5:04 p.m. UTC | #1
On Thu, 14 Feb 2019, Peter Maydell wrote:
> Currently the handleEvent method will directly call the NSApp

> sendEvent method for any events that we want to let OSX deal

> with. When we rearrange the event handling code, the way that

> we say "let OSX have this event" is going to change. Prepare

> for that by refactoring so that handleEvent returns a flag

> indicating whether it consumed the event.

>

> Suggested-by: BALATON Zoltan <balaton@eik.bme.hu>

> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

> ---

> New patch in v2

> ---

> ui/cocoa.m | 49 ++++++++++++++++++++++++++++++++++---------------

> 1 file changed, 34 insertions(+), 15 deletions(-)

>

> diff --git a/ui/cocoa.m b/ui/cocoa.m

> index 2d943b6e2a..5a84e1aea7 100644

> --- a/ui/cocoa.m

> +++ b/ui/cocoa.m

> @@ -129,8 +129,9 @@

> NSTextField *pauseLabel;

> NSArray * supportedImageFileTypes;

>

> -// Utility function to run specified code block with iothread lock held

> +// Utility functions to run specified code block with iothread lock held

> typedef void (^CodeBlock)(void);

> +typedef bool (^BoolCodeBlock)(void);

>

> static void with_iothread_lock(CodeBlock block)

> {

> @@ -144,6 +145,21 @@ static void with_iothread_lock(CodeBlock block)

>     }

> }

>

> +static bool bool_with_iothread_lock(BoolCodeBlock block)

> +{

> +    bool locked = qemu_mutex_iothread_locked();

> +    bool val;

> +


Git complained about extra white space in the end of the empty line above 
but not sure if it was added during mailing or you have it in the original 
patch.

> +    if (!locked) {

> +        qemu_mutex_lock_iothread();

> +    }

> +    val = block();

> +    if (!locked) {

> +        qemu_mutex_unlock_iothread();

> +    }

> +    return val;

> +}

> +

> // Mac to QKeyCode conversion

> const int mac_to_qkeycode_map[] = {

>     [kVK_ANSI_A] = Q_KEY_CODE_A,

> @@ -320,8 +336,8 @@ - (void) grabMouse;

> - (void) ungrabMouse;

> - (void) toggleFullScreen:(id)sender;

> - (void) handleMonitorInput:(NSEvent *)event;

> -- (void) handleEvent:(NSEvent *)event;

> -- (void) handleEventLocked:(NSEvent *)event;

> +- (bool) handleEvent:(NSEvent *)event;

> +- (bool) handleEventLocked:(NSEvent *)event;

> - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;

> /* The state surrounding mouse grabbing is potentially confusing.

>  * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated

> @@ -664,15 +680,16 @@ - (void) handleMonitorInput:(NSEvent *)event

>     }

> }

>

> -- (void) handleEvent:(NSEvent *)event

> +- (bool) handleEvent:(NSEvent *)event

> {

> -    with_iothread_lock(^{

> -        [self handleEventLocked:event];

> +    return bool_with_iothread_lock(^{

> +        return [self handleEventLocked:event];

>     });

> }


If this is only ever used for this one method, wouldn't it be easier to 
move locking to the method below (even with some goto after setting a ret 
variable to unlock at the end of the method where now it returns in the 
middle, but maybe it could even be done without goto as the whole code is 
one big switch that can be exited with break and an if that can be skipped 
by a flag)? That may be easier to follow than this method within block 
within method and then you wouldn't need bool_with_iothread_lock and 
neither handleEvent. Unless there's something I'm missing which makes this 
convoluted way needed.

Other than that
Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu>


Regards,
BALATON Zoltan

> -- (void) handleEventLocked:(NSEvent *)event

> +- (bool) handleEventLocked:(NSEvent *)event

> {

> +    /* Return true if we handled the event, false if it should be given to OSX */

>     COCOA_DEBUG("QemuCocoaView: handleEvent\n");

>     int buttons = 0;

>     int keycode = 0;

> @@ -743,8 +760,7 @@ - (void) handleEventLocked:(NSEvent *)event

>                 if (keycode == Q_KEY_CODE_F) {

>                     switched_to_fullscreen = true;

>                 }

> -                [NSApp sendEvent:event];

> -                return;

> +                return false;

>             }

>

>             // default

> @@ -759,12 +775,12 @@ - (void) handleEventLocked:(NSEvent *)event

>                         // enable graphic console

>                         case '1' ... '9':

>                             console_select(key - '0' - 1); /* ascii math */

> -                            return;

> +                            return true;

>

>                         // release the mouse grab

>                         case 'g':

>                             [self ungrabMouse];

> -                            return;

> +                            return true;

>                     }

>                 }

>             }

> @@ -781,7 +797,7 @@ - (void) handleEventLocked:(NSEvent *)event

>             // don't pass the guest a spurious key-up if we treated this

>             // command-key combo as a host UI action

>             if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) {

> -                return;

> +                return true;

>             }

>

>             if (qemu_console_is_graphic(NULL)) {

> @@ -875,7 +891,7 @@ - (void) handleEventLocked:(NSEvent *)event

>             mouse_event = false;

>             break;

>         default:

> -            [NSApp sendEvent:event];

> +            return false;

>     }

>

>     if (mouse_event) {

> @@ -911,10 +927,11 @@ - (void) handleEventLocked:(NSEvent *)event

>                 qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);

>             }

>         } else {

> -            [NSApp sendEvent:event];

> +            return false;

>         }

>         qemu_input_event_sync();

>     }

> +    return true;

> }

>

> - (void) grabMouse

> @@ -1749,7 +1766,9 @@ static void cocoa_refresh(DisplayChangeListener *dcl)

>         event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast

>                         inMode: NSDefaultRunLoopMode dequeue:YES];

>         if (event != nil) {

> -            [cocoaView handleEvent:event];

> +            if (![cocoaView handleEvent:event]) {

> +                [NSApp sendEvent:event];

> +            }

>         }

>     } while(event != nil);

>     [pool release];

>
Peter Maydell Feb. 14, 2019, 5:41 p.m. UTC | #2
On Thu, 14 Feb 2019 at 17:04, BALATON Zoltan <balaton@eik.bme.hu> wrote:
>

> On Thu, 14 Feb 2019, Peter Maydell wrote:

> > Currently the handleEvent method will directly call the NSApp

> > sendEvent method for any events that we want to let OSX deal

> > with. When we rearrange the event handling code, the way that

> > we say "let OSX have this event" is going to change. Prepare

> > for that by refactoring so that handleEvent returns a flag

> > indicating whether it consumed the event.

> >

> > Suggested-by: BALATON Zoltan <balaton@eik.bme.hu>

> > Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

> > ---


> > +static bool bool_with_iothread_lock(BoolCodeBlock block)

> > +{

> > +    bool locked = qemu_mutex_iothread_locked();

> > +    bool val;

> > +

>

> Git complained about extra white space in the end of the empty line above

> but not sure if it was added during mailing or you have it in the original

> patch.


Almost certainly an error in the original patch. I'll fix it.

> > +    if (!locked) {

> > +        qemu_mutex_lock_iothread();

> > +    }

> > +    val = block();

> > +    if (!locked) {

> > +        qemu_mutex_unlock_iothread();

> > +    }

> > +    return val;

> > +}

> > +

> > // Mac to QKeyCode conversion

> > const int mac_to_qkeycode_map[] = {

> >     [kVK_ANSI_A] = Q_KEY_CODE_A,

> > @@ -320,8 +336,8 @@ - (void) grabMouse;

> > - (void) ungrabMouse;

> > - (void) toggleFullScreen:(id)sender;

> > - (void) handleMonitorInput:(NSEvent *)event;

> > -- (void) handleEvent:(NSEvent *)event;

> > -- (void) handleEventLocked:(NSEvent *)event;

> > +- (bool) handleEvent:(NSEvent *)event;

> > +- (bool) handleEventLocked:(NSEvent *)event;

> > - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;

> > /* The state surrounding mouse grabbing is potentially confusing.

> >  * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated

> > @@ -664,15 +680,16 @@ - (void) handleMonitorInput:(NSEvent *)event

> >     }

> > }

> >

> > -- (void) handleEvent:(NSEvent *)event

> > +- (bool) handleEvent:(NSEvent *)event

> > {

> > -    with_iothread_lock(^{

> > -        [self handleEventLocked:event];

> > +    return bool_with_iothread_lock(^{

> > +        return [self handleEventLocked:event];

> >     });

> > }

>

> If this is only ever used for this one method, wouldn't it be easier to

> move locking to the method below (even with some goto after setting a ret

> variable to unlock at the end of the method where now it returns in the

> middle, but maybe it could even be done without goto as the whole code is

> one big switch that can be exited with break and an if that can be skipped

> by a flag)? That may be easier to follow than this method within block

> within method and then you wouldn't need bool_with_iothread_lock and

> neither handleEvent. Unless there's something I'm missing which makes this

> convoluted way needed.


The aim was to avoid having to do changes to handleEvent's code flow
in order to do "run it with the lock held"; it also means that the
invariant "we always unlock the lock" is easy to confirm, whereas
if you do the lock/unlock inside a single handleEvent method you have
to look for whether it was done right in early-exit cases. It's
a bit less of a convincing argument than it was in v1 (where we were
making no changes to handleEvent at all other than wrapping it in a
lock), but I think it still makes sense this way.

> Other than that

> Reviewed-by: BALATON Zoltan <balaton@eik.bme.hu>


thanks
-- PMM
BALATON Zoltan Feb. 15, 2019, 12:42 a.m. UTC | #3
On Thu, 14 Feb 2019, Peter Maydell wrote:
> On Thu, 14 Feb 2019 at 17:04, BALATON Zoltan <balaton@eik.bme.hu> wrote:

>>> -- (void) handleEvent:(NSEvent *)event

>>> +- (bool) handleEvent:(NSEvent *)event

>>> {

>>> -    with_iothread_lock(^{

>>> -        [self handleEventLocked:event];

>>> +    return bool_with_iothread_lock(^{

>>> +        return [self handleEventLocked:event];

>>>     });

>>> }

>>

>> If this is only ever used for this one method, wouldn't it be easier to

>> move locking to the method below (even with some goto after setting a ret

>> variable to unlock at the end of the method where now it returns in the

>> middle, but maybe it could even be done without goto as the whole code is

>> one big switch that can be exited with break and an if that can be skipped

>> by a flag)? That may be easier to follow than this method within block

>> within method and then you wouldn't need bool_with_iothread_lock and

>> neither handleEvent. Unless there's something I'm missing which makes this

>> convoluted way needed.

>

> The aim was to avoid having to do changes to handleEvent's code flow

> in order to do "run it with the lock held"; it also means that the

> invariant "we always unlock the lock" is easy to confirm, whereas

> if you do the lock/unlock inside a single handleEvent method you have

> to look for whether it was done right in early-exit cases. It's

> a bit less of a convincing argument than it was in v1 (where we were

> making no changes to handleEvent at all other than wrapping it in a

> lock), but I think it still makes sense this way.


I thought of something like below which to me is simpler than your 
proposal and makes less changes to the original but feel free to choose 
whichever you like, I don't mind either way.

But the comment about sendEvent near the end is now outdated after either 
patch so you may want to update that even in your patch.

Regards,
BALATON Zoltan

diff --git a/ui/cocoa.m b/ui/cocoa.m
index 4727f9c392..de6606e017 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -132,9 +132,8 @@
 QemuSemaphore display_init_sem;
 QemuSemaphore app_started_sem;

-// Utility functions to run specified code block with iothread lock held
+// Utility function to run specified code block with iothread lock held
 typedef void (^CodeBlock)(void);
-typedef bool (^BoolCodeBlock)(void);

 static void with_iothread_lock(CodeBlock block)
 {
@@ -148,21 +147,6 @@ static void with_iothread_lock(CodeBlock block)
     }
 }

-static bool bool_with_iothread_lock(BoolCodeBlock block)
-{
-    bool locked = qemu_mutex_iothread_locked();
-    bool val;
- 
-    if (!locked) {
-        qemu_mutex_lock_iothread();
-    }
-    val = block();
-    if (!locked) {
-        qemu_mutex_unlock_iothread();
-    }
-    return val;
-}
-
 // Mac to QKeyCode conversion
 const int mac_to_qkeycode_map[] = {
     [kVK_ANSI_A] = Q_KEY_CODE_A,
@@ -341,7 +325,6 @@ - (void) ungrabMouse;
 - (void) toggleFullScreen:(id)sender;
 - (void) handleMonitorInput:(NSEvent *)event;
 - (bool) handleEvent:(NSEvent *)event;
-- (bool) handleEventLocked:(NSEvent *)event;
 - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
 /* The state surrounding mouse grabbing is potentially confusing.
  * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated
@@ -689,15 +672,15 @@ - (void) handleMonitorInput:(NSEvent *)event

 - (bool) handleEvent:(NSEvent *)event
 {
-    return bool_with_iothread_lock(^{
-        return [self handleEventLocked:event];
-    });
-}
-
-- (bool) handleEventLocked:(NSEvent *)event
-{
-    /* Return true if we handled the event, false if it should be given to OSX */
     COCOA_DEBUG("QemuCocoaView: handleEvent\n");
+    /* Return true if we handled the event, false if it should be given to OSX */
+    bool handled = true;
+    bool locked = qemu_mutex_iothread_locked();
+    if (!locked) {
+        qemu_mutex_lock_iothread();
+    }
+    /* Make sure to release lock so don't return before the end */
+
     int buttons = 0;
     int keycode = 0;
     bool mouse_event = false;
@@ -767,7 +750,8 @@ - (bool) handleEventLocked:(NSEvent *)event
                 if (keycode == Q_KEY_CODE_F) {
                     switched_to_fullscreen = true;
                 }
-                return false;
+                handled = false;
+                goto unlock;
             }

             // default
@@ -782,12 +766,12 @@ - (bool) handleEventLocked:(NSEvent *)event
                         // enable graphic console
                         case '1' ... '9':
                             console_select(key - '0' - 1); /* ascii math */
-                            return true;
+                            goto unlock;

                         // release the mouse grab
                         case 'g':
                             [self ungrabMouse];
-                            return true;
+                            goto unlock;
                     }
                 }
             }
@@ -804,7 +788,7 @@ - (bool) handleEventLocked:(NSEvent *)event
             // don't pass the guest a spurious key-up if we treated this
             // command-key combo as a host UI action
             if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) {
-                return true;
+                goto unlock;
             }

             if (qemu_console_is_graphic(NULL)) {
@@ -898,16 +882,16 @@ - (bool) handleEventLocked:(NSEvent *)event
             mouse_event = false;
             break;
         default:
-            return false;
+            handled = false;
+            goto unlock;
     }

     if (mouse_event) {
         /* Don't send button events to the guest unless we've got a
          * mouse grab or window focus. If we have neither then this event
          * is the user clicking on the background window to activate and
-         * bring us to the front, which will be done by the sendEvent
-         * call below. We definitely don't want to pass that click through
-         * to the guest.
+         * bring us to the front, which will be done by sendEvent.
+         * We definitely don't want to pass that click through to the guest.
          */
         if ((isMouseGrabbed || [[self window] isKeyWindow]) &&
             (last_buttons != buttons)) {
@@ -934,11 +918,16 @@ - (bool) handleEventLocked:(NSEvent *)event
                 qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);
             }
         } else {
-            return false;
+            handled = false;
+            goto unlock;
         }
         qemu_input_event_sync();
     }
-    return true;
+unlock:
+    if (!locked) {
+        qemu_mutex_unlock_iothread();
+    }
+    return handled;
 }

 - (void) grabMouse
Roman Bolshakov Feb. 22, 2019, 7:20 p.m. UTC | #4
On Thu, Feb 14, 2019 at 10:28:14AM +0000, Peter Maydell wrote:
> Currently the handleEvent method will directly call the NSApp

> sendEvent method for any events that we want to let OSX deal

> with. When we rearrange the event handling code, the way that

> we say "let OSX have this event" is going to change. Prepare

> for that by refactoring so that handleEvent returns a flag

> indicating whether it consumed the event.

> 

> Suggested-by: BALATON Zoltan <balaton@eik.bme.hu>

> Signed-off-by: Peter Maydell <peter.maydell@linaro.org>

> ---

> New patch in v2

> ---

>  ui/cocoa.m | 49 ++++++++++++++++++++++++++++++++++---------------

>  1 file changed, 34 insertions(+), 15 deletions(-)

> 

> @@ -1749,7 +1766,9 @@ static void cocoa_refresh(DisplayChangeListener *dcl)

>          event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast

>                          inMode: NSDefaultRunLoopMode dequeue:YES];

>          if (event != nil) {

> -            [cocoaView handleEvent:event];

> +            if (![cocoaView handleEvent:event]) {

> +                [NSApp sendEvent:event];

> +            }

>          }

>      } while(event != nil);

>      [pool release];

> -- 

> 2.17.2 (Apple Git-113)

> 


I like the patch. It makes clear that cocoa_refresh performs the work
of [NSApp run].

Besides the trailing whitespace issue,

Reviewed-by: Roman Bolshakov <r.bolshakov@yadro.com>

Tested-by: Roman Bolshakov <r.bolshakov@yadro.com>


Thanks,
Roman
diff mbox series

Patch

diff --git a/ui/cocoa.m b/ui/cocoa.m
index 2d943b6e2a..5a84e1aea7 100644
--- a/ui/cocoa.m
+++ b/ui/cocoa.m
@@ -129,8 +129,9 @@ 
 NSTextField *pauseLabel;
 NSArray * supportedImageFileTypes;
 
-// Utility function to run specified code block with iothread lock held
+// Utility functions to run specified code block with iothread lock held
 typedef void (^CodeBlock)(void);
+typedef bool (^BoolCodeBlock)(void);
 
 static void with_iothread_lock(CodeBlock block)
 {
@@ -144,6 +145,21 @@  static void with_iothread_lock(CodeBlock block)
     }
 }
 
+static bool bool_with_iothread_lock(BoolCodeBlock block)
+{
+    bool locked = qemu_mutex_iothread_locked();
+    bool val;
+    
+    if (!locked) {
+        qemu_mutex_lock_iothread();
+    }
+    val = block();
+    if (!locked) {
+        qemu_mutex_unlock_iothread();
+    }
+    return val;
+}
+
 // Mac to QKeyCode conversion
 const int mac_to_qkeycode_map[] = {
     [kVK_ANSI_A] = Q_KEY_CODE_A,
@@ -320,8 +336,8 @@  - (void) grabMouse;
 - (void) ungrabMouse;
 - (void) toggleFullScreen:(id)sender;
 - (void) handleMonitorInput:(NSEvent *)event;
-- (void) handleEvent:(NSEvent *)event;
-- (void) handleEventLocked:(NSEvent *)event;
+- (bool) handleEvent:(NSEvent *)event;
+- (bool) handleEventLocked:(NSEvent *)event;
 - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled;
 /* The state surrounding mouse grabbing is potentially confusing.
  * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated
@@ -664,15 +680,16 @@  - (void) handleMonitorInput:(NSEvent *)event
     }
 }
 
-- (void) handleEvent:(NSEvent *)event
+- (bool) handleEvent:(NSEvent *)event
 {
-    with_iothread_lock(^{
-        [self handleEventLocked:event];
+    return bool_with_iothread_lock(^{
+        return [self handleEventLocked:event];
     });
 }
 
-- (void) handleEventLocked:(NSEvent *)event
+- (bool) handleEventLocked:(NSEvent *)event
 {
+    /* Return true if we handled the event, false if it should be given to OSX */
     COCOA_DEBUG("QemuCocoaView: handleEvent\n");
     int buttons = 0;
     int keycode = 0;
@@ -743,8 +760,7 @@  - (void) handleEventLocked:(NSEvent *)event
                 if (keycode == Q_KEY_CODE_F) {
                     switched_to_fullscreen = true;
                 }
-                [NSApp sendEvent:event];
-                return;
+                return false;
             }
 
             // default
@@ -759,12 +775,12 @@  - (void) handleEventLocked:(NSEvent *)event
                         // enable graphic console
                         case '1' ... '9':
                             console_select(key - '0' - 1); /* ascii math */
-                            return;
+                            return true;
 
                         // release the mouse grab
                         case 'g':
                             [self ungrabMouse];
-                            return;
+                            return true;
                     }
                 }
             }
@@ -781,7 +797,7 @@  - (void) handleEventLocked:(NSEvent *)event
             // don't pass the guest a spurious key-up if we treated this
             // command-key combo as a host UI action
             if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) {
-                return;
+                return true;
             }
 
             if (qemu_console_is_graphic(NULL)) {
@@ -875,7 +891,7 @@  - (void) handleEventLocked:(NSEvent *)event
             mouse_event = false;
             break;
         default:
-            [NSApp sendEvent:event];
+            return false;
     }
 
     if (mouse_event) {
@@ -911,10 +927,11 @@  - (void) handleEventLocked:(NSEvent *)event
                 qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]);
             }
         } else {
-            [NSApp sendEvent:event];
+            return false;
         }
         qemu_input_event_sync();
     }
+    return true;
 }
 
 - (void) grabMouse
@@ -1749,7 +1766,9 @@  static void cocoa_refresh(DisplayChangeListener *dcl)
         event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast
                         inMode: NSDefaultRunLoopMode dequeue:YES];
         if (event != nil) {
-            [cocoaView handleEvent:event];
+            if (![cocoaView handleEvent:event]) {
+                [NSApp sendEvent:event];
+            }
         }
     } while(event != nil);
     [pool release];