singrdk/base/Applications/Pong/Pong.cs

637 lines
20 KiB
C#

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// 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;
}
}
}