Skip to content

Commit

Permalink
SDL 2 support (without removing SDL 1)
Browse files Browse the repository at this point in the history
Second binary, which is actually partially because I plan to stick a
menu on this thing for HyperPixel touchscreen purposes.

Slightly ugly SDLVERSION hacks, but mostly the two are compatible. The
big changes are contained to the separate main binary, needing to do
different graphics setup.

render() must now use the provided, not remembered, framebuffer, and
report if it wants to update the screen, which the main binary takes
care of (flip for SDL1, more complicated dance for SDL2).

Some changes around palette handling. Also surface creation---SDL2 is
more pedantic about complaining if you hand it a pixel format bitfield
at the same time as saying "paletted, please".

VSCode noise.

Flag which hacks are working or misbehaving weirdly under SDL2.

Yes, this all needs a pile of refactoring, but as always time is short.
  • Loading branch information
LionsPhil committed Dec 16, 2023
1 parent 062b45d commit 2b499d3
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 43 deletions.
7 changes: 5 additions & 2 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
"${workspaceFolder}/**",
"/usr/include/SDL"
],
"defines": ["DESKTOP"],
"defines": [
"DESKTOP"
],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++14",
"intelliSenseMode": "clang-x64"
"intelliSenseMode": "${default}",
"configurationProvider": "ms-vscode.makefile-tools"
}
],
"version": 4
Expand Down
25 changes: 19 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ CPPFLAGSEX = $(CFLAGSEX)
#CPPFLAGSEX = $(CFLAGSEX)
# LDFLAGSEX =

ifndef SDLVERSION
SDLVERSION=2
endif

# SOURCES ---------------------------------------------------------------------
CPPSOURCES = pixmas.cpp \
snowfp.cpp snowint.cpp snowclock.cpp popclock.cpp colorcycle.cpp
# Main changes based on SDL version so is inferred below.
CPPSOURCES = snowfp.cpp snowint.cpp snowclock.cpp popclock.cpp colorcycle.cpp
HEADERS = hack.hpp
# Anything else you want put in the distributed version
EXTRADIST = Makefile README.md
Expand All @@ -42,10 +46,18 @@ CPPSOURCES = pixmas.cpp \
OBJECTS = $(CPPSOURCES:%.cpp=%.o)
NOTOBJECTS = $(filter-out %.o, $(OBJECTS))
ifneq ($(NOTOBJECTS),)
$(error OBJECTS contains non-object(s) $(NOTOBJECTS))
$(error OBJECTS contains non-object(s) $(NOTOBJECTS))
endif
SOURCES = $(CPPSOURCES)

ifeq ($(SDLVERSION),1)
SDLCONFIG = sdl-config
CPPSOURCES += pixmas.cpp
else
SDLCONFIG = sdl2-config
CPPSOURCES += pixmas2.cpp
endif

# All files which are sources, /including/ non-compiled ones (e.g. headers)
ALLSOURCESMANU = $(SOURCES) $(HEADERS)

Expand All @@ -57,14 +69,14 @@ CPPWFLAGS = $(WARNFLAGS)
# Tool flags
# Don't make CXXFLAGS include CFLAGS or it'll get duplicate CFLAGSEX
CPPFLAGS = $(CPPWFLAGS) -std=c++14 -pedantic -DVERSION='"$(VERSION)"' \
`sdl-config --cflags` $(CPPFLAGSEX)
LDFLAGS = `sdl-config --libs` -lm $(LDFLAGSEX)
`$(SDLCONFIG) --cflags` -DSDLVERSION='$(SDLVERSION)' $(CPPFLAGSEX)
LDFLAGS = `$(SDLCONFIG) --libs` -lm $(LDFLAGSEX)

EXTRACDEPS = Makefile $(HEADERS)

# Kinda sloppy autodetection that we should fake the display resolution.
ifeq ($(shell uname -m),x86_64)
CPPFLAGS += -DDESKTOP
CPPFLAGS += -DDESKTOP
endif

# MAKEFILE METADATA AND MISCELLANY --------------------------------------------
Expand Down Expand Up @@ -127,6 +139,7 @@ work: $(SOURCES) $(HEADERS)

# Build environment information
env:
@$(ECHO) "SDL config: : $(SDLCONFIG)"
@$(ECHO) "C++ compiler : $(CPPC)"
@$(ECHO) "Linker : $(LD)"
@$(ECHO) "C++ compile flags: $(CPPFLAGS)"
Expand Down
8 changes: 4 additions & 4 deletions src/colorcycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
#include <stdexcept>
#include <vector>

#include <SDL.h>

#include "hack.hpp"

namespace Hack {
Expand Down Expand Up @@ -53,7 +51,9 @@ struct ColorCycle : public Hack::Base {
}*/
}

void render() override {
bool render(SDL_Surface* framebuffer) override {
fb = framebuffer;

/*if(black) {
SDL_FillRect(fb, nullptr,
SDL_MapRGB(fb->format, 0, 0, 0));
Expand All @@ -78,7 +78,7 @@ struct ColorCycle : public Hack::Base {
b = /*b ? 0 :*/ 255;
SDL_FillRect(fb, nullptr,
SDL_MapRGB(fb->format, r, g, b));
SDL_Flip(fb);
return true;
}

Uint32 tick_duration() override { return 33; } // 30Hz
Expand Down
26 changes: 24 additions & 2 deletions src/hack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,32 @@

#include <memory>

#include <SDL.h>
// This is a bit wrong, given sdl-config output, but, makes VSCode happy? :/
#ifndef SDLVERSION
#error SDLVERSION not set, but the Makefile should have?
#endif
#if SDLVERSION == 1
#include <SDL/SDL.h>
#else
#include <SDL2/SDL.h>
// Dirty compat hacks; this flag is gone.
#define SDL_ASYNCBLIT 0
// Dirty compat hacks; this is a slightly gratuitous API change.
// https://stackoverflow.com/questions/29609544/how-to-use-palettes-in-sdl-2
inline int SDL_SetColors(SDL_Surface *surface, SDL_Color *colors,
int firstcolor, int ncolors) {
// Note change from true-success/false-fail to 0-success/negative-fail.
return SDL_SetPaletteColors(surface->format->palette, colors,
firstcolor, ncolors) == 0;
}
#endif

namespace Hack {
struct Base {
virtual ~Base() {}
virtual void simulate() = 0;
virtual void render() = 0;
// Return true if screen should be updated.
virtual bool render(SDL_Surface* framebuffer) = 0;
virtual Uint32 tick_duration() = 0;
};

Expand All @@ -24,6 +43,9 @@ namespace Hack {
* for the 100th time.They're not even runtime switchable at the
* moment anyway. */

/* Constructing with a framebuffer is old hackery; render() must use the
* one provided, not one saved by the c'tor. */

std::unique_ptr<Hack::Base> MakeSnowFP(SDL_Surface* framebuffer);
std::unique_ptr<Hack::Base> MakeSnowInt(SDL_Surface* framebuffer);
std::unique_ptr<Hack::Base> MakeSnowClock(SDL_Surface* framebuffer);
Expand Down
6 changes: 3 additions & 3 deletions src/pixmas.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include <iostream>
#include <memory>

#include <SDL.h>

// Force VSCode to know this is going to get the version 1 define here.
#define SDLVERSION 1
#include "hack.hpp"

// This is just used to get SDL init/deinit via RAII for nice error handling
Expand Down Expand Up @@ -99,7 +99,7 @@ int main(int argc, char** argv) {
tickerror -= hack->tick_duration();
hack->simulate();
} while(tickerror >= hack->tick_duration());
hack->render();
if(hack->render(fb)) { SDL_Flip(fb); }
} else {
/// Have a nap until we actually have at least one tick to run.
SDL_Delay(hack->tick_duration());
Expand Down
145 changes: 145 additions & 0 deletions src/pixmas2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) 2020 Philip Boulain; see LICENSE for terms.
#include <exception>
#include <iostream>
#include <memory>

// Force VSCode to know this is going to get the version 2 define here.
#define SDLVERSION 2
#include "hack.hpp"

// This is just used to get SDL init/deinit via RAII for nice error handling.
namespace SDL {
struct Error : public std::exception {
virtual const char* what() const throw()
{ return SDL_GetError(); }
};

struct Graphics {
SDL_Window* window;
SDL_Renderer *renderer;
SDL_Texture* texture;
int w, h;

Graphics() {
if(SDL_Init(SDL_INIT_VIDEO) != 0) { throw Error(); }
if(SDL_CreateWindowAndRenderer(
#ifdef DESKTOP
// Resolution of the Pimoroni HyperPixel.
800, 480,
0,
#else
// Counterintuitively, the non-"desktop" behaviour is fullscreen.
0, 0,
SDL_WINDOW_FULLSCREEN_DESKTOP,
#endif
&window, &renderer) != 0 ) { throw Error(); }
SDL_SetWindowTitle(window, "pixmas");
#ifndef DESKTOP
SDL_SetWindowAlwaysOnTop(window, SDL_TRUE);
#endif
if(SDL_GetRendererOutputSize(renderer, &w, &h) != 0)
{ throw Error(); }
texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_ARGB8888,
SDL_TEXTUREACCESS_STREAMING,
w, h);
}
~Graphics() {
// Let SDL free all its own things.
SDL_Quit();
}
};
};

int main(int argc, char** argv) {
SDL::Graphics graphics;
#ifndef DESKTOP
SDL_ShowCursor(0);
#endif
SDL_SetRenderDrawColor(graphics.renderer, 0x77, 0x77, 0x77, 0xff);
SDL_RenderClear(graphics.renderer);
SDL_RenderPresent(graphics.renderer);

SDL_Surface* temp_stale_fb;
temp_stale_fb = SDL_CreateRGBSurface(0, graphics.w, graphics.h, 32,
0x00FF0000,
0x0000FF00,
0x000000FF,
0xFF000000);

// Pick one of the factory functions from hack.hpp here.
// TODO: Allow picking at startup or runtime.
// BROKEN: Segfaults, resets snow on win move(?!), double-frees the vector(?!)
//std::unique_ptr<Hack::Base> hack = Hack::MakeSnowFP(temp_stale_fb);
// BROKEN: Somehow makes the texture invalid(?!)
//std::unique_ptr<Hack::Base> hack = Hack::MakeSnowInt(temp_stale_fb);
// WORKS but window move resets snow(?!)
//std::unique_ptr<Hack::Base> hack = Hack::MakeSnowClock(temp_stale_fb);
// WORKS
std::unique_ptr<Hack::Base> hack = Hack::MakePopClock(temp_stale_fb);
// WORKS
//std::unique_ptr<Hack::Base> hack = Hack::MakeColorCycle(temp_stale_fb);

Uint32 tickerror = 0;
Uint32 ticklast = SDL_GetTicks();
SDL_Event event;
bool run = true;
while(run) {
// Process events.
while(SDL_PollEvent(&event)) { switch(event.type) {
case SDL_QUIT:
run = false; break;
case SDL_KEYDOWN:
switch(event.key.keysym.sym) {
case SDLK_ESCAPE:
case SDLK_q:
run = false; break;
default:; // Don't care.
}
default:; // Don't care.
}}

// Process the passage of time.
{
const Uint32 now = SDL_GetTicks();
if (now < ticklast) {
// Timer wraparound! You could do fancier code here to figure
// out the interval, but eh, stutter a bit every ~49 days.
ticklast = now;
}
tickerror += (now - ticklast);
ticklast = now;
}
if(tickerror >= hack->tick_duration()) {
if(tickerror > hack->tick_duration() * 10) {
static bool once = false;
if(!once) {
std::cerr << "Running too slow! Skipping ticks!" << std::endl;
once = true;
}
tickerror = hack->tick_duration();
}
do {
tickerror -= hack->tick_duration();
hack->simulate();
} while(tickerror >= hack->tick_duration());
SDL_Surface* fb = nullptr;
if(SDL_LockTextureToSurface(graphics.texture, NULL, &fb) != 0)
{ throw std::runtime_error(SDL_GetError()); }
bool rendered = hack->render(fb);
SDL_UnlockTexture(graphics.texture); // Also frees fb
fb = nullptr;
if(rendered) {
SDL_RenderClear(graphics.renderer);
SDL_RenderCopy(graphics.renderer, graphics.texture,
NULL, NULL);
SDL_RenderPresent(graphics.renderer);
}
} else {
/// Have a nap until we actually have at least one tick to run.
SDL_Delay(hack->tick_duration());
}
}

return EXIT_SUCCESS;
}
25 changes: 17 additions & 8 deletions src/popclock.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#include <stdexcept>
#include <vector>

#include <SDL.h>

#include "hack.hpp"

constexpr size_t k_defragment_threshold = 128; // Don't defrag to < this.
Expand Down Expand Up @@ -369,9 +367,9 @@ struct PopClock : public Hack::Base {
SDL_Surface* make_surface(int w, int h) {
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> s(
SDL_CreateRGBSurface(SDL_SWSURFACE | SDL_ASYNCBLIT,
w, h, 8, 0x00ff0000, 0x0000ff00, 0x000000ff, 0),
w, h, 8, 0, 0, 0, 0),
SDL_FreeSurface);
if(s.get() == nullptr) { throw std::bad_alloc(); }
if(s.get() == nullptr) { throw std::runtime_error(SDL_GetError()); }
if(SDL_MUSTLOCK(s.get())) {
// This is bad, because solid_at will randomly fail (SDL is
// allowed to make the pixels member of lockable surfaces
Expand All @@ -389,9 +387,18 @@ struct PopClock : public Hack::Base {
* https://discourse.libsdl.org/t/pixels-getting-set-to-null/9811/10
* But we're not any more, whee!
*/
if(SDL_SetColorKey(s.get(), SDL_SRCCOLORKEY|SDL_RLEACCEL, 0) != 0) {
#if SDLVERSION == 1
if(SDL_SetColorKey(s.get(), SDL_SRCCOLORKEY | SDL_RLEACCEL, 0) != 0) {
throw std::runtime_error("failed to set color key");
}
#else
if(SDL_SetSurfaceRLE(s.get(), SDL_TRUE) != 0) {
throw std::runtime_error("failed to set RLE");
}
if(SDL_SetColorKey(s.get(), SDL_TRUE, 0) != 0) {
throw std::runtime_error("failed to set color key");
}
#endif
return s.release();
}

Expand Down Expand Up @@ -646,8 +653,10 @@ struct PopClock : public Hack::Base {
[&](auto x, auto y){return digital_clock.solid_at(x, y);});
}

void render() override {
if(!needs_paint) { return; }
bool render(SDL_Surface* framebuffer) override {
fb = framebuffer;
if(!needs_paint) { return false; }

int w = partfb->w;
int h = partfb->h;
if(SDL_MUSTLOCK(partfb.get())) { SDL_LockSurface(partfb.get()); }
Expand Down Expand Up @@ -679,8 +688,8 @@ struct PopClock : public Hack::Base {
// Merge in the digital clock, which is color-keyed for transparency.
SDL_BlitSurface(digital_clock.rendered(), nullptr, fb, nullptr);
//SDL_BlitSurface(prev_clock.get(), nullptr, fb, nullptr); // DEBUG
SDL_Flip(fb);
needs_paint = false;
return true;
}

Uint32 tick_duration() override { return 33; } // 30Hz
Expand Down
Loading

0 comments on commit 2b499d3

Please sign in to comment.