Package org.javatari.pc.screen

Source Code of org.javatari.pc.screen.Monitor

// Copyright 2011-2012 Paulo Augusto Peccin. See licence.txt distributed with this file.

package org.javatari.pc.screen;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.security.AccessControlException;
import java.util.Arrays;
import java.util.List;

import javax.jnlp.FileContents;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;

import org.javatari.atari.cartridge.Cartridge;
import org.javatari.atari.cartridge.CartridgeInsertionListener;
import org.javatari.atari.cartridge.CartridgeSocket;
import org.javatari.atari.console.savestate.SaveStateSocket;
import org.javatari.general.av.video.VideoMonitor;
import org.javatari.general.av.video.VideoSignal;
import org.javatari.general.av.video.VideoStandard;
import org.javatari.general.board.Clock;
import org.javatari.general.board.ClockDriven;
import org.javatari.parameters.Parameters;
import org.javatari.pc.cartridge.FileROMChooser;
import org.javatari.pc.cartridge.FileServiceROMChooser;
import org.javatari.pc.cartridge.ROMLoader;
import org.javatari.pc.cartridge.URLROMChooser;
import org.javatari.utils.Environment;
import org.javatari.utils.SwingHelper;

public final class Monitor implements ClockDriven, VideoMonitor, CartridgeInsertionListener {
 
  public Monitor() {
    super();
    this.fps = DEFAULT_FPS;
    init();
  }

  public void connect(VideoSignal videoSignal, CartridgeSocket cartridgeSocket, SaveStateSocket savestateSocket) {
    this.cartridgeSocket = cartridgeSocket;
    this.savestateSocket = savestateSocket;
    cartridgeSocket.addInsertionListener(this);
    this.videoSignal = videoSignal;
    videoSignal.connectMonitor(this);
    adjustToVideoSignal();
  }

  public synchronized void setDisplay(MonitorDisplay monitorDisplay) {
    display = monitorDisplay;
    float scX = display.displayDefaultOpenningScaleX(displayWidth, displayHeight);
    setDisplayScale(scX, scX / DEFAULT_SCALE_ASPECT_X);
    displayCenter();
  }
 
  public void setFixedSize(boolean fixed) {
    fixedSizeMode = fixed;
  }
 
  public boolean isFixedSize() {
    return fixedSizeMode;
  }

  public void setCartridgeChangeEnabled(boolean state) {
    cartridgeChangeEnabled = state;
  }

  public boolean isCartridgeChangeEnabled() {
    return cartridgeChangeEnabled;
  }

  public void addControlInputComponents(List<Component> inputs) {
    monitorControls.addInputComponents(inputs);
  }
 
  public void powerOn() {
    synchronized(refreshMonitor) {
      cleanFrontBuffer();
      cleanBackBuffer();
      powerOn = true;
      signalState(false);
      clock.go();
    }
  }

  public void powerOff() {
    synchronized(refreshMonitor) {
      clock.pause();
      powerOn = false;
      signalState(false);
    }
  }

  public void destroy() {
    synchronized(refreshMonitor) {
      clock.terminate();
    }
  }

  @Override
  // Synchronize to avoid changing the standard while receiving lines
  public synchronized  boolean nextLine(final int[] pixels, boolean vSynchSignal) {
    // Adjusts to the new signal state (on or off) as necessary
    if (!signalState(pixels != null))    // If signal is off, we are done
      return false;
    // Process new line received
    boolean vSynched = false;
    if (line < signalHeight) {
      // Copy only contents that will be displayed
      if (line >= displayOriginY && line < displayOriginY + displayHeight)
        System.arraycopy(pixels, displayOriginX, backBuffer, (line - displayOriginY) * displayWidth, displayWidth);
    } else
      vSynched = maxLineExceeded();
    line++;
    if (videoStandardDetected == null) videoStandardDetectionFrameLineCount++;
    if (vSynchSignal) {
      if (videoStandardDetected == null) videoStandardDetectionNewFrame();
      vSynched = newFrame() || vSynched;
    }
    return vSynched;
  }

  @Override
  public VideoStandard videoStandardDetected() {
    return videoStandardDetected;
  }

  @Override
  public void videoStandardDetectionStart() {
    videoStandardDetected = null;
    videoStandardDetectionFrameCount = 0;
    videoStandardDetectionFrameLineCount = 0;   
  }

  @Override
  public int currentLine() {
    return line;
  }

  @Override
  public void synchOutput() {
    if (fps < 0) clock.interrupt();    // Just ask for a refresh if in Adaptive mode
    else synchOutputInSwing();
  }

  @Override
  public void showOSD(String message, boolean overlap) {
    if (!overlap && osdFramesLeft > 0) return;
    osdMessage = message;
    osdFramesLeft = message == null ? 0 : OSD_FRAMES;
  }
 
  @Override
  public void clockPulse() {
    synchOutputInSwing();
    // If in "On Demand" mode (fps < 0) then just wait for the next frame to interrupt the sleep, but no more than 2 frames
    if (fps < 0 && !Thread.interrupted()) try { Thread.sleep(1000 / 60 * 20); } catch (InterruptedException e) { /* Awake! */ };
  }

  public void cartridgeInsert(Cartridge cart, boolean autoPower) {
    cartridgeSocket.insert(cart, autoPower);
    display.displayRequestFocus();
  };

  @Override
  public void cartridgeInserted(Cartridge cartridge) {
    // Only change mode if not forced
    if (CRT_MODE >= 0) return;
    if (crtMode == 0 || crtMode == 1)
      setCrtMode(cartridge == null ? 0 : cartridge.getInfo().crtMode == -1 ? 0 : cartridge.getInfo().crtMode);
  }

  private void synchOutputInSwing() {
    if (BUFFER_SYNC_WAIT) SwingHelper.edtSmartInvokeAndWait(refresher);
    else SwingHelper.edtInvokeLater(refresher);
  }

  private boolean newFrame() {
    if (line < signalHeight - VSYNC_TOLERANCE) return false;

    // Flip front and back buffers
    int[] aux = frontBuffer;
    frontBuffer = backBuffer;
    backBuffer = aux;
   
    // Start a new frame
    if (debug > 0) cleanBackBuffer();
    if (showStats) showOSD(videoSignal.standard() + "  " + line + " lines,  CRT mode: " + crtModeNames[crtMode], true);
    line = 0;
    frame++;
    return true;
  }

  private boolean maxLineExceeded() {
    if (line > signalHeight + VSYNC_TOLERANCE + EXTRA_UPPER_VSYNC_TOLERANCE) {
      // if (debug > 0) System.out.println("Display maximum scanlines exceeded: " + line);
      return newFrame();
    }
    return false;
  }
 
  private boolean signalState(boolean state) {
    if (state) {
      signalOn = true;
      adjustToVideoSignal();
    } else {
      signalOn = false;
      adjustToVideoSignalOff();
    }
    return state;
  }

  private void cleanFrontBuffer() {
    Arrays.fill(frontBuffer, Color.BLACK.getRGB());    
  }

  private void cleanBackBuffer() {
    // If in debug mode, put a nice green for detection of undrawn lines
    Arrays.fill(backBuffer, debug > 0 ? Color.GREEN.getRGB() : Color.BLACK.getRGB());    
  }

  private void videoStandardDetectionNewFrame() {
    int linesCount = videoStandardDetectionFrameLineCount;
    videoStandardDetectionFrameLineCount = 0;
    // Only consider frames with linesCount in range with tolerances (NTSC 262, PAL 312)
    if ((linesCount >= 250 && linesCount <= 281)
        || (linesCount >= 300 && linesCount <= 325))
      if (++videoStandardDetectionFrameCount >= 5)
        videoStandardDetectionFinish(linesCount);
  }

  private void videoStandardDetectionFinish(int linesCount) {
    videoStandardDetected = linesCount < 290 ? VideoStandard.NTSC : VideoStandard.PAL;
   
    // Compute an additional number of lines to make the display bigger, if needed
    // Only used when the detected number of lines per frame is bigger than standard by a reasonable amount
    int prevAdd = videoStandardDetectionAdtLinesPerFrame;
    int newAdd = linesCount - videoStandardDetected.height;
    if (newAdd > 2) newAdd = (newAdd > 6 ? 6 : newAdd) - 2;
    else newAdd = 0;

    // Only sets size now if additional lines changed
    if (newAdd != prevAdd) {
      videoStandardDetectionAdtLinesPerFrame = newAdd;
      adjustToVideoStandard(videoStandardDetected);
    }
  }

  private void displayUpdateSize() {
    if (display == null) return;
    synchronized (refreshMonitor) {
      Dimension size = new Dimension((int) (displayWidth * displayScaleX), (int) (displayHeight * displayScaleY));
      display.displaySize(size);
      display.displayMinimumSize(new Dimension((int) (displayWidth * DEFAULT_SCALE_X / DEFAULT_SCALE_Y), displayHeight));
    }
  }

  private void displayCenter() {
    if (display == null) return;
    synchronized (refreshMonitor) {
      display.displayCenter();
    }
  }

  private Graphics2D displayGraphics() {
    if (display == null) return null;
    Graphics2D displayGraphics = display.displayGraphics();
    displayGraphics.setComposite(AlphaComposite.Src);
    // Adjusts the Render Quality if needed
    if (displayGraphics != null && crtFilter)
      displayGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    return displayGraphics;
  }

  private void displayFrameFinished(Graphics2D graphics) {
    if (display == null) return;
    display.displayFinishFrame(graphics);
  }

  private void init() {
    monitorControls = new MonitorControls(this);
    prepareResources();    
    adjustToVideoStandard(VideoStandard.NTSC);
    setDisplayDefaultSize()
    clock = new Clock("Video Monitor", this, fps);
    paintLogo();
  }

  private void prepareResources() {
    // Prepare Buffers and FrameImage with maximum possible sizes (PAL)
    backBuffer = new int[VideoStandard.PAL.width * VideoStandard.PAL.height];
    frontBuffer = new int[VideoStandard.PAL.width * VideoStandard.PAL.height];
    frameImage = new BufferedImage(VideoStandard.PAL.width, VideoStandard.PAL.height, BufferedImage.TYPE_INT_ARGB);
    if (FRAME_ACCELERATION >= 0) frameImage.setAccelerationPriority(FRAME_ACCELERATION);
    // Prepare the Logo image
    try {
      logoIcon = SwingHelper.loadAsCompatibleImage("org/javatari/pc/screen/images/Logo.png");
    } catch (IOException e) {}
    // Prepare the OSD paint component
    osdComponent = new JLabel();
    osdComponent.setForeground(Color.GREEN);
    osdComponent.setBackground(new Color(0x50000000, true));
    osdComponent.setFont(new Font(Environment.ARIAL_FONT ? "Arial"
        : Environment.LIBERATION_FONT ? "Liberation Sans" : "SansSerif", Font.BOLD, 15));
    osdComponent.setBorder(new EmptyBorder(5, 12, 5, 12));
    osdComponent.setOpaque(true);
    // Prepare CRT mode 2 texture
    scanlinesTextureImage = new BufferedImage(2048, 1280, BufferedImage.TYPE_INT_ARGB_PRE);
    Graphics2D g = scanlinesTextureImage.createGraphics();
    g.setColor(new Color((int)(SCANLINES_STRENGTH * 255) << 24, true));
    for(int i = 1; i < scanlinesTextureImage.getHeight(); i += 2)
      g.drawLine(0, i, scanlinesTextureImage.getWidth(), i);
    g.dispose();
    if (SCANLINES_ACCELERATION >= 0) scanlinesTextureImage.setAccelerationPriority(SCANLINES_ACCELERATION);
    // Prepare CRT mode 3 and 4 composition
    crtTriadComposite = new CRTTriadComposite();
    // Prepare intermediate image for CRT modes or OSD rendering in SingleBuffer mode
    intermFrameImage = new BufferedImage(2048, 1280, BufferedImage.TYPE_INT_RGB);
    if (IMTERM_FRAME_ACCELERATION >= 0) intermFrameImage.setAccelerationPriority(IMTERM_FRAME_ACCELERATION);
  }

  private void adjustToVideoSignal() {
    if (signalStandard != videoSignal.standard())
      adjustToVideoStandard(videoSignal.standard());
  }

  // Synchronize to avoid changing the standard while refreshing frame or receiving lines
  private synchronized void adjustToVideoStandard(VideoStandard videoStandard) {
    synchronized (refreshMonitor) {
      signalStandard = videoStandard;
      signalWidth = videoStandard.width;
      signalHeight = videoStandard.height;
      setDisplaySize(displayWidth, displayHeightPct);
      setDisplayOrigin(displayOriginX, displayOriginYPct);
    }
  }

  private void adjustToVideoSignalOff() {
    line = 0;
    display.displayClear();
    paintLogo();
    if (fps == 0 && powerOn && signalOffRefresher == null) {
      SignalOffRefresher ref = new SignalOffRefresher();
      signalOffRefresher = ref;
      ref.start();
    }
  }

  private void paintLogo() {
    synchronized (refreshMonitor) {
      Graphics2D canvasGraphics = displayGraphics();
      if (canvasGraphics == null) return;
      Dimension ces = display.displayEffectiveSize();
      int w = ces.width;
      int h = ces.height;
      Graphics2D intermGraphics = intermFrameImage.createGraphics();
      intermGraphics.setBackground(Color.BLACK);
      intermGraphics.clearRect(0, 0, w, h);
      int lw = logoIcon.getWidth(null);
      int lh = logoIcon.getHeight(null);
      float r = h < lh ? (float)h / lh : 1;
      lw *= r; lh *= r;
      intermGraphics.drawImage(logoIcon, (w - lw) / 2, (h - lh) / 2, lw, lh, null);
      paintOSD(intermGraphics);
      canvasGraphics.drawImage(intermFrameImage, 0, 0, w, h, 0, 0, w, h, null);
      intermGraphics.dispose();
      displayFrameFinished(canvasGraphics);
    }
  }
   
  private void paintOSD(Graphics2D canvasGraphics) {
    if (--osdFramesLeft < 0) return;
    canvasGraphics.setComposite(AlphaComposite.SrcOver);
    osdComponent.setText(osdMessage);
    Dimension s = osdComponent.getPreferredSize();
    SwingUtilities.paintComponent(
      canvasGraphics, osdComponent, display.displayContainer(),
      (display.displayEffectiveSize().width - s.width) - 12, 12,
      s.width, s.height
    );
  }

  private void refresh() {
    if (!signalOn) {
      paintLogo();
      return;
    }
    // Synchronize to avoid changing image properties while refreshing frame
    synchronized (refreshMonitor) {
      int f = frame;
      if (lastFrameRendered == f) return;

      int[] drawBuffer = frontBuffer;
      Graphics2D displayGraphics = displayGraphics();
      if (displayGraphics == null) return;
      // Get the entire Canvas
      Dimension ces = display.displayEffectiveSize();
      int displayEffectiveWidth = ces.width;
      int displayEffectiveHeight = ces.height;
      // CRT mode 3 OR no MultiBuffering active and needs to superimpose (CRT mode 1, 2 or OSD)
      // draw frameImage to intermediate image with composite then transfer to Canvas
      if (crtMode >= 3 || (MULTI_BUFFERING < 2 && (osdFramesLeft >= 0 || crtMode > 0))) {
        int intermWidth = Math.min(displayEffectiveWidth, 2048);
        int intermHeight = Math.min(displayEffectiveHeight, 1280);
        Graphics2D intermGraphics = intermFrameImage.createGraphics();
        intermGraphics.setComposite(AlphaComposite.Src);
        // Renders to intermediate image
        renderFrame(intermGraphics, drawBuffer, intermWidth, intermHeight);
        // If CRT mode 2, alpha-superimpose the prepared scanlines image
        if (crtMode == 2)
          renderScanlines(intermGraphics, intermWidth, intermHeight);
        // If CRT mode 3 or 4, sets the CRTTriadComposite and rewrite
        if (crtMode >= 3) {
          intermGraphics.setComposite(crtTriadComposite);
          intermGraphics.drawImage(
            intermFrameImage,
            0, 0, intermWidth, intermHeight,
            0, 0, intermWidth, intermHeight,
            null);
        }
        paintOSD(intermGraphics);
        intermGraphics.dispose();
        // Then transfer to Canvas
        displayGraphics.drawImage(
          intermFrameImage,
          0, 0, intermWidth, intermHeight,
          0, 0, intermWidth, intermHeight,
          null);
      } else {
        // Renders directly to Canvas
        renderFrame(displayGraphics, drawBuffer, displayEffectiveWidth, displayEffectiveHeight);
        // If CRT mode 2, alpha-superimpose the prepared scanlines image
        if (crtMode == 2)
          renderScanlines(displayGraphics, displayEffectiveWidth, displayEffectiveHeight);
        paintOSD(displayGraphics);
      }
      displayFrameFinished(displayGraphics);
      lastFrameRendered = f;
    }
  }

  private void renderFrame(Graphics2D graphics, int[] drawBuffer, int effectiveWidth, int effectiveHeight) {
    // If CRT mode 1, 2 or 4, set composite for last and new frame over each other, and draw old frame
    if (crtMode > 0 && crtMode != 3) {
      // Clear last image
      graphics.setBackground(Color.BLACK);
      graphics.clearRect(0, 0, effectiveWidth, effectiveHeight);
      // Draw old frame
      graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, CRT_RETENTION_ALPHA));
      graphics.drawImage(frameImage, 0, 0, effectiveWidth, effectiveHeight, 0, 0, displayWidth, displayHeight, null);
    }
    // Update the image to draw with contents stored in the frontBuffer
    frameImage.getRaster().setDataElements(0, 0, displayWidth, displayHeight, drawBuffer);
    // Draw new frame
    graphics.drawImage(frameImage, 0, 0, effectiveWidth, effectiveHeight, 0, 0, displayWidth, displayHeight, null);
  }

  private void renderScanlines(Graphics2D graphics, int effectiveWidth, int effectiveHeight) {
    graphics.setComposite(AlphaComposite.SrcOver);
    graphics.drawImage(scanlinesTextureImage, 0, 0, effectiveWidth, effectiveHeight, 0, 0, effectiveWidth, effectiveHeight, null);
  }

  private synchronized void setDisplayDefaultSize() {
    setDisplaySize(DEFAULT_WIDTH, DEFAULT_HEIGHT_PCT);
    setDisplayOrigin(DEFAULT_ORIGIN_X, DEFAULT_ORIGIN_Y_PCT);
    if (display != null) {
      float scX = display.displayDefaultOpenningScaleX(displayWidth, displayHeight);
      setDisplayScale(scX, scX / DEFAULT_SCALE_ASPECT_X);
    } else
      setDisplayScale(DEFAULT_SCALE_X, DEFAULT_SCALE_Y);
    displayCenter();
  }

  private synchronized void setDisplayOrigin(int x, double yPct) {
    displayOriginX = x;
    if (displayOriginX < 0) displayOriginX = 0;
    else if (displayOriginX > signalWidth - displayWidth) displayOriginX = signalWidth - displayWidth;
   
    displayOriginYPct = yPct;
    if (displayOriginYPct < 0) displayOriginYPct = 0;
    else if ((displayOriginYPct / 100 * signalHeight) > signalHeight - displayHeight)
      displayOriginYPct = ((double)signalHeight - displayHeight) / signalHeight * 100;

    // Compute final display originY, adding a little for additional lines as discovered in last video standard detection
    int adtOriginY = videoStandardDetectionAdtLinesPerFrame / 2;
    displayOriginY = (int) (displayOriginYPct / 100 * signalHeight) + adtOriginY;
    if ((displayOriginY + displayHeight) > signalHeight) displayOriginY = signalHeight - displayHeight;
  }

  private synchronized void setDisplaySize(int width, double heightPct) {
    displayWidth = width;
    if (displayWidth < 10) displayWidth = 10;
    else if (displayWidth > signalWidth) displayWidth = signalWidth;
   
    displayHeightPct = heightPct;
    if (displayHeightPct < 10) displayHeightPct = 10;
    else if (displayHeightPct > 100) displayHeightPct = 100;
   
    // Compute final display height, considering additional lines as discovered in last video standard detection
    displayHeight = (int) (displayHeightPct / 100 * (signalHeight + videoStandardDetectionAdtLinesPerFrame));
    if (displayHeight > signalHeight) displayHeight = signalHeight;

    setDisplayOrigin(displayOriginX, displayOriginYPct);
    displayUpdateSize();
  }

  private void setDisplayScale(float x, float y) {
    displayScaleX = x;
    if (displayScaleX < 1) displayScaleX = 1;
    displayScaleY = y;
    if (displayScaleY < 1) displayScaleY = 1;
    displayUpdateSize();
  }

  private void setDisplayScaleDefaultAspect(float y) {
    int scaleY = (int) y;
    if (scaleY < 1) scaleY = 1;
    setDisplayScale(scaleY * DEFAULT_SCALE_ASPECT_X, scaleY);
  }

  private void loadCartridgeFromFile(boolean autoPower) {
    if (cartridgeChangeDisabledWarning()) return;
    display.displayLeaveFullscreen();
    Cartridge cart = null;
    try {
      File file = FileROMChooser.chooseFileToLoad();
      if (file != null) cart = ROMLoader.load(file);
    } catch (AccessControlException e) {
      // Automatically tries FileServiceChooser if access is denied
      FileContents fileContents = FileServiceROMChooser.chooseFileToLoad();
      if (fileContents != null) cart = ROMLoader.load(fileContents);
    }
    if (cart != null) cartridgeInsert(cart, autoPower);
    else display.displayRequestFocus();
  }

  private void loadCartridgeFromURL(boolean autoPower) {
    if (cartridgeChangeDisabledWarning()) return;
    display.displayLeaveFullscreen();
    Cartridge cart = null;
    String url = URLROMChooser.chooseURLToLoad();
    if (url != null) cart = ROMLoader.load(url, false);
    if (cart != null) cartridgeInsert(cart, autoPower);
    else display.displayRequestFocus();
  }

  private void loadCartridgeEmpty() {
    if (cartridgeChangeDisabledWarning()) return;
    cartridgeSocket.insert(null, false);
  }

  private void loadCartridgePaste() {
    if (cartridgeChangeDisabledWarning()) return;
    try {
      Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
      Transferable transf = clip.getContents("Ignored");
      if (transf == null) return;
      Cartridge cart = ROMTransferHandlerUtil.importCartridgeData(transf);
      if (cart != null) cartridgeInsert(cart, true);
    } catch (Exception ex) {
      // Simply give up
    }
  }

  private void saveStateCartridge() {
    savestateSocket.saveStateFile();
    display.displayRequestFocus();
  }

  private boolean cartridgeChangeDisabledWarning() {
    if (!isCartridgeChangeEnabled()) {
      showOSD("Cartridge change is disabled", true);
      return true;
    }
    return false;
  }

  private void crtModeToggle() {
    setCrtMode(crtMode + 1);
  }

  private void setCrtMode(int mode) {
    synchronized (refreshMonitor) {
      int newMode = mode > 4 || mode < 0 ? 0 : mode;
      if (crtMode == newMode) return;
      crtMode = newMode;
      showOSD("CRT mode: " + crtModeNames[crtMode], true);
    }
  }

  public void controlActivated(Control control) {
    // All controls are Press-only and repeatable
    switch(control) {
      case LOAD_CARTRIDGE_FILE:
        loadCartridgeFromFile(true); break;
      case LOAD_CARTRIDGE_FILE_NO_AUTO_POWER:
        loadCartridgeFromFile(false); break;
      case LOAD_CARTRIDGE_URL:
        loadCartridgeFromURL(true); break;
      case LOAD_CARTRIDGE_URL_NO_AUTO_POWER:
        loadCartridgeFromURL(false); break;
      case LOAD_CARTRIDGE_EMPTY:
        loadCartridgeEmpty(); break;
      case LOAD_CARTRIDGE_PASTE:
        loadCartridgePaste(); break;
      case SAVE_STATE_CARTRIDGE:
        saveStateCartridge(); break;
      case CRT_FILTER:
        crtFilter = !crtFilter;
        showOSD(crtFilter ? "CRT Filter: ON" : "CRT Filter: OFF", true);
        break;
      case CRT_MODES:
        crtModeToggle(); break;
      case STATS:
        showStats = !showStats; showOSD(null, true); break;
      case DEBUG:
        debug++;
        if (debug > 4) debug = 0;
        break;
      case ORIGIN_X_MINUS:
        setDisplayOrigin(displayOriginX + 1, displayOriginYPct); break;
      case ORIGIN_X_PLUS:   
        setDisplayOrigin(displayOriginX - 1, displayOriginYPct); break;
      case ORIGIN_Y_MINUS:
        setDisplayOrigin(displayOriginX, displayOriginYPct + 0.5); break;
      case ORIGIN_Y_PLUS:
        setDisplayOrigin(displayOriginX, displayOriginYPct - 0.5); break;
      case SIZE_DEFAULT:
        setDisplayDefaultSize(); break;
    }
    if (fixedSizeMode) return;
    switch(control) {
      case WIDTH_MINUS:
        setDisplaySize(displayWidth - 1, displayHeightPct); break;
      case WIDTH_PLUS:   
        setDisplaySize(displayWidth + 1, displayHeightPct); break;
      case HEIGHT_MINUS:
        setDisplaySize(displayWidth, displayHeightPct - 0.5); break;
      case HEIGHT_PLUS:
        setDisplaySize(displayWidth, displayHeightPct + 0.5); break;
      case SCALE_X_MINUS:
        setDisplayScale(displayScaleX - 0.5f, displayScaleY); break;
      case SCALE_X_PLUS:   
        setDisplayScale(displayScaleX + 0.5f, displayScaleY); break;
      case SCALE_Y_MINUS:
        setDisplayScale(displayScaleX, displayScaleY - 0.5f); break;
      case SCALE_Y_PLUS:
        setDisplayScale(displayScaleX, displayScaleY + 0.5f); break;
      case SIZE_MINUS:
        setDisplayScaleDefaultAspect(displayScaleY - 1); break;
      case SIZE_PLUS:
        setDisplayScaleDefaultAspect(displayScaleY + 1); break;
    }
  }


  public Clock clock;
 
  public String refreshMonitor = "refreshMonitor";    // Used only for synchronization

  private MonitorControls monitorControls;

  private boolean fixedSizeMode = FIXED_SIZE;
  private boolean cartridgeChangeEnabled = CARTRIDGE_CHANGE;
 
  private VideoSignal videoSignal;
  private CartridgeSocket cartridgeSocket;
  private SaveStateSocket savestateSocket;
  private final double fps;
 
  private VideoStandard signalStandard;
  private int signalWidth;
  private int signalHeight;

  private VideoStandard videoStandardDetected;
  private int videoStandardDetectionFrameCount;
  private int videoStandardDetectionFrameLineCount = 0;
  private int videoStandardDetectionAdtLinesPerFrame = 0;

  private int[] backBuffer;
  private int[] frontBuffer;

  private int displayWidth;
  private int displayHeight;
  private double displayHeightPct;
  private int displayOriginX;
  private int displayOriginY;
  private double displayOriginYPct;
  private float displayScaleX;
  private float displayScaleY;
 
  private boolean powerOn = false;
  private boolean signalOn = false;
  private SignalOffRefresher signalOffRefresher;
 
  private int osdFramesLeft = -1;
  private String osdMessage;
  private JLabel osdComponent;
 
  private boolean crtFilter = CRT_FILTER;
  private int crtMode = CRT_MODE < 0 ? 0 : CRT_MODE;

  private int debug = 0;
  private boolean showStats = false;
 
  private int line = 0;
  private int frame = 0;
  private int lastFrameRendered = -1;

  private MonitorDisplay display;

  private BufferedImage frameImage;
 
  private BufferedImage scanlinesTextureImage;
  private CRTTriadComposite crtTriadComposite;
  private BufferedImage intermFrameImage;
 
  private Image logoIcon;
 
  private final Runnable refresher = new Runnable() {
    @Override
    public void run() {
      refresh();
    }
  };
 
  private final class SignalOffRefresher extends Thread {
    @Override
    public void run() {
      try { sleep(1000/30); } catch (InterruptedException e) {}
      while(powerOn && !signalOn) {
        synchOutput();
        try { sleep(1000/30); } catch (InterruptedException e) {}  // 30 fps
      }
      signalOffRefresher = null;
    }
  };

  private static final String[] crtModeNames = { "OFF", "Phosphor", "Phosphor Scanlines", "RGB", "RGB Phosphor" };
 
  private static final int EXTRA_UPPER_VSYNC_TOLERANCE = 5;

  public static final double   DEFAULT_FPS = Parameters.SCREEN_DEFAULT_FPS;
  public static final int      BUFFER_VSYNC = Parameters.SCREEN_BUFFER_VSYNC;
  public static final int      MULTI_BUFFERING = Parameters.SCREEN_MULTI_BUFFERING;
  public static final boolean  PAGE_FLIPPING = Parameters.SCREEN_PAGE_FLIPPING;
  private static final int     VSYNC_TOLERANCE = Parameters.SCREEN_VSYNC_TOLERANCE;
  private static final boolean BUFFER_SYNC_WAIT = Parameters.SCREEN_BUFFER_SYNC_WAIT;
  private static final int     DEFAULT_ORIGIN_X = Parameters.SCREEN_DEFAULT_ORIGIN_X;
  private static final double  DEFAULT_ORIGIN_Y_PCT = Parameters.SCREEN_DEFAULT_ORIGIN_Y_PCT;    // Percentage of height
  private static final int     DEFAULT_WIDTH = Parameters.SCREEN_DEFAULT_WIDTH;
  private static final double  DEFAULT_HEIGHT_PCT = Parameters.SCREEN_DEFAULT_HEIGHT_PCT;      // Percentage of height
  public static final float    DEFAULT_SCALE_X = Parameters.SCREEN_DEFAULT_SCALE_X;
  public static final float    DEFAULT_SCALE_ASPECT_X = Parameters.SCREEN_DEFAULT_SCALE_ASPECT_X;
  private static final float   DEFAULT_SCALE_Y = Parameters.SCREEN_DEFAULT_SCALE_Y;
  private static final int     OSD_FRAMES = Parameters.SCREEN_OSD_FRAMES;
  private static final boolean CRT_FILTER = Parameters.SCREEN_CRT_FILTER;
  private static final int     CRT_MODE = Parameters.SCREEN_CRT_MODE;
  private static final float   CRT_RETENTION_ALPHA = Parameters.SCREEN_CRT_RETENTION_ALPHA;
  private static final float   SCANLINES_STRENGTH = Parameters.SCREEN_SCANLINES_STRENGTH;
  private static final float   FRAME_ACCELERATION = Parameters.SCREEN_FRAME_ACCELERATION;
  private static final float   IMTERM_FRAME_ACCELERATION = Parameters.SCREEN_INTERM_FRAME_ACCELERATION;
  private static final float   SCANLINES_ACCELERATION = Parameters.SCREEN_SCANLINES_ACCELERATION;
  private static final boolean CARTRIDGE_CHANGE = Parameters.SCREEN_CARTRIDGE_CHANGE;
  private static final boolean FIXED_SIZE = Parameters.SCREEN_FIXED_SIZE;

  public static final long serialVersionUID = 0L;


  public static enum Control {
    WIDTH_PLUS, HEIGHT_PLUS,
    WIDTH_MINUS, HEIGHT_MINUS,
    ORIGIN_X_PLUS, ORIGIN_Y_PLUS,
    ORIGIN_X_MINUS, ORIGIN_Y_MINUS,
    SCALE_X_PLUS, SCALE_Y_PLUS,
    SCALE_X_MINUS, SCALE_Y_MINUS,
    SIZE_PLUS, SIZE_MINUS,
    SIZE_DEFAULT,
    LOAD_CARTRIDGE_FILE, LOAD_CARTRIDGE_FILE_NO_AUTO_POWER,
    LOAD_CARTRIDGE_URL, LOAD_CARTRIDGE_URL_NO_AUTO_POWER,
    LOAD_CARTRIDGE_EMPTY,
    LOAD_CARTRIDGE_PASTE,
    SAVE_STATE_CARTRIDGE,
    CRT_FILTER, CRT_MODES,
    DEBUG, STATS
  }

}

TOP

Related Classes of org.javatari.pc.screen.Monitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.