// Presentor by 7kasper (Kasper Müller) // Version 2.2 const SpecialReport = new Uint8Array([ 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x02, // USAGE (Mouse) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x01, // REPORT_ID (1) 0x09, 0x01, // USAGE (Pointer) 0xa1, 0x00, // COLLECTION (Physical) 0x05, 0x09, // USAGE_PAGE (Button) 0x19, 0x01, // USAGE_MINIMUM (Button 1) 0x29, 0x05, // USAGE_MAXIMUM (Button 5) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x01, // REPORT_SIZE (1) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x03, // REPORT_SIZE (3) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x30, // USAGE (X) 0x09, 0x31, // USAGE (Y) 0x09, 0x38, // USAGE (Wheel) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x03, // REPORT_COUNT (3) 0x81, 0x06, // INPUT (Data,Var,Rel) 0x05, 0x0c, // USAGE_PAGE (Consumer Devices) 0x0a, 0x38, 0x02, // USAGE (AC Pan) 0x15, 0x81, // LOGICAL_MINIMUM (-127) 0x25, 0x7f, // LOGICAL_MAXIMUM (127) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x06, // INPUT (Data,Var,Rel) 0xc0, // END_COLLECTION 0xc0, // END_COLLECTION 0x05, 0x01, // USAGE_PAGE (Generic Desktop) 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x02, // REPORT_ID (2) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x75, 0x08, // REPORT_SIZE (8) 0x95, 0x01, // REPORT_COUNT (1) 0x81, 0x01, // INPUT (Cnst,Ary,Abs) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x73, // USAGE_MAXIMUM (Keyboard F24) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x73, // LOGICAL_MAXIMUM (115) 0x95, 0x05, // REPORT_COUNT (5) 0x75, 0x08, // REPORT_SIZE (8) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0 // END_COLLECTION ]); const MouseButton = { NONE : 0, LEFT : 1, RIGHT : 2, MIDDLE : 4, BACK : 8, FORWARD: 16 }; const kb = require("ble_hid_keyboard"); const Layout = require("Layout"); const Locale = require("locale"); let mainLayout = new Layout( { 'type': 'v', filly: 1, c: [ { type: 'txt', font: '6x8', label: 'Presentor', valign: -1, halign: 0, col: g.theme.fg1, // bgCol: g.theme.bg2, bgCol: '#00F', fillx: 1, }, { type: 'h', fillx: 1, c: [ { type: 'txt', font: '15%', label: '00:00', id: 'Time', halign: -1, pad: 3 }, { fillx: 1 }, { type: 'txt', font: '15%', label: '00:00', id: 'Timer', halign: 1, pad: 3 } ] }, { type: 'txt', font: '10%', label: '+00:00', id: 'RestTime', col: '#fff' }, { type: 'txt', font: '10%', label: '--------------' }, { type: 'txt', font: '15%', label: 'Subject', id: 'Subject' }, { type: 'txt', font: '6x8', label: 'Notes...', id: 'Notes', col: '#ff0', fillx: 1, filly: 1, valign: 1 } ] }, {lazy:true}); let settings = require("Storage").readJSON('presentor.json'); let HIDenabled = false; // Application variables let pparti = 0; let lastx = 0; let lasty = 0; let timeoutId = -1; let holding = false; let timeoutHolding = -1; let timeoutDraw = -1; let homeRoll = 0; let homePitch = 0; let trackPadMode = false; let mCal = 0; let clearToSend = true; let mttl = 0; let cttl = 0; function drawMainFrame() { var d = new Date(); // update time mainLayout.Time.label = Locale.time(d,1); mainLayout.render(); // schedule a draw for the next minute if (timeoutDraw != -1) clearTimeout(timeoutDraw); timeoutDraw = setTimeout(function() { timeoutDraw = -1; drawMainFrame(); }, 60000 - (Date.now() % 60000)); } function drawMain() { g.clear(); mainLayout.forgetLazyState(); drawMainFrame(); // mainLayout.render(); // E.showMessage('Presentor'); } NRF.setServices(undefined, { hid : SpecialReport }); // TODO: figure out how to detect HID. NRF.on('HID', function() { HIDenabled = true; }); function moveMouse(x,y,b,wheel,hwheel,callback) { if (!HIDenabled) return; if (!b) b = 0; if (!wheel) wheel = 0; if (!hwheel) hwheel = 0; NRF.sendHIDReport([1,b,x,y,wheel,hwheel,0,0], function() { if (callback) callback(); }); } // function getSign(x) { // return ((x > 0) - (x < 0)) || +x; // } function scroll(wheel,hwheel,callback) { moveMouse(0,0,0,wheel,hwheel,callback); } // Single click a certain button (immidiatly release). function clickMouse(b, callback) { if (!HIDenabled) return; NRF.sendHIDReport([1,b,0,0,0,0,0,0], function() { NRF.sendHIDReport([1,0,0,0,0,0,0,0], function() { if (callback) callback(); }); }); } function pressKey(keyCode, modifiers, callback) { if (!HIDenabled) return; if (!modifiers) modifiers = 0; NRF.sendHIDReport([2, modifiers,0,keyCode,0,0,0,0], function() { NRF.sendHIDReport([2,0,0,0,0,0,0,0], function() { if (callback) callback(); }); }); } function handleAcc(acc) { let rRoll = acc.y * -50; let rPitch = acc.x * -100; if (mCal > 10) { //console.log("x: " + (rRoll - homeRoll) + " y:" + (rPitch - homePitch)); moveMouse(acc.y * -50 - homeRoll, acc.x * -100 - homePitch); } else { //console.log("homeroll: " +homeRoll +"homepitch: " + homePitch); homeRoll = rRoll * 0.7 + homeRoll * 0.3; homePitch = rPitch * 0.7 + homePitch * 0.3; mCal = mCal + 1; } } Bangle.on('lock', function(on) { if (on && holding) { Bangle.setLocked(false); Bangle.setLCDPower(1); } }); function startHolding() { pressKey(kb.KEY.F10); holding = true; Bangle.buzz(); E.showMessage('Holding'); Bangle.on('accel', handleAcc); Bangle.setLCDPower(1); } function stopHolding() { clearTimeout(timeoutId); if (holding) { pressKey(kb.KEY.F10); homePitch = 0; homeRoll = 0; holding = false; mCal = 0; Bangle.removeListener('accel', handleAcc); Bangle.buzz(); drawMain(); } else { timeoutId = setTimeout(drawMain, 1000); } clearTimeout(timeoutHolding); timeoutHolding = -1; } Bangle.on('drag', function(e) { if (cttl == 0) { cttl = getTime(); } if (trackPadMode) { if (lastx + lasty == 0) { lastx = e.x; lasty = e.y; mttl = getTime(); } if (clearToSend) { clearToSend = false; let difX = e.x - lastx, difY = e.y - lasty; let dT = getTime() - mttl; let vX = difX / dT, vY = difY / dT; //let qX = getSign(difX) * Math.pow(Math.abs(difX), 1.2); //let qY = getSign(difY) * Math.pow(Math.abs(difY), 1.2); let qX = difX + 0.02 * vX, qY = difY + 0.02 * vY; moveMouse(qX, qY, 0, 0, 0, function() { setTimeout(function() {clearToSend = true;}, 50); }); lastx = e.x; lasty = e.y; mttl = getTime(); console.log("Dx: " + (qX) + " Dy: " + (qY)); } if (!e.b) { // short press if (getTime() - cttl < 0.2) { clickMouse(MouseButton.LEFT); console.log("click left"); } // longer press in center else if (getTime() - cttl < 0.6 && e.x > g.getWidth()/4 && e.x < 3 * g.getWidth()/4 && e.y > g.getHeight() / 4 && e.y < 3 * g.getHeight() / 4) { clickMouse(MouseButton.RIGHT); console.log("click right"); } cttl = 0; lastx = 0; lasty = 0; } } else { if(!e.b){ Bangle.buzz(100); if(lasty > 40){ E.showMessage('down'); scroll(0,1); } else if(lasty < -40){ E.showMessage(kb.KEY.A); pressKey(kb.KEY.A); } else if(lastx > 40){ E.showMessage('right'); //kb.tap(kb.KEY.RIGHT, 0); scroll(-1); } else if(lastx < -40){ E.showMessage('left'); //kb.tap(kb.KEY.LEFT, 0); scroll(1); } else if(lastx==0 && lasty==0 && holding == false){ E.showMessage('press'); clickMouse(MouseButton.LEFT); } stopHolding(); lastx = 0; lasty = 0; } else{ lastx = lastx + e.dx; lasty = lasty + e.dy; if (timeoutHolding == -1) { timeoutHolding = setTimeout(startHolding, 500); } } } }); function onBtn() { if (trackPadMode) { trackPadMode = false; stopHolding(); drawMain(); } else { clearToSend = true; trackPadMode = true; E.showMessage('Mouse'); } Bangle.buzz(); } setWatch(onBtn, (process.env.HWVERSION==2) ? BTN1 : BTN2, {repeat: true}); drawMain();