Skip to content

Instantly share code, notes, and snippets.

@lachezar
Created March 22, 2010 11:16
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lachezar/339983 to your computer and use it in GitHub Desktop.
Save lachezar/339983 to your computer and use it in GitHub Desktop.
Web Sockets based application for realtime drawing
<!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>
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()
@lachezar
Copy link
Author

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment