code:
/* MegaZeux
 *
 * Copyright (C) 2004-2006 Gilead Kutnick <exophase@adelphia.net>
 * Copyright (C) 2007 Alistair John Strachan <alistair@devzero.co.uk>
 *
 * YUV Renderers:
 *   Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * OpenGL #2 Renderer:
 *   Copyright (C) 2007 Joel Bouchard Lamontagne <logicow@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>

#include "graphics.h"
#include "world.h"
#include "configure.h"
#include "event.h"
#include "render.h"
#include "renderers.h"
#include "platform.h"

#ifdef CONFIG_PNG
#include "pngops.h"
#endif

#ifdef CONFIG_SDL
#include "SDL.h"
#ifdef CONFIG_ICON
#include "SDL_syswm.h"
#endif // CONFIG_ICON
#endif // CONFIG_SDL

#include "util.h"

#define CURSOR_BLINK_RATE 115

__editor_maybe_static struct graphics_data graphics;
static bool graphics_was_initialized;

static const struct renderer_data renderers[] =
{
#if defined(CONFIG_RENDER_SOFT)
  { "software", render_soft_register },
#endif
#if defined(CONFIG_RENDER_GL_FIXED)
  { "opengl1", render_gl1_register },
  { "opengl2", render_gl2_register },
#endif
#if defined(CONFIG_RENDER_GL_PROGRAM)
  { "glsl", render_glsl_register },
#endif
#if defined(CONFIG_RENDER_YUV)
  { "overlay1", render_yuv1_register },
  { "overlay2", render_yuv2_register },
#endif
#if defined(CONFIG_RENDER_GP2X)
  { "gp2x", render_gp2x_register },
#endif
#if defined(CONFIG_NDS)
  { "nds", render_nds_register },
#endif
#if defined(CONFIG_RENDER_GX)
  { "gx", render_gx_register },
#endif
  { NULL, NULL }
};

static const struct rgb_color default_pal[16] =
{
  /* r, g, b, unused */
  { 0, 0, 0, 0 },
  { 0, 0, 170, 0 },
  { 0, 170, 0, 0 },
  { 0, 170, 170, 0 },
  { 170, 0, 0, 0 },
  { 170, 0, 170, 0 },
  { 170, 85, 0, 0 },
  { 170, 170, 170, 0 },
  { 85, 85, 85, 0 },
  { 85, 85, 255, 0 },
  { 85, 255, 85, 0 },
  { 85, 255, 255, 0 },
  { 255, 85, 85, 0 },
  { 255, 85, 255, 0 },
  { 255, 255, 85, 0 },
  { 255, 255, 255, 0 }
};

void ec_change_byte(Uint8 chr, Uint8 byte, Uint8 new_value)
{
  graphics.charset[(chr * CHAR_SIZE) + byte] = new_value;

  if(graphics.renderer.remap_charbyte)
    graphics.renderer.remap_charbyte(&graphics, chr, byte);
}

void ec_change_char(Uint8 chr, char *matrix)
{
  memcpy(graphics.charset + (chr * CHAR_SIZE), matrix, CHAR_SIZE);

  if(graphics.renderer.remap_char)
    graphics.renderer.remap_char(&graphics, chr);
}

Uint8 ec_read_byte(Uint8 chr, Uint8 byte)
{
  return graphics.charset[(chr * CHAR_SIZE) + byte];
}

void ec_read_char(Uint8 chr, char *matrix)
{
  memcpy(matrix, graphics.charset + (chr * CHAR_SIZE), CHAR_SIZE);
}

Sint32 ec_load_set(char *name)
{
  FILE *fp = fopen_unsafe(name, "rb");

  if(fp == NULL)
   return -1;

  fread(graphics.charset, CHAR_SIZE, CHARSET_SIZE, fp);

  fclose(fp);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);

  return 0;
}

__editor_maybe_static void ec_load_set_secondary(const char *name,
 Uint8 *dest)
{
  FILE *fp = fopen_unsafe(name, "rb");

  if(!fp)
    return;

  fread(dest, CHAR_SIZE, CHARSET_SIZE, fp);
  fclose(fp);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);
}

Sint32 ec_load_set_var(char *name, Uint8 pos)
{
  Uint32 size = CHARSET_SIZE;
  FILE *fp = fopen_unsafe(name, "rb");

  if(!fp)
    return -1;

  size = ftell_and_rewind(fp) / CHAR_SIZE;
  if(size + pos > CHARSET_SIZE)
    size = CHARSET_SIZE - pos;

  fread(graphics.charset + (pos * CHAR_SIZE), CHAR_SIZE, size, fp);
  fclose(fp);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);

  return size;
}

void ec_mem_load_set(Uint8 *chars)
{
  memcpy(graphics.charset, chars, CHAR_SIZE * CHARSET_SIZE);

  // some renderers may want to map charsets to textures
  if(graphics.renderer.remap_charsets)
    graphics.renderer.remap_charsets(&graphics);
}

void ec_mem_save_set(Uint8 *chars)
{
  memcpy(chars, graphics.charset, CHAR_SIZE * CHARSET_SIZE);
}

__editor_maybe_static void ec_load_mzx(void)
{
  ec_mem_load_set(graphics.default_charset);
}

static void update_colors(struct rgb_color *palette, Uint32 count)
{
  graphics.renderer.update_colors(&graphics, palette, count);
}

static Uint32 make_palette(struct rgb_color *palette)
{
  Uint32 i;

  // Is SMZX mode set?
  if(graphics.screen_mode)
  {
    // SMZX mode 1: set all colors to diagonals
    if(graphics.screen_mode == 1)
    {
      for(i = 0; i < SMZX_PAL_SIZE; i++)
      {
        palette[i].r =
         ((graphics.intensity_palette[i & 15].r << 1) +
          graphics.intensity_palette[i >> 4].r) / 3;
        palette[i].g =
         ((graphics.intensity_palette[i & 15].g << 1) +
          graphics.intensity_palette[i >> 4].g) / 3;
        palette[i].b =
         ((graphics.intensity_palette[i & 15].b << 1) +
          graphics.intensity_palette[i >> 4].b) / 3;
      }
    }
    else
    {
      memcpy(palette, graphics.intensity_palette,
       sizeof(struct rgb_color) * SMZX_PAL_SIZE);
    }
    return SMZX_PAL_SIZE;
  }
  else
  {
    memcpy(palette, graphics.intensity_palette,
     sizeof(struct rgb_color) * PAL_SIZE * NUM_PALS);
    return PAL_SIZE * NUM_PALS;
  }
}

void update_palette(void)
{
  struct rgb_color new_palette[SMZX_PAL_SIZE];
  update_colors(new_palette, make_palette(new_palette));
}

void set_gui_palette(void)
{
  int i;

  memcpy(graphics.palette + PAL_SIZE, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);
  memcpy(graphics.intensity_palette + PAL_SIZE, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);

  for(i = 16; i < PAL_SIZE * NUM_PALS; i++)
    graphics.current_intensity[i] = 100;
}

static void init_palette(void)
{
  Uint32 i;

  memcpy(graphics.palette, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);
  memcpy(graphics.intensity_palette, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);
  memset(graphics.current_intensity, 0,
   sizeof(Uint32) * PAL_SIZE);

  for(i = 0; i < SMZX_PAL_SIZE; i++)
    graphics.saved_intensity[i] = 100;

  graphics.fade_status = 1;

  set_gui_palette();
  update_palette();
}

void set_color_intensity(Uint32 color, Uint32 percent)
{
  if(graphics.fade_status)
  {
    graphics.saved_intensity[color] = percent;
  }
  else
  {
    int r = (graphics.palette[color].r * percent) / 100;
    int g = (graphics.palette[color].g * percent) / 100;
    int b = (graphics.palette[color].b * percent) / 100;

    if(r > 255)
      r = 255;

    if(g > 255)
      g = 255;

    if(b > 255)
      b = 255;

    graphics.intensity_palette[color].r = r;
    graphics.intensity_palette[color].g = g;
    graphics.intensity_palette[color].b = b;

    graphics.current_intensity[color] = percent;
  }
}

void set_palette_intensity(Uint32 percent)
{
  Uint32 i, num_colors;

  if(graphics.screen_mode >= 2)
    num_colors = SMZX_PAL_SIZE;
  else
    num_colors = PAL_SIZE;

  for(i = 0; i < num_colors; i++)
  {
    set_color_intensity(i, percent);
  }
}

void set_rgb(Uint32 color, Uint32 r, Uint32 g, Uint32 b)
{
  int intensity = graphics.current_intensity[color];
  r = r * 255 / 63;
  g = g * 255 / 63;
  b = b * 255 / 63;

  graphics.palette[color].r = r;
  r = (r * intensity) / 100;
  if(r > 255)
    r = 255;
  graphics.intensity_palette[color].r = r;

  graphics.palette[color].g = g;
  g = (g * intensity) / 100;
  if(g > 255)
    g = 255;
  graphics.intensity_palette[color].g = g;

  graphics.palette[color].b = b;
  b = (b * intensity) / 100;
  if(b > 255)
    b = 255;
  graphics.intensity_palette[color].b = b;
}

void set_red_component(Uint32 color, Uint32 r)
{
  r = r * 255 / 63;
  graphics.palette[color].r = r;

  r = r * graphics.current_intensity[color] / 100;
  if(r > 255)
    r = 255;

  graphics.intensity_palette[color].r = r;
}

void set_green_component(Uint32 color, Uint32 g)
{
  g = g * 255 / 63;
  graphics.palette[color].g = g;

  g = g * graphics.current_intensity[color] / 100;
  if(g > 255)
    g = 255;

  graphics.intensity_palette[color].g = g;
}

void set_blue_component(Uint32 color, Uint32 b)
{
  b = b * 255 / 63;
  graphics.palette[color].b = b;

  b = b * graphics.current_intensity[color] / 100;
  if(b > 255)
    b = 255;

  graphics.intensity_palette[color].b = b;
}

Uint32 get_color_intensity(Uint32 color)
{
  return graphics.current_intensity[color];
}

void get_rgb(Uint32 color, Uint8 *r, Uint8 *g, Uint8 *b)
{
  *r = ((graphics.palette[color].r * 126) + 255) / 510;
  *g = ((graphics.palette[color].g * 126) + 255) / 510;
  *b = ((graphics.palette[color].b * 126) + 255) / 510;
}

Uint32 get_red_component(Uint32 color)
{
  return ((graphics.palette[color].r * 126) + 255) / 510;
}

Uint32 get_green_component(Uint32 color)
{
  return ((graphics.palette[color].g * 126) + 255) / 510;
}

Uint32 get_blue_component(Uint32 color)
{
  return ((graphics.palette[color].b * 126) + 255) / 510;
}

void load_palette(const char *fname)
{
  int file_size, i, r, g, b;
  FILE *pal_file;

  pal_file = fopen_unsafe(fname, "rb");
  if(!pal_file)
    return;

  file_size = ftell_and_rewind(pal_file);

  switch(graphics.screen_mode)
  {
    // Regular text mode, 16 colors
    case 0:
      file_size = MIN(file_size, 16 * 3);
      break;

    // SMZX modes, up to 256 colors
    default:
      file_size = MIN(file_size, 256 * 3);
      break;
  }

  for(i = 0; i < file_size / 3; i++)
  {
    r = fgetc(pal_file);
    g = fgetc(pal_file);
    b = fgetc(pal_file);
    set_rgb(i, r, g, b);
  }

  fclose(pal_file);
}

void smzx_palette_loaded(int val)
{
  graphics.default_smzx_loaded = val;
}

static void update_intensity_palette(void)
{
  int i;
  for(i = 0; i < SMZX_PAL_SIZE; i++)
  {
    set_color_intensity(i, graphics.current_intensity[i]);
  }
}

static void swap_palettes(void)
{
  struct rgb_color temp_colors[SMZX_PAL_SIZE];
  Uint32 temp_intensities[SMZX_PAL_SIZE];
  memcpy(temp_colors, graphics.backup_palette,
   sizeof(struct rgb_color) * SMZX_PAL_SIZE);
  memcpy(graphics.backup_palette, graphics.palette,
   sizeof(struct rgb_color) * SMZX_PAL_SIZE);
  memcpy(graphics.palette, temp_colors,
   sizeof(struct rgb_color) * SMZX_PAL_SIZE);
  memcpy(temp_intensities, graphics.backup_intensity,
   sizeof(Uint32) * SMZX_PAL_SIZE);
  if(graphics.fade_status)
  {
    memcpy(graphics.backup_intensity,
     graphics.saved_intensity, sizeof(Uint32) * SMZX_PAL_SIZE);
    memcpy(graphics.saved_intensity, temp_intensities,
     sizeof(Uint32) * SMZX_PAL_SIZE);
  }
  else
  {
    memcpy(graphics.backup_intensity,
     graphics.current_intensity, sizeof(Uint32) * SMZX_PAL_SIZE);
    memcpy(graphics.current_intensity, temp_intensities,
     sizeof(Uint32) * SMZX_PAL_SIZE);
    update_intensity_palette();
  }
}

Uint32 get_fade_status(void)
{
  return graphics.fade_status;
}

void dialog_fadein(void)
{
  if(get_fade_status())
  {
    clear_screen(32, 0);
    insta_fadein();
  }
}

void dialog_fadeout(void)
{
  if(get_fade_status())
  {
    insta_fadeout();
  }
}

void set_screen_mode(Uint32 mode)
{
  mode %= 4;

  if((mode >= 2) && (graphics.screen_mode < 2))
  {
    swap_palettes();
    graphics.screen_mode = mode;
    if(!graphics.default_smzx_loaded)
    {
      int i;

      if(graphics.fade_status)
      {
        for(i = 0; i < SMZX_PAL_SIZE; i++)
        {
          graphics.current_intensity[i] = 0;
        }
      }
      set_palette_intensity(100);
      load_palette(mzx_res_get_by_id(SMZX_PAL));
      graphics.default_smzx_loaded = 1;
    }
    update_palette();
  }
  else

  if((graphics.screen_mode >= 2) && (mode < 2))
  {
    swap_palettes();

    graphics.screen_mode = mode;

    set_gui_palette();
    update_palette();
  }
  else
  {
    graphics.screen_mode = mode;
  }

  if(mode == 1)
  {
    update_palette();
  }
}

Uint32 get_screen_mode(void)
{
  return graphics.screen_mode;
}

void update_screen(void)
{
  Uint32 ticks = get_ticks();

  if((ticks - graphics.cursor_timestamp) > CURSOR_BLINK_RATE)
  {
    graphics.cursor_flipflop ^= 1;
    graphics.cursor_timestamp = ticks;
  }

  graphics.renderer.render_graph(&graphics);
  if(graphics.cursor_flipflop &&
   (graphics.cursor_mode != cursor_mode_invisible))
  {
    struct char_element *cursor_element =
     graphics.text_video + graphics.cursor_x + (graphics.cursor_y * SCREEN_W);
    Uint8 cursor_color;
    Uint32 cursor_char = cursor_element->char_value;
    Uint32 lines = 0;
    Uint32 offset = 0;
    Uint32 i;
    Uint32 cursor_solid = 0xFFFFFFFF;
    Uint32 *char_offset = (Uint32 *)(graphics.charset +
     (cursor_char * CHAR_SIZE));
    Uint32 bg_color = cursor_element->bg_color;

    // Choose FG
    cursor_color = cursor_element->fg_color;

    // See if the cursor char is completely solid or completely empty
    for(i = 0; i < 3; i++)
    {
      cursor_solid &= *char_offset;
      char_offset++;
    }
    cursor_solid &= (*((Uint16 *)char_offset)) | 0xFFFF0000;
    if(cursor_solid == 0xFFFFFFFF)
    {
      // But wait! What if the background is the same as the foreground?
      // If so, use +8 instead.
      if(bg_color == cursor_color)
        cursor_color = bg_color ^ 8;
      else
        cursor_color = bg_color;
    }
    else
      if(bg_color == cursor_color)
        cursor_color = cursor_color ^ 8;

    switch(graphics.cursor_mode)
    {
      case cursor_mode_underline:
        lines = 2;
        offset = 12;
        break;
      case cursor_mode_solid:
        lines = 14;
        offset = 0;
        break;
      case cursor_mode_invisible:
        break;
    }

    if(graphics.screen_mode)
    {
      if(graphics.screen_mode != 3)
        cursor_color = (cursor_color << 4) | (cursor_color & 0x0F);
      else
        cursor_color = ((bg_color << 4) | (cursor_color & 0x0F)) + 3;
    }

    graphics.renderer.render_cursor(&graphics,
     graphics.cursor_x, graphics.cursor_y, cursor_color, lines, offset);
  }
  if(graphics.mouse_status)
  {
    int mouse_x, mouse_y;
    get_real_mouse_position(&mouse_x, &mouse_y);

    mouse_x = (mouse_x / graphics.mouse_width_mul) * graphics.mouse_width_mul;
    mouse_y = (mouse_y / graphics.mouse_height_mul) * graphics.mouse_height_mul;

    graphics.renderer.render_mouse(&graphics, mouse_x, mouse_y,
     graphics.mouse_width_mul, graphics.mouse_height_mul);
  }
  graphics.renderer.sync_screen(&graphics);
}

// Very quick fade out. Saves intensity table for fade in. Be sure
// to use in conjuction with the next function.
void vquick_fadeout(void)
{
  if(!graphics.fade_status)
  {
    Sint32 i, i2, num_colors;
    Uint32 ticks;

    if(graphics.screen_mode >= 2)
      num_colors = SMZX_PAL_SIZE;
    else
      num_colors = PAL_SIZE;

    memcpy(graphics.saved_intensity, graphics.current_intensity,
     sizeof(Uint32) * num_colors);

    for(i = 10; i >= 0; i--)
    {
      ticks = get_ticks();

      for(i2 = 0; i2 < num_colors; i2++)
        set_color_intensity(i2, (graphics.saved_intensity[i2] * i / 10));

      update_palette();
      update_screen();
      ticks = get_ticks() - ticks;
      if(ticks <= 16)
        delay(16 - ticks);
    }
    graphics.fade_status = 1;
  }
}

// Very quick fade in. Uses intensity table saved from fade out. For
// use in conjuction with the previous function.
void vquick_fadein(void)
{
  if(graphics.fade_status)
  {
    Uint32 i, i2, num_colors;
    Uint32 ticks;

    graphics.fade_status = 0;

    if(graphics.screen_mode >= 2)
      num_colors = SMZX_PAL_SIZE;
    else
      num_colors = PAL_SIZE;

    for(i = 0; i <= 10; i++)
    {
      ticks = get_ticks();

      for(i2 = 0; i2 < num_colors; i2++)
        set_color_intensity(i2, (graphics.saved_intensity[i2] * i / 10));

      update_palette();
      update_screen();
      ticks = get_ticks() - ticks;
      if(ticks <= 16)
        delay(16 - ticks);
    }
  }
}

// Instant fade out
void insta_fadeout(void)
{
  Uint32 i, num_colors;

  if(graphics.fade_status)
    return;

  if(graphics.screen_mode >= 2)
    num_colors = SMZX_PAL_SIZE;
  else
    num_colors = PAL_SIZE;

  for(i = 0; i < num_colors; i++)
    set_color_intensity(i, 0);

  delay(1);
  update_palette();
  update_screen(); // NOTE: this was called conditionally in 2.81e

  graphics.fade_status = true;
}

// Instant fade in
void insta_fadein(void)
{
  Uint32 i, num_colors;

  if(!graphics.fade_status)
    return;

  graphics.fade_status = false;

  if(graphics.screen_mode >= 2)
    num_colors = SMZX_PAL_SIZE;
  else
    num_colors = PAL_SIZE;

  for(i = 0; i < num_colors; i++)
    set_color_intensity(i, graphics.saved_intensity[i]);

  update_palette();
  update_screen(); // NOTE: this was called conditionally in 2.81e
}

void default_palette(void)
{
  memcpy(graphics.palette, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);
  memcpy(graphics.intensity_palette, default_pal,
   sizeof(struct rgb_color) * PAL_SIZE);
  update_palette();
}

static bool set_graphics_output(struct config_info *conf)
{
  const char *video_output = conf->video_output;
  const struct renderer_data *renderer = renderers;

  // The first renderer was NULL, this shouldn't happen
  if(!renderer->name)
  {
    warn("No renderers built, please provide a valid config.h!\n");
    return false;
  }

  while(renderer->name)
  {
    if(!strcasecmp(video_output, renderer->name))
      break;
    renderer++;
  }

  // If no match found, use first renderer in the renderer list
  if(!renderer->name)
    renderer = renderers;

  renderer->reg(&graphics.renderer);

  debug("Video: using '%s' renderer.\n", renderer->name);
  return true;
}

#if defined(CONFIG_PNG) && defined(CONFIG_SDL) && \
    defined(CONFIG_ICON) && !defined(__WIN32__)

static bool icon_w_h_constraint(png_uint_32 w, png_uint_32 h)
{
  // Icons must be multiples of 16 and square
  return (w == h) && ((w % 16) == 0) && ((h % 16) == 0);
}

static void *sdl_alloc_rgba_surface(png_uint_32 w, png_uint_32 h,
 png_uint_32 *stride, void **pixels)
{
  Uint32 rmask, gmask, bmask, amask;
  SDL_Surface *s;

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
  rmask = 0xff000000;
  gmask = 0x00ff0000;
  bmask = 0x0000ff00;
  amask = 0x000000ff;
#else
  rmask = 0x000000ff;
  gmask = 0x0000ff00;
  bmask = 0x00ff0000;
  amask = 0xff000000;
#endif

  s = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, 32, rmask, gmask, bmask, amask);
  if(!s)
    return NULL;

  *stride = s->pitch;
  *pixels = s->pixels;
  return s;
}

static SDL_Surface *png_read_icon(const char *name)
{
  return png_read_file(name, NULL, NULL, icon_w_h_constraint,
   sdl_alloc_rgba_surface);
}

#endif // CONFIG_PNG && CONFIG_SDL && CONFIG_ICON && !__WIN32__

bool init_video(struct config_info *conf, const char *caption)
{
  graphics.screen_mode = 0;
  graphics.fullscreen = conf->fullscreen;
  graphics.resolution_width = conf->resolution_width;
  graphics.resolution_height = conf->resolution_height;
  graphics.window_width = conf->window_width;
  graphics.window_height = conf->window_height;
  graphics.mouse_status = false;
  graphics.cursor_timestamp = get_ticks();
  graphics.cursor_flipflop = 1;
  graphics.system_mouse = conf->system_mouse;

  if(!set_graphics_output(conf))
    return false;

#ifdef CONFIG_SDL
  SDL_WM_SetCaption(caption, "");
#endif

  // These values (the defaults, actually) are special and tell MZX to try
  // to use the current desktop resolution as the fullscreen resolution
  if(conf->resolution_width == -1 && conf->resolution_height == -1)
  {
    graphics.resolution_width = 640;
    graphics.resolution_height = 480;

#ifdef CONFIG_SDL
#if !(SDL_MAJOR_VERSION == 1 && SDL_MINOR_VERSION == 2 && SDL_PATCHLEVEL < 10)
    // We'll only do this for hardware scaling renderers
    if(strcmp(conf->video_output, "software") != 0)
    {
      const SDL_VideoInfo *video_info = SDL_GetVideoInfo();
      graphics.resolution_width = video_info->current_w;
      graphics.resolution_height = video_info->current_h;
    }
#endif
#endif // CONFIG_SDL
  }

  if(!graphics.renderer.init_video(&graphics, conf))
  {
    // Try falling back to the first registered renderer
    strcpy(conf->video_output, "");
    if(!set_graphics_output(conf))
      return false;

    // Fallback failed; bail out
    if(!graphics.renderer.init_video(&graphics, conf))
      return false;
  }

#ifdef CONFIG_SDL
  if(!graphics.system_mouse)
    SDL_ShowCursor(SDL_DISABLE);
#endif

#if defined(CONFIG_SDL) && defined(CONFIG_ICON)
#ifdef __WIN32__
  {
    /* Roll our own icon code; the SDL stuff is a mess and for some
     * reason limits us to 256 colour icons (with keying but no alpha).
     *
     * We also pull off some nifty WIN32 hackery to load the executable's
     * icon resource; saves having an external dependency.
     */
    HICON icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(1));
    if(icon)
    {
      SDL_SysWMinfo info;

      SDL_VERSION(&info.version);
      SDL_GetWMInfo(&info);

      SendMessage(info.window, WM_SETICON, ICON_BIG, (LPARAM)icon);
    }
  }
#else // !__WIN32__
#if defined(CONFIG_PNG)
  {
    SDL_Surface *icon = png_read_icon("/usr/share/icons/megazeux.png");
    if(icon)
    {
      SDL_WM_SetIcon(icon, NULL);
      SDL_FreeSurface(icon);
    }
  }
#endif // CONFIG_PNG
#endif // __WIN32__
#endif // CONFIG_SDL && CONFIG_ICON

  ec_load_set_secondary(mzx_res_get_by_id(MZX_DEFAULT_CHR),
   graphics.default_charset);
  ec_load_set_secondary(mzx_res_get_by_id(MZX_EDIT_CHR),
   graphics.charset + (CHARSET_SIZE * CHAR_SIZE));

  ec_load_mzx();
  init_palette();
  graphics_was_initialized = true;
  return true;
}

bool has_video_initialized(void)
{
  return graphics_was_initialized;
}

bool set_video_mode(void)
{
  int target_width, target_height;
  int target_depth = graphics.bits_per_pixel;
  bool fullscreen = graphics.fullscreen;
  bool resize = graphics.allow_resize;

  if(fullscreen)
  {
    target_width = graphics.resolution_width;
    target_height = graphics.resolution_height;
  }
  else
  {
    target_width = graphics.window_width;
    target_height = graphics.window_height;
  }

  // If video mode fails, replace it with 'safe' defaults
  if(!(graphics.renderer.check_video_mode(&graphics,
    target_width, target_height, target_depth, fullscreen, resize)))
  {
    target_width = 640;
    target_height = 350;
    target_depth = 8;
    fullscreen = false;
    resize = false;

    graphics.resolution_width = target_width;
    graphics.resolution_height = target_height;
    graphics.bits_per_pixel = target_depth;
    graphics.fullscreen = fullscreen;
  }

  return graphics.renderer.set_video_mode(&graphics,
   target_width, target_height, target_depth, fullscreen, resize);
}

#if 0

static bool change_video_output(struct config_info *conf, const char *output)
{
  char old_video_output[16];

  strncpy(old_video_output, conf->video_output, 16);
  old_video_output[15] = 0;

  strncpy(conf->video_output, output, 16);
  conf->video_output[15] = 0;

  if(graphics.renderer.free_video)
    graphics.renderer.free_video(&graphics);

  set_graphics_output(conf);

  if(!graphics.renderer.init_video(&graphics, conf))
  {
    strcpy(conf->video_output, old_video_output);
    if(!set_graphics_output(conf))
    {
      warn("Failed to roll back renderer, aborting!\n");
      exit(0);
    }

    if(!graphics.renderer.init_video(&graphics, conf))
    {
      warn("Failed to roll back video mode, aborting!\n");
      exit(0);
    }
  }

  return true;
}

#endif

void toggle_fullscreen(void)
{
  graphics.fullscreen = !graphics.fullscreen;
  set_video_mode();
  update_screen();
  update_palette();
}

void resize_screen(Uint32 w, Uint32 h)
{
  if(!graphics.fullscreen && graphics.allow_resize)
  {
    graphics.window_width = w;
    graphics.window_height = h;
    set_video_mode();
    graphics.renderer.resize_screen(&graphics, w, h);
  }
}

void color_string_ext_special(const char *str, Uint32 x, Uint32 y,
 Uint8 *color, Uint32 offset, Uint32 c_offset, bool respect_newline)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  const char *src = str;
  Uint8 cur_char = *src;
  Uint8 next;
  Uint8 bg_color = (*color >> 4) + c_offset;
  Uint8 fg_color = (*color & 0x0F) + c_offset;

  char next_str[2];
  next_str[1] = 0;

  while(cur_char)
  {
    switch(cur_char)
    {
      // Color character
      case '@':
      {
        str++;
        next = *str;

        // If 0, stop right there
        if(!next)
          goto exit_out;

        // If the next isn't hex, count as one
        if(isxdigit(next))
        {
          next_str[0] = next;
          bg_color = (Uint8)(strtol(next_str, NULL, 16) + c_offset);
        }
        else
        {
          if(next == '@')
          {
            dest->char_value = '@' + offset;
            dest->bg_color = bg_color;
            dest->fg_color = fg_color;
            dest++;
          }
          else
          {
            str--;
          }
        }

        break;
      }
      case '~':
      {
        str++;
        next = *str;

        // If 0, stop right there
        if(!next)
          goto exit_out;

        // If the next isn't hex, count as one
        if(isxdigit(next))
        {
          next_str[0] = next;
          fg_color = (Uint8)(strtol(next_str, NULL, 16) + c_offset);
        }
        else
        {
          if(next == '~')
          {
            dest->char_value = '~' + offset;
            dest->bg_color = bg_color;
            dest->fg_color = fg_color;
            dest++;
          }
          else
          {
            str--;
          }
        }

        break;
      }
      // Newline
      case '\n':
      {
        if(respect_newline)
        {
          y++;
          dest = graphics.text_video + (y * SCREEN_W) + x;
          break;
        }
        // Fall thru
      }
      default:
      {
        dest->char_value = cur_char + offset;
        dest->bg_color = bg_color;
        dest->fg_color = fg_color;
        dest++;
      }
    }
    if(dest >= graphics.text_video + (SCREEN_W * SCREEN_H))
      break;

    str++;
    cur_char = *str;
  }

exit_out:
  *color = (((bg_color - c_offset) << 4) & 0xF0) |
           (((fg_color - c_offset) << 0) & 0x0F);
}

void color_string_ext(const char *str, Uint32 x, Uint32 y, Uint8 color,
 Uint32 offset, Uint32 c_offset, bool respect_newline)
{
  color_string_ext_special(str, x, y, &color, offset,
   c_offset, respect_newline);
}

// Write a normal string

void write_string_ext(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed, Uint32 offset,
 Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  const char *src = str;
  Uint8 cur_char = *src;
  Uint8 bg_color = (color >> 4) + c_offset;
  Uint8 fg_color = (color & 0x0F) + c_offset;

  while(cur_char && (cur_char != 0))
  {
    switch(cur_char)
    {
      // Newline
      case '\n':
      {
        y++;
        dest = graphics.text_video + (y * SCREEN_W) + x;
        break;
      }
      case '\t':
      {
        if(tab_allowed)
        {
          dest += 5;
          break;
        }
      }
      default:
      {
        dest->char_value = cur_char + offset;
        dest->bg_color = bg_color;
        dest->fg_color = fg_color;
        dest++;
      }
    }
    if(dest >= graphics.text_video + (SCREEN_W * SCREEN_H))
      break;

    str++;
    cur_char = *str;
  }
}

void write_string_mask(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  const char *src = str;
  Uint8 cur_char = *src;
  Uint8 bg_color = (color >> 4) + 16;
  Uint8 fg_color = (color & 0x0F) + 16;

  while(cur_char && (cur_char != 0))
  {
    switch(cur_char)
    {
      // Newline
      case '\n':
      {
        y++;
        dest = graphics.text_video + (y * SCREEN_W) + x;
        break;
      }
      case '\t':
      {
        if(tab_allowed)
        {
          dest += 5;
          break;
        }
      }
      default:
      {
        if((cur_char >= 32) && (cur_char <= 127))
          dest->char_value = cur_char + 256;
        else
          dest->char_value = cur_char;

        dest->bg_color = bg_color;
        dest->fg_color = fg_color;
        dest++;
      }
    }
    if(dest >= graphics.text_video + (SCREEN_W * SCREEN_H))
      break;

    str++;
    cur_char = *str;
  }
}

// Write a normal string, without carriage returns

void write_line_ext(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed, Uint32 offset,
 Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  const char *src = str;
  Uint8 cur_char = *src;
  Uint8 bg_color = (color >> 4) + c_offset;
  Uint8 fg_color = (color & 0x0F) + c_offset;

  while(cur_char && (cur_char != '\n'))
  {
    switch(cur_char)
    {
      // Color character
      case '\t':
      {
        if(tab_allowed)
        {
          dest += 10;
          break;
        }
      }
      default:
      {
        dest->char_value = cur_char + offset;
        dest->bg_color = bg_color;
        dest->fg_color = fg_color;
        dest++;
      }
    }
    str++;
    cur_char = *str;
  }
}

void write_line_mask(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  const char *src = str;
  Uint8 cur_char = *src;
  Uint8 bg_color = (color >> 4) + 16;
  Uint8 fg_color = (color & 0x0F) + 16;

  while(cur_char && (cur_char != '\n'))
  {
    switch(cur_char)
    {
      // Color character
      case '\t':
      {
        if(tab_allowed)
        {
          dest += 10;
          break;
        }
      }
      default:
      {
        if((cur_char >= 32) && (cur_char <= 127))
          dest->char_value = cur_char + 256;
        else
          dest->char_value = cur_char;

        dest->bg_color = bg_color;
        dest->fg_color = fg_color;
        dest++;
      }
    }
    str++;
    cur_char = *str;
  }
}

// Set rightalign to print the rightmost char at xy and proceed to the left
// minlen is the minimum length to print. Pad with 0.

void write_number(int number, char color, int x, int y,
 int minlen, int rightalign, int base)
{
  char temp[12];
  int t1, t2;

  if(base == 10)
    snprintf(temp, 12, "%d", number);
  else
    snprintf(temp, 12, "%x", number);

  temp[11] = 0;

  if(rightalign)
  {
    t1 = (int)strlen(temp);
    if(minlen > t1)
      t1 = minlen;
    x -= t1 - 1;
  }

  if((t2 = (int)strlen(temp)) < minlen)
  {
    t2 = minlen - t2;
    for(t1 = 0; t1 < t2; t1++)
      draw_char('0', color, x++, y);
  }

  write_string(temp, x, y, color, 0);
}

static void color_line_ext(Uint32 length, Uint32 x, Uint32 y,
 Uint8 color, Uint32 offset, Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + (y * 80) + x;
  Uint8 bg_color = (color >> 4) + c_offset;
  Uint8 fg_color = (color & 0x0F) + c_offset;
  Uint32 i;

  for(i = 0; i < length; i++)
  {
    dest->bg_color = bg_color;
    dest->fg_color = fg_color;
    dest++;
  }
}

void fill_line_ext(Uint32 length, Uint32 x, Uint32 y,
 Uint8 chr, Uint8 color, Uint32 offset, Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  Uint8 bg_color = (color >> 4) + c_offset;
  Uint8 fg_color = (color & 0x0F) + c_offset;
  Uint32 i;

  for(i = 0; i < length; i++)
  {
    dest->char_value = chr + offset;
    dest->bg_color = bg_color;
    dest->fg_color = fg_color;
    dest++;
  }
}

void draw_char_ext(Uint8 chr, Uint8 color, Uint32 x,
 Uint32 y, Uint32 offset, Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + (y * SCREEN_W) + x;
  dest->char_value = chr + offset;
  dest->bg_color = (color >> 4) + c_offset;
  dest->fg_color = (color & 0x0F) + c_offset;
}

void draw_char_linear_ext(Uint8 color, Uint8 chr,
 Uint32 offset, Uint32 offset_b, Uint32 c_offset)
{
  struct char_element *dest = graphics.text_video + offset;
  dest->char_value = chr + offset_b;
  dest->bg_color = (color >> 4) + c_offset;
  dest->fg_color = (color & 0x0F) + c_offset;
}

void color_string(const char *string, Uint32 x, Uint32 y, Uint8 color)
{
  color_string_ext(string, x, y, color, 256, 16, false);
}

void write_string(const char *string, Uint32 x, Uint32 y, Uint8 color,
 Uint32 tab_allowed)
{
  write_string_ext(string, x, y, color, tab_allowed, 256, 16);
}

void color_line(Uint32 length, Uint32 x, Uint32 y, Uint8 color)
{
  color_line_ext(length, x, y, color, 256, 16);
}

void fill_line(Uint32 length, Uint32 x, Uint32 y, Uint8 chr,
 Uint8 color)
{
  fill_line_ext(length, x, y, chr, color, 256, 16);
}
void draw_char(Uint8 chr, Uint8 color, Uint32 x, Uint32 y)
{
  draw_char_ext(chr, color, x, y, 256, 16);
}

Uint8 get_color_linear(Uint32 offset)
{
  struct char_element *dest = graphics.text_video + offset;
  return (dest->bg_color << 4) | (dest->fg_color & 0x0F);
}

void clear_screen(Uint8 chr, Uint8 color)
{
  Uint32 i;
  Uint8 fg_color = color & 0x0F;
  Uint8 bg_color = color >> 4;
  struct char_element *dest = graphics.text_video;

  for(i = 0; i < (SCREEN_W * SCREEN_H); i++)
  {
    dest->char_value = chr;
    dest->fg_color = fg_color;
    dest->bg_color = bg_color;
    dest++;
  }
  update_screen();
}

void set_screen(struct char_element *src)
{
  memcpy(graphics.text_video, src,
   SCREEN_W * SCREEN_H * sizeof(struct char_element));
}

void get_screen(struct char_element *dest)
{
  memcpy(dest, graphics.text_video,
   SCREEN_W * SCREEN_H * sizeof(struct char_element));
}

void cursor_underline(void)
{
  graphics.cursor_mode = cursor_mode_underline;
}

void cursor_solid(void)
{
  graphics.cursor_mode = cursor_mode_solid;
}

void cursor_off(void)
{
  graphics.cursor_mode = cursor_mode_invisible;
}

void move_cursor(Uint32 x, Uint32 y)
{
  graphics.cursor_x = x;
  graphics.cursor_y = y;
}

#ifdef CONFIG_HELPSYS

void set_cursor_mode(enum cursor_mode_types mode)
{
  graphics.cursor_mode = mode;
}

enum cursor_mode_types get_cursor_mode(void)
{
  return graphics.cursor_mode;
}

#endif // CONFIG_HELPSYS

void m_hide(void)
{
  if(!graphics.system_mouse)
    graphics.mouse_status = false;
}

void m_show(void)
{
  if(!graphics.system_mouse)
    graphics.mouse_status = true;
}

#ifdef CONFIG_PNG

#define DUMP_FMT_EXT "png"

/* Trivial PNG dumper; this routine is a modification (simplification) of
 * code pinched from http://www2.autistici.org/encelo/prog_sdldemos.php.
 *
 * Palette support was added, the original support was broken.
 *
 * Copyright (C) 2006 Angelo "Encelo" Theodorou
 * Copyright (C) 2007 Alistair John Strachan <alistair@devzero.co.uk>
 */
static void dump_screen_real(Uint8 *pix, struct rgb_color *pal, int count,
 const char *name)
{
  png_write_screen(pix, pal, count, name);
}

#else

#define DUMP_FMT_EXT "bmp"

static void dump_screen_real(Uint8 *pix, struct rgb_color *pal, int count,
 const char *name)
{
  FILE *file;
  int i;

  file = fopen_unsafe(name, "wb");
  if(!file)
    return;

  // BMP header
  fputw(0x4D42, file); // BM
  fputd(14 + 40 + count * 4 + 640 * 350, file); // BMP + DIB + palette + image
  fputd(0, file); // Reserved
  fputd(14, file); // DIB header offset

  // DIB header
  fputd(40, file); // DIB header size (Windows 3/BITMAPINFOHEADER)
  fputd(640, file); // Width in pixels
  fputd(350, file); // Height in pixels
  fputw(1, file); // Number of color planes
  fputw(8, file); // Bits per pixel
  fputd(0, file); // Compression method (none)
  fputd(640 * 350, file); // Image data size
  fputd(3780, file); // Horizontal dots per meter
  fputd(3780, file); // Vertical dots per meter
  fputd(count, file); // Number of colors in palette
  fputd(0, file); // Number of important colors

  // Color palette
  for(i = 0; i < count; i++)
  {
    fputc(pal[i].b, file);
    fputc(pal[i].g, file);
    fputc(pal[i].r, file);
    fputc(0, file);
  }

  // Image data
  for(i = 349; i >= 0; i--)
  {
    fwrite(pix + i * 640, sizeof(Uint8), 640, file);
  }

  fclose(file);
}

#endif // CONFIG_PNG

#define MAX_NAME_SIZE 16

void dump_screen(void)
{
  struct rgb_color palette[SMZX_PAL_SIZE];
  char name[MAX_NAME_SIZE];
  struct stat file_info;
  Uint8 *ss;
  int i;

  for(i = 0; i < 99999; i++)
  {
    snprintf(name, MAX_NAME_SIZE - 1, "screen%d.%s", i, DUMP_FMT_EXT);
    name[MAX_NAME_SIZE - 1] = '\0';
    if(stat(name, &file_info))
      break;
  }

  ss = cmalloc(sizeof(Uint8) * 640 * 350);
  if(ss)
  {
    render_graph8(ss, 640, &graphics, set_colors8[graphics.screen_mode]);
    dump_screen_real(ss, palette, make_palette(palette), name);
    free(ss);
  }
}

void get_screen_coords(int screen_x, int screen_y, int *x, int *y,
 int *min_x, int *min_y, int *max_x, int *max_y)
{
  graphics.renderer.get_screen_coords(&graphics, screen_x, screen_y, x, y,
   min_x, min_y, max_x, max_y);
}

void set_screen_coords(int x, int y, int *screen_x, int *screen_y)
{
  graphics.renderer.set_screen_coords(&graphics, x, y, screen_x, screen_y);
}

void set_mouse_mul(int width_mul, int height_mul)
{
  graphics.mouse_width_mul = width_mul;
  graphics.mouse_height_mul = height_mul;
}

void focus_screen(int x, int y)
{
  int pixel_x = x * graphics.resolution_width  / SCREEN_W +
                    graphics.resolution_width  / SCREEN_W / 2;
  int pixel_y = y * graphics.resolution_height / SCREEN_H +
                    graphics.resolution_height / SCREEN_H / 2;
  focus_pixel(pixel_x, pixel_y);
}

void focus_pixel(int x, int y)
{
  if(graphics.renderer.focus_pixel)
    graphics.renderer.focus_pixel(&graphics, x, y);
}


code:
/* MegaZeux
 *
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __GRAPHICS_H
#define __GRAPHICS_H

#include "compat.h"

__M_BEGIN_DECLS

#include "platform.h"
#include "configure.h"

enum cursor_mode_types
{
  cursor_mode_underline,
  cursor_mode_solid,
  cursor_mode_invisible
};

struct rgb_color
{
  Uint8 r;
  Uint8 g;
  Uint8 b;
  Uint8 unused;
};

struct char_element
{
  Uint16 char_value;
  Uint8 bg_color;
  Uint8 fg_color;
};

#define SCREEN_W 80
#define SCREEN_H 25

#define CHAR_W 8
#define CHAR_H 14

#define CHAR_SIZE 14
#define CHARSET_SIZE 256
#define NUM_CHARSETS 16

#define PAL_SIZE 16
#define NUM_PALS 2
#define SMZX_PAL_SIZE 256

struct graphics_data;

struct renderer
{
  bool (*init_video)       (struct graphics_data *, struct config_info *);
  void (*free_video)       (struct graphics_data *);
  bool (*check_video_mode) (struct graphics_data *, int, int, int, bool, bool);
  bool (*set_video_mode)   (struct graphics_data *, int, int, int, bool, bool);
  void (*update_colors)    (struct graphics_data *, struct rgb_color *,
                             Uint32);
  void (*resize_screen)    (struct graphics_data *, int, int);
  void (*remap_charsets)   (struct graphics_data *);
  void (*remap_char)       (struct graphics_data *, Uint16 chr);
  void (*remap_charbyte)   (struct graphics_data *, Uint16 chr, Uint8 byte);
  void (*get_screen_coords)(struct graphics_data *, int, int, int *, int *,
                             int *, int *, int *, int *);
  void (*set_screen_coords)(struct graphics_data *, int, int, int *, int *);
  void (*render_graph)     (struct graphics_data *);
  void (*render_cursor)    (struct graphics_data *, Uint32, Uint32, Uint8,
                             Uint8, Uint8);
  void (*render_mouse)     (struct graphics_data *, Uint32, Uint32, Uint8,
                             Uint8);
  void (*sync_screen)      (struct graphics_data *);
  void (*focus_pixel)      (struct graphics_data *, Uint32, Uint32);
};

struct graphics_data
{
  Uint32 screen_mode;
  struct char_element text_video[SCREEN_W * SCREEN_H];
  Uint8 charset[CHAR_SIZE * CHARSET_SIZE * NUM_CHARSETS];
  struct rgb_color palette[SMZX_PAL_SIZE];
  struct rgb_color intensity_palette[SMZX_PAL_SIZE];
  struct rgb_color backup_palette[SMZX_PAL_SIZE];
  struct rgb_color editor_backup_palette[SMZX_PAL_SIZE];
  Uint32 current_intensity[SMZX_PAL_SIZE];
  Uint32 saved_intensity[SMZX_PAL_SIZE];
  Uint32 backup_intensity[SMZX_PAL_SIZE];

  enum cursor_mode_types cursor_mode;
  Uint32 fade_status;
  Uint32 cursor_x;
  Uint32 cursor_y;
  Uint32 mouse_width_mul;
  Uint32 mouse_height_mul;
  bool mouse_status;
  bool system_mouse;
  bool fullscreen;
  Uint32 resolution_width;
  Uint32 resolution_height;
  Uint32 window_width;
  Uint32 window_height;
  Uint32 bits_per_pixel;
  bool allow_resize;
  Uint32 cursor_timestamp;
  Uint32 cursor_flipflop;
  Uint32 default_smzx_loaded;
  char *gl_filter_method;
  int gl_vsync;

  Uint8 default_charset[CHAR_SIZE * CHARSET_SIZE];

  Uint32 flat_intensity_palette[SMZX_PAL_SIZE];
  struct renderer renderer;
  void *render_data;
};

CORE_LIBSPEC void color_string(const char *string, Uint32 x, Uint32 y,
 Uint8 color);
CORE_LIBSPEC void write_string(const char *string, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed);
CORE_LIBSPEC void write_number(int number, char color, int x, int y,
 int minlen, int rightalign, int base);
CORE_LIBSPEC void color_line(Uint32 length, Uint32 x, Uint32 y, Uint8 color);
CORE_LIBSPEC void fill_line(Uint32 length, Uint32 x, Uint32 y, Uint8 chr,
 Uint8 color);
CORE_LIBSPEC void draw_char(Uint8 chr, Uint8 color, Uint32 x, Uint32 y);

CORE_LIBSPEC void write_string_ext(const char *string, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed, Uint32 offset, Uint32 c_offset);
CORE_LIBSPEC void draw_char_ext(Uint8 chr, Uint8 color, Uint32 x,
 Uint32 y, Uint32 offset, Uint32 c_offset);
CORE_LIBSPEC void draw_char_linear_ext(Uint8 color, Uint8 chr,
 Uint32 offset, Uint32 offset_b, Uint32 c_offset);
CORE_LIBSPEC void write_string_mask(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed);

CORE_LIBSPEC void clear_screen(Uint8 chr, Uint8 color);

CORE_LIBSPEC void cursor_solid(void);
CORE_LIBSPEC void cursor_off(void);
CORE_LIBSPEC void move_cursor(Uint32 x, Uint32 y);

CORE_LIBSPEC bool init_video(struct config_info *conf, const char *caption);
CORE_LIBSPEC bool has_video_initialized(void);
CORE_LIBSPEC void update_screen(void);

CORE_LIBSPEC void ec_read_char(Uint8 chr, char *matrix);
CORE_LIBSPEC void ec_change_char(Uint8 chr, char *matrix);
CORE_LIBSPEC Sint32 ec_load_set_var(char *name, Uint8 pos);
CORE_LIBSPEC void ec_mem_load_set(Uint8 *chars);

CORE_LIBSPEC void update_palette(void);
CORE_LIBSPEC void set_gui_palette(void);
CORE_LIBSPEC void load_palette(const char *fname);
CORE_LIBSPEC void smzx_palette_loaded(int val);
CORE_LIBSPEC void set_screen_mode(Uint32 mode);
CORE_LIBSPEC Uint32 get_screen_mode(void);
CORE_LIBSPEC void set_palette_intensity(Uint32 percent);
CORE_LIBSPEC void set_rgb(Uint32 color, Uint32 r, Uint32 g, Uint32 b);
CORE_LIBSPEC void set_red_component(Uint32 color, Uint32 r);
CORE_LIBSPEC void set_green_component(Uint32 color, Uint32 g);
CORE_LIBSPEC void set_blue_component(Uint32 color, Uint32 b);
CORE_LIBSPEC void get_rgb(Uint32 color, Uint8 *r, Uint8 *g, Uint8 *b);
CORE_LIBSPEC Uint32 get_red_component(Uint32 color);
CORE_LIBSPEC Uint32 get_green_component(Uint32 color);
CORE_LIBSPEC Uint32 get_blue_component(Uint32 color);
CORE_LIBSPEC void vquick_fadeout(void);
CORE_LIBSPEC void insta_fadein(void);
CORE_LIBSPEC void insta_fadeout(void);
CORE_LIBSPEC void default_palette(void);

CORE_LIBSPEC void m_hide(void);
CORE_LIBSPEC void m_show(void);

CORE_LIBSPEC void set_mouse_mul(int width_mul, int height_mul);

void color_string_ext(const char *string, Uint32 x, Uint32 y,
 Uint8 color, Uint32 offset, Uint32 c_offset, bool respect_newline);
void color_string_ext_special(const char *string, Uint32 x, Uint32 y,
 Uint8 *color, Uint32 offset, Uint32 c_offset, bool respect_newline);
void write_line_ext(const char *string, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed, Uint32 offset,
 Uint32 c_offset);
void fill_line_ext(Uint32 length, Uint32 x, Uint32 y,
 Uint8 chr, Uint8 color, Uint32 offset, Uint32 c_offset);
void write_line_mask(const char *str, Uint32 x, Uint32 y,
 Uint8 color, Uint32 tab_allowed);

Uint8 get_color_linear(Uint32 offset);

void cursor_underline(void);

bool set_video_mode(void);
void toggle_fullscreen(void);
void resize_screen(Uint32 w, Uint32 h);
void set_screen(struct char_element *src);
void get_screen(struct char_element *dest);

void ec_change_byte(Uint8 chr, Uint8 byte, Uint8 new_value);
Uint8 ec_read_byte(Uint8 chr, Uint8 byte);
Sint32 ec_load_set(char *name);
void ec_mem_save_set(Uint8 *chars);

void set_color_intensity(Uint32 color, Uint32 percent);
Uint32 get_color_intensity(Uint32 color);
Uint32 get_fade_status(void);
void dialog_fadein(void);
void dialog_fadeout(void);
void vquick_fadein(void);
void dump_screen(void);

void get_screen_coords(int screen_x, int screen_y, int *x, int *y,
 int *min_x, int *min_y, int *max_x, int *max_y);
void set_screen_coords(int x, int y, int *screen_x, int *screen_y);

/* Renderers might have the facility to center a screen on a given
 * region of the window. Currently only the NDS renderer implements
 * this feature.
 */
void focus_screen(int x, int y); // Board coordinates
void focus_pixel(int x, int y);  // Pixel coordinates

#ifdef CONFIG_EDITOR
CORE_LIBSPEC extern struct graphics_data graphics;
CORE_LIBSPEC void ec_load_mzx(void);
CORE_LIBSPEC void ec_load_set_secondary(const char *name, Uint8 *dest);
#endif // CONFIG_EDITOR

#ifdef CONFIG_HELPSYS
void set_cursor_mode(enum cursor_mode_types mode);
enum cursor_mode_types get_cursor_mode(void);
#endif // CONFIG_HELPSYS

__M_END_DECLS

#endif // __GRAPHICS_H

repo_name: pgimeno/megazeux

license: mit


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

// internal help system

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "scrdisp.h"
#include "helpsys.h"
#include "data.h"
#include "graphics.h"
#include "world.h"
#include "window.h"

static char *help;

void help_open(struct world *mzx_world, const char *file_name)
{
  mzx_world->help_file = fopen_unsafe(file_name, "rb");
  if(!mzx_world->help_file)
    return;

  help = cmalloc(1024 * 64);
}

void help_close(struct world *mzx_world)
{
  if(!mzx_world->help_file)
    return;

  fclose(mzx_world->help_file);
  free(help);
}

void help_system(struct world *mzx_world)
{
  char file[13], file2[13], label[13];
  int where, offs, size, t1, t2;
  enum cursor_mode_types old_cmode;
  FILE *fp;

  fp = mzx_world->help_file;
  if(!fp)
    return;

  old_cmode = get_cursor_mode();

  rewind(fp);
  t1 = fgetw(fp);
  fseek(fp, t1 * 21 + 4 + get_context() * 12, SEEK_SET);

  // At proper context info
  where = fgetd(fp);    // Where file to load is
  size = fgetd(fp);     // Size of file to load
  offs = fgetd(fp);     // Offset within file of link

  // Jump to file
  fseek(fp, where, SEEK_SET);
  // Read it in
  fread(help, 1, size, fp);
  // Display it
  cursor_off();

labelled:
  help_display(mzx_world, help, offs, file, label);

  // File?
  if(file[0])
  {
    // Yep. Search for file.
    fseek(fp, 2, SEEK_SET);
    for(t2 = 0; t2 < t1; t2++)
    {
      fread(file2, 1, 13, fp);
      if(!strcmp(file, file2))
        break;
      fseek(fp, 8, SEEK_CUR);
    }

    if(t2 < t1)
    {
      // Found file.
      where = fgetd(fp);
      size = fgetd(fp);
      fseek(fp, where, SEEK_SET);
      fread(help, 1, size, fp);

      // Search for label
      for(t2 = 0; t2 < size; t2++)
      {
        if(help[t2] != 255)
          continue;
        if(help[t2 + 1] != ':')
          continue;
        if(!strcmp(help + t2 + 3, label))
          break; // Found label!
      }

      if(t2 < size)
      {
        // Found label. t2 is offset.
        offs = t2;
        goto labelled;
      }
    }
  }

  set_cursor_mode(old_cmode);
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __HELPSYS_H
#define __HELPSYS_H

#include "compat.h"

__M_BEGIN_DECLS

#include "world_struct.h"

#ifdef CONFIG_HELPSYS

CORE_LIBSPEC void help_open(struct world *mzx_world, const char *file_name);
CORE_LIBSPEC void help_close(struct world *mzx_world);
CORE_LIBSPEC void help_system(struct world *mzx_world);

#endif

__M_END_DECLS

#endif // __HELPSYS_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 * Copyright (C) 2004 Gilead Kutnick <exophase@adelphia.net>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "idarray.h"
#include "data.h"
#include "const.h"
#include "util.h"

// Place an id/color/param combo in an array position, moving current to
// "under" status if possible, and clearing original "under". If placing an
// "under", then automatically clear lower no matter what. Also mark as
// updated.

void id_place(struct world *mzx_world, int array_x, int array_y,
 enum thing id, char color, char param)
{
  struct board *src_board = mzx_world->current_board;
  int offset;

  array_x = CLAMP(array_x, 0, src_board->board_width - 1);
  array_y = CLAMP(array_y, 0, src_board->board_height - 1);

  offset = (array_y * src_board->board_width) + array_x;
  offs_place_id(mzx_world, offset, id, color, param);
}

// Place ID using an offset instead of coordinate
void offs_place_id(struct world *mzx_world, unsigned int offset,
 enum thing id, char color, char param)
{
  struct board *src_board = mzx_world->current_board;
  enum thing p_id = (enum thing)src_board->level_id[offset];
  int p_flag = flags[p_id];
  int d_flag = flags[(int)id];

  // Mark as updated
  mzx_world->update_done[offset] = 1;

  // Is it a sensor and is the player being put on it?
  // Or, can it be moved under and can the new item not be moved under?
  if(((p_flag & A_SPEC_STOOD) && (id == PLAYER)) ||
   ((p_flag & A_UNDER) && !(d_flag & A_UNDER)))
  {
    // If moving the player down, move what's there underneath the player
    // into the under_player variables
    if(id == PLAYER)
    {
      mzx_world->under_player_id = src_board->level_under_id[offset];
      mzx_world->under_player_param = src_board->level_under_param[offset];
      mzx_world->under_player_color = src_board->level_under_color[offset];
    }

    // Then move what's currently on top under
    src_board->level_under_id[offset] = p_id;
    src_board->level_under_param[offset] = src_board->level_param[offset];
    src_board->level_under_color[offset] = src_board->level_color[offset];
  }
  else
  {
    // Clear anything that might be underneath
    src_board->level_under_id[offset] = 0;
    src_board->level_under_param[offset] = 0;
    src_board->level_under_color[offset] = 7;
  }

  // Put new combo
  src_board->level_id[offset] = (char)id;
  src_board->level_param[offset] = param;
  src_board->level_color[offset] = color;
}

// Remove the top thing at a position
void id_remove_top(struct world *mzx_world, int array_x, int array_y)
{
  struct board *current_board = mzx_world->current_board;
  int offset = (array_y * current_board->board_width) + array_x;

  offs_remove_id(mzx_world, offset);
}

// Remove the top thing at an offset
void offs_remove_id(struct world *mzx_world, unsigned int offset)
{
  struct board *src_board = mzx_world->current_board;
  enum thing id = (enum thing)src_board->level_id[offset];

  src_board->level_id[offset] = src_board->level_under_id[offset];
  src_board->level_param[offset] = src_board->level_under_param[offset];
  src_board->level_color[offset] = src_board->level_under_color[offset];

  if(id == PLAYER)
  {
    // Removing the player? Then put the under_player stuff under
    src_board->level_under_id[offset] = mzx_world->under_player_id;
    src_board->level_under_param[offset] = mzx_world->under_player_param;
    src_board->level_under_color[offset] = mzx_world->under_player_color;

    mzx_world->under_player_id = (char)SPACE;
    mzx_world->under_player_param = 0;
    mzx_world->under_player_color = 7;
  }
  else
  {
    src_board->level_under_id[offset] = (char)SPACE;
    src_board->level_under_param[offset] = 0;
    src_board->level_under_color[offset] = 7;
  }
}

void id_remove_under(struct world *mzx_world, int array_x, int array_y)
{
  struct board *src_board = mzx_world->current_board;
  int offset = (array_y * src_board->board_width) + array_x;

  // If removing something under the player place this stuff instead
  if((enum thing)src_board->level_id[offset] == PLAYER)
  {
    src_board->level_under_id[offset] = mzx_world->under_player_id;
    src_board->level_under_param[offset] = mzx_world->under_player_param;
    src_board->level_under_color[offset] = mzx_world->under_player_color;
    mzx_world->under_player_id = (char)SPACE;
    mzx_world->under_player_param = 0;
    mzx_world->under_player_color = 0;
  }
  else
  {
    src_board->level_under_id[offset] = (char)SPACE;
    src_board->level_under_param[offset] = 0;
    src_board->level_under_color[offset] = 7;
  }

  mzx_world->update_done[offset] = 1;
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declarations for IDARRAY.ASM */

#ifndef __IDARRAY_H
#define __IDARRAY_H

#include "compat.h"

__M_BEGIN_DECLS

#include "world_struct.h"

CORE_LIBSPEC void id_remove_top(struct world *mzx_world,
 int array_x, int array_y);

void id_place(struct world *mzx_world, int array_x, int array_y,
 enum thing id, char color, char param);
void offs_place_id(struct world *mzx_world, unsigned int offset,
 enum thing id, char color, char param);
void offs_remove_id(struct world *mzx_world, unsigned int offset);
void id_remove_under(struct world *mzx_world, int array_x, int array_y);

__M_END_DECLS

#endif // __IDARRAY_H


code:
/* MegaZeux
 *
 * Copyright (C) 1998 MenTaLguY - mentalg@geocities.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include <stdlib.h>

#include "const.h"
#include "graphics.h"
#include "data.h"
#include "idput.h"
#include "world.h"
#include "board.h"

#define thin_line            128
#define thick_line           144
#define ice_anim             160
#define lava_anim            164
#define low_ammo             167
#define hi_ammo              168
#define lit_bomb             169
#define energizer_glow       176
#define explosion_colors     184
#define horiz_door           188
#define vert_door            189
#define cw_anim              190
#define ccw_anim             194
#define open_door            198
#define trans_north          230
#define trans_south          234
#define trans_east           238
#define trans_west           242
#define trans_all            246
#define thick_arrow          250
#define thin_arrow           254
#define horiz_lazer          258
#define vert_lazer           262
#define fire_anim            266
#define fire_colors          272
#define life_anim            278
#define life_colors          282
#define ricochet_panels      286
#define mine_anim            288
#define shooting_fire_anim   290
#define shooting_fire_colors 292
#define seeker_anim          294
#define seeker_colors        298
#define whirlpool_glow       302

unsigned char id_chars[455];

unsigned char bullet_color[3] = { 15, 15, 15 };
unsigned char missile_color = 8;
unsigned char id_dmg[128];

static unsigned char get_special_id_char(struct board *src_board,
 enum thing cell_id, char param, int offset)
{
  char *level_id = src_board->level_id;
  int board_width = src_board->board_width;
  int board_height = src_board->board_height;

  switch(cell_id)
  {
    case LINE:
    {
      int array_x = offset % board_width;
      int array_y = offset / board_width;

      int bits = 0;
      char *ptr = level_id + offset;

      if(array_y > 0)
      {
        if(*(ptr - board_width) == 4)
          bits = 1;
      }
      else
      {
        bits = 1;
      }

      if(array_y < (board_height - 1))
      {
        if(*(ptr + board_width) == 4)
          bits |= 2;
      }
      else
      {
        bits |= 2;
      }

      if(array_x > 0)
      {
        if(*(ptr - 1) == 4)
          bits |= 8;
      }
      else
      {
        bits |= 8;
      }

      if(array_x < (board_width - 1))
      {
        if(*(ptr + 1) == 4 )
          bits |= 4;
      }
      else
      {
        bits |= 4;
      }

      return id_chars[thick_line + bits];
    }

    case WEB:
    case THICK_WEB:
    {
      int array_x = offset % board_width;
      int array_y = offset / board_width;
      int bits = 0;

      char *ptr;
      ptr = level_id + offset;

      if(array_y > 0 )
      {
        if(*(ptr - board_width) != 0)
          bits = 1;
      }
      else
      {
        bits = 1;
      }

      if(array_y < (board_height - 1))
      {
        if(*(ptr + board_width) != 0)
          bits |= 2;
      }
      else
      {
        bits |= 2;
      }

      if(array_x > 0)
      {
        if(*(ptr - 1) != 0)
          bits |= 8;
      }
      else
      {
        bits |= 8;
      }

      if(array_x < (board_width - 1))
      {
        if(*(ptr + 1) != 0)
          bits |= 4;
      }
      else
      {
        bits |= 4;
      }

      if(cell_id == 18)
        return id_chars[thin_line + bits];
      return id_chars[thick_line + bits];
    }

    case ICE:
    {
      return id_chars[ice_anim + param];
    }

    case LAVA:
    {
      return id_chars[lava_anim + param];
    }

    case AMMO:
    {
      if(param < 10)
        return id_chars[low_ammo];
      else
        return id_chars[hi_ammo];
    }

    case LIT_BOMB:
    {
      return id_chars[lit_bomb + (param & 0x0F)];
    }

    case DOOR:
    {
      if(param & 1)
        return id_chars[vert_door];
      else
        return id_chars[horiz_door];
    }

    case OPEN_DOOR:
    {
      return id_chars[open_door + (param & 0x1F)];
    }

    case CW_ROTATE:
    {
      return id_chars[cw_anim + param];
    }

    case CCW_ROTATE:
    {
      return id_chars[ccw_anim + param];
    }

    case TRANSPORT:
    {
      switch(param & 0x07)
      {
        case 0:
          return id_chars[trans_north + ((param >> 3) & 0x03)];
        case 1:
          return id_chars[trans_south + ((param >> 3) & 0x03)];
        case 2:
          return id_chars[trans_east  + ((param >> 3) & 0x03)];
        case 3:
          return id_chars[trans_west  + ((param >> 3) & 0x03)];
        default:
          return id_chars[trans_all   + ((param >> 3) & 0x03)];
      }
    }

    case PUSHER:
    case MISSILE:
    case SPIKE:
    {
      return id_chars[thick_arrow + param];
    }

    case LAZER:
    {
      if(param & 1)
        return id_chars[vert_lazer + ((param >> 1) & 0x03)];
      else
        return id_chars[horiz_lazer + ((param >> 1) & 0x03)];
    }

    case BULLET:
    {
      return id_chars[bullet_char + param];
    }

    case FIRE:
    {
      return id_chars[fire_anim + param];
    }

    case LIFE:
    {
      return id_chars[life_anim + param];
    }

    case RICOCHET_PANEL:
    {
      return id_chars[ricochet_panels + param];
    }

    case MINE:
    {
      return id_chars[mine_anim + (param & 0x01)];
    }

    case SHOOTING_FIRE:
    {
      return id_chars[shooting_fire_anim + (param & 0x01)];
    }

    case SEEKER:
    {
      return id_chars[seeker_anim + (param & 0x03)];
    }

    case BULLET_GUN:
    case SPINNING_GUN:
    {
      return id_chars[thin_arrow + ((param >> 3) & 0x03)];
    }

    case MISSILE_GUN:
    {
      return id_chars[thick_arrow + ((param >> 3) & 0x03)];
    }

    case SENSOR:
    {
      int idx = param;
      return (src_board->sensor_list[idx])->sensor_char;
    }

    case ROBOT:
    case ROBOT_PUSHABLE:
    {
      int idx = param;
      return (src_board->robot_list[idx])->robot_char;
    }

    case PLAYER:
    {
      return id_chars[player_char + (src_board->player_last_dir >> 4)];
    }

    // everything else
    default:
    {
      return id_chars[cell_id];
    }
  }
}

static unsigned char get_special_id_color(struct board *src_board,
 enum thing cell_id, char color, char param)
{
  char normal_color = color, spec_color = 0;
  switch(cell_id)
  {
    case ENERGIZER:
      spec_color = id_chars[energizer_glow + param];
      break;

    case EXPLOSION:
      spec_color = id_chars[explosion_colors + (param & 0x0F)];
      break;

    case FIRE:
      spec_color = id_chars[fire_colors + param];
      break;

    case LIFE:
      spec_color = id_chars[life_colors + param];
      break;

    case WHIRLPOOL_1:
    case WHIRLPOOL_2:
    case WHIRLPOOL_3:
    case WHIRLPOOL_4:
      spec_color = id_chars[whirlpool_glow + (cell_id - 67)];
      break;

    case SHOOTING_FIRE:
      spec_color = id_chars[shooting_fire_colors + (param&1)];
      break;

    case SEEKER:
      spec_color = id_chars[seeker_colors + (param & 0x03)];
      break;

    case SCROLL:
      spec_color = scroll_color;
      break;

    case PLAYER: /* player */
      return id_chars[player_color];

    default:
      return color;
  }
  if(!(spec_color & 0xF0))
  {
    normal_color &= 0xF0;
    normal_color |= (spec_color & 0x0F);
  }
  else
  {
    normal_color = spec_color;
  }
  return normal_color;
}

unsigned char get_id_char(struct board *src_board, int id_offset)
{
  unsigned char cell_id, cell_char;
  cell_id = src_board->level_id[id_offset];
  cell_char = id_chars[cell_id];

  switch(cell_char)
  {
    case 255: return src_board->level_param[id_offset];
    case 0: return
      get_special_id_char(src_board, (enum thing)cell_id,
       src_board->level_param[id_offset], id_offset);
    default: return cell_char;
  }
}

unsigned char get_id_board_color(struct board *src_board, int id_offset, int ignore_under)
{
  unsigned char color = get_special_id_color(src_board,
   (enum thing)src_board->level_id[id_offset],
   src_board->level_color[id_offset],
   src_board->level_param[id_offset]
  );

  if(!(color & 0xF0) && !(ignore_under))
  {
    color |=
      (src_board->level_under_color[id_offset] & 0xF0);
  }
  return color;
}

unsigned char get_id_color(struct board *src_board, int id_offset)
{
  return get_id_board_color(src_board, id_offset, 0);
}

unsigned char get_id_under_char(struct board *src_board, int id_offset)
{
  unsigned char cell_id, cell_char;
  cell_id = src_board->level_under_id[id_offset];
  cell_char = id_chars[cell_id];

  switch(cell_char)
  {
    case 255: return src_board->level_under_param[id_offset];
    case 0: return
      get_special_id_char(src_board, (enum thing)cell_id,
       src_board->level_under_param[id_offset], id_offset);
    default: return cell_char;
  }
}

unsigned char get_id_under_color(struct board *src_board, int id_offset)
{
  return get_special_id_color(src_board,
   (enum thing)src_board->level_under_id[id_offset],
   src_board->level_under_color[id_offset],
   src_board->level_under_param[id_offset]
  );
}

void id_put(struct board *src_board, unsigned char x_pos, unsigned char y_pos,
 int array_x, int array_y, int ovr_x, int ovr_y)
{
  int array_offset, overlay_offset;
  int overlay_mode = src_board->overlay_mode;
  int board_width = src_board->board_width;

  unsigned char c, color;

  array_offset = (array_y * board_width) + array_x;

  if(!(overlay_mode & 128) && (overlay_mode & 3) &&
   ((overlay_mode & 3) != 3))
  {
    if(overlay_mode & 2)
    {
      overlay_offset = (ovr_y * board_width) + ovr_x;
    }
    else
    {
      overlay_offset = array_offset;
    }

    c = src_board->overlay[overlay_offset];
    color = src_board->overlay_color[overlay_offset];
    if(overlay_mode & 64)
    {
      draw_char_ext(c, color, x_pos, y_pos, 0, 0);
      return;
    }

    if(overlay_mode == 4)
    {
      if(c == 32)
      {
        c = get_id_char(src_board, array_offset);
        color = get_id_color(src_board, array_offset);
      }
      else
      {
        if(!(color & 0xF0) && !(overlay_mode & 64))
        {
          c = get_id_char(src_board, array_offset);
          color = (color & 0x0F) |
           (get_id_color(src_board, array_offset) & 0xF0);
        }
      }
    }
    else
    {
      if(c == 32)
      {
        c = get_id_char(src_board, array_offset);
        color = get_id_color(src_board, array_offset);
      }
      else
      {
        if(!(color & 0xF0) && !(overlay_mode & 64))
        {
          color = (color & 0x0F) |
           (get_id_color(src_board, array_offset) & 0xF0);
        }
      }
    }
  }
  else
  {
    c = get_id_char(src_board, array_offset);
    color = get_id_color(src_board, array_offset);
  }

  draw_char_ext(c, color, x_pos, y_pos, 0, 0);
}

void draw_game_window(struct board *src_board, int array_x, int array_y)
{
  int x_limit, y_limit;
  int x, y;
  int a_x, a_y;
  int o_x, o_y;

  x_limit = src_board->viewport_width;
  y_limit = src_board->viewport_height;

  for(y = src_board->viewport_y, a_y = array_y, o_y = 0; o_y < y_limit;
   y++, a_y++, o_y++)
  {
    for(x = src_board->viewport_x, a_x = array_x, o_x = 0;
     o_x < x_limit; x++, a_x++, o_x++)
    {
      id_put(src_board, x, y, a_x, a_y, o_x, o_y);
    }
  }
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* Declarations for IDPUT.ASM */

#ifndef __IDPUT_H
#define __IDPUT_H

#include "compat.h"

__M_BEGIN_DECLS

#include "world_struct.h"
#include "board_struct.h"
#include "data.h"

CORE_LIBSPEC void id_put(struct board *src_board, unsigned char x_pos,
 unsigned char y_pos, int array_x, int array_y, int ovr_x, int ovr_y);
CORE_LIBSPEC void draw_game_window(struct board *src_board,
 int array_x, int array_y);

CORE_LIBSPEC unsigned char get_id_char(struct board *src_board, int id_offset);
CORE_LIBSPEC unsigned char get_id_color(struct board *src_board, int id_offset);
CORE_LIBSPEC unsigned char get_id_board_color(struct board *src_board, int id_offset, int ignore_under);
CORE_LIBSPEC unsigned char get_id_under_char(struct board *src_board, int id_offset);
CORE_LIBSPEC unsigned char get_id_under_color(struct board *src_board, int id_offset);

#define bullet_char  306
#define player_char  318
#define player_color 322

CORE_LIBSPEC extern unsigned char id_chars[455];
CORE_LIBSPEC extern unsigned char id_dmg[128];
CORE_LIBSPEC extern unsigned char bullet_color[3];
CORE_LIBSPEC extern unsigned char missile_color;

__M_END_DECLS

#endif // __IDPUT_H


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

// Code to the intake function, which inputs text with friendliness.

#include <string.h>
#include <ctype.h>

#include "event.h"
#include "intake.h"
#include "graphics.h"
#include "window.h"

// Global status of insert
static char insert_on = 1;

static char last_char = 0;

// (returns the key used to exit) String points to your memory for storing
// the new string. The current "value" is used- clear the string before
// calling intake if you need a blank string. Max_len is the maximum length
// of the string. X, y, segment, and color are self-explanitory. Exit_type
// determines when intake exits- 0 means exit only on Enter, 1 on Enter or
// ESC, 2 on any non-display char except the editing keys. Filter_type is
// bits to turn on filters- 1 changes all alpha to upper, 2 changes all
// alpha to lower, 4 blocks all numbers, 8 blocks all alpha, 16 blocks
// spaces, 32 blocks graphics, (ascii>126) 64 blocks punctuation that isn't
// allowed in a filename/path/drive combo, 128 blocks punctuation that isn't
// allowed in a filename only combo (use w/64), 256 blocks all punctuation.
// (IE non-alphanumerics and spaces) The editing keys supported are as
// follows- Keys to enter characters, Enter/ESC to exit, Home/End to move to
// the front/back of the line, Insert to toggle insert/overwrite, Left/Right
// to move within the line, Bkspace/Delete to delete as usual, and Alt-Bksp
// to clear the entire line. No screen saving is performed. After this
// function, the cursor is automatically off. If password is set, all
// characters appear as x.

// Mouse support- Clicking inside string sends cursor there. Clicking
// outside string returns a MOUSE_EVENT to caller without acknowledging
// the event.

// Returns a backspace if attempted at start of line. (and exit_type==2)
// Returns a delete if attempted at end of line. (and exit_type==2)

// If robo_intk is set, only 76 chars are shown and symbols are altered
// on the left/right if there is more to the left/right. Scrolling
// of the line is supported. The current character (1 on) is shown at
// x = 32, y = 0, in color 79, min. 3 chars.

int intake(struct world *mzx_world, char *string, int max_len,
 int x, int y, char color, int exit_type, int filter_type,
 int *return_x_pos, bool robo_intk, char *macro)
{
  int currx, curr_len, macro_position = -1;
  int done = 0, place = 0;
  char cur_char = 0;
  char temp_char;
  int use_mask = mzx_world->conf.mask_midchars;
  int mouse_press;
  int key;

  if(macro != NULL)
    macro_position = 0;

  // Activate cursor
  if(insert_on)
    cursor_underline();
  else
    cursor_solid();
  // Put cursor at the end of the string...
  currx = curr_len = (int)strlen(string);

  // ...unless return_x_pos says not to.
  if((return_x_pos) && (*return_x_pos < currx))
    currx = *return_x_pos;

  if(robo_intk && (currx > 75))
    move_cursor(77, y);
  else
    move_cursor(x + currx, y);

  if(insert_on)
    cursor_underline();
  else
    cursor_solid();

  do
  {
    if(!robo_intk)
    {
      if(use_mask)
        write_string_mask(string, x, y, color, 0);
      else
        write_string_ext(string, x, y, color, 0, 0, 16);
    }
    else
    {
      draw_char('\x11', color, 79, y);
      if((curr_len < 76) || (currx < 76))
      {
        draw_char('\x10', color, 0, y);

        if(curr_len < 76)
        {
          if(use_mask)
            write_line_mask(string, x, y, color, 0);
          else
            write_line_ext(string, x, y, color, 0, 0, 16);
          fill_line(76 - curr_len, x + curr_len, y, 32, color);
        }
        else
        {
          temp_char = string[76];
          string[76] = 0;
          if(use_mask)
            write_line_mask(string, x, y, color, 0);
          else
            write_line_ext(string, x, y, color, 0, 0, 16);
          string[76] = temp_char;

          draw_char('\xaf', color, 79, y);
        }
      }
      else
      {
        draw_char('\x20', color, 77, y);
        if(strlen(string + currx - 75) > 78)
        {
          temp_char = string[currx + 1];
          string[currx + 1] = 0;
          if(use_mask)
          {
            write_line_mask(string + currx - 75, x, y, color, 0);
          }
          else
          {
            write_line_ext(string + currx - 75, x, y,
             color, 0, 0, 16);
          }
          string[currx + 1] = temp_char;
        }
        else
        {
          if(use_mask)
          {
            write_line_mask(string + currx - 75, x, y, color, 0);
          }
          else
          {
            write_line_ext(string + currx - 75, x, y,
             color, 0, 0, 16);
          }
        }
        draw_char('\xae', color, 0, y);
        if(currx < curr_len)
          draw_char('\xaf', color, 79, y);
      }
      draw_char('\x20', color, 78, y);
    }

    if(!robo_intk)
    {
      fill_line(max_len + 1 - curr_len, x + curr_len, y, 32, color);
    }
    else
    {
      write_number(currx + 1, 79, 32, 0, 3, 0, 10);
      write_number(curr_len + 1, 79, 36, 0, 3, 0, 10);
    }

    // Get key
    if(macro_position != -1)
    {
      key = macro[macro_position];
      cur_char = key;

      macro_position++;
      if(macro[macro_position] == 0)
        macro_position = -1;

      if(key == '^')
        key = IKEY_RETURN;
    }
    else
    {
      update_screen();
      update_event_status_delay();
      key = get_key(keycode_internal);
      place = 0;

      cur_char = get_key(keycode_unicode);
    }

    mouse_press = get_mouse_press_ext();

    if(get_mouse_press_ext())
    {
      int mouse_x, mouse_y;
      get_mouse_position(&mouse_x, &mouse_y);
      if((mouse_y == y) && (mouse_x >= x) &&
       (mouse_x <= (x + max_len)) && (mouse_press <= MOUSE_BUTTON_RIGHT))
      {
        // Yep, reposition cursor.
        currx = mouse_x - x;
        if(currx > curr_len)
          currx = curr_len;
      }
      else
      {
        key = -1;
        done = 1;
      }
    }

    // Handle key cases
    switch(key)
    {
      case IKEY_ESCAPE:
      {
        // ESC
        if(exit_type > 0)
        {
          done = 1;
        }
        break;
      }

      case IKEY_RETURN:
      {
        // Enter
        done = 1;
        break;
      }

      case IKEY_HOME:
      {
        if(get_alt_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else
        {
          // Home
          currx = 0;
        }
        break;
      }

      case IKEY_END:
      {
        if(get_alt_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else
        {
          // End
          currx = curr_len;
        }
        break;
      }

      case IKEY_LEFT:
      {
        if(get_ctrl_status(keycode_internal))
        {
          // Find nearest space to the left
          if(currx)
          {
            char *current_position = string + currx;

            if(currx)
              current_position--;

            if(!isalnum((int)*current_position))
            {
              while(currx && !isalnum((int)*current_position))
              {
                current_position--;
                currx--;
              }
            }

            do
            {
              current_position--;
              currx--;
            } while(currx && isalnum((int)*current_position));

            if(currx < 0)
              currx = 0;
          }
        }
        else
        {
          // Left
          if(currx > 0)
            currx--;
        }

        break;
      }

      case IKEY_RIGHT:
      {
        if(get_ctrl_status(keycode_internal))
        {
          // Find nearest space to the right
          if(currx < curr_len)
          {
            char *current_position = string + currx;
            char current_char = *current_position;
            if(!isalnum((int)current_char))
            {
              do
              {
                current_position++;
                currx++;
                current_char = *current_position;
              } while(current_char && !isalnum((int)current_char));
            }

            while(current_char && isalnum((int)current_char))
            {
              current_position++;
              currx++;
              current_char = *current_position;
            }
          }
        }
        else
        {
          // Right
          if(currx < curr_len)
            currx++;
        }

        break;
      }

      case IKEY_F1:
      case IKEY_F2:
      case IKEY_F3:
      case IKEY_F4:
      case IKEY_F5:
      case IKEY_F6:
      case IKEY_F7:
      case IKEY_F8:
      case IKEY_F9:
      case IKEY_F10:
      case IKEY_F11:
      case IKEY_F12:
      case IKEY_UP:
      case IKEY_DOWN:
      case IKEY_TAB:
      case IKEY_PAGEUP:
      case IKEY_PAGEDOWN:
      {
        done = 1;
        break;
      }

      case IKEY_INSERT:
      {
        if(get_alt_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else
        {
          // Insert
          if(insert_on)
            cursor_solid();
          else
            cursor_underline();

          insert_on ^= 1;
        }
        break;
      }

      case IKEY_BACKSPACE:
      {
        // Backspace, at 0 it might exit
        if(get_alt_status(keycode_internal))
        {
          // Alt-backspace, erase input
          curr_len = currx = 0;
          string[0] = 0;
        }
        else

        if(get_ctrl_status(keycode_internal))
        {
          // Find nearest space to the left
          if(currx)
          {
            int old_position = currx;

            if(!isalnum((int)string[currx]))
            {
              while(currx && !isalnum((int)string[currx]))
              {
                currx--;
              }
            }

            while(currx && isalnum((int)string[currx]))
            {
              currx--;
            }

            curr_len -= old_position - currx;

            memmove(string + currx, string + old_position,
             strlen(string + old_position) + 1);
          }
        }
        else

        if(currx == 0)
        {
          if(exit_type == 2)
          {
            done = 1;
          }
        }
        else
        {
          // Move all back 1, decreasing string length
          memmove(string + currx - 1, string + currx, curr_len - currx + 1);
          curr_len--;
          // Cursor back one
          currx--;
        }
        break;
      }

      case IKEY_DELETE:
      {
        // Delete, at the end might exit
        if(currx == curr_len)
        {
          if(exit_type == 2)
            done = 1;
        }
        else
        {
          if(curr_len)
          {
            // Move all back 1, decreasing string length
            memmove(string + currx, string + currx + 1, curr_len - currx);
            curr_len--;
          }
        }
        break;
      }

      case IKEY_c:
      {
        if(get_ctrl_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else

        if(get_alt_status(keycode_internal) && !filter_type)
        {
          // If alt - C is pressed, choose character
          int new_char = char_selection(last_char);
          if(new_char >= 32)
          {
            cur_char = new_char;
            last_char = new_char;
            place = 1;
          }
          else
          {
            place = 0;
          }
        }
        else
        {
          place = 1;
        }

        break;
      }

      case IKEY_t:
      {
        if(get_alt_status(keycode_internal))
        {
          done = 1;
        }
        else
        {
          place = 1;
        }
        break;
      }

      case IKEY_l:
      case IKEY_g:
      case IKEY_d:
      case IKEY_f:
      case IKEY_r:
      {
        if(get_ctrl_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else
        {
          place = 1;
        }
        break;
      }

      case IKEY_i:
      {
        if((get_ctrl_status(keycode_internal) ||
         get_alt_status(keycode_internal)) && robo_intk)
        {
          done = 1;
        }
        else
        {
          place = 1;
        }
        break;
      }

      case IKEY_u:
      case IKEY_o:
      case IKEY_x:
      case IKEY_b:
      case IKEY_s:
      case IKEY_e:
      case IKEY_v:
      case IKEY_p:
      case IKEY_h:
      case IKEY_m:
      {
        if(get_alt_status(keycode_internal) && robo_intk)
        {
          done = 1;
        }
        else
        {
          place = 1;
        }
        break;
      }

      case IKEY_LSHIFT:
      case IKEY_RSHIFT:
      case 0:
      {
        place = 0;
        break;
      }

      default:
      {
        // Place the char
        place = 1;
        break;
      }

      case -1:
      {
        break;
      }
    }

    if(place)
    {
      if((cur_char != 0) && (cur_char < 32) && (exit_type == 2))
      {
        done = 1;
        key = cur_char;
      }
      else

      // Keycode.. Filter.
      if(filter_type & 1)
      {
        if((cur_char >= 'a') && (cur_char <= 'z'))
          cur_char -= 32;
      }

      if(filter_type & 2)
      {
        if((cur_char >= 'A') && (cur_char <= 'Z'))
          cur_char += 32;
      }

      // Block numbers
      if((filter_type & 4) && ((cur_char >= '0') && (cur_char <= '9')))
      {
        place = 0;
      }

      // Block alpha
      if((filter_type & 8) &&
       (((cur_char >= 'a') && (cur_char <= 'z')) ||
       ((cur_char >= 'A') && (cur_char <= 'Z'))))
      {
        place = 0;
      }

      // Block spaces
      if((filter_type & 16) && (cur_char == ' '))
      {
        place = 0;
      }

      // Block high-ASCII
      if((filter_type & 32) && (cur_char > 126))
      {
        place = 0;
      }

      // Block these chars
      if((filter_type & 64) &&
       ((cur_char == '*') || (cur_char == '[') ||
       (cur_char == ']') || (cur_char == '>') ||
       (cur_char == '<') || (cur_char == ',') ||
       (cur_char == '|') || (cur_char == '?') ||
       (cur_char == '=') || (cur_char == ';') ||
       (cur_char == '\"') || (cur_char =='/')))
      {
        place = 0;
      }

      // Block these chars
      if((filter_type & 128) &&
       ((cur_char == ':') || (cur_char == '\\')))
      {
        place = 0;
      }

      // Block these chars
      if((filter_type & 256) &&
       (((cur_char > ' ') && (cur_char < '0')) ||
       ((cur_char > '9') && (cur_char < 'A')) ||
       ((cur_char > 'Z') && (cur_char < 'a')) ||
       ((cur_char > 'z') && (cur_char < 127))))
      {
        place = 0;
      }

      // Now, can it still be placed?
      if(place && (curr_len != max_len) && (!done) && cur_char)
      {
        // Overwrite or insert?
        if((insert_on) || (currx == curr_len))
        {
          // Insert- Move all ahead 1, increasing string length
          curr_len++;
          memmove(string + currx + 1, string + currx, curr_len - currx);
        }
        // Add character and move forward one
        string[currx++] = cur_char;
      }
    }

    // Move cursor
    if(robo_intk && (currx > 75))
      move_cursor(77, y);
    else
      move_cursor(x + currx, y);

    if(insert_on)
      cursor_underline();
    else
      cursor_solid();

    // Loop
  } while(!done);

  cursor_off();
  if(return_x_pos)
    *return_x_pos = currx;

  return key;
}


code:
/* MegaZeux
 *
 * Copyright (C) 1996 Greg Janson
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/* INTAKE.H- Declarations for INTAKE.CPP */

#ifndef __INTAKE_H
#define __INTAKE_H

#include "compat.h"

__M_BEGIN_DECLS

#include <stdlib.h>

#include "world_struct.h"
#include "keysym.h"

// See code for full docs, preserves mouse cursor, be prepared for a
// MOUSE_EVENT! (must acknowledge_event() it)
CORE_LIBSPEC int intake(struct world *mzx_world, char *string, int max_len,
 int x, int y, char color, int exit_type, int filter_type,
 int *return_x_pos, bool robo_intk, char *macro);

__M_END_DECLS

#endif // __INTAKE_H


code:
/* MegaZeux
 *
 * Copyright (C) 2007 Alan Williams <mralert@gmail.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef __KEYSYM_H
#define __KEYSYM_H

#include "compat.h"

__M_BEGIN_DECLS

enum keycode
{
  IKEY_UNKNOWN      = 0,
  IKEY_FIRST        = 0,
  IKEY_UNICODE      = 1,
  IKEY_BACKSPACE    = 8,
  IKEY_TAB          = 9,
  IKEY_RETURN       = 13,
  IKEY_ESCAPE       = 27,
  IKEY_SPACE        = 32,
  IKEY_QUOTE        = 39,
//IKEY_PLUS         = 43,
  IKEY_COMMA        = 44,
  IKEY_MINUS        = 45,
  IKEY_PERIOD       = 46,
  IKEY_SLASH        = 47,
  IKEY_0            = 48,
  IKEY_1            = 49,
  IKEY_2            = 50,
  IKEY_3            = 51,
  IKEY_4            = 52,
  IKEY_5            = 53,
  IKEY_6            = 54,
  IKEY_7            = 55,
  IKEY_8            = 56,
  IKEY_9            = 57,
  IKEY_SEMICOLON    = 59,
  IKEY_EQUALS       = 61,
  IKEY_LEFTBRACKET  = 91,
  IKEY_BACKSLASH    = 92,
  IKEY_RIGHTBRACKET = 93,
  IKEY_BACKQUOTE    = 96,
  IKEY_a            = 97,
  IKEY_b            = 98,
  IKEY_c            = 99,
  IKEY_d            = 100,
  IKEY_e            = 101,
  IKEY_f            = 102,
  IKEY_g            = 103,
  IKEY_h            = 104,
  IKEY_i            = 105,
  IKEY_j            = 106,
  IKEY_k            = 107,
  IKEY_l            = 108,
  IKEY_m            = 109,
  IKEY_n            = 110,
  IKEY_o            = 111,
  IKEY_p            = 112,
  IKEY_q            = 113,
  IKEY_r            = 114,
  IKEY_s            = 115,
  IKEY_t            = 116,
  IKEY_u            = 117,
  IKEY_v            = 118,
  IKEY_w            = 119,
  IKEY_x            = 120,
  IKEY_y            = 121,
  IKEY_z            = 122,
  IKEY_DELETE       = 127,
  IKEY_KP0          = 256,
  IKEY_KP1          = 257,
  IKEY_KP2          = 258,
  IKEY_KP3          = 259,
  IKEY_KP4          = 260,
  IKEY_KP5          = 261,
  IKEY_KP6          = 262,
  IKEY_KP7          = 263,
  IKEY_KP8          = 264,
  IKEY_KP9          = 265,
  IKEY_KP_PERIOD    = 266,
  IKEY_KP_DIVIDE    = 267,
  IKEY_KP_MULTIPLY  = 268,
  IKEY_KP_MINUS     = 269,
  IKEY_KP_PLUS      = 270,
  IKEY_KP_ENTER     = 271,
  IKEY_UP           = 273,
  IKEY_DOWN         = 274,
  IKEY_RIGHT        = 275,
  IKEY_LEFT         = 276,
  IKEY_INSERT       = 277,
  IKEY_HOME         = 278,
  IKEY_END          = 279,
  IKEY_PAGEUP       = 280,
  IKEY_PAGEDOWN     = 281,
  IKEY_F1           = 282,
  IKEY_F2           = 283,
  IKEY_F3           = 284,
  IKEY_F4           = 285,
  IKEY_F5           = 286,
  IKEY_F6           = 287,
  IKEY_F7           = 288,
  IKEY_F8           = 289,
  IKEY_F9           = 290,
  IKEY_F10          = 291,
  IKEY_F11          = 292,
  IKEY_F12          = 293,
  IKEY_NUMLOCK      = 300,
  IKEY_CAPSLOCK     = 301,
  IKEY_SCROLLOCK    = 302,
  IKEY_RSHIFT       = 303,
  IKEY_LSHIFT       = 304,
  IKEY_RCTRL        = 305,
  IKEY_LCTRL        = 306,
  IKEY_RALT         = 307,
  IKEY_LALT         = 308,
  IKEY_LSUPER       = 311,
  IKEY_RSUPER       = 312,
  IKEY_SYSREQ       = 317,
  IKEY_BREAK        = 318,
  IKEY_MENU         = 319,
  IKEY_LAST
};

__M_END_DECLS

#endif // __KEYSYM_H