//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Pong.cs // // Note: // using System; using System.Text; using System.GC; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Threading; using System.Collections; using System.Diagnostics; using Microsoft.Singularity; using Microsoft.Singularity.Directory; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.Io; using Microsoft.Singularity.Channels; using Keyboard = Microsoft.Singularity.Io.Keyboard; namespace Microsoft.Singularity.Shell { public class Pong { const int screenWidth = 1024; const int screenHeight = 768; const int paddleWidth = 16; const int paddleHeight = 128; const int ballSize = 8; const double paddleSpeed = 0.20; const double ballSpeed = 0.35; const double ballStartAngle = Math.PI / 8; const double ballMaxAngle = Math.PI / 3; const int screenMinX = 0; const int screenMinY = 0; const int screenMaxX = screenMinX + screenWidth - 1; const int screenMaxY = screenMinY + screenHeight - 1; const int paddleMin = screenMinY; const int paddleMax = screenMaxY - paddleHeight; const int ballMinX = screenMinX + paddleWidth; const int ballMaxX = screenMaxX - paddleWidth - ballSize; const int ballMinY = screenMinY; const int ballMaxY = screenMaxY - ballSize; const int paddleInit = screenMinY + ((screenHeight - paddleHeight) / 2); double ballX = 0.0; double ballY = 0.0; double ballAngle = 0.0; int ballDrawX = 0; int ballDrawY = 0; double[] paddlesY = new double [2] { paddleInit, paddleInit }; int[] paddlesDrawX = new int [2] { screenMinX, screenMaxX - paddleWidth + 1 }; int[] paddlesDrawY = new int [2] { paddleInit, paddleInit }; bool[] inputUp = new bool [2] { false, false }; bool[] inputDown = new bool [2] { false, false }; int[] scores = new int [2] { 0, 0 }; int lastWinner = 0; bool done = false; bool playing = false; ISvgaDevice svga = null; Random random = new Random(); public static KeyboardDeviceContract.Imp OpenKeyboard(string! devName) { KeyboardDeviceContract.Exp! exp; KeyboardDeviceContract.Imp! imp; KeyboardDeviceContract.NewChannel(out imp, out exp); // get NS endpoint DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); bool success = false; ns.SendBind(Bitter.FromString2(devName),exp); switch receive { case ns.AckBind(): success = true; break; case ns.NakBind(exp): delete exp; break; case ns.ChannelClosed(): break; } if (!success) { DebugStub.Print("OpenKeyboard lookup of {0} failed.\n", __arglist(devName); delete imp; delete ns; return null; } switch receive { case imp.Success(): break; case unsatisfiable: throw new Exception("Didn't imp.RecvAckConnect"); break; } delete ns; return imp; } public static VideoDeviceContract.Imp OpenVideo(string! devName) { VideoDeviceContract.Exp! exp; VideoDeviceContract.Imp! imp; VideoDeviceContract.NewChannel(out imp, out exp); // get NS endpoint DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint(); bool success = false; ns.SendBind(Bitter.FromString2(devName),exp); switch receive { case ns.AckBind(): success = true; break; case ns.NakBind(reject): delete reject; break; case ns.ChannelClosed(): break; } if (!success) { DebugStub.Print("OpenVideo lookup of {0} failed.\n", __arglist(devName)); delete imp; delete ns; return null; } switch receive { case imp.Success(): break; case unsatisfiable: throw new Exception("Didn't imp.RecvAckConnect"); break; } delete ns; return imp; } public static int Main(string[] args) { VideoDeviceContract.Imp video = OpenVideo("/dev/video"); Console.WriteLine("Singularity Slide Show Viewer (PID={0})", ProcessService.GetCurrentProcessId()); if (video == null) { Console.WriteLine("Can only display slides on a graphic display."); return 1; } if (Slides.Content == null || Slides.Content.Length == 0) { Console.WriteLine("No slides to display."); delete video; return 1; } KeyboardDeviceContract.Imp keyboard = OpenKeyboard("/dev/keyboard"); if (keyboard == null) { // TODO: show keyboardless slides with timer? delete video; return 1; } int slide = 0; byte[] currentSlide = (!)Slides.Content[slide]; byte * opt(ExHeap[]) current = new[ExHeap] byte [currentSlide.Length]; Bitter.FromByteArray(current, 0, currentSlide.Length, currentSlide, 0); video.SendFill(0, 0, 1023, 767, 0); video.RecvAckFill(); video.SendBitBltBmp(32, 16, current); video.RecvAckBitBltBmp(out current); for (;;) { int x; int y; bool go_back = false; bool go_fore = false; bool go_home = false; bool go_end = false; uint key = 0; keyboard.SendGetKey(); switch receive { case keyboard.AckKey(ikey): key = ikey; break; case keyboard.NakKey(): break; case unsatisfiable: throw new Exception("Didn't get reply from Keyboard"); } if (key == 0) { Tracing.Log(Tracing.Warning, "GetKey failed."); break; } if ((key & (uint)Keyboard.Qualifiers.KEY_DOWN) == 0) { continue; } if ((key & (uint)Keyboard.Qualifiers.KEY_MOUSE) == 0) { char c = (char)(key & (uint)Keyboard.Qualifiers.KEY_BASE_CODE); switch (c) { case (char)Keyboard.Keys.ESCAPE: delete video; delete keyboard; delete current; return 0; case (char)Keyboard.Keys.HOME: go_home = true; break; case (char)Keyboard.Keys.UP_ARROW: case (char)Keyboard.Keys.LEFT_ARROW: case (char)Keyboard.Keys.PAGE_UP: case '\b': go_back = true; break; case (char)Keyboard.Keys.DOWN_ARROW: case (char)Keyboard.Keys.RIGHT_ARROW: case (char)Keyboard.Keys.PAGE_DOWN: case '\n': case ' ': go_fore = true; break; case (char)Keyboard.Keys.END: go_end = true; break; default: continue; } } else { if ((key & (uint)Keyboard.Qualifiers.MOUSE_BUTTON_1) != 0) { go_fore = true; } else if ((key & (uint)Keyboard.Qualifiers.MOUSE_BUTTON_0) != 0) { go_back = true; } else { continue; } } if (go_home) { slide = 0; } else if (go_fore) { if (++slide >= Slides.Content.Length) { slide = Slides.Content.Length - 1; } } else if (go_back) { if (--slide < 0) { slide = 0; } } else if (go_end) { slide = Slides.Content.Length - 1; } if (Slides.Content.Length > 0) { currentSlide = (!)Slides.Content[slide]; if (current.Length < currentSlide.Length) { delete current; current = new[ExHeap] byte [currentSlide.Length]; } Bitter.FromByteArray(current, 0, currentSlide.Length, currentSlide, 0); video.SendFill(0, 0, 1023, 767, 0); video.RecvAckFill(); video.SendBitBltBmp(32, 16, current); video.RecvAckBitBltBmp(out current); } else { video.SendFill(0, 0, 1023, 767, 0); video.RecvAckFill(); } } delete keyboard; delete video; delete current; return 0; } public Pong() { svga = IoSystem.FindDeviceWithInterface(typeof(ISvgaDevice)) as ISvgaDevice; } public void DrawPaddle(int paddle, bool erase) { svga.Fill(paddlesDrawX[paddle], paddlesDrawY[paddle], paddlesDrawX[paddle] + paddleWidth - 1, paddlesDrawY[paddle] + paddleHeight - 1, erase ? RGB.Black : RGB.Blue); } public void RedrawPaddle(int paddle) { if (paddlesDrawY[paddle] != (int) paddlesY[paddle]) { DrawPaddle(paddle, true); paddlesDrawY[paddle] = (int) paddlesY[paddle]; DrawPaddle(paddle, false); } } public void MovePaddle(int paddle) { paddlesY[paddle] += GetPaddleState(paddle) * paddleSpeed; if (paddlesY[paddle] > paddleMax) { paddlesY[paddle] = paddleMax; } else if (paddlesY[paddle] < paddleMin) { paddlesY[paddle] = paddleMin; } RedrawPaddle(paddle); } public bool PaddleHit(int paddle) { return ((ballY > paddlesY[paddle] - ballSize) && (ballY < paddlesY[paddle] + paddleHeight)); } public void ResetBall() { ballY = screenMinY + ((screenHeight - ballSize) / 2); ballAngle = (random.NextDouble() * ballStartAngle) - (ballStartAngle / 2); if (lastWinner == 0) { ballX = ballMinX; if (ballAngle < 0) { ballAngle += 2 * Math.PI; } } else { ballX = ballMaxX; ballAngle += Math.PI; } ballDrawX = (int) ballX; ballDrawY = (int) ballY; } public void DrawBall(bool erase) { svga.Fill(ballDrawX, ballDrawY, ballDrawX + ballSize - 1, ballDrawY + ballSize - 1, erase ? RGB.Black : RGB.Red); } public void RedrawBall() { if (ballDrawX != (int) ballX || ballDrawY != (int) ballY) { DrawBall(true); ballDrawX = (int) ballX; ballDrawY = (int) ballY; DrawBall(false); } } public void ReflectBallY(int line) { ballY = (2 * line) - ballY; ballAngle = (2 * Math.PI) - ballAngle; } public void SetAngle(int paddle) { // Find out where the ball hit the paddle, from -1 to 1. double ratio = ((ballY + (ballSize / 2)) - (paddlesY[paddle] + (paddleHeight / 2))) / ((paddleHeight + ballSize) / 2); // Set the new angle accordingly. ballAngle = ratio * ballMaxAngle; if (paddle == 0) { if (ballAngle < 0) { ballAngle += 2 * Math.PI; } } else { ballAngle = Math.PI - ballAngle; } } public void MoveBall() { ballY += Math.Sin(ballAngle) * ballSpeed; if (ballY < ballMinY) { ReflectBallY(ballMinY); } else if (ballY > ballMaxY) { ReflectBallY(ballMaxY); } ballX += Math.Cos(ballAngle) * ballSpeed; if (ballX < ballMinX) { if (PaddleHit(0)) { SetAngle(0); } else { lastWinner = 1; playing = false; } } else if (ballX > ballMaxX) { if (PaddleHit(1)) { SetAngle(1); } else { lastWinner = 0; playing = false; } } RedrawBall(); } public int GetPaddleState(int paddle) { int result; if (inputUp[paddle]) { result = -1; } else if (inputDown[paddle]) { result = 1; } else { result = 0; } return result; } public void SetStateFromChar(char c, bool @state) { switch (c) { case (char)Keyboard.Keys.UP_ARROW: inputUp[1] = @state; break; case (char)Keyboard.Keys.DOWN_ARROW: inputDown[1] = @state; break; case 'a': inputUp[0] = @state; break; case 'z': inputDown[0] = @state; break; } } public void GetInput(KeyboardDeviceContract.Imp keyboard) { uint key = 0; keyboard.SendPollKey(); switch receive { case keyboard.AckKey(ikey): key = ikey; break; case keyboard.NakKey(): key = 0; break; case unsatisfiable: throw new Exception("Didn't get reply from Keyboard"); } char c = (char) (key & (uint) Keyboard.Qualifiers.KEY_BASE_CODE); if ((key & (uint) Keyboard.Qualifiers.KEY_DOWN) != 0) { switch (c) { case (char) Keyboard.Keys.ESCAPE: done = true; break; case ' ': if (!playing) { ResetBall(); ClearScreen(); DrawPaddle(0, false); DrawPaddle(1, false); DrawBall(false); playing = true; } break; default: SetStateFromChar(c, true); break; } } else if ((key & (uint) Keyboard.Qualifiers.KEY_UP) != 0) { SetStateFromChar(c, false); } } public void FillScreen(RGB color) { svga.Fill(screenMinX, screenMinY, screenMaxX, screenMaxY, color); } public void ClearScreen() { FillScreen(RGB.Black); } public void FlashScreen() { FillScreen(RGB.Red); Sleep(1000); FillScreen(RGB.Green); Sleep(1000); FillScreen(RGB.Blue); Sleep(1000); } public void ShowMessage() { ClearScreen(); for (int i = 0; i < 100; i++) { Console.WriteLine(); } Console.Write(" "); Console.Write("Left: {0,-10}", scores[0]); Console.Write(" "); Console.Write("Right: {0,-10}", scores[1]); Console.WriteLine(); Console.WriteLine(); Console.Write(" "); Console.Write("Press space to begin or escape to quit..."); Console.WriteLine(); DrawPaddle(0, false); DrawPaddle(1, false); DrawBall(false); } public void Play() { KeyboardDeviceContract.Imp keyboard = Shell.OpenKeyboard("/dev/keyboard"); Console.CursorHide(); ShowMessage(); while (!done) { GetInput(keyboard); if (playing) { MovePaddle(0); MovePaddle(1); MoveBall(); if (!playing) { scores[lastWinner]++; FlashScreen(); ShowMessage(); } } Sleep(10); } ClearScreen(); Console.CursorShow(); } public void Sleep(int multiplier) { for (int j = 0; j < multiplier; j++) { for (int i = 0; i < 10000; i++) { // Do nothing. } } } public static int Run(string[] args) { (new Pong()).Play(); return 0; } } }