1
0
mirror of https://github.com/livingcomputermuseum/sImlac.git synced 2026-01-14 07:40:23 +00:00
Josh Dersch 0af475cc27 Added new commands:
"draw invisible vectors" - displays non-drawing beam motion in red.
"hide invisible vectors" - returns to normal rendring mode
"step display" - runs until the next display rendering command completes, refreshes the display to allow easier debugging of display programs.

Console input buffer is now cleared after a break operation.
2020-05-12 14:00:35 -07:00

1084 lines
37 KiB
C#

/*
This file is part of sImlac.
sImlac is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
sImlac is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with sImlac. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System.Threading;
using imlac.IO;
using SDL2;
using static SDL2.SDL;
using System.Runtime.InteropServices;
namespace imlac
{
public enum DataSwitchMappingMode
{
Toggle, // Pressing a key toggles the switch (from 0 to 1 or from 1 to 0)
Momentary, // Holding a key down indicates a "1", release indicates "0"
MomentaryInverted, // Same as above, but inverted
}
//
// Provides a console using SDL.
//
public class SDLConsole : IImlacConsole
{
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetProcessDPIAware();
public SDLConsole(float scaleFactor)
{
if (scaleFactor <= 0)
{
throw new ArgumentOutOfRangeException("scaleFactor");
}
_scaleFactor = scaleFactor;
_throttleFramerate = true;
_lock = new ReaderWriterLockSlim();
_keyLatchedLock = new ReaderWriterLockSlim();
_syncEvent = new AutoResetEvent(false);
_frame = 0;
try
{
_frameTimer = new FrameTimer(40);
}
catch
{
// Unable to initialize frame timer, we will not be able
// to throttle execution.
_frameTimer = null;
}
_fullScreen = false;
_displayList = new List<Vector>(_displayListSize);
_displayListIndex = 0;
_blink = false;
_intensity = 1.0f;
//
// Prepopulate the display list with Vectors. Only those used in the current frame are
// actually rendered, we prepopulate the list to prevent having to cons up new ones
// constantly.
//
for (int i = 0; i < _displayListSize; i++)
{
_displayList.Add(new Vector(DrawingMode.Off, 1.0f, false, 0, 0, 0, 0));
}
BuildKeyMappings();
}
public bool NewKeyPressed
{
get
{
_keyLatchedLock.EnterReadLock();
bool pressed = _newKeyPressed;
_keyLatchedLock.ExitReadLock();
return pressed;
}
}
public ImlacKey Key
{
get { return _currentKeyCode; }
}
public ImlacKeyModifiers KeyModifiers
{
get { return _keyModifiers; }
}
public void UnlatchKey()
{
_keyLatchedLock.EnterReadLock();
_newKeyPressed = false;
_keyLatchedLock.ExitReadLock();
}
public ushort DataSwitches
{
get { return (ushort)_dataSwitches; }
}
public int MouseX
{
get { return _mouseX; }
}
public int MouseY
{
get { return _mouseY; }
}
public bool ThrottleFramerate
{
get { return _throttleFramerate; }
set { _throttleFramerate = value; }
}
public bool DataSwitchMappingEnabled
{
get { return _dataSwitchMappingEnabled; }
set { _dataSwitchMappingEnabled = value; }
}
public DataSwitchMappingMode DataSwitchMode
{
get { return _dataSwitchMappingMode; }
set { _dataSwitchMappingMode = value; }
}
public bool FullScreen
{
get { return _fullScreen; }
set
{
if (value != _fullScreen)
{
_fullScreen = value;
UpdateScreenMode();
}
}
}
public void Shutdown()
{
//
// Tell the SDL event loop to wrap things up.
//
_userEvent.type = SDL.SDL_EventType.SDL_QUIT;
SDL.SDL_PushEvent(ref _userEvent);
}
/// <summary>
/// Waits for the screen to be ready for access.
/// </summary>
public void WaitForSync()
{
_syncEvent.WaitOne();
}
public void Show()
{
ShowInternal();
}
public void ClearDisplay()
{
_lock.EnterWriteLock();
_displayListIndex = 0;
_lock.ExitWriteLock();
RenderCurrent(true);
}
public void SetScale(float scale)
{
if (scale <= 0)
{
throw new ArgumentOutOfRangeException("scale");
}
_scaleFactor = scale;
UpdateDisplayScale();
}
public void SetIntensity(int intensity)
{
_intensity = ((float)intensity / 16.0f);
}
public void SetBlink(bool on)
{
_blink = on;
}
public void MapDataSwitch(uint switchNumber, VKeys key)
{
_dataSwitchMappings[switchNumber] = key;
}
public VKeys GetDataSwitchMapping(uint switchNumber)
{
return _dataSwitchMappings[switchNumber];
}
private void InitializeSDL()
{
try
{
// Set high DPI awareness on Windows platforms, if this fails it's OK.
SetProcessDPIAware();
}
catch
{
// Nothing to do.
}
DoUpdateDisplayScale();
int retVal = 0;
// Get SDL humming
if ((retVal = SDL.SDL_Init(SDL.SDL_INIT_VIDEO)) < 0)
{
throw new InvalidOperationException(String.Format("SDL_Init failed. Error {0:x}", retVal));
}
//
if (SDL.SDL_SetHint(SDL.SDL_HINT_RENDER_SCALE_QUALITY, "0") == SDL.SDL_bool.SDL_FALSE)
{
throw new InvalidOperationException("SDL_SetHint failed to set scale quality.");
}
SDL.SDL_WindowFlags flags = SDL.SDL_WindowFlags.SDL_WINDOW_ALLOW_HIGHDPI | SDL.SDL_WindowFlags.SDL_WINDOW_SHOWN;
_sdlWindow = SDL.SDL_CreateWindow(
"Imlac PDS-1",
SDL.SDL_WINDOWPOS_UNDEFINED,
SDL.SDL_WINDOWPOS_UNDEFINED,
_xResolution,
_yResolution,
_fullScreen ? SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP | flags : flags);
if (_sdlWindow == IntPtr.Zero)
{
throw new InvalidOperationException("SDL_CreateWindow failed.");
}
_sdlRenderer = SDL.SDL_CreateRenderer(_sdlWindow, -1, SDL.SDL_RendererFlags.SDL_RENDERER_ACCELERATED);
if (_sdlRenderer == IntPtr.Zero)
{
// Fall back to software
_sdlRenderer = SDL.SDL_CreateRenderer(_sdlWindow, -1, SDL.SDL_RendererFlags.SDL_RENDERER_SOFTWARE);
if (_sdlRenderer == IntPtr.Zero)
{
// Still no luck.
throw new InvalidOperationException("SDL_CreateRenderer failed.");
}
}
SDL.SDL_SetRenderDrawBlendMode(_sdlRenderer, SDL.SDL_BlendMode.SDL_BLENDMODE_BLEND);
// Clear the screen.
SDL.SDL_SetRenderDrawColor(_sdlRenderer, 0, 0, 0, 0xff);
SDL.SDL_RenderFillRect(_sdlRenderer, ref _displayRect);
SDL.SDL_RenderPresent(_sdlRenderer);
// Register a User event for rendering and resizing.
_userEventType = SDL.SDL_RegisterEvents(1);
_userEvent = new SDL.SDL_Event();
_userEvent.type = SDL.SDL_EventType.SDL_USEREVENT;
_userEvent.user.type = _userEventType;
}
private void UpdateDisplayScale()
{
//
// Send a render event to the SDL message loop so that things
// will get rendered.
//
_userEvent.user.code = (int)UserEventType.Resize;
SDL.SDL_PushEvent(ref _userEvent);
}
private void DoUpdateDisplayScale()
{
_xResolution = (int)(2048.0 * _scaleFactor);
_yResolution = (int)(2048.0 * _scaleFactor);
_displayRect = new SDL.SDL_Rect();
_displayRect.x = 0;
_displayRect.y = 0;
_displayRect.h = (int)_yResolution;
_displayRect.w = (int)_xResolution;
if (_sdlWindow != null)
{
_lock.EnterWriteLock();
SDL.SDL_SetWindowSize(_sdlWindow, _xResolution, _yResolution);
SDL.SDL_SetWindowFullscreen(_sdlWindow,
_fullScreen ? (uint)SDL.SDL_WindowFlags.SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
//
// Calculate x/y offsets to center display rendering
///
int newWidth = 0;
int newHeight = 0;
SDL.SDL_GetWindowSize(_sdlWindow, out newWidth, out newHeight);
_xOffset = Math.Max(0, (newWidth - _xResolution) / 2);
_yOffset = Math.Max(0, (newHeight - _yResolution) / 2);
_displayRect.h = newHeight == 0 ? _yResolution : newHeight;
_displayRect.w = newWidth == 0 ? _xResolution : newWidth;
// Clear the display list so no garbage remains.
_displayListIndex = 0;
_lock.ExitWriteLock();
}
}
public void MoveAbsolute(int x, int y, DrawingMode mode)
{
//
// Take coordinates as an 11-bit quantity (0-2048) even though we may not be displaying the full resolution.
//
if (mode != DrawingMode.Off)
{
AddNewVector(mode, _x, _y, x, y);
}
else if(Configuration.ShowInvisibleVectors)
{
AddNewVector(DrawingMode.Debug, _x, _y, x, y);
}
_x = x;
_y = y;
}
public void DrawPoint(int x, int y)
{
_x = x;
_y = y;
AddNewVector(DrawingMode.Point, x, y, x, y);
}
public void FrameDone()
{
RenderCurrent(true);
//
// Sync to 40hz framerate
//
if (_throttleFramerate && _frameTimer != null)
{
_frameTimer.WaitForFrame();
}
}
private void ShowInternal()
{
InitializeSDL();
// Signal that the display is ready.
_syncEvent.Set();
bool quit = false;
while (!quit)
{
SDL.SDL_Event e;
//
// Run main message loop
//
while (SDL.SDL_WaitEvent(out e) != 0)
{
switch (e.type)
{
case SDL.SDL_EventType.SDL_USEREVENT:
if (e.user.code == (int)UserEventType.Render)
{
DoRender(_renderCompleteFrame);
}
else if (e.user.code == (int)UserEventType.Resize)
{
DoUpdateDisplayScale();
}
break;
case SDL.SDL_EventType.SDL_QUIT:
quit = true;
return;
case SDL.SDL_EventType.SDL_KEYDOWN:
SdlKeyDown(e.key.keysym.sym, e.key.repeat > 0);
break;
case SDL.SDL_EventType.SDL_KEYUP:
SdlKeyUp(e.key.keysym.sym);
break;
case SDL.SDL_EventType.SDL_MOUSEMOTION:
SdlMouseMove(e.motion.x, e.motion.y);
break;
}
}
SDL.SDL_Delay(0);
}
//
// Done, clean up.
//
SDL.SDL_DestroyRenderer(_sdlRenderer);
SDL.SDL_DestroyWindow(_sdlWindow);
SDL.SDL_Quit();
}
private void SdlKeyDown(SDL_Keycode key, bool repeat)
{
switch(key)
{
case SDL_Keycode.SDLK_LSHIFT:
case SDL_Keycode.SDLK_RSHIFT:
_keyModifiers |= ImlacKeyModifiers.Shift;
break;
case SDL_Keycode.SDLK_LCTRL:
case SDL_Keycode.SDLK_RCTRL:
_keyModifiers |= ImlacKeyModifiers.Ctrl;
break;
case SDL_Keycode.SDLK_LALT:
case SDL_Keycode.SDLK_RALT:
_keyModifiers |= ImlacKeyModifiers.Rept;
break;
default:
UpdateDataSwitches(key, true /* key down */);
if (key == SDL_Keycode.SDLK_INSERT && !repeat)
{
FullScreenToggle();
break;
}
// If this is a repeated keystroke, we'll only log it if
// the Repeat key (Alt) is being held down.
if (repeat && (_keyModifiers & ImlacKeyModifiers.Rept) == 0)
{
break;
}
_keyLatchedLock.EnterWriteLock();
// Only accept this key if we're not waiting
// for the Imlac to read the last one.
if (!_newKeyPressed)
{
ImlacKey newCode = TranslateKeyCode(key);
// Only latch valid keys.
if (newCode != ImlacKey.Invalid)
{
_newKeyPressed = true;
_currentKeyCode = newCode;
}
}
if (!repeat)
{
// Only count new keystrokes.
_keyPressedCount++;
}
_keyLatchedLock.ExitWriteLock();
break;
}
}
private void SdlKeyUp(SDL.SDL_Keycode key)
{
switch (key)
{
case SDL_Keycode.SDLK_LSHIFT:
case SDL_Keycode.SDLK_RSHIFT:
_keyModifiers &= ~ImlacKeyModifiers.Shift;
break;
case SDL_Keycode.SDLK_LCTRL:
case SDL_Keycode.SDLK_RCTRL:
_keyModifiers &= ~ImlacKeyModifiers.Ctrl;
break;
case SDL_Keycode.SDLK_LALT:
case SDL_Keycode.SDLK_RALT:
_keyModifiers &= ~ImlacKeyModifiers.Rept;
break;
default:
UpdateDataSwitches(key, false /* key up */);
_keyLatchedLock.EnterWriteLock();
//
// Decrement our pressed keycount, when this reaches zero
// this means the last pressed key has been released and we
// can set the keycode to None to indicate no keys being down.
// (This avoids issues with n-key rollover.)
//
_keyPressedCount--;
if (_keyPressedCount == 0)
{
_currentKeyCode = ImlacKey.None;
}
_keyLatchedLock.ExitWriteLock();
break;
}
}
private void SdlMouseMove(int x, int y)
{
_mouseX = x;
_mouseY = y;
}
void FullScreenToggle()
{
_fullScreen = !_fullScreen;
UpdateScreenMode();
}
public void RenderCurrent(bool completeFrame)
{
//
// Send a render event to the SDL message loop so that things
// will get rendered.
//
_renderCompleteFrame = completeFrame;
_userEvent.user.code = (int)UserEventType.Render;
SDL.SDL_PushEvent(ref _userEvent);
//
// Wait for rendering to complete before returning.
//
WaitForSync();
}
public void DoRender(bool completeFrame)
{
// Draw the current set of vectors
_lock.EnterReadLock();
_frame++;
// Blinking items are on for 16 frames, off for 16.
if ((_frame % 16) == 0)
{
_frameBlink = !_frameBlink;
}
//
// If we're drawing a complete frame (not running in debug mode)
// fade out the last frame by drawing an alpha-blended black rectangle over the display.
// (slow persistence phosphor simulation!)
// Otherwise clear the display completely.
//
if (completeFrame)
{
SDL.SDL_SetRenderDrawColor(_sdlRenderer, 0, 0, 0, 32);
}
else
{
SDL.SDL_SetRenderDrawColor(_sdlRenderer, 0, 0, 0, 0xff);
}
SDL.SDL_RenderFillRect(_sdlRenderer, ref _displayRect);
// And draw in this frame's vectors
for (int i = 0; i < _displayListIndex; i++)
{
_displayList[i].Draw(_sdlRenderer, _frameBlink);
}
SDL.SDL_RenderPresent(_sdlRenderer);
if (completeFrame)
{
_displayListIndex = 0;
}
_lock.ExitReadLock();
//
// Indicate that we're through rendering.
//
_syncEvent.Set();
}
private void AddNewVector(DrawingMode mode, int startX, int startY, int endX, int endY)
{
//
// Scale the vector to the current scaling factor.
// The Imlac specifies 11 bits of resolution (2048 points in X and Y)
// which corresponds to a _scaleFactor of 1.0.
//
startX = (int)(startX * _scaleFactor + _xOffset);
startY = (int)(startY *_scaleFactor - _yOffset);
endX = (int)(endX * _scaleFactor + _xOffset);
endY = (int)(endY * _scaleFactor - _yOffset);
_lock.EnterWriteLock();
Vector newVector = _displayList[_displayListIndex];
newVector.Modify(mode, _intensity, _blink, (short)startX, (short)(_yResolution - startY), (short)endX, (short)(_yResolution - endY));
_displayListIndex++;
_lock.ExitWriteLock();
}
private void UpdateScreenMode()
{
UpdateDisplayScale();
}
private class Vector
{
static Vector()
{
_rng = new Random();
}
public Vector(DrawingMode mode, float intensity, bool blink, uint startX, uint startY, uint endX, uint endY)
{
_mode = mode;
_intensity = intensity;
_blink = blink;
_x1 = (int)startX;
_y1 = (int)startY;
_x2 = (int)endX;
_y2 = (int)endY;
UpdateColor();
}
public void Modify(DrawingMode mode, float intensity, bool blink, short startX, short startY, short endX, short endY)
{
if (_mode != mode)
{
_mode = mode;
}
_intensity = intensity;
_blink = blink;
_x1 = (int)(startX + Perturbation());
_y1 = (int)(startY + Perturbation());
_x2 = (int)(endX + Perturbation());
_y2 = (int)(endY + Perturbation());
UpdateColor();
}
public void Draw(IntPtr sdlRenderer, bool blinkOn)
{
// TODO: handle dotted lines, line thickness options
if (!_blink || blinkOn)
{
SDL.SDL_SetRenderDrawColor(
sdlRenderer,
(byte)(_color.R * _intensity),
(byte)(_color.G * _intensity),
(byte)(_color.B * _intensity),
_color.A);
SDL.SDL_RenderDrawLine(sdlRenderer, _x1, _y1, _x2, _y2);
}
}
private int Perturbation()
{
if (Configuration.SquiggleMode)
{
double factor = 2.5;
return (int)(_rng.NextDouble() * factor - (factor / 2.0));
}
else
{
return 0;
}
}
private void UpdateColor()
{
// Special case for single point vectors.
if (_mode != DrawingMode.Off &&
_x1 == _x2 &&
_y1 == _y2)
{
_mode = DrawingMode.Point;
}
switch (_mode)
{
case DrawingMode.Normal:
_color = NormalColor;
break;
case DrawingMode.Point:
case DrawingMode.Dotted:
_color = PointColor;
break;
case DrawingMode.SGR1:
_color = SGRColor;
break;
case DrawingMode.Debug:
_color = DebugColor;
break;
}
}
private DrawingMode _mode;
private Color _color;
private float _intensity;
private bool _blink;
private int _x1;
private int _y1;
private int _x2;
private int _y2;
private static Color NormalColor = Color.FromArgb(255, Color.GreenYellow);
private static Color PointColor = Color.FromArgb(255, Color.GreenYellow);
private static Color SGRColor = Color.FromArgb(127, Color.DarkGreen);
private static Color DebugColor = Color.FromArgb(127, Color.Red);
private static Random _rng;
}
private static void BuildKeyMappings()
{
_sdlImlacKeymap = new Dictionary<SDL_Keycode, ImlacKey>();
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_END, ImlacKey.DataXmit);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_DOWN, ImlacKey.Down);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_RIGHT, ImlacKey.Right);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_UP, ImlacKey.Up);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_LEFT, ImlacKey.Left);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_TAB, ImlacKey.Tab);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_RETURN, ImlacKey.CR);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_PAGEUP, ImlacKey.FF);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_RIGHTBRACKET, ImlacKey.LF);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_PAGEDOWN, ImlacKey.PageXmit);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_HOME, ImlacKey.Home);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_PAUSE, ImlacKey.Brk);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_ESCAPE, ImlacKey.Esc);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_SPACE, ImlacKey.Space);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_COMMA, ImlacKey.Comma);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_EQUALS, ImlacKey.Minus);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_PERIOD, ImlacKey.Period);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_SLASH, ImlacKey.Slash);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_0, ImlacKey.K0);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_1, ImlacKey.K1);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_2, ImlacKey.K2);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_3, ImlacKey.K3);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_4, ImlacKey.K4);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_5, ImlacKey.K5);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_6, ImlacKey.K6);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_7, ImlacKey.K7);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_8, ImlacKey.K8);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_9, ImlacKey.K9);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_MINUS, ImlacKey.Colon);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_SEMICOLON, ImlacKey.Semicolon);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_KP_0, ImlacKey.D0);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_KP_2, ImlacKey.D2);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_KP_4, ImlacKey.D4);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_KP_5, ImlacKey.D5);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_KP_6, ImlacKey.D6);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_QUOTE, ImlacKey.Unlabeled);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_a, ImlacKey.A);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_b, ImlacKey.B);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_c, ImlacKey.C);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_d, ImlacKey.D);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_e, ImlacKey.E);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_f, ImlacKey.F);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_g, ImlacKey.G);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_h, ImlacKey.H);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_i, ImlacKey.I);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_j, ImlacKey.J);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_k, ImlacKey.K);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_l, ImlacKey.L);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_m, ImlacKey.M);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_n, ImlacKey.N);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_o, ImlacKey.O);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_p, ImlacKey.P);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_q, ImlacKey.Q);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_r, ImlacKey.R);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_s, ImlacKey.S);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_t, ImlacKey.T);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_u, ImlacKey.U);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_v, ImlacKey.V);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_w, ImlacKey.W);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_x, ImlacKey.X);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_y, ImlacKey.Y);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_z, ImlacKey.Z);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_DELETE, ImlacKey.Del);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_BACKSPACE, ImlacKey.Del);
_sdlImlacKeymap.Add(SDL_Keycode.SDLK_BACKSLASH, ImlacKey.Brk);
_sdlVKeymap = new Dictionary<SDL_Keycode, VKeys>();
_sdlVKeymap.Add(SDL_Keycode.SDLK_END, VKeys.End);
_sdlVKeymap.Add(SDL_Keycode.SDLK_DOWN, VKeys.DownArrow);
_sdlVKeymap.Add(SDL_Keycode.SDLK_RIGHT, VKeys.RightArrow);
_sdlVKeymap.Add(SDL_Keycode.SDLK_UP, VKeys.UpArrow);
_sdlVKeymap.Add(SDL_Keycode.SDLK_LEFT, VKeys.LeftArrow);
_sdlVKeymap.Add(SDL_Keycode.SDLK_TAB, VKeys.Tab);
_sdlVKeymap.Add(SDL_Keycode.SDLK_RETURN, VKeys.Return);
_sdlVKeymap.Add(SDL_Keycode.SDLK_PAGEUP, VKeys.PageUp);
_sdlVKeymap.Add(SDL_Keycode.SDLK_PAGEDOWN, VKeys.PageDown);
_sdlVKeymap.Add(SDL_Keycode.SDLK_HOME, VKeys.Home);
_sdlVKeymap.Add(SDL_Keycode.SDLK_PAUSE, VKeys.Pause);
_sdlVKeymap.Add(SDL_Keycode.SDLK_ESCAPE, VKeys.Escape);
_sdlVKeymap.Add(SDL_Keycode.SDLK_SPACE, VKeys.Space);
_sdlVKeymap.Add(SDL_Keycode.SDLK_COMMA, VKeys.Comma);
_sdlVKeymap.Add(SDL_Keycode.SDLK_PLUS, VKeys.Plus);
_sdlVKeymap.Add(SDL_Keycode.SDLK_PERIOD, VKeys.Period);
_sdlVKeymap.Add(SDL_Keycode.SDLK_QUESTION, VKeys.QuestionMark);
_sdlVKeymap.Add(SDL_Keycode.SDLK_0,VKeys.Zero);
_sdlVKeymap.Add(SDL_Keycode.SDLK_1,VKeys.One);
_sdlVKeymap.Add(SDL_Keycode.SDLK_2,VKeys.Two);
_sdlVKeymap.Add(SDL_Keycode.SDLK_3,VKeys.Three);
_sdlVKeymap.Add(SDL_Keycode.SDLK_4,VKeys.Four);
_sdlVKeymap.Add(SDL_Keycode.SDLK_5,VKeys.Five);
_sdlVKeymap.Add(SDL_Keycode.SDLK_6,VKeys.Six);
_sdlVKeymap.Add(SDL_Keycode.SDLK_7,VKeys.Seven);
_sdlVKeymap.Add(SDL_Keycode.SDLK_8,VKeys.Eight);
_sdlVKeymap.Add(SDL_Keycode.SDLK_9,VKeys.Nine);
_sdlVKeymap.Add(SDL_Keycode.SDLK_MINUS, VKeys.Minus);
_sdlVKeymap.Add(SDL_Keycode.SDLK_SEMICOLON, VKeys.Semicolon);
_sdlVKeymap.Add(SDL_Keycode.SDLK_KP_0, VKeys.Keypad0);
_sdlVKeymap.Add(SDL_Keycode.SDLK_KP_2, VKeys.Keypad2);
_sdlVKeymap.Add(SDL_Keycode.SDLK_KP_4, VKeys.Keypad4);
_sdlVKeymap.Add(SDL_Keycode.SDLK_KP_5, VKeys.Keypad5);
_sdlVKeymap.Add(SDL_Keycode.SDLK_KP_6, VKeys.Keypad6);
_sdlVKeymap.Add(SDL_Keycode.SDLK_QUOTEDBL, VKeys.DoubleQuote);
_sdlVKeymap.Add(SDL_Keycode.SDLK_a,VKeys.A);
_sdlVKeymap.Add(SDL_Keycode.SDLK_b,VKeys.B);
_sdlVKeymap.Add(SDL_Keycode.SDLK_c,VKeys.C);
_sdlVKeymap.Add(SDL_Keycode.SDLK_d,VKeys.D);
_sdlVKeymap.Add(SDL_Keycode.SDLK_e,VKeys.E);
_sdlVKeymap.Add(SDL_Keycode.SDLK_f,VKeys.F);
_sdlVKeymap.Add(SDL_Keycode.SDLK_g,VKeys.G);
_sdlVKeymap.Add(SDL_Keycode.SDLK_h,VKeys.H);
_sdlVKeymap.Add(SDL_Keycode.SDLK_i,VKeys.I);
_sdlVKeymap.Add(SDL_Keycode.SDLK_j,VKeys.J);
_sdlVKeymap.Add(SDL_Keycode.SDLK_k,VKeys.K);
_sdlVKeymap.Add(SDL_Keycode.SDLK_l,VKeys.L);
_sdlVKeymap.Add(SDL_Keycode.SDLK_m,VKeys.M);
_sdlVKeymap.Add(SDL_Keycode.SDLK_n,VKeys.N);
_sdlVKeymap.Add(SDL_Keycode.SDLK_o,VKeys.O);
_sdlVKeymap.Add(SDL_Keycode.SDLK_p,VKeys.P);
_sdlVKeymap.Add(SDL_Keycode.SDLK_q,VKeys.Q);
_sdlVKeymap.Add(SDL_Keycode.SDLK_r,VKeys.R);
_sdlVKeymap.Add(SDL_Keycode.SDLK_s,VKeys.S);
_sdlVKeymap.Add(SDL_Keycode.SDLK_t,VKeys.T);
_sdlVKeymap.Add(SDL_Keycode.SDLK_u,VKeys.U);
_sdlVKeymap.Add(SDL_Keycode.SDLK_v,VKeys.V);
_sdlVKeymap.Add(SDL_Keycode.SDLK_w,VKeys.W);
_sdlVKeymap.Add(SDL_Keycode.SDLK_x,VKeys.X);
_sdlVKeymap.Add(SDL_Keycode.SDLK_y,VKeys.Y);
_sdlVKeymap.Add(SDL_Keycode.SDLK_z,VKeys.Z);
_sdlVKeymap.Add(SDL_Keycode.SDLK_DELETE, VKeys.Delete);
}
private ImlacKey TranslateKeyCode(SDL_Keycode virtualKey)
{
if (_sdlImlacKeymap.ContainsKey(virtualKey))
{
return _sdlImlacKeymap[virtualKey];
}
else
{
return ImlacKey.Invalid;
}
}
private void UpdateDataSwitches(SDL_Keycode sdlKey, bool keyDown)
{
if (_sdlVKeymap.ContainsKey(sdlKey))
{
VKeys virtualKey = _sdlVKeymap[sdlKey];
//
// If this is a key mapped to a front panel switch
// we will toggle the bit in the DS register based on whether
// the key is down or up and the specified mapping mode.
//
for (int i = 0; i < 16; i++)
{
if (_dataSwitchMappings[i] == VKeys.None0)
{
_dataSwitches &= ~(0x1 << (15 - i));
}
else if (_dataSwitchMappings[i] == VKeys.None1)
{
_dataSwitches |= (0x1 << (15 - i));
}
else if (virtualKey == _dataSwitchMappings[i])
{
switch (_dataSwitchMappingMode)
{
case DataSwitchMappingMode.Momentary:
case DataSwitchMappingMode.MomentaryInverted:
if (_dataSwitchMappingMode == DataSwitchMappingMode.MomentaryInverted)
{
// Invert the sense
keyDown = !keyDown;
}
// toggle this bit
if (keyDown)
{
// or it in
_dataSwitches |= (0x1 << (15 - i));
}
else
{
// mask it out
_dataSwitches &= ~(0x1 << (15 - i));
}
break;
case DataSwitchMappingMode.Toggle:
if (keyDown)
{
// toggle it
_dataSwitches ^= (0x1 << (15 - i));
}
break;
}
}
}
}
}
private static Dictionary<SDL_Keycode, ImlacKey> _sdlImlacKeymap;
private static Dictionary<SDL_Keycode, VKeys> _sdlVKeymap;
private ImlacKey _currentKeyCode;
private ImlacKeyModifiers _keyModifiers;
private int _keyPressedCount;
private bool _newKeyPressed;
private ReaderWriterLockSlim _keyLatchedLock;
// Data switch mappings:
// There are 16 switches mapped here, a value of None0 or None1 means
// that no key is mapped and to hardcode return value to 0 or 1
// The first entry corresponds to bit 0 (MSB)
// and the last entry corresponds to bit 15 (LSB)
private VKeys[] _dataSwitchMappings =
{
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
VKeys.None0,
};
private int _dataSwitches;
private DataSwitchMappingMode _dataSwitchMappingMode;
//
// Mouse Data
//
private int _mouseX;
private int _mouseY;
private int _mouseButtons;
//
// SDL
//
private IntPtr _sdlWindow = IntPtr.Zero;
private IntPtr _sdlRenderer = IntPtr.Zero;
private SDL.SDL_Rect _displayRect;
//
// SDL User events
//
private UInt32 _userEventType;
private SDL.SDL_Event _userEvent;
private enum UserEventType
{
Render = 0,
Resize
}
private AutoResetEvent _syncEvent;
private int _x;
private int _y;
private int _xResolution;
private int _yResolution;
private float _scaleFactor;
private float _intensity;
private bool _blink; // The Blink attribute is applied to vectors while this is set.
private bool _frameBlink; // Blink'ed vectors are blanked while this is set (toggled every 16 frames).
private int _xOffset;
private int _yOffset;
private bool _renderCompleteFrame;
private bool _fullScreen;
private bool _throttleFramerate;
private bool _dataSwitchMappingEnabled;
private int _displayListIndex;
private List<Vector> _displayList;
private const int _displayListSize = 100000; // Considerably more than a real Imlac could ever hope to draw in a single frame.
private System.Threading.ReaderWriterLockSlim _lock;
private uint _frame;
// Framerate management
FrameTimer _frameTimer;
}
}