   /*
 * File: main-sdl.c
 * Purpose: Angband SDL port 
 *
 * Copyright (c) 2007 Ben Harrison, Gregory Velichansky, Eric Stevens,
 * Leon Marrick, Iain McFall, and others
 *
 * This work is free software; you can redistribute it and/or modify it
 * under the terms of either:
 *
 * a) the GNU General Public License as published by the Free Software
 *    Foundation, version 2, or
 *
 * b) the "Angband licence":
 *    This software may be copied and distributed for educational, research,
 *    and not for profit purposes provided that this copyright and statement
 *    are included in all such copies.  Other copyrights may also apply.
 */
#include "angband.h"
#include "cmds.h"

/*
 * Comments and suggestions are welcome. The UI probably needs some
 * adjustment, and I need comments from you.
 *perhaps also something like "Angband 3.0.8 by Andrew Sidwell and others;
 SDL port by Iain McFall an others, please see the accompanying documentation
 for credits" or something
 */

/*
 * This file helps Angband work with at least some computers capable of running
 * SDL, a set of simple and quite popular game development libraries that work
 * on many different operating systems, including Windows, most flavours of
 * UNIX or similar, and Mac OS X.  It requires a 32-bit (or higher) machine
 * capable of displaying at least 640x480 in 256 colors.  A Pentium or better
 * is strongly recommended (for speed reasons treated more fully below).
 *
 * To use this file, use an appropriate "Makefile" or "Project File", install
 * the required libraries (described below), make sure that "USE_SDL" is
 * defined somewhere, and obtain various extra files (described below).  If
 * you are new to all this, read "makefile.sdl".
 *
 * This port uses the following libraries:  SDL (v1.2+) and  SDL_ttf.
 * All are available as source code, pre-compiled libs for developers,
 * and libs (or dlls) for players from www.libsdl.org
 *
 *
 * Other files used by this port:
 * - The game must have a collection of bitmap .fon files in /lib/xtra/font.
 *
 * - If "USE_GRAPHICS" is defined, then it also needs some bitmapped (.bmp)
 *   graphics files in /lib/xtra/graf, such as "16x16.bmp" and "16x16m.bmp".
 *
 * - The "lib/pref/pref-sdl.prf" file contains keymaps, macro definitions,
 *   and/or color redefinitions.
 * - The "lib/pref/font-sdl.prf" contains attr/char mappings for use with the
 *   normal "*.fon" font files in the "lib/xtra/font/" directory.
 *
 *
 *
 * "Term" framework by Ben Harrison (benh@phial.com).
 *
 * Original Sangband SDL port and the "intrface" module by Leon Marrick
 * (www.runegold.org/sangband).
 *
 * Additional helpful ideas by:
 * 2001 Gregory Velichansky <hmaon@bumba.net>, creator of the first Angband SDL
 * port.
 * 2006 Eric Stevens <sdltome@gmail.com>, main author of the TOME SDL port.
 */

/*
 * Comments on using SDL with Angband:
 *
 * The good news:
 * - SDL is cross-platform.  Really.  No joke.  If this port doesn't work on
 *   your system, it probably isn't SDL's fault.
 * - SDL is relatively easy to use, allowing you you to cobble up feature-rich
 *   apps that look half-way decent without too much fuss and bother.  It's
 *   wonderful for prototyping.
 * - SDL does most of what *band developers are likely to need, plus a whole
 *   lot more we haven't realized we want yet.
 * - SDL is a cleanly written open-source API; it is much less painful to get
 *   the straight word then it is with most other libraries.  Also, the SDL
 *   community offers active discussion boards, solid documentation, and
 *   numerous code examples relating to almost any question you might have
 *   occasion to ask.
 *
 * The bad news:
 * - SDL can be tedious to install.  Each individual library is straightforward
 *   enough, but *band development work requires several, the number growing as
 *   you get more sophisticated.
 * - SDL (as a stand-alone lib, without the assistance of OpenGL) can be very
 *   sluggish if you aren't careful.  It is poor at detecting, let alone making
 *   fullest use of, available video hardware, which cripples speed.  So,
 *   getting half-way decent performance in a game like Angband takes some
 *   skill and vast amounts of effort.  Speed - the lack thereof - is this
 *   port's biggest problem.  More comments below.
 * - SDL is not a complete game development library (although the add-ons help
 *   tremendously).  Much-needed functionality - text output, blit stretching,
 *   and video refresh synching being three examples - were either missing
 *   altogether or covered by functions that proved too slow or delicate for
 *   production use.  I ended up having to spend at least as much time, and
 *   write at least as much low-level code, as I did using the Win32 API.
 * - SDL, like Allegro but to a lesser extent, is falling behind current tech-
 *   nology.  Development progresses, but obsolescence looms, especially on
 *   Windows machines.
 */

#ifdef USE_SDL

#include "main.h"
#include "SDL.h"
#include "SDL_ttf.h"
#include "SDL_image.h"

/* SDL flags used for the main window surface */
static Uint32 vflags = SDL_ANYFORMAT;

/* Current screen dimensions */
static int screen_w = 800;
static int screen_h = 600;

/* Fullscreen dimensions */
static int full_w;
static int full_h;

/* Want fullscreen? */
static bool fullscreen = FALSE;

/* XXXXXXXXX */
static char *ANGBAND_DIR_USER_SDL;

/*
 * Used as 'system' font
 */
static cptr DEFAULT_FONT_FILE = "6x10.fon";

#define MAX_FONTS 20
char *FontList[MAX_FONTS];
static int num_fonts = 0;


/*
 * A font structure
 * Note that the data is only valid for a surface with matching
 * values for pitch & bpp. If a surface is resized the data _must_ be
 * recalculated.
 */
typedef struct sdl_Font sdl_Font;
struct sdl_Font
{
        int width;                                      /* The dimensions of this font (in pixels)*/
        int height;
        
        char name[32];                          /* The name of this font */
        
        Uint16 pitch;                           /* Pitch of the surface this font is made for */
        Uint8 bpp;                                      /* Bytes per pixel of the surface */
        Uint8 something;                        /* Padding */
        
        int *data;                                      /* The data */
};

static sdl_Font SystemFont;

#define NUM_GLYPHS 256

/*
 * Window information
 * Each window has its own surface and coordinates
 */
typedef struct term_window term_window;
struct term_window
{
        term term_data;
        
        SDL_Surface *surface;   /* The surface for this window */
#ifdef USE_GRAPHICS
        SDL_Surface *tiles;             /* The appropriately sized tiles for this window */
#endif  
        byte Term_idx;                  /* Index of term that relates to this */
        
        int top;                                /* Window Coordinates on the main screen */
        int left;
        
        int keys;                               /* Size of keypress storage */
        
        sdl_Font font;                  /* Font info */
        char *req_font;                 /* Requested font */
        int rows;                               /* Dimension in tiles */
        int cols;
        
        int border;                             /* Border width */
        int title_height;               /* Height of title bar */
        
        int width;                              /* Dimension in pixels == tile_wid * cols + 2 x border*/
        int height;
        
        int tile_wid;                   /* Size in pixels of a char */
        int tile_hgt;
        
        bool visible;                   /* Can we see this window? */
        
        SDL_Rect uRect;                 /* The part that needs to be updated */
};




typedef struct mouse_info mouse_info;
struct mouse_info
{
        int left;                       /* Is it pressed? */
        int right;
        
        int leftx;                      /* _IF_ left button is pressed these */
        int lefty;                      /* show where it was pressed */
        
        int rightx;
        int righty;
        
        int x;                                          /* Current position of mouse */
        int y;
        
};

#define WINDOW_DRAW (SDL_USEREVENT + 1)

typedef struct sdl_ButtonBank sdl_ButtonBank;
typedef struct sdl_Button sdl_Button;
typedef struct sdl_Window sdl_Window;

typedef void (*button_press_func)(sdl_Button *sender);
struct sdl_Button
{
        SDL_Rect pos;                           /* Position & Size */
        bool selected;                          /* Selected? */
        bool visible;                           /* Visible? */
        button_press_func activate;     /* A function to call when pressed */
        sdl_ButtonBank *owner;          /* Which bank is this in? */
        char caption[50];                       /* Text for this button */
        Uint32 unsel_colour;            /* Button unselected colour */
        Uint32 sel_colour;                      /* Selected colour*/
        Uint32 cap_colour;                      /* Caption colour */
        void *data;                                     /* Something */
        int tag;                                        /* Something */
        
};

struct sdl_ButtonBank
{
        sdl_Button *buttons;            /* A collection of buttons */
        bool *used;                                     /* What buttons are available? */
        sdl_Window *window;                     /* The window that these buttons are on */
        bool need_update;
};

/*
 * Other 'windows' (basically a surface with a position and buttons on it)
 * Currently used for the top status bar and popup windows
 */
typedef void (*sdl_WindowCustomDraw)(sdl_Window *window);
struct sdl_Window
{
        int top;                                /* Position on main window */
        int left;
        
        int width;                              /* Dimensions */
        int height;
        
        bool visible;                   /* Visible? */
        
        SDL_Surface *surface;   /* SDL surface info */
        
        sdl_ButtonBank buttons;         /* Buttons */
        
        sdl_Font font;                  /* Font */
        
        SDL_Surface *owner;             /* Who shall I display on */
        
        sdl_WindowCustomDraw draw_extra; /* Stuff to draw on the surface */
        bool need_update;
};




/*
 * The main surface of the application
 */
static SDL_Surface *AppWin;

/*
 * The status bar
 */
static sdl_Window StatusBar;

/*
 * The Popup window
 */
static sdl_Window PopUp;
static bool popped;

/*
 * Term windows
 */
static term_window windows[ANGBAND_TERM_MAX];
static int Zorder[ANGBAND_TERM_MAX];

/* Keep track of the mouse status */
static mouse_info mouse;

/*
 * Hack -- define which keys should be ignored
 */
static bool ignore_key[1024];

/*
 * We ignore all keypresses involving only modifier keys
 */
static int ignore_key_list[] =
{
        SDLK_NUMLOCK, SDLK_CAPSLOCK, SDLK_SCROLLOCK, SDLK_RSHIFT,
        SDLK_LSHIFT, SDLK_RCTRL, SDLK_LCTRL, SDLK_RALT, SDLK_LALT,
        SDLK_RMETA, SDLK_LMETA, SDLK_LSUPER, SDLK_RSUPER, SDLK_MODE,
        SDLK_COMPOSE, 0
};

/*
 * The number pad consists of 10 keys, each with an SDL identifier
 */
#define is_numpad(k) \
((k == SDLK_KP0) || (k == SDLK_KP1) || (k == SDLK_KP2) || (k == SDLK_KP3) || \
 (k == SDLK_KP4) || (k == SDLK_KP5) || (k == SDLK_KP6) || \
 (k == SDLK_KP7) || (k == SDLK_KP8) || (k == SDLK_KP9) || (k == SDLK_KP_ENTER))

static int SnapRange = 5;       /* Window snap range (pixels) */
static int StatusHeight;        /* The height in pixels of the status bar */
static int SelectedTerm;        /* Current selected Term */

static int AboutSelect;         /* About button */
static int TermSelect;          /* Term selector button */
static int FontSelect;          /* Font selector button */
static int VisibleSelect;       /* Hide/unhide window button*/
static int MoreSelect;          /* Other options button */
static int QuitSelect;          /* Quit button */

/* Buttons on the 'More' panel */
static int MoreOK;                      /* Accept changes */
static int MoreFullscreen;      /* Fullscreen toggle button */
static int MoreSnapPlus;        /* Increase snap range */
static int MoreSnapMinus;       /* Decrease snap range */

static bool Moving;                             /* Moving a window */
static bool Sizing;                             /* Sizing a window */
static SDL_Rect SizingSpot;             /* Rect to descibe the sizing area */
static bool Sizingshow = FALSE; /* Is the resize thingy displayed? */
static SDL_Rect SizingRect;             /* Rect to describe the current resize window */

#ifdef USE_GRAPHICS
typedef struct GfxInfo GfxInfo;
struct GfxInfo
{
        cptr name;                              /* Name to show on button */
        cptr gfxfile;                   /* The file with tiles */
        int width;                              /* Width of a tile */
        int height;                             /* Height of a tile */
        cptr pref;                              /* Preference file to use */
        int x;                                  /* Yuk - Pixel location of colour key */
        int y;                                  /* ditto */
        bool avail;                             /* Are the appropriate files available? */
};

static SDL_Surface *GfxSurface = NULL;  /* A surface for the graphics */

#define GfxModes 4
static GfxInfo GfxDesc[GfxModes] =
{
        /* No gfx (GRAPHICS_NONE) */
        {"None", NULL, -1, -1, NULL, 0, 0, TRUE},
        /* 8x8 tiles (GRAPHICS_ORIGINAL) */
        {"8x8", "8x8.png", 8, 8, "old", 0, 0, TRUE},
        /* 16x16 tiles (GRAPHICS_ADAM_BOLT) */
        {"16x16", "16x16.png", 16, 16, "new", 0, 65, TRUE},
        /* XXX (GRAPHICS_DAVID_GERVAIS) */
        {"32x32", "32x32.png", 32, 32, "david", 0, 0, TRUE},
        
        /* XXX (GRAPHICS_PSEUDO ???) */
        /*{NULL, NULL, NULL, -1, -1},   */                                              
};

static int MoreBigtile;                         /* Toggle bigtile button */
static int GfxButtons[GfxModes];        /* Graphics mode buttons */
static int SelectedGfx;                         /* Current selected gfx */
#endif

/*
 * The basic angband text colours in an sdl friendly form
 */
static u32b text_colours[MAX_COLORS];

u32b back_colour;               /* Background colour */


/*
 * Fill in an SDL_Rect structure.
 * Note it also returns the value adjusted
 */
static SDL_Rect *RECT(int x, int y, int w, int h, SDL_Rect *rect)
{       
        rect->x = x;
        rect->y = y;
        rect->w = w;
        rect->h = h;
        
        return rect;
}

/*
 * Is a point(x, y) in a rectangle?
 */
static bool point_in(SDL_Rect *rect, int x, int y)
{
        if (x < rect->x) return (FALSE);
        if (y < rect->y) return (FALSE);
        if (x >= rect->x + rect->w) return (FALSE);
        if (y >= rect->y + rect->h) return (FALSE);
        
        /* Must be inside */
        return (TRUE);
}

/*
 * Draw an outline box
 * Given the top, left, width & height
 */
static void sdl_DrawBox(SDL_Surface *surface, SDL_Rect *rect, Uint32 colour, int width)
{
        SDL_Rect rc;
        int left = rect->x;
        int right = rect->x + rect->w - width;
        int top = rect->y;
        int bottom = rect->y + rect->h - width;
        
        /* Top left -> Top Right */
        RECT(left, top, rect->w, width, &rc);
        SDL_FillRect(surface, &rc, colour);
        
        /* Bottom left -> Bottom Right */
        RECT(left, bottom, rect->w, width, &rc);
        SDL_FillRect(surface, &rc, colour);
        
        /* Top left -> Bottom left */
        RECT(left, top, width, rect->h, &rc);
        SDL_FillRect(surface, &rc, colour);
        
        /* Top right -> Bottom right */
        RECT(right, top, width, rect->h, &rc);
        SDL_FillRect(surface, &rc, colour);
}

/*
 * Get the width and height of a given font file
 */
static errr sdl_CheckFont(cptr fontname, int *width, int *height)
{
        char buf[1024];
        
        TTF_Font *ttf_font;
        
        /* Build the path */
        path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_FONT, fontname);
        
        /* Attempt to load it */
        ttf_font = TTF_OpenFont(buf, 0);
        
        /* Bugger */
        if (!ttf_font) return (-1);
        
        /* Get the size */
        if (TTF_SizeText(ttf_font, "M", width, height)) return (-1);
        
        /* Finished with the font */
        TTF_CloseFont(ttf_font);
        
        return (0);
}

/* 
* The sdl_Font routines
 */

/*
 * Free any memory assigned by Create()
 */
static void sdl_FontFree(sdl_Font *font)
{
        /* The only memory reserved is the data */
        FREE(font->data);
}


/*
 * Create new font data with font fontname, optimizing the data
 * for the surface given
 */
static errr sdl_FontCreate(sdl_Font *font, cptr fontname, SDL_Surface *surface)
{
        char buf[1024];
        int i;
        
        TTF_Font *ttf_font;
        
        /* Our temporary glyph will be rendered in white */
        SDL_Color white = { 255, 255, 255, 0 };
        
        int *temp_array;
        
        /* The first pixel data is stored immediately after the character indexes */
        /* But we need an extra space to store the total pixel count */
        int num_pixels = NUM_GLYPHS + 1;
        
        /* Free any old data */
        if (font->data) sdl_FontFree(font);
        
        /* Build the path */
        path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_FONT, fontname);
        
        /* Attempt to load it */
        ttf_font = TTF_OpenFont(buf, 0);
        
        /* Bugger */
        if (!ttf_font) return (-1);
        
        /* Get the size */
        if (TTF_SizeText(ttf_font, "M", &font->width, &font->height)) return (-1);
        
        /* Fill in some of the font struct */
        my_strcpy(font->name, fontname, 30);
        font->pitch = surface->pitch;
        font->bpp = surface->format->BytesPerPixel;
        
        /* Make a very large temporary holding bin for pixel offset data  XXX */
        temp_array = C_ZNEW(NUM_GLYPHS * 1000, int);
        
        /* Render and encode all the characters */
        for (i = 0; i < NUM_GLYPHS; i++)
        {
                SDL_Surface *temp_glyph;
                int x, y, v;
                Uint8 *p;
                
                /* Render this glyph, in white, onto an 8-bit surface */
                temp_glyph = TTF_RenderGlyph_Solid(ttf_font, i, white);
                
                /* Remember the current pixel array location */
                temp_array[i] = num_pixels;
                
                /* If glyph does not exist, no pixels get plotted */
                if (!temp_glyph)
                {
                        fprintf(stderr, "char:%d : %s\n", i, TTF_GetError());
                        continue;
                }
                
                /* Store this glyph into our temporary array */
                for (x = 0; x < temp_glyph->w; x++)
                {
                        for (y = 0; y < temp_glyph->h; y++)
                        {
                                /* Access this pixel.  "pitch" is the pixel width in bytes of an SDL surface */
                                p = (Uint8 *)temp_glyph->pixels + (y * temp_glyph->pitch) + (x * temp_glyph->format->BytesPerPixel);
                                
                                /* Ignore pixels without color data */
                                if (!*p) continue;
                                
                                /* Precalculate some of the math needed to access this pixel on screen */
                                v = (y * font->pitch) + (x * font->bpp);
                                
                                /* And store it */
                                temp_array[num_pixels++] = v;
                        }
                }
                
                /* Free the temporary surface */
                SDL_FreeSurface(temp_glyph);
        }
        
        /* Calculate and store access position data for the final character */
        temp_array[NUM_GLYPHS] = num_pixels;
        
        /* Make a pixel access array for this font */
        font->data = C_ZNEW(num_pixels, int);
        
        /* Save the data gathered in our temporary array */
        for (i = 0; i < num_pixels; i++) font->data[i] = temp_array[i];
        
        /* Free the temporary pixel data */
        FREE(temp_array);
        
        /* Finished with the font */
        TTF_CloseFont(ttf_font);
        
        /* Success */
        return (0);     
}




/*
 * Draw some text onto a surface
 * The surface is first checked to see if it is compatable with
 * this font, if it isn't the the font will be 're-precalculated'
 *
 * You can, I suppose, use one font on many surfaces, but it is
 * definately not recommended. One font per surface is good enough.
 */
static errr sdl_FontDraw(sdl_Font *font, SDL_Surface *surface, Uint32 colour, int x, int y, int n , cptr s)
{
        Uint8 bpp = surface->format->BytesPerPixel;
        Uint16 pitch = surface->pitch;
        
        int i, j;
        
        int start, end;
        
        Uint8 *pd0;
        
        int *data;
        
        if ((bpp != font->bpp) || (pitch != font->pitch))
                sdl_FontCreate(font, font->name, surface);
        
        data = font->data;
        /* Lock the window surface (if necessary) */
        if (SDL_MUSTLOCK(surface))
        {
                if (SDL_LockSurface(surface) < 0) return (-1);
        }
        
        /* Point to the left-top corner of the screen pixel data for this tile */
        pd0 = (Uint8 *)surface->pixels + (y * pitch) + (x * bpp);
        
        /* Advance along the string.  Adjust pixel pointer one tile per character. */
        for (i = 0; i < n; i++, pd0 += (font->width * bpp))
        {
                /* Translate the character */
                byte c = *(s + i);
                
                /* Get the first and last pixel locator we need to plot */
                start = data[c];
                end   = data[c + 1];
                
                /* Handle all of the legal bit depths */
                switch (bpp)
                {
                        /* 256 colors -- pixel data is 8-bit */
                        case 1:
                        {
                                /*
                                 * Use entries in the pixel location array to jump to and
                                 * change pixels to our desired color.
                                 */
                                for (j = start; j < end; j++) *(Uint8*)(pd0 + data[j]) = (Uint8)colour;
                                break;
                        }
                                
                                /* 15 and 16-bit color (SDL handles the details) */
                        case 2:
                        {
                                for (j = start; j < end; j++) *(Uint16*)(pd0 + data[j]) = (Uint16)colour;
                                break;
                        }
                                
                                /* 32-bit color is easy.  24-bit color needs more testing. */
                        case 3:
                        case 4:
                        default:
                        {
                                for (j = start; j < end; j++) *(Uint32*)(pd0 + data[j]) = colour;
                                break;
                        }
                }
        }
        
        /* Unlock the surface */
        if (SDL_MUSTLOCK(surface)) SDL_UnlockSurface(surface);
        
        /* Success */
        return (0);
}



/*
 * Draw a button on the screen
 */
static void sdl_ButtonDraw(sdl_Button *button)
{
        SDL_Surface *surface = button->owner->window->surface;
        sdl_Font *font = &button->owner->window->font;
        
        Uint32 colour = button->selected ? button->sel_colour : button->unsel_colour;
        
        if (!button->visible) return;
        
        SDL_FillRect(surface, &button->pos, colour);
        
        if (strlen(button->caption))
        {
                size_t len = strlen(button->caption);

                unsigned max = button->pos.w / font->width;
                int n = MIN(len, max);
                int l = n * font->width / 2;
                int x = button->pos.x + ((button->pos.w) / 2) - l;
                
                sdl_FontDraw(font, surface, button->cap_colour,
                                         x, button->pos.y + 1, n, button->caption);
        }
}

/*
 * Adjust the position of a button
 */
static void sdl_ButtonMove(sdl_Button *button, int x, int y)
{
        button->pos.x = x;
        button->pos.y = y;
        button->owner->need_update = TRUE;
}

/*
 * Adjust the size of a button
 */
static void sdl_ButtonSize(sdl_Button *button, int w, int h)
{
        button->pos.w = w;
        button->pos.h = h;
        button->owner->need_update = TRUE;
}

/*
 * Set the caption
 */
static void sdl_ButtonCaption(sdl_Button *button, cptr s)
{
        my_strcpy(button->caption, s, sizeof(button->caption));
        button->owner->need_update = TRUE;
}

/*
 * Set the visibility of a button
 */
static void sdl_ButtonVisible(sdl_Button *button, bool visible)
{
        if (button->visible != visible)
        {
                button->visible = visible;
                
                button->owner->need_update = TRUE;
        }
}


/* Maximum amount of buttons in a bank */
#define MAX_BUTTONS 20

/*
 * The button_bank package
 */
/*
 * Initialize it
 */
static void sdl_ButtonBankInit(sdl_ButtonBank *bank, sdl_Window *window)
{
        bank->window = window;
        bank->buttons = C_ZNEW(MAX_BUTTONS, sdl_Button);
        bank->used = C_ZNEW(MAX_BUTTONS, bool);
        bank->need_update = TRUE;
}

/*
 * Clear the bank
 */
static void sdl_ButtonBankFree(sdl_ButtonBank *bank)
{
        FREE(bank->buttons);
        FREE(bank->used);
}

/*
 * Draw all the buttons on the screen
 */
static void sdl_ButtonBankDrawAll(sdl_ButtonBank *bank)
{
        int i;
        
        for (i = 0; i < MAX_BUTTONS; i++)
        {
                sdl_Button *button = &bank->buttons[i];
                
                if (!bank->used[i]) continue;
                if (!button->visible) continue;
                
                sdl_ButtonDraw(button);
        }
        bank->need_update = FALSE;
}

/*
 * Get a new button index
 */
static int sdl_ButtonBankNew(sdl_ButtonBank *bank)
{
        int i = 0;
        sdl_Button *new_button;
        
        while (bank->used[i] && (i < MAX_BUTTONS)) i++;
        
        if (i == MAX_BUTTONS)
        {
                /* Bugger! */
                return (-1);
        }
        
        /* Get the button */
        new_button = &bank->buttons[i];
        
        /* Mark the button as used */
        bank->used[i] = TRUE;
        
        /* Clear it */
        WIPE(new_button, sdl_Button);
        
        /* Mark it as mine */
        new_button->owner = bank;
        
        /* Default colours */
        new_button->unsel_colour = SDL_MapRGB(bank->window->surface->format, 160, 160, 60);
        new_button->sel_colour = SDL_MapRGB(bank->window->surface->format, 210, 210, 110);
        new_button->cap_colour = SDL_MapRGB(bank->window->surface->format, 0, 0, 0);
        
        /* Success */
        return (i);
}

/*
 * Retrieve button 'idx' or NULL
 */
static sdl_Button *sdl_ButtonBankGet(sdl_ButtonBank *bank, int idx)
{
        /* Check the index */
        if ((idx < 0) || (idx >= MAX_BUTTONS)) return (NULL);
        if (!bank->used[idx]) return (NULL);
        
        /* Return it */
        return &bank->buttons[idx];
}

#if 0
/*
 * Remove a Button by its index
 */
static void sdl_ButtonBankRemove(sdl_ButtonBank *bank, int idx)
{
        sdl_Button *button;
        
        /* Check the index */
        if ((idx < 0) || (idx >= MAX_BUTTONS)) return;
        if (!bank->used[idx]) return;
        
        /* Grab it */
        button = &bank->buttons[idx];
        
        /* Hide it */
        button->visible = FALSE;
        
        /* Draw it */
        bank->need_update = TRUE;
        
        /* Forget */
        bank->used[idx] = FALSE;
}

#endif

/*
 * Examine and respond to mouse presses
 * Return if we 'handled' the click
 */
static bool sdl_ButtonBankMouseDown(sdl_ButtonBank *bank, int x, int y)
{
        int i;
        
        /* Check every button */
        for (i = 0; i < MAX_BUTTONS; i++)
        {
                sdl_Button *button = &bank->buttons[i];
                
                /* Discard some */
                if (!bank->used[i]) continue;
                if (!button->visible) continue;
                
                /* Check the coordinates */
                if (point_in(&button->pos, x, y))
                {
                        button->selected = TRUE;
                        
                        /* Draw it */
                        bank->need_update = TRUE;
                        
                        return (TRUE);
                }
        }
        return (FALSE);
}

/*
 * Respond to a mouse button release
 */
static bool sdl_ButtonBankMouseUp(sdl_ButtonBank *bank, int x, int y)
{
        int i;
        
        /* Check every button */
        for (i = 0; i < MAX_BUTTONS; i++)
        {
                sdl_Button *button = &bank->buttons[i];
                
                /* Discard some */
                if (!bank->used[i]) continue;
                if (!button->visible) continue;
                
                /* Check the coordinates */
                if (point_in(&button->pos, x, y))
                {
                        /* Has this butoon been 'selected'? */
                        if (button->selected)
                        {
                                /* Activate the button (usually) */
                                if (button->activate) (*button->activate)(button);
                                
                                /* Now not selected */
                                button->selected = FALSE;
                                
                                /* Draw it */
                                bank->need_update = TRUE;
                                
                                return (TRUE);
                        }
                }
                else
                {
                        /* This button was 'selected' but the release of the */
                        /* mouse button was outside the area of this button */
                        if (button->selected)
                        {
                                /* Now not selected */
                                button->selected = FALSE;
                                
                                /* Draw it */
                                bank->need_update = TRUE;
                        }
                }
        }
        
        return (FALSE);
}

/*
 * sdl_Window functions
 */
static void sdl_WindowFree(sdl_Window* window)
{
        if (window->surface)
        {
                SDL_FreeSurface(window->surface);
                sdl_ButtonBankFree(&window->buttons);
                sdl_FontFree(&window->font);
                WIPE(window, sdl_Window);
        }
}

/*
 * Initialize a window
 */
static void sdl_WindowInit(sdl_Window* window, int w, int h, SDL_Surface *owner, cptr fontname)
{
        sdl_WindowFree(window);
        window->owner = owner;
        window->width = w;
        window->height = h;
        window->surface = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h,
                                                                                   owner->format->BitsPerPixel,
                                                                                   owner->format->Rmask, owner->format->Gmask,
                                                                                   owner->format->Bmask, owner->format->Amask);
        sdl_ButtonBankInit(&window->buttons, window);
        sdl_FontCreate(&window->font, fontname, window->surface);
        window->visible = TRUE;
        window->need_update = TRUE;
}


static void sdl_WindowBlit(sdl_Window* window)
{
        SDL_Rect rc;
        
        if (!window->visible) return;
        
        RECT(window->left, window->top, window->width, window->height, &rc);
        
        SDL_BlitSurface(window->surface, NULL, window->owner, &rc);
        SDL_UpdateRects(window->owner, 1, &rc);
}

static void sdl_WindowText(sdl_Window* window, Uint32 c, int x, int y, cptr s)
{
        sdl_FontDraw(&window->font, window->surface, c, x, y, strlen(s), s);
}

static void sdl_WindowUpdate(sdl_Window* window)
{
        if ((window->need_update || window->buttons.need_update) && (window->visible))
        {
                SDL_Event Event;
                
                SDL_FillRect(window->surface, NULL, back_colour);
                
                if (window->draw_extra) (*window->draw_extra)(window);
                
                sdl_ButtonBankDrawAll(&window->buttons);
                
                window->need_update = FALSE;
                
                WIPE(&Event, SDL_Event);
                
                Event.type = WINDOW_DRAW;
                
                Event.user.data1 = (void*)window;
                
                SDL_PushEvent(&Event);
        }
}



static void term_windowFree(term_window* win)
{
        if (win->surface)
        {
                SDL_FreeSurface(win->surface);
                win->surface = NULL;
#ifdef USE_GRAPHICS
                /* Invalidate the gfx surface */
                if (win->tiles)
                {
                        SDL_FreeSurface(win->tiles);
                        win->tiles = NULL;
                }
#endif
                term_nuke(&win->term_data);
        }
        
        sdl_FontFree(&win->font);
}

static errr save_prefs(void);
static void hook_quit(cptr str)
{
        int i;
        
        save_prefs();
        
        string_free(ANGBAND_DIR_USER_SDL);
        
        /* Free the surfaces of the windows */
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                term_windowFree(&windows[i]);
                string_free(windows[i].req_font);
        }
        
#ifdef USE_GRAPHICS
        /* Free the graphics surface */
        if (GfxSurface) SDL_FreeSurface(GfxSurface);
#endif
        /* Free the 'System font' */
        sdl_FontFree(&SystemFont);
        
        /* Free the statusbar window */
        sdl_WindowFree(&StatusBar);
        
        /* free the popup window */
        sdl_WindowFree(&PopUp);
        
        /* Free the main surface */
        SDL_FreeSurface(AppWin);
        
        /* Shut down the font library */
        TTF_Quit(); 
        
        /* Shut down SDL */
        SDL_Quit();
        
        for (i = 0; i < MAX_FONTS; i++)
                string_free(FontList[i]);
}

static void BringToTop(void)
{
        int i, idx;
        
        for (idx = 0; idx < ANGBAND_TERM_MAX; idx++)
        {
                if (Zorder[idx] == SelectedTerm) break;
        }
        
        if (idx == ANGBAND_TERM_MAX) return;
        
        for (i = idx; i < ANGBAND_TERM_MAX - 1; i++)
        {
                Zorder[i] = Zorder[i + 1];
        }
        
        Zorder[ANGBAND_TERM_MAX - 1] = SelectedTerm;
}

        
        
/*
 * Validate a file
 */
static void validate_file(cptr s)
{
        if (!file_exists(s))
                quit_fmt("Cannot find required file:\n%s", s);
}

/*
 * Find a window that is under the points x,y on
 * the main screen
 */

static int sdl_LocateWin(int x, int y)
{
        int i;
        
        for (i = ANGBAND_TERM_MAX - 1; i >= 0; i--)
        {
                term_window *win = &windows[Zorder[i]];
                SDL_Rect rc;
                
                if (!win->visible) continue;
                if (!point_in(RECT(win->left, win->top, win->width, win->height, &rc), x, y)) continue;
                
                return (Zorder[i]);
        }
        
        return (-1);
}

static void draw_statusbar(sdl_Window *window)
{
        char buf[128];
        term_window *win = &windows[SelectedTerm];
        int fw = window->font.width;
        int x = 1;
        sdl_Button *button;
        
        SDL_Rect rc;
        
        u32b c = SDL_MapRGB(window->surface->format, 160, 160, 60);

        RECT(0, StatusBar.height - 1, StatusBar.width, 1, &rc);
        SDL_FillRect(StatusBar.surface, &rc, c);
        
        button = sdl_ButtonBankGet(&StatusBar.buttons, AboutSelect);
        x += button->pos.w + 20;
        
        sdl_WindowText(&StatusBar, c, x, 1, "Term:");
        x += 5 * fw;
        
        button = sdl_ButtonBankGet(&StatusBar.buttons, TermSelect);
        button->pos.x = x;
        x += button->pos.w + 10;
        
        my_strcpy(buf, format("(%dx%d)", win->cols, win->rows), sizeof(buf));
        sdl_WindowText(&StatusBar, c, x, 1, buf);
        x += strlen(buf) * fw + 20;
        
        sdl_WindowText(&StatusBar, c, x, 1, "Visible:");
        x += 8 * fw;
        
        button = sdl_ButtonBankGet(&StatusBar.buttons, VisibleSelect);
        button->pos.x = x;
        x += button->pos.w + 20;
        
        button = sdl_ButtonBankGet(&StatusBar.buttons, FontSelect);
        if (button->visible) sdl_WindowText(&StatusBar, c, x, 1, "Font:");
        x += 5 * fw;
        
        
        button->pos.x = x;
        x += button->pos.w + 20;
        
        button = sdl_ButtonBankGet(&StatusBar.buttons, MoreSelect);
        button->pos.x = x;
        
        x += button->pos.w + 20;
        
        
}



static void sdl_BlitWin(term_window *win)
{
        SDL_Rect rc;
        
        if (!win->surface) return;
        if (!win->visible) return;
        if (win->uRect.x == -1) return;
        
        /* Select the area to be updated */
        RECT(win->left + win->uRect.x, win->top + win->uRect.y, win->uRect.w, win->uRect.h, &rc);
        
        SDL_BlitSurface(win->surface, &win->uRect, AppWin, &rc);
        SDL_UpdateRects(AppWin, 1, &rc);
        
        /* Mark the update as complete */
        win->uRect.x = -1;
}

static void sdl_BlitAll(void)
{
        SDL_Rect rc;
        sdl_Window *window = &StatusBar;
        int i;
        Uint32 colour = SDL_MapRGB(AppWin->format, 160, 160, 60);
        /* int32 ccolour = SDL_MapRGB(AppWin->format, 160, 40, 40); */
        SDL_FillRect(AppWin, NULL, back_colour);
        
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                term_window *win = &windows[Zorder[i]];
                
                if (!win->surface) continue;
                if (!win->visible) continue;
                
                RECT(win->left, win->top, win->width, win->height, &rc);
                SDL_BlitSurface(win->surface, NULL, AppWin, &rc);
                
                if (Zorder[i] == SelectedTerm)
                {
                        SizingSpot.w = 10;
                        SizingSpot.h = 10;
                        SizingSpot.x = win->left + win->width - 10;
                        SizingSpot.y = win->top + win->height - 10;
                        /* SDL_FillRect(AppWin, &SizingSpot, ccolour); */
                        
                        if (Sizing)
                        {
                                int width = 2;
                                int grabsize = 10;
                                rc = SizingRect;
                                SizingSpot.w = grabsize;
                                SizingSpot.h = grabsize;
                                SizingSpot.x = SizingRect.x + SizingRect.w - grabsize;
                                SizingSpot.y = SizingRect.y + SizingRect.h - grabsize;
                                sdl_DrawBox(AppWin, &rc, colour, width);
                        }
                }
        }
        
        RECT(window->left, window->top, window->width, window->height, &rc);
        
        SDL_BlitSurface(window->surface, NULL, AppWin, &rc);
        
        SDL_UpdateRect(AppWin, 0, 0, AppWin->w, AppWin->h);
        
        
}

static void RemovePopUp(void)
{
        PopUp.visible = FALSE;
        popped = FALSE;
        sdl_BlitAll();
}

static void QuitActivate(sdl_Button *sender)
{
        SDL_Event Event;
        
        Event.type = SDL_QUIT;
        
        SDL_PushEvent(&Event);
}

static void SetStatusButtons(void)
{
        term_window *win = &windows[SelectedTerm];
        sdl_Button *button = sdl_ButtonBankGet(&StatusBar.buttons, TermSelect);
        sdl_Button *fontbutton = sdl_ButtonBankGet(&StatusBar.buttons, FontSelect);
        sdl_Button *visbutton = sdl_ButtonBankGet(&StatusBar.buttons, VisibleSelect);
        
        sdl_ButtonCaption(button, angband_term_name[SelectedTerm]);
        
        if (!win->visible)
        {
                sdl_ButtonVisible(fontbutton, FALSE);
                sdl_ButtonCaption(visbutton, "No");
        }
        else
        {
                sdl_ButtonVisible(fontbutton, TRUE);
                sdl_ButtonCaption(fontbutton, win->font.name);
                sdl_ButtonCaption(visbutton, "Yes");
        }
}
        
static void TermFocus(int idx)
{
        if (SelectedTerm == idx) return;
        
        SelectedTerm = idx;
        
        BringToTop();
        
        SetStatusButtons();
        
        sdl_BlitAll();
}       

static void AboutDraw(sdl_Window *win)
{
        SDL_Rect rc;
        
        /* Wow - a different colour! */
        Uint32 colour = SDL_MapRGB(win->surface->format, 160, 60, 60);
        
        RECT(0, 0, win->width, win->height, &rc);
        
        /* Draw a nice box */
        sdl_DrawBox(win->surface, &rc, colour, 5);
}

        
static void AboutActivate(sdl_Button *sender)
{
        int width = 300;
        int height = 300;
        
        sdl_WindowInit(&PopUp, width, height, AppWin, StatusBar.font.name);
        PopUp.left = (AppWin->w / 2) - width / 2;
        PopUp.top = (AppWin->h / 2) - height / 2;
        PopUp.draw_extra = AboutDraw;
        
        popped = TRUE;
}

static void SelectTerm(sdl_Button *sender)
{
        RemovePopUp();
        
        TermFocus(sender->tag);
}
        
static void TermActivate(sdl_Button *sender)
{
        int i, maxl = 0; 
        int width, height = ANGBAND_TERM_MAX * (StatusBar.font.height + 1);

        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                int l = strlen(angband_term_name[i]); 
                if (l > maxl) maxl = l;
        }
        
        width = maxl * StatusBar.font.width + 20;
        
        sdl_WindowInit(&PopUp, width, height, AppWin, StatusBar.font.name);
        PopUp.left = sender->pos.x;
        PopUp.top = sender->pos.y;
        
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                int h = PopUp.font.height;
                int b = sdl_ButtonBankNew(&PopUp.buttons);
                sdl_Button *button = sdl_ButtonBankGet(&PopUp.buttons, b);
                sdl_ButtonSize(button, width - 2 , h);
                sdl_ButtonMove(button, 1, i * (h + 1));
                sdl_ButtonCaption(button, angband_term_name[i]);
                sdl_ButtonVisible(button, TRUE);
                button->tag = i;
                button->activate = SelectTerm;
        }
        popped = TRUE;
}

static void ResizeWin(term_window* win, int w, int h);
static void term_data_link_sdl(term_window *win);

static void VisibleActivate(sdl_Button *sender)
{
        term_window *window = &windows[SelectedTerm];
        
        if (SelectedTerm == 0) return;
        
        if (window->visible)
        {
                window->visible = FALSE;
                term_windowFree(window);
                angband_term[SelectedTerm] = NULL;
                
        }
        else
        {
                window->visible = TRUE;
                ResizeWin(window, window->width, window->height);
                
        }
        
        SetStatusButtons();
        sdl_BlitAll();
}

static void SelectFont(sdl_Button *sender)
{
        term_window *window = &windows[SelectedTerm];
        int w, h;
        
        sdl_FontFree(&window->font);
        string_free(window->req_font);

        window->req_font = string_make(sender->caption);
        
        sdl_CheckFont(window->req_font, &w, &h);
#ifdef USE_GRAPHICS     
        /* Invalidate the gfx surface */
        if (window->tiles)
        {
                SDL_FreeSurface(window->tiles);
                window->tiles = NULL;
        }
#endif
        
        ResizeWin(window, (w * window->cols) + (2 * window->border),
                          (h * window->rows) + window->border + window->title_height);
        
        SetStatusButtons();
        
        RemovePopUp();
        
}


static void FontActivate(sdl_Button *sender)
{
        int i, maxl = 0; 
        int width, height = num_fonts * (StatusBar.font.height + 1);

        for (i = 0; i < num_fonts; i++)
        {
                int l = strlen(FontList[i]); 
                if (l > maxl) maxl = l;
        }
        
        width = maxl * StatusBar.font.width + 20;
        
        sdl_WindowInit(&PopUp, width, height, AppWin, StatusBar.font.name);
        PopUp.left = sender->pos.x;
        PopUp.top = sender->pos.y;
        
        for (i = 0; i < num_fonts; i++)
        {
                int h = PopUp.font.height;
                int b = sdl_ButtonBankNew(&PopUp.buttons);
                sdl_Button *button = sdl_ButtonBankGet(&PopUp.buttons, b);
                sdl_ButtonSize(button, width - 2 , h);
                sdl_ButtonMove(button, 1, i * (h + 1));
                sdl_ButtonCaption(button, FontList[i]);
                sdl_ButtonVisible(button, TRUE);
                button->activate = SelectFont;
        }
        popped = TRUE;
}

#ifdef USE_GRAPHICS
static errr load_gfx(void);

static void SelectGfx(sdl_Button *sender)
{
        SelectedGfx = sender->tag;
}
#endif

static void AcceptChanges(sdl_Button *sender)
{
        sdl_Button *button;
        bool do_update = FALSE;
        bool do_video_reset = FALSE;
        
#ifdef USE_GRAPHICS
        /* Check to see if bigtile has changed */
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreBigtile);
        
        if (button->tag != use_bigtile)
        {
                do_update = TRUE;
                
                use_bigtile = !use_bigtile;
        }
        
        if (use_graphics != SelectedGfx)
        {
                do_update = TRUE;
                
                use_graphics = SelectedGfx;
                
                if (use_graphics)
                {
                        arg_graphics = TRUE;
                        load_gfx();
                }
                else
                {
                        arg_graphics = FALSE;
                        use_bigtile = FALSE;
                        reset_visuals(TRUE);
                }
        }
        
        /* Invalidate all the gfx surfaces */
        if (do_update)
        {
                int i;
                for (i = 0; i < ANGBAND_TERM_MAX; i++)
                {
                        term_window *win = &windows[i];
                        if (win->tiles)
                        {
                                SDL_FreeSurface(win->tiles);
                                win->tiles = NULL;
                        }
                }
        }       
        
#endif
        
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreFullscreen);
        
        if (button->tag != fullscreen)
        {
                fullscreen = !fullscreen;
                
                do_video_reset = TRUE;
        }
        
        
        SetStatusButtons();
        
        RemovePopUp();
        
        if (do_update)
        {
                if (character_dungeon) do_cmd_redraw();
        }
        
        if (do_video_reset)
        {
                SDL_Event Event;
                
                WIPE(&Event, SDL_Event);
                
                Event.type = SDL_VIDEORESIZE;
                Event.resize.w = screen_w;
                Event.resize.h = screen_h;
                
                SDL_PushEvent(&Event);
        }
        
}

static void FlipTag(sdl_Button *sender)
{
        if (sender->tag)
        {
                sender->tag = 0;
                sdl_ButtonCaption(sender, "Off");
        }
        else
        {
                sender->tag = 1;
                sdl_ButtonCaption(sender, "On");
        }
}

static void SnapChange(sdl_Button *sender)
{
        SnapRange += sender->tag;
        if (SnapRange < 0) SnapRange = 0;
        if (SnapRange > 20) SnapRange = 20;
        PopUp.need_update = TRUE;
}

static void MoreDraw(sdl_Window *win)
{
        SDL_Rect rc;
        sdl_Button *button;
        int y = 20, i;
        
        /* Wow - a different colour! */
        Uint32 colour = SDL_MapRGB(win->surface->format, 160, 60, 60);
        
        RECT(0, 0, win->width, win->height, &rc);
        
        /* Draw a nice box */
        sdl_DrawBox(win->surface, &rc, colour, 5);
        
#ifdef USE_GRAPHICS
        
        button = sdl_ButtonBankGet(&win->buttons, MoreBigtile);
        
        if (SelectedGfx)
        {
                sdl_ButtonVisible(button, TRUE);
                sdl_WindowText(win, colour, 20, y, "Bigtile is:");
                
                sdl_ButtonMove(button, 200, y);
                
                y += 20;
        }
        else
        {
                sdl_ButtonVisible(button, FALSE);
        }
        
        
        
        sdl_WindowText(win, colour, 20, y, "Selected Graphics:");
        sdl_WindowText(win, SDL_MapRGB(win->surface->format, 210, 110, 110),
                                   200, y, GfxDesc[SelectedGfx].name);
        
        y += 20;
        
        sdl_WindowText(win, colour, 20, y, "Available Graphics:");
        
        for (i = 0; i < GfxModes; i++)
        {
                if (!GfxDesc[i].avail) continue;
                button = sdl_ButtonBankGet(&win->buttons, GfxButtons[i]);
                sdl_ButtonMove(button, 200, y);
                y += 20;
        }
#endif  
        
        button = sdl_ButtonBankGet(&win->buttons, MoreFullscreen);
        sdl_WindowText(win, colour, 20, y, "Fullscreen is:");
        
        sdl_ButtonMove(button, 200, y);
        y+= 20;
        
        sdl_WindowText(win, colour, 20, y, format("Snap range is %d.", SnapRange));
        button = sdl_ButtonBankGet(&win->buttons, MoreSnapMinus);
        sdl_ButtonMove(button, 200, y);
        
        button = sdl_ButtonBankGet(&win->buttons, MoreSnapPlus);
        sdl_ButtonMove(button, 230, y);
}

static void MoreActivate(sdl_Button *sender)
{
        int width = 300;
        int height = 300;
        int i;
        sdl_Button *button;
        
        Uint32 ucolour;
        Uint32 scolour;
        
        sdl_WindowInit(&PopUp, width, height, AppWin, StatusBar.font.name);
        PopUp.left = (AppWin->w / 2) - width / 2;
        PopUp.top = (AppWin->h / 2) - height / 2;
        PopUp.draw_extra = MoreDraw;
        
        ucolour = SDL_MapRGB(PopUp.surface->format, 160, 60, 60);
        scolour = SDL_MapRGB(PopUp.surface->format, 210, 110, 110);
        
#ifdef USE_GRAPHICS
        MoreBigtile = sdl_ButtonBankNew(&PopUp.buttons);
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreBigtile);
        
        button->unsel_colour = ucolour;
        button->sel_colour = scolour;
        sdl_ButtonSize(button, 50 , PopUp.font.height + 2);
        sdl_ButtonVisible(button, TRUE);
        sdl_ButtonCaption(button, use_bigtile ? "On" : "Off");
        button->tag = use_bigtile;
        button->activate = FlipTag;
        
        SelectedGfx = use_graphics;
        
        for (i = 0; i < GfxModes; i++)
        {
                if (!GfxDesc[i].avail) continue;
                GfxButtons[i] = sdl_ButtonBankNew(&PopUp.buttons);
                button = sdl_ButtonBankGet(&PopUp.buttons, GfxButtons[i]);
                
                button->unsel_colour = ucolour;
                button->sel_colour = scolour;
                sdl_ButtonSize(button, 50 , PopUp.font.height + 2);
                sdl_ButtonVisible(button, TRUE);
                sdl_ButtonCaption(button, GfxDesc[i].name);
                button->tag = i;
                button->activate = SelectGfx;
        }
#endif
        MoreFullscreen = sdl_ButtonBankNew(&PopUp.buttons);
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreFullscreen);
        
        button->unsel_colour = ucolour;
        button->sel_colour = scolour;
        sdl_ButtonSize(button, 50 , PopUp.font.height + 2);
        sdl_ButtonVisible(button, TRUE);
        sdl_ButtonCaption(button, fullscreen ? "On" : "Off");
        button->tag = fullscreen;
        button->activate = FlipTag;
        
        MoreSnapPlus = sdl_ButtonBankNew(&PopUp.buttons);
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreSnapPlus);
        
        button->unsel_colour = ucolour;
        button->sel_colour = scolour;
        sdl_ButtonSize(button, 20, PopUp.font.height + 2);
        sdl_ButtonCaption(button, "+");
        button->tag = 1;
        sdl_ButtonVisible(button, TRUE);
        button->activate = SnapChange;
        
        MoreSnapMinus = sdl_ButtonBankNew(&PopUp.buttons);
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreSnapMinus);
        
        button->unsel_colour = ucolour;
        button->sel_colour = scolour;
        sdl_ButtonSize(button, 20, PopUp.font.height + 2);
        sdl_ButtonCaption(button, "-");
        button->tag = -1;
        sdl_ButtonVisible(button, TRUE);
        button->activate = SnapChange;
        
        MoreOK = sdl_ButtonBankNew(&PopUp.buttons);
        button = sdl_ButtonBankGet(&PopUp.buttons, MoreOK);
        
        button->unsel_colour = ucolour;
        button->sel_colour = scolour;
        sdl_ButtonSize(button, 50 , PopUp.font.height + 2);
        sdl_ButtonVisible(button, TRUE);
        sdl_ButtonCaption(button, "OK");
        sdl_ButtonMove(button, width / 2 - 25, height - 40);
        button->activate = AcceptChanges;
        
        popped = TRUE;
}

static errr Term_xtra_sdl_clear(void);

/*
 * Make a window with size (x,y) pixels
 * Note: The actual size of the window may end up smaller.
 * This may be called when a window wants resizing,
 * is made visible, or the font has changed.
 * This function doesn't go in for heavy optimization, and doesn't need it-
 * it may initialize a few too many redraws or whatnot, but everything gets done!
 */
static void ResizeWin(term_window* win, int w, int h)
{
        /* Don't bother */
        if (!win->visible) return;
        
        win->border = 2;
        win->title_height = StatusHeight;
        
        /* No font - a new font is needed -> get dimensions */
        if (!win->font.data)
        {
                /* Get font dimensions */
                sdl_CheckFont(win->req_font, &win->tile_wid, &win->tile_hgt);
                
                /* Oops */
                if (!win->tile_wid || !win->tile_hgt)
                        quit(format("Unable to find font '%s'.", win->req_font));
        }
        
        /* Get the amount of columns & rows */
        win->cols = (w - (win->border * 2)) / win->tile_wid;
        win->rows = (h - win->border - win->title_height) / win->tile_hgt;
        
        /* Calculate the width & height */
        win->width = (win->cols * win->tile_wid) + (win->border * 2);
        win->height = (win->rows * win->tile_hgt) + win->border + win->title_height;
        
        /* Delete the old surface */
        if (win->surface) SDL_FreeSurface(win->surface);
        
        /* Create a new surface */
        win->surface = SDL_CreateRGBSurface(SDL_SWSURFACE, win->width, win->height,
                                                                                   AppWin->format->BitsPerPixel,
                                                                                   AppWin->format->Rmask, AppWin->format->Gmask,
                                                                                   AppWin->format->Bmask, AppWin->format->Amask);
        
        /* Fill it */
        SDL_FillRect(win->surface, NULL, SDL_MapRGB(AppWin->format, 160, 160, 60));
        
        /* Label it */
        sdl_FontDraw(&SystemFont, win->surface, back_colour, 1, 1,
                                 strlen(angband_term_name[win->Term_idx]), angband_term_name[win->Term_idx]);
        
        /* Mark the whole window for redraw */
        RECT(0, 0, win->width, win->height, &win->uRect);
        
        /* Create the font if we need to */
        if (!win->font.data)
        {
                sdl_FontCreate(&win->font, win->req_font, win->surface);
        }
        
        /* This window was never visible before */
        if (!angband_term[win->Term_idx])
        {
                term *old = Term;
                
                /* Initialize the term data */
                term_data_link_sdl(win);
                
                /* Make it visible to angband */
                angband_term[win->Term_idx] = &win->term_data;
                
                /* Activate it */
                Term_activate((term*)&win->term_data);
                
                /* Redraw */
                Term_redraw();
                
                /* Restore */
                Term_activate(old);
        }
        /* Resize the term */
        else
        {
                term *old = Term;
                
                /* Activate it */
                Term_activate((term*)&win->term_data);
                
                /* Resize */
                Term_resize(win->cols, win->rows);
                
                /* Redraw */
                Term_redraw();
                
                /* Restore */
                Term_activate(old);
        }
        
        /* Calculate the hotspot */
        if (win->Term_idx == SelectedTerm)
        {
                SizingSpot.w = 10;
                SizingSpot.h = 10;
                SizingSpot.x = win->left + win->width - 10;
                SizingSpot.y = win->top + win->height - 10;
        }
        
        StatusBar.need_update = TRUE;
        
        /* HACK - Redraw all windows */
        if (character_dungeon) do_cmd_redraw();
}


static errr load_prefs(void)
{
        char buf[1024];
        ang_file *fff;
        term_window *win;
        int i;
        
        /* Initialize the windows with crappy defaults! */
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                win = &windows[i];
                
                /* Clear the data */
                WIPE(win, term_window);
                
                /* Who? */
                win->Term_idx = i;
                
                win->req_font = string_make(DEFAULT_FONT_FILE);
                
                if (i == 0)
                {
                        win->top = StatusHeight;
                        win->width = 600;
                        win->height = 380;
                        win->keys = 1024;
                        win->visible = TRUE;
                }
                else
                {
                        win->top = 400 + (i * 10);
                        win->left = (i - 1) * 10;
                        win->width = 400;
                        win->height = 200;
                        win->keys = 32;
                        
                        win->visible = FALSE;
                }
        }
        
        /* Build the path */
        path_build(buf, sizeof(buf), ANGBAND_DIR_USER, "sdlinit.txt");
        
        /* XXXXX */
        ANGBAND_DIR_USER_SDL = string_make(buf);
        
        /* Open the file */
        fff = file_open(buf, MODE_READ, -1);
        
        /* Check it */
        if (!fff) return (1);
        
        /* Process the file */
        while (file_getl(fff, buf, sizeof(buf)))
        {
                char *s;
                if (!buf[0]) continue;
                
                if (buf[0] == '#') continue;
                
                s = strchr(buf, '=');
                s++;
                while (!isalnum(*s)) s++;
                
                if (strstr(buf, "Resolution"))
                {
                        screen_w = atoi(s);
                        s = strchr(buf, 'x');
                        screen_h = atoi(s + 1);
                }
                else if (strstr(buf, "Fullscreen"))
                {
                        fullscreen = atoi(s);
                }
                else if (strstr(buf, "Graphics"))
                {
                        use_graphics = atoi(s);
                        if (use_graphics) arg_graphics = TRUE;
                }
                else if (strstr(buf, "Bigtile"))
                {
                        use_bigtile = atoi(s);
                }
                else if (strstr(buf, "Window"))
                {
                        win = &windows[atoi(s)];
                }
                else if (strstr(buf, "Visible"))
                {
                        win->visible = atoi(s);
                }
                else if (strstr(buf, "Left"))
                {
                        win->left = atoi(s);
                }
                else if (strstr(buf, "Top"))
                {
                        win->top = atoi(s);
                }
                else if (strstr(buf, "Width"))
                {
                        win->width = atoi(s);
                }
                else if (strstr(buf, "Height"))
                {
                        win->height = atoi(s);
                }
                else if (strstr(buf, "Keys"))
                {
                        win->keys = atoi(s);
                }
                else if (strstr(buf, "Font"))
                {
                        win->req_font = string_make(s);
                }
                
                
        }
        
        if (screen_w < 640) screen_w = 640;
        if (screen_h < 480) screen_h = 480;
        
        file_close(fff);
        
        return (0);
}

static errr save_prefs(void)
{
        ang_file *fff;
        int i;

        /* Open the file */
        fff = file_open(ANGBAND_DIR_USER_SDL, MODE_WRITE, FTYPE_TEXT);

        /* Check it */
        if (!fff) return (1);

        file_putf(fff, "Resolution = %dx%d\n", screen_w, screen_h);
        file_putf(fff, "Fullscreen = %d\n", fullscreen);
        file_putf(fff, "Graphics = %d\n", use_graphics);
        file_putf(fff, "Bigtile = %d\n\n", use_bigtile);
        
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                term_window *win = &windows[i];
                
                file_putf(fff, "Window = %d\n", i);
                file_putf(fff, "Visible = %d\n", (int)win->visible);
                file_putf(fff, "Left = %d\n", win->left);
                file_putf(fff, "Top = %d\n", win->top);
                file_putf(fff, "Width = %d\n", win->width);
                file_putf(fff, "Height = %d\n", win->height);
                
                file_putf(fff, "Keys = %d\n", win->keys);
                
                file_putf(fff, "Font = %s\n\n", win->req_font);
        }       
        
        file_close(fff);
        
        /* Done */
        return (0);
}

static void set_update_rect(term_window *win, SDL_Rect *rc);

static void DrawSizeWidget(void)
{
        Uint32 colour = SDL_MapRGB(AppWin->format, 30, 160, 70);
        SDL_FillRect(AppWin, &SizingSpot, colour);
        SDL_UpdateRects(AppWin, 1, &SizingSpot);
}

static int Movingx;
static int Movingy;

/*
 * Is What within Range units of Origin
 */

#define closeto(Origin, What, Range) \
        ((ABS((Origin) - (What))) < (Range))

/*
 * This function keeps the 'mouse' info up to date,
 * and reacts to mouse buttons appropriately.
 */
static void sdl_HandleMouseEvent(SDL_Event *event)
{
        term *old = Term;
        term_window *win;
        switch (event->type)
        {
                /* Mouse moved */
                case SDL_MOUSEMOTION:
                {
                        mouse.x = event->motion.x;
                        mouse.y = event->motion.y;
                        win = &windows[SelectedTerm];
                        
                        /* We are moving a window */
                        if (Moving)
                        {
                                int i;
                                
                                /* Move the window */
                                win->left = (mouse.x - Movingx);
                                win->top = (mouse.y - Movingy);
                                
                                /* Left bounds check */
                                if (win->left < 0)
                                {
                                        win->left = 0;
                                        Movingx = mouse.x;
                                }
                                        
                                /* Right bounds check */
                                if ((win->left + win->width) > AppWin->w)
                                {
                                        win->left = AppWin->w - win->width;
                                        Movingx = mouse.x - win->left;
                                }
                                
                                /* Top bounds check */
                                if (win->top < StatusHeight)
                                {
                                        win->top = StatusHeight;
                                        Movingy = mouse.y - win->top;
                                }
                                
                                /* Bottom bounds check */
                                if ((win->top + win->height) > AppWin->h)
                                {
                                        win->top = AppWin->h - win->height;
                                        Movingy = mouse.y - win->top;
                                }
                                
                                for (i = 0; i < ANGBAND_TERM_MAX; i++)
                                {
                                        term_window *snapper = &windows[i];
                                        
                                        /* Can't snap to self... */
                                        if (i == SelectedTerm) continue;
                                        
                                        /* Can't snap to the invisible */
                                        if (!snapper->visible) continue;
                                        
                                        /* Check the windows are across from each other */
                                        if ((snapper->top < win->top + win->height) && (win->top < snapper->top + snapper->height))
                                        {
                                                /* Lets try to the left... */
                                                if (closeto(win->left, snapper->left + snapper->width, SnapRange))
                                                {
                                                        win->left = snapper->left + snapper->width;
                                                        Movingx = mouse.x - win->left;
                                                }
                                                /* Maybe to the right */
                                                if (closeto(win->left + win->width, snapper->left, SnapRange))
                                                {
                                                        win->left = snapper->left - win->width;
                                                        Movingx = mouse.x - win->left;
                                                }
                                        }
                                        
                                        /* Check the windows are above/below each other */
                                        if ((snapper->left < win->left + win->width) && (win->left < snapper->left + snapper->width))
                                        {
                                                /* Lets try to the top... */
                                                if (closeto(win->top, snapper->top + snapper->height, SnapRange))
                                                {
                                                        win->top = snapper->top + snapper->height;
                                                        Movingy = mouse.y - win->top;
                                                }
                                                /* Maybe to the bottom */
                                                if (closeto(win->top + win->height, snapper->top, SnapRange))
                                                {
                                                        win->top = snapper->top - win->height;
                                                        Movingy = mouse.y - win->top;
                                                }
                                        }
                                        
                                }
                                
                                /* Show on the screen */
                                sdl_BlitAll();
                        }
                        
                        /* A window is being re-sized */
                        else if (Sizing)
                        {
                                /* Adjust the sizing rectangle */
                                SizingRect.w = win->width - win->left + (mouse.x - Movingx);
                                SizingRect.h = win->height - win->top + (mouse.y - Movingy);
                                
                                /* XXX - The main window can't be too small */
                                if (SelectedTerm == 0)
                                {
                                        int minwidth = (win->tile_wid * 80) + 2 * win->border;
                                        int minheight = (win->tile_hgt * 24) + win->border + win->title_height;
                                        if (SizingRect.w < minwidth)
                                                SizingRect.w = minwidth;
                                        if (SizingRect.h < minheight)
                                                SizingRect.h = minheight;
                                }
                                
                                /* Show on the screen */                                
                                sdl_BlitAll();
                        }
                        else if (!popped)
                        {
                                /* Have a look for the corner stuff */
                                if (point_in(&SizingSpot, mouse.x, mouse.y))
                                {
                                        if (!Sizingshow)
                                        {
                                                /* Indicate the hotspot */
                                                Sizingshow = TRUE;
                                                DrawSizeWidget();
                                        }
                                }
                                else if (Sizingshow)
                                {
                                        SDL_Rect rc;
                                        Sizingshow = FALSE;
                                        RECT(win->width - 10, win->height - 10, 10, 10, &rc);
                                        set_update_rect(win, &rc);
                                        sdl_BlitWin(win);
                                }
                        }
                        break;
                }
                        
                /* A button has been pressed */
                case SDL_MOUSEBUTTONDOWN:
                {
                        
                        sdl_Window *window;
                        bool res;
                        int idx = sdl_LocateWin(mouse.x, mouse.y);
                        
                        if (event->button.button == SDL_BUTTON_LEFT)
                        {
                                bool just_gained_focus = FALSE;
                                mouse.left = 1;
                                mouse.leftx = event->button.x;
                                mouse.lefty = event->button.y;
                                
                                /* Pop up window gets priority */
                                if (popped) window = &PopUp; else window = &StatusBar;
                                
                                /* React to a button press */
                                res = sdl_ButtonBankMouseDown(&window->buttons, mouse.x - window->left, mouse.y - window->top);
                                
                                /* If pop-up window active and no reaction then cancel the popup */
                                if (popped && !res)
                                {
                                        RemovePopUp();
                                        break;
                                }
                                
                                /* Has this mouse press been handled */
                                if (res) break;
                                
                                /* Is the mouse press in a term_window? */
                                if (idx < 0) break;
                                
                                /* The 'focused' window has changed */
                                if (idx != SelectedTerm)
                                {
                                        TermFocus(idx);
                                        just_gained_focus = TRUE;
                                }
        
                                /* A button press has happened on the focused term window */
                                win = &windows[idx];
                                
                                /* Check for mouse press in the title bar */
                                if (mouse.y < win->top + win->title_height)
                                {
                                        /* Let's get moving */
                                        Moving = TRUE;
                                        
                                        /* BringToTop(idx); */
                                        
                                        /* Remember where we started */
                                        Movingx = mouse.x - win->left;
                                        Movingy = mouse.y - win->top;
                                }
                                
                                /* Check for the little hotspot in the botton right corner */
                                else if (point_in(&SizingSpot, mouse.x, mouse.y))
                                {
                                        /* Let's get sizing */
                                        Sizing = TRUE;
                                        
                                        /* Create the sizing rectangle */
                                        RECT(win->left, win->top, win->width, win->height, &SizingRect);
                                        
                                        /* Remember where we started */
                                        Movingx = mouse.x - win->left;
                                        Movingy = mouse.y - win->top;
                                        
                                }
                                
                                /* Signal a mouse press to angband (only if the window is already focused) */
                                else if (!just_gained_focus)
                                {
                                        if (win->visible)
                                        {
                                                /* Calculate the 'cell' coords */
                                                int x = (mouse.x - win->left - win->border) / win->tile_wid;
                                                int y = (mouse.y - win->top - win->title_height) / win->tile_hgt;
                                                
                                                /* Send the mousepress to the appropriate term */
                                                Term_activate(angband_term[idx]);
                                                Term_mousepress(x, y, 1);
                                                Term_activate(old);
                                        }
                                }
                        }
                        
                        /* Process right mouse button */
                        else if (event->button.button == SDL_BUTTON_RIGHT)
                        {
                                mouse.right = 1;
                                mouse.rightx = event->button.x;
                                mouse.righty = event->button.y;
                                
                                /* Right-click always cancels the popup */
                                if (popped)
                                {
                                        popped = FALSE;
                                }
                                /* See if it's inside a term_window */
                                else if (idx != -1)
                                {
                                        int x, y;
                                        win = &windows[idx];
                                        
                                        /* Calculate the 'cell' coords */
                                        x = (mouse.x - win->left - win->border) / win->tile_wid;
                                        y = (mouse.y - win->top - win->title_height) / win->tile_hgt;
                                        
                                        /* Bounds check */
                                        if ((x >= 0) && (y >= 0) && (x < win->cols) && (y < win->rows))
                                        {
                                                /* Send the mousepress to the appropriate term */
                                                Term_activate(angband_term[idx]);
                                                Term_mousepress(x, y, 2);
                                                Term_activate(old);
                                        }
                                }
                        }
                        
                        break;
                }
                case SDL_MOUSEBUTTONUP:
                {
                        /* Handle release of left button */
                        if (event->button.button == SDL_BUTTON_LEFT)
                        {
                                sdl_Window *window;
                                bool res;
                                mouse.left = 0;
                                
                                /* Pop up window gets priority */
                                if (popped) window = &PopUp; else window = &StatusBar;
                                
                                /* React to a button release */
                                res = sdl_ButtonBankMouseUp(&window->buttons, mouse.x - window->left, mouse.y - window->top);
                                
                                /* Cancel popup */
                                if (popped && !res)
                                {
                                        RemovePopUp();
                                }
                                
                                /* Finish moving */
                                if (Moving)
                                {
                                        Moving = FALSE;
                                        
                                        /* Update */
                                        sdl_BlitAll();
                                }
                                
                                /* Finish sizing */
                                if (Sizing)
                                {
                                        /* Sort out the window */
                                        ResizeWin(&windows[SelectedTerm], SizingRect.w, SizingRect.h);
                                        Sizing = FALSE;
                                        
                                        /* Update */
                                        sdl_BlitAll();
                                }
                        }
                        else if (event->button.button == SDL_BUTTON_RIGHT)
                        {
                                mouse.right = 0;
                        }
                        break;
                }       
                        
        }
}

/*
 * Handle keypresses.
 *
 * We treat left and right modifier keys as equivalent.
 * We ignore any key without a valid SDLK index.
 */
static void sdl_keypress(SDL_keysym keysym)
{
        u16b key_code = keysym.unicode;
        SDLKey key_sym = keysym.sym;
        
        /* Store the value of various modifier keys */
        bool mc = (bool)(keysym.mod & (KMOD_CTRL));
        bool ms = (bool)(keysym.mod & (KMOD_SHIFT));
        bool ma = (bool)(keysym.mod & (KMOD_ALT));
        bool mm = (bool)(keysym.mod & (KMOD_META));
        
        
        /* Ignore if main term is not initialized */
        if (!Term) return;
        
        /* Handle print screen */
        if (key_sym == SDLK_PRINT)
        {
                /* sdl_print_screen();*/
                return;
        }
        
        /* Ignore various keypress, including pure modifier keys */
        if (ignore_key[key_sym]) return;
        
        /*
         * If the keycode is 7-bit ASCII (except numberpad), and ALT and META are not
         * pressed, send it directly to the application.
         */
        if ((key_code) && !(key_code & (0xFF80)) && !is_numpad(key_sym) && !ma && !mm)
        {
                (void)Term_keypress(key_code);
        }
        
        /* Handle all other valid SDL keys */
        else if (key_sym < SDLK_LAST)
        {
                char buf[80];
                int i;
                
                /* Begin the macro trigger */
                (void)Term_keypress(31);
                
                /* Send the modifiers */
                if (mc) Term_keypress('C');
                if (ms) Term_keypress('S');
                if (ma) Term_keypress('A');
                if (mm) Term_keypress('M');
                
                /* Build the SDL key name */
                my_strcpy(buf, format("[%s]", SDL_GetKeyName(key_sym)), 80);
                
                /* Convert and store key name */
                for (i = 0; buf[i]; i++)
                {
                        /* Make lowercase */
                        buf[i] = tolower(buf[i]);
                        
                        /* Replace spaces with underscores */
                        if (buf[i] == ' ') buf[i] = '_';
                        
                        /* Add to the keyqueue */
                        (void)Term_keypress(buf[i]);
                }
        }
}


static void init_windows(void);
static void init_morewindows(void);
/*
 * Handle a single message sent to the application.
 *
 * Functions that are either called from a separate thread or which need to
 * create a separate thread (such as sounds) need to pass messages to this
 * function in order to execute most operations.  See the useage of
 * "SDL_USEREVENT".
 */
static errr sdl_HandleEvent(SDL_Event *event)
{
        
        /* Handle message */
        switch (event->type)
        {
                /* Keypresses */
                case SDL_KEYDOWN:
                {
                        /* Handle keypress */
                        sdl_keypress(event->key.keysym);
                        
                        break;
                }
                case SDL_MOUSEBUTTONDOWN:
                case SDL_MOUSEBUTTONUP:
                {
                        /* Handle mouse stuff */
                        sdl_HandleMouseEvent(event);
                        break;
                }

                case SDL_MOUSEMOTION:
                {
                        int i;  
                        SDL_Event events[10];

                        /*
                         * If there are a bundle of mouse movements pending,
                         * we'll just take every tenth one - this makes a
                         * simple approach to dragging practical, for instance.
                         */  
                        i = SDL_PeepEvents(events, 10, SDL_GETEVENT, SDL_EVENTMASK(SDL_MOUSEMOTION));
                        if (i > 0) 
                        {
                                *event = events[i - 1];
                        }

                        /* Handle mouse stuff */
                        sdl_HandleMouseEvent(event);
                        break;
                }
                        
                /* Shut down the game */
                /* XXX - check for stuck inside menu etc... */
                case SDL_QUIT:
                {
                        /* We are playing a game with an active character */
                        if (character_generated && inkey_flag)
                        {
                                /* Hack -- Forget messages */
                                msg_flag = FALSE;
                                
                                /* Save the game */
                                save_game();
                        }
                        
                        save_prefs();
                        
                        quit(NULL);
                        break;
                }
                        
                /* Resize the application */
                case SDL_VIDEORESIZE:
                {
                        /* Free the surface */
                        SDL_FreeSurface(AppWin);
                        
                        if (!fullscreen)
                        {
                                /* Make sure */
                                vflags &= ~(SDL_FULLSCREEN);
                                vflags |= SDL_RESIZABLE;
                                
                                screen_w = event->resize.w;
                                screen_h = event->resize.h;
                                
                                if (screen_w < 640) screen_w = 640;
                                if (screen_h < 480) screen_h = 480;
                                
                                /* Resize the application surface */
                                AppWin = SDL_SetVideoMode(screen_w, screen_h, 0, vflags);
                        }
                        else
                        {
                                /* Make sure */
                                vflags |= SDL_FULLSCREEN;
                                vflags &= ~(SDL_RESIZABLE);
                                
                                AppWin = SDL_SetVideoMode(full_w, full_h, 0, vflags);
                        }
                        init_morewindows();
                        init_windows();
                        
                        break;
                }
                        
                case WINDOW_DRAW:
                {
                        /* Redraw window that have asked */
                        sdl_Window *window = (sdl_Window*)event->user.data1;
                        sdl_WindowBlit(window);
                        break;
                }
                default:
                {
                        /* Do nothing */
                        break;
                }
        }       
        sdl_WindowUpdate(&StatusBar);
        sdl_WindowUpdate(&PopUp);
        return (0);
}

/*
 * Update the redraw rect
 * A simple but effective way to keep track of what
 * parts of a window need to updated.
 * Any new areas that are updated before a blit are simply combined
 * into a new larger rectangle to encompass all changes.
 */
static void set_update_rect(term_window *win, SDL_Rect *rc)
{
        /* No outstanding update areas yet? */
        if (win->uRect.x == -1)
        {
                /* Simple copy */
                win->uRect = *rc;
        }
        else
        {
                /* Combine the old update area with the new */
                int x = MIN(win->uRect.x, rc->x);
                int y = MIN(win->uRect.y, rc->y);
                int x2 = MAX(win->uRect.x + win->uRect.w, rc->x + rc->w);
                int y2 = MAX(win->uRect.y + win->uRect.h, rc->y + rc->h);
                RECT(x, y, x2 - x, y2 - y, &win->uRect);
        }
}

/*
 * Clear a terminal window
 */
static errr Term_xtra_sdl_clear(void)
{
        term_window *win = (term_window*)(Term->data);
        SDL_Rect rc;
        
        /* Oops */
        if (!win->surface) return (1);
        
        /* Create the fill area */
        RECT(win->border, win->title_height, win->width - (2 * win->border),
                 win->height - win->border - win->title_height, &rc);
        
        
        /* Fill the rectangle */
        SDL_FillRect(win->surface, &rc, back_colour);
        
        /* Rectangle to update */
        set_update_rect(win, &rc);
        
        /* Success */
        return (0);
}

/*
 * Process at least one event
 */
static errr Term_xtra_sdl_event(int v)
{
        SDL_Event event;
        errr error = 0;
        
        /* Wait for an event */
        if (v)
        {
                /* Wait for an event */
                if (SDL_WaitEvent(&event))
                {
                        /* Handle it */
                        error = sdl_HandleEvent(&event);
                }
                else return (1);
        }
        
        /* Check for an event */
        else
        {
                /* Get a single pending event */
                if (SDL_PollEvent(&event))
                {
                        /* Handle it */
                        error = sdl_HandleEvent(&event);
                }
        }
        
        /* Note success or failure */
        return (error);
}

/*
 * Process all pending events
 */
static errr Term_xtra_sdl_flush(void)
{
        SDL_Event event;
        
        /* Get all pending events */
        while (SDL_PollEvent(&event))
        {
                /* Handle them (ignore errors) */
                (void)sdl_HandleEvent(&event);
        }
        
        /* Done */
        return (0);
}

/*
 * Delay for "x" milliseconds
 */
static errr Term_xtra_sdl_delay(int v)
{
        /* Sleep */
        if (v > 0)
        {
                Term_xtra_sdl_event(0);
                SDL_Delay(v);
        }
        
        /* Success */
        return (0);
}

static errr Term_bigcurs_sdl(int col, int row)
{
        term_window *win = (term_window*)(Term->data);
        
        Uint32 colour = text_colours[TERM_YELLOW];
        
        SDL_Rect rc;
        
        /* Make a rectangle */
        RECT(col * win->tile_wid, row * win->tile_hgt, win->tile_wid * 2, win->tile_hgt, &rc);
        
        /* Translate it */
        rc.x += win->border;
        rc.y += win->title_height;
        
        /* Draw it */
        sdl_DrawBox(win->surface, &rc, colour, 1);
        
        /* Update area */
        set_update_rect(win, &rc);
        
        /* Success */
        return (0);
}

static errr Term_curs_sdl(int col, int row)
{
        term_window *win = (term_window*)(Term->data);
        
        Uint32 colour = text_colours[TERM_YELLOW];
        
        SDL_Rect rc;
        
        /* Make a rectangle */
        RECT(col * win->tile_wid, row * win->tile_hgt, win->tile_wid, win->tile_hgt, &rc);
        
        /* Translate it */
        rc.x += win->border;
        rc.y += win->title_height;
        
        /* Draw it */
        sdl_DrawBox(win->surface, &rc, colour, 1);
        
        /* Update area */
        set_update_rect(win, &rc);

        /* Success */
        return (0);
}

static errr Term_xtra_sdl(int n, int v)
{
        switch (n)
        {
                /* Process an event */
                case TERM_XTRA_EVENT:
                {
                        return (Term_xtra_sdl_event(v));
                }
                /* Flush all events */
                case TERM_XTRA_FLUSH:
                {
                        return (Term_xtra_sdl_flush());
                }
                /* Clear the screen */
                case TERM_XTRA_CLEAR:
                {
                        return (Term_xtra_sdl_clear());
                }
                /* Show or hide the cursor */
                case TERM_XTRA_SHAPE:
                {
                        int x, y;
                        
                        /* Obtain the cursor */
                        (void)Term_locate(&x, &y);
                        
                        /* Show or hide the cursor */
                        Term_curs_sdl(x, y);
                        return (0);
                }
                case TERM_XTRA_FRESH:
                {
                        /* Get the current window data */
                        term_window *win = (term_window*)(Term->data);
                        
                        /* Blat it! */
                        sdl_BlitWin(win);
                        
                        /* Done */
                        return (0);
                }
                case TERM_XTRA_DELAY:
                {
                        return (Term_xtra_sdl_delay(v));
                }
                case TERM_XTRA_REACT:
                {
                        int i;
                        /* Re-initialize the colours */
                        for (i = 0; i < MAX_COLORS; i++)
                        {
                                text_colours[i] = SDL_MapRGB(AppWin->format, angband_color_table[i][1],
                                                                                         angband_color_table[i][2],
                                                                                         angband_color_table[i][3]);
                        }
                }
        }
        
        return (1);
}



static errr Term_wipe_sdl(int col, int row, int n)
{
        term_window *win = (term_window*)(Term->data);
        
        SDL_Rect rc;
        
        /*if (use_bigtile) n*=2;*/
        /* Build the area to black out */
        rc.x = col * win->tile_wid;
        rc.y = row * win->tile_hgt;
        rc.w = win->tile_wid * n;
        rc.h = win->tile_hgt;
        
        /* Translate it */
        rc.x += win->border;
        rc.y += win->title_height;
        
        /* Wipe it */
        SDL_FillRect(win->surface, &rc, back_colour);
        
        /* Update */
        set_update_rect(win, &rc);
        
        return (0);
}

/*
 * Draw some text to a window
 */
static errr Term_text_sdl(int col, int row, int n, byte a, cptr s)
{
        term_window *win = (term_window*)(Term->data);
        Uint32 colour = text_colours[a];
        int x = col * win->tile_wid;
        int y = row * win->tile_hgt;
        
        /* Translate */
        x += win->border;
        y += win->title_height;
        
        /* Not much point really... */
        if (!win->visible) return (0);
        
        /* Clear the way */
        Term_wipe_sdl(col, row, n);
        
        /* Draw it */
        return (sdl_FontDraw(&win->font, win->surface, colour, x, y, n, s));
}

#ifdef USE_GRAPHICS
/*
 * Do a 'stretched blit'
 * SDL has no support for stretching... What a bastard!
 * 
 */
static void sdl_StretchBlit(SDL_Surface *src, SDL_Rect *srcRect, SDL_Surface *dest, SDL_Rect *destRect)
{
        int x, y;
        int sx, sy, dx, dy;
        Uint8 *ps, *pd;
        
        for (y = 0; y < destRect->h; y++)
        {
                for (x = 0; x < destRect->w; x++)
                {
                        /* Actual source coords */
                        sx = (srcRect->w * x / (destRect->w)) + srcRect->x;
                        sy = (srcRect->h * y / (destRect->h)) + srcRect->y;
                        
                        /* Find a source pixel */
                        ps = (Uint8 *)src->pixels + (sx * src->format->BytesPerPixel) + (sy * src->pitch);
#if 0
                        /* Do we need to draw it? */
                        switch (src->format->BytesPerPixel)
                        {
                                case 1:
                                {
                                        if (*ps == src->format->colorkey) continue;
                                        break;
                                }
                                case 2:
                                {
                                        Uint16 *ps16 = (Uint16*) ps;
                                        if (*ps16 == src->format->colorkey) continue;
                                        break;
                                }
                                case 3:
                                case 4:
                                {
                                        Uint32 *ps32 = (Uint32*) ps;
                                        if (*ps32 == src->format->colorkey) continue;
                                        break;
                                }
                        }
#endif
                                        
                        /* Actual destination pixel coords */
                        dx = x + destRect->x;
                        dy = y + destRect->y;
                        
                        /* Destination pixel */
                        pd = (Uint8 *)dest->pixels + (dx * dest->format->BytesPerPixel) + (dy * dest->pitch);
                        
                        switch (dest->format->BytesPerPixel)
                        {
                                case 1:
                                {
                                        *pd = *ps;
                                        break;
                                }
                                case 2:
                                {
                                        Uint16 *ps16 = (Uint16*) ps;
                                        Uint16 *pd16 = (Uint16*) pd;
                                        *pd16 = *ps16;
                                        break;
                                }
                                case 3:
                                case 4:
                                {
                                        Uint32 *ps32 = (Uint32*) ps;
                                        Uint32 *pd32 = (Uint32*) pd;
                                        *pd32 = *ps32;
                                }
                        }
                        
                }
        }
        
}

/*
 * Make the 'pre-stretched' tiles for this window
 * Assumes the tiles surface was freed elsewhere
 */
static errr sdl_BuildTileset(term_window *win)
{
        int x, y;
        int ta, td;
        int xx, yy;
        GfxInfo *info = &GfxDesc[use_graphics];
        
        /* Calculate the number of tiles across & down*/
        ta = GfxSurface->w / info->width;
        td = GfxSurface->h / info->height;
        
        /* Calculate the size of the new surface */
        x = ta * win->tile_wid;
        if (use_bigtile) x *= 2;
        y = td * win->tile_hgt;
        
        /* Make it */
        win->tiles = SDL_CreateRGBSurface(SDL_SWSURFACE, x, y,
                                                                          GfxSurface->format->BitsPerPixel,
                                                                          GfxSurface->format->Rmask, GfxSurface->format->Gmask,
                                                                          GfxSurface->format->Bmask, GfxSurface->format->Amask);
        
        /* Bugger */
        if (!win->tiles) return (1);
        
        /* For every tile... */
        for (xx = 0; xx < ta; xx++)
        {
                for (yy = 0; yy < td; yy++)
                {
                        SDL_Rect src, dest;
                        int dwid = (use_bigtile ? win->tile_wid * 2 : win->tile_wid);
                        
                        /* Source rectangle (on GfxSurface) */
                        RECT(xx * info->width, yy * info->height, info->width, info->height, &src);
                        
                        /* Destination rectangle (win->tiles) */
                        RECT(xx * dwid, yy * win->tile_hgt, dwid, win->tile_hgt, &dest);
                        
                        /* Do the stretch thing */
                        sdl_StretchBlit(GfxSurface, &src, win->tiles, &dest);
                }
        }
        
        /* Copy across the colour key */
        SDL_SetColorKey(win->tiles, SDL_SRCCOLORKEY, GfxSurface->format->colorkey);
                                        
        return (0);
}
#endif

/*
 * Put some gfx on the screen
 * XXX - This function _never_ seems to get called with n > 1 ?
 * This needs improvement...
 */
static errr Term_pict_sdl(int col, int row, int n, const byte *ap, const char *cp,
                                                  const byte *tap, const char *tcp)
{
        
#ifdef USE_GRAPHICS
        /* Get the right window */
        term_window *win = (term_window*)(Term->data);
        
        SDL_Rect rc, src;
        int i;
        
        /* First time a pict is requested we load the tileset in */
        if (!win->tiles)
        {
                sdl_BuildTileset(win);
                if (!win->tiles) return (1);
        }
        
        /* Make the destination rectangle */
        RECT(col * win->tile_wid, row * win->tile_hgt, win->tile_wid, win->tile_hgt, &rc);
        
        /* Translate it */
        rc.x += win->border;
        rc.y += win->title_height;
        
        /* Stretch for bigtile mode */
        if (use_bigtile) rc.w *= 2;
        
        /* Get the dimensions of the graphic surface */
        src.w = rc.w;
        src.h = rc.h;
        
        /* Clear the way */
        Term_wipe_sdl(col, row, use_bigtile ? n + 1 : n);
        
        /* Blit 'em! (it) */
        for (i = 0; i < n; i++)
        {
                /* Get the terrain tile */
                src.x = (tcp[i] & 0x7F) * src.w;
                src.y = (tap[i] & 0x7F) * src.h;
                
                SDL_BlitSurface(win->tiles, &src, win->surface, &rc);
                
                /* If foreground is the same as background, we're done */
                if ((tap[i] == ap[i]) && (tcp[i] == cp[i])) continue;
                
                /* Get the foreground tile */
                src.x = (cp[i] & 0x7F) * src.w;
                src.y = (ap[i] & 0x7F) * src.h;
                
                SDL_BlitSurface(win->tiles, &src, win->surface, &rc);
        }
        
        /* Update area */
        set_update_rect(win, &rc);
#endif
        
        return (0);
}

/*
 * Create and initialize the Term contined within this window.
 */
static void term_data_link_sdl(term_window *win)
{
        term *t = &win->term_data;
        
        /* Initialize the term */
        term_init(t, win->cols, win->rows, win->keys);
        
        t->higher_pict = TRUE;
        
        /* Use a "software" cursor */
        t->soft_cursor = TRUE;
        
        /* Erase with "white space" */
        t->attr_blank = TERM_WHITE;
        t->char_blank = ' ';
        
        /* Never refresh one row */
        t->never_frosh = TRUE;
        
        /* Ignore the init/nuke hooks */
        
        /* Prepare the template hooks */
        t->xtra_hook = Term_xtra_sdl;
        t->curs_hook = Term_curs_sdl;
        t->bigcurs_hook = Term_bigcurs_sdl;
        t->wipe_hook = Term_wipe_sdl;
        t->text_hook = Term_text_sdl;
        t->pict_hook = Term_pict_sdl;
        
        /* Remember where we came from */
        t->data = win;
}

/*
 * Initialize the status bar:
 *  Populate it with some buttons
 *  Set the custom draw function for the bar
 */
static void init_morewindows(void)
{
        char buf[128];
        sdl_Button *button;
        int x;

        popped = FALSE;
        
        /* Make sure */
        sdl_WindowFree(&PopUp);
        
        /* Initialize the status bar */
        sdl_WindowInit(&StatusBar, AppWin->w, StatusHeight, AppWin, DEFAULT_FONT_FILE);
        
        /* Cusom drawing function */
        StatusBar.draw_extra = draw_statusbar;
        
        AboutSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, AboutSelect);
        
        my_strcpy(buf, format("%s v%s", VERSION_NAME, VERSION_STRING), sizeof(buf));
        
        /* Initialize the 'about' button */
        sdl_ButtonSize(button, StatusBar.font.width * strlen(buf) + 5, StatusHeight - 2);
        sdl_ButtonMove(button, 1, 1);
        sdl_ButtonVisible(button, TRUE);
        sdl_ButtonCaption(button, buf);
        button->activate = AboutActivate;
        
        
        /* New button */
        TermSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, TermSelect);
        
        /* Initialize the 'term' button */
        sdl_ButtonSize(button, 60, StatusHeight - 2);
        x = 100 + (StatusBar.font.width * 5);
        sdl_ButtonMove(button, x, 1);
        sdl_ButtonVisible(button, TRUE);
        button->activate = TermActivate;
        
        /* Another new button */
        VisibleSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, VisibleSelect);
        
        /* Initialize the 'visible' button */
        sdl_ButtonSize(button, 60,  StatusHeight - 2);
        x = 200 + (StatusBar.font.width * 8);
        sdl_ButtonMove(button, x, 1);
        sdl_ButtonVisible(button, TRUE);
        button->activate = VisibleActivate;
        
        /* Another new button */
        FontSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, FontSelect);
        
        /* Initialize the 'font_select' button */
        sdl_ButtonSize(button, 60,  StatusHeight - 2);
        sdl_ButtonMove(button, 400, 1);
        button->activate = FontActivate;
        
        /* Another new button */
        MoreSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, MoreSelect);
        
        /* Initialize the 'more' button */
        sdl_ButtonSize(button, 60,  StatusHeight - 2);
        sdl_ButtonMove(button, 400, 1);
        sdl_ButtonVisible(button, TRUE);
        sdl_ButtonCaption(button, "Options");
        button->activate = MoreActivate;
        
        /* Another new button */
        QuitSelect = sdl_ButtonBankNew(&StatusBar.buttons);
        button = sdl_ButtonBankGet(&StatusBar.buttons, QuitSelect);
        
        /* Initialize the 'quit' button */
        sdl_ButtonSize(button, 60,  StatusHeight - 2);
        sdl_ButtonMove(button, AppWin->w - 61, 1);
        sdl_ButtonCaption(button, "Quit");
        button->activate = QuitActivate;
        sdl_ButtonVisible(button, TRUE);
        
        SetStatusButtons();
        
        TermFocus(0);
}

#ifdef USE_GRAPHICS

/*
 * The new streamlined graphics loader.
 * Only uses colour keys.
 * Much more tolerant of different bit-planes
 */
static errr load_gfx(void)
{
        char buf[1024];
        cptr filename = GfxDesc[use_graphics].gfxfile;
        SDL_Surface *temp;
        Uint8 r, g, b;
        Uint32 key;
        Uint32 Pixel = 0;
        int x = GfxDesc[use_graphics].x, y = GfxDesc[use_graphics].y;
        
        /* This may be called when GRAPHICS_NONE is set */
        if (!filename) return (0);
        
        /* Free the old surface */
        if (GfxSurface) SDL_FreeSurface(GfxSurface);
        
        /* Find and load the file into a temporary surface */
        path_build(buf, sizeof(buf), ANGBAND_DIR_XTRA_GRAF, filename);
        temp = IMG_Load(buf);
        
        /* Oops */
        if (!temp) return (1);
        
        /* Change the surface type to the current video surface format */
        GfxSurface = SDL_DisplayFormat(temp);
        
        /* Use a colour key */
        /* Get a pixel value depending on bit-depth */
        switch (GfxSurface->format->BytesPerPixel)
        {
                case 1:
                {
                        Uint8 *p = (Uint8*)GfxSurface->pixels + x + (y * GfxSurface->pitch);
                        Pixel = *p;
                }
                case 2:
                {
                        Uint16 *p = (Uint16*)GfxSurface->pixels + (x * 2) + (y * GfxSurface->pitch);
                        Pixel = *p;
                }
                case 3:
                case 4:
                {
                        Uint32 *p = (Uint32*)GfxSurface->pixels + (x * GfxSurface->format->BytesPerPixel) + (y * GfxSurface->pitch);
                        Pixel = *p;
                }
        }
        
        /* Get the colour values */
        SDL_GetRGB(Pixel, GfxSurface->format, &r, &g, &b);
        
        /* Create a key */
        key = SDL_MapRGB(GfxSurface->format, r, g, b);
        
        /* Set the colour key */
        SDL_SetColorKey(GfxSurface, SDL_SRCCOLORKEY, key);
        
        /* Lose the temporary surface */
        SDL_FreeSurface(temp);
        
        /* Make sure we know what pref file to use */
        ANGBAND_GRAF = GfxDesc[use_graphics].pref;
        
        /* Reset the graphics mapping for this tileset */
        if (character_dungeon) reset_visuals(TRUE);
        
        /* All good */
        return (0);
}
#endif

/*
 * Initialize the graphics
 */
static void init_gfx(void)
{
#ifndef USE_GRAPHICS
        /* User requested gfx but USE_GRAPHICS not defined... */
        if (arg_graphics)
        {
                arg_graphics = FALSE;
        }
        
        /* Make sure */
        use_graphics = GRAPHICS_NONE;
        use_bigtile = FALSE;
#else
        GfxInfo *info = &GfxDesc[use_graphics];
        int i;
        
        /* Check for existence of required files */
        for (i = 0; i < GfxModes; i++)
        {
                char path[1024];
                
                /* Check the graphic file */
                if (GfxDesc[i].gfxfile)
                {
                        path_build(path, sizeof(path), ANGBAND_DIR_XTRA_GRAF, GfxDesc[i].gfxfile);

                        if (!file_exists(path))
                        {
                                plog_fmt("Can't find file %s - graphics mode '%s' will be disabled.", path, GfxDesc[i].name);
                                GfxDesc[i].avail = FALSE;
                        }
                }
        }
        
        /* Check availability (default to no graphics) */
        if (!info->avail)
        {
                use_graphics = GRAPHICS_NONE;
                arg_graphics = FALSE;
                use_bigtile = FALSE;
        }
        
        /* Load the graphics stuff in */
        load_gfx();
#endif
}

/*
 * Create the windows
 * Called sometime after load_prefs()
 */
static void init_windows(void)
{
        int i;
        
        for (i = 0; i < ANGBAND_TERM_MAX; i++)
        {
                term_window *win = &windows[i];
                
                /* Only bother with visible windows */
                if (win->visible)
                {
                        /* Don't crowd out the status bar... */
                        if (win->top < StatusHeight) win->top = StatusHeight;
#ifdef USE_GRAPHICS                     
                        /* Invalidate the gfx surface */
                        if (win->tiles)
                        {
                                SDL_FreeSurface(win->tiles);
                                win->tiles = NULL;
                        }
#endif
                        /* This will set up the window correctly */
                        ResizeWin(win, win->width, win->height);
                }
                else
                {
                        /* Doesn't exist */
                        angband_term[i] = NULL;
                }
                
                /* Term 0 is at the top */
                Zorder[i] = ANGBAND_TERM_MAX - i - 1;
        }
        
        /* Good to go... */
        Term_activate(term_screen);
}

/*
 * Set up some SDL stuff
 *
 */
static void init_sdl_local(void)
{
        const SDL_VideoInfo *VideoInfo;
        
        int i;
        int h, w;
        
        /* Get information about the video hardware */
        VideoInfo = SDL_GetVideoInfo();
        
        /* Require at least 256 colors */
        if (VideoInfo->vfmt->BitsPerPixel < 8) quit(format("This %s port requires lots of colors.", VERSION_NAME));
        
        full_w = VideoInfo->current_w;
        full_h = VideoInfo->current_h;
        
        /* Use a software surface - A tad inefficient, but stable... */
        vflags |= SDL_SWSURFACE;
        
        /* Set fullscreen flag */
        if (fullscreen) vflags |= SDL_FULLSCREEN;
        
        /* otherwise we make this surface resizable */
        else vflags |= SDL_RESIZABLE;
        
        /* Create the main window */
        AppWin = SDL_SetVideoMode(fullscreen ? full_w : screen_w,fullscreen ? full_h : screen_h, 0, vflags);
        
        /* Handle failure */
        if (!AppWin) quit(format("Failed to create %dx%d window at %d bpp!",
                                                         screen_w, screen_h, VideoInfo->vfmt->BitsPerPixel));
        
        /* Set the window caption */
        SDL_WM_SetCaption(VERSION_NAME, NULL);
        
        /* Initialize the ignored keypresses */
        for (i = 0; ignore_key_list[i]; i++)
        {
                ignore_key[ignore_key_list[i]] = TRUE;
        }
        
        /* Enable key repeating; use defaults */
        (void)SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
        
        /* Enable Unicode (so we can read key codes) */
        (void)SDL_EnableUNICODE(1);
        
        /* Build a color for "black" that matches the pixel depth of this surface */
        back_colour = SDL_MapRGB(AppWin->format, angband_color_table[TERM_DARK][1],
                                                   angband_color_table[TERM_DARK][2],
                                                   angband_color_table[TERM_DARK][3]);
        
        
        
        /* Initialize the colours */
        for (i = 0; i < MAX_COLORS; i++)
        {
                text_colours[i] = SDL_MapRGB(AppWin->format, angband_color_table[i][1],
                                                                         angband_color_table[i][2],
                                                                         angband_color_table[i][3]);
        }
        
        /* Get the height of the status bar */
        sdl_CheckFont(DEFAULT_FONT_FILE, &w, &h);
        StatusHeight = h + 3;
        
        /* Font used for window titles */
        sdl_FontCreate(&SystemFont, DEFAULT_FONT_FILE, AppWin); 
}

static void init_paths(void)
{
        int i;
        char path[1024];
        char buf[1024];
        ang_dir *dir;
        
        /* Build the gfx path */
        path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "graf");
        ANGBAND_DIR_XTRA_GRAF = string_make(path);
        
        /* Build the "font" path */
        path_build(path, sizeof(path), ANGBAND_DIR_XTRA, "font");
        ANGBAND_DIR_XTRA_FONT = string_make(path);
        
        /* Build the filename */
        path_build(path, sizeof(path), ANGBAND_DIR_XTRA_FONT, DEFAULT_FONT_FILE);
        
        /* Hack -- Validate the basic font */
        validate_file(path);
        
        for (i = 0; i < MAX_FONTS; i++)
                FontList[i] = NULL;


        /** Scan for fonts **/

        /* Open the directory */
        dir = my_dopen(ANGBAND_DIR_XTRA_FONT);
        if (!dir) return;

        /* Read every font to the limit */
        while (my_dread(dir, buf, sizeof buf))
        {
                /* Check for file extension */
                if (suffix(buf, ".fon"))
                        FontList[num_fonts++] = string_make(buf);

                /* Don't grow to long */
                if (num_fonts == MAX_FONTS) break;
        }

        /* Done */
        my_dclose(dir);
}


const char help_sdl[] = "SDL frontend";
/*
 * The SDL port's "main()" function.
 */
int init_sdl(int argc, char *argv[])
{
        /* Initialize SDL:  Timer, video, and audio functions */
        if (SDL_Init(SDL_INIT_VIDEO) < 0)
        {
                fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
                return (2);
        }
        
        /* Initialize the TTF library */
        if (TTF_Init() < 0)
        {
                fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError());
                SDL_Quit();
                return (2);
        }

        /* Init some extra paths */
        init_paths();
        
        /* Load prefs */
        load_prefs();
        
        /* Get sdl going */
        init_sdl_local();
        
        /* Prepare the windows */
        init_windows();
        
        /* Prepare the gfx */
        init_gfx();
        
        /* Prepare some more windows(!) */
        init_morewindows();
        
        /* Activate  quit hook */
        quit_aux = hook_quit;

        /* Paranoia */
        return (0);
}

#endif /* USE_SDL */
