1 module awebview.gui.application; 2 3 import std.exception; 4 import std.file; 5 6 import awebview.wrapper.webcore; 7 import awebview.gui.resourceinterceptor; 8 9 import awebview.gui.activity; 10 import awebview.gui.html; 11 import derelict.sdl2.sdl; 12 import msgpack; 13 import carbon.utils; 14 15 import core.thread; 16 17 18 abstract class Application 19 { 20 this(string savedFileName) 21 { 22 _savedFileName = savedFileName; 23 if(exists(savedFileName)) 24 _savedData = unpack!(ubyte[][string])(cast(ubyte[])std.file.read(savedFileName)); 25 } 26 27 28 void onDestroy() 29 { 30 if(_savedData.length) 31 std.file.write(_savedFileName, pack(_savedData)); 32 } 33 34 35 final 36 @property 37 ref ubyte[][string] savedData() pure nothrow @safe @nogc { return _savedData; } 38 39 void addActivity(Activity activity); 40 41 final 42 void opOpAssign(string op : "~")(Activity activity) 43 { 44 addActivity(activity); 45 } 46 47 48 bool hasActivity(string id); 49 Activity getActivity(string id); 50 51 final 52 Activity opIndex(string id) { return getActivity(id); } 53 54 int opApplyActivities(scope int delegate(Activity activity)); 55 56 void attachActivity(string id); 57 void detachActivity(string id); 58 void destroyActivity(string id); 59 60 void runAtNextFrame(void delegate()); 61 62 void run(); 63 bool isRunning() @property; 64 65 void shutdown(); 66 67 68 69 final 70 @property 71 string exeDir() const 72 { 73 import std.path : dirName; 74 import std.file : thisExePath; 75 76 return dirName(thisExePath); 77 } 78 79 80 private: 81 ubyte[][string] _savedData; 82 string _savedFileName; 83 } 84 85 86 class SDLApplication : Application 87 { 88 static immutable savedDataFileName = "saved.mpac"; 89 90 private 91 this() 92 { 93 super(savedDataFileName); 94 } 95 96 97 static 98 SDLApplication instance() @property 99 { 100 if(_instance is null){ 101 DerelictSDL2.load(); 102 enforce(SDL_Init(SDL_INIT_VIDEO) >= 0); 103 104 _instance = new SDLApplication(); 105 106 auto config = WebConfig(); 107 config.additionalOptions ~= "--use-gl=desktop"; 108 WebCore.initialize(config); 109 WebCore.instance.resourceInterceptor = new LocalResourceInterceptor(_instance, getcwd()); 110 } 111 112 return _instance; 113 } 114 115 116 override 117 void onDestroy() 118 { 119 while(_acts.length){ 120 foreach(id, activity; _acts.maybeModified){ 121 activity.onDetach(); 122 activity.onDestroy(); 123 if(_acts[id] is activity) 124 _acts.remove(id); 125 } 126 } 127 128 while(_detachedActs.length){ 129 foreach(id, activity; _detachedActs.maybeModified){ 130 activity.onDestroy(); 131 if(_detachedActs[id] is activity) 132 _detachedActs.remove(id); 133 } 134 } 135 136 super.onDestroy(); 137 } 138 139 140 A createActivity(A : SDLActivity)(WebPreferences pref, A delegate(WebSession) dg) 141 { 142 auto session = WebCore.instance.createWebSession(WebString(""), pref); 143 144 auto act = dg(session); 145 addActivity(act); 146 147 return act; 148 } 149 150 151 SDLActivity createActivity(WebPreferences pref, HTMLPage page, string actID, uint width, uint height, string title) 152 { 153 return this.createActivity(pref, delegate(WebSession session){ 154 auto act = new SDLActivity(actID, width, height, title, session); 155 act ~= page; 156 act.load(page); 157 return act; 158 }); 159 } 160 161 162 override 163 void addActivity(Activity act) 164 in { 165 assert(typeid(act) == typeid(SDLActivity)); 166 } 167 body { 168 addActivity(cast(SDLActivity)act); 169 } 170 171 172 void addActivity(SDLActivity act) 173 in { 174 assert(act !is null); 175 } 176 body { 177 _acts[act.id] = act; 178 179 if(_isRunning){ 180 act.onStart(this); 181 act.onAttach(); 182 } 183 } 184 185 186 SDLPopupActivity initPopup(WebPreferences pref) 187 { 188 auto act = this.createActivity(pref, delegate(WebSession session){ 189 return new SDLPopupActivity(0, session); 190 }); 191 this.detachActivity(act.id); 192 _popupRoot = act; 193 return act; 194 } 195 196 197 final 198 @property 199 auto activities(this This)() pure nothrow @safe @nogc 200 { 201 static struct Result{ 202 auto opIndex(string id) { return _app.getActivity(id); } 203 auto opIndex(uint windowID) { return _app.getActivity(windowID); } 204 auto opIndex(SDL_Window* sdlWindow) { return _app.getActivity(sdlWindow); } 205 206 auto opBinaryRight(string op : "in")(string id) 207 { 208 if(auto p = id in _app._acts) 209 return p; 210 else if(auto p = id in _app._detachedActs) 211 return p; 212 else 213 return null; 214 } 215 216 private: 217 This _app; 218 } 219 220 return Result(this); 221 } 222 223 224 final override 225 bool hasActivity(string id) 226 { 227 if(auto p = id in _acts) 228 return true; 229 else if(auto p = id in _detachedActs) 230 return true; 231 else 232 return false; 233 } 234 235 236 final 237 SDLActivity getActivity(uint windowID) 238 { 239 foreach(k, a; _acts) 240 if(a.windowID == windowID) 241 return a; 242 243 foreach(k, a; _detachedActs) 244 if(a.windowID == windowID) 245 return a; 246 247 return null; 248 } 249 250 251 final override 252 SDLActivity getActivity(string id) 253 { 254 return _acts.get(id, _detachedActs.get(id, null)); 255 } 256 257 258 final 259 SDLActivity getActivity(SDL_Window* sdlWind) 260 { 261 foreach(k, a; _acts) 262 if(a.sdlWindow == sdlWind) 263 return a; 264 265 foreach(k, a; _detachedActs) 266 if(a.sdlWindow == sdlWind) 267 return a; 268 269 return null; 270 } 271 272 273 final override 274 int opApplyActivities(scope int delegate(Activity activity) dg) 275 { 276 foreach(k, ref e; _acts) 277 if(auto res = dg(e)) 278 return res; 279 280 foreach(k, ref e; _detachedActs) 281 if(auto res = dg(e)) 282 return res; 283 284 return 0; 285 } 286 287 288 final 289 @property 290 SDLPopupActivity popupActivity() 291 { 292 return _popupRoot; 293 } 294 295 296 override 297 void attachActivity(string id) 298 { 299 if(id in _acts) 300 return; 301 302 auto act = _detachedActs[id]; 303 if(_isRunning) act.onAttach(); 304 _detachedActs.remove(id); 305 _acts[id] = act; 306 } 307 308 309 override 310 void detachActivity(string id) 311 { 312 if(id in _detachedActs) 313 return; 314 315 auto act = _acts[id]; 316 if(_isRunning) act.onDetach(); 317 _acts.remove(id); 318 _detachedActs[id] = act; 319 } 320 321 322 override 323 void destroyActivity(string id) 324 { 325 if(auto p = id in _acts){ 326 auto act = *p; 327 act.onDetach(); 328 act.onDestroy(); 329 _acts.remove(id); 330 }else if(auto p = id in _detachedActs){ 331 auto act = *p; 332 act.onDestroy(); 333 _detachedActs.remove(id); 334 }else 335 enforce(0); 336 } 337 338 339 override 340 void runAtNextFrame(void delegate() dg) 341 { 342 _runNextFrame ~= dg; 343 } 344 345 346 override 347 void run() 348 { 349 _isRunning = true; 350 351 auto wc = WebCore.instance; 352 wc.update(); 353 354 foreach(k, a; _acts.maybeModified){ 355 a.onStart(this); 356 a.onAttach(); 357 } 358 359 foreach(k, a; _detachedActs.maybeModified){ 360 a.onStart(this); 361 } 362 363 LInf: 364 while(!_isShouldQuit) 365 { 366 foreach(e; _runNextFrame) 367 e(); 368 369 _runNextFrame.length = 0; 370 371 { 372 SDL_Event event; 373 while(SDL_PollEvent(&event)){ 374 onSDLEvent(&event); 375 if(_isShouldQuit) 376 break LInf; 377 } 378 } 379 380 foreach(k, a; _acts.maybeModified){ 381 a.onUpdate(); 382 383 if(a.isShouldClosed) 384 destroyActivity(a.id); 385 386 if(_isShouldQuit) 387 break LInf; 388 } 389 390 if(_acts.length == 0) 391 shutdown(); 392 393 foreach(k, a; _detachedActs.maybeModified){ 394 if(a.isShouldClosed) 395 destroyActivity(a.id); 396 397 if(_isShouldQuit) 398 break LInf; 399 } 400 401 Thread.sleep(dur!"msecs"(5)); 402 wc.update(); 403 } 404 _isRunning = false; 405 406 shutdown(); 407 } 408 409 410 override 411 @property 412 bool isRunning() { return _isRunning; } 413 414 415 override 416 void shutdown() 417 { 418 if(!_isShouldQuit && _isRunning) 419 _isShouldQuit = true; 420 else{ 421 _isRunning = false; 422 _isShouldQuit = true; 423 424 this.onDestroy(); 425 426 SDL_Quit(); 427 WebCore.shutdown(); 428 } 429 } 430 431 432 void onSDLEvent(const SDL_Event* event) 433 { 434 foreach(k, a; _acts.maybeModified) 435 a.onSDLEvent(event); 436 437 switch(event.type) 438 { 439 case SDL_QUIT: 440 shutdown(); 441 break; 442 443 default: 444 break; 445 } 446 } 447 448 449 private: 450 SDLActivity[string] _acts; 451 SDLActivity[string] _detachedActs; 452 SDLPopupActivity _popupRoot; 453 bool _isRunning; 454 bool _isShouldQuit; 455 ubyte[][string] _savedData; 456 457 void delegate()[] _runNextFrame; 458 459 static SDLApplication _instance; 460 }