poweratom
poweratom

Reputation: 2434

Custom mouse cursor + IE woes

I'm working on a project that has these attributes:

  1. jQuery driving the page
  2. Unity 3d player embedded to the page using jQuery and unityobject.js (per instruction from Unity)
  3. Unity has custom mouse cursor when user's cursor is inside of Unity's canvas area

Issue: Unity captures the mouse cursor but won't release it when the web page that contains it becomes inactive. This means if the user switches to a different tab (or opens a new window), the mouse cursor disappears when the user mouses over the area where Unity would have been. This is a unity bug described here in Unity 3d forum: http://forum.unity3d.com/threads/4565-Unity-Web-player-issue-mouse-hidden-for-all-new-windows

And no, it's still not fixed in the latest version of Unity web client.

To mitigate the issue, we decided to have Unity listen to the current browser window status via Javascript and capture/release the custom mouse cursor based on the status it receives. But there are other issues:

How Unity receives window status:

// GetUnity returns the Unity object in the DOM<br>
NAMESPACE.windowStatus = function( statusStr ) {
    GetUnity().SendMessage( "_AppShell", "OnRecieveWindowStatusFromWebPage", statusStr );
}

I've tried the following methods...

Method 1: Binding events to the "window" object

$( window ).live( 'focus', function() {
    NAMESPACE.windowStatus( 'active' );
} );

$( window ).live( 'blur', function() {
    NAMESPACE.windowStatus( 'inactive' );
} );

The problem: in IE7 and IE8, new browser tabs aren't considered different "window", so the code has no effect on Unity. It only works if a true separate browser window was open.


Method 2: Binding events to the "document" object
Like such: $( document ).live(...);

The problem: $( document ) in jQuery actually does nothing. Found out the hard way.
Source: http://forum.jquery.com/topic/should-document-live-map-to-document-bind-window-too


Method 3: Binding events to the <body> object
Like such: $( 'body' ).live(...);

The problem: Somehow IE doesn't recognize that the Unity object is part of the DOM (despite the use of the .live() method). So every time user clicks on the embedded Unity player, the browser sends a blur event to Unity, and the mouse cursor gets locked up (console.log() prints out inactive when Unity is clicked on). And in some cases it won't even release the mouse cursor, rendering the entire browser unresponsive to mouse events; the only way to release the cursor is to click outside of the browser window and click on the browser again).


Other things I've tried:

window.addEventListener( 'focus', function() {...} );
document.addEventListener( 'focus', function() {...} );
document.body.addEventListener( 'focus', function() {...} );
document.getElementsByTagName('body')[0].addEventListener( 'focus', function() {...} );

The problem: .addEventListener() method is not supported in IE on those elements at all! GAH!


Bonus:
The more observant of you may chime in a say, "Hey, why not detect the mouse click event on the Unity object itself and ignore the blur event, .live() or .bind() events." Tried that. IE doesn't like it either. It completely ignores the event and says those events are not available for the object.

So my fellow Javascript gurus. I'm out of ideas as how to best approach this problem in an elegant way. Any pointers would be appreciated.

Upvotes: 2

Views: 1673

Answers (1)

poweratom
poweratom

Reputation: 2434

Problem solved. A solution was provide on Unity3d's forum buried down somewhere... http://forum.unity3d.com/threads/27516-Mouse-input-possible-from-different-tab

The gist of it is: Frameworks are not always the answer. Go back to the roots.

I'd forgotten that the .addEventListener() is not native to IE, .attachEvent() is. So my code above would have worked had I just replaced all the event listeners with IE-specific ones when IE is detected.

So the modified code is as follow:

if ( navigator.appName == 'Microsoft Internet Explorer' ) {
    GetUnity().attachEvent( 'onmouseover', onFocus, false );
    GetUnity().attachEvent( 'onfocusin', onFocus, false );
    GetUnity().attachEvent( 'onmouseout', onBlur, false );
} else {
    // Netscape, Firefox, Mozilla, Chrome and Safari.
    GetUnity().addEventListener( 'mouseover', onFocus, false );
    GetUnity().addEventListener( 'focus', onFocus, false );
    GetUnity().addEventListener( 'mouseout', onBlur, false );
}

function onFocus() {
    NAMESPACE.WindowStatus( 'active' );
}

function onBlur() {
    NAMESPACE.WindowStatus( 'inactive' );
}

To truly make this work, you'd have to make the event attachments in a callback function of the unityobject.embedUnity() method. If you try to attach the events prior to Unity being loaded, the browser will complain that GetUnity() is null or undefined.

Hope this helps someone scratching his head out there with the same issues.

Upvotes: 1

Related Questions