/* 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 . */ 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(_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); } /// /// Waits for the screen to be ready for access. /// 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(); _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(); _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 _sdlImlacKeymap; private static Dictionary _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 _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; } }