Created
March 22, 2010 11:16
-
-
Save lachezar/339983 to your computer and use it in GitHub Desktop.
Web Sockets based application for realtime drawing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html xmlns="http://www.w3.org/1999/xhtml"> | |
<head> | |
<title>MultiDraw - Offline</title> | |
<meta charset="UTF-8"> | |
<script type="text/javascript"> | |
window.onload = function() { | |
try { | |
var s = new WebSocket("ws://" + document.location.host + ":6789/"); | |
s.onopen = function(e) { document.title = 'MultiDraw - Online'; } | |
s.onclose = function(e) { document.title = 'MultiDraw - Offline'; } | |
s.onmessage = function(e) { | |
var data = e.data.split(" "); | |
draw(data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]); | |
} | |
} catch (e) { | |
alert("Your browser does not support WebSockets!"); | |
} | |
canvas = document.getElementById("canvas"); | |
context = canvas.getContext("2d"); | |
isMouseDown = false; | |
x = y = -1; | |
canvas.onmousedown = function() { isMouseDown = true; }; | |
canvas.onmouseup = function() { isMouseDown = false; x = y = -1; }; | |
canvas.onmouseover = function() { document.body.style.cursor = 'crosshair'; }; | |
canvas.onmouseblur = function() { document.body.style.cursor = 'default'; }; | |
canvas.onmousemove = function(e) { | |
if (isMouseDown) { | |
var color = document.getElementById('color').value; | |
var brushSize = document.getElementById('brush_size').value; | |
var lineCap = document.getElementById('line_cap').options[document.getElementById('line_cap').selectedIndex].value; | |
var lineJoin = document.getElementById('line_join').options[document.getElementById('line_join').selectedIndex].value; | |
draw(x, y, e.pageX, e.pageY, color, brushSize, lineCap, lineJoin); | |
try { | |
s.send([x, y, e.pageX, e.pageY, color, brushSize, lineCap, lineJoin].join(' ')); | |
} catch(e) {} | |
x = e.pageX; | |
y = e.pageY; | |
}; | |
}; | |
function draw(x, y, newx, newy, color, brushSize, lineCap, lineJoin) { | |
context.strokeStyle = color.trim() || "black"; | |
context.lineWidth = brushSize.trim() || 1; | |
context.lineCap = lineCap.trim() || "butt"; | |
context.lineJoin = lineJoin.trim() || "round"; | |
context.beginPath(); | |
if (x == -1 || y == -1) { | |
x = newx; | |
y = newy; | |
} | |
context.moveTo(x, y); | |
context.lineTo(newx, newy); | |
context.closePath(); | |
context.stroke(); | |
} | |
}; | |
</script> | |
</head> | |
<body style="margin: 0; padding: 0"> | |
<canvas style="margin: 0; padding: 0; border: solid 1px #000" id="canvas" width="640" height="480" ></canvas> | |
<br/> | |
<ul style="margin: 5px 0 0 10px; padding: 0; list-style: none"> | |
<li style="margin-bottom: 3px"> | |
Color: <input type="text" id="color" value="black" list="colors" /> | |
</li> | |
<li style="margin-bottom: 3px"> | |
Brush Size: <input type="range" id="brush_size" min="1" max="32" onchange="javascript: document.getElementById('brush_size_field').innerHTML = this.value + ' px'" value="3"/> | |
<span id="brush_size_field">3 px</span> | |
</li> | |
<li style="margin-bottom: 3px"> | |
Line Cap: | |
<select id="line_cap"> | |
<option value="butt">butt</option> | |
<option value="round">round</option> | |
<option value="square">square</option> | |
</select> | |
</li> | |
<li style="margin-bottom: 3px"> | |
Line Join: | |
<select id="line_join"> | |
<option value="round">round</option> | |
<option value="bevel">bevel</option> | |
<option value="mitter">mitter</option> | |
</select> | |
</li> | |
</ul> | |
<datalist id="colors"> | |
<option value="black"/> | |
<option value="white"/> | |
<option value="grey"/> | |
<option value="red"/> | |
<option value="green"/> | |
<option value="blue"/> | |
<option value="yellow"/> | |
<option value="pink"/> | |
<option value="navy"/> | |
<option value="orange"/> | |
<option value="purple"/> | |
<option value="magenta"/> | |
<option value="cyan"/> | |
</datalist> | |
</body> | |
</html> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import socket, asyncore, sys | |
class DrawingServer(asyncore.dispatcher): | |
HANDSHAKE = ''' | |
HTTP/1.1 101 Web Socket Protocol Handshake\r | |
Upgrade: WebSocket\r | |
Connection: Upgrade\r | |
WebSocket-Origin: http://%ADDRESS%\r | |
WebSocket-Location: ws://%ADDRESS%:%PORT%/\r | |
WebSocket-Protocol: sample | |
'''.strip() + '\r\n\r\n' | |
def __init__(self, address='', port=6789): | |
self.broadcasters = [] | |
self.HANDSHAKE = self.HANDSHAKE.replace('%ADDRESS%', address or 'localhost').replace('%PORT%', str(port)) | |
asyncore.dispatcher.__init__ (self) | |
self.create_socket (socket.AF_INET, socket.SOCK_STREAM) | |
self.set_reuse_addr() | |
self.bind ((address, port)) | |
self.listen (5) | |
def handle_accept(self): | |
try: | |
sock, addr = self.accept() | |
handler = DrawingHandler(self, sock) | |
except socket.error: | |
print 'warning: server accept() threw an exception' | |
return | |
except TypeError: | |
print 'warning: server accept() threw EWOULDBLOCK' | |
return | |
class DrawingHandler(asyncore.dispatcher): | |
def __init__(self, server, sock): | |
asyncore.dispatcher.__init__(self, sock=sock) | |
self.server = server | |
self.server.broadcasters.append(self) | |
self.queue = [self.server.HANDSHAKE] | |
def handle_read(self): | |
response = self.recv(4096)[1:-1] | |
if (response): | |
commands = ['\x00' + c + '\xff' for c in response.split('\xff\x00')] | |
for b in self.server.broadcasters: | |
if (b != self): | |
#print repr(commands) | |
b.queue += commands | |
def handle_write(self): | |
if self.queue: | |
message = self.queue.pop(0) | |
print "outgoing: " + repr(message) | |
self.sendall(message) | |
def writable(self): | |
return len(self.queue) > 0 | |
def handle_close(self): | |
self.close() | |
if __name__ == '__main__': | |
address = sys.argv[1] if len(sys.argv) > 1 else '' | |
server = DrawingServer(address or '') | |
asyncore.loop() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Put those files in one folder, then run the Python's https server:
python -m SimpleHTTPServer 80
And this server:
python ./MultiDrawServer.py <your_ip_address_or_localhost>