auto Program::updateVideoDriver(Window parent) -> void {
  auto changed = (bool)video;
  video.create(settings.video.driver);
  video.setContext(presentation.viewport.handle());
  if(changed) {
    settings.video.format = video.format();
  }
  updateVideoExclusive();
  updateVideoBlocking();
  updateVideoFlush();
  updateVideoMonitor();
  updateVideoFormat();
  updateVideoShader();

  if(video.ready()) {
    video.clear();
    updateVideoShader();
  }

  video.onUpdate([&](uint width, uint height) {
    if(!emulator->loaded()) video.clear();
  });

  if(!video.ready()) {
    MessageDialog({
      "Error: failed to initialize [", settings.video.driver, "] video driver."
    }).setAlignment(parent).error();
    settings.video.driver = "None";
    return updateVideoDriver(parent);
  }

  presentation.updateShaders();
}

auto Program::updateVideoExclusive() -> void {
  video.setExclusive(settings.video.exclusive);
}

auto Program::updateVideoBlocking() -> void {
  video.setBlocking(settings.video.blocking);
}

auto Program::updateVideoFlush() -> void {
  video.setFlush(settings.video.flush);
}

auto Program::updateVideoMonitor() -> void {
  if(!video.hasMonitor(settings.video.monitor)) {
    settings.video.monitor = video.monitor();
  }
  video.setMonitor(settings.video.monitor);
}

auto Program::updateVideoFormat() -> void {
  if(!video.hasFormat(settings.video.format)) {
    settings.video.format = video.format();
  }
  video.setFormat(settings.video.format);
}

auto Program::updateVideoShader() -> void {
  video.setShader(settings.video.shader);
}

auto Program::updateVideoPalette() -> void {
  double luminance = settings.video.luminance / 100.0;
  double saturation = settings.video.saturation / 100.0;
  double gamma = settings.video.gamma / 100.0;

  uint depth = 24;
  if(video.format() == "RGB30") depth = 30;

  for(uint color : range(32768)) {
    uint16 r = (color >> 10) & 31;
    uint16 g = (color >>  5) & 31;
    uint16 b = (color >>  0) & 31;

    r = r << 3 | r >> 2; r = r << 8 | r << 0;
    g = g << 3 | g >> 2; g = g << 8 | g << 0;
    b = b << 3 | b >> 2; b = b << 8 | b << 0;

    if(saturation != 1.0) {
      uint16 grayscale = uclamp<16>((r + g + b) / 3);
      double inverse = max(0.0, 1.0 - saturation);
      r = uclamp<16>(r * saturation + grayscale * inverse);
      g = uclamp<16>(g * saturation + grayscale * inverse);
      b = uclamp<16>(b * saturation + grayscale * inverse);
    }

    if(gamma != 1.0) {
      double reciprocal = 1.0 / 32767.0;
      r = r > 32767 ? r : uint16(32767 * pow(r * reciprocal, gamma));
      g = g > 32767 ? g : uint16(32767 * pow(g * reciprocal, gamma));
      b = b > 32767 ? b : uint16(32767 * pow(b * reciprocal, gamma));
    }

    if(luminance != 1.0) {
      r = uclamp<16>(r * luminance);
      g = uclamp<16>(g * luminance);
      b = uclamp<16>(b * luminance);
    }

    switch(depth) {
    case 24: palette[color] = r >> 8 << 16 | g >> 8 <<  8 | b >> 8 << 0; break;
    case 30: palette[color] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
    }

    r >>= 1;
    g >>= 1;
    b >>= 1;

    switch(depth) {
    case 24: paletteDimmed[color] = r >> 8 << 16 | g >> 8 <<  8 | b >> 8 << 0; break;
    case 30: paletteDimmed[color] = r >> 6 << 20 | g >> 6 << 10 | b >> 6 << 0; break;
    }
  }

  emulator->configure("Video/ColorEmulation", false);
}

auto Program::updateVideoEffects() -> void {
  emulator->configure("Video/BlurEmulation", settings.video.blur);
}

auto Program::toggleVideoFullScreen() -> void {
  if(!video.hasFullScreen()) return;
  if(presentation.fullScreen()) return;

  video.clear();

  if(!video.fullScreen()) {
    video.setFullScreen(true);
    if(!input.acquired() && (video.exclusive() || video.hasMonitors().size() == 1)) input.acquire();
  } else {
    if(input.acquired()) input.release();
    video.setFullScreen(false);
    presentation.viewport.setFocused();
  }
}

auto Program::toggleVideoPseudoFullScreen() -> void {
  if(video.fullScreen()) return;

  if(!presentation.fullScreen()) {
    presentation.setFullScreen(true);
    presentation.menuBar.setVisible(false);
    if(!input.acquired() && video.hasMonitors().size() == 1) input.acquire();
  } else {
    if(input.acquired()) input.release();
    presentation.menuBar.setVisible(true);
    presentation.setFullScreen(false);
  }
}
