Three source patches the modern clang/C++ frontend requires: - graphics/window.cpp: strchr on const char* now returns const char* - filemap.cpp, prefs.cpp: C++11 forbids no-space concatenation of a string literal with an adjacent macro identifier Autotools state regenerated with autoreconf -fi against current autoconf/automake so configure can be run on macOS 26 / Apple Silicon. Added run-kobodl.sh to launch the binary from the source tree (the compiled-in data paths assume a .app bundle), and a .gitignore for build artifacts. Out-of-tree dependency: SDL_image 1.2 must be built with --disable-imageio. Apple's ImageIO backend in IMG_ImageIO.m yields SDL_Surfaces with all-zero pixel data on current macOS, leaving every sprite invisible; libpng decodes correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
655 lines
12 KiB
C++
655 lines
12 KiB
C++
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
window.cpp - Generic Rendering Window
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2001-2003, 2006, 2007 David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "logger.h"
|
|
#include "window.h"
|
|
#include "gfxengine.h"
|
|
#include "sofont.h"
|
|
|
|
#define SELECT if(selected != this) _select();
|
|
|
|
window_t *window_t::selected = NULL;
|
|
|
|
window_t::window_t()
|
|
{
|
|
engine = NULL;
|
|
surface = NULL;
|
|
next = NULL;
|
|
prev = NULL;
|
|
phys_rect.x = phys_rect.y = 0;
|
|
phys_rect.w = 320;
|
|
phys_rect.h = 240;
|
|
fgcolor = bgcolor = 0;
|
|
selected = 0;
|
|
xs = ys = 256;
|
|
bg_bank = -1;
|
|
bg_frame = -1;
|
|
_visible = 1;
|
|
_offscreen = 0;
|
|
}
|
|
|
|
|
|
window_t::~window_t()
|
|
{
|
|
if(_offscreen && surface)
|
|
SDL_FreeSurface(surface);
|
|
if(selected == this)
|
|
selected = NULL;
|
|
unlink();
|
|
}
|
|
|
|
|
|
void window_t::init(gfxengine_t *e)
|
|
{
|
|
link(e);
|
|
xs = engine->xs;
|
|
ys = engine->ys;
|
|
if(_offscreen && surface)
|
|
{
|
|
SDL_FreeSurface(surface);
|
|
surface = NULL;
|
|
}
|
|
_offscreen = 0;
|
|
surface = engine->surface();
|
|
}
|
|
|
|
|
|
void window_t::visible(int vis)
|
|
{
|
|
if(_offscreen)
|
|
return; // Cannot be visible!
|
|
if(_visible == vis)
|
|
return;
|
|
_visible = vis;
|
|
invalidate();
|
|
}
|
|
|
|
|
|
void window_t::link(gfxengine_t *e)
|
|
{
|
|
unlink();
|
|
engine = e;
|
|
next = NULL;
|
|
prev = engine->windows;
|
|
if(prev)
|
|
{
|
|
while(prev->next)
|
|
prev = prev->next;
|
|
prev->next = this;
|
|
}
|
|
else
|
|
engine->windows = this;
|
|
}
|
|
|
|
|
|
void window_t::unlink(void)
|
|
{
|
|
if(engine)
|
|
{
|
|
if(engine->windows == this)
|
|
engine->windows = next;
|
|
if(engine->surface() == surface)
|
|
surface = NULL;
|
|
}
|
|
if(next)
|
|
next->prev = prev;
|
|
if(prev)
|
|
prev->next = next;
|
|
}
|
|
|
|
|
|
void window_t::place(int left, int top, int sizex, int sizey)
|
|
{
|
|
int x2 = ((left + sizex) * xs + 128) >> 8;
|
|
int y2 = ((top + sizey) * ys + 128) >> 8;
|
|
phys_rect.x = (left * xs + 128) >> 8;
|
|
phys_rect.y = (top * ys + 128) >> 8;
|
|
phys_rect.w = x2 - phys_rect.x;
|
|
phys_rect.h = y2 - phys_rect.y;
|
|
}
|
|
|
|
|
|
int window_t::offscreen()
|
|
{
|
|
if(!engine)
|
|
return -1;
|
|
if(!engine->surface())
|
|
return -1;
|
|
if(_offscreen)
|
|
return 0; // Already offscreen!
|
|
visible(0);
|
|
_offscreen = 1;
|
|
SDL_Surface *s = SDL_CreateRGBSurface(SDL_SWSURFACE,
|
|
phys_rect.w, phys_rect.h,
|
|
32, 0xff000000, 0x00ff0000,
|
|
0x0000ff00, 0x000000ff);
|
|
if(!s)
|
|
return -1;
|
|
surface = SDL_DisplayFormat(s);
|
|
SDL_FreeSurface(s);
|
|
if(!surface)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void window_t::select()
|
|
{
|
|
if(engine)
|
|
_select();
|
|
}
|
|
|
|
|
|
void window_t::_select()
|
|
{
|
|
SDL_Rect r = phys_rect;
|
|
selected = this;
|
|
if(surface)
|
|
SDL_SetClipRect(surface, &r);
|
|
}
|
|
|
|
|
|
void window_t::invalidate(SDL_Rect *r)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!engine->surface())
|
|
return;
|
|
|
|
SELECT
|
|
|
|
if(_offscreen)
|
|
{
|
|
if(!r)
|
|
{
|
|
SDL_Rect rr;
|
|
rr.x = 0;
|
|
rr.y = 0;
|
|
rr.w = phys_rect.w;
|
|
rr.h = phys_rect.h;
|
|
phys_refresh(&rr);
|
|
glSDL_Invalidate(surface, &rr);
|
|
}
|
|
else
|
|
{
|
|
phys_refresh(r);
|
|
glSDL_Invalidate(surface, r);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if(!r)
|
|
engine->invalidate(&phys_rect, this);
|
|
else
|
|
{
|
|
/* Translate to screen coordinates */
|
|
SDL_Rect dr = *r;
|
|
dr.x = (dr.x * xs + 128) >> 8;
|
|
dr.y = (dr.y * ys + 128) >> 8;
|
|
dr.w = (((dr.w + dr.x) * xs + 128) >> 8) - dr.x;
|
|
dr.h = (((dr.h + dr.y) * ys + 128) >> 8) - dr.y;
|
|
dr.x += phys_rect.x;
|
|
dr.y += phys_rect.y;
|
|
|
|
/* Clip to window (stolen from SDL_surface.c) */
|
|
int Amin, Amax, Bmin, Bmax;
|
|
|
|
/* Horizontal intersection */
|
|
Amin = dr.x;
|
|
Amax = Amin + dr.w;
|
|
Bmin = phys_rect.x;
|
|
Bmax = Bmin + phys_rect.w;
|
|
if(Bmin > Amin)
|
|
Amin = Bmin;
|
|
dr.x = Amin;
|
|
if(Bmax < Amax)
|
|
Amax = Bmax;
|
|
dr.w = Amax - Amin > 0 ? Amax - Amin : 0;
|
|
|
|
/* Vertical intersection */
|
|
Amin = dr.y;
|
|
Amax = Amin + dr.h;
|
|
Bmin = phys_rect.y;
|
|
Bmax = Bmin + phys_rect.h;
|
|
if(Bmin > Amin)
|
|
Amin = Bmin;
|
|
dr.y = Amin;
|
|
if(Bmax < Amax)
|
|
Amax = Bmax;
|
|
dr.h = Amax - Amin > 0 ? Amax - Amin : 0;
|
|
|
|
if(dr.w && dr.h)
|
|
engine->invalidate(&dr, this);
|
|
}
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------
|
|
Rendering API
|
|
---------------------------------------------------------------*/
|
|
|
|
Uint32 window_t::map_rgb(Uint8 r, Uint8 g, Uint8 b)
|
|
{
|
|
if(!engine)
|
|
return 0;
|
|
|
|
if(surface)
|
|
return SDL_MapRGB(surface->format, r, g, b);
|
|
else
|
|
return 0xffffff;
|
|
}
|
|
|
|
Uint32 window_t::map_rgb(Uint32 rgb)
|
|
{
|
|
if(!engine)
|
|
return 0;
|
|
|
|
Uint8 r = (rgb >> 16) & 0xff;
|
|
Uint8 g = (rgb >> 8) & 0xff;
|
|
Uint8 b = rgb & 0xff;
|
|
|
|
if(surface)
|
|
return SDL_MapRGB(surface->format, r, g, b);
|
|
else
|
|
return 0xffffff;
|
|
}
|
|
|
|
void window_t::bgimage(int bank, int frame)
|
|
{
|
|
bg_bank = bank;
|
|
bg_frame = frame;
|
|
}
|
|
|
|
|
|
void window_t::colorkey(Uint32 color)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!engine->surface())
|
|
return;
|
|
if(!_offscreen)
|
|
return;
|
|
SDL_SetColorKey(surface, SDL_SRCCOLORKEY, color);
|
|
}
|
|
|
|
void window_t::colorkey()
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!engine->surface())
|
|
return;
|
|
if(!_offscreen)
|
|
return;
|
|
SDL_SetColorKey(surface, 0, 0);
|
|
}
|
|
|
|
void window_t::alpha(float a)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!engine->surface())
|
|
return;
|
|
if(!_offscreen)
|
|
return;
|
|
SDL_SetAlpha(surface, SDL_SRCALPHA, (int)(a * 255.0));
|
|
}
|
|
|
|
|
|
void window_t::font(int fnt)
|
|
{
|
|
_font = fnt;
|
|
}
|
|
|
|
|
|
void window_t::string(int _x, int _y, const char *txt)
|
|
{
|
|
string_fxp(PIXEL2CS(_x), PIXEL2CS(_y), txt);
|
|
}
|
|
|
|
|
|
void window_t::center(int _y, const char *txt)
|
|
{
|
|
center_fxp(PIXEL2CS(_y), txt);
|
|
}
|
|
|
|
|
|
void window_t::center_token(int _x, int _y, const char *txt, char token)
|
|
{
|
|
center_token_fxp(PIXEL2CS(_x), PIXEL2CS(_y), txt, token);
|
|
}
|
|
|
|
|
|
void window_t::string_fxp(int _x, int _y, const char *txt)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
|
|
_x = CS2PIXEL((_x * xs + 128) >> 8);
|
|
_y = CS2PIXEL((_y * ys + 128) >> 8);
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return;
|
|
SELECT
|
|
_x += phys_rect.x;
|
|
_y += phys_rect.y;
|
|
if(surface)
|
|
f->PutString(surface, _x, _y, txt);
|
|
}
|
|
|
|
|
|
void window_t::center_fxp(int _y, const char *txt)
|
|
{
|
|
_y = CS2PIXEL((_y * ys + 128) >> 8);
|
|
|
|
if(!engine)
|
|
return;
|
|
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return;
|
|
SELECT
|
|
int _x = (phys_rect.w - f->TextWidth(txt) + 1) / 2;
|
|
_x += phys_rect.x;
|
|
_y += phys_rect.y;
|
|
if(surface)
|
|
f->PutString(surface, _x, _y, txt);
|
|
}
|
|
|
|
|
|
void window_t::center_token_fxp(int _x, int _y, const char *txt, char token)
|
|
{
|
|
_x = CS2PIXEL((_x * xs + 128) >> 8);
|
|
_y = CS2PIXEL((_y * ys + 128) >> 8);
|
|
|
|
if(!engine)
|
|
return;
|
|
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return;
|
|
SELECT
|
|
int _cx;
|
|
if(-1 == token)
|
|
_cx = _x - f->TextWidth(txt)/2;
|
|
else
|
|
{
|
|
int tokpos;
|
|
/*
|
|
* My docs won't say if strchr(???, 0) is legal
|
|
* or even defined, so I'm not taking any chances...
|
|
*/
|
|
if(token)
|
|
{
|
|
const char *tok = strchr(txt, token);
|
|
if(tok)
|
|
tokpos = tok-txt;
|
|
else
|
|
tokpos = 255;
|
|
}
|
|
else
|
|
tokpos = 255;
|
|
_cx = _x - f->TextWidth(txt, 0, tokpos);
|
|
}
|
|
_cx += phys_rect.x;
|
|
_y += phys_rect.y;
|
|
if(surface)
|
|
f->PutString(surface, _cx, _y, txt);
|
|
}
|
|
|
|
|
|
int window_t::textwidth_fxp(const char *txt, int min, int max)
|
|
{
|
|
if(!engine)
|
|
return strlen(txt);
|
|
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return strlen(txt);
|
|
|
|
return (f->TextWidth(txt, min, max) << 16) / xs;
|
|
}
|
|
|
|
|
|
int window_t::textwidth(const char *txt, int min, int max)
|
|
{
|
|
if(!engine)
|
|
return strlen(txt);
|
|
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return strlen(txt);
|
|
|
|
return (f->TextWidth(txt, min, max) << 8) / xs;
|
|
}
|
|
|
|
|
|
int window_t::fontheight()
|
|
{
|
|
if(!engine)
|
|
return 1;
|
|
|
|
SoFont *f = engine->get_font(_font);
|
|
if(!f)
|
|
return 1;
|
|
|
|
return (f->FontHeight() * 256) / ys;
|
|
}
|
|
|
|
|
|
void window_t::clear(SDL_Rect *r)
|
|
{
|
|
SDL_Rect sr, dr;
|
|
if(!engine)
|
|
return;
|
|
if(!surface)
|
|
return;
|
|
SELECT
|
|
if(!r)
|
|
{
|
|
sr = phys_rect;
|
|
sr.x = 0;
|
|
sr.y = 0;
|
|
dr = phys_rect;
|
|
}
|
|
else
|
|
{
|
|
sr.x = ((int)r->x * xs + 128) >> 8;
|
|
sr.y = ((int)r->y * ys + 128) >> 8;
|
|
sr.w = (((int)(r->x + r->w) * xs + 128) >> 8) - sr.x;
|
|
sr.h = (((int)(r->y + r->h) * ys + 128) >> 8) - sr.y;
|
|
dr = sr;
|
|
dr.x += phys_rect.x;
|
|
dr.y += phys_rect.y;
|
|
}
|
|
if((-1 == bg_bank) && (-1 == bg_frame))
|
|
SDL_FillRect(surface, &dr, bgcolor);
|
|
else
|
|
{
|
|
s_sprite_t *s = engine->get_sprite(bg_bank, bg_frame);
|
|
if(!s || !s->surface)
|
|
{
|
|
SDL_FillRect(surface, &dr, bgcolor);
|
|
return;
|
|
}
|
|
SDL_BlitSurface(s->surface, &sr, surface, &dr);
|
|
}
|
|
}
|
|
|
|
|
|
void window_t::point(int _x, int _y)
|
|
{
|
|
int x2 = ((_x + 1) * xs + 128) >> 8;
|
|
int y2 = ((_y + 1) * ys + 128) >> 8;
|
|
_x = (_x * xs + 128) >> 8;
|
|
_y = (_y * ys + 128) >> 8;
|
|
|
|
if(!engine)
|
|
return;
|
|
SELECT
|
|
/* Quick hack; slow */
|
|
SDL_Rect r;
|
|
r.x = phys_rect.x + _x;
|
|
r.y = phys_rect.y + _y;
|
|
r.w = x2 - _x;
|
|
r.h = y2 - _y;
|
|
if(surface)
|
|
SDL_FillRect(surface, &r, fgcolor);
|
|
}
|
|
|
|
|
|
void window_t::fillrect(int _x, int _y, int w, int h)
|
|
{
|
|
int x2 = ((_x + w) * xs + 128) >> 8;
|
|
int y2 = ((_y + h) * ys + 128) >> 8;
|
|
_x = (_x * xs + 128) >> 8;
|
|
_y = (_y * ys + 128) >> 8;
|
|
|
|
if(!engine)
|
|
return;
|
|
SELECT
|
|
SDL_Rect r;
|
|
r.x = phys_rect.x + _x;
|
|
r.y = phys_rect.y + _y;
|
|
r.w = x2 - _x;
|
|
r.h = y2 - _y;
|
|
if(surface)
|
|
SDL_FillRect(surface, &r, fgcolor);
|
|
}
|
|
|
|
|
|
void window_t::rectangle(int _x, int _y, int w, int h)
|
|
{
|
|
fillrect(_x, _y, w, 1);
|
|
fillrect(_x, _y + h - 1, w, 1);
|
|
fillrect(_x, _y + 1, 1, h - 2);
|
|
fillrect(_x + w - 1, _y + 1, 1, h - 2);
|
|
}
|
|
|
|
|
|
void window_t::fillrect_fxp(int _x, int _y, int w, int h)
|
|
{
|
|
int xx = CS2PIXEL((_x * xs + 128) >> 8);
|
|
int yy = CS2PIXEL((_y * ys + 128) >> 8);
|
|
w = CS2PIXEL(((w + _x) * xs + 128) >> 8) - xx;
|
|
h = CS2PIXEL(((h + _y) * ys + 128) >> 8) - yy;
|
|
|
|
if(!engine)
|
|
return;
|
|
SELECT
|
|
SDL_Rect r;
|
|
r.x = phys_rect.x + xx;
|
|
r.y = phys_rect.y + yy;
|
|
r.w = w;
|
|
r.h = h;
|
|
if(surface)
|
|
SDL_FillRect(surface, &r, fgcolor);
|
|
}
|
|
|
|
|
|
void window_t::sprite(int _x, int _y, int bank, int frame, int inval)
|
|
{
|
|
sprite_fxp(PIXEL2CS(_x), PIXEL2CS(_y), bank, frame, inval);
|
|
}
|
|
|
|
|
|
void window_t::sprite_fxp(int _x, int _y, int bank, int frame, int inval)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
s_sprite_t *s = engine->get_sprite(bank, frame);
|
|
if(!s || !s->surface)
|
|
return;
|
|
_x = CS2PIXEL(((_x - (s->x << 8)) * xs + 128) >> 8);
|
|
_y = CS2PIXEL(((_y - (s->y << 8)) * ys + 128) >> 8);
|
|
SDL_Rect dest_rect;
|
|
|
|
SELECT
|
|
dest_rect.x = phys_rect.x + _x;
|
|
dest_rect.y = phys_rect.y + _y;
|
|
if(surface)
|
|
SDL_BlitSurface(s->surface, NULL, surface, &dest_rect);
|
|
|
|
if(inval && !engine->autoinvalidate())
|
|
{
|
|
dest_rect.w = s->surface->w;
|
|
dest_rect.h = s->surface->h;
|
|
engine->invalidate(&dest_rect, this);
|
|
}
|
|
}
|
|
|
|
|
|
void window_t::blit(int dx, int dy,
|
|
int sx, int sy, int sw, int sh, window_t *src)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!src)
|
|
return;
|
|
if(!surface)
|
|
return;
|
|
if(!src->surface)
|
|
return;
|
|
SELECT
|
|
|
|
SDL_Rect src_rect;
|
|
int sx2 = ((sx + sw) * xs + 128) >> 8;
|
|
int sy2 = ((sy + sh) * ys + 128) >> 8;
|
|
src_rect.x = (sx * xs + 128) >> 8;
|
|
src_rect.y = (sy * ys + 128) >> 8;
|
|
src_rect.w = sx2 - src_rect.x;
|
|
src_rect.h = sy2 - src_rect.y;
|
|
|
|
SDL_Rect dest_rect;
|
|
dest_rect.x = phys_rect.x + ((dx * xs + 128) >> 8);
|
|
dest_rect.y = phys_rect.y + ((dy * ys + 128) >> 8);
|
|
|
|
SDL_BlitSurface(src->surface, &src_rect, surface, &dest_rect);
|
|
}
|
|
|
|
|
|
void window_t::blit(int dx, int dy, window_t *src)
|
|
{
|
|
if(!engine)
|
|
return;
|
|
if(!src)
|
|
return;
|
|
if(!surface)
|
|
return;
|
|
if(!src->surface)
|
|
return;
|
|
|
|
SELECT
|
|
dx = (dx * xs + 128) >> 8;
|
|
dy = (dy * ys + 128) >> 8;
|
|
|
|
SDL_Rect src_rect;
|
|
src_rect.x = 0;
|
|
src_rect.y = 0;
|
|
src_rect.w = src->surface->w;
|
|
src_rect.h = src->surface->h;
|
|
|
|
SDL_Rect dest_rect;
|
|
dest_rect.x = phys_rect.x + dx;
|
|
dest_rect.y = phys_rect.y + dy;
|
|
|
|
SDL_BlitSurface(src->surface, &src_rect, surface, &dest_rect);
|
|
}
|