Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emscripten focus / blur #7237

Closed

Conversation

Jonathhhan
Copy link
Contributor

@Jonathhhan Jonathhhan commented Dec 8, 2022

With this line you can get it in ofApp.cpp: int focused = EM_ASM_INT({console.log(canvas.hasFocus); return canvas.hasFocus});.
Not sure if it makes sense in ofxAppEmscriptenWindow.cpp, you can also set that line in the app. And a little drawback: I tried to initialize the value with true, but it does not work. So it is activated with the first interaction with the canvas (a mouse click). I guess, it would need a short delay, because the canvas does not exist yet?

@Jonathhhan
Copy link
Contributor Author

I set a delay of 0 and now only the first frame is not recognized.

@Jonathhhan
Copy link
Contributor Author

Hmm, maybe this would be better: emscripten_set_blur_callback, emscripten_set_focus_callback, emscripten_set_focusin_callback, emscripten_set_focusout_callback...

@Jonathhhan
Copy link
Contributor Author

@ofTheo changed it, and it works quite well...

@Jonathhhan
Copy link
Contributor Author

Jonathhhan commented Dec 10, 2022

Haha, I can print the correct result in ofxAppEmscriptenWindow.cpp, but I realized that I do not know how to pass it to ofApp.cpp. Maybe with a .notifyFocusEvent()? Same with the gamepad thing, I guess...
And I think (but not completely sure), that focus and blur is enough, they both fire earlier than focusin / focusout.

@ofTheo
Copy link
Member

ofTheo commented Dec 13, 2022

@Jonathhhan - there isn't currently a good method to set focus in ofApp.
Is the idea that you want to be able to know when the GL context has focus from the user?

The problem is that emscripten uses the standard ofApp class, so it would be weird to add something that is just for emscripten at that level.

You could potentially make it a function of ofxAppEmscriptenWindow ie:

bool ofxAppEmscriptenWindow::hasFocus(){

So from ofApp you would have to cast the ofGetWindowPtr() to ofxAppEmscriptenWindow * and then you could query it. Or seeing as Emscripten is single window you could potentially have a static function.

So you could do this in ofApp.cpp

if( ofxAppEmscriptenWindow::hasFocus() ){
//do something here 
}

Neither is a super elegant solution but I think could be useful for the few cases people might need it.

Also open to other ideas too! 🙂

@Jonathhhan
Copy link
Contributor Author

Jonathhhan commented Dec 13, 2022

@ofTheo thanks, thats very helpful. Yes, I want to get the focus callback in ofApp (in my case for detecting, if the filebrowser is canceled or if the mouse clicked outside of the filebrowser - but I am sure there are other cases, too). So in my case the focus status would be enough.
This is my use case, only that I call the status directly from java script (which could also be an option, because it works well - that was actually my first attempt):

EM_ASYNC_JS(const char*, loadAudio, (), {
	try {
	var input = document.createElement('input');
	input.type = 'file';
	input.accept = '.adts, .aiff, .caf, .flac, .mp3, .mp4, .ogg, .wav, .webm';
	input.click();
	canvas.hasFocus = false;
	var url = await new Promise((resolve, reject) => {		
		input.onchange = (event) => {
			resolve(URL.createObjectURL(event.target.files[0]));
		};
		canvas.onfocus = (event) => {
			canvas.hasFocus = true;
			reject(input.value.length);
		};
	});
	var size = lengthBytesUTF8(url) + 1;
	var stringPointer = stackAlloc(size);
	stringToUTF8Array(url, HEAP8, stringPointer, size);
	return stringPointer;
	} catch {};
});

void ofApp::bang_1_event(bool & e) { 
	if (EM_ASM_INT(return canvas.hasFocus)) {
		std::string url = loadAudio();
		if (!url.empty()) {
			audioPlayer.unload();
			audioPlayer.load(url);
			audioPlayer.setLoop(true);
			audioPlayer.play();
		}
	}
}

I was setting the status in ofxAppEmscriptenWindow.cpp like that:
EM_ASM({canvas.onfocus = function() {canvas.focused = true}; canvas.onblur = function() {canvas.focused = false}}); (in ofSetup);
The other idea was to make a class ofxEmscriptenEvents that does not use the standard ofApp, so maybe one problem less.
Not sure if my thoughts make sense, have to think about it (and try it...).

@ofTheo
Copy link
Member

ofTheo commented Dec 13, 2022

Yeah, there could be a few different ways to do it.

I could imagine doing something like ofAppEmscriptenWindow::enableEvents() and then you could do ofAddListener and register for different events with custom ofApp callback functions. eg: https://github.com/openframeworks/openFrameworks/blob/master/examples/events/simpleEventsExample/src/ofApp.cpp#L18-L21

For the file stuff I wonder if it could be integrated similar to the desktop dialogs:

eg: https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofSystemUtils.cpp#L319-L321

but for the load here:
https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofSystemUtils.cpp#L347

@Jonathhhan
Copy link
Contributor Author

Jonathhhan commented Dec 14, 2022

I tried your suggestion, but I get the error:
invalid use of member 'onMousePressed' in static member function and invalid use of 'this' outside of a non-static member function
because the callbacks are static like:
static int gamepadconnected_cb(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData);
and if i try to make them non static it also does not work, error:

candidate function not viable: no known conversion from 'int (ofxEmscriptenGamePad::*)(int, const EmscriptenGamepadEvent *, void *)' to 'em_gamepad_callback_func' (aka 'int (*)(int, const EmscriptenGamepadEvent *, void *)') for 3rd argument
EMSCRIPTEN_RESULT emscripten_set_gamepadconnected_callback_on_thread(void *userData, EM_BOOL useCapture, em_gamepad_callback_func callback, pthread_t targetThread);

@Jonathhhan
Copy link
Contributor Author

Jonathhhan commented Dec 19, 2022

Quite a simple method for getting blur/focus and other callbacks, is to get them directly in ofApp.
Thats:

	emscripten_set_focus_callback("#canvas",this,1,&focus_cb);    
	emscripten_set_blur_callback("#canvas",this,1,&blur_cb);

In ofSetup()

int ofApp::focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData){
ofLog(OF_LOG_NOTICE, "focus " + ofToString(10));
	return 0;
}

int ofApp::blur_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData){
ofLog(OF_LOG_NOTICE, "blur " + ofToString(10));
	return 0;
}

And:

		static int focus_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);
		static int blur_cb(int eventType, const EmscriptenFocusEvent *focusEvent, void *userData);

in ofApp.h.

@Jonathhhan
Copy link
Contributor Author

Yeah, there could be a few different ways to do it.

I could imagine doing something like ofAppEmscriptenWindow::enableEvents() and then you could do ofAddListener and register for different events with custom ofApp callback functions. eg: https://github.com/openframeworks/openFrameworks/blob/master/examples/events/simpleEventsExample/src/ofApp.cpp#L18-L21

For the file stuff I wonder if it could be integrated similar to the desktop dialogs:

eg: https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofSystemUtils.cpp#L319-L321

but for the load here: https://github.com/openframeworks/openFrameworks/blob/master/libs/openFrameworks/utils/ofSystemUtils.cpp#L347

Yeah, it would be great, if the filesystem stuff could be integrated like that.

@Jonathhhan Jonathhhan closed this Jan 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants