|
| 1 | +package drawing |
| 2 | + |
| 3 | +import scala.js._ |
| 4 | +import scala.virtualization.lms.common._ |
| 5 | + |
| 6 | +import java.io.PrintWriter |
| 7 | + |
| 8 | +trait MiceApi extends JSProxyBase with JSLiteral { |
| 9 | + val window: Rep[Any] |
| 10 | + |
| 11 | + type DataLiteral = JSLiteral {val data : String} |
| 12 | + def initWebSocket(): Rep[WebSocket] |
| 13 | + trait WebSocket { |
| 14 | + var onmessage: Rep[DataLiteral => Any] |
| 15 | + def send(data: Rep[String]): Rep[Unit] |
| 16 | + } |
| 17 | + implicit def repToSocket(x: Rep[WebSocket]): WebSocket = repProxy[WebSocket](x) |
| 18 | + |
| 19 | + type ActionLiteral = JSLiteral {val action: String; val id: String} |
| 20 | + type MoveLiteral = JSLiteral {val id: String; val cx: Int; val cy: Int; val w: Int; val h: Int; val color: String} |
| 21 | + |
| 22 | + def currentTime(): Rep[Int] |
| 23 | + |
| 24 | + val json: Rep[JSON] |
| 25 | + trait JSON { |
| 26 | + def stringify(literal: Rep[JSLiteral]): Rep[String] |
| 27 | + def parse[T <: JSLiteral](data: Rep[String]): Rep[T] |
| 28 | + } |
| 29 | + implicit def repToJSON(x: Rep[JSON]): JSON = repProxy[JSON](x) |
| 30 | + |
| 31 | + def jQuery(x: Rep[Any]): Rep[JQuery] |
| 32 | + trait JQuery { |
| 33 | + var length: Rep[Int] |
| 34 | + |
| 35 | + def width(): Rep[Int] |
| 36 | + def height(): Rep[Int] |
| 37 | + |
| 38 | + def append(s: Rep[String]): Rep[JQuery] |
| 39 | + def css(o: Rep[JSLiteral]): Rep[JQuery] |
| 40 | + def click(fn: Rep[JQueryEvent => Any]): Rep[JQuery] |
| 41 | + def mousemove(fn: Rep[JQueryEvent => Any]): Rep[JQuery] |
| 42 | + def remove(): Rep[JQuery] |
| 43 | + } |
| 44 | + implicit def repToJQuery(x: Rep[JQuery]): JQuery = repProxy[JQuery](x) |
| 45 | + type JQueryEvent = JSLiteral {val pageX: Int; val pageY: Int} |
| 46 | +} |
| 47 | + |
| 48 | +trait MiceApiExp extends MiceApi with JSProxyExp with JSLiteralExp with DomsExp { |
| 49 | + |
| 50 | + case object WindowVar extends Exp[Any] |
| 51 | + val window = WindowVar |
| 52 | + |
| 53 | + case object InitWebSocket extends Def[WebSocket] |
| 54 | + def initWebSocket() = reflectEffect(InitWebSocket) |
| 55 | + |
| 56 | + case object CurrentTime extends Def[Int] |
| 57 | + def currentTime() = reflectEffect(CurrentTime) |
| 58 | + |
| 59 | + case object JSONVar extends Exp[JSON] |
| 60 | + val json = JSONVar |
| 61 | + |
| 62 | + case class JQueryCall(x: Exp[Any]) extends Def[JQuery] |
| 63 | + def jQuery(x: Exp[Any]) = JQueryCall(x) |
| 64 | +} |
| 65 | + |
| 66 | +trait JSGenMiceApi extends JSGenProxy with JSGenLiteral { |
| 67 | + val IR: MiceApiExp |
| 68 | + import IR._ |
| 69 | + |
| 70 | + override def quote(x: Exp[Any]) : String = x match { |
| 71 | + case WindowVar => "window" |
| 72 | + case JSONVar => "JSON" |
| 73 | + case _ => super.quote(x) |
| 74 | + } |
| 75 | + |
| 76 | + override def emitNode(sym: Sym[Any], rhs: Def[Any]) = rhs match { |
| 77 | + case InitWebSocket => emitValDef(sym, |
| 78 | + "createSocket()") |
| 79 | + case CurrentTime => emitValDef(sym, |
| 80 | + "(new Date()).getTime()") |
| 81 | + case JQueryCall(x) => emitValDef(sym, |
| 82 | + "$(" + quote(x) + ")") |
| 83 | + case _ => super.emitNode(sym, rhs) |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +object Mice { |
| 88 | + trait DrawingProg extends MiceProg { |
| 89 | + override lazy val move = fun { (mouse: Rep[MoveLiteral]) => |
| 90 | + val canvas = document.getElementById("canvas").as[Canvas] |
| 91 | + val c = canvas.getContext("2d") |
| 92 | + val x = (mouse.cx*6)/17 |
| 93 | + val y = (mouse.cy*4)/17 |
| 94 | + log(string_plus("(", string_plus(String.valueOf(x), string_plus(", ", string_plus(String.valueOf(y), ")"))))) |
| 95 | + c.fillStyle = mouse.color |
| 96 | + c.fillRect(x, y, 5, 5) |
| 97 | + } |
| 98 | + override lazy val initialPen = false |
| 99 | + } |
| 100 | + |
| 101 | + trait CursorProg extends MiceProg { |
| 102 | + override lazy val move = fun { (mouse: Rep[MoveLiteral]) => |
| 103 | + if (jQuery("#mouse_"+mouse.id).length == 0) |
| 104 | + jQuery("body").append("<span class='mouse' id='mouse_"+mouse.id+"'><span style='display:none;' class='chat'/></span>") |
| 105 | + jQuery("#mouse_"+mouse.id).css(new JSLiteral { |
| 106 | + val left = ((jQuery(window).width() - mouse.w) / 2 + mouse.cx) + "px" |
| 107 | + val top = mouse.cy + "px" |
| 108 | + val `background-color` = mouse.color |
| 109 | + }) |
| 110 | + () |
| 111 | + } |
| 112 | + override lazy val initialPen = true |
| 113 | + } |
| 114 | + |
| 115 | + trait MiceProg extends JS with MiceApi with LiftVariables with Doms with JSDebug with Casts { |
| 116 | + val move: Rep[MoveLiteral => Unit] |
| 117 | + val initialPen: Boolean |
| 118 | + def main(): Rep[Unit] = { |
| 119 | + var penDown = initialPen |
| 120 | + |
| 121 | + val ratelimit = fun { (ms: Rep[Int]) => fun { (fn: Rep[JQueryEvent => Any]) => |
| 122 | + var last = currentTime() |
| 123 | + fun { (e: Rep[JQueryEvent]) => |
| 124 | + val now = currentTime() |
| 125 | + if (now - last > ms) { |
| 126 | + last = now |
| 127 | + fn(e) |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + val socket = initWebSocket() |
| 134 | + socket.onmessage = fun { (m: Rep[DataLiteral]) => |
| 135 | + val data = json.parse(m.data).asInstanceOf[Rep[ActionLiteral]] |
| 136 | + if (data.action == "close") |
| 137 | + jQuery("#mouse_"+data.id).remove() |
| 138 | + else if (data.action == "move") |
| 139 | + move(data.asInstanceOf[Rep[MoveLiteral]]) |
| 140 | + } |
| 141 | + |
| 142 | + if (!initialPen) { |
| 143 | + jQuery(document).click { (e: Rep[JQueryEvent]) => |
| 144 | + penDown = !penDown |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + jQuery(document).mousemove { |
| 149 | + val r = ratelimit(40) |
| 150 | + r { (e: Rep[JQueryEvent]) => |
| 151 | + if (penDown) { |
| 152 | + socket.send(json.stringify(new JSLiteral { |
| 153 | + val action = "move" |
| 154 | + val cx = e.pageX |
| 155 | + val cy = e.pageY |
| 156 | + val w = jQuery(window).width() |
| 157 | + val h = jQuery(window).height() |
| 158 | + })) |
| 159 | + } |
| 160 | + } |
| 161 | + } |
| 162 | + |
| 163 | + () |
| 164 | + } |
| 165 | + } |
| 166 | + |
| 167 | + trait MiceProgExp extends MiceProg with JSExp with MiceApiExp with LiftVariables with DomsExp with JSDebugExp with Casts |
| 168 | + |
| 169 | + def codegenDrawing(pw: PrintWriter) { |
| 170 | + new DrawingProg with MiceProgExp { self => |
| 171 | + val codegen = new JSGenOpt with JSGenMiceApi with GenDoms with JSGenDebug { val IR: self.type = self } |
| 172 | + codegen.emitSource0(main _, "main", pw) |
| 173 | + } |
| 174 | + } |
| 175 | + |
| 176 | + def codegenCursor(pw: PrintWriter) { |
| 177 | + new CursorProg with MiceProgExp { self => |
| 178 | + val codegen = new JSGenOpt with JSGenMiceApi with GenDoms with JSGenDebug { val IR: self.type = self } |
| 179 | + codegen.emitSource0(main _, "main", pw) |
| 180 | + } |
| 181 | + } |
| 182 | + |
| 183 | + def writeJs(filename: String, codegen: PrintWriter => Unit) = { |
| 184 | + val out = new PrintWriter(filename) |
| 185 | + codegen(out) |
| 186 | + out.close() |
| 187 | + } |
| 188 | +} |
| 189 | + |
| 190 | +object Main extends App { |
| 191 | + Mice.writeJs("../public/javascripts/drawing_.js", Mice.codegenDrawing) |
| 192 | + Mice.writeJs("../public/javascripts/cursor_.js", Mice.codegenCursor) |
| 193 | +} |
0 commit comments