shithub: m8c

ref: bfa999b28198f019b9e9fe9a66363a6a9bb85c76
dir: /src/main.c/

View raw version
// Copyright 2021 Jonne Kokkonen
// Released under the MIT licence, https://opensource.org/licenses/MIT

/* Uncomment this line to enable debug messages or call make with `make
   CFLAGS=-DDEBUG_MSG` */
// #define DEBUG_MSG

#include <SDL3/SDL.h>
#include <signal.h>

#include "SDL2_inprint.h"
#include "backends/audio.h"
#include "backends/m8.h"
#include "command.h"
#include "config.h"
#include "gamecontrollers.h"
#include "input.h"
#include "render.h"

#include <stdlib.h>

enum app_state app_state = WAIT_FOR_DEVICE;

// Handle CTRL+C / SIGINT, SIGKILL etc.
static void signal_handler(int unused) {
  (void)unused;
  app_state = QUIT;
}

static void initialize_signals() {
  signal(SIGINT, signal_handler);
  signal(SIGTERM, signal_handler);
#ifdef SIGQUIT
  signal(SIGQUIT, signal_handler);
#endif
}


static void do_wait_for_device(const char *preferred_device, unsigned char *m8_connected,
                        config_params_s *conf) {
  static Uint64 ticks_poll_device = 0;
  static Uint64 ticks_update_screen = 0;

  if (*m8_connected == 0) {
    screensaver_init();
  }

  while (app_state == WAIT_FOR_DEVICE) {
    // get current input
    const input_msg_s input = input_get_msg(&*conf);
    if (input.type == special && input.value == msg_quit) {
      SDL_LogDebug(SDL_LOG_CATEGORY_SYSTEM, "Input message QUIT.");
      app_state = QUIT;
    }

    if (SDL_GetTicks() - ticks_update_screen > 16) {
      ticks_update_screen = SDL_GetTicks();
      screensaver_draw();
      render_screen();
    }

    // Poll for M8 device every second
    if (*m8_connected == 0 && SDL_GetTicks() - ticks_poll_device > 1000) {
      ticks_poll_device = SDL_GetTicks();
      if (app_state == WAIT_FOR_DEVICE && m8_initialize(0, preferred_device) == 1) {

        if (conf->audio_enabled == 1) {
          if (audio_initialize(conf->audio_device_name, conf->audio_buffer_size) == 0) {
            SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "Cannot initialize audio");
            conf->audio_enabled = 0;
          }
        }

        const int m8_enabled = m8_enable_and_reset_display();
        // Device was found; enable display and proceed to the main loop
        if (m8_enabled == 1) {
          app_state = RUN;
          *m8_connected = 1;
          screensaver_destroy();
        } else {
          SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
          app_state = QUIT;
          screensaver_destroy();
        }
      }
    }
    SDL_Delay(conf->idle_ms);
  }
}

static config_params_s initialize_config(int argc, char *argv[], char **preferred_device, char **config_filename) {
  for (int i = 1; i < argc; i++) {
    if (SDL_strcmp(argv[i], "--list") == 0) {
      exit(m8_list_devices());
    }
    if (SDL_strcmp(argv[i], "--dev") == 0 && i + 1 < argc) {
      *preferred_device = argv[i + 1];
      SDL_Log("Using preferred device: %s.\n", *preferred_device);
      i++;
    } else if (SDL_strcmp(argv[i], "--config") == 0 && i + 1 < argc) {
      *config_filename = argv[i + 1];
      SDL_Log("Using config file: %s.\n", *config_filename);
      i++;
    }
  }

  config_params_s conf = config_initialize(*config_filename);
  config_read(&conf);
  return conf;
}

static void cleanup_resources(const unsigned char device_connected, const config_params_s *conf) {
  if (conf->audio_enabled) {
    audio_close();
  }
  gamecontrollers_close();
  renderer_close();
  inline_font_close();
  if (device_connected) {
    m8_close();
  }
  SDL_Quit();
  SDL_Log("Shutting down.");
}

static unsigned char handle_device_initialization(unsigned char wait_for_device, const char *preferred_device) {
  const unsigned char device_connected = m8_initialize(1, preferred_device);
  if (!wait_for_device && device_connected == 0) {
    SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected!");
    exit(EXIT_FAILURE);
  }
  return device_connected;
}

static void main_loop(config_params_s *conf, const char *preferred_device) {
  unsigned char device_connected = 0;

  do {
    device_connected = m8_initialize(1, preferred_device);
    if (device_connected && m8_enable_and_reset_display()) {
      if (conf->audio_enabled) {
        audio_initialize(conf->audio_device_name, conf->audio_buffer_size);
        m8_reset_display();  // Avoid display glitches.
      }
      app_state = RUN;
    } else {
      SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Device not detected.");
      app_state = conf->wait_for_device ? WAIT_FOR_DEVICE : QUIT;
    }

    if (conf->wait_for_device && app_state == WAIT_FOR_DEVICE) {
      do_wait_for_device(preferred_device, &device_connected, conf);
    } else if (!device_connected && app_state != WAIT_FOR_DEVICE) {
      cleanup_resources(0, conf);
      exit(EXIT_FAILURE);
    }

    // Handle input, process data, and render screen while running.
    while (app_state == RUN) {
      input_process(conf, &app_state);
      const int result = m8_process_data(conf);
      if (result == DEVICE_DISCONNECTED) {
        device_connected = 0;
        app_state = WAIT_FOR_DEVICE;
        audio_close();
      } else if (result == DEVICE_FATAL_ERROR) {
        app_state = QUIT;
      }
      render_screen();
      SDL_Delay(conf->idle_ms);
    }
  } while (app_state > QUIT);

  cleanup_resources(device_connected, conf);
}


int main(const int argc, char *argv[]) {
  char *preferred_device = NULL;
  char *config_filename = NULL;

  config_params_s conf = initialize_config(argc, argv, &preferred_device, &config_filename);
  initialize_signals();

  const unsigned char initial_device_connected = handle_device_initialization(conf.wait_for_device, preferred_device);
  if (!renderer_initialize(conf.init_fullscreen)) {
    SDL_LogCritical(SDL_LOG_CATEGORY_ERROR, "Failed to initialize renderer.");
    cleanup_resources(initial_device_connected, &conf);
    return EXIT_FAILURE;
  }

  gamecontrollers_initialize();

#ifndef NDEBUG
  SDL_SetLogPriorities(SDL_LOG_PRIORITY_DEBUG);
  SDL_LogDebug(SDL_LOG_CATEGORY_TEST, "Running a Debug build");
#endif

  main_loop(&conf, preferred_device);
  return EXIT_SUCCESS;
}