| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377 |
- /******************************************************************************/
- #include "stdafx.h"
- #if WEB
- namespace EE{
- /******************************************************************************/
- static Bool BackgroundUpdateTimeSet, Firefox;
- static UInt BackgroundUpdateTime;
- Flt ScreenScale=1, MouseWheelScale;
- /******************************************************************************/
- static void SetMaximized(Int window_width=0, Int window_height=0)
- {
- VecI2 win_size(window_width ? window_width : JavaScriptRunI("window.outerWidth"),
- window_height ? window_height : JavaScriptRunI("window.outerHeight"));
- App._maximized=(win_size.x>=JavaScriptRunI("screen.availWidth") && win_size.y>=JavaScriptRunI("screen.availHeight")); // this sets if the browser window is maximized
- }
- /******************************************************************************/
- static EM_BOOL MouseMove(int eventType, const EmscriptenMouseEvent *e, void *userData)
- {
- Ms._delta_relative.x+=e->movementX;
- Ms._delta_relative.y-=e->movementY;
- VecI2 posi(RoundPos(e->screenX*ScreenScale),
- RoundPos(e->screenY*ScreenScale));
- Ms._deltai+=posi-Ms._desktop_posi;
- Ms._desktop_posi=posi;
- Ms. _window_posi.set(RoundPos(e->canvasX*ScreenScale), // have to use 'canvas' instead of 'client' because that one is the window client but not the canvas
- RoundPos(e->canvasY*ScreenScale));
- return 0;
- }
- static EM_BOOL MouseDown(int eventType, const EmscriptenMouseEvent *e, void *userData)
- {
- if(Ms._on_client)
- {
- Int b=e->button; switch(b){case 1: b=2; break; case 2: b=1; break;}
- Ms.push(b);
- return 1;
- }
- return 0;
- }
- static EM_BOOL MouseUp(int eventType, const EmscriptenMouseEvent *e, void *userData)
- {
- Int b=e->button; switch(b){case 1: b=2; break; case 2: b=1; break;}
- Ms.release(b);
- return 1;
- }
- static EM_BOOL MouseWheel(int eventType, const EmscriptenWheelEvent *e, void *userData)
- {
- if(Ms._on_client || Ms.b(0)) // process the wheel only if on client or if there's a mouse button pressed (dragging from the client)
- {
- Ms._wheel.x+=e->deltaX*MouseWheelScale;
- Ms._wheel.y-=e->deltaY*MouseWheelScale;
- return 1; // eat it so it won't be processed by the browser
- }
- return 0;
- }
- static EM_BOOL MouseLock(int eventType, const EmscriptenPointerlockChangeEvent *pointerlockChangeEvent, void *userData)
- {
- Ms._locked=pointerlockChangeEvent->isActive;
- return 1;
- }
- /******************************************************************************
- Currently used 'charCode' and 'keyCode' are deprecated in Emscripten, however they fit perfectly in the needs of the engine.
- 'keyCode' maps exactly to KB_KEY (which are mapped to Windows VK_ Virtual Keys) that was tested on Chrome on Win and Mac.
- The suggested replacements are 'key' and 'code',
- 'key' should be used only in 'KeyPressed' as WM_CHAR, however it's UTF8 based and would cause overhead to decode.
- 'code' should be treated as scan code, it produces different results than 'keyCode' on QWERTY/AZERTY leyouts, however it's UTF8 text-based and would cause overhead to decode.
- /******************************************************************************/
- static EM_BOOL KeyPressed(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
- {
- //LogN(S+"KeyPressed, key:"+FromUTF8(e->key)+", code:"+FromUTF8(e->code)+", char:"+Char(e->charCode));
- Kb.queue(Char(e->charCode), e->keyCode);
- return 1;
- }
- static EM_BOOL KeyDown(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
- {
- //LogN(S+"KeyDown, key:"+FromUTF8(e->key)+", code:"+FromUTF8(e->code)+", keyName:"+Kb.keyName(KB_KEY(e->keyCode))+", location:"+(Int)e->location);
- KB_KEY key=KB_KEY(e->keyCode);
- switch(e->location)
- {
- case DOM_KEY_LOCATION_LEFT: switch(key)
- {
- case KB_CTRL : key=KB_LCTRL ; break;
- case KB_SHIFT: key=KB_LSHIFT; break;
- case KB_ALT : key=KB_LALT ; break;
- //case KB_WIN : key=KB_LWIN ; break;
- }break;
- case DOM_KEY_LOCATION_RIGHT: switch(key)
- {
- case KB_CTRL : key=KB_RCTRL ; break;
- case KB_SHIFT: key=KB_RSHIFT; break;
- case KB_ALT : key=KB_RALT ; break;
- //case KB_WIN : key=KB_RWIN ; break;
- }break;
- }
- Kb.push(key, key);
- // !! queue characters after push !!
- if(e->ctrlKey) // if control is pressed then we need to eat it to avoid browser taking over Ctrl+A, Ctrl+C, Ctrl+V, etc.
- {
- if(key>='A' && key<='Z')Kb.queue(Char(key + (e->shiftKey ? 0 : 'a'-'A')), key);else
- if(key>='0' && key<='9')Kb.queue(Char(key ), key);
- return 1; // eat the key
- }
- return Kb.keyChar(key) ? 0 : 1; // if the key will generate a character then don't eat it (0) so KeyPress can get called, otherwise eat it (1)
- }
- static EM_BOOL KeyUp(int eventType, const EmscriptenKeyboardEvent *e, void *userData)
- {
- //LogN(S+"KeyUp, key:"+FromUTF8(e->key)+", code:"+FromUTF8(e->code)+", keyName:"+Kb.keyName(KB_KEY(e->keyCode))+", location:"+(Int)e->location);
- KB_KEY key=KB_KEY(e->keyCode);
- switch(e->location)
- {
- case DOM_KEY_LOCATION_LEFT: switch(key)
- {
- case KB_CTRL : key=KB_LCTRL ; break;
- case KB_SHIFT: key=KB_LSHIFT; break;
- case KB_ALT : key=KB_LALT ; break;
- //case KB_WIN : key=KB_LWIN ; break;
- }break;
- case DOM_KEY_LOCATION_RIGHT: switch(key)
- {
- case KB_CTRL : key=KB_RCTRL ; break;
- case KB_SHIFT: key=KB_RSHIFT; break;
- case KB_ALT : key=KB_RALT ; break;
- //case KB_WIN : key=KB_RWIN ; break;
- }break;
- }
- Kb.release(key);
- return 1;
- }
- /******************************************************************************/
- static EM_BOOL TouchStart(int eventType, const EmscriptenTouchEvent *e, void *userData)
- {
- REP(e->numTouches)
- {
- C EmscriptenTouchPoint &s=e->touches[i]; if(s.isChanged)
- {
- Bool stylus=false;
- CPtr pid=CPtr(s.identifier);
- VecI2 posi(RoundPos(s.screenX*ScreenScale),
- RoundPos(s.screenY*ScreenScale));
- Vec2 pos=D.windowPixelToScreen(Vec2(s.canvasX, s.canvasY)*ScreenScale);
- Touch *touch=FindTouchByHandle(pid);
- if( !touch)touch=&Touches.New().init(posi, pos, pid, stylus);else
- {
- touch->_remove=false; // disable 'remove' in case it was enabled (for example the same touch was released in same/previous frame)
- touch-> reinit(posi, pos);
- }
- touch->_state=BS_ON|BS_PUSHED;
- touch->_force=1;
- }
- }
- return 1;
- }
- static EM_BOOL TouchEnd(int eventType, const EmscriptenTouchEvent *e, void *userData)
- {
- REP(e->numTouches)
- {
- C EmscriptenTouchPoint &s=e->touches[i];
- if(s.isChanged)
- if(Touch *touch=FindTouchByHandle(CPtr(s.identifier)))
- {
- touch->_posi .set(RoundPos(s.screenX*ScreenScale),
- RoundPos(s.screenY*ScreenScale));
- touch->_pos =D.windowPixelToScreen(Vec2(s.canvasX, s.canvasY)*ScreenScale);
- touch->_remove=true;
- if(touch->_state&BS_ON) // check for state in case it was manually eaten
- {
- touch->_state|= BS_RELEASED;
- touch->_state&=~BS_ON;
- }
- }
- }
- return 1;
- }
- static EM_BOOL TouchMove(int eventType, const EmscriptenTouchEvent *e, void *userData)
- {
- REP(e->numTouches)
- {
- C EmscriptenTouchPoint &s=e->touches[i];
- if(s.isChanged)
- if(Touch *touch=FindTouchByHandle(CPtr(s.identifier)))
- {
- VecI2 posi(RoundPos(s.screenX*ScreenScale),
- RoundPos(s.screenY*ScreenScale));
- touch->_deltai+=posi-touch->_posi;
- touch->_posi =posi;
- touch->_pos =D.windowPixelToScreen(Vec2(s.canvasX, s.canvasY)*ScreenScale);
- }
- }
- return 1;
- }
- /******************************************************************************/
- static EM_BOOL LostFocus(int eventType, const EmscriptenFocusEvent *e, void *userData)
- {
- Kb.release(KB_LCTRL); Kb.release(KB_LSHIFT); Kb.release(KB_LALT); Kb.release(KB_LWIN);
- Kb.release(KB_RCTRL); Kb.release(KB_RSHIFT); Kb.release(KB_RALT); Kb.release(KB_RWIN);
- return 0;
- }
- static EM_BOOL VisibilityChanged(int eventType, const EmscriptenVisibilityChangeEvent *e, void *userData)
- {
- if(e->hidden)PauseSound();else ResumeSound();
- App.setActive(!e->hidden);
- BackgroundUpdateTimeSet=false; // if app just got deactivated then make sure that we will recalc the time instead of using an old out-dated value
- return 0;
- }
- static EM_BOOL OrientationChanged(int eventType, const EmscriptenOrientationChangeEvent *e, void *userData)
- {
- /* FIXME test on mobile devices
- switch(e->orientationIndex)
- {
- case EMSCRIPTEN_ORIENTATION_PORTRAIT_PRIMARY : App._orientation=DIR_UP ; break;
- case EMSCRIPTEN_ORIENTATION_PORTRAIT_SECONDARY : App._orientation=DIR_DOWN ; break;
- case EMSCRIPTEN_ORIENTATION_LANDSCAPE_PRIMARY : App._orientation=DIR_RIGHT; break;
- case EMSCRIPTEN_ORIENTATION_LANDSCAPE_SECONDARY: App._orientation=DIR_LEFT ; break;
- }*/
- return 0;
- }
- static EM_BOOL FullChanged(int eventType, const EmscriptenFullscreenChangeEvent *e, void *userData)
- {
- return 0;
- }
- static EM_BOOL Resized(int eventType, const EmscriptenUiEvent *e, void *userData) // this gets called when window is resized or zoom is changed
- {
- SetMaximized(e->windowOuterWidth, e->windowOuterHeight);
- int width, height; emscripten_get_canvas_element_size(null, &width, &height);
- EmscriptenFullscreenChangeEvent full; emscripten_get_fullscreen_status(&full);
- #if 0
- LogN(S+"Resized:"+width+'x'+height+' '+full.isFullscreen+", Zoom:"+D.browserZoom());
- LogN(S+"inner:"+e->windowInnerWidth+'x'+e->windowInnerHeight+' '+e->windowInnerWidth*D.browserZoom()+'x'+e->windowInnerHeight*D.browserZoom());
- LogN(S+"body:"+e->documentBodyClientWidth+'x'+e->documentBodyClientHeight+' '+e->documentBodyClientWidth*D.browserZoom()+'x'+e->documentBodyClientHeight*D.browserZoom());
- #endif
- if(!(App.flag&APP_WEB_DISABLE_AUTO_RESIZE) && !full.isFullscreen)
- {
- D._full=false; D._res.zero(); // clear values to make sure D.mode will get executed
- Flt zoom=D.browserZoom();
- #if 0 // can't use this because the values are always Int's
- D.mode(Max(1, RoundPos(e->windowInnerWidth *zoom)),
- Max(1, RoundPos(e->windowInnerHeight*zoom)));
- #else
- JavaScriptRun("var target=Module['canvas'];if(target){target.style.width='100vw';target.style.height='100vh';}"); // resize to max
- Dbl css_w, css_h; emscripten_get_element_css_size(null, &css_w, &css_h); // get actual size
- Int w=Max(1, RoundPos(css_w*zoom)), h=Max(1, RoundPos(css_h*zoom)); // calculate pixels
- //LogN(S+css_w+' '+css_h+' '+css_w*zoom+' '+css_h*zoom+' '+w+' '+h);
- D.mode(w, h);
- #endif
- }else
- if(width!=D.resW() || height!=D.resH() || full.isFullscreen!=D.full())
- {
- // handle bug when leaving fullscreen we still get the same full resolution, this can happen when pressing Esc in fullscreen, or just sometimes when manually disabling fullscreen
- if(D.full() && !full.isFullscreen // if leaving full screen
- && width==D.resW() && height==D.resH()) // but the resolution is the same
- {
- D._full=false; D._res.zero(); // clear values to make sure D.mode will get executed
- D.mode(App._window_size.x, App._window_size.y, false);
- }else D.modeSet(width, height, full.isFullscreen);
- }else // res wasn't changed but zoom may have been
- {
- Dbl css_w, css_h; emscripten_get_element_css_size(null, &css_w, &css_h);
- ScreenScale=D.resW()/css_w; // here can't use zoom, because we don't maintain 1:1 pixel ratio
- }
- return 0;
- }
- /******************************************************************************/
- static void SetCallbacks()
- {
- emscripten_set_fullscreenchange_callback (0, 0, 1, FullChanged);
- emscripten_set_resize_callback (0, 0, 1, Resized);
- emscripten_set_blur_callback (0, 0, 1, LostFocus);
- emscripten_set_visibilitychange_callback ( 0, 1, VisibilityChanged);
- emscripten_set_orientationchange_callback ( 0, 1, OrientationChanged);
- //TODO: WEB joypad support
- //emscripten_set_gamepadconnected_callback ( 0, 1, GamepadConnected);
- //emscripten_set_gamepaddisconnected_callback( 0, 1, GamepadDisconnected);
- //emscripten_get_num_gamepads();
- //emscripten_get_gamepad_status(int index, EmscriptenGamepadEvent *gamepadState);
- emscripten_set_mousemove_callback (0, 0, 1, MouseMove);
- emscripten_set_mousedown_callback (0, 0, 1, MouseDown);
- emscripten_set_mouseup_callback (0, 0, 1, MouseUp);
- emscripten_set_wheel_callback (0, 0, 1, MouseWheel);
- emscripten_set_pointerlockchange_callback(0, 0, 1, MouseLock);
- emscripten_set_keypress_callback (0, 0, 1, KeyPressed);
- emscripten_set_keydown_callback (0, 0, 1, KeyDown);
- emscripten_set_keyup_callback (0, 0, 1, KeyUp);
- emscripten_set_touchstart_callback (0, 0, 1, TouchStart);
- emscripten_set_touchend_callback (0, 0, 1, TouchEnd);
- emscripten_set_touchcancel_callback (0, 0, 1, TouchEnd);
- emscripten_set_touchmove_callback (0, 0, 1, TouchMove);
- }
- /******************************************************************************/
- static void MainLoop()
- {
- if(App._close)
- {
- App.del();
- emscripten_cancel_main_loop();
- }else
- {
- UpdateThreads();
- if(!App.active())
- if(Int wait=((App.flag&APP_WORK_IN_BACKGROUND) ? App.background_wait : -1))
- {
- if(wait>0) // finite wait
- {
- if(BackgroundUpdateTimeSet) // if we already have the end time limit
- {
- wait=BackgroundUpdateTime-Time.curTimeMs(); MAX(wait, 0); // calculate remaining time, convert to Int first before maximizing
- }else
- {
- BackgroundUpdateTime=Time.curTimeMs()+wait; BackgroundUpdateTimeSet=true;
- }
- }
- if(!wait)
- {
- App.update();
- BackgroundUpdateTime=Time.curTimeMs()+App.background_wait; BackgroundUpdateTimeSet=true; // for WEB immediately recalc new time, because browsers already introduce long delays between updates when app is not active
- }
- return;
- }
- App.update();
- }
- }
- static void PreloadLoop()
- {
- UpdateThreads();
- Time.update();
- App._callbacks.update();
- if(!Preload()) // user has finished preloading
- {
- emscripten_cancel_main_loop(); // clear main loop (needs to be called before 'emscripten_set_main_loop')
- if(App.create1())
- {
- Firefox=Contains(D.deviceName(), "Mozilla");
- MouseWheelScale=(Firefox ? 1.0f/3 : 1.0f/100); // FireFox uses a different scale
- ScreenScale=D.browserZoom();
- SetCallbacks();
- emscripten_set_main_loop(MainLoop, 0, false); // don't set 'true' as the exception has already been called in 'emscripten_exit_with_live_runtime' below
- }
- }
- }
- /******************************************************************************/
- } // namespace EE
- /******************************************************************************/
- extern "C" EMSCRIPTEN_KEEPALIVE void FileSystemReady()
- {
- SetMaximized();
- if(App.create0())
- {
- App.setActive(true);
- emscripten_set_main_loop(PreloadLoop, 0, false); // don't set 'true' as the exception has already been called in 'emscripten_exit_with_live_runtime' below
- }
- }
- int main() // initialize the FileSystem first
- {
- EM_ASM
- ({
- // can't mount IDBFS to root "/" - https://github.com/kripken/emscripten/issues/5632
- FS.mkdir('/data'); FS.mount(IDBFS, {}, '/data');
- FS.syncfs(true, function(err) // load data
- {
- FS.chdir('/data');
- ccall('FileSystemReady', 'v', '', []); // call once the filesystem is ready
- });
- }, FileSystemReady);
- emscripten_exit_with_live_runtime(); // this will prevent the app from exiting and calling destructors, instead we'll just wait until the FS is ready
- return 0;
- }
- /******************************************************************************/
- #endif // WEB
- /******************************************************************************/
|