Skip to content

Commit 5e8c143

Browse files
committed
allow remote control from web browser
1 parent 1994d3a commit 5e8c143

7 files changed

Lines changed: 353 additions & 247 deletions

File tree

app.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ var ipc = require('ipc')
33

44
var mb = menubar({
55
dir: __dirname,
6-
width: 780,
7-
height: 480
6+
width: 700,
7+
height: 300
88
})
99

1010
mb.on('ready', function ready () {
@@ -14,3 +14,7 @@ mb.on('ready', function ready () {
1414
ipc.on('terminate', function terminate (ev) {
1515
mb.app.terminate()
1616
})
17+
18+
ipc.on('resize', function resize (ev, data) {
19+
mb.window.setSize(data.width, data.height)
20+
})

create.js

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
var zlib = require('zlib')
2+
3+
var SimplePeer = require('simple-peer')
4+
var request = require('request')
5+
var ssejson = require('ssejson')
6+
7+
module.exports = function create (opts, connected) {
8+
var DEV = process.env.LOCALDEV || false
9+
// var server = 'http://catlobby.maxogden.com'
10+
var server = 'http://localhost:5005'
11+
12+
var video, videoSize
13+
14+
var ui = {}
15+
16+
ui.containers = {
17+
share: document.querySelector('.share-container'),
18+
join: document.querySelector('.join-container'),
19+
content: document.querySelector('.content-container'),
20+
choose: document.querySelector('.choose-container'),
21+
video: document.querySelector('.video-container'),
22+
sharing: document.querySelector('.sharing-container')
23+
}
24+
25+
ui.buttons = {
26+
share: document.querySelector('.share-button'),
27+
join: document.querySelector('.join-button'),
28+
copy: document.querySelector('.code-copy-button'),
29+
paste: document.querySelector('.code-paste-button'),
30+
quit: document.querySelector('.quit-button'),
31+
back: document.querySelector('.back-button')
32+
}
33+
34+
ui.inputs = {
35+
copy: document.querySelector('.code-copy-input'),
36+
paste: document.querySelector('.code-paste-input')
37+
}
38+
39+
var constraints = {
40+
audio: false,
41+
video: {
42+
mandatory: {
43+
chromeMediaSource: 'screen',
44+
maxWidth: 1280,
45+
maxHeight: 720,
46+
maxFrameRate: 15
47+
},
48+
optional: []
49+
}
50+
}
51+
52+
var app = {
53+
ui: ui,
54+
startHandshake: startHandshake,
55+
hide: hide,
56+
show: show
57+
}
58+
59+
return app
60+
61+
function startHandshake (remote) {
62+
if (remote) {
63+
var peer = new SimplePeer({ trickle: false })
64+
console.log('client peer')
65+
handleSignal(peer, remote)
66+
} else {
67+
navigator.webkitGetUserMedia(constraints, function (stream) {
68+
var peer = new SimplePeer({ initiator: true, stream: stream, trickle: false })
69+
console.log('host peer', peer)
70+
ui.inputs.copy.value = 'Loading...'
71+
handleSignal(peer, remote)
72+
}, function (e) {
73+
if (e.code === e.PERMISSION_DENIED) {
74+
console.error(e)
75+
throw new Error('SCREENSHARING PERMISSION DENIED')
76+
}
77+
})
78+
}
79+
}
80+
81+
function handleSignal (peer, remote) {
82+
window.peer = peer
83+
var pingName
84+
85+
peer.on('signal', function (data) {
86+
// sdp is ~2.5k usually, that's too big for a URL, so we zlib deflate it
87+
var stringified = JSON.stringify(data)
88+
zlib.deflate(stringified, function (err, deflated) {
89+
if (err) {
90+
ui.containers.content.innerHTML = 'Error! Please Quit'
91+
return
92+
}
93+
var connectionString = deflated.toString('base64')
94+
var code = encodeURIComponent(connectionString)
95+
console.log('sdp length', code.length)
96+
97+
// upload pong sdp
98+
if (remote) {
99+
if (!pingName) {
100+
ui.inputs.paste.value = 'Error! Please Quit'
101+
return
102+
}
103+
request.post({body: code, uri: server + '/pong/' + pingName}, function resp (err, resp, body) {
104+
if (err) {
105+
ui.inputs.paste.value = err.message
106+
return
107+
}
108+
})
109+
}
110+
111+
// upload initial sdp
112+
if (!remote) {
113+
request.post({body: code, uri: server + '/ping'}, function resp (err, resp, body) {
114+
if (err) {
115+
ui.inputs.copy.value = 'Error! ' + err.message
116+
return
117+
}
118+
var ping = JSON.parse(body)
119+
ui.inputs.copy.value = ping.name
120+
121+
// listen for sdp pongs
122+
var req = request(server + '/pongs/' + ping.name)
123+
.pipe(ssejson.parse())
124+
.on('data', function data (pong) {
125+
console.log('pong sdp length', pong.length)
126+
inflate(pong, function inflated (err, stringified) {
127+
if (err) {
128+
ui.inputs.copy.value = 'Error! Please Quit'
129+
return
130+
}
131+
peer.signal(JSON.parse(stringified.toString()))
132+
req.end()
133+
})
134+
})
135+
.on('error', function error (err) {
136+
ui.inputs.copy.value = err.message
137+
})
138+
})
139+
}
140+
})
141+
})
142+
143+
ui.buttons.paste.addEventListener('click', function (e) {
144+
console.log('paste button click')
145+
e.preventDefault()
146+
var ping = ui.inputs.paste.value
147+
ui.inputs.paste.value = 'Connecting...'
148+
if (!ping) return
149+
request({uri: server + '/ping/' + ping}, function resp (err, resp, data) {
150+
if (err) {
151+
ui.inputs.paste.value = 'Error! ' + err.message
152+
return
153+
}
154+
if (resp.statusCode !== 200) return ui.inputs.paste.value = "Invalid or expired invite code"
155+
console.log('sdp response length', data.length)
156+
inflate(data, function inflated (err, stringified) {
157+
if (err) return
158+
pingName = ping
159+
peer.signal(JSON.parse(stringified.toString()))
160+
})
161+
})
162+
})
163+
164+
peer.on('data', function (data) {
165+
console.log(JSON.stringify(data))
166+
app.hide(ui.containers.content)
167+
if (app.robot) app.robot(data)
168+
})
169+
170+
if (peer.connected) onConnect()
171+
else peer.on('connect', onConnect)
172+
173+
function onConnect () {
174+
if (connected) connected(peer, remote)
175+
app.show(ui.containers.video)
176+
app.hide(ui.containers.content)
177+
if (!remote) {
178+
app.show(ui.containers.sharing)
179+
return
180+
}
181+
console.log('start sending...')
182+
183+
window.addEventListener('mousedown', function mousedown (e) {
184+
var data = getMouseData(e)
185+
data.click = true
186+
187+
if (!DEV) peer.send(data)
188+
else console.log('not sending mousedown')
189+
})
190+
191+
window.addEventListener('keydown', function keydown (e) {
192+
var data = {
193+
keyCode: e.keyCode,
194+
shift: e.shiftKey,
195+
meta: e.metaKey,
196+
control: e.ctrlKey,
197+
alt: e.altKey
198+
}
199+
200+
if (!DEV) peer.send(data)
201+
else console.log('not sending keydown ' + e.keyCode)
202+
})
203+
204+
function getMouseData (e) {
205+
var data = {}
206+
data.clientX = e.clientX
207+
data.clientY = e.clientY
208+
209+
if (video) {
210+
videoSize = video.getBoundingClientRect()
211+
data.canvasWidth = videoSize.width
212+
data.canvasHeight = videoSize.height
213+
}
214+
215+
return data
216+
}
217+
}
218+
219+
function inflate (data, cb) {
220+
data = decodeURIComponent(data.toString())
221+
zlib.inflate(new Buffer(data, 'base64'), cb)
222+
}
223+
224+
peer.on('stream', function (stream) {
225+
video = document.createElement('video')
226+
video.src = window.URL.createObjectURL(stream)
227+
video.autoplay = true
228+
ui.containers.video.appendChild(video)
229+
app.hide(ui.containers.video)
230+
})
231+
}
232+
233+
function show (ele) {
234+
ele.classList.remove('dn')
235+
}
236+
237+
function hide (ele) {
238+
ele.classList.add('dn')
239+
ele.classList.remove('db')
240+
}
241+
}

css/screencat.css

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,8 @@
1111

1212
.cr {
1313
overflow: auto;
14-
}
14+
}
15+
16+
.video-container video {
17+
width: 100%;
18+
}

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ <h2 class="f4 ttu book">Share</h2>
5252
<div class="code-copy-button tc phs pvs pvm-ns f4 btn w-100 mw-fill">Copy</div>
5353
</div>
5454
</div>
55-
<p class="mbx">Send this code to the person you want to share your screen with</p>
55+
<p class="mbx">Send this code to the person you want to share your screen with. They can use ScreenCat.app or http://maxogden.github.io/screencat/remote in Google Chrome.</p>
5656
</div>
5757

5858
<div class="join-container dn">

0 commit comments

Comments
 (0)