From 46d0e7a3881550f5fe9884961b1ef7894f835c60 Mon Sep 17 00:00:00 2001 From: Joachim de Groot Date: Thu, 6 Nov 2025 07:24:10 +0100 Subject: [PATCH] SDL3 port --- README.md | 1 + cmake/project_common.cmake | 11 + curses.h | 3 +- demos/ozdemo.c | 2 +- sdl3/CMakeLists.txt | 87 ++++++ sdl3/Makefile.vc | 132 +++++++++ sdl3/README.md | 214 +++++++++++++++ sdl3/pdcclip.c | 93 +++++++ sdl3/pdcdisp.c | 545 ++++++++++++++++++++++++++++++++++++ sdl3/pdcgetsc.c | 30 ++ sdl3/pdckbd.c | 550 +++++++++++++++++++++++++++++++++++++ sdl3/pdcscrn.c | 541 ++++++++++++++++++++++++++++++++++++ sdl3/pdcsdl.h | 41 +++ sdl3/pdcsetsc.c | 99 +++++++ sdl3/pdcutil.c | 35 +++ sdl3/sdltest.c | 90 ++++++ sdl3/versions.mif | 2 + 17 files changed, 2474 insertions(+), 2 deletions(-) create mode 100644 sdl3/CMakeLists.txt create mode 100644 sdl3/Makefile.vc create mode 100644 sdl3/README.md create mode 100644 sdl3/pdcclip.c create mode 100644 sdl3/pdcdisp.c create mode 100644 sdl3/pdcgetsc.c create mode 100644 sdl3/pdckbd.c create mode 100644 sdl3/pdcscrn.c create mode 100644 sdl3/pdcsdl.h create mode 100644 sdl3/pdcsetsc.c create mode 100644 sdl3/pdcutil.c create mode 100644 sdl3/sdltest.c create mode 100644 sdl3/versions.mif diff --git a/README.md b/README.md index 17a23a9cc..1a23041b3 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Build instructions are in the README file for each platform: - [Plan9](plan9/README.md) for use on Plan9 (APE) - [SDL 1.x](sdl1/README.md) for use as separate SDL version 1 window - [SDL 2.x](sdl2/README.md) for use as separate SDL version 2 window +- [SDL 3.x](sdl3/README.md) for use as separate SDL version 3 window - [wincon](wincon/README.md) (formerly win32) for use on Windows Console - [WinGUI](wingui/README.md) for use on Windows Graphics Mode - [X11](x11/README.md) (also called XCurses) for use as separate X11 window (but see below) diff --git a/cmake/project_common.cmake b/cmake/project_common.cmake index f9eaeb159..48e4f09df 100644 --- a/cmake/project_common.cmake +++ b/cmake/project_common.cmake @@ -31,18 +31,22 @@ if(WIN32 AND NOT WATCOM) set(EXTRA_LIBS gdi32.lib winspool.lib shell32.lib ole32.lib comdlg32.lib advapi32.lib) set(WINCON_WINGUI_DEP_LIBS winmm.lib) set(SDL2_DEP_LIBRARIES version.lib winmm.lib imm32.lib) + set(SDL3_DEP_LIBRARIES version.lib winmm.lib imm32.lib) elseif(WATCOM_WIN32) set(EXTRA_LIBS "") set(WINCON_WINGUI_DEP_LIBS winmm.lib) set(SDL2_DEP_LIBRARIES "dl") + set(SDL3_DEP_LIBRARIES "dl") elseif(APPLE) set(EXTRA_LIBS "") set(WINCON_WINGUI_DEP_LIBS "") set(SDL2_DEP_LIBRARIES "dl") + set(SDL3_DEP_LIBRARIES "dl") else() set(EXTRA_LIBS "") set(WINCON_WINGUI_DEP_LIBS "") set(SDL2_DEP_LIBRARIES "dl") + set(SDL3_DEP_LIBRARIES "dl") endif() if (APPLE) @@ -91,6 +95,13 @@ if(PDC_BUILD_SHARED) else() target_link_libraries(${PDCURSE_PROJ} PRIVATE ${SDL2_LIBRARIES} ${SDL2_DEP_LIBRARIES}) endif() + elseif(${PROJECT_NAME} STREQUAL "sdl3") + if(PDC_WIDE OR PDC_UTF8) + target_link_libraries(${PDCURSE_PROJ} PRIVATE ${SDL3_LIBRARIES} ${SDL3_TTF_LIBRARY} + ${FT2_LIBRARY} ${ZLIB_LIBRARY} ${SDL3_DEP_LIBRARIES}) + else() + target_link_libraries(${PDCURSE_PROJ} PRIVATE ${SDL3_LIBRARIES} ${SDL3_DEP_LIBRARIES}) + endif() elseif((${PROJECT_NAME} STREQUAL "wincon") OR (${PROJECT_NAME} STREQUAL "wingui") OR (${PROJECT_NAME} STREQUAL "vt")) target_link_libraries(${PDCURSE_PROJ} PRIVATE ${WINCON_WINGUI_DEP_LIBS}) endif() diff --git a/curses.h b/curses.h index 6aa469aa2..3049ee89d 100644 --- a/curses.h +++ b/curses.h @@ -178,7 +178,8 @@ enum PDC_port PDC_PORT_PLAN9 = 9, PDC_PORT_LINUX_FB = 10, PDC_PORT_OPENGL = 11, - PDC_PORT_OS2GUI = 12 + PDC_PORT_OS2GUI = 12, + PDC_PORT_SDL3 = 13 }; /* Use this structure with PDC_get_version() for run-time info about the diff --git a/demos/ozdemo.c b/demos/ozdemo.c index 2420d5897..ac281951d 100644 --- a/demos/ozdemo.c +++ b/demos/ozdemo.c @@ -223,7 +223,7 @@ int main(int argc, char **argv) const char *versions_1 = " DOS, DOSVGA, GL, Linux framebuffer/DRM, OS/2, OS/2 GUI,"; const char *versions_2 = - " Plan 9, SDL 1/2, VT, Windows console & GUI, X11"; + " Plan 9, SDL 1/2/3, VT, Windows console & GUI, X11"; const char *hit_any_key = " Type a key to continue or ESC to quit "; SCREEN *screen_pointer = newterm(NULL, stdout, stdin); diff --git a/sdl3/CMakeLists.txt b/sdl3/CMakeLists.txt new file mode 100644 index 000000000..9b43649cc --- /dev/null +++ b/sdl3/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "MinSizeRel" CACHE STRING "Choose the type of build, options are: Debug, Release, or MinSizeRel." FORCE) + message(STATUS "No build type specified, defaulting to MinSizeRel.") +endif() + +project(sdl3 VERSION "${PROJECT_VERSION}" LANGUAGES C) +message(STATUS "${PROJECT_NAME} version: ${PROJECT_VERSION}") + +message(STATUS "SDL3_LIBRARIES = ${SDL3_LIBRARIES}") +message(STATUS "SDL3_INCLUDE_DIR = ${SDL3_INCLUDE_DIR}") +message(STATUS "SDL3_LIBRARY_DIR = ${SDL3_LIBRARY_DIR}") +if(PDC_WIDE OR PDC_UTF8) + message(STATUS "SDL3_TTF_LIBRARY = ${SDL3_TTF_LIBRARY}") + message(STATUS "SDL3_TTF_INCLUDE_DIR = ${SDL3_TTF_INCLUDE_DIR}") + message(STATUS "SDL3_TTF_LIBRARY_DIR = ${SDL3_TTF_LIBRARY_DIR}") +endif() + +include_directories(${SDL3_INCLUDE_DIR} ${SDL3_TTF_INCLUDE_DIR}) +link_directories(${SDL3_LIBRARY_DIR} ${SDL3_TTF_LIBRARY_DIR}) + +if(NOT pdcurses_src_files) + file(GLOB pdcurses_src_files ${CMAKE_CURRENT_SOURCE_DIR}/../pdcurses/*.c) +endif() + +include(project_common) + +macro (sdl3_app dir targ) + + set(bin_name "${PROJECT_NAME}_${targ}") + + if(${targ} STREQUAL "tuidemo") + set(src_files ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/tuidemo.c ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/tui.c) + else() + set(src_files ${CMAKE_CURRENT_SOURCE_DIR}/${dir}/${targ}.c) + endif() + + if(${ARGV2}) + add_executable(${bin_name} WIN32 ${src_files}) + else() + add_executable(${bin_name} ${src_files}) + endif() + + if(PDC_WIDE OR PDC_UTF8) + target_link_libraries(${bin_name} PUBLIC ${PDCURSE_PROJ} ${EXTRA_LIBS} + "${SDL3_LIBRARIES};${SDL3_TTF_LIBRARY};${FT2_LIBRARY};${ZLIB_LIBRARY};${SDL3_DEP_LIBRARIES}") + else() + target_link_libraries(${bin_name} PUBLIC ${PDCURSE_PROJ} ${EXTRA_LIBS} + "${SDL3_LIBRARIES};${SDL3_TTF_DEP_LIBRARIES};${SDL3_DEP_LIBRARIES}") + endif() + + add_dependencies(${bin_name} ${PDCURSE_PROJ}) + set_target_properties(${bin_name} PROPERTIES OUTPUT_NAME ${targ}) + + install(TARGETS ${bin_name} RUNTIME DESTINATION ${PDCURSES_DIST}/bin/${PROJECT_NAME} COMPONENT applications) + +endmacro () + +sdl3_app(../demos version) +sdl3_app(../demos calendar) +sdl3_app(../demos firework) +sdl3_app(../demos init_col) +sdl3_app(../demos mbrot) +sdl3_app(../demos ozdemo) +sdl3_app(../demos newtest WIN32) +sdl3_app(../demos picsview) +sdl3_app(../demos ptest) +sdl3_app(../demos rain) +sdl3_app(../demos speed) +sdl3_app(../demos testcurs) +sdl3_app(../demos test_pan) +sdl3_app(../demos tuidemo) +sdl3_app(../demos widetest) +sdl3_app(../demos worm) +sdl3_app(../demos xmas) +sdl3_app(./ sdltest) + +if(PDC_SDL3_DEPS_BUILD) + if(PDC_WIDE OR PDC_UTF8) + add_dependencies(${PDCURSE_PROJ} sdl3_ext sdl3_ttf_ext) + else() + add_dependencies(${PDCURSE_PROJ} sdl3_ext) + endif() +endif() + +set(CPACK_COMPONENTS_ALL applications) diff --git a/sdl3/Makefile.vc b/sdl3/Makefile.vc new file mode 100644 index 000000000..b54bbb9dc --- /dev/null +++ b/sdl3/Makefile.vc @@ -0,0 +1,132 @@ +# Visual C++ Makefile for PDCurses - SDL3 +# +# Usage: nmake -f [path\]Makefile.vc [DEBUG=Y] [DLL=Y] [WIDE=Y] [UTF8=Y] +# [target] +# +# where target can be any of: +# [all|demos|pdcurses.lib|testcurs.exe...] + +O = obj +E = .exe +RM = del + +!ifndef PDCURSES_SRCDIR +PDCURSES_SRCDIR = .. +!endif + +osdir = $(PDCURSES_SRCDIR)\sdl3 +common = $(PDCURSES_SRCDIR)\common + +!include $(common)\libobjs.mif +!include $(osdir)\versions.mif + +SDL3_INCLUDE = -I$(SDLBASE)\include +SDL3_LIB = $(SDLBASE)\lib\$(PLATFORM)\SDL3.lib +SDL3_LIBMAIN = $(SDLBASE)\lib\$(PLATFORM)\SDL3main.lib + +PDCURSES_WIN_H = $(osdir)\pdcsdl.h + +CC = cl.exe -nologo + +!ifndef PLATFORM +PLATFORM = $(VSCMD_ARG_TGT_ARCH) +!endif + +!ifdef DEBUG +CFLAGS = -Z7 -W4 -DPDCDEBUG -D_CRT_SECURE_NO_DEPRECATE +LDFLAGS = -debug -pdb:none +!else +CFLAGS = -Ox -W4 -D_CRT_SECURE_NO_DEPRECATE +LDFLAGS = +!endif + +!ifdef WIDE +WIDEOPT = -DPDC_WIDE +!endif + +!ifdef UTF8 +WIDEOPT = -DPDC_FORCE_UTF8 +!endif + +!ifdef WIDEOPT +TTF_INCLUDE = -I$(TTFBASE)\include +TTF_LIB = $(TTFBASE)\lib\$(PLATFORM)\SDL3_ttf.lib +!endif + +SHL_LD = link $(LDFLAGS) -nologo -dll -machine:$(PLATFORM) -out:pdcurses.dll + +LINK = link.exe -nologo -subsystem:windows + +LIBEXE = lib -nologo + +LIBCURSES = pdcurses.lib +CURSESDLL = pdcurses.dll + +!ifdef DLL +DLLOPT = -DPDC_DLL_BUILD +PDCLIBS = $(CURSESDLL) +LIBLIBS = $(SDL3_LIB) $(TTF_LIB) +CCLIBS = $(SDL3_LIBMAIN) $(SDL3_LIB) shell32.lib +!else +PDCLIBS = $(LIBCURSES) +CCLIBS = $(SDL3_LIBMAIN) $(SDL3_LIB) $(TTF_LIB) shell32.lib +!endif + +BUILD = $(CC) -I$(PDCURSES_SRCDIR) \ +-c $(CFLAGS) $(DLLOPT) $(WIDEOPT) $(INFOPT) + +all: $(PDCLIBS) + +clean: + -$(RM) *.obj + -$(RM) $(LIBCURSES) + -$(RM) *.exe + -$(RM) $(CURSESDLL) + -$(RM) *.exp + -$(RM) pdcurses.res + +demos: $(PDCLIBS) $(DEMOS) sdltest.exe + +DEMOOBJS = $(DEMOS:.exe=.obj) tui.obj sdltest.obj + +$(LIBOBJS) $(PDCOBJS) : $(PDCURSES_HEADERS) +$(PDCOBJS) : $(PDCURSES_WIN_H) +$(DEMOOBJS) : $(PDCURSES_CURSES_H) +$(DEMOS) : $(LIBCURSES) +panel.obj : $(PANEL_HEADER) + +!ifndef DLL +$(LIBCURSES) : $(LIBOBJS) $(PDCOBJS) + $(LIBEXE) -out:$@ $(LIBOBJS) $(PDCOBJS) +!endif + +$(CURSESDLL) : $(LIBOBJS) $(PDCOBJS) pdcurses.obj + $(SHL_LD) $(LIBOBJS) $(PDCOBJS) pdcurses.obj $(LIBLIBS) + +pdcurses.res pdcurses.obj: $(common)\pdcurses.rc + rc -r -fopdcurses.res $(common)\pdcurses.rc + cvtres -machine:$(PLATFORM) -nologo -out:pdcurses.obj pdcurses.res + +{$(srcdir)\}.c{}.obj:: + $(BUILD) $< + +{$(osdir)\}.c{}.obj:: + $(BUILD) -Dmain=SDL_main $(SDL3_INCLUDE) $(TTF_INCLUDE) $< + +{$(demodir)\}.c{}.obj:: + $(BUILD) -Dmain=SDL_main $< + +.obj.exe: + $(LINK) $(LDFLAGS) $< $(LIBCURSES) $(CCLIBS) + +tuidemo.exe: tuidemo.obj tui.obj pdcurses.lib + $(LINK) $(LDFLAGS) $*.obj tui.obj $(LIBCURSES) $(CCLIBS) + +tui.obj: $(demodir)\tui.c $(demodir)\tui.h + $(BUILD) -I$(demodir) $(demodir)\tui.c + +tuidemo.obj: $(demodir)\tuidemo.c + $(BUILD) -Dmain=SDL_main -I$(demodir) $(demodir)\tuidemo.c + +sdltest.exe: sdltest.obj pdcurses.obj pdcurses.lib + $(LINK) $(LDFLAGS) $*.obj $(LIBCURSES) $(LIBLIBS) $(CCLIBS) diff --git a/sdl3/README.md b/sdl3/README.md new file mode 100644 index 000000000..5b0395e22 --- /dev/null +++ b/sdl3/README.md @@ -0,0 +1,214 @@ +PDCurses for SDL 3.x +==================== + +This is a port of PDCurses for version 3.x of SDL (aka SDL3). + + +Building +-------- + +- On *nix (including Linux and Mac OS X), run "make" in the sdl3 + directory. There is no configure script (yet?) for this port. This + assumes a working sdl-config, and GNU make. It builds the library + pdcurses.a (or pdcurses.so/pdcurses.dylib with DLL=Y). + +- With MinGW, edit the Makefile to point to the appropriate include and + library paths, and then run "mingw32-make". + +- With MSVC, edit Makefile.vc if needed, and do "nmake -f Makefile.vc". + +- The makefile recognizes the optional PDCURSES_SRCDIR environment + variable, and the option "DEBUG=Y", as with the console ports. + "WIDE=Y" builds a version that not only uses 32-bit Unicode + characters, but depends on the SDL3_ttf library, instead of using + simple bitmap fonts. "UTF8=Y" makes PDCurses ignore the system locale, + and treat all narrow-character strings as UTF-8; this option has no + effect unless WIDE=Y is also set. You can specify "DLL=Y" to build a dynamic + rather than static library. The dynamic library is called pdcurses.dll, + pdcurses.so, or pdcurses.dylib on Windows, Linux, or Mac OS X respectively. + And on all platforms, add the target "demos" to build the sample programs. + + +Usage +----- + +There are no special requirements to use PDCurses for SDL -- all +PDCurses-compatible code should work fine. Nothing extra is needed +beyond the base SDL library. However, there are some optional special +features, described here. + +The SDL ports operate in one of two ways, depending on whether or not +they were built with WIDE=Y: + + +### 8-bit mode + +The font is a simple BMP, 32 characters wide by 8 characters tall, +preferably with a palette. (BMPs without palettes still work, but in +that case, no attributes will be available, nor will the cursor work.) +The first entry in the palette (usually black) is treated as the +background color; the last entry (usually white) is treated as the +foreground. These are changed or made transparent as appropriate; any +other colors in the palette are passed through unchanged. So -- although +a one-bit depth is sufficient for a normal font -- you could redraw some +characters as multi-colored tiles. + +The font must be monospaced. The size of each character is derived by +dividing the width of the BMP by 32 and the height by 8. There is no +constraint on the dimensions. + +As provided in the default font and expected by acs_map[], the font is +in Code Page 437 form. But you can of course use any layout if you're +not relying on correct values for the ACS_* macros. + +The font can be set via the environment variable PDC_FONT. If it's not +set, PDCurses looks for a file named "pdcfont.bmp" in the current +directory at the time of initscr(). If neither is found, it uses the +built-in default font encoded in font437.h. + + +### 32-bit mode + +Instead of a BMP, PDC_FONT points to a TrueType font. Only true +monospaced fonts work well. The font can be set at compile time via +PDC_FONT_PATH, and/or at runtime via pdc_ttffont. The environment +variable PDC_FONT_SIZE is also available to control the font size (also +as a compile-time define, and at runtime as pdc_font_size.) The +character mapping for chtypes is UTF-32. + + +The default font (if not redefined) is based on the OS: + +- Windows: C:/Windows/Fonts/consola.ttf + +- Mac: /Library/Fonts/Menlo.ttc + +- Other: /usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf + + +Backgrounds +----------- + +PDCurses for SDL supports an optional background image BMP. This is used +whenever start_color() has not been called (see the ptest demo for an +example), or when use_default_colors() has been called after +start_color(), and the background color of a pair has been set to -1 +(see ozdemo, worm, and rain for examples). The usage parallels that of +ncurses in an appropriate terminal (e.g., Gnome Terminal). The image is +tiled to cover the PDCurses window, and can be any size or depth. + +As with the font, you can point to a location for the background via the +environment variable PDC_BACKGROUND; "pdcback.bmp" is the fallback. +(There is no default background.) + + +Icons +----- + +The icon (used with SDL_SetWindowIcon() -- not used for the executable +file) can be set via the environment variable PDC_ICON, and falls back +to "pdcicon.bmp", and then to the built-in icon from iconbmp.h. The +built-in icon is the PDCurses logo, as seen in ../common/icon32.xpm. + +If pdc_screen is preinitialized (see below), PDCurses does not attempt +to set the icon. + + +Screen size +----------- + +The default screen size is 80x25 characters (whatever size they may be), +but you can override this via the environment variables PDC_COLS and/or +PDC_LINES. If pdc_screen is preinitialized (see below), these are +ignored. + + +Integration with SDL +-------------------- + +If you want to go further, you can mix PDCurses and SDL functions. (Of +course this is extremely non-portable!) To aid you, there are several +external variables and functions specific to the SDL ports; you could +include pdcsdl.h, or just add the declarations you need in your code: + + PDCEX SDL_Window *pdc_window; + PDCEX SDL_Surface *pdc_screen, *pdc_font, *pdc_icon, *pdc_back; + PDCEX int pdc_sheight, pdc_swidth, pdc_yoffset, pdc_xoffset, pdc_sdl_render_mode; + + PDCEX void PDC_update_rects(void); + PDCEX void PDC_retile(void); + +pdc_window is the main window, created by SDL_CreateWindow(), unless +it's preset before initscr(); and pdc_screen is the main surface, set by +SDL_GetWindowSurface(pdc_window). (See sdltest.c for examples.) You can +perform normal SDL operations on this surface, but PDCurses won't +respect them when it updates. (For that, see PDC_retile().) As an +alternative, you can preinitialize this surface before calling +initscr(). In that case, you can use pdc_sheight, pdc_swidth, +pdc_yoffset and/or pdc_xoffset (q.v.) to confine PDCurses to only a +specific area of the surface, reserving the rest for other SDL +operations. If you preinitialize pdc_window, you'll have to close it +yourself; PDCurses will ignore resize events, and won't try to set the +icon. Also note that if you preinitialize pdc_screen, it need not be the +display surface. + +pdc_font (in 8-bit mode), pdc_icon, and pdc_back are the SDL_surfaces +for the font, icon, and background, respectively. You can set any or all +of them before initscr(), and thus override any of the other ways to set +them. But note that pdc_icon will be ignored if pdc_screen is preset. + +pdc_sdl_render_mode (in 32-bit mode) can be set to `PDC_SDL_RENDER_SOLID`, +`PDC_SDL_RENDER_SHADED` or `PDC_SDL_RENDER_BLENDED`. This determines which SDL TTF +render mode will be used for rendering text: `TTF_RenderGlyph32_Solid()`, +`TTF_RenderGlyph32_Shaded()` or `TTF_RenderGlyph32_Blended()` respectively. +This will default to `PDC_SDL_RENDER_BLENDED`. If you wish to use this feature +without including `pdcsdl.h`, you must define the following constants: + +``` +#define PDC_SDL_RENDER_SOLID 1 +#define PDC_SDL_RENDER_SHADED 2 +#define PDC_SDL_RENDER_BLENDED 3 +``` + +pdc_sheight and pdc_swidth are the dimensions of the area of pdc_screen +to be used by PDCurses. You can preset them before initscr(); if either +is not set, it defaults to the full screen size minus the x or y offset, +as appropriate. + +pdc_xoffset and pdc_yoffset are the x and y offset for the area of +pdc_screen to be used by PDCurses. See the sdltest demo for an example. + +PDC_retile() makes a copy of pdc_screen, then tiles it with the +background image, if any. The resulting surface is used as the +background for transparent character cells. PDC_retile() is called from +initscr() and resize_term(). However, you can also use it at other +times, to take advantage of the way it copies pdc_screen: Draw some SDL +stuff; call PDC_retile(); do some curses stuff -- it will use whatever +was on pdc_screen as the background. Then you can erase the curses +screen, do some more SDL stuff, and call PDC_retile() again to make a +new background. (If you don't erase the curses screen, it will be +incorporated into the background when you call PDC_retile().) But this +only works if no background image is set. + + +Interaction with stdio +---------------------- + +As with X11, it's a bad idea to mix curses and stdio calls. (In fact, +that's true for PDCurses on any platform; but especially these two, +which don't run under terminals.) Depending on how SDL is built, stdout +and stderr may be redirected to files. + + +Distribution Status +------------------- + +The files in this directory are released to the public domain. + + +Acknowledgements +---------------- + +Original SDL port was provided by William McBrine +SDL2 modifications by Laura Michaels and Robin Gustafsson +SDL3 modifications by Joachim de Groot diff --git a/sdl3/pdcclip.c b/sdl3/pdcclip.c new file mode 100644 index 000000000..691f1b990 --- /dev/null +++ b/sdl3/pdcclip.c @@ -0,0 +1,93 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +#include +#include + +/*man-start************************************************************** + +clipboard +--------- + +### Synopsis + + int PDC_getclipboard(char **contents, long *length); + int PDC_setclipboard(const char *contents, long length); + int PDC_freeclipboard(char *contents); + int PDC_clearclipboard(void); + +### Description + + PDC_getclipboard() gets the textual contents of the system's + clipboard. This function returns the contents of the clipboard in the + contents argument. It is the responsibility of the caller to free the + memory returned, via PDC_freeclipboard(). The length of the clipboard + contents is returned in the length argument. + + PDC_setclipboard() copies the supplied text into the system's + clipboard, emptying the clipboard prior to the copy. + + PDC_clearclipboard() clears the internal clipboard. + +### Return Values + + indicator of success/failure of call. + PDC_CLIP_SUCCESS the call was successful + PDC_CLIP_MEMORY_ERROR unable to allocate sufficient memory for + the clipboard contents + PDC_CLIP_EMPTY the clipboard contains no text + PDC_CLIP_ACCESS_ERROR no clipboard support + +### Portability + X/Open ncurses NetBSD + PDC_getclipboard - - - + PDC_setclipboard - - - + PDC_freeclipboard - - - + PDC_clearclipboard - - - + +**man-end****************************************************************/ + +int PDC_getclipboard(char **contents, long *length) +{ + PDC_LOG(("PDC_getclipboard() - called\n")); + + if (SDL_HasClipboardText() == false) + return PDC_CLIP_EMPTY; + *contents = SDL_GetClipboardText(); + *length = (long)strlen(*contents); + + return PDC_CLIP_SUCCESS; +} + +int PDC_setclipboard(const char *contents, long length) +{ + PDC_LOG(("PDC_setclipboard() - called\n")); + + INTENTIONALLY_UNUSED_PARAMETER( length); + if (!SDL_SetClipboardText(contents)) + return PDC_CLIP_ACCESS_ERROR; + + return PDC_CLIP_SUCCESS; +} + +int PDC_freeclipboard(char *contents) +{ + PDC_LOG(("PDC_freeclipboard() - called\n")); + + SDL_free(contents); + + return PDC_CLIP_SUCCESS; +} + +int PDC_clearclipboard(void) +{ + PDC_LOG(("PDC_clearclipboard() - called\n")); + + if (SDL_HasClipboardText()) + { + SDL_ClearClipboardData(); + } + + return PDC_CLIP_SUCCESS; +} diff --git a/sdl3/pdcdisp.c b/sdl3/pdcdisp.c new file mode 100644 index 000000000..c77f7c4fd --- /dev/null +++ b/sdl3/pdcdisp.c @@ -0,0 +1,545 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +#include +#include +#include + +# ifdef PDC_WIDE +# define USE_UNICODE_ACS_CHARS 1 +# else +# define USE_UNICODE_ACS_CHARS 0 +# endif + +# include "../common/acs_defs.h" +# include "../common/pdccolor.h" +# include "../common/blink.c" + +#define MAXRECT 200 /* maximum number of rects to queue up before + an update is forced; the number was chosen + arbitrarily */ + +static SDL_Rect uprect[MAXRECT]; /* table of rects to update */ +static chtype oldch = (chtype)(-1); /* current attribute */ +static int rectcount = 0; /* index into uprect */ +static int foregr = -2, backgr = -2; /* current foreground, background */ +/* do the real updates on a delay */ + +void PDC_update_rects(void) +{ + if (rectcount) + { + /* if the maximum number of rects has been reached, we're + probably better off doing a full screen update */ + + if (rectcount == MAXRECT) + SDL_UpdateWindowSurface(pdc_window); + else + { + int w = pdc_screen->w; + int h = pdc_screen->h; + int i; + + for (i = 0; i < rectcount; i++) + { + if (uprect[i].x > w || + uprect[i].y > h || + !uprect[i].w || !uprect[i].h) + { + if (i + 1 < rectcount) + { + memmove(uprect + i, uprect + i + 1, + (rectcount - i + 1) * sizeof(*uprect)); + --i; + } + rectcount--; + continue; + } + + if (uprect[i].x + uprect[i].w > w) + uprect[i].w = min(w, w - uprect[i].x); + + if (uprect[i].y + uprect[i].h > h) + uprect[i].h = min(h, h - uprect[i].y); + } + + if (rectcount > 0) + SDL_UpdateWindowSurfaceRects(pdc_window, uprect, rectcount); + } + + rectcount = 0; + } +} + +static SDL_Color *get_pdc_color( const int color_idx) +{ + static SDL_Color c; + const PACKED_RGB rgb = PDC_get_palette_entry( color_idx > 0 ? color_idx : 0); + + c.r = (Uint8)Get_RValue( rgb); + c.g = (Uint8)Get_GValue( rgb); + c.b = (Uint8)Get_BValue( rgb); + return( &c); +} + +static Uint32 get_pdc_mapped( const int color_idx) +{ + SDL_Color *c = get_pdc_color( color_idx); + + return(SDL_MapSurfaceRGB( pdc_screen, c->r, c->g, c->b)); +} + +/* set the font colors to match the chtype's attribute */ + +static void _set_attr(chtype ch) +{ + attr_t sysattrs = SP->termattrs; + +#ifdef PDC_WIDE + TTF_SetFontStyle(pdc_ttffont, + ( ((ch & A_BOLD) && (sysattrs & A_BOLD)) ? + TTF_STYLE_BOLD : 0) | + ( ((ch & A_ITALIC) && (sysattrs & A_ITALIC)) ? + TTF_STYLE_ITALIC : 0) ); +#endif + + ch &= (A_COLOR|A_BOLD|A_BLINK|A_REVERSE); + + if (oldch != ch) + { + int newfg, newbg; + + if (SP->mono) + return; + + extended_pair_content(PAIR_NUMBER(ch), &newfg, &newbg); + + if ((ch & A_BOLD) && !(sysattrs & A_BOLD)) + newfg |= 8; + if ((ch & A_BLINK) && !(sysattrs & A_BLINK)) + newbg |= 8; + + if (ch & A_REVERSE) + { + int tmp = newfg; + newfg = newbg; + newbg = tmp; + } + + if (newfg != foregr) + { +#ifndef PDC_WIDE + SDL_Palette *palette = SDL_GetSurfacePalette(pdc_font); + SDL_SetPaletteColors(palette, get_pdc_color(newfg), pdc_flastc, 1); +#endif + foregr = newfg; + } + + if (newbg != backgr) + { +#ifndef PDC_WIDE + if (newbg == -1) + SDL_SetSurfaceColorKey(pdc_font, true, 0); + else + { + if (backgr == -1) + SDL_SetSurfaceColorKey(pdc_font, false, 0); + + SDL_Palette *palette = SDL_GetSurfacePalette(pdc_font); + SDL_SetPaletteColors(palette, get_pdc_color( newbg), 0, 1); + } +#endif + backgr = newbg; + } + + oldch = ch; + } +} + +#ifdef PDC_WIDE +/* Draw some of the ACS_* "graphics" */ + +#define BIT_UP 1 +#define BIT_DN 2 +#define BIT_RT 4 +#define BIT_LT 8 +#define HORIZ (BIT_LT | BIT_RT) +#define VERTIC (BIT_UP | BIT_DN) + /* Macros used for deciphering ACS_S1, ACS_S3, ACS_S7, ACS_S9 */ +#define Sn_CHARS 0x10 +#define SCAN_LINE( n) (Sn_CHARS | ((n - 1) << 8)) + +static bool _grprint(chtype ch, const SDL_Rect dest) +{ + int i = 0; + bool rval; + const int remap_tbl[] = { + ACS_ULCORNER, BIT_DN | BIT_RT, ACS_LLCORNER, BIT_UP | BIT_RT, + ACS_URCORNER, BIT_DN | BIT_LT, ACS_LRCORNER, BIT_UP | BIT_LT, + ACS_LTEE, VERTIC | BIT_RT, ACS_RTEE, VERTIC | BIT_LT, + ACS_TTEE, HORIZ | BIT_DN, ACS_BTEE, HORIZ | BIT_UP, + ACS_HLINE, HORIZ, ACS_VLINE, VERTIC, + ACS_PLUS, HORIZ | VERTIC, ACS_BLOCK, 0, + ACS_S1, HORIZ | SCAN_LINE( 1), ACS_S3, HORIZ | SCAN_LINE( 3), + ACS_S7, HORIZ | SCAN_LINE( 7), ACS_S9, HORIZ | SCAN_LINE( 9), 0 }; + + while( remap_tbl[i] && remap_tbl[i] != (int)ch) + i += 2; + if( remap_tbl[i] == (int)ch) + { + const int hmid = (pdc_fheight - pdc_fthick) >> 1; + const int wmid = (pdc_fwidth - pdc_fthick) >> 1; + const int mask = remap_tbl[i + 1]; + SDL_Rect temp = dest; + const Uint32 col = get_pdc_mapped( foregr); + + if( ch == ACS_BLOCK) + SDL_FillSurfaceRect(pdc_screen, &temp, col); + if( mask & HORIZ) + { + temp.h = pdc_fthick; + if( mask & Sn_CHARS) /* extract scan line for ACS_Sn characters */ + temp.y += (mask >> 8) * hmid >> 2; + else + temp.y += hmid; + switch( mask & HORIZ) + { + case BIT_RT: + temp.x += wmid; + temp.w -= wmid; + break; + case BIT_LT: + temp.w = wmid + pdc_fthick; + break; + case HORIZ: + break; + } + SDL_FillSurfaceRect(pdc_screen, &temp, col); + } + temp = dest; + if( mask & VERTIC) + { + temp.x += wmid; + temp.w = pdc_fthick; + switch( mask & VERTIC) + { + case BIT_DN: + temp.y += hmid; + temp.h -= hmid; + break; + case BIT_UP: + temp.h = hmid + pdc_fthick; + break; + case VERTIC: + break; + } + SDL_FillSurfaceRect(pdc_screen, &temp, col); + } + rval = TRUE; + } + else + rval = FALSE; /* didn't draw it -- fall back to acs_map */ + return( rval); +} + +static SDL_Surface *_render_ttf_glyph(chtype ch) +{ + ch &= A_CHARTEXT; + +#ifdef PDC_SDL_SUPPLEMENTARY_PLANES_SUPPORT + switch (pdc_sdl_render_mode) + { + case PDC_SDL_RENDER_SOLID: + return TTF_RenderGlyph32_Solid(pdc_ttffont, (Uint32)ch, + *(get_pdc_color( foregr))); + case PDC_SDL_RENDER_SHADED: + return TTF_RenderGlyph32_Shaded(pdc_ttffont, (Uint32)ch, + *(get_pdc_color( foregr)), + *(get_pdc_color( backgr))); + default: + return TTF_RenderGlyph32_Blended(pdc_ttffont, (Uint32)ch, + *(get_pdc_color( foregr))); + } +#else + /* no support for supplementary planes */ + if (ch > 0xffff) + ch = '?'; + + switch (pdc_sdl_render_mode) + { + case PDC_SDL_RENDER_SOLID: + return TTF_RenderGlyph_Solid(pdc_ttffont, (Uint16)ch, + *(get_pdc_color( foregr))); + break; + case PDC_SDL_RENDER_SHADED: + return TTF_RenderGlyph_Shaded(pdc_ttffont, (Uint16)ch, + *(get_pdc_color( foregr)), + *(get_pdc_color( backgr))); + default: + return TTF_RenderGlyph_Blended(pdc_ttffont, (Uint16)ch, + *(get_pdc_color( foregr))); + } +#endif +} + +#endif + +/* draw a cursor at (y, x) */ + +static bool _merge_rects( SDL_Rect *a, const SDL_Rect *b) +{ + if( a->x == b->x && a->w == b->w) + { + const int ay = a->y + a->h; + const int by = b->y + b->h; + + if( ay >= b->y && by >= a->y) + { + const int y1 = min( a->y, b->y); + const int y2 = max( ay, by); + + a->y = y1; + a->h = y2 - y1; + return TRUE; + } + } + if( a->y == b->y && a->h == b->h) + { + const int ax = a->x + a->w; + const int bx = b->x + b->w; + + if( ax >= b->x && bx >= a->x) + { + const int x1 = min( a->x, b->x); + const int x2 = max( ax, bx); + + a->x = x1; + a->w = x2 - x1; + return TRUE; + } + } + return FALSE; +} + +static void _new_packet( attr_t attr, const int lineno, const int x, const int len, const chtype *srcp) +{ + SDL_Rect src, dest; + int j; +#ifdef PDC_WIDE + chtype prev_ch = 0; +#endif + attr_t sysattrs = SP->termattrs; + short hcol = SP->line_color; + const bool blink = SP->blink_state && (attr & A_BLINK) && (sysattrs & A_BLINK); + + if (rectcount == MAXRECT) + PDC_update_rects(); + +#ifdef PDC_WIDE + src.x = 0; + src.y = 0; +#endif + src.h = pdc_fheight; + src.w = pdc_fwidth; + + dest.y = pdc_fheight * lineno + pdc_yoffset; + dest.x = pdc_fwidth * x + pdc_xoffset; + dest.h = pdc_fheight; + dest.w = pdc_fwidth * len; + uprect[rectcount++] = dest; + + /* we try merging the new rectangle with the previous one. It's + possible the result can be merged with the one before that, + and so on. */ + + while( rectcount >= 2 && _merge_rects( uprect + rectcount - 2, + uprect + rectcount - 1)) + rectcount--; + + _set_attr(attr); + + if (backgr == -1) + SDL_BlitSurface(pdc_tileback, &dest, pdc_screen, &dest); +#ifdef PDC_WIDE + else + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( backgr)); +#endif + + if (hcol == -1) + hcol = (short)foregr; + + for (j = 0; j < len; j++) + { + chtype ch = srcp[j]; + + dest.w = pdc_fwidth; + + if( _is_altcharset( ch)) + { +#ifdef PDC_WIDE + if (_grprint(ch & (0x7f | A_ALTCHARSET), dest)) + { + dest.x += pdc_fwidth; + continue; + } +#endif + ch = acs_map[ch & 0x7f]; + } + if (blink) + ch = ' '; + +#ifdef PDC_WIDE + ch &= A_CHARTEXT; + + if (ch != ' ') + { + if (prev_ch != ch) + { + prev_ch = ch; + if (pdc_font) + SDL_DestroySurface(pdc_font); + + pdc_font = _render_ttf_glyph(ch); + } + + if (pdc_font) + { + int center = pdc_fwidth > pdc_font->w ? + (pdc_fwidth - pdc_font->w) >> 1 : 0; + dest.x += center; + SDL_BlitSurface(pdc_font, &src, pdc_screen, &dest); + dest.x -= center; + } + } +#else + src.x = (ch & 0xff) % 32 * pdc_fwidth; + src.y = (ch & 0xff) / 32 * pdc_fheight; + + SDL_BlitSurface(pdc_font, &src, pdc_screen, &dest); +#endif + if (!blink && (attr & (WA_LEFT | WA_RIGHT))) + { + dest.w = pdc_fthick; + + if (attr & WA_LEFT) + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + + if (attr & WA_RIGHT) + { + dest.x += pdc_fwidth - pdc_fthick; + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + dest.x -= pdc_fwidth - pdc_fthick; + } + } + + dest.x += pdc_fwidth; + } + +#ifdef PDC_WIDE + if (pdc_font) + { + SDL_DestroySurface(pdc_font); + pdc_font = NULL; + } +#endif + + if( (!blink && (attr & (WA_UNDERLINE | WA_TOP | WA_STRIKEOUT))) || SP->drawing_cursor) + { + dest.x = pdc_fwidth * x + pdc_xoffset; + dest.h = pdc_fthick; + dest.w = pdc_fwidth * len; + if( attr & WA_TOP) + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + if( attr & WA_UNDERLINE) + { + dest.y += pdc_fheight - pdc_fthick; + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + dest.y -= pdc_fheight - pdc_fthick; + } + if( attr & WA_STRIKEOUT) + { + dest.y += (pdc_fheight - pdc_fthick) / 2; + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + dest.y -= (pdc_fheight - pdc_fthick) / 2; + } + if( SP->drawing_cursor == 5 /* bottom half block cursor */ + || SP->drawing_cursor == 1) /* underscore */ + { + dest.h = pdc_fheight / (SP->drawing_cursor == 1 ? 5 : 2); + dest.y += pdc_fheight - dest.h; + SDL_FillSurfaceRect(pdc_screen, &dest, get_pdc_mapped( hcol)); + } + } +} + +/* update the given physical line to look like the corresponding line in + curscr */ + +void PDC_transform_line(int lineno, int x, int len, const chtype *srcp) +{ + attr_t old_attr; + int i, j; + chtype tch; + + PDC_LOG(("PDC_transform_line() - called: lineno=%d\n", lineno)); + if( SP->drawing_cursor) + { + assert( len == 1); + tch = *srcp; + if( SP->drawing_cursor == 2) /* full-cell blinking */ + tch ^= A_REVERSE; + if( SP->drawing_cursor == 3) /* hollow box */ + tch ^= A_UNDERLINE | A_TOP | A_LEFT | A_RIGHT; + srcp = &tch; + } + + old_attr = *srcp & (A_ATTRIBUTES ^ A_ALTCHARSET); + + for (i = 1, j = 1; j < len; i++, j++) + { + const chtype attr = srcp[i] & (A_ATTRIBUTES ^ A_ALTCHARSET); + + if (attr != old_attr) + { + _new_packet(old_attr, lineno, x, i, srcp); + old_attr = attr; + srcp += i; + x += i; + i = 0; + } + } + + _new_packet(old_attr, lineno, x, i, srcp); +} + +void PDC_doupdate(void) +{ + PDC_update_rects(); +} + +void PDC_pump_and_peep(void) +{ + SDL_Event win_events[8]; + int count, i; + + /* Pump OS events into the SDL queue without removing anything. */ + SDL_PumpEvents(); + + /* Extract all window events (0x202-0x23F range) without disturbing + keyboard/text events (0x300+ range), which must stay in order. */ + count = SDL_PeepEvents(win_events, 8, SDL_GETEVENT, + SDL_EVENT_WINDOW_FIRST, SDL_EVENT_WINDOW_LAST); + for (i = 0; i < count; i++) + { + if (win_events[i].type == SDL_EVENT_WINDOW_RESTORED || + win_events[i].type == SDL_EVENT_WINDOW_EXPOSED || + win_events[i].type == SDL_EVENT_WINDOW_SHOWN) + { + SDL_UpdateWindowSurface(pdc_window); + rectcount = 0; + } + else + SDL_PushEvent(&win_events[i]); /* e.g. RESIZED, handled in pdckbd.c */ + } +} diff --git a/sdl3/pdcgetsc.c b/sdl3/pdcgetsc.c new file mode 100644 index 000000000..3dd2e1802 --- /dev/null +++ b/sdl3/pdcgetsc.c @@ -0,0 +1,30 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +/* get the cursor size/shape */ + +int PDC_get_cursor_mode(void) +{ + PDC_LOG(("PDC_get_cursor_mode() - called\n")); + + return 0; +} + +/* return number of screen rows */ + +int PDC_get_rows(void) +{ + PDC_LOG(("PDC_get_rows() - called\n")); + + return pdc_sheight / pdc_fheight; +} + +/* return width of screen/viewport */ + +int PDC_get_columns(void) +{ + PDC_LOG(("PDC_get_columns() - called\n")); + + return pdc_swidth / pdc_fwidth; +} diff --git a/sdl3/pdckbd.c b/sdl3/pdckbd.c new file mode 100644 index 000000000..2f85b49c9 --- /dev/null +++ b/sdl3/pdckbd.c @@ -0,0 +1,550 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +#include +#include +#include + +static SDL_Event event; +static SDL_Keycode oldkey; +static MOUSE_STATUS old_mouse_status; + +static const struct +{ + SDL_Keycode keycode; + bool numkeypad; + unsigned short normal; + unsigned short shifted; + unsigned short control; + unsigned short alt; +} key_table[] = +{ +/* keycode keypad normal shifted control alt*/ + {SDLK_LEFT, FALSE, KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT}, + {SDLK_RIGHT, FALSE, KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT}, + {SDLK_UP, FALSE, KEY_UP, KEY_SUP, CTL_UP, ALT_UP}, + {SDLK_DOWN, FALSE, KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN}, + {SDLK_HOME, FALSE, KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME}, + {SDLK_END, FALSE, KEY_END, KEY_SEND, CTL_END, ALT_END}, + {SDLK_PAGEUP, FALSE, KEY_PPAGE, KEY_SPREVIOUS,CTL_PGUP, ALT_PGUP}, + {SDLK_PAGEDOWN,FALSE, KEY_NPAGE, KEY_SNEXT, CTL_PGDN, ALT_PGDN}, + {SDLK_INSERT, FALSE, KEY_IC, KEY_SIC, CTL_INS, ALT_INS}, + {SDLK_DELETE, FALSE, KEY_DC, KEY_SDC, CTL_DEL, ALT_DEL}, + {SDLK_F1, FALSE, KEY_F(1), KEY_F(13), KEY_F(25), KEY_F(37)}, + {SDLK_F2, FALSE, KEY_F(2), KEY_F(14), KEY_F(26), KEY_F(38)}, + {SDLK_F3, FALSE, KEY_F(3), KEY_F(15), KEY_F(27), KEY_F(39)}, + {SDLK_F4, FALSE, KEY_F(4), KEY_F(16), KEY_F(28), KEY_F(40)}, + {SDLK_F5, FALSE, KEY_F(5), KEY_F(17), KEY_F(29), KEY_F(41)}, + {SDLK_F6, FALSE, KEY_F(6), KEY_F(18), KEY_F(30), KEY_F(42)}, + {SDLK_F7, FALSE, KEY_F(7), KEY_F(19), KEY_F(31), KEY_F(43)}, + {SDLK_F8, FALSE, KEY_F(8), KEY_F(20), KEY_F(32), KEY_F(44)}, + {SDLK_F9, FALSE, KEY_F(9), KEY_F(21), KEY_F(33), KEY_F(45)}, + {SDLK_F10, FALSE, KEY_F(10), KEY_F(22), KEY_F(34), KEY_F(46)}, + {SDLK_F11, FALSE, KEY_F(11), KEY_F(23), KEY_F(35), KEY_F(47)}, + {SDLK_F12, FALSE, KEY_F(12), KEY_F(24), KEY_F(36), KEY_F(48)}, + {SDLK_F13, FALSE, KEY_F(13), KEY_F(25), KEY_F(37), KEY_F(49)}, + {SDLK_F14, FALSE, KEY_F(14), KEY_F(26), KEY_F(38), KEY_F(50)}, + {SDLK_F15, FALSE, KEY_F(15), KEY_F(27), KEY_F(39), KEY_F(51)}, + {SDLK_BACKSPACE,FALSE, 0x08, 0x08, CTL_BKSP, ALT_BKSP}, + {SDLK_TAB, FALSE, 0x09, KEY_BTAB, CTL_TAB, ALT_TAB}, + {SDLK_PRINTSCREEN,FALSE,KEY_PRINT, KEY_SPRINT, KEY_PRINT, KEY_PRINT}, + {SDLK_PAUSE, FALSE, KEY_SUSPEND, KEY_SSUSPEND, KEY_SUSPEND, KEY_SUSPEND}, + {SDLK_CLEAR, FALSE, KEY_CLEAR, KEY_CLEAR, KEY_CLEAR, KEY_CLEAR}, + {SDLK_HELP, FALSE, KEY_HELP, KEY_SHELP, KEY_LHELP, KEY_HELP}, + {SDLK_MENU, FALSE, KEY_OPTIONS, KEY_SOPTIONS, KEY_OPTIONS, KEY_OPTIONS}, + {SDLK_ESCAPE, FALSE, 0x1B, 0x1B, 0x1B, ALT_ESC}, + {SDLK_KP_ENTER,TRUE, PADENTER, PADENTER, CTL_PADENTER,ALT_PADENTER}, + {SDLK_KP_PLUS, TRUE, PADPLUS, '+', CTL_PADPLUS, ALT_PADPLUS}, + {SDLK_KP_MINUS,TRUE, PADMINUS, '-', CTL_PADMINUS,ALT_PADMINUS}, + {SDLK_KP_MULTIPLY,TRUE,PADSTAR, '*', CTL_PADSTAR, ALT_PADSTAR}, + {SDLK_KP_DIVIDE,TRUE, PADSLASH, '/', CTL_PADSLASH,ALT_PADSLASH}, + {SDLK_KP_PERIOD,TRUE, PADSTOP, '.', CTL_PADSTOP, ALT_PADSTOP}, + {SDLK_KP_0, TRUE, PAD0, '0', CTL_PAD0, ALT_PAD0}, + {SDLK_KP_1, TRUE, KEY_C1, '1', CTL_PAD1, ALT_PAD1}, + {SDLK_KP_2, TRUE, KEY_C2, '2', CTL_PAD2, ALT_PAD2}, + {SDLK_KP_3, TRUE, KEY_C3, '3', CTL_PAD3, ALT_PAD3}, + {SDLK_KP_4, TRUE, KEY_B1, '4', CTL_PAD4, ALT_PAD4}, + {SDLK_KP_5, TRUE, KEY_B2, '5', CTL_PAD5, ALT_PAD5}, + {SDLK_KP_6, TRUE, KEY_B3, '6', CTL_PAD6, ALT_PAD6}, + {SDLK_KP_7, TRUE, KEY_A1, '7', CTL_PAD7, ALT_PAD7}, + {SDLK_KP_8, TRUE, KEY_A2, '8', CTL_PAD8, ALT_PAD8}, + {SDLK_KP_9, TRUE, KEY_A3, '9', CTL_PAD9, ALT_PAD9}, + {SDLK_VOLUMEUP, FALSE, KEY_VOLUME_UP, KEY_VOLUME_UP, KEY_VOLUME_UP, KEY_VOLUME_UP}, + {SDLK_VOLUMEDOWN, FALSE, KEY_VOLUME_DOWN, KEY_VOLUME_DOWN, KEY_VOLUME_DOWN, KEY_VOLUME_DOWN}, + {SDLK_MUTE, FALSE, KEY_VOLUME_MUTE, KEY_VOLUME_MUTE, KEY_VOLUME_MUTE, KEY_VOLUME_MUTE}, + {SDLK_AGAIN, FALSE, KEY_REDO, KEY_REDO, KEY_REDO, KEY_REDO }, + {SDLK_SCROLLLOCK, FALSE, KEY_SCROLLLOCK, KEY_SCROLLLOCK, KEY_SCROLLLOCK, KEY_SCROLLLOCK }, + {SDLK_UNDO, FALSE, KEY_UNDO, KEY_UNDO, KEY_UNDO, KEY_UNDO }, + {SDLK_MEDIA_NEXT_TRACK, FALSE, KEY_NEXT_TRACK, KEY_NEXT_TRACK, KEY_NEXT_TRACK, KEY_NEXT_TRACK }, + {SDLK_MEDIA_PREVIOUS_TRACK, FALSE, KEY_PREV_TRACK, KEY_PREV_TRACK, KEY_PREV_TRACK, KEY_PREV_TRACK }, + {SDLK_MEDIA_STOP, FALSE, KEY_MEDIA_STOP, KEY_MEDIA_STOP, KEY_MEDIA_STOP, KEY_MEDIA_STOP }, + {SDLK_MEDIA_PLAY, FALSE, KEY_PLAY_PAUSE, KEY_PLAY_PAUSE, KEY_PLAY_PAUSE, KEY_PLAY_PAUSE }, + {0, 0, 0, 0, 0, 0} +}; + +void PDC_set_keyboard_binary(bool on) +{ + INTENTIONALLY_UNUSED_PARAMETER( on); + PDC_LOG(("PDC_set_keyboard_binary() - called\n")); +} + +/* check if a key or mouse event is waiting */ + +static int _stored_key; +static Uint64 _stored_timestamp; + +/* SDL3 event.text.text is a const char * into SDL-internal memory that may be + freed by any subsequent SDL_PollEvent call (even into a local variable). + We copy it here so we can safely walk multi-char IME sequences and avoid + the stale-pointer hang in PDC_flushinp. */ +static char _text_buf[64]; +static int _text_pos; + +bool PDC_check_key(void) +{ + bool haveevent; + + if( _stored_key && SDL_GetTicks( ) > _stored_timestamp + 2) + return( TRUE); + PDC_pump_and_peep(); + + /* Walk remaining bytes from a multi-char IME TEXT_INPUT before polling. */ + if (_text_buf[_text_pos]) + return TRUE; + + haveevent = (bool)SDL_PollEvent(&event); + if (haveevent && event.type == SDL_EVENT_TEXT_INPUT) + { + SDL_strlcpy(_text_buf, event.text.text, sizeof(_text_buf)); + _text_pos = 0; + } + return haveevent; +} + +#ifdef PDC_WIDE +static int _utf8_to_unicode(const char *chstr, size_t *b) +{ + int i, bytes, unicode; + unsigned char byte = chstr[0]; + + if (byte > 0xf0) + { + bytes = 4; + unicode = byte & 0x7; + } + else if (byte > 0xe0) + { + bytes = 3; + unicode = byte & 0xf; + } + else if (byte > 0xc0) + { + bytes = 2; + unicode = byte & 0x1f; + } + else if (byte > 0x80) { + /* starts with a continuation byte; invalid character */ + return -1; + } + else + { + bytes = 1; + unicode = byte; + } + + for (i = 1; i < bytes; i++) + unicode = (unicode << 6) + (chstr[i] & 0x3f); + + *b = bytes; + return unicode; +} +#endif + +/* Handle ALT and CTRL sequences */ +static int _handle_alt_keys(int key) +{ + if (key > 0x7f) + return key; + + if (SP->key_modifiers & PDC_KEY_MODIFIER_CONTROL) + { + if (key >= 'A' && key <= 'Z') key -= 64; + if (key >= 'a' && key <= 'z') key -= 96; + if( key == 3 && !SP->raw_inp) + exit( 0); + } + else if (SP->key_modifiers & PDC_KEY_MODIFIER_ALT) + { + if (key >= 'A' && key <= 'Z') + key += ALT_A - 'A'; + else if (key >= 'a' && key <= 'z') + key += ALT_A - 'a'; + else if (key >= '0' && key <= '9') + key += ALT_0 - '0'; + } + + return key; +} + +static int _process_key_event(void) +{ + int i, key = 0; + static int repeat_count; + static int _key_already_handled = 0; +#ifdef PDC_WIDE + size_t bytes = 0; +#endif + + if( event.key.repeat && event.type == SDL_EVENT_KEY_DOWN) + repeat_count++; + else + repeat_count = 0; + + if (event.type == SDL_EVENT_KEY_UP) + { + switch (event.key.key) + { + case SDLK_LCTRL: + case SDLK_RCTRL: + SP->key_modifiers &= ~PDC_KEY_MODIFIER_CONTROL; + break; + case SDLK_LALT: + case SDLK_RALT: + SP->key_modifiers &= ~PDC_KEY_MODIFIER_ALT; + break; + case SDLK_LSHIFT: + case SDLK_RSHIFT: + SP->key_modifiers &= ~PDC_KEY_MODIFIER_SHIFT; + break; + } + + if (!(SDL_GetModState() & SDL_KMOD_NUM)) + SP->key_modifiers &= ~PDC_KEY_MODIFIER_NUMLOCK; + if( !repeat_count) + SP->key_modifiers &= ~PDC_KEY_MODIFIER_REPEAT; + + if (SP->return_key_modifiers && event.key.key == oldkey) + { + switch (event.key.key) + { + case SDLK_RSHIFT: + return KEY_SHIFT_R; + case SDLK_LSHIFT: + return KEY_SHIFT_L; + case SDLK_RCTRL: + return KEY_CONTROL_R; + case SDLK_LCTRL: + return KEY_CONTROL_L; + case SDLK_RALT: + return KEY_ALT_R; + case SDLK_LALT: + return KEY_ALT_L; + default: + break; + } + } + + return -1; + } + else if (event.type == SDL_EVENT_TEXT_INPUT) + { + int rval; + +#ifdef PDC_WIDE + key = _utf8_to_unicode(_text_buf + _text_pos, &bytes); + _text_pos += (int)bytes; + rval = _handle_alt_keys(key); +#else + key = (unsigned char)_text_buf[_text_pos++]; + rval = (key > 0x7f ? -1 : _handle_alt_keys(key)); +#endif + if (!_text_buf[_text_pos]) + { + /* All text consumed — clear so PDC_check_key won't see it again. */ + _text_buf[0] = '\0'; + _text_pos = 0; + event.type = 0; + } + if( strchr( "/+*-", rval)) /* may actually be PADSLASH, PADPLUS, */ + { /* etc. Wait 2 ms to see if a PADx */ + _stored_key = rval; /* keystroke is coming in. */ + _stored_timestamp = SDL_GetTicks(); /* milliseconds */ + rval = -1; + } + _key_already_handled = rval; + return( rval); + } + + oldkey = event.key.key; + if (SDL_GetModState() & SDL_KMOD_NUM) + SP->key_modifiers |= PDC_KEY_MODIFIER_NUMLOCK; + if( repeat_count) + SP->key_modifiers |= PDC_KEY_MODIFIER_REPEAT; + + switch (event.key.key) + { + case SDLK_LCTRL: + case SDLK_RCTRL: + SP->key_modifiers |= PDC_KEY_MODIFIER_CONTROL; + break; + case SDLK_LALT: + case SDLK_RALT: + SP->key_modifiers |= PDC_KEY_MODIFIER_ALT; + break; + case SDLK_LSHIFT: + case SDLK_RSHIFT: + SP->key_modifiers |= PDC_KEY_MODIFIER_SHIFT; + break; + case SDLK_RETURN: + return 0x0d; + default: + key = event.key.key; + } + + for (i = 0; key_table[i].keycode; i++) + { + if (key_table[i].keycode == event.key.key) + { + if ((event.key.mod & SDL_KMOD_SHIFT) || + (key_table[i].numkeypad && (event.key.mod & SDL_KMOD_NUM))) + { + key = key_table[i].shifted; + /* Is the following really Linux-only? */ + if( (key >= '0' && key <= '9') || strchr( ".+-*/", key)) + key = -1; + } + else if (event.key.mod & SDL_KMOD_CTRL) + { + key = key_table[i].control; + } + else if (event.key.mod & SDL_KMOD_ALT) + { + key = key_table[i].alt; + } + else + { + /* To get here, we ignore all other modifiers */ + key = key_table[i].normal; + } + _stored_key = 0; + + return key; + } + } + + /* SDL with TextInput ignores keys with CTRL */ + if( key) + if( SP->key_modifiers & (PDC_KEY_MODIFIER_CONTROL | PDC_KEY_MODIFIER_ALT)) + { + int rval = _handle_alt_keys( key); + + if( rval == _key_already_handled) + rval = -1; /* don't return this key twice */ + return( rval); + } + return -1; +} + +static int _process_mouse_event(void) +{ + SDL_Keymod keymods; + short shift_flags = 0; + int wheel_x = 0, wheel_y = 0; + + memset(&SP->mouse_status, 0, sizeof(MOUSE_STATUS)); + + keymods = SDL_GetModState(); + + if (keymods & SDL_KMOD_SHIFT) + shift_flags |= BUTTON_SHIFT; + + if (keymods & SDL_KMOD_CTRL) + shift_flags |= BUTTON_CONTROL; + + if (keymods & SDL_KMOD_ALT) + shift_flags |= BUTTON_ALT; + + if (event.type == SDL_EVENT_MOUSE_WHEEL) + { + wheel_x = event.wheel.integer_x; + wheel_y = event.wheel.integer_y; + SDL_GetMouseState( &event.motion.x, &event.motion.y); + } + + SP->mouse_status.x = (event.motion.x - pdc_xoffset) / pdc_fwidth; + SP->mouse_status.y = (event.motion.y - pdc_yoffset) / pdc_fheight; + if( SP->mouse_status.x >= COLS || SP->mouse_status.y >= SP->lines) + return -1; + + if (event.type == SDL_EVENT_MOUSE_MOTION) + { + int i; + + if (!event.motion.state || + (SP->mouse_status.x == old_mouse_status.x && + SP->mouse_status.y == old_mouse_status.y)) + return -1; + + SP->mouse_status.changes = PDC_MOUSE_MOVED; + + for (i = 0; i < 3; i++) + { + if (event.motion.state & SDL_BUTTON_MASK(i + 1)) + { + SP->mouse_status.button[i] = BUTTON_MOVED | shift_flags; + SP->mouse_status.changes |= (1 << i); + } + } + } + else if (event.type == SDL_EVENT_MOUSE_WHEEL) + { + if (wheel_y > 0) + SP->mouse_status.changes = PDC_MOUSE_WHEEL_UP; + else if (wheel_y < 0) + SP->mouse_status.changes = PDC_MOUSE_WHEEL_DOWN; + else if (wheel_x > 0) + SP->mouse_status.changes = PDC_MOUSE_WHEEL_RIGHT; + else if (wheel_x < 0) + SP->mouse_status.changes = PDC_MOUSE_WHEEL_LEFT; + else + return -1; + + return KEY_MOUSE; + } + else + { + short action = (event.button.down == true) ? + BUTTON_PRESSED : BUTTON_RELEASED; + Uint8 btn = event.button.button; + + if (btn < 1 || btn > 3) + return -1; + + /* check for a click -- a press followed immediately by a release */ + + if (action == BUTTON_PRESSED && SP->mouse_wait) + { + SDL_Event rel; + + while (action != BUTTON_TRIPLE_CLICKED && SDL_WaitEventTimeout(&rel, SP->mouse_wait)) + { + if (rel.type == SDL_EVENT_MOUSE_BUTTON_UP && rel.button.button == btn) + { + if (action == BUTTON_PRESSED) + action = BUTTON_CLICKED; + else if (action == BUTTON_CLICKED) + action = BUTTON_DOUBLE_CLICKED; + else if (action == BUTTON_DOUBLE_CLICKED) + action = BUTTON_TRIPLE_CLICKED; + } + else if(rel.type != SDL_EVENT_MOUSE_BUTTON_DOWN || rel.button.button != btn) + { + SDL_PushEvent(&rel); + break; + } + } + } + + btn--; + + SP->mouse_status.button[btn] = action | shift_flags; + SP->mouse_status.changes = (1 << btn); + } + + old_mouse_status = SP->mouse_status; + + return KEY_MOUSE; +} + +/* return the next available key or mouse event */ + +int PDC_get_key(void) +{ + if( _stored_key && SDL_GetTicks( ) > _stored_timestamp + 2) + { /* One of +/-* was hit and not cancelled */ + const int key = _stored_key; /* out within 2 ms by a PADPLUS, */ + /* PADSLASH, PADMINUS, or PADSTAR. */ + _stored_key = 0; + return( key); + } + + switch (event.type) + { + case SDL_EVENT_QUIT: + if( !PDC_get_function_key( FUNCTION_KEY_SHUT_DOWN)) + { + exit(1); + } + return PDC_get_function_key( FUNCTION_KEY_SHUT_DOWN); + case SDL_EVENT_WINDOW_RESIZED: + { + const int prev_rows = pdc_sheight / pdc_fheight; + const int prev_cols = pdc_swidth / pdc_fwidth; + bool size_actually_changed; + + pdc_screen = SDL_GetWindowSurface(pdc_window); + pdc_sheight = pdc_screen->h - pdc_xoffset; + pdc_swidth = pdc_screen->w - pdc_yoffset; + size_actually_changed = ( pdc_sheight / pdc_fheight != prev_rows || + pdc_swidth / pdc_fwidth != prev_cols); + if( size_actually_changed && curscr) + { + touchwin(curscr); + wrefresh(curscr); + } + if( size_actually_changed && !SP->resized) + { + SP->resized = TRUE; + return KEY_RESIZE; + } + } + break; + case SDL_EVENT_MOUSE_MOTION: + SDL_ShowCursor(); + /* FALLTHRU */ + case SDL_EVENT_MOUSE_BUTTON_UP: + case SDL_EVENT_MOUSE_BUTTON_DOWN: + case SDL_EVENT_MOUSE_WHEEL: + oldkey = SDLK_SPACE; + return _process_mouse_event(); + case SDL_EVENT_KEY_UP: + case SDL_EVENT_KEY_DOWN: + case SDL_EVENT_TEXT_INPUT: + PDC_mouse_set(); + return _process_key_event(); + } + + return -1; +} + +/* discard any pending keyboard or mouse input -- this is the core + routine for flushinp() */ + +void PDC_flushinp(void) +{ + PDC_LOG(("PDC_flushinp() - called\n")); + + while (PDC_check_key()) + PDC_get_key(); +} + +bool PDC_has_mouse(void) +{ + return TRUE; +} + +int PDC_mouse_set(void) +{ + if(SP->_trap_mbe) + SDL_ShowCursor(); + else + SDL_HideCursor(); + + return OK; +} + +int PDC_modifiers_set(void) +{ + return OK; +} diff --git a/sdl3/pdcscrn.c b/sdl3/pdcscrn.c new file mode 100644 index 000000000..a6be33cf6 --- /dev/null +++ b/sdl3/pdcscrn.c @@ -0,0 +1,541 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +#include +#include +#ifndef PDC_WIDE +# include "../common/font437.h" +#endif +#include "../common/iconbmp.h" +#include "../common/pdccolor.h" +#include "../common/pdccolor.c" + +#ifdef PDC_WIDE +# ifndef PDC_FONT_PATH +# ifdef _WIN32 +# define PDC_FONT_PATH "C:/Windows/Fonts/consola.ttf" +# elif defined(__APPLE__) +# define PDC_FONT_PATH "/System/Library/Fonts/Menlo.ttc" +# else +# define PDC_FONT_PATH "/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf" +# define PDC_FONT_PATH2 "/usr/share/fonts/dejavu-sans-mono-fonts/DejaVuSansMono.ttf" +# endif +# endif +TTF_Font *pdc_ttffont = NULL; +int pdc_font_size = +# ifdef _WIN32 + 16; +# else + 17; +# endif +int pdc_sdl_render_mode = PDC_SDL_RENDER_BLENDED; +#endif + +SDL_Window *pdc_window = NULL; +SDL_Surface *pdc_screen = NULL, *pdc_font = NULL, *pdc_icon = NULL, + *pdc_back = NULL, *pdc_tileback = NULL; +int pdc_sheight = 0, pdc_swidth = 0, pdc_yoffset = 0, pdc_xoffset = 0; + +int pdc_fheight, pdc_fwidth, pdc_fthick, pdc_flastc; +bool pdc_own_window; + +#ifndef PDC_WIDE + +/* Load a bitmap from bmp_rw and also determine its palette size. + ->format->palette->ncolors may be greater than the actual size. + To get the real palette size, some custom bitmap parsing is needed. */ + +static SDL_Surface *_load_bmp_and_palette_size(SDL_IOStream *bmp_ios, int *palette_size) +{ + SDL_Surface *bmp = NULL; + Sint64 start_offset; + + *palette_size = -1; + if (!bmp_ios) + return( NULL); + + start_offset = SDL_TellIO(bmp_ios); + + /* Non-monochromatic bitmaps only */ + const SDL_Palette *palette; + if( start_offset >= 0 && (bmp = SDL_LoadBMP_IO(bmp_ios, 0)) != NULL + && ((palette = SDL_GetSurfacePalette(bmp)))) + { + *palette_size = palette->ncolors; + + /* Offsets from https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_(bitmap_information_header) */ + if (SDL_SeekIO(bmp_ios, start_offset + 14, SDL_IO_SEEK_SET) >= 0) + { + Uint32 header_size; + SDL_ReadU32LE(bmp_ios, &header_size); + + /* If the info header is at least 40 bytes in size, palette size + information is available. */ + if (header_size >= 40 + && SDL_SeekIO(bmp_ios, start_offset + 46, SDL_IO_SEEK_SET) >= 0) + { + Uint32 num_colors; + SDL_ReadU32LE(bmp_ios, &num_colors); + + if (num_colors > 0 && num_colors <= (Uint32)INT_MAX && + (int)num_colors <= palette->ncolors) + *palette_size = (int)num_colors; + else + { + /* Fall back to calculating num_colors from bits_per_pixel. */ + + if (SDL_SeekIO(bmp_ios, start_offset + 28, SDL_IO_SEEK_SET) >= 0) + { + Uint16 bits_per_pixel; + SDL_ReadU16LE(bmp_ios, &bits_per_pixel); + + num_colors = (Uint32)1 << bits_per_pixel; + if (num_colors > 0 && num_colors <= (Uint32)INT_MAX && + (int)num_colors <= palette->ncolors) + *palette_size = (int)num_colors; + } + } + } + } + } + + SDL_CloseIO(bmp_ios); + return bmp; +} + +#endif + +static void _clean(void) +{ +#ifdef PDC_WIDE + if (pdc_ttffont) + { + TTF_CloseFont(pdc_ttffont); + TTF_Quit(); + pdc_ttffont = NULL; + } +#endif + if( pdc_tileback) + SDL_DestroySurface(pdc_tileback); + if( pdc_back) + SDL_DestroySurface(pdc_back); + if( pdc_icon) + SDL_DestroySurface(pdc_icon); + if( pdc_font) + SDL_DestroySurface(pdc_font); + /* pdc_screen is owned by pdc_window (SDL_GetWindowSurface); do NOT call + SDL_DestroySurface on it — SDL_DestroyWindow handles it. */ + pdc_screen = pdc_tileback = pdc_back = pdc_icon = pdc_font = NULL; + if( pdc_own_window && pdc_window) + { + SDL_StopTextInput(pdc_window); + SDL_DestroyWindow(pdc_window); + pdc_window = NULL; + } + + SDL_Quit(); + pdc_sheight = pdc_swidth = 0; +} + +void PDC_retile(void) +{ + if (pdc_tileback) + SDL_DestroySurface(pdc_tileback); + + pdc_tileback = SDL_ConvertSurface(pdc_screen, pdc_screen->format); + if (pdc_tileback == NULL) + return; + + if (pdc_back) + { + SDL_Rect dest; + + dest.y = 0; + + while (dest.y < pdc_tileback->h) + { + dest.x = 0; + + while (dest.x < pdc_tileback->w) + { + SDL_BlitSurface(pdc_back, 0, pdc_tileback, &dest); + dest.x += pdc_back->w; + } + + dest.y += pdc_back->h; + } + + SDL_BlitSurface(pdc_tileback, 0, pdc_screen, 0); + } +} + +void PDC_scr_close(void) +{ + PDC_LOG(("PDC_scr_close() - called\n")); +} + +void PDC_scr_free(void) +{ + PDC_free_palette( ); + _clean( ); +} + + +/* find the display where the mouse pointer is */ + +SDL_DisplayID _get_displaynum(void) +{ + int num_displays; + SDL_DisplayID *displays = SDL_GetDisplays(&num_displays); + + if (displays && num_displays > 1) + { + float xpos, ypos; + SDL_GetGlobalMouseState(&xpos, &ypos); + + for (int i = 0; i < num_displays; i++) + { + SDL_Rect size; + + if (SDL_GetDisplayBounds(displays[i], &size) && + size.x <= xpos && xpos < size.x + size.w && + size.y <= ypos && ypos < size.y + size.h) + { + SDL_DisplayID id = displays[i]; + SDL_free(displays); + return id; + } + } + } + + SDL_free(displays); + return 0; +} + +/* open the physical screen -- miscellaneous initialization */ + +int PDC_scr_open(void) +{ + SDL_Event event; +#ifdef PDC_WIDE + const char *ptsz, *fname; +#endif + + PDC_LOG(("PDC_scr_open() - called\n")); + + pdc_own_window = !pdc_window; + + if (pdc_own_window) + { + if (!SDL_Init(SDL_INIT_VIDEO|SDL_INIT_EVENTS)) + { + fprintf(stderr, "Could not start SDL: %s\n", SDL_GetError()); + return ERR; + } + + atexit(_clean); + } + +#ifdef PDC_WIDE + if (!TTF_Init()) + { + fprintf(stderr, "Could not start SDL_TTF: %s\n", SDL_GetError()); + return ERR; + } + + ptsz = getenv("PDC_FONT_SIZE"); + if (ptsz != NULL) + pdc_font_size = atoi(ptsz); + if (pdc_font_size <= 0) + pdc_font_size = 18; + + fname = getenv("PDC_FONT"); + pdc_ttffont = TTF_OpenFont(fname ? fname : PDC_FONT_PATH, + pdc_font_size); +# ifdef PDC_FONT_PATH2 + if (!pdc_ttffont && !fname) + { + pdc_ttffont = TTF_OpenFont(PDC_FONT_PATH2, + pdc_font_size); + } +# endif + + if (!pdc_ttffont) + { + if (fname) + fprintf(stderr, "Could not load specified font: %s\n",fname); + else + { +# ifdef PDC_FONT_PATH2 + fprintf(stderr, "Could not load default font: %s or %s\n", + PDC_FONT_PATH, PDC_FONT_PATH2); +# else + fprintf(stderr, "Could not load default font: %s\n", + PDC_FONT_PATH); +# endif + + } + return ERR; + } + + TTF_SetFontKerning(pdc_ttffont, 0); + TTF_SetFontHinting(pdc_ttffont, TTF_HINTING_MONO); + + SP->mono = FALSE; +#else + SDL_Palette *palette = NULL; + + if (pdc_font) + { + palette = SDL_GetSurfacePalette(pdc_font); + if (palette) + pdc_flastc = palette->ncolors - 1; + } + else + { + int palette_size; + + const char *fname = getenv("PDC_FONT"); + SDL_IOStream *file = SDL_IOFromFile(fname ? fname : "pdcfont.bmp", "r"); + if (file) + pdc_font = _load_bmp_and_palette_size(file, &palette_size); + + if (!pdc_font) + pdc_font = _load_bmp_and_palette_size( + SDL_IOFromMem(font437, sizeof(font437)), &palette_size); + + if (!pdc_font) + { + fprintf(stderr, "Could not load font\n"); + return ERR; + } + + palette = SDL_GetSurfacePalette(pdc_font); + if (palette) + pdc_flastc = palette_size - 1; + } + + SP->mono = !palette; +#endif + + if (!SP->mono && !pdc_back) + { + const char *bname = getenv("PDC_BACKGROUND"); + pdc_back = SDL_LoadBMP(bname ? bname : "pdcback.bmp"); + } + + if (!SP->mono && (pdc_back || !pdc_own_window)) + { + SP->orig_attr = TRUE; + SP->orig_fore = COLOR_WHITE; + SP->orig_back = -1; + } + else + SP->orig_attr = FALSE; + +#ifdef PDC_WIDE + TTF_GetStringSize(pdc_ttffont, "W", 1, &pdc_fwidth, &pdc_fheight); + pdc_fthick = pdc_font_size / 20 + 1; +#else + pdc_fheight = pdc_font->h / 8; + pdc_fwidth = pdc_font->w / 32; + pdc_fthick = 1; +#endif + + if (pdc_own_window && !pdc_icon) + { + const char *iname = getenv("PDC_ICON"); + pdc_icon = SDL_LoadBMP(iname ? iname : "pdcicon.bmp"); + + if (!pdc_icon) + pdc_icon = SDL_LoadBMP_IO(SDL_IOFromMem(iconbmp, + sizeof(iconbmp)), 0); + } + + if (pdc_own_window) + { + if( !pdc_sheight) + { + const char *env = getenv("PDC_LINES"); + pdc_sheight = (env ? atoi(env) : 25); + } + pdc_sheight *= pdc_fheight; + + if( !pdc_swidth) + { + const char *env = getenv("PDC_COLS"); + pdc_swidth = (env ? atoi(env) : 80); + } + pdc_swidth *= pdc_fwidth; + + pdc_window = SDL_CreateWindow("PDCurses", + pdc_swidth, pdc_sheight, SDL_WINDOW_RESIZABLE); + + if (pdc_window == NULL) + { + fprintf(stderr, "Could not open SDL window: %s\n", SDL_GetError()); + return ERR; + } + + SDL_SetWindowIcon(pdc_window, pdc_icon); + } + + /* Events must be pumped before calling SDL_GetWindowSurface, or + initial modifiers (e.g. numlock) will be ignored and out-of-sync. */ + + SDL_PumpEvents(); + + /* Wait until window is exposed before getting surface */ + + while (SDL_PollEvent(&event)) + if (SDL_EVENT_WINDOW_EXPOSED == event.type) + break; + + if( !pdc_screen) + { + pdc_screen = SDL_GetWindowSurface(pdc_window); + + if( !pdc_screen) + { + fprintf(stderr, "Could not open SDL window surface: %s\n", + SDL_GetError()); + return ERR; + } + } + + if (!pdc_sheight) + pdc_sheight = pdc_screen->h - pdc_yoffset; + + if (!pdc_swidth) + pdc_swidth = pdc_screen->w - pdc_xoffset; + + if (SP->orig_attr) + PDC_retile(); + + SDL_StartTextInput(pdc_window); + + PDC_mouse_set(); + + SP->mouse_wait = PDC_CLICK_PERIOD; + SP->audible = FALSE; + + SP->termattrs = A_COLOR | WA_UNDERLINE | WA_LEFT | WA_RIGHT | + WA_REVERSE | WA_STRIKEOUT | WA_TOP | WA_BLINK | WA_DIM | WA_BOLD; +#ifdef PDC_WIDE + SP->termattrs |= WA_ITALIC; +#endif + + PDC_reset_prog_mode(); + + return OK; +} + +/* the core of resize_term() */ + +int PDC_resize_screen(int nlines, int ncols) +{ + if( !stdscr) /* Specifying the initial screen size */ + { /* before calling initscr(). */ + pdc_sheight = nlines; + pdc_swidth = ncols; + return OK; + } + + if (!pdc_own_window) + return ERR; + + if (nlines && ncols) + { + SDL_Rect max; + int top, left, bottom, right; + SDL_DisplayID display = SDL_GetDisplayForWindow(pdc_window); + + if (display && SDL_GetDisplayUsableBounds(display, &max) && + SDL_GetWindowBordersSize(pdc_window, &top, &left, &bottom, &right)) + { + max.h -= top + bottom; + max.w -= left + right; + + while (nlines * pdc_fheight > max.h) + nlines--; + while (ncols * pdc_fwidth > max.w) + ncols--; + } + + pdc_sheight = nlines * pdc_fheight; + pdc_swidth = ncols * pdc_fwidth; + + SDL_SetWindowSize(pdc_window, pdc_swidth, pdc_sheight); + pdc_screen = SDL_GetWindowSurface(pdc_window); + } + + if (pdc_tileback) + PDC_retile(); + + return OK; +} + +void PDC_reset_prog_mode(void) +{ + PDC_LOG(("PDC_reset_prog_mode() - called.\n")); + + PDC_flushinp(); +} + +void PDC_reset_shell_mode(void) +{ + PDC_LOG(("PDC_reset_shell_mode() - called.\n")); + + PDC_flushinp(); +} + +void PDC_restore_screen_mode(int i) +{ + INTENTIONALLY_UNUSED_PARAMETER( i); +} + +void PDC_save_screen_mode(int i) +{ + INTENTIONALLY_UNUSED_PARAMETER( i); +} + +bool PDC_can_change_color(void) +{ + return TRUE; +} + +int PDC_color_content( int color, int *red, int *green, int *blue) +{ + const PACKED_RGB col = PDC_get_palette_entry( color); + + *red = DIVROUND( Get_RValue(col) * 1000, 255); + *green = DIVROUND( Get_GValue(col) * 1000, 255); + *blue = DIVROUND( Get_BValue(col) * 1000, 255); + + return OK; +} + +int PDC_init_color( int color, int red, int green, int blue) +{ + const PACKED_RGB new_rgb = PACK_RGB(DIVROUND(red * 255, 1000), + DIVROUND(green * 255, 1000), + DIVROUND(blue * 255, 1000)); + + if( !PDC_set_palette_entry( color, new_rgb)) + curscr->_clear = TRUE; + return OK; +} + +/* Does nothing in the SDL flavors of PDCurses. That may change, eventually, +allowing one to limit the range of user-resizable windows. See X11 or Win32a +versions of this function for details. */ + +void PDC_set_resize_limits( const int new_min_lines, const int new_max_lines, + const int new_min_cols, const int new_max_cols) +{ + INTENTIONALLY_UNUSED_PARAMETER( new_min_lines); + INTENTIONALLY_UNUSED_PARAMETER( new_max_lines); + INTENTIONALLY_UNUSED_PARAMETER( new_min_cols); + INTENTIONALLY_UNUSED_PARAMETER( new_max_cols); +} diff --git a/sdl3/pdcsdl.h b/sdl3/pdcsdl.h new file mode 100644 index 000000000..8ad07f5d7 --- /dev/null +++ b/sdl3/pdcsdl.h @@ -0,0 +1,41 @@ +/* PDCurses */ + +#if defined( PDC_FORCE_UTF8) && !defined( PDC_WIDE) + #define PDC_WIDE +#endif + +#include +#ifdef PDC_WIDE +# include +#endif + +#include + +#ifdef PDC_WIDE +PDCEX TTF_Font *pdc_ttffont; +PDCEX int pdc_font_size; +#define PDC_SDL_RENDER_SOLID 1 +#define PDC_SDL_RENDER_SHADED 2 +#define PDC_SDL_RENDER_BLENDED 3 + +PDCEX int pdc_sdl_render_mode; +#endif +PDCEX SDL_Window *pdc_window; +PDCEX SDL_Surface *pdc_screen, *pdc_font, *pdc_icon, *pdc_back; +PDCEX int pdc_sheight, pdc_swidth, pdc_yoffset, pdc_xoffset; + +extern SDL_Surface *pdc_tileback; /* used to regenerate the background + of "transparent" cells */ +extern int pdc_fheight, pdc_fwidth; /* font height and width */ +extern int pdc_fthick; /* thickness for highlights and + rendered ACS glyphs */ +extern int pdc_flastc; /* font palette's last color + (treated as the foreground) */ +extern bool pdc_own_window; /* if pdc_window was not set + before initscr(), PDCurses is + responsible for (owns) it */ + +PDCEX void PDC_update_rects(void); +PDCEX void PDC_retile(void); + +extern void PDC_pump_and_peep(void); diff --git a/sdl3/pdcsetsc.c b/sdl3/pdcsetsc.c new file mode 100644 index 000000000..535381c7f --- /dev/null +++ b/sdl3/pdcsetsc.c @@ -0,0 +1,99 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +/*man-start************************************************************** + +pdcsetsc +-------- + +### Synopsis + + int PDC_set_blink(bool blinkon); + int PDC_set_bold(bool boldon); + void PDC_set_title(const char *title); + +### Description + + PDC_set_blink() toggles whether the A_BLINK attribute sets an actual + blink mode (TRUE), or sets the background color to high intensity + (FALSE). The default is platform-dependent (FALSE in most cases). It + returns OK if it could set the state to match the given parameter, + ERR otherwise. + + PDC_set_bold() toggles whether the A_BOLD attribute selects an actual + bold font (TRUE), or sets the foreground color to high intensity + (FALSE). It returns OK if it could set the state to match the given + parameter, ERR otherwise. + + PDC_set_title() sets the title of the window in which the curses + program is running. This function may not do anything on some + platforms. + +### Portability + X/Open ncurses NetBSD + PDC_set_blink - - - + PDC_set_title - - - + +**man-end****************************************************************/ + +int PDC_curs_set(int visibility) +{ + int ret_vis; + + PDC_LOG(("PDC_curs_set() - called: visibility=%d\n", visibility)); + + ret_vis = SP->visibility; + + SP->visibility = visibility; + + PDC_gotoyx(SP->cursrow, SP->curscol); + + return ret_vis; +} + +void PDC_set_title(const char *title) +{ + PDC_LOG(("PDC_set_title() - called:<%s>\n", title)); + + SDL_SetWindowTitle(pdc_window, title); +} + +int PDC_set_blink(bool blinkon) +{ + if (!SP) + return ERR; + + if (SP->color_started) /* see ../common/pdccolor.txt */ + COLORS = 256 + (256 * 256 * 256); + + if (blinkon) + { + if (!(SP->termattrs & A_BLINK)) + SP->termattrs |= A_BLINK; + } + else + { + if (SP->termattrs & A_BLINK) + SP->termattrs &= ~A_BLINK; + } + + return OK; +} + +int PDC_set_bold(bool boldon) +{ + if (!SP) + return ERR; + +#ifdef PDC_WIDE + if (boldon) + SP->termattrs |= A_BOLD; + else + SP->termattrs &= ~A_BOLD; + + return OK; +#else + return boldon ? ERR : OK; +#endif +} diff --git a/sdl3/pdcutil.c b/sdl3/pdcutil.c new file mode 100644 index 000000000..e3d68238a --- /dev/null +++ b/sdl3/pdcutil.c @@ -0,0 +1,35 @@ +/* PDCurses */ + +#include "pdcsdl.h" + +void PDC_beep(void) +{ + PDC_LOG(("PDC_beep() - called\n")); +} + +void PDC_check_for_blinking( void); + +void PDC_napms(int ms) +{ + PDC_LOG(("PDC_napms() - called: ms=%d\n", ms)); + + while (ms > 50) + { + PDC_pump_and_peep(); + SDL_Delay(50); + ms -= 50; + PDC_check_for_blinking( ); + PDC_doupdate(); + } + PDC_pump_and_peep(); + SDL_Delay(ms); + PDC_check_for_blinking( ); + PDC_doupdate(); +} + +const char *PDC_sysname(void) +{ + return "SDL3"; +} + +enum PDC_port PDC_port_val = PDC_PORT_SDL3; diff --git a/sdl3/sdltest.c b/sdl3/sdltest.c new file mode 100644 index 000000000..450cb6054 --- /dev/null +++ b/sdl3/sdltest.c @@ -0,0 +1,90 @@ +/* Here's a simple example of combining SDL and PDCurses functionality. + The top portion of the window is devoted to SDL, with a four-line + (assuming the default 8x16 font) stdscr at the bottom. +*/ + +#define SDL_MAIN_HANDLED + +#include +#include +#include +#include + +#define INTENTIONALLY_UNUSED_PARAMETER( param) (void)(param) + +#include "pdcsdl.h" + +int main(int argc, char **argv) +{ + char inp[60]; + int i, j; + const unsigned seed = (unsigned)time((time_t *)0); + + srand(seed); + + /* Initialize SDL */ + + if (!SDL_Init(SDL_INIT_VIDEO)) + exit(1); + +#ifdef PDC_WIDE + if( argc > 1) + { + pdc_sdl_render_mode = atoi( argv[1]); + } +#else + INTENTIONALLY_UNUSED_PARAMETER( argc); + INTENTIONALLY_UNUSED_PARAMETER( argv); +#endif + + atexit(SDL_Quit); + + pdc_window = SDL_CreateWindow("PDCurses for SDL", 640, 480, 0); + pdc_screen = SDL_GetWindowSurface(pdc_window); + + /* Initialize PDCurses */ + + pdc_yoffset = 416; /* 480 - 4 * 16 */ + + initscr(); + start_color(); + scrollok(stdscr, TRUE); + + PDC_set_title("PDCurses for SDL"); + + /* Do some SDL stuff */ + + for (i = 640, j = 416; j; i -= 2, j -= 2) + { + SDL_Rect dest; + + dest.x = (640 - i) / 2; + dest.y = (416 - j) / 2; + dest.w = i; + dest.h = j; + + SDL_FillSurfaceRect(pdc_screen, &dest, + SDL_MapSurfaceRGB(pdc_screen, rand() % 256, + rand() % 256, rand() % 256)); + } + + SDL_UpdateWindowSurface(pdc_window); + + /* Do some curses stuff */ + + init_pair(1, COLOR_WHITE + 8, COLOR_BLUE); + bkgd(COLOR_PAIR(1)); + + addstr("This is a demo of "); + attron(A_UNDERLINE); + addstr("PDCurses for SDL"); + attroff(A_UNDERLINE); + addstr(".\nYour comments here: "); + getnstr(inp, 59); + addstr("Press any key to exit."); + + getch(); + endwin(); + + return 0; +} diff --git a/sdl3/versions.mif b/sdl3/versions.mif new file mode 100644 index 000000000..de9354c49 --- /dev/null +++ b/sdl3/versions.mif @@ -0,0 +1,2 @@ +SDLBASE = C:\SDL3-3.4.10 +TTFBASE = C:\SDL3_ttf-3.2.2