diff --git a/apps/boot/ChangeLog b/apps/boot/ChangeLog index 1d8e44b72..dd47229f7 100644 --- a/apps/boot/ChangeLog +++ b/apps/boot/ChangeLog @@ -72,3 +72,6 @@ 0.61: Instead of breaking execution with an Exception when updating boot, just use if..else (fix 'Uncaught undefined') 0.62: Handle setting for configuring BLE privacy 0.63: Only set BLE `display:1` if we have a passkey +0.64: Automatically create .widcache and .clkinfocache to speed up loads + Bangle.loadWidgets overwritten with fast version on success + Refuse to work on firmware <2v16 and remove old polyfills \ No newline at end of file diff --git a/apps/boot/bootupdate.js b/apps/boot/bootupdate.js index aa4a7e7b5..416d5939c 100644 --- a/apps/boot/bootupdate.js +++ b/apps/boot/bootupdate.js @@ -12,14 +12,13 @@ if (DEBUG) { boot += "var _tm=Date.now()\n"; bootPost += "delete _tm;"; } -if (require('Storage').hash) { // new in 2v11 - helps ensure files haven't changed - let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.boot\.js/)+E.CRC32(process.env.GIT_COMMIT); - boot += `if(E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.boot\\.js/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; -} else { - let CRC = E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\.boot\.js/))+E.CRC32(process.env.GIT_COMMIT); - boot += `if(E.CRC32(require('Storage').read('setting.json'))+E.CRC32(require('Storage').list(/\\.boot\\.js/))+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; +if (FWVERSION < 216) { + E.showMessage(/*LANG*/"Please update Bangle.js firmware\n\nCurrent = "+process.env.VERSION,{title:"ERROR"}); + throw new Error("Old firmware"); } -boot += `{eval(require('Storage').read('bootupdate.js'));print("Storage Updated!")}else{\n`; +let CRC = E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\.js$/)+E.CRC32(process.env.GIT_COMMIT); +boot += `if(E.CRC32(require('Storage').read('setting.json'))+require('Storage').hash(/\\.js$/)+E.CRC32(process.env.GIT_COMMIT)!=${CRC})`; +boot += `{eval(require('Storage').read('bootupdate.js'));}else{\n`; boot += `E.setFlags({pretokenise:1});\n`; boot += `var bleServices = {}, bleServiceOptions = { uart : true};\n`; bootPost += `NRF.setServices(bleServices,bleServiceOptions);delete bleServices,bleServiceOptions;\n`; // executed after other boot code @@ -44,7 +43,7 @@ LoopbackA.setConsole(true);\n`; boot += ` Bluetooth.line=""; Bluetooth.on('data',function(d) { - var l = (Bluetooth.line + d).split(/[\\n\\r]/); + let l = (Bluetooth.line + d).split(/[\\n\\r]/); Bluetooth.line = l.pop(); l.forEach(n=>Bluetooth.emit("line",n)); }); @@ -86,11 +85,8 @@ if (s.bleprivacy || (s.passkey!==undefined && s.passkey.length==6)) { if (s.blename === false) boot+=`NRF.setAdvertising({},{showName:false});\n`; if (s.whitelist && !s.whitelist_disabled) boot+=`NRF.on('connect', function(addr) { if (!NRF.ignoreWhitelist) { let whitelist = (require('Storage').readJSON('setting.json',1)||{}).whitelist; if (NRF.resolveAddress !== undefined) { let resolvedAddr = NRF.resolveAddress(addr); if (resolvedAddr !== undefined) addr = resolvedAddr + " (resolved)"; } if (!whitelist.includes(addr)) NRF.disconnect(); }});\n`; if (s.rotate) boot+=`g.setRotation(${s.rotate&3},${s.rotate>>2});\n` // screen rotation +boot+=`Bangle.loadWidgets=function(){if(!global.WIDGETS)eval(require("Storage").read(".widcache"))};\n`; // ================================================== FIXING OLDER FIRMWARES -if (FWVERSION<215.068) // 2v15.68 and before had compass heading inverted. - boot += `Bangle.on('mag',e=>{if(!isNaN(e.heading))e.heading=360-e.heading;}); -Bangle.getCompass=(c=>(()=>{e=c();if(!isNaN(e.heading))e.heading=360-e.heading;return e;}))(Bangle.getCompass);`; - // deleting stops us getting confused by our own decl. builtins can't be deleted // this is a polyfill without fastloading capability delete Bangle.showClock; @@ -98,18 +94,10 @@ if (!Bangle.showClock) boot += `Bangle.showClock = ()=>{load(".bootcde")};\n`; delete Bangle.load; if (!Bangle.load) boot += `Bangle.load = load;\n`; let date = new Date(); -delete date.toLocalISOString; // toLocalISOString was only introduced in 2v15 -if (!date.toLocalISOString) boot += `Date.prototype.toLocalISOString = function() { - var o = this.getTimezoneOffset(); - var d = new Date(this.getTime() - o*60000); - var sign = o>0?"-":"+"; - o = Math.abs(o); - return d.toISOString().slice(0,-1)+sign+Math.floor(o/60).toString().padStart(2,0)+(o%60).toString().padStart(2,0); -};\n`; // show timings if (DEBUG) boot += `print(".boot0",0|(Date.now()-_tm),"ms");_tm=Date.now();\n` -// ================================================== BOOT.JS +// ================================================== .BOOT0 // Append *.boot.js files. // Name files with a number - eg 'foo.5.boot.js' to enforce order (lowest first). Numbered files get placed before non-numbered // These could change bleServices/bleServiceOptions if needed @@ -128,51 +116,105 @@ let bootFiles = require('Storage').list(/\.boot\.js$/).sort((a,b)=>{ }); // precalculate file size bootPost += "}"; -let fileSize = boot.length + bootPost.length; -bootFiles.forEach(bootFile=>{ - // match the size of data we're adding below in bootFiles.forEach - if (DEBUG) fileSize += 2+bootFile.length+1; // `//${bootFile}\n` comment - fileSize += require('Storage').read(bootFile).length+2; // boot code plus ";\n" - if (DEBUG) fileSize += 48+E.toJS(bootFile).length; // `print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n` -}); -// write file in chunks (so as not to use up all RAM) -require('Storage').write('.boot0',boot,0,fileSize); -let fileOffset = boot.length; -bootFiles.forEach(bootFile=>{ +let fileOffset,fileSize; +/* code to output a file, plus preable and postable +when called with dst==undefined it just increments +fileOffset so we can see ho wbig the file has to be */ +let outputFile = (dst,src,pre,post) => {"ram"; + if (DEBUG) { + if (dst) require('Storage').write(dst,`//${src}\n`,fileOffset); + fileOffset+=2+src.length+1; + } + if (pre) { + if (dst) require('Storage').write(dst,pre,fileOffset); + fileOffset+=pre.length; + } + let f = require('Storage').read(src); + if (src.endsWith("clkinfo.js") && f[0]!="(") { + /* we shouldn't have to do this but it seems sometimes (sched 0.28) folks have + used libraries which get added into the clockinfo, and we can't use them directly + to we have to rever back to eval. */ + f = `eval(require('Storage').read(${E.toJS(src)}))`; + } + if (dst) { + // we can't just write 'f' in one go because it can be too big + let len = f.length; + let offset = 0; + while (len) { + let chunk = Math.min(len, 2048); + require('Storage').write(dst,f.substr(offset, chunk),fileOffset); + fileOffset+=chunk; + offset+=chunk; + len-=chunk; + } + } else + fileOffset+=f.length; + if (dst) require('Storage').write(dst,post,fileOffset); + fileOffset+=post.length; + if (DEBUG) { + if (dst) require('Storage').write(dst,`print(${E.toJS(src)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset); + fileOffset += 48+E.toJS(src).length; + } +}; +let outputFileComplete = (dst,fn) => { // we add a semicolon so if the file is wrapped in (function(){ ... }() // with no semicolon we don't end up with (function(){ ... }()(function(){ ... }() // which would cause an error! // we write: // "//"+bootFile+"\n"+require('Storage').read(bootFile)+";\n"; // but we need to do this without ever loading everything into RAM as some - // boot files seem to be getting pretty big now. - if (DEBUG) { - require('Storage').write('.boot0',`//${bootFile}\n`,fileOffset); - fileOffset+=2+bootFile.length+1; - } - let bf = require('Storage').read(bootFile); - // we can't just write 'bf' in one go because at least in 2v13 and earlier - // Espruino wants to read the whole file into RAM first, and on Bangle.js 1 - // it can be too big (especially BTHRM). - let bflen = bf.length; - let bfoffset = 0; - while (bflen) { - let bfchunk = Math.min(bflen, 2048); - require('Storage').write('.boot0',bf.substr(bfoffset, bfchunk),fileOffset); - fileOffset+=bfchunk; - bfoffset+=bfchunk; - bflen-=bfchunk; - } - require('Storage').write('.boot0',";\n",fileOffset); - fileOffset+=2; - if (DEBUG) { - require('Storage').write('.boot0',`print(${E.toJS(bootFile)},0|(Date.now()-_tm),"ms");_tm=Date.now();\n`,fileOffset); - fileOffset += 48+E.toJS(bootFile).length - } -}); + // boot files seem to be getting pretty big now. + outputFile(dst,fn,"",";\n"); +}; +fileOffset = boot.length + bootPost.length; +bootFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.boot0',boot,0,fileSize); +fileOffset = boot.length; +bootFiles.forEach(fn=>outputFileComplete('.boot0',fn)); require('Storage').write('.boot0',bootPost,fileOffset); +delete boot,bootPost,bootFiles; +// ================================================== .WIDCACHE for widgets +let widgetFiles = require("Storage").list(/\.wid\.js$/); +let widget = `// Made by bootupdate.js\nglobal.WIDGETS={};`, widgetPost = `var W=WIDGETS;WIDGETS={}; +Object.keys(W).sort((a,b)=>(0|W[b].sortorder)-(0|W[a].sortorder)).forEach(k=>WIDGETS[k]=W[k]);`; // sort +if (DEBUG) widget+="var _tm=Date.now();"; +outputFileComplete = (dst,fn) => { + outputFile(dst,fn,"try{",`}catch(e){print(${E.toJS(fn)},e,e.stack)}\n`); +}; +fileOffset = widget.length + widgetPost.length; +widgetFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.widcache',widget,0,fileSize); +fileOffset = widget.length; +widgetFiles.forEach(fn=>outputFileComplete('.widcache',fn)); +require('Storage').write('.widcache',widgetPost,fileOffset); +delete widget,widgetPost,widgetFiles; +// ================================================== .clkinfocache for clockinfos +let ciFiles = require("Storage").list(/\.clkinfo\.js$/); +let ci = `// Made by bootupdate.js\n`; +if (DEBUG) ci+="var _tm=Date.now();"; +outputFileComplete = (dst,fn) => { + outputFile(dst,fn,"try{let a=",`(),b=menu.find(x=>x.name===a.name);if(b)b.items=b.items.concat(a.items)else menu=menu.concat(a);}catch(e){print(${E.toJS(fn)},e,e.stack)}\n`); +}; +fileOffset = ci.length; +ciFiles.forEach(fn=>outputFileComplete(undefined,fn)); // just get sizes +fileSize = fileOffset; +require('Storage').write('.clkinfocache',ci,0,fileSize); +fileOffset = ci.length; +ciFiles.forEach(fn=>outputFileComplete('.clkinfocache',fn)); +delete ci,ciFiles; +// test with require("clock_info").load() +// ================================================== END E.showMessage(/*LANG*/"Reloading..."); } // .bootcde should be run automatically after if required, since // we normally get called automatically from '.boot0' eval(require('Storage').read('.boot0')); +/* +f = require("Storage").read("sched.clkinfo.js") +if (f.startsWith("Modules.addCached")) { + +} + +*/ \ No newline at end of file diff --git a/apps/boot/metadata.json b/apps/boot/metadata.json index dcc55da58..c71de37c7 100644 --- a/apps/boot/metadata.json +++ b/apps/boot/metadata.json @@ -1,7 +1,7 @@ { "id": "boot", "name": "Bootloader", - "version": "0.63", + "version": "0.64", "description": "This is needed by Bangle.js to automatically load the clock, menu, widgets and settings", "icon": "bootloader.png", "type": "bootloader", @@ -11,6 +11,9 @@ {"name":".boot0","url":"boot0.js"}, {"name":".bootcde","url":"bootloader.js"}, {"name":"bootupdate.js","url":"bootupdate.js"} + ],"data": [ + {"name":".widcache"}, + {"name":".clkinfocache"} ], "sortorder": -10 } diff --git a/apps/clock_info/ChangeLog b/apps/clock_info/ChangeLog index 4fb438097..7d2043899 100644 --- a/apps/clock_info/ChangeLog +++ b/apps/clock_info/ChangeLog @@ -11,4 +11,5 @@ 0.10: Fix focus bug when changing focus between two clock infos 0.11: Prepend swipe listener if possible 0.12: Add drawFilledImage to allow drawing icons with a separately coloured middle -0.13: Cache loaded ClockInfos so if we have clockInfoWidget and a clock, we don't load them twice (saves ~300ms) \ No newline at end of file +0.13: Cache loaded ClockInfos so if we have clockInfoWidget and a clock, we don't load them twice (saves ~300ms) +0.14: Check for .clkinfocache and use that if exists (from boot 0.64) \ No newline at end of file diff --git a/apps/clock_info/lib.js b/apps/clock_info/lib.js index 7e0653c22..1134749c2 100644 --- a/apps/clock_info/lib.js +++ b/apps/clock_info/lib.js @@ -135,10 +135,14 @@ exports.load = function() { hide : function() { clearInterval(this.interval); delete this.interval; }, }); } - + var clkInfoCache = require('Storage').read('.clkinfocache'); + if (clkInfoCache!==undefined) { + // note: code below is included in clkinfocache by bootupdate.js + // we use clkinfocache if it exists as it's faster + eval(clkInfoCache); + } else require("Storage").list(/clkinfo.js$/).forEach(fn => { // In case there exists already a menu object b with the same name as the next // object a, we append the items. Otherwise we add the new object a to the list. - require("Storage").list(/clkinfo.js$/).forEach(fn => { try{ var a = eval(require("Storage").read(fn))(); var b = menu.find(x => x.name === a.name); diff --git a/apps/clock_info/metadata.json b/apps/clock_info/metadata.json index 46e230a60..1d9a73ce3 100644 --- a/apps/clock_info/metadata.json +++ b/apps/clock_info/metadata.json @@ -1,7 +1,7 @@ { "id": "clock_info", "name": "Clock Info Module", "shortName": "Clock Info", - "version":"0.13", + "version":"0.14", "description": "A library used by clocks to provide extra information on the clock face (Altitude, BPM, etc)", "icon": "app.png", "type": "module",