diff --git a/wingui/.gitignore b/wingui/.gitignore new file mode 100644 index 000000000..edf81c4a2 --- /dev/null +++ b/wingui/.gitignore @@ -0,0 +1,10 @@ +*.a +*.dll +*.err +*.exe +*.exp +*.lib +*.map +*.o +*.obj +*.res diff --git a/wingui/CMakeLists.txt b/wingui/CMakeLists.txt new file mode 100644 index 000000000..900f8025b --- /dev/null +++ b/wingui/CMakeLists.txt @@ -0,0 +1,26 @@ +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(wingui VERSION "${PROJECT_VERSION}" LANGUAGES C) + +INCLUDE(project_common) + +demo_app(../demos version) +demo_app(../demos firework) +demo_app(../demos ozdemo) +demo_app(../demos ptest) +demo_app(../demos rain) +demo_app(../demos testcurs) +demo_app(../demos tuidemo) +demo_app(../demos worm) +demo_app(../demos xmas) + +IF(WIN32) + SET_TARGET_PROPERTIES(${PROJECT_NAME}_newtest PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS") +ENDIF(WIN32) + +SET(CPACK_COMPONENTS_ALL applications) diff --git a/wingui/Makefile.bcc b/wingui/Makefile.bcc new file mode 100644 index 000000000..c28cd03dd --- /dev/null +++ b/wingui/Makefile.bcc @@ -0,0 +1,85 @@ +# Borland MAKE Makefile for PDCurses library - Win32 BC++ 4.0+ +# +# Usage: make -f [path\]Makefile.bcc [DEBUG=] [WIDE=] [UTF8=] [target] +# +# where target can be any of: +# [all|demos|pdcurses.lib|testcurs.exe...] + +O = obj + +!ifndef PDCURSES_SRCDIR +PDCURSES_SRCDIR = .. +!endif + +!include $(PDCURSES_SRCDIR)\version.mif +!include $(PDCURSES_SRCDIR)\libobjs.mif + +osdir = $(PDCURSES_SRCDIR)\wingui + +CC = bcc32 -q + +!ifdef DEBUG +CFLAGS = -N -v -y -DPDCDEBUG +!else +CFLAGS = -O +!endif + +!ifdef WIDE +WIDEOPT = -DPDC_WIDE +!endif + +!ifdef UTF8 +UTF8OPT = -DPDC_FORCE_UTF8 +!endif + +BUILD = $(CC) -I$(PDCURSES_SRCDIR) -c -Tpe -w32 $(CFLAGS) -w-par \ +$(WIDEOPT) $(UTF8OPT) + +LIBEXE = tlib /C /E /0 /a + +LIBCURSES = pdcurses.lib + +all: $(LIBCURSES) $(DEMOS) + +clean: + -del *.obj + -del *.lib + -del *.tds + -del *.exe + +$(LIBCURSES) : $(LIBOBJS) $(PDCOBJS) + -del $@ + $(LIBEXE) $@ $(LIBOBJS) $(PDCOBJS) + -copy $(LIBCURSES) panel.lib + +.autodepend + +{$(srcdir)\}.c.obj: + $(BUILD) $< + +{$(osdir)\}.c.obj: + $(BUILD) $< + +{$(demodir)\}.c.obj: + $(BUILD) $< + +.c.obj: + $(BUILD) $< + +.obj.exe: + $(CC) -e$@ $** $(LIBCURSES) + +tuidemo.exe: tuidemo.obj tui.obj $(LIBCURSES) + $(CC) -e$@ $** + +tui.obj: $(demodir)\tui.c $(demodir)\tui.h $(PDCURSES_CURSES_H) + $(BUILD) -I$(demodir) $(demodir)\tui.c + +tuidemo.obj: $(demodir)\tuidemo.c $(PDCURSES_CURSES_H) + $(BUILD) -I$(demodir) $(demodir)\tuidemo.c + +PLATFORM1 = Borland C++ Win32 +PLATFORM2 = Borland C/C++ 5.5 for Win32 +ARCNAME = pdc$(VER)_bcc_w32 + +!include $(PDCURSES_SRCDIR)\makedist.mif diff --git a/wingui/Makefile.dmc b/wingui/Makefile.dmc new file mode 100644 index 000000000..d1e654965 --- /dev/null +++ b/wingui/Makefile.dmc @@ -0,0 +1,248 @@ +# Makefile for PDCurses library - WIN32 Digital Mars +# +# Usage: make -f Makefile.dmc [target] +# +# where target can be any of: +# [all|demos|pdcurses.lib|testcurs.exe...] + +PDCURSES_SRCDIR = .. + +PDCURSES_CURSES_H = $(PDCURSES_SRCDIR)\curses.h +PDCURSES_CURSPRIV_H = $(PDCURSES_SRCDIR)\curspriv.h +PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) + +PANEL_HEADER = $(PDCURSES_SRCDIR)\panel.h + +srcdir = $(PDCURSES_SRCDIR)\pdcurses +osdir = $(PDCURSES_SRCDIR)\wingui +demodir = $(PDCURSES_SRCDIR)\demos + +PDCURSES_WIN_H = $(osdir)\pdcwin.h + +CC = dmc + +CFLAGS = -c -o+space -Nc + +# CPPFLAGS = -I$(PDCURSES_SRCDIR) +CPPFLAGS = -I$(PDCURSES_SRCDIR) -DPDC_WIDE + +LINK = dmc +LIBEXE = lib + +LIBCURSES = pdcurses.lib + +BUILD = $(CC) $(CFLAGS) $(CPPFLAGS) + +DEMOS = testcurs.exe ozdemo.exe xmas.exe tuidemo.exe \ +firework.exe ptest.exe rain.exe worm.exe + +all: $(LIBCURSES) $(DEMOS) + +clean: + -del *.obj + -del *.lib + -del *.exe + -del *.map + -del advapi32.def + +LIBOBJS = addch.obj addchstr.obj addstr.obj attr.obj beep.obj bkgd.obj \ +border.obj clear.obj color.obj delch.obj deleteln.obj getch.obj \ +getstr.obj getyx.obj inch.obj inchstr.obj initscr.obj inopts.obj \ +insch.obj insstr.obj instr.obj kernel.obj keyname.obj mouse.obj move.obj \ +outopts.obj overlay.obj pad.obj panel.obj printw.obj refresh.obj \ +scanw.obj scr_dump.obj scroll.obj slk.obj termattr.obj \ +touch.obj util.obj window.obj debug.obj + +PDCOBJS = pdcclip.obj pdcdisp.obj pdcgetsc.obj pdckbd.obj pdcscrn.obj \ +pdcsetsc.obj pdcutil.obj + +DEMOOBJS = testcurs.obj ozdemo.obj xmas.obj tuidemo.obj tui.obj \ +firework.obj ptest.obj rain.obj worm.obj + +LIBS = advapi32.lib gdi32.lib user32.lib shell32.lib comdlg32.lib + +$(LIBOBJS) $(PDCOBJS) : $(PDCURSES_HEADERS) +$(PDCOBJS) : $(PDCURSES_WIN_H) +panel.obj ptest.obj: $(PANEL_HEADER) + +$(DEMOOBJS) : $(PDCURSES_CURSES_H) +$(DEMOS) : $(LIBCURSES) + +$(LIBCURSES) : $(LIBOBJS) $(PDCOBJS) + $(LIBEXE) -c $@ $(LIBOBJS) $(PDCOBJS) + -copy $(LIBCURSES) panel.lib + +SRCBUILD = $(BUILD) $(srcdir)\$*.c +OSBUILD = $(BUILD) $(osdir)\$*.c +DEMOBUILD = $(LINK) $(CPPFLAGS) -o+space $@ $** + +addch.obj: $(srcdir)\addch.c + $(SRCBUILD) + +addchstr.obj: $(srcdir)\addchstr.c + $(SRCBUILD) + +addstr.obj: $(srcdir)\addstr.c + $(SRCBUILD) + +attr.obj: $(srcdir)\attr.c + $(SRCBUILD) + +beep.obj: $(srcdir)\beep.c + $(SRCBUILD) + +bkgd.obj: $(srcdir)\bkgd.c + $(SRCBUILD) + +border.obj: $(srcdir)\border.c + $(SRCBUILD) + +clear.obj: $(srcdir)\clear.c + $(SRCBUILD) + +color.obj: $(srcdir)\color.c + $(SRCBUILD) + +delch.obj: $(srcdir)\delch.c + $(SRCBUILD) + +deleteln.obj: $(srcdir)\deleteln.c + $(SRCBUILD) + +getch.obj: $(srcdir)\getch.c + $(SRCBUILD) + +getstr.obj: $(srcdir)\getstr.c + $(SRCBUILD) + +getyx.obj: $(srcdir)\getyx.c + $(SRCBUILD) + +inch.obj: $(srcdir)\inch.c + $(SRCBUILD) + +inchstr.obj: $(srcdir)\inchstr.c + $(SRCBUILD) + +initscr.obj: $(srcdir)\initscr.c + $(SRCBUILD) + +inopts.obj: $(srcdir)\inopts.c + $(SRCBUILD) + +insch.obj: $(srcdir)\insch.c + $(SRCBUILD) + +insstr.obj: $(srcdir)\insstr.c + $(SRCBUILD) + +instr.obj: $(srcdir)\instr.c + $(SRCBUILD) + +kernel.obj: $(srcdir)\kernel.c + $(SRCBUILD) + +keyname.obj: $(srcdir)\keyname.c + $(SRCBUILD) + +mouse.obj: $(srcdir)\mouse.c + $(SRCBUILD) + +move.obj: $(srcdir)\move.c + $(SRCBUILD) + +outopts.obj: $(srcdir)\outopts.c + $(SRCBUILD) + +overlay.obj: $(srcdir)\overlay.c + $(SRCBUILD) + +pad.obj: $(srcdir)\pad.c + $(SRCBUILD) + +panel.obj: $(srcdir)\panel.c + $(SRCBUILD) + +printw.obj: $(srcdir)\printw.c + $(SRCBUILD) + +refresh.obj: $(srcdir)\refresh.c + $(SRCBUILD) + +scanw.obj: $(srcdir)\scanw.c + $(SRCBUILD) + +scr_dump.obj: $(srcdir)\scr_dump.c + $(SRCBUILD) + +scroll.obj: $(srcdir)\scroll.c + $(SRCBUILD) + +slk.obj: $(srcdir)\slk.c + $(SRCBUILD) + +termattr.obj: $(srcdir)\termattr.c + $(SRCBUILD) + +touch.obj: $(srcdir)\touch.c + $(SRCBUILD) + +util.obj: $(srcdir)\util.c + $(SRCBUILD) + +window.obj: $(srcdir)\window.c + $(SRCBUILD) + +debug.obj: $(srcdir)\debug.c + $(SRCBUILD) + +pdcclip.obj: $(osdir)\pdcclip.c + $(OSBUILD) + +pdcdisp.obj: $(osdir)\pdcdisp.c + $(OSBUILD) + +pdcgetsc.obj: $(osdir)\pdcgetsc.c + $(OSBUILD) + +pdckbd.obj: $(osdir)\pdckbd.c + $(OSBUILD) + +pdcscrn.obj: $(osdir)\pdcscrn.c + $(OSBUILD) + +pdcsetsc.obj: $(osdir)\pdcsetsc.c + $(OSBUILD) + +pdcutil.obj: $(osdir)\pdcutil.c + $(OSBUILD) + +firework.exe: $(demodir)\firework.c + $(DEMOBUILD) $(LIBS) + +ozdemo.exe: $(demodir)\ozdemo.c + $(DEMOBUILD) $(LIBS) + +ptest.exe: $(demodir)\ptest.c + $(DEMOBUILD) $(LIBS) + +rain.exe: $(demodir)\rain.c + $(DEMOBUILD) $(LIBS) + +testcurs.exe: $(demodir)\testcurs.c + $(DEMOBUILD) $(LIBS) + +tuidemo.exe: tuidemo.obj tui.obj + $(DEMOBUILD) $(LIBS) + +worm.exe: $(demodir)\worm.c + $(DEMOBUILD) $(LIBS) + +xmas.exe: $(demodir)\xmas.c + $(DEMOBUILD) $(LIBS) + +tui.obj: $(demodir)\tui.c $(demodir)\tui.h + $(BUILD) -I$(demodir) $(demodir)\$*.c + +tuidemo.obj: $(demodir)\tuidemo.c + $(BUILD) -I$(demodir) $(demodir)\$*.c diff --git a/wingui/Makefile.lcc b/wingui/Makefile.lcc new file mode 100644 index 000000000..3169e5b04 --- /dev/null +++ b/wingui/Makefile.lcc @@ -0,0 +1,265 @@ +# Makefile for PDCurses library - WIN32 LCC-Win32 +# +# Usage: make -f Makefile.lcc [target] +# +# where target can be any of: +# [all|demos|pdcurses.lib|testcurs.exe...] + +PDCURSES_SRCDIR = .. + +PDCURSES_CURSES_H = $(PDCURSES_SRCDIR)\curses.h +PDCURSES_CURSPRIV_H = $(PDCURSES_SRCDIR)\curspriv.h +PDCURSES_HEADERS = $(PDCURSES_CURSES_H) $(PDCURSES_CURSPRIV_H) + +PANEL_HEADER = $(PDCURSES_SRCDIR)\panel.h + +srcdir = $(PDCURSES_SRCDIR)\pdcurses +osdir = $(PDCURSES_SRCDIR)\wingui +demodir = $(PDCURSES_SRCDIR)\demos + +PDCURSES_WIN_H = $(osdir)\pdcwin.h + +CC = lcc + +#CFLAGS = -c -g3 -A -ansic +CFLAGS = -c -O -A -ansic + +CPPFLAGS = -I$(PDCURSES_SRCDIR) #-DPDC_WIDE -DPDC_FORCE_UTF8 + +LINK = lcclnk +LIBEXE = lcclib + +LIBCURSES = pdcurses.lib + +BUILD = $(CC) $(CFLAGS) $(CPPFLAGS) +PDCLIBS = $(LIBCURSES) + +DEMOS = testcurs.exe ozdemo.exe xmas.exe tuidemo.exe \ +firework.exe ptest.exe rain.exe worm.exe + +all: $(PDCLIBS) $(DEMOS) + +clean: + -del *.obj + -del *.lib + -del *.exe + +LIBOBJS = addch.obj addchstr.obj addstr.obj attr.obj beep.obj bkgd.obj \ +border.obj clear.obj color.obj delch.obj deleteln.obj getch.obj \ +getstr.obj getyx.obj inch.obj inchstr.obj initscr.obj inopts.obj \ +insch.obj insstr.obj instr.obj kernel.obj keyname.obj mouse.obj move.obj \ +outopts.obj overlay.obj pad.obj panel.obj printw.obj refresh.obj \ +scanw.obj scr_dump.obj scroll.obj slk.obj termattr.obj \ +touch.obj util.obj window.obj debug.obj + +PDCOBJS = pdcclip.obj pdcdisp.obj pdcgetsc.obj pdckbd.obj pdcscrn.obj \ +pdcsetsc.obj pdcutil.obj + +DEMOOBJS = testcurs.obj ozdemo.obj xmas.obj tuidemo.obj tui.obj \ +firework.obj ptest.obj rain.obj worm.obj + +$(LIBOBJS) $(PDCOBJS) : $(PDCURSES_HEADERS) +$(PDCOBJS) : $(PDCURSES_WIN_H) +panel.obj ptest.obj: $(PANEL_HEADER) + +$(DEMOOBJS) : $(PDCURSES_CURSES_H) +$(DEMOS) : $(LIBCURSES) + +$(LIBCURSES) : $(LIBOBJS) $(PDCOBJS) + $(LIBEXE) /out:$@ $(LIBOBJS) $(PDCOBJS) + -copy $(LIBCURSES) panel.lib + +SRCBUILD = $(BUILD) $(srcdir)\$*.c +OSBUILD = $(BUILD) $(osdir)\$*.c + +addch.obj: $(srcdir)\addch.c + $(SRCBUILD) + +addchstr.obj: $(srcdir)\addchstr.c + $(SRCBUILD) + +addstr.obj: $(srcdir)\addstr.c + $(SRCBUILD) + +attr.obj: $(srcdir)\attr.c + $(SRCBUILD) + +beep.obj: $(srcdir)\beep.c + $(SRCBUILD) + +bkgd.obj: $(srcdir)\bkgd.c + $(SRCBUILD) + +border.obj: $(srcdir)\border.c + $(SRCBUILD) + +clear.obj: $(srcdir)\clear.c + $(SRCBUILD) + +color.obj: $(srcdir)\color.c + $(SRCBUILD) + +delch.obj: $(srcdir)\delch.c + $(SRCBUILD) + +deleteln.obj: $(srcdir)\deleteln.c + $(SRCBUILD) + +getch.obj: $(srcdir)\getch.c + $(SRCBUILD) + +getstr.obj: $(srcdir)\getstr.c + $(SRCBUILD) + +getyx.obj: $(srcdir)\getyx.c + $(SRCBUILD) + +inch.obj: $(srcdir)\inch.c + $(SRCBUILD) + +inchstr.obj: $(srcdir)\inchstr.c + $(SRCBUILD) + +initscr.obj: $(srcdir)\initscr.c + $(SRCBUILD) + +inopts.obj: $(srcdir)\inopts.c + $(SRCBUILD) + +insch.obj: $(srcdir)\insch.c + $(SRCBUILD) + +insstr.obj: $(srcdir)\insstr.c + $(SRCBUILD) + +instr.obj: $(srcdir)\instr.c + $(SRCBUILD) + +kernel.obj: $(srcdir)\kernel.c + $(SRCBUILD) + +keyname.obj: $(srcdir)\keyname.c + $(SRCBUILD) + +mouse.obj: $(srcdir)\mouse.c + $(SRCBUILD) + +move.obj: $(srcdir)\move.c + $(SRCBUILD) + +outopts.obj: $(srcdir)\outopts.c + $(SRCBUILD) + +overlay.obj: $(srcdir)\overlay.c + $(SRCBUILD) + +pad.obj: $(srcdir)\pad.c + $(SRCBUILD) + +panel.obj: $(srcdir)\panel.c + $(SRCBUILD) + +printw.obj: $(srcdir)\printw.c + $(SRCBUILD) + +refresh.obj: $(srcdir)\refresh.c + $(SRCBUILD) + +scanw.obj: $(srcdir)\scanw.c + $(SRCBUILD) + +scr_dump.obj: $(srcdir)\scr_dump.c + $(SRCBUILD) + +scroll.obj: $(srcdir)\scroll.c + $(SRCBUILD) + +slk.obj: $(srcdir)\slk.c + $(SRCBUILD) + +termattr.obj: $(srcdir)\termattr.c + $(SRCBUILD) + +touch.obj: $(srcdir)\touch.c + $(SRCBUILD) + +util.obj: $(srcdir)\util.c + $(SRCBUILD) + +window.obj: $(srcdir)\window.c + $(SRCBUILD) + +debug.obj: $(srcdir)\debug.c + $(SRCBUILD) + +pdcclip.obj: $(osdir)\pdcclip.c + $(OSBUILD) + +pdcdisp.obj: $(osdir)\pdcdisp.c + $(OSBUILD) + +pdcgetsc.obj: $(osdir)\pdcgetsc.c + $(OSBUILD) + +pdckbd.obj: $(osdir)\pdckbd.c + $(OSBUILD) + +pdcscrn.obj: $(osdir)\pdcscrn.c + $(OSBUILD) + +pdcsetsc.obj: $(osdir)\pdcsetsc.c + $(OSBUILD) + +pdcutil.obj: $(osdir)\pdcutil.c + $(OSBUILD) + +firework.exe: firework.obj + $(LINK) -o $@ firework.obj $(LIBCURSES) + +ozdemo.exe: ozdemo.obj + $(LINK) -o $@ ozdemo.obj $(LIBCURSES) + +ptest.exe: ptest.obj + $(LINK) -o $@ ptest.obj $(LIBCURSES) + +rain.exe: rain.obj + $(LINK) -o $@ rain.obj $(LIBCURSES) + +testcurs.exe: testcurs.obj + $(LINK) -o $@ testcurs.obj $(LIBCURSES) + +tuidemo.exe: tuidemo.obj tui.obj + $(LINK) -o $@ tuidemo.obj tui.obj $(LIBCURSES) + +worm.exe: worm.obj + $(LINK) -o $@ worm.obj $(LIBCURSES) + +xmas.exe: xmas.obj + $(LINK) -o $@ xmas.obj $(LIBCURSES) + +firework.obj: $(demodir)\firework.c + $(BUILD) $(demodir)\$*.c + +ozdemo.obj: $(demodir)\ozdemo.c + $(BUILD) $(demodir)\$*.c + +ptest.obj: $(demodir)\ptest.c + $(BUILD) $(demodir)\$*.c + +rain.obj: $(demodir)\rain.c + $(BUILD) $(demodir)\$*.c + +testcurs.obj: $(demodir)\testcurs.c + $(BUILD) $(demodir)\$*.c + +tui.obj: $(demodir)\tui.c $(demodir)\tui.h + $(BUILD) -I$(demodir) $(demodir)\$*.c + +tuidemo.obj: $(demodir)\tuidemo.c + $(BUILD) -I$(demodir) $(demodir)\$*.c + +worm.obj: $(demodir)\worm.c + $(BUILD) $(demodir)\$*.c + +xmas.obj: $(demodir)\xmas.c + $(BUILD) $(demodir)\$*.c diff --git a/wingui/Makefile.mng b/wingui/Makefile.mng new file mode 100644 index 000000000..37a2af403 --- /dev/null +++ b/wingui/Makefile.mng @@ -0,0 +1,174 @@ +# GNU MAKE Makefile for PDCurses library - WIN32 MinGW GCC +# +# Usage: make -f [path\]Makefile.mng [DEBUG=Y] [DLL=Y] [WIDE=Y] [UTF8=Y] [target] +# +# where target can be any of: +# [all|demos|pdcurses.a|testcurs.exe...] + +O = o +E = .exe + +ifndef PDCURSES_SRCDIR + PDCURSES_SRCDIR = .. +endif + +#include $(PDCURSES_SRCDIR)/version.mif +include $(PDCURSES_SRCDIR)/common/libobjs.mif + +uname_S := $(shell uname -s 2>/dev/null) + +CAT = cat +PREFIX = +PATH_SEP = / +CP = cp +DELETE = rm -f + +# It appears we have three cases: we're running in Cygwin/MSYS; or we're +# running in command.com on Windows with MinGW; or we're on Linux or BSD +# or similar system, cross-compiling with MinGW. + +ifneq (,$(findstring CYGWIN,$(uname_S))) + # Insert Cygwin-specific changes here + ON_WINDOWS = 1 +endif +ifneq (,$(findstring MINGW32_NT,$(uname_S))) + # Insert MINGW32-specific changes here + ON_WINDOWS = 1 +endif +ifneq (,$(findstring MINGW64_NT,$(uname_S))) + # Insert MINGW64-specific changes here + ON_WINDOWS = 1 +endif + +ifeq ($(uname_S),) + CAT = type + PATH_SEP = \\ + CP = -copy + DELETE = -del + ON_WINDOWS = 1 +endif + +# If we aren't on Windows, assume MinGW on a Linux-like host +# Only decision is: are we doing a 64-bit compile (_w64 defined)? + +ifndef ON_WINDOWS + ifdef _w64 + PREFIX = x86_64-w64-mingw32- + else + PREFIX = i686-w64-mingw32- + endif +endif + +osdir = $(PDCURSES_SRCDIR)/wingui + +PDCURSES_WIN_H = $(osdir)/pdcwin.h + +CC = $(PREFIX)gcc + +AR = $(PREFIX)ar +STRIP = $(PREFIX)strip + +ifeq ($(DEBUG),Y) + CFLAGS = -g -Wall -DPDCDEBUG + LDFLAGS = -g +else + CFLAGS = -O4 -Wall -pedantic + LDFLAGS = +endif + +CFLAGS += -I$(PDCURSES_SRCDIR) + +ifdef CHTYPE_32 + CFLAGS += -DCHTYPE_32 +endif + +ifdef CHTYPE_16 + CFLAGS += -DCHTYPE_16 +endif + +ifeq ($(WIDE),Y) + CFLAGS += -DPDC_WIDE +endif + +ifeq ($(UTF8),Y) + CFLAGS += -DPDC_FORCE_UTF8 +endif + +LINK = $(CC) + +ifeq ($(DLL),Y) + CFLAGS += -DPDC_DLL_BUILD + LIBEXE = $(CC) + LIBFLAGS = -Wl,--out-implib,pdcurses.a -shared -o + LIBCURSES = pdcurses.dll + LIBDEPS = $(LIBOBJS) $(PDCOBJS) + LIBSADDED = -lgdi32 -lcomdlg32 + EXELIBS = + CLEAN = $(LIBCURSES) *.a +else + LIBEXE = $(AR) +ifeq ($(PREFIX),) + LIBFLAGS = rcv +else + LIBFLAGS = rv +endif + LIBCURSES = pdcurses.a + LIBDEPS = $(LIBOBJS) $(PDCOBJS) + LIBSADDED = + EXELIBS = -lgdi32 -lcomdlg32 + CLEAN = *.a +endif + +.PHONY: all libs clean demos dist + +all: libs demos + +libs: $(LIBCURSES) + +clean: + $(DELETE) *.o + $(DELETE) *.exe + $(DELETE) *.dll + $(DELETE) $(CLEAN) + +demos: $(DEMOS) +ifneq ($(DEBUG),Y) + $(STRIP) *.exe +endif + +$(LIBCURSES) : $(LIBDEPS) + $(LIBEXE) $(LIBFLAGS) $@ $? $(LIBSADDED) + $(CP) pdcurses.a panel.a + +$(LIBOBJS) $(PDCOBJS) : $(PDCURSES_HEADERS) +$(PDCOBJS) : $(PDCURSES_WIN_H) +$(DEMOS) : $(PDCURSES_CURSES_H) $(LIBCURSES) +panel.o : $(PANEL_HEADER) + +$(LIBOBJS) : %.o: $(srcdir)/%.c + $(CC) -c $(CFLAGS) $< + +$(PDCOBJS) : %.o: $(osdir)/%.c + $(CC) -c $(CFLAGS) $< + +firework.exe ozdemo.exe newtest.exe ptest.exe rain.exe testcurs.exe \ +version.exe worm.exe xmas.exe: %.exe: $(demodir)/%.c + $(CC) $(CFLAGS) -mwindows -o$@ $< $(LIBCURSES) $(EXELIBS) + +tuidemo.exe: tuidemo.o tui.o + @echo "tuidemo.exe rule" + $(CC) $(CFLAGS) -mwindows -o$@ tuidemo.o tui.o $(LIBCURSES) $(EXELIBS) + +tui.o: $(demodir)/tui.c $(demodir)/tui.h $(PDCURSES_CURSES_H) + @echo "tui.o rule" + $(CC) -c $(CFLAGS) -I$(demodir) -o$@ $< + +tuidemo.o: $(demodir)/tuidemo.c $(PDCURSES_CURSES_H) + @echo "tuidemo.o rule" + $(CC) -c $(CFLAGS) -I$(demodir) -o$@ $< + +PLATFORM1 = MinGW Win32 +PLATFORM2 = MinGW for Win32 +ARCNAME = pdc$(VER)_ming_w32 + +#include $(PDCURSES_SRCDIR)/makedist.mif diff --git a/wingui/Makefile.vc b/wingui/Makefile.vc new file mode 100644 index 000000000..1a64e1460 --- /dev/null +++ b/wingui/Makefile.vc @@ -0,0 +1,120 @@ +# Visual C++ Makefile for PDCurses - Windows GUI +# +# Usage: nmake -f [path\]Makefile.vc [DEBUG=Y] [DLL=Y] [WIDE=Y] [UTF8=Y] +# [INFOEX=N] [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 + +!include $(PDCURSES_SRCDIR)\common\libobjs.mif + +osdir = $(PDCURSES_SRCDIR)\wingui +common = $(PDCURSES_SRCDIR)\common + +PDCURSES_WIN_H = $(osdir)\pdcwin.h + +CC = cl.exe -nologo + +!ifdef DEBUG +CFLAGS = -Z7 -DPDCDEBUG -MT -D_CRT_SECURE_NO_WARNINGS +LDFLAGS = -debug -pdb:none +!else +CFLAGS = -Ox -MT -W3 -D_CRT_SECURE_NO_WARNINGS +LDFLAGS = +!endif + +!ifdef WIDE +WIDEOPT = -DPDC_WIDE +!endif + +!ifdef UTF8 +UTF8OPT = -DPDC_FORCE_UTF8 +!endif + +!ifdef INFOEX +INFOPT = -DHAVE_NO_INFOEX +!endif + +SHL_LD = link $(LDFLAGS) -nologo -dll -machine:$(PLATFORM) -out:pdcurses.dll + +LINK = link.exe -nologo + +CCLIBS = user32.lib gdi32.lib advapi32.lib shell32.lib comdlg32.lib +# may need to add msvcrt for older compilers +#CCLIBS = msvcrt.lib user32.lib gdi32.lib advapi32.lib comdlg32.lib + +LIBEXE = lib -nologo + +LIBCURSES = pdcurses.lib +CURSESDLL = pdcurses.dll + +!ifdef DLL +DLLOPT = -DPDC_DLL_BUILD +PDCLIBS = $(CURSESDLL) +!else +PDCLIBS = $(LIBCURSES) +!endif + +BUILD = $(CC) -I$(PDCURSES_SRCDIR) -c $(CFLAGS) $(DLLOPT) \ +$(WIDEOPT) $(UTF8OPT) $(INFOPT) + +all: $(PDCLIBS) + +clean: + -$(RM) *.obj + -$(RM) *.lib + -$(RM) *.exe + -$(RM) *.dll + -$(RM) *.exp + -$(RM) *.res + +demos: $(PDCLIBS) $(DEMOS) + +DEMOOBJS = $(DEMOS:.exe=.obj) tui.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 $(CCLIBS) + +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) $< + +{$(demodir)\}.c{}.obj:: + $(BUILD) $< + +.obj.exe: + $(LINK) $(LDFLAGS) $< $(LIBCURSES) $(CCLIBS) + +tuidemo.exe: tuidemo.obj tui.obj + $(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) -I$(demodir) $(demodir)\tuidemo.c diff --git a/wingui/Makefile.wcc b/wingui/Makefile.wcc new file mode 100644 index 000000000..1fac3bca8 --- /dev/null +++ b/wingui/Makefile.wcc @@ -0,0 +1,37 @@ +# Watcom Makefile for PDCurses - Windows GUI +# +# Usage: wmake -f [path\]Makefile.wcc [DEBUG=Y] [WIDE=Y] [UTF8=Y] +# [INFOEX=N] [target] +# +# where target can be any of: +# [all|demos|pdcurses.lib|testcurs.exe...] + +!ifdef %PDCURSES_SRCDIR +PDCURSES_SRCDIR = $(%PDCURSES_SRCDIR) +!else +PDCURSES_SRCDIR = .. +!endif + +osdir = $(PDCURSES_SRCDIR)/wingui + +CC = wcc386 +TARGET = nt_win + +CFLAGS = -bt=$(TARGET) + +!ifeq WIDE Y +CFLAGS += -DPDC_WIDE +!endif + +!ifeq UTF8 Y +CFLAGS += -DPDC_FORCE_UTF8 +!endif + +!ifeq INFOEX N +CFLAGS += -DHAVE_NO_INFOEX +!endif + +!include $(PDCURSES_SRCDIR)/common/watcom.mif + +$(LIBCURSES) : $(LIBOBJS) $(PDCOBJS) + $(LIBEXE) $@ $(LIBOBJS) $(PDCOBJS) diff --git a/wingui/README.md b/wingui/README.md new file mode 100644 index 000000000..4b58370aa --- /dev/null +++ b/wingui/README.md @@ -0,0 +1,93 @@ +PDCurses for WinGUI +================== + +This directory contains PDCurses source code files specific to Win32 +graphics mode (Win95 and all subsequent forks of Windows). + +Building +-------- + + (Note that the following is almost identical to the instructions + for the Win32 console flavor of PDCurses.) + +- Choose the appropriate makefile for your compiler: + + Makefile.bcc - Borland C++ 4.0.2+ + Makefile.dmc - Digital Mars + Makefile.lcc - LCC-Win32 + Makefile.mng - MinGW, Cygnus GNU Compiler + Makefile.vc - Microsoft Visual C++ 2.0+ or later & Intel(R) compiler + Makefile.wcc - Watcom 10.6+ or OpenWATCOM + +- Optionally, you can build in a different directory than the platform + directory by setting PDCURSES_SRCDIR to point to the directory where + you unpacked PDCurses, and changing to your target directory: + + set PDCURSES_SRCDIR=c:\pdcurses + + This won't work with the LCC or Digital Mars makefiles, nor will the + options described below. + +- Build it: + + make -f makefilename + + (For Watcom, use "wmake" instead of "make"; for MSVC, "nmake".) You'll + get the libraries (pdcurses.lib or .a, depending on your compiler; and + panel.lib or .a), the demos (*.exe), and a lot of object files. Note + that the panel library is just a copy of the main library, provided + for convenience; both panel and curses functions are in the main + library. + + You can also give the optional parameter "WIDE=Y", to build the + library with wide-character (Unicode) support: + + make -f Makefile.mng WIDE=Y + + When built this way, the library is not compatible with Windows 9x, + unless you also link with the Microsoft Layer for Unicode (not + tested). + + For the Intel(R) compiler, use Makefile.vc and add ICC=Y. + + By default, Makefile.vc results in 64-bit code for both VC and Intel(R). + Add IX86=Y to generate 32-bit code. (Other builds are 32-bit only.) + + Another option, "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. This was originally provided to get around + poor support for UTF-8 in the Win32 console: + + make -f Makefile.mng WIDE=Y UTF8=Y + + WinGUI doesn't have the same limitations as the Win32 console flavor, + but UTF-8 and non-UTF-8 versions are still available. If nothing else, + this means that if you've built a Win32 console PDCurses DLL with any + configuration, you can build a matching WinGUI DLL and swap between + console or GUI PDCurses just by swapping DLLs. + + You can also use the optional parameter "DLL=Y" with Visual C++, + MinGW or Cygwin, to build the library as a DLL: + + nmake -f Makefile.vc WIDE=Y DLL=Y + + When you build the library as a Windows DLL, you must always define + PDC_DLL_BUILD when linking against it. (Or, if you only want to use + the DLL, you could add this definition to your curses.h.) + + If cross-compiling from Linux, add the parameter `_w64=1` to get + 64-bit code (default will be 32-bit). + + make -f Makefile.mng _w64=1 [WIDE=Y UTF8=Y DLL=Y] + +Distribution Status +------------------- + +The files in this directory are released to the Public Domain. + +Acknowledgements +---------------- + +Based heavily on the Win32 console flavor of PDCurses by Chris Szurgot +, ported to Win32 GUI by Bill Gray +. diff --git a/wingui/pdcclip.c b/wingui/pdcclip.c new file mode 100644 index 000000000..0b86951ae --- /dev/null +++ b/wingui/pdcclip.c @@ -0,0 +1,174 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" + +/*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 BSD SYS V + PDC_getclipboard - - - + PDC_setclipboard - - - + PDC_freeclipboard - - - + PDC_clearclipboard - - - + +**man-end****************************************************************/ + +#ifdef PDC_WIDE +# define PDC_TEXT CF_UNICODETEXT +#else +# define PDC_TEXT CF_OEMTEXT +#endif + +int PDC_getclipboard_handle( HANDLE *handle) +{ + PDC_LOG(("PDC_getclipboard() - called\n")); + + if (!OpenClipboard(NULL)) + { + return PDC_CLIP_ACCESS_ERROR; + } + + if ((*handle = GetClipboardData(PDC_TEXT)) == NULL) + { + CloseClipboard(); + return PDC_CLIP_EMPTY; + } + + return PDC_CLIP_SUCCESS; +} + +int PDC_getclipboard(char **contents, long *length) +{ + HANDLE handle; + int rval = PDC_getclipboard_handle( &handle); + + if( rval == PDC_CLIP_SUCCESS) + { + void *tptr = GlobalLock( handle); + + if( tptr) + { +#ifdef PDC_WIDE + size_t len = wcslen((wchar_t *)tptr) * 3; +#else + size_t len = strlen( tptr); +#endif + + *contents = (char *)GlobalAlloc( GMEM_FIXED, len + 1); + + if( !*contents) + rval = PDC_CLIP_MEMORY_ERROR; + else + { +#ifdef PDC_WIDE + len = PDC_wcstombs( (char *)*contents, tptr, len); +#else + strcpy((char *)*contents, tptr); +#endif + } + *length = (long)len; + GlobalUnlock( handle); + } + else + rval = PDC_CLIP_MEMORY_ERROR; + CloseClipboard(); + } + return rval; +} + +int PDC_setclipboard_raw( const char *contents, long length, + const bool translate_multibyte_to_wide_char) +{ + HGLOBAL handle; + LPTSTR buff; + + PDC_LOG(("PDC_setclipboard() - called\n")); + + if (!OpenClipboard(NULL)) + return PDC_CLIP_ACCESS_ERROR; + + handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, + (length + 1) * sizeof(TCHAR)); + + if (!handle) + return PDC_CLIP_MEMORY_ERROR; + + buff = GlobalLock(handle); + +#ifdef PDC_WIDE + if( translate_multibyte_to_wide_char) + PDC_mbstowcs((wchar_t *)buff, contents, length); + else + memcpy((char *)buff, contents, (length + 1) * sizeof( wchar_t)); +#else + memcpy((char *)buff, contents, length); + buff[length] = 0; /* ensure null termination */ +#endif + GlobalUnlock(handle); + EmptyClipboard(); + + if( !SetClipboardData(PDC_TEXT, handle)) + { + GlobalFree(handle); + return PDC_CLIP_ACCESS_ERROR; + } + + CloseClipboard(); + + return PDC_CLIP_SUCCESS; +} + +int PDC_setclipboard(const char *contents, long length) +{ + return( PDC_setclipboard_raw( contents, length, TRUE)); +} + +int PDC_freeclipboard(char *contents) +{ + PDC_LOG(("PDC_freeclipboard() - called\n")); + + GlobalFree(contents); + return PDC_CLIP_SUCCESS; +} + +int PDC_clearclipboard(void) +{ + PDC_LOG(("PDC_clearclipboard() - called\n")); + + EmptyClipboard(); + + return PDC_CLIP_SUCCESS; +} diff --git a/wingui/pdcdisp.c b/wingui/pdcdisp.c new file mode 100644 index 000000000..6cfeb784a --- /dev/null +++ b/wingui/pdcdisp.c @@ -0,0 +1,677 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" + +#include "../common/acsuni.h" + +#include +#include +#include +#include +#ifdef WIN32_LEAN_AND_MEAN + #include +#endif + +static void PDC_get_rgb_values( const chtype srcp, + COLORREF *foreground_rgb, COLORREF *background_rgb); + +/* For this 'real Windows' version, we use all Unicode all the time, +including for ACS characters, and even when PDC_WIDE isn't #defined +(i.e., when running in 'legacy' 8-bit character mode) See 'acs_defs.h' +for details. */ + + +static const unsigned short starting_ascii_to_unicode[32] = { + 0, + 0x263a, /* 1 smiling face */ + 0x263b, /* 2 smiling face inverted */ + 0x2665, /* 3 heart */ + 0x2666, /* 4 diamond */ + 0x2663, /* 5 club */ + 0x2660, /* 6 spade */ + 0x2024, /* 7 small bullet */ + 0x25d8, /* 8 inverted bullet */ + 0x25bc, /* 9 hollow bullet */ + 0x25d9, /* 10 inverted hollow bullet */ + 0x2642, /* 11 male/Mars symbol */ + 0x2640, /* 12 female/Venus symbol */ + 0x266a, /* 13 eighth note */ + 0x266c, /* 14 two sixteenth notes */ + 0x263c, /* 15 splat */ + 0x25b6, /* 16 right-pointing triangle */ + 0x25c0, /* 17 left-pointing triangle */ + 0x2195, /* 18 double up/down arrow */ + 0x203c, /* 19 double exclamation !! */ + 0x00b6, /* 20 pilcrow */ + 0xa7, /* 21 */ + 0x2582, /* 22 lower 1/3 block */ + 0x280d, /* 23 double up/down arrow */ + 0x2191, /* 24 up arrow */ + 0x2193, /* 25 down arrow */ + 0x2192, /* 26 right arrow */ + 0x2190, /* 27 left arrow */ + 0x2319, /* 28 */ + 0x280c, /* 29 left & right arrow */ + 0x25b2, /* 30 up triangle */ + 0x25bc}; /* 31 down triangle */ + +/* Cursors may be added to the 'shapes' array. A 'shapes' string +defines the cursor as one or more rectangles, separated by semicolons. +The coordinates of the upper left and lower right corners are given, +usually just as integers from zero to eight. Thus, "0488" means a +rectangle running from (0,4), middle of the left side, to (8,8), +bottom right corner: a rectangle filling the bottom half of the +character cell. "0048" would fill the left half of the cell, and +"0082;6088" would fill the top and bottom quarters of the cell. + + However, a coordinate may be followed by a + or -, and then by a +single-digit offset in pixels. So "08-4" refers to a point on the +left-hand side of the character cell, four pixels from the bottom. I +admit that the cursor descriptions themselves look a little strange! +But this way of describing cursors is compact and lends itself to some +pretty simple code. + + The first three lines are standard PDCurses cursors: 0=no cursor, +1=four-pixel thick line at bottom of the cell, 2="high-intensity", +i.e., a filled block. The rest are extended cursors, not currently +available in other PDCurses flavors. */ + +#define N_CURSORS 9 + +static void redraw_cursor_from_index( const HDC hdc, const int idx) +{ + const char *shapes[N_CURSORS] = { + "", /* 0: invisible */ + "08-488", /* 1: normal: four lines at bottom */ + "0088", /* 2: full block */ + "0088;0+10+18-18-1", /* 3: outlined block */ + "28-368;4-10+34+18-3;2060+3", /* 4: caret */ + "0488", /* 5: bottom half block */ + "2266", /* 6: central block */ + "0385;3053;3558", /* 7: cross */ + "0088;0+10+48-18-4" }; /* 8: outlined block: heavy top/bottom*/ + const char *sptr = shapes[idx]; + LONG left, top; + + left = SP->curscol * PDC_cxChar; + top = SP->cursrow * PDC_cyChar; + while( *sptr) + { + int i; + LONG coords[4]; + RECT rect; + + for( i = 0; i < 4; i++) + { + coords[i] = (( i & 1) ? + top + (PDC_cyChar * (*sptr - '0') + 4) / 8 : + left + (PDC_cxChar * (*sptr - '0') + 4) / 8); + sptr++; + if( *sptr == '+' || *sptr == '-') + { + if( *sptr == '+') + coords[i] += sptr[1] - '0'; + else + coords[i] -= sptr[1] - '0'; + sptr += 2; + } + } + rect.left = coords[0]; + rect.top = coords[1]; + rect.right = coords[2]; + rect.bottom = coords[3]; + InvertRect( hdc, &rect); + if( *sptr == ';') + sptr++; + } +} + +/* PDC_current_cursor_state( ) determines which cursor, if any, +is currently shown. This may depend on the blink state. Also, +if the window currently lacks the focus, we show cursor 3 (a hollow +box) in place of any visible cursor. */ + +static int PDC_current_cursor_state( void) +{ + const int shift_amount = (PDC_blink_state ? 0 : 8); + const int cursor_style_for_unfocussed_window = + PDC_CURSOR( PDC_CURSOR_OUTLINE, PDC_CURSOR_OUTLINE); + int cursor_style; + + /* for unfocussed windows, show an hollow box: */ + if( SP->visibility && (PDC_hWnd != GetForegroundWindow( ))) + cursor_style = cursor_style_for_unfocussed_window; + else /* otherwise, just show the cursor "normally" */ + cursor_style = SP->visibility; + return( (cursor_style >> shift_amount) & 0xff); +} + +static void redraw_cursor( const HDC hdc) +{ + const int cursor_style = PDC_current_cursor_state( ); + + if( cursor_style > 0 && cursor_style < N_CURSORS) + redraw_cursor_from_index( hdc, cursor_style); +} + +/* position "hardware" cursor at (y, x). We don't have a for-real hardware */ +/* cursor in this version, of course, but we can fake it. Note that much */ +/* of the logic was borrowed from the SDL version. In particular, the */ +/* cursor is moved by first overwriting the "original" location. */ + +void PDC_gotoyx(int row, int col) +{ + PDC_LOG(("PDC_gotoyx() - called: row %d col %d from row %d col %d\n", + row, col, SP->cursrow, SP->curscol)); + + /* clear the old cursor, if it's on-screen: */ + if( SP->cursrow >= 0 && SP->curscol >= 0 && + SP->cursrow < SP->lines && SP->curscol < SP->cols) + { + const int temp_visibility = SP->visibility; + + SP->visibility = 0; + PDC_transform_line( SP->cursrow, SP->curscol, 1, + curscr->_y[SP->cursrow] + SP->curscol); + SP->visibility = temp_visibility; + } + + /* ...then draw the new (assuming it's actually visible). */ + /* This used to require some logic. Now the redraw_cursor() */ + /* function figures out what cursor should be drawn, if any. */ + if( SP->visibility) + { + HDC hdc = GetDC( PDC_hWnd) ; + + SP->curscol = col; + SP->cursrow = row; + redraw_cursor( hdc); + ReleaseDC( PDC_hWnd, hdc) ; + } +} + +int PDC_font_size = 12; +TCHAR PDC_font_name[80]; + +static LOGFONT PDC_get_logical_font( const int font_idx) +{ + LOGFONT lf; + + memset(&lf, 0, sizeof(LOGFONT)); /* Clear out structure. */ + lf.lfHeight = -PDC_font_size; +#ifdef PDC_WIDE + if( !*PDC_font_name) + wcscpy( PDC_font_name, _T("Courier New")); + if( font_idx & 4) + wcscpy( lf.lfFaceName, _T("Unifont")); + else + wcscpy( lf.lfFaceName, PDC_font_name ); +/* wprintf( L"New font: %s\n", PDC_font_name); */ +#else + if( !*PDC_font_name) + strcpy( PDC_font_name, "Courier New"); + if( font_idx & 4) + strcpy( lf.lfFaceName, "Unifont"); + else + strcpy( lf.lfFaceName, PDC_font_name); +#endif +/* lf.lfPitchAndFamily = FIXED_PITCH | FF_MODERN; */ + lf.lfPitchAndFamily = FF_MODERN; + lf.lfWeight = ((font_idx & 1) ? FW_EXTRABOLD : FW_NORMAL); + lf.lfItalic = ((font_idx & 2) ? TRUE : FALSE); + lf.lfCharSet = ANSI_CHARSET; + lf.lfQuality = PROOF_QUALITY; + lf.lfOutPrecision = OUT_RASTER_PRECIS; + return( lf); +} + +HFONT PDC_get_font_handle( const int font_idx) +{ + LOGFONT lf = PDC_get_logical_font( font_idx); + + return( CreateFontIndirect( &lf)); +} + +int PDC_choose_a_new_font( void) +{ + LOGFONT lf = PDC_get_logical_font( 0); + CHOOSEFONT cf; + int rval; + + lf.lfHeight = -PDC_font_size; + debug_printf( "In PDC_choose_a_new_font: %d\n", lf.lfHeight); + memset( &cf, 0, sizeof( CHOOSEFONT)); + cf.lStructSize = sizeof( CHOOSEFONT); + cf.Flags = CF_INITTOLOGFONTSTRUCT | CF_SCREENFONTS; + cf.hwndOwner = PDC_hWnd; + cf.lpLogFont = &lf; + cf.rgbColors = RGB( 0, 0, 0); + rval = ChooseFont( &cf); + if( rval) +#ifdef PDC_WIDE + wcscpy( PDC_font_name, lf.lfFaceName); +#else + strcpy( PDC_font_name, lf.lfFaceName); +#endif + debug_printf( "rval %d; %ld\n", rval, CommDlgExtendedError( )); + debug_printf( "output size: %d\n", lf.lfHeight); + PDC_font_size = -lf.lfHeight; + return( rval); +} + +/* see 'addch.c' for an explanation of how combining chars are handled. */ + +#ifdef USING_COMBINING_CHARACTER_SCHEME + int PDC_expand_combined_characters( const cchar_t c, cchar_t *added); /* addch.c */ +#endif + +#ifdef PDC_WIDE +const chtype MAX_UNICODE = 0x110000; +#endif + +#ifdef USE_FALLBACK_FONT +GLYPHSET *PDC_unicode_range_data = NULL; + +/* Note that in the following, if a character has a Unicode point +greater than 64K (i.e., it's not in the Basic Multilingual Plane), +we assume it has a glyph in the font. The problem is that, due to +the brain-dead implementation of Unicode in Windoze, we can't +actually tell if there is a glyph or not; the "range" data is 16-bit. */ + +static bool character_is_in_font( chtype ichar) +{ + int i; + WCRANGE *wptr = PDC_unicode_range_data->ranges; + + if( (ichar & A_ALTCHARSET) && (ichar & A_CHARTEXT) < 0x80) + ichar = acs_map[ichar & 0x7f]; + ichar &= A_CHARTEXT; + if( ichar > MAX_UNICODE) /* assume combining chars won't be */ + return( FALSE); /* supported; they rarely are */ + if( ichar > 0xffff) /* see above comments */ + return( TRUE); + for( i = PDC_unicode_range_data->cRanges; i; i--, wptr++) + if( wptr->wcLow > ichar) + return( FALSE); + else if( wptr->wcLow + wptr->cGlyphs > ichar) + return( TRUE); + /* Didn't find it in any range; it must not be in the font */ + return( FALSE); +} +#endif /* #ifdef USE_FALLBACK_FONT */ + +/* update the given physical line to look like the corresponding line in +curscr. + + NOTE that if x > 0, we decrement it and srcp, and increment the +length. In other words, we draw the preceding character, too. This +is done because, at certain font sizes, characters break out and +overwrite the preceding character. That results in debris left on +the screen. + + The code also now increments the length only, drawing one more +character (i.e., draws the character following the "text we really +want"). Again, this helps to avoid debris left on the screen. + + The 'ExtTextOut' function takes an lpDx array that specifies the exact +placement of each character relative to the previous character. This seems +to help avoid most (but not all) stray pixels from being displayed. The +problem is that, at certain font sizes, letters may be drawn that don't +fit properly in the clip rectangle; and they aren't actually clipped +correctly, despite the use of the ETO_CLIPPED flag. But things do seem +to be much better than was the case back when plain 'TextOut' was used. */ + +#define N_CACHED_FONTS 8 + +static HFONT hFonts[N_CACHED_FONTS]; + +#define BUFFSIZE 50 + +void PDC_transform_line_given_hdc( const HDC hdc, const int lineno, + int x, int len, const chtype *srcp) +{ + HFONT hOldFont = (HFONT)0; + int i, curr_color = -1; + attr_t font_attrib = (attr_t)-1; + int cursor_overwritten = FALSE; + COLORREF foreground_rgb = 0; + chtype prev_ch = 0; + + if( !srcp) /* just freeing up fonts */ + { + for( i = 0; i < N_CACHED_FONTS; i++) + if( hFonts[i]) + { + DeleteObject( hFonts[i]); + hFonts[i] = NULL; + } +#ifdef USE_FALLBACK_FONT + if( PDC_unicode_range_data) + { + free( PDC_unicode_range_data); + PDC_unicode_range_data = NULL; + } +#endif + return; + } + /* Seems to me as if the input text to this function */ + if( x < 0) /* should _never_ be off-screen. But it sometimes is. */ + { /* Clipping is therefore necessary. */ + len += x; + srcp -= x; + x = 0; + } + len++; /* draw an extra char to avoid leaving garbage on screen */ + if( len > SP->cols - x) + len = SP->cols - x; + if( lineno >= SP->lines || len <= 0 || lineno < 0) + return; + if( x) /* back up by one character to avoid */ + { /* leaving garbage on the screen */ + x--; + len++; + srcp--; + } + if( lineno == SP->cursrow && SP->curscol >= x && SP->curscol < x + len) + if( PDC_current_cursor_state( )) + cursor_overwritten = TRUE; + + + while( len) + { + const attr_t attrib = (attr_t)( *srcp >> PDC_REAL_ATTR_SHIFT); + const int color = (int)(( *srcp & A_COLOR) >> PDC_COLOR_SHIFT); + attr_t new_font_attrib = (*srcp & (A_BOLD | A_ITALIC)); + RECT clip_rect; + wchar_t buff[BUFFSIZE]; + int lpDx[BUFFSIZE + 1]; + int olen = 0; +#ifdef USE_FALLBACK_FONT + const bool in_font = character_is_in_font( *srcp); +#endif + + for( i = 0; i < len && olen < BUFFSIZE - 1 +#ifdef USE_FALLBACK_FONT + && (in_font == character_is_in_font( srcp[i]) + || (srcp[i] & A_CHARTEXT) == MAX_UNICODE) +#endif + && attrib == (attr_t)( srcp[i] >> PDC_REAL_ATTR_SHIFT); i++) + { + chtype ch = srcp[i] & A_CHARTEXT; + +#ifdef USING_COMBINING_CHARACTER_SCHEME + if( ch > 0xffff && ch < MAX_UNICODE) /* use Unicode surrogates to fit */ + { /* >64K values into 16-bit wchar_t: */ + ch -= 0x10000; + buff[olen] = (wchar_t)( 0xd800 | (ch >> 10)); + lpDx[olen] = 0; /* ^ upper 10 bits */ + olen++; + ch = (wchar_t)( 0xdc00 | (ch & 0x3ff)); /* lower 10 bits */ + } + if( ch > MAX_UNICODE) /* chars & fullwidth supported */ + { + cchar_t added[10], root = ch; + int n_combined = 0; + + while( (root = PDC_expand_combined_characters( root, + &added[n_combined])) > MAX_UNICODE) + { + n_combined++; + } + buff[olen] = (wchar_t)root; + lpDx[olen] = 0; + olen++; + ch = (wchar_t)added[n_combined]; + while( n_combined) + { + n_combined--; + buff[olen] = (wchar_t)added[n_combined]; + lpDx[olen] = 0; + olen++; + } + } +#endif + if( (srcp[i] & A_ALTCHARSET) && ch < 0x80) + ch = acs_map[ch & 0x7f]; + else if( ch < 32) + ch = starting_ascii_to_unicode[ch]; +#ifndef PDC_WIDE /* If we're in Unicode, assume */ + else if( ch <= 0xff) /* the incoming text doesn't need */ + { /* code-page translation */ + char c = (char)ch; + wchar_t z; + + mbtowc( &z, &c, 1); + ch = (chtype)z; + } + assert( "We should never get here"); +#endif + buff[olen] = (wchar_t)ch; + lpDx[olen] = PDC_cxChar; +#ifdef PDC_WIDE + if( ch != MAX_UNICODE) + olen++; + else if( olen) /* prev char is double-width */ + lpDx[olen - 1] = 2 * PDC_cxChar; +#else + olen++; +#endif + } + lpDx[olen] = PDC_cxChar; + if( color != curr_color || ((prev_ch ^ *srcp) & (A_REVERSE | A_BLINK | A_BOLD | A_DIM))) + { + COLORREF background_rgb; + + PDC_get_rgb_values( *srcp, &foreground_rgb, &background_rgb); + curr_color = color; + SetTextColor( hdc, (COLORREF)foreground_rgb); + SetBkColor( hdc, (COLORREF)background_rgb); + } + if( !(SP->termattrs & A_BLINK) && (*srcp & A_BLINK)) + new_font_attrib &= ~A_BLINK; +#ifdef USE_FALLBACK_FONT + if( !in_font) /* flag to indicate use of */ + new_font_attrib |= 1; /* fallback font */ +#endif + if( new_font_attrib != font_attrib) + { + HFONT hFont; + int idx = 0; + + font_attrib = new_font_attrib; + if( font_attrib & A_BOLD & SP->termattrs) + idx |= 1; + if( font_attrib & A_ITALIC) + idx |= 2; + if( font_attrib & 1) /* use Unifont or other fallback font */ + idx |= 4; + if( !hFonts[idx]) + hFonts[idx] = PDC_get_font_handle( idx); + hFont = SelectObject( hdc, hFonts[idx]); + if( !hOldFont) + hOldFont = hFont; + } + prev_ch = *srcp; + clip_rect.left = x * PDC_cxChar; + clip_rect.top = lineno * PDC_cyChar; + clip_rect.right = clip_rect.left + i * PDC_cxChar; + clip_rect.bottom = clip_rect.top + PDC_cyChar; + ExtTextOutW( hdc, clip_rect.left, clip_rect.top, + ETO_CLIPPED | ETO_OPAQUE, &clip_rect, + buff, olen, (olen > 1 ? lpDx : NULL)); +#ifdef A_OVERLINE + if( *srcp & (A_UNDERLINE | A_RIGHTLINE | A_LEFTLINE | A_OVERLINE | A_STRIKEOUT)) +#else + if( *srcp & (A_UNDERLINE | A_RIGHTLINE | A_LEFTLINE)) +#endif + { + const int y1 = clip_rect.top; + const int y2 = clip_rect.bottom - 1; + const int x1 = clip_rect.left; + const int x2 = clip_rect.right; + int j; + const COLORREF rgb = (SP->line_color == -1 ? + foreground_rgb : PDC_palette[SP->line_color]); + const HPEN pen = CreatePen( PS_SOLID, 1, (COLORREF)rgb); + const HPEN old_pen = SelectObject( hdc, pen); + + if( *srcp & A_UNDERLINE) + { + MoveToEx( hdc, x1, y2, NULL); + LineTo( hdc, x2, y2); + } +#ifdef A_OVERLINE + if( *srcp & A_OVERLINE) + { + MoveToEx( hdc, x1, y1, NULL); + LineTo( hdc, x2, y1); + } + if( *srcp & A_STRIKEOUT) + { + MoveToEx( hdc, x1, (y1 + y2) / 2, NULL); + LineTo( hdc, x2, (y1 + y2) / 2); + } +#endif + if( *srcp & A_RIGHTLINE) + for( j = 0; j < i; j++) + { + MoveToEx( hdc, x2 - j * PDC_cxChar - 1, y1, NULL); + LineTo( hdc, x2 - j * PDC_cxChar - 1, y2); + } + if( *srcp & A_LEFTLINE) + for( j = 0; j < i; j++) + { + MoveToEx( hdc, x1 + j * PDC_cxChar, y1, NULL); + LineTo( hdc, x1 + j * PDC_cxChar, y2); + } + SelectObject( hdc, old_pen); + DeleteObject( pen); + } + len -= i; + x += i; + srcp += i; + } + SelectObject( hdc, hOldFont); + /* ...did we step on the cursor? If so, redraw it: */ + if( cursor_overwritten) + redraw_cursor( hdc); +} + +void PDC_transform_line(int lineno, int x, int len, const chtype *srcp) +{ + if( !srcp) /* just freeing up fonts */ + PDC_transform_line_given_hdc( 0, 0, 0, 0, NULL); + else + { + const HDC hdc = GetDC( PDC_hWnd) ; + + PDC_transform_line_given_hdc( hdc, lineno, x, len, srcp); + ReleaseDC( PDC_hWnd, hdc); + } +} + +void PDC_doupdate(void) +{ +} + + /* This function 'intensifies' a color by shifting it toward white. */ + /* It used to average the input color with white. Then it did a */ + /* weighted average: 2/3 of the input color, 1/3 white, for a */ + /* lower "intensification" level. */ + /* Then Mark Hessling suggested that the output level should */ + /* remap zero to 85 (= 255 / 3, so one-third intensity), and input */ + /* of 192 or greater should be remapped to 255 (full intensity). */ + /* Assuming we want a linear response between zero and 192, that */ + /* leads to output = 85 + input * (255-85)/192. */ + /* This should lead to proper handling of bold text in legacy */ + /* apps, where "bold" means "high intensity". */ + +static COLORREF intensified_color( COLORREF ival) +{ + int rgb, i; + COLORREF oval = 0; + + for( i = 0; i < 3; i++, ival >>= 8) + { + rgb = (int)( ival & 0xff); + if( rgb >= 192) + rgb = 255; + else + rgb = 85 + rgb * (255 - 85) / 192; + oval |= ((COLORREF)rgb << (i * 8)); + } + return( oval); +} + + /* For use in adjusting colors for A_DIMmed characters. Just */ + /* knocks down the intensity of R, G, and B by 1/3. */ + +static COLORREF dimmed_color( COLORREF ival) +{ + unsigned i; + COLORREF oval = 0; + + for( i = 0; i < 3; i++, ival >>= 8) + { + unsigned rgb = (unsigned)( ival & 0xff); + + rgb -= (rgb / 3); + oval |= ((COLORREF)rgb << (i * 8)); + } + return( oval); +} + +static void PDC_get_rgb_values( const chtype srcp, + COLORREF *foreground_rgb, COLORREF *background_rgb) +{ + const int color = (int)(( srcp & A_COLOR) >> PDC_COLOR_SHIFT); + bool reverse_colors = ((srcp & A_REVERSE) ? TRUE : FALSE); + bool intensify_backgnd = FALSE; + bool default_foreground = FALSE, default_background = FALSE; + + *foreground_rgb = (COLORREF)-1; + *background_rgb = (COLORREF)-1; + + { + short foreground_index, background_index; + + pair_content( color, &foreground_index, &background_index); + if( foreground_index < 0 && SP->orig_attr) + default_foreground = TRUE; + else + *foreground_rgb = PDC_palette[foreground_index]; + if( background_index < 0 && SP->orig_attr) + default_background = TRUE; + else + *background_rgb = PDC_palette[background_index]; + } + + if( srcp & A_BLINK) + { + if( !(SP->termattrs & A_BLINK)) /* convert 'blinking' to 'bold' */ + intensify_backgnd = TRUE; + else if( PDC_blink_state) + reverse_colors ^= 1; + } + if( reverse_colors) + { + const COLORREF temp = *foreground_rgb; + + *foreground_rgb = *background_rgb; + *background_rgb = temp; + } + + if( srcp & A_BOLD & ~SP->termattrs) + *foreground_rgb = intensified_color( *foreground_rgb); + if( intensify_backgnd) + *background_rgb = intensified_color( *background_rgb); + if( srcp & A_DIM) + { + *foreground_rgb = dimmed_color( *foreground_rgb); + *background_rgb = dimmed_color( *background_rgb); + } + if( default_foreground) + *foreground_rgb = (COLORREF)-1; + if( default_background) + *background_rgb = (COLORREF)-1; +} diff --git a/wingui/pdcgetsc.c b/wingui/pdcgetsc.c new file mode 100644 index 000000000..6a3d4ea26 --- /dev/null +++ b/wingui/pdcgetsc.c @@ -0,0 +1,26 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" + +/* get the cursor size/shape */ + +int PDC_get_cursor_mode(void) +{ + PDC_LOG(("PDC_get_cursor_mode() - called\n")); + + return SP->visibility; +} + +/* return number of screen rows */ + +int PDC_get_rows(void) +{ + PDC_LOG(("PDC_get_rows() - called\n")); + return( PDC_n_rows); +} + +int PDC_get_columns(void) +{ + PDC_LOG(("PDC_get_columns() - called\n")); + return( PDC_n_cols); +} diff --git a/wingui/pdckbd.c b/wingui/pdckbd.c new file mode 100644 index 000000000..cada9df59 --- /dev/null +++ b/wingui/pdckbd.c @@ -0,0 +1,69 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" +#include + +void PDC_set_keyboard_binary(bool on) +{ + PDC_LOG(("PDC_set_keyboard_binary() - called\n")); +} + +/* check if a key or mouse event is waiting */ + +/* PDCurses message/event callback */ +/* Calling PDC_napms for one millisecond ensures that the message loop */ +/* is called and messages in general, and keyboard events in particular, */ +/* get processed. */ + +bool PDC_check_key(void) +{ + PDC_napms( 1); + if( PDC_key_queue_low != PDC_key_queue_high) + return TRUE; + return FALSE; +} + +/* return the next available key or mouse event */ + +int PDC_get_key(void) +{ + int rval = -1; + + if( PDC_key_queue_low != PDC_key_queue_high) + { + rval = PDC_key_queue[PDC_key_queue_low++]; + if( PDC_key_queue_low == KEY_QUEUE_SIZE) + PDC_key_queue_low = 0; + } + SP->key_code = (rval >= KEY_MIN && rval <= KEY_MAX); + return rval; +} + +/* discard any pending keyboard or mouse input -- this is the core + routine for flushinp() */ + +void PDC_flushinp(void) +{ + PDC_LOG(("PDC_flushinp() - called\n")); + PDC_key_queue_low = PDC_key_queue_high = 0; +} + +bool PDC_has_mouse( void) +{ + return TRUE; +} + +int PDC_mouse_set(void) +{ + /* If turning on mouse input: Set ENABLE_MOUSE_INPUT, and clear + all other flags, including the extended flags; + If turning off the mouse: Set QuickEdit Mode to the status it + had on startup, and clear all other flags */ + + return OK; +} + +int PDC_modifiers_set(void) +{ + return OK; +} diff --git a/wingui/pdcscrn.c b/wingui/pdcscrn.c new file mode 100644 index 000000000..da4b81fe6 --- /dev/null +++ b/wingui/pdcscrn.c @@ -0,0 +1,2431 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" +#include +#include +#include +#ifdef WIN32_LEAN_AND_MEAN + #include + #include +#endif + +COLORREF PDC_palette[PDC_MAXCOL]; +int PDC_blink_state = 0; + +static int menu_shown = 1; +static int min_lines = 25, max_lines = 25; +static int min_cols = 80, max_cols = 80; + +/* Some older versions of Microsoft C/C++ don't understand about +inlined functions. Until we puzzle out which ones do and which +don't, we'll just leave "inlined" functions as plain old static +functions. */ + +#ifdef _MSC_VER +#define INLINE static +#else +#define INLINE static inline +#endif + +static int add_mouse( int button, const int action, const int x, const int y); +static int keep_size_within_bounds( int *lines, int *cols); +INLINE int set_default_sizes_from_registry( const int n_cols, const int n_rows, + const int xloc, const int yloc, const int menu_shown); +static int PDC_init_palette(void); + +#ifdef A_OVERLINE +#define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE | A_OVERLINE | A_STRIKEOUT) +#else +#define A_ALL_LINES (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE) +#endif + + /* If PDC_MAX_MOUSE_BUTTONS is undefined, it means the user hasn't */ + /* gotten a current 'curses.h' in which five-button mice are supported. */ + /* To handle this gracefully, we'll just fall back to three buttons. */ + +#ifndef PDC_MAX_MOUSE_BUTTONS + #define PDC_MAX_MOUSE_BUTTONS 3 +#endif + +#define VERTICAL_WHEEL_EVENT PDC_MAX_MOUSE_BUTTONS +#define HORIZONTAL_WHEEL_EVENT (PDC_MAX_MOUSE_BUTTONS + 1) + +int PDC_show_ctrl_alts = 0; + +/* RR: Removed statis on next line */ +bool PDC_bDone = FALSE; +static HWND originally_focussed_window; + +int debug_printf( const char *format, ...) +{ + static bool debugging = TRUE; + + if( debugging) + { + const char *output_filename = getenv( "PDC_DEBUG"); + + if( !output_filename) + debugging = FALSE; /* don't bother trying again */ + else + { + FILE *ofile = fopen( output_filename, "a"); + + if( ofile) + { + va_list argptr; + va_start( argptr, format); + vfprintf( ofile, format, argptr); + va_end( argptr); + fclose( ofile); + } + else + { + printf( "Opening '%s' failed\n", output_filename); + exit( 0); + debugging = FALSE; /* don't bother trying again */ + } + } + } + return( 0); +} + +HWND PDC_hWnd; +static int PDC_argc = 0; +static char **PDC_argv = NULL; + +static void final_cleanup( void) +{ + debug_printf( "final_cleanup: SP = %p\n", SP); + if (SP) + { + RECT rect; + + GetWindowRect( PDC_hWnd, &rect); + set_default_sizes_from_registry( SP->cols, SP->lines, + rect.left, rect.top, menu_shown); + } + PDC_LOG(( "final_cleanup: freeing fonts\n")); + PDC_transform_line( 0, 0, 0, NULL); /* free any fonts */ + if( originally_focussed_window) + SetForegroundWindow( originally_focussed_window); + if( PDC_argc) + { + int i; + + for( i = 0; i < PDC_argc; i++) + free( PDC_argv[i]); + free( PDC_argv); + PDC_argc = 0; + PDC_argv = NULL; + } +#ifdef USING_COMBINING_CHARACTER_SCHEME + PDC_expand_combined_characters( 0, NULL); /* free internal buffer */ +#endif + debug_printf( "reset foreground window\n"); +} + +void PDC_scr_close(void) +{ + PDC_LOG(("PDC_scr_close() - called\n")); + final_cleanup( ); + PDC_bDone = TRUE; +} + +/* NOTE that PDC_scr_free( ) is called only from delscreen( ), */ +/* which is rarely called. It appears that most programs simply */ +/* rely on the memory getting freed when the program terminates. */ + +void PDC_scr_free(void) +{ +} + +int PDC_n_rows, PDC_n_cols; +int PDC_cxChar, PDC_cyChar, PDC_key_queue_low = 0, PDC_key_queue_high = 0; +int PDC_key_queue[KEY_QUEUE_SIZE]; + + /* If the following is true, you can enter Unicode values by hitting */ + /* Alt and holding it down while typing the value of the character on */ + /* the numeric keypad (for decimal entry); _or_ you can hit Alt-Padplus */ + /* and then enter a hex value, while holding down the Alt key. In */ + /* either case, when you release the Alt key, the Unicode character */ + /* is added to the queue. For hex entry, 0-9 can come either from */ + /* the numeric keypad or the "usual" keyboard. */ +bool PDC_allow_numpad_unicode = TRUE; +static int numpad_unicode_value = 0; + +static void adjust_font_size( const int font_size_change); + +static void add_key_to_queue( const int new_key) +{ + const int new_idx = ((PDC_key_queue_high + 1) % KEY_QUEUE_SIZE); + /* This is usually 10, but is set to 16 if the user */ + /* hits ALT_PADPLUS and is about to enter a hex value: */ + static int unicode_radix = 10; + + if( PDC_allow_numpad_unicode) + { + int digit = -1; + + if( new_key >= ALT_PAD0 && new_key <= ALT_PAD9) + digit = new_key - ALT_PAD0; + /* In hex Unicode entry, you can enter digits on both */ + /* the numeric and "standard" keyboards : */ + if( unicode_radix == 16 && new_key >= ALT_0 && new_key <= ALT_9) + digit = new_key - ALT_0; + if( unicode_radix == 16 && new_key >= ALT_A && new_key <= ALT_F) + digit = new_key - ALT_A + 10; + if( digit >= 0) + { + numpad_unicode_value = numpad_unicode_value * unicode_radix + digit; + return; + } + if( new_key == ALT_PADPLUS) + { /* signal to begin hex Unicode entry */ + unicode_radix = 16; + return; + } + } + unicode_radix = 10; + if( new_idx != PDC_key_queue_low) + { + PDC_key_queue[PDC_key_queue_high] = new_key; + PDC_key_queue_high = new_idx; + } +} + +/************************************************************************ + * Table for key code translation of function keys in keypad mode * + * These values are for strict IBM keyboard compatibles only * + ************************************************************************/ + +typedef struct +{ + unsigned short normal; + unsigned short shift; + unsigned short control; + unsigned short alt; + unsigned short extended; +} KPTAB; + + +static const KPTAB kptab[] = +{ + {0, 0, 0, 0, 0 }, /* 0 */ + {0, 0, 0, 0, 0 }, /* 1 VK_LBUTTON */ + {0, 0, 0, 0, 0 }, /* 2 VK_RBUTTON */ + {0, 'a', 'b', 'c', 0 }, /* 3 VK_CANCEL */ + {0, 0, 0, 0, 0 }, /* 4 VK_MBUTTON */ + {0, 0, 0, 0, 0 }, /* 5 */ + {0, 0, 0, 0, 0 }, /* 6 */ + {0, 0, 0, 0, 0 }, /* 7 */ + {0x08, 0x08, 0x7F, ALT_BKSP, 0 }, /* 8 VK_BACK */ + {0x09, KEY_BTAB, CTL_TAB, ALT_TAB, 999 }, /* 9 VK_TAB */ + {0, 0, 0, 0, 0 }, /* 10 */ + {0, 0, 0, 0, 0 }, /* 11 */ + {KEY_B2, 0x35, CTL_PAD5, ALT_PAD5, 0 }, /* 12 VK_CLEAR */ + {0x0D, 0x0D, CTL_ENTER, ALT_ENTER, 1 }, /* 13 VK_RETURN */ + {0, 0, 0, 0, 0 }, /* 14 */ + {0, 0, 0, 0, 0 }, /* 15 */ + {0, 0, 0, 0, 0 }, /* 16 VK_SHIFT HANDLED SEPARATELY */ + {0, 0, 0, 0, 0 }, /* 17 VK_CONTROL HANDLED SEPARATELY */ + {0, 0, 0, 0, 0 }, /* 18 VK_MENU HANDLED SEPARATELY */ + {0, 0, 0, 0, 0 }, /* 19 VK_PAUSE */ + {0, 0, 0, 0, 0 }, /* 20 VK_CAPITAL HANDLED SEPARATELY */ + {0, 0, 0, 0, 0 }, /* 21 VK_HANGUL */ + {0, 0, 0, 0, 0 }, /* 22 */ + {0, 0, 0, 0, 0 }, /* 23 VK_JUNJA */ + {0, 0, 0, 0, 0 }, /* 24 VK_FINAL */ + {0, 0, 0, 0, 0 }, /* 25 VK_HANJA */ + {0, 0, 0, 0, 0 }, /* 26 */ + {0x1B, 0x1B, 0x1B, ALT_ESC, 0 }, /* 27 VK_ESCAPE */ + {0, 0, 0, 0, 0 }, /* 28 VK_CONVERT */ + {0, 0, 0, 0, 0 }, /* 29 VK_NONCONVERT */ + {0, 0, 0, 0, 0 }, /* 30 VK_ACCEPT */ + {0, 0, 0, 0, 0 }, /* 31 VK_MODECHANGE */ + {0x20, 0x20, 0x20, 0x20, 0 }, /* 32 VK_SPACE */ + {KEY_A3, 0x39, CTL_PAD9, ALT_PAD9, 3 }, /* 33 VK_PRIOR */ + {KEY_C3, 0x33, CTL_PAD3, ALT_PAD3, 4 }, /* 34 VK_NEXT */ + {KEY_C1, 0x31, CTL_PAD1, ALT_PAD1, 5 }, /* 35 VK_END */ + {KEY_A1, 0x37, CTL_PAD7, ALT_PAD7, 6 }, /* 36 VK_HOME */ + {KEY_B1, 0x34, CTL_PAD4, ALT_PAD4, 7 }, /* 37 VK_LEFT */ + {KEY_A2, 0x38, CTL_PAD8, ALT_PAD8, 8 }, /* 38 VK_UP */ + {KEY_B3, 0x36, CTL_PAD6, ALT_PAD6, 9 }, /* 39 VK_RIGHT */ + {KEY_C2, 0x32, CTL_PAD2, ALT_PAD2, 10 }, /* 40 VK_DOWN */ + {0, 0, 0, 0, 0 }, /* 41 VK_SELECT */ + {0, 0, 0, 0, 0 }, /* 42 VK_PRINT */ + {0, 0, 0, 0, 0 }, /* 43 VK_EXECUTE */ + {0, 0, 0, 0, 0 }, /* 44 VK_SNAPSHOT*/ + {PAD0, 0x30, CTL_PAD0, ALT_PAD0, 11 }, /* 45 VK_INSERT */ + {PADSTOP, 0x2E, CTL_PADSTOP, ALT_PADSTOP,12 }, /* 46 VK_DELETE */ + {0, 0, 0, 0, 0 }, /* 47 VK_HELP */ + {0x30, 0x29, 0, ALT_0, 0 }, /* 48 */ + {0x31, 0x21, 0, ALT_1, 0 }, /* 49 */ + {0x32, 0x40, 0, ALT_2, 0 }, /* 50 */ + {0x33, 0x23, 0, ALT_3, 0 }, /* 51 */ + {0x34, 0x24, 0, ALT_4, 0 }, /* 52 */ + {0x35, 0x25, 0, ALT_5, 0 }, /* 53 */ + {0x36, 0x5E, 0, ALT_6, 0 }, /* 54 */ + {0x37, 0x26, 0, ALT_7, 0 }, /* 55 */ + {0x38, 0x2A, 0, ALT_8, 0 }, /* 56 */ + {0x39, 0x28, 0, ALT_9, 0 }, /* 57 */ + {0, 0, 0, 0, 0 }, /* 58 */ + {0, 0, 0, 0, 0 }, /* 59 */ + {0, 0, 0, 0, 0 }, /* 60 */ + {0, 0, 0, 0, 0 }, /* 61 */ + {0, 0, 0, 0, 0 }, /* 62 */ + {0, 0, 0, 0, 0 }, /* 63 */ + {0, 0, 0, 0, 0 }, /* 64 */ + {0x61, 0x41, 0x01, ALT_A, 0 }, /* 65 */ + {0x62, 0x42, 0x02, ALT_B, 0 }, /* 66 */ + {0x63, 0x43, 0x03, ALT_C, 0 }, /* 67 */ + {0x64, 0x44, 0x04, ALT_D, 0 }, /* 68 */ + {0x65, 0x45, 0x05, ALT_E, 0 }, /* 69 */ + {0x66, 0x46, 0x06, ALT_F, 0 }, /* 70 */ + {0x67, 0x47, 0x07, ALT_G, 0 }, /* 71 */ + {0x68, 0x48, 0x08, ALT_H, 0 }, /* 72 */ + {0x69, 0x49, 0x09, ALT_I, 0 }, /* 73 */ + {0x6A, 0x4A, 0x0A, ALT_J, 0 }, /* 74 */ + {0x6B, 0x4B, 0x0B, ALT_K, 0 }, /* 75 */ + {0x6C, 0x4C, 0x0C, ALT_L, 0 }, /* 76 */ + {0x6D, 0x4D, 0x0D, ALT_M, 0 }, /* 77 */ + {0x6E, 0x4E, 0x0E, ALT_N, 0 }, /* 78 */ + {0x6F, 0x4F, 0x0F, ALT_O, 0 }, /* 79 */ + {0x70, 0x50, 0x10, ALT_P, 0 }, /* 80 */ + {0x71, 0x51, 0x11, ALT_Q, 0 }, /* 81 */ + {0x72, 0x52, 0x12, ALT_R, 0 }, /* 82 */ + {0x73, 0x53, 0x13, ALT_S, 0 }, /* 83 */ + {0x74, 0x54, 0x14, ALT_T, 0 }, /* 84 */ + {0x75, 0x55, 0x15, ALT_U, 0 }, /* 85 */ + {0x76, 0x56, 0x16, ALT_V, 0 }, /* 86 */ + {0x77, 0x57, 0x17, ALT_W, 0 }, /* 87 */ + {0x78, 0x58, 0x18, ALT_X, 0 }, /* 88 */ + {0x79, 0x59, 0x19, ALT_Y, 0 }, /* 89 */ + {0x7A, 0x5A, 0x1A, ALT_Z, 0 }, /* 90 */ + {0, 0, 0, 0, 0 }, /* 91 VK_LWIN */ + {0, 0, 0, 0, 0 }, /* 92 VK_RWIN */ + {0, 0, 0, 0, 13 }, /* 93 VK_APPS */ + {0, 0, 0, 0, 0 }, /* 94 */ + {0, 0, 0, 0, 0 }, /* 95 */ + {0x30, 0, CTL_PAD0, ALT_PAD0, 0 }, /* 96 VK_NUMPAD0 */ + {0x31, 0, CTL_PAD1, ALT_PAD1, 0 }, /* 97 VK_NUMPAD1 */ + {0x32, 0, CTL_PAD2, ALT_PAD2, 0 }, /* 98 VK_NUMPAD2 */ + {0x33, 0, CTL_PAD3, ALT_PAD3, 0 }, /* 99 VK_NUMPAD3 */ + {0x34, 0, CTL_PAD4, ALT_PAD4, 0 }, /* 100 VK_NUMPAD4 */ + {0x35, 0, CTL_PAD5, ALT_PAD5, 0 }, /* 101 VK_NUMPAD5 */ + {0x36, 0, CTL_PAD6, ALT_PAD6, 0 }, /* 102 VK_NUMPAD6 */ + {0x37, 0, CTL_PAD7, ALT_PAD7, 0 }, /* 103 VK_NUMPAD7 */ + {0x38, 0, CTL_PAD8, ALT_PAD8, 0 }, /* 104 VK_NUMPAD8 */ + {0x39, 0, CTL_PAD9, ALT_PAD9, 0 }, /* 105 VK_NUMPAD9 */ + {PADSTAR, SHF_PADSTAR,CTL_PADSTAR, ALT_PADSTAR,999 }, /* 106 VK_MULTIPLY*/ + {PADPLUS, SHF_PADPLUS,CTL_PADPLUS, ALT_PADPLUS,999 }, /* 107 VK_ADD */ + {0, 0, 0, 0, 0 }, /* 108 VK_SEPARATOR */ + {PADMINUS, SHF_PADMINUS,CTL_PADMINUS,ALT_PADMINUS,999}, /* 109 VK_SUBTRACT*/ + {0x2E, 0, CTL_PADSTOP, ALT_PADSTOP,0 }, /* 110 VK_DECIMAL */ + {PADSLASH, SHF_PADSLASH,CTL_PADSLASH,ALT_PADSLASH,2 }, /* 111 VK_DIVIDE */ + {KEY_F(1), KEY_F(13), KEY_F(25), KEY_F(37), 0 }, /* 112 VK_F1 */ + {KEY_F(2), KEY_F(14), KEY_F(26), KEY_F(38), 0 }, /* 113 VK_F2 */ + {KEY_F(3), KEY_F(15), KEY_F(27), KEY_F(39), 0 }, /* 114 VK_F3 */ + {KEY_F(4), KEY_F(16), KEY_F(28), KEY_F(40), 0 }, /* 115 VK_F4 */ + {KEY_F(5), KEY_F(17), KEY_F(29), KEY_F(41), 0 }, /* 116 VK_F5 */ + {KEY_F(6), KEY_F(18), KEY_F(30), KEY_F(42), 0 }, /* 117 VK_F6 */ + {KEY_F(7), KEY_F(19), KEY_F(31), KEY_F(43), 0 }, /* 118 VK_F7 */ + {KEY_F(8), KEY_F(20), KEY_F(32), KEY_F(44), 0 }, /* 119 VK_F8 */ + {KEY_F(9), KEY_F(21), KEY_F(33), KEY_F(45), 0 }, /* 120 VK_F9 */ + {KEY_F(10), KEY_F(22), KEY_F(34), KEY_F(46), 0 }, /* 121 VK_F10 */ + {KEY_F(11), KEY_F(23), KEY_F(35), KEY_F(47), 0 }, /* 122 VK_F11 */ + {KEY_F(12), KEY_F(24), KEY_F(36), KEY_F(48), 0 }, /* 123 VK_F12 */ + + /* 124 through 218 */ + + {0, 0, 0, 0, 0}, /* 124 VK_F13 */ + {0, 0, 0, 0, 0}, /* 125 VK_F14 */ + {0, 0, 0, 0, 0}, /* 126 VK_F15 */ + {0, 0, 0, 0, 0}, /* 127 VK_F16 */ + {0, 0, 0, 0, 0}, /* 128 VK_F17 */ + {0, 0, 0, 0, 0}, /* 129 VK_F18 */ + {0, 0, 0, 0, 0}, /* 130 VK_F19 */ + {0, 0, 0, 0, 0}, /* 131 VK_F20 */ + {0, 0, 0, 0, 0}, /* 132 VK_F21 */ + {0, 0, 0, 0, 0}, /* 133 VK_F22 */ + {0, 0, 0, 0, 0}, /* 134 VK_F23 */ + {0, 0, 0, 0, 0}, /* 135 VK_F24 */ + {0, 0, 0, 0, 0}, /* 136 unassigned */ + {0, 0, 0, 0, 0}, /* 137 unassigned */ + {0, 0, 0, 0, 0}, /* 138 unassigned */ + {0, 0, 0, 0, 0}, /* 139 unassigned */ + {0, 0, 0, 0, 0}, /* 140 unassigned */ + {0, 0, 0, 0, 0}, /* 141 unassigned */ + {0, 0, 0, 0, 0}, /* 142 unassigned */ + {0, 0, 0, 0, 0}, /* 143 unassigned */ + {0, 0, 0, 0, 0}, /* 144 VK_NUMLOCK */ + {0, 0, 0, 0, 0}, /* 145 VKSCROLL */ + {0, 0, 0, 0, 0}, /* 146 OEM specific */ + {0, 0, 0, 0, 0}, /* 147 OEM specific */ + {0, 0, 0, 0, 0}, /* 148 OEM specific */ + {0, 0, 0, 0, 0}, /* 149 OEM specific */ + {0, 0, 0, 0, 0}, /* 150 OEM specific */ + {0, 0, 0, 0, 0}, /* 151 Unassigned */ + {0, 0, 0, 0, 0}, /* 152 Unassigned */ + {0, 0, 0, 0, 0}, /* 153 Unassigned */ + {0, 0, 0, 0, 0}, /* 154 Unassigned */ + {0, 0, 0, 0, 0}, /* 155 Unassigned */ + {0, 0, 0, 0, 0}, /* 156 Unassigned */ + {0, 0, 0, 0, 0}, /* 157 Unassigned */ + {0, 0, 0, 0, 0}, /* 158 Unassigned */ + {0, 0, 0, 0, 0}, /* 159 Unassigned */ + {0, 0, 0, 0, 0}, /* 160 VK_LSHIFT */ + {0, 0, 0, 0, 0}, /* 161 VK_RSHIFT */ + {0, 0, 0, 0, 0}, /* 162 VK_LCONTROL */ + {0, 0, 0, 0, 0}, /* 163 VK_RCONTROL */ + {0, 0, 0, 0, 0}, /* 164 VK_LMENU */ + {0, 0, 0, 0, 0}, /* 165 VK_RMENU */ + {0, 0, 0, 0, 14}, /* 166 VK_BROWSER_BACK */ + {0, 0, 0, 0, 15}, /* 167 VK_BROWSER_FORWARD */ + {0, 0, 0, 0, 16}, /* 168 VK_BROWSER_REFRESH */ + {0, 0, 0, 0, 17}, /* 169 VK_BROWSER_STOP */ + {0, 0, 0, 0, 18}, /* 170 VK_BROWSER_SEARCH */ + {0, 0, 0, 0, 19}, /* 171 VK_BROWSER_FAVORITES */ + {0, 0, 0, 0, 20}, /* 172 VK_BROWSER_HOME */ + {0, 0, 0, 0, 21}, /* 173 VK_VOLUME_MUTE */ + {0, 0, 0, 0, 22}, /* 174 VK_VOLUME_DOWN */ + {0, 0, 0, 0, 23}, /* 175 VK_VOLUME_UP */ + {0, 0, 0, 0, 24}, /* 176 VK_MEDIA_NEXT_TRACK */ + {0, 0, 0, 0, 25}, /* 177 VK_MEDIA_PREV_TRACK */ + {0, 0, 0, 0, 26}, /* 178 VK_MEDIA_STOP */ + {0, 0, 0, 0, 27}, /* 179 VK_MEDIA_PLAY_PAUSE */ + {0, 0, 0, 0, 28}, /* 180 VK_LAUNCH_MAIL */ + {0, 0, 0, 0, 29}, /* 181 VK_LAUNCH_MEDIA_SELECT */ + {0, 0, 0, 0, 30}, /* 182 VK_LAUNCH_APP1 */ + {0, 0, 0, 0, 31}, /* 183 VK_LAUNCH_APP2 */ + {0, 0, 0, 0, 0}, /* 184 Reserved */ + {0, 0, 0, 0, 0}, /* 185 Reserved */ + {';', ':', 0, ALT_SEMICOLON, 0}, /* 186 VK_OEM_1 */ + {'=', '+', 0, ALT_EQUAL, 0}, /* 187 VK_OEM_PLUS */ + {',', '<', 0, ALT_COMMA, 0}, /* 188 VK_OEM_COMMA */ + {'-', '_', 0, ALT_MINUS, 0}, /* 189 VK_OEM_MINUS */ + {'.', '>', 0, ALT_STOP, 0}, /* 190 VK_OEM_PERIOD */ + {'/', '?', 0, ALT_FSLASH, 0}, /* 191 VK_OEM_2 */ + {'`', '~', 0, ALT_BQUOTE, 0}, /* 192 VK_OEM_3 */ + {0, 0, 0, 0, 0}, /* 193 */ + {0, 0, 0, 0, 0}, /* 194 */ + {0, 0, 0, 0, 0}, /* 195 */ + {0, 0, 0, 0, 0}, /* 196 */ + {0, 0, 0, 0, 0}, /* 197 */ + {0, 0, 0, 0, 0}, /* 198 */ + {0, 0, 0, 0, 0}, /* 199 */ + {0, 0, 0, 0, 0}, /* 200 */ + {0, 0, 0, 0, 0}, /* 201 */ + {0, 0, 0, 0, 0}, /* 202 */ + {0, 0, 0, 0, 0}, /* 203 */ + {0, 0, 0, 0, 0}, /* 204 */ + {0, 0, 0, 0, 0}, /* 205 */ + {0, 0, 0, 0, 0}, /* 206 */ + {0, 0, 0, 0, 0}, /* 207 */ + {0, 0, 0, 0, 0}, /* 208 */ + {0, 0, 0, 0, 0}, /* 209 */ + {0, 0, 0, 0, 0}, /* 210 */ + {0, 0, 0, 0, 0}, /* 211 */ + {0, 0, 0, 0, 0}, /* 212 */ + {0, 0, 0, 0, 0}, /* 213 */ + {0, 0, 0, 0, 0}, /* 214 */ + {0, 0, 0, 0, 0}, /* 215 */ + {0, 0, 0, 0, 0}, /* 216 */ + {0, 0, 0, 0, 0}, /* 217 */ + {0, 0, 0, 0, 0}, /* 218 */ + {0x5B, 0x7B, 0x1B, ALT_LBRACKET,0 }, /* 219 VK_OEM_4 */ + {0x5C, 0x7C, 0x1C, ALT_BSLASH, 0 }, /* 220 VK_OEM_5 */ + {0x5D, 0x7D, 0x1D, ALT_RBRACKET,0 }, /* 221 VK_OEM_6 */ + {'\'', '"', 0x27, ALT_FQUOTE, 0 }, /* 222 VK_OEM_7 */ + {0, 0, 0, 0, 0 }, /* 223 VK_OEM_8 */ + {0, 0, 0, 0, 0 }, /* 224 */ + {0, 0, 0, 0, 0 } /* 225 */ +}; +/* End of kptab[] */ + +static const KPTAB ext_kptab[] = +{ + {0, 0, 0, 0, }, /* 0 MUST BE EMPTY */ + {PADENTER, SHF_PADENTER, CTL_PADENTER, ALT_PADENTER}, /* 1 13 */ + {PADSLASH, SHF_PADSLASH, CTL_PADSLASH, ALT_PADSLASH}, /* 2 111 */ + {KEY_PPAGE, KEY_SPREVIOUS, CTL_PGUP, ALT_PGUP }, /* 3 33 */ + {KEY_NPAGE, KEY_SNEXT, CTL_PGDN, ALT_PGDN }, /* 4 34 */ + {KEY_END, KEY_SEND, CTL_END, ALT_END }, /* 5 35 */ + {KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME }, /* 6 36 */ + {KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT }, /* 7 37 */ + {KEY_UP, KEY_SUP, CTL_UP, ALT_UP }, /* 8 38 */ + {KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT }, /* 9 39 */ + {KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN }, /* 10 40 */ + {KEY_IC, KEY_SIC, CTL_INS, ALT_INS }, /* 11 45 */ + {KEY_DC, KEY_SDC, CTL_DEL, ALT_DEL }, /* 12 46 */ + {0, 0, 0, 0 }, /* 13 93 VK_APPS */ + {0, 0, 0, 0, }, /* 14 166 VK_BROWSER_BACK */ + {0, 0, 0, 0, }, /* 15 167 VK_BROWSER_FORWARD */ + {0, 0, 0, 0, }, /* 16 168 VK_BROWSER_REFRESH */ + {0, 0, 0, 0, }, /* 17 169 VK_BROWSER_STOP */ + {0, 0, 0, 0, }, /* 18 170 VK_BROWSER_SEARCH */ + {0, 0, 0, 0, }, /* 19 171 VK_BROWSER_FAVORITES */ + {0, 0, 0, 0, }, /* 20 172 VK_BROWSER_HOME */ + {0, 0, 0, 0, }, /* 21 173 VK_VOLUME_MUTE */ + {0, 0, 0, 0, }, /* 22 174 VK_VOLUME_DOWN */ + {0, 0, 0, 0, }, /* 23 175 VK_VOLUME_UP */ + {0, 0, 0, 0, }, /* 24 176 VK_MEDIA_NEXT_TRACK */ + {0, 0, 0, 0, }, /* 25 177 VK_MEDIA_PREV_TRACK */ + {0, 0, 0, 0, }, /* 26 178 VK_MEDIA_STOP */ + {0, 0, 0, 0, }, /* 27 179 VK_MEDIA_PLAY_PAUSE */ + {0, 0, 0, 0, }, /* 28 180 VK_LAUNCH_MAIL */ + {0, 0, 0, 0, }, /* 29 181 VK_LAUNCH_MEDIA_SELECT */ + {0, 0, 0, 0, }, /* 30 182 VK_LAUNCH_APP1 */ + {0, 0, 0, 0, }, /* 31 183 VK_LAUNCH_APP2 */ +}; + + +/* Mouse handling is done as follows: + + What we want is a setup wherein, if the user presses and releases a +mouse button within SP->mouse_wait milliseconds, there will be a +KEY_MOUSE issued through getch( ) and the "button state" for that button +will be set to BUTTON_CLICKED. + + If the user presses and releases the button, and it takes _longer_ +than SP->mouse_wait milliseconds, then there should be a KEY_MOUSE +issued with the "button state" set to BUTTON_PRESSED. Then, later, +another KEY_MOUSE with a BUTTON_RELEASED. + + To accomplish this: when a message such as WM_LBUTTONDOWN, +WM_RBUTTONDOWN, or WM_MBUTTONDOWN is issued (and more recently WM_XBUTTONDOWN +for five-button mice), we set up a timer with a period of SP->mouse_wait +milliseconds. There are then two possibilities. The user will release the +button quickly (so it's a "click") or they won't (and it's a "press/release"). + + In the first case, we'll get the WM_xBUTTONUP message before the +WM_TIMER one. We'll kill the timer and set up the BUTTON_CLICKED state. (*) + + In the second case, we'll get the WM_TIMER message first, so we'll +set the BUTTON_PRESSED state and kill the timer. Eventually, the user +will get around to letting go of the mouse button, and we'll get that +WM_xBUTTONUP message. At that time, we'll set the BUTTON_RELEASED state +and add the second KEY_MOUSE to the key queue. + + Also, note that if there is already a KEY_MOUSE to the queue, there's +no point in adding another one. At least at present, the actual mouse +events aren't queued anyway. So if there was, say, a click and then a +release without getch( ) being called in between, you'd then have two +KEY_MOUSEs on the queue, but would have lost all information about what +the first one actually was. Hence the code near the end of this function +to ensure there isn't already a KEY_MOUSE in the queue. + + Also, a note about wheel handling. Pre-Vista, you could just say +"the wheel went up" or "the wheel went down". Vista introduced the possibility +that the mouse motion could be a smoothly varying quantity. So on each +mouse move, we add in the amount moved, then check to see if that's +enough to trigger a wheel up/down event (or possibly several). The idea +is that whereas before, each movement would be 120 units (the default), +you might now get a series of 40-unit moves and should emit a wheel up/down +event on every third move. + + (*) Things are actually slightly more complicated than this. In general, +it'll just be a plain old BUTTON_CLICKED state. But if there was another +BUTTON_CLICKED within the last 2 * SP->mouse_wait milliseconds, then this +must be a _double_ click, so we set the BUTTON_DOUBLE_CLICKED state. And +if, within that time frame, there was a double or triple click, then we +set the BUTTON_TRIPLE_CLICKED state. There isn't a "quad" or higher state, +so if you quadruple-click the mouse, with each click separated by less +than 2 * SP->mouse_wait milliseconds, then the messages sent will be +BUTTON_CLICKED, BUTTON_DOUBLE_CLICKED, BUTTON_TRIPLE_CLICKED, and +then another BUTTON_TRIPLE_CLICKED. */ + +static bool mouse_key_already_in_queue( void) +{ + int i = PDC_key_queue_low; + + while( i != PDC_key_queue_high) + { + if( PDC_key_queue[i] == KEY_MOUSE) + { + debug_printf( "Mouse key already in queue\n"); + return( TRUE); + } + i = (i + 1) % KEY_QUEUE_SIZE; + } + return( FALSE); +} + +static int set_mouse( const int button_index, const int button_state, + const int x, const int y) +{ + int n_key_mouse_to_add = 1; + POINT pt; + + /* If there is already a KEY_MOUSE in the queue, we */ + /* don't really want to add another one. See above. */ + if( mouse_key_already_in_queue( )) + return( -1); + pt.x = x; + pt.y = y; + if( button_index == -1) /* mouse moved, no button */ + n_key_mouse_to_add = 1; + else + { + memset(&SP->mouse_status, 0, sizeof(MOUSE_STATUS)); + if( button_index < PDC_MAX_MOUSE_BUTTONS) + { + SP->mouse_status.button[button_index] = (short)button_state; + if( button_index < 3) + SP->mouse_status.changes = (1 << button_index); + else + SP->mouse_status.changes = (0x40 << button_index); + } + else /* actually a wheel mouse movement */ + { /* button_state = number of units moved */ + static int mouse_wheel_vertical_loc = 0; + static int mouse_wheel_horizontal_loc = 0; + const int mouse_wheel_sensitivity = 120; + + n_key_mouse_to_add = 0; + if( button_index == VERTICAL_WHEEL_EVENT) + { + mouse_wheel_vertical_loc += button_state; + while( mouse_wheel_vertical_loc > mouse_wheel_sensitivity / 2) + { + n_key_mouse_to_add++; + mouse_wheel_vertical_loc -= mouse_wheel_sensitivity; + SP->mouse_status.changes |= PDC_MOUSE_WHEEL_UP; + } + while( mouse_wheel_vertical_loc < -mouse_wheel_sensitivity / 2) + { + n_key_mouse_to_add++; + mouse_wheel_vertical_loc += mouse_wheel_sensitivity; + SP->mouse_status.changes |= PDC_MOUSE_WHEEL_DOWN; + } + } + else /* must be a horizontal event: */ + { + mouse_wheel_horizontal_loc += button_state; + while( mouse_wheel_horizontal_loc > mouse_wheel_sensitivity / 2) + { + n_key_mouse_to_add++; + mouse_wheel_horizontal_loc -= mouse_wheel_sensitivity; + SP->mouse_status.changes |= PDC_MOUSE_WHEEL_RIGHT; + } + while( mouse_wheel_horizontal_loc < -mouse_wheel_sensitivity / 2) + { + n_key_mouse_to_add++; + mouse_wheel_horizontal_loc += mouse_wheel_sensitivity; + SP->mouse_status.changes |= PDC_MOUSE_WHEEL_LEFT; + } + } + /* I think it may be that for wheel events, we */ + /* return x = y = -1, rather than getting the */ + /* actual mouse position. I don't like this, but */ + /* I like messing up existing apps even less. */ + pt.x = pt.y = -1; + } + } + if( button_state == BUTTON_MOVED) + SP->mouse_status.changes |= (button_index >= 0 ? PDC_MOUSE_MOVED : PDC_MOUSE_POSITION); + SP->mouse_status.x = pt.x; + SP->mouse_status.y = pt.y; + { + int i, button_flags = 0; + + if( GetKeyState( VK_MENU) & 0x8000) + button_flags |= PDC_BUTTON_ALT; + + if( GetKeyState( VK_SHIFT) & 0x8000) + button_flags |= PDC_BUTTON_SHIFT; + + if( GetKeyState( VK_CONTROL) & 0x8000) + button_flags |= PDC_BUTTON_CONTROL; + + for (i = 0; i < PDC_MAX_MOUSE_BUTTONS; i++) + SP->mouse_status.button[i] |= button_flags; + } + /* If the window is maximized, the click may occur just */ + /* outside the "real" screen area. If so, we again */ + /* don't want to add a key to the queue: */ + if( SP->mouse_status.x >= PDC_n_cols || SP->mouse_status.y >= PDC_n_rows) + n_key_mouse_to_add = 0; + /* OK, there isn't a KEY_MOUSE already in the queue. */ + /* So we'll add one (or zero or more, for wheel mice): */ + while( n_key_mouse_to_add--) + add_key_to_queue( KEY_MOUSE); + return( 0); +} + + /* The following should be #defined in 'winuser.h', but such is */ + /* not always the case. The following fixes the exceptions: */ +#ifndef WM_MOUSEWHEEL + #define WM_MOUSEWHEEL 0x020A +#endif +#ifndef WM_MOUSEHWHEEL + #define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WM_XBUTTONDOWN + #define WM_XBUTTONDOWN 0x020B + #define WM_XBUTTONUP 0x020C +#endif +#ifndef MK_XBUTTON1 + #define MK_XBUTTON1 0x0020 + #define MK_XBUTTON2 0x0040 +#endif + +#define TIMER_ID_FOR_BLINKING 0x2000 + +/* When first loading a font, we use 'get_character_sizes' to briefly +load the (non-bold, non-italic flavor of the) font, get its height and +width, and call GetFontUnicodeRanges to determine which characters are +actually available from that font. That set of ranges is used so that, +when we come across characters not in the font, we can switch to a +"fallback" font (Unifont, most likely). */ + +static void get_character_sizes( const HWND hwnd, + int *xchar_size, int *ychar_size) +{ + HFONT hFont = PDC_get_font_handle( 0); + HFONT prev_font; + HDC hdc = GetDC (hwnd) ; + TEXTMETRIC tm ; +#ifdef USE_FALLBACK_FONT + DWORD size; +#endif + + prev_font = SelectObject (hdc, hFont); + GetTextMetrics (hdc, &tm) ; +#ifdef USE_FALLBACK_FONT + assert( !PDC_unicode_range_data); + size = GetFontUnicodeRanges( hdc, NULL); + PDC_unicode_range_data = (GLYPHSET *)calloc( 1, size); + PDC_unicode_range_data->cbThis = size; + size = GetFontUnicodeRanges( hdc, PDC_unicode_range_data); +#endif /* #ifdef USE_FALLBACK_FONT */ + SelectObject( hdc, prev_font); + ReleaseDC (hwnd, hdc) ; + DeleteObject( hFont); + *xchar_size = tm.tmAveCharWidth ; + *ychar_size = tm.tmHeight; +} + +/* Cygwin lacks _splitpath, _wsplitpath. THE FOLLOWING ARE NOT FULLY +TESTED IMPLEMENTATIONS OF THOSE TWO FUNCTIONS, because the only use we +make of them is to get fname. (Though I did write a little test program, +and they seem to work.) */ + +#ifdef __CYGWIN__ +#ifdef PDC_WIDE +static void my_wsplitpath( const wchar_t *path, wchar_t *drive, + wchar_t *dir, wchar_t *fname, wchar_t *ext) +{ + size_t i, loc = 0; + + assert( path); + assert( fname); + if( path[0] && path[1] == ':') + { + if( drive) + { + drive[0] = path[0]; + drive[1] = ':'; + drive[2] = '\0'; + } + path += 2; + } + else if( drive) + *drive = '\0'; + for( i = 0; path[i]; i++) + if( path[i] == '/' || path[i] == '\\') + loc = i + 1; + if( dir) + { + memcpy( dir, path, loc * sizeof( wchar_t)); + dir[loc] = '\0'; + } + if( loc) + path += loc; + loc = 0; + while( path[loc] && path[loc] != '.') + loc++; + if( fname) + { + memcpy( fname, path, loc * sizeof( wchar_t)); + fname[loc] = '\0'; + } + if( ext) + wcscpy( ext, path + loc); +} +#endif /* #ifdef PDC_WIDE */ + +static void my_splitpath( const char *path, char *drive, + char *dir, char *fname, char *ext) +{ + size_t i, loc = 0; + + assert( path); + assert( fname); + if( path[0] && path[1] == ':') + { + if( drive) + { + drive[0] = path[0]; + drive[1] = ':'; + drive[2] = '\0'; + } + path += 2; + } + else if( drive) + *drive = '\0'; + for( i = 0; path[i]; i++) + if( path[i] == '/' || path[i] == '\\') + loc = i + 1; + if( dir) + { + memcpy( dir, path, loc * sizeof( char)); + dir[loc] = '\0'; + } + if( loc) + path += loc; + loc = 0; + while( path[loc] && path[loc] != '.') + loc++; + if( fname) + { + memcpy( fname, path, loc * sizeof( char)); + fname[loc] = '\0'; + } + if( ext) + strcpy( ext, path + loc); +} +#else /* non-Cygwin case : */ + #define my_splitpath _splitpath + #define my_wsplitpath _wsplitpath + #define GOT_ARGV_ARGC +#endif /* #ifdef __CYGWIN__ */ + +/* This function looks at the full command line, which includes a fully +specified path to the executable and arguments; and strips out just the +name of the app, with the arguments optionally appended. Hence, + +C:\PDCURSES\WINGUI\TESTCURS.EXE arg1 arg2 + + would be reduced to 'Testcurs' (if include_args == 0) or +'Testcurs arg1 arg2' (if include_args == 1). The former case is used to +create a (hopefully unique) registry key for the app, so that the app's +specific settings (screen and font size) will be stored for the next run. +The latter case is used to generate a default window title. + + Unfortunately, this code has to do some pretty strange things. In the +Unicode (PDC_WIDE) case, we really should use __wargv; but that pointer +may or may not be NULL. If it's NULL, we fall back on __argv. In at +least one case, where this code is compiled into a DLL using MinGW and +then used in an app compiled with MS Visual C, __argv isn't set either, +and we drop back to looking at GetCommandLine( ). Which leads to a real +oddity: GetCommandLine( ) may return something such as, say, + +"C:\PDCurses\WinGUI\testcurs.exe" -lRussian + + ...which, after being run through _splitpath or _wsplitpath, becomes + +testcurs.exe" -lRussian + + The .exe" is removed, and the command-line arguments shifted or removed, +depending on the value of include_args. Pretty strange stuff. + + However, if one calls Xinitscr( ) and passed command-line arguments when +starting this library, those arguments will be stored in PDC_argc and +PDC_argv, and will be used instead of GetCommandLine. +*/ + +#ifdef UNICODE + #define my_stprintf wsprintf + #define my_tcslen wcslen +#ifdef __CYGWIN__ + /* Can't lowercase Unicode text in Cygwin */ + #define my_tcslwr +#elif defined(_MSC_VER) || defined(__WATCOMC__) + #define my_tcslwr _wcslwr +#else + #define my_tcslwr wcslwr +#endif /* __CYGWIN__ */ + #define my_tcscat wcscat + #define my_tcscpy wcscpy + #define my_stscanf swscanf + +#else /* UNICODE */ + + #define my_stprintf sprintf + #define my_tcslen strlen + #define my_tcslwr strlwr +#ifdef _MSC_VER + #define strlwr _strlwr +#endif + #define my_tcscat strcat + #define my_tcscpy strcpy + #define my_stscanf sscanf +#endif /* UNICODE */ + + +static void get_app_name( TCHAR *buff, const size_t buff_size, const bool include_args) +{ + int i; + size_t buff_space; +#ifdef GOT_ARGV_ARGC + int argc = (PDC_argc ? PDC_argc : __argc); + char **argv = (PDC_argc ? PDC_argv : __argv); +#else + int argc = PDC_argc; + char **argv = PDC_argv; +#endif + +#ifdef PDC_WIDE +#ifdef _MSC_VER + wchar_t **wargv = __wargv; +#else + wchar_t **wargv = NULL; +#endif +#ifdef GOT_ARGV_ARGC + /* in case we can not access the array directly try to get it otherwise */ + if( !wargv) { + wchar_t *cmd_linew = GetCommandLine( ); + if (cmd_linew) { + wargv = CommandLineToArgvW (cmd_linew, &argc); + } + } + if( wargv) + { + my_wsplitpath( wargv[0], NULL, NULL, buff, NULL); + if ( include_args) + { + buff_space = buff_size - my_tcslen( buff) - 1; + for ( i = 1; i < argc; i++) + { + size_t arg_len = my_tcslen( wargv[i]) + 1; + if ( buff_space < arg_len) { + break; + } + buff_space -= arg_len; + wcscat( buff, L" "); + wcscat( buff, wargv[i]); + } + } + } + else +#endif /* #ifdef GOT_ARGV_ARGC */ + if( argv) + { + char tbuff[MAX_PATH]; + my_splitpath( argv[0], NULL, NULL, tbuff, NULL); + if ( include_args) + { + buff_space = buff_size - strlen( tbuff) - 1; + for ( i = 1; i < argc; i++) + { + size_t arg_len = strlen( argv[i]) + 1; + if ( buff_space < arg_len) { + break; + } + buff_space -= arg_len; + strcat( tbuff, " "); + strcat( tbuff, argv[i]); + } + } + mbstowcs( buff, tbuff, strlen( tbuff) + 1); + } + else /* no __argv or PDC_argv pointer available */ + { + wchar_t *tptr; + + my_wsplitpath( GetCommandLine( ), NULL, NULL, buff, NULL); + my_tcslwr( buff + 1); + tptr = wcsstr( buff, L".exe\""); + if( tptr) + { + if( include_args) + memmove( tptr, tptr + 5, wcslen( tptr + 4) * sizeof( wchar_t)); + else + *tptr = '\0'; + } + } +#else /* non-Unicode case */ + if( argv) + { + my_splitpath( argv[0], NULL, NULL, buff, NULL); + debug_printf( "Path: %s; exe: %s\n", argv[0], buff); + if ( include_args) + { + buff_space = buff_size - my_tcslen( buff) - 1; + for ( i = 1; i < argc; i++) + { + size_t arg_len = my_tcslen( argv[i]) + 1; + if ( buff_space < arg_len) { + break; + } + buff_space -= arg_len; + strcat( buff, " "); + strcat( buff, argv[i]); + } + } + } + else /* no __argv pointer available */ + { + char *tptr; + + my_splitpath( GetCommandLine( ), NULL, NULL, buff, NULL); + strlwr( buff + 1); + tptr = strstr( buff, ".exe\""); + if( tptr) + { + if( include_args) + memmove( tptr, tptr + 5, strlen( tptr + 4)); + else + *tptr = '\0'; + } + } +#endif + my_tcslwr( buff + 1); +} + +/* This function extracts the first icon from the executable that is +executing this DLL */ + +INLINE HICON get_app_icon( ) +{ +#ifdef PDC_WIDE + wchar_t filename[MAX_PATH]; +#else + char filename[MAX_PATH]; +#endif + + HICON icon = NULL; + if ( GetModuleFileName( NULL, filename, sizeof(filename) ) != 0 ) + icon = ExtractIcon( 0, filename, 0 ); + return icon; +} + +/* This flavor of Curses tries to store the window and font sizes on +an app-by-app basis. To do this, it uses the above get_app_name( ) +function, then sets or gets a corresponding value from the Windoze +registry. The benefit should be that one can have one screen size/font +for, say, Testcurs, while having different settings for, say, Firework +or Rain or one's own programs. */ + +INLINE int set_default_sizes_from_registry( const int n_cols, const int n_rows, + const int xloc, const int yloc, const int menu_shown) +{ + DWORD is_new_key; + HKEY hNewKey; + long rval = RegCreateKeyEx( HKEY_CURRENT_USER, _T( "SOFTWARE\\PDCurses"), + 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, + 0, &hNewKey, &is_new_key); + + if( rval == ERROR_SUCCESS) + { + TCHAR buff[180]; + TCHAR key_name[MAX_PATH]; + + if( IsZoomed( PDC_hWnd)) /* -1x-1 indicates a maximized window */ + my_stprintf( buff, + _T( "-1x-1,%d,0,0,%d"), PDC_font_size, menu_shown); + else + my_stprintf( buff, + _T( "%dx%d,%d,%d,%d,%d"), n_cols, n_rows, PDC_font_size, + xloc, yloc, menu_shown); + my_stprintf( buff + my_tcslen( buff), + _T(";%d,%d,%d,%d:"), + min_lines, max_lines, + min_cols, max_cols); + my_tcscat( buff, PDC_font_name); + + get_app_name( key_name, MAX_PATH, FALSE); + rval = RegSetValueEx( hNewKey, key_name, 0, REG_SZ, + (BYTE *)buff, (DWORD)( my_tcslen( buff) * sizeof( TCHAR))); + RegCloseKey( hNewKey); + } + debug_printf( "Size: %d %d; %d\n", n_cols, n_rows, rval); + return( rval != ERROR_SUCCESS); +} + +/* If the window is maximized, there will usually be a fractional +character width at the right and bottom edges. The following code fills +that in with a black brush. It takes the "paint rectangle", the area +passed with a WM_PAINT message that specifies what chunk of the client +area needs to be redrawn. + + If the window is _not_ maximized, this shouldn't happen; the window +width/height should always be an integral multiple of the character +width/height, with no slivers at the right and bottom edges. */ + +static void fix_up_edges( const HDC hdc, const RECT *rect) +{ + const int x = PDC_n_cols * PDC_cxChar; + const int y = PDC_n_rows * PDC_cyChar; + + if( rect->right >= x || rect->bottom >= y) + { + const HBRUSH hOldBrush = + SelectObject( hdc, GetStockObject( BLACK_BRUSH)); + + SelectObject( hdc, GetStockObject( NULL_PEN)); + if( rect->right >= x) + Rectangle( hdc, x, rect->top, rect->right + 1, rect->bottom + 1); + if( rect->bottom >= y) + Rectangle( hdc, rect->left, y, rect->right + 1, rect->bottom + 1); + SelectObject( hdc, hOldBrush); + } +} + +static void adjust_font_size( const int font_size_change) +{ + PDC_font_size += font_size_change; + if( PDC_font_size < 2) + PDC_font_size = 2; + PDC_transform_line( 0, 0, 0, NULL); /* free any fonts */ + get_character_sizes( PDC_hWnd, &PDC_cxChar, &PDC_cyChar); + /* When the font size changes, do we want to keep */ + /* the window the same size (except to remove any */ + /* fractional character)? Or do we keep the number */ + /* of rows/columns the same? For the nonce, I'm */ + /* keeping the window size fixed if the window is */ + /* maximized, but keeping the number of rows/lines */ + /* fixed if it's windowed. That's my opinion. If */ + /* you disagree, I have others. */ + if( IsZoomed( PDC_hWnd)) + { + RECT client_rect; + HDC hdc; + + GetClientRect( PDC_hWnd, &client_rect); + PDC_n_rows = client_rect.bottom / PDC_cyChar; + PDC_n_cols = client_rect.right / PDC_cxChar; + keep_size_within_bounds( &PDC_n_rows, &PDC_n_cols); + PDC_resize_screen( PDC_n_rows, PDC_n_cols); + add_key_to_queue( KEY_RESIZE); + SP->resized = TRUE; + hdc = GetDC (PDC_hWnd) ; + GetClientRect( PDC_hWnd, &client_rect); + fix_up_edges( hdc, &client_rect); + ReleaseDC( PDC_hWnd, hdc) ; + } + else + { + PDC_resize_screen( PDC_n_rows, PDC_n_cols); + InvalidateRect( PDC_hWnd, NULL, FALSE); + } +} + +#define WM_ENLARGE_FONT (WM_USER + 1) +#define WM_SHRINK_FONT (WM_USER + 2) +#define WM_MARK_AND_COPY (WM_USER + 3) +#define WM_TOGGLE_MENU (WM_USER + 4) +#define WM_EXIT_GRACELESSLY (WM_USER + 5) +#define WM_CHOOSE_FONT (WM_USER + 6) + +static int add_resize_key = 1; + +/*man-start************************************************************** + +Resize limits +------------- + +### Synopsis + + 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); + +### Description + + For platforms supporting resizable windows (SDLx, WinGUI, X11). Some + programs may be unprepared for a resize event; for these, calling + this function with the max and min limits equal ensures that no + user resizing can be done. Other programs may require at least a + certain number, and/or no more than a certain number, of columns + and/or lines. + +### Portability + + PDCurses-only function. + +**man-end****************************************************************/ + +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) +{ + min_lines = max( new_min_lines, 2); + max_lines = max( new_max_lines, min_lines); + min_cols = max( new_min_cols, 2); + max_cols = max( new_max_cols, min_cols); +} + + /* The screen should hold the characters (PDC_cxChar * n_default_columns */ + /* pixels wide, similarly high). In width, we need two frame widths, */ + /* one on each side. Vertically, we need two frame heights, plus room */ + /* for the application title and the menu. */ + +static void adjust_window_size( int *xpixels, int *ypixels, int window_style, + const int menu_shown) +{ + RECT rect; + + rect.left = rect.top = 0; + rect.right = *xpixels; + rect.bottom = *ypixels; +/* printf( "Adjusting to %d, %d\n", *xpixels, *ypixels); */ + AdjustWindowRect( &rect, window_style, menu_shown); + *xpixels = rect.right - rect.left; + *ypixels = rect.bottom - rect.top; +} + +static int keep_size_within_bounds( int *lines, int *cols) +{ + int rval = 0; + + if( *lines < min_lines) + { + *lines = min_lines; + rval = 1; + } + else if( *lines > max_lines) + { + *lines = max_lines; + rval = 2; + } + if( *cols < min_cols) + { + *cols = min_cols; + rval |= 4; + } + else if( *cols > max_cols) + { + *cols = max_cols; + rval |= 8; + } + return( rval); +} + +INLINE int get_default_sizes_from_registry( int *n_cols, int *n_rows, + int *xloc, int *yloc, int *menu_shown) +{ + TCHAR data[100]; + DWORD size_out = sizeof( data); + HKEY hKey = 0; + long rval = RegOpenKeyEx( HKEY_CURRENT_USER, _T( "SOFTWARE\\PDCurses"), + 0, KEY_READ, &hKey); + + if( !hKey) + return( 1); + if( rval == ERROR_SUCCESS) + { + TCHAR key_name[MAX_PATH]; + + get_app_name( key_name, MAX_PATH, FALSE); + rval = RegQueryValueEx( hKey, key_name, + NULL, NULL, (BYTE *)data, &size_out); + if( rval == ERROR_SUCCESS) + { + int x = -1, y = -1, bytes_read = 0; + + my_stscanf( data, _T( "%dx%d,%d,%d,%d,%d;%d,%d,%d,%d:%n"), + &x, &y, &PDC_font_size, + xloc, yloc, menu_shown, + &min_lines, &max_lines, + &min_cols, &max_cols, + &bytes_read); + if( bytes_read > 0 && data[bytes_read - 1] == ':') + my_tcscpy( PDC_font_name, data + bytes_read); + if( n_cols) + *n_cols = x; + if( n_rows) + *n_rows = y; + if( *n_cols > 0 && *n_rows > 0) /* i.e., not maximized */ + keep_size_within_bounds( n_rows, n_cols); + } + RegCloseKey( hKey); + } + if( rval != ERROR_SUCCESS) + debug_printf( "get_default_sizes_from_registry error: %d\n", rval); + return( rval != ERROR_SUCCESS); +} + +/* Ensure that the dragged rectangle */ +/* is an even multiple of the char size */ +INLINE void HandleSizing( WPARAM wParam, LPARAM lParam ) +{ + RECT *rect = (RECT *)lParam; + RECT window_rect, client_rect; + int hadd, vadd, width, height; + int n_rows, n_cols; + int rounded_width, rounded_height; + + GetWindowRect( PDC_hWnd, &window_rect); + GetClientRect( PDC_hWnd, &client_rect); + hadd = (window_rect.right - window_rect.left) - client_rect.right; + vadd = (window_rect.bottom - window_rect.top) - client_rect.bottom; + width = rect->right - rect->left - hadd; + height = rect->bottom - rect->top - vadd; + + n_cols = (width + PDC_cxChar / 2) / PDC_cxChar; + n_rows = (height + PDC_cyChar / 2) / PDC_cyChar; + keep_size_within_bounds( &n_rows, &n_cols); + + rounded_width = hadd + n_cols * PDC_cxChar; + rounded_height = vadd + n_rows * PDC_cyChar; + + if( wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMLEFT + || wParam == WMSZ_BOTTOMRIGHT) + rect->bottom = rect->top + rounded_height; + if( wParam == WMSZ_TOP || wParam == WMSZ_TOPLEFT + || wParam == WMSZ_TOPRIGHT) + rect->top = rect->bottom - rounded_height; + if( wParam == WMSZ_RIGHT || wParam == WMSZ_BOTTOMRIGHT + || wParam == WMSZ_TOPRIGHT) + rect->right = rect->left + rounded_width; + if( wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT + || wParam == WMSZ_TOPLEFT) + rect->left = rect->right - rounded_width; +} + +/* Under Wine, it appears that the code to force the window size to be +an integral number of columns and rows doesn't work. This is because +WM_SIZING messages aren't sent (this is apparently fixed as of Wine 1.7.18, +though I've not tried it yet; I'm still on Wine 1.6, the stable branch.) +You can therefore end up in a loop where the code keeps trying to resize a +window that isn't actually resizing. So, _when running in Wine only_, +we want that code not to be executed... which means having to figure out: +are we running under Wine? Which means that when PDCurses/WinGUI is +initialized, we set the following 'wine_version' pointer. One could +actually call wine_version(), if not NULL, to get the current Wine +version. */ + +typedef const char *(CDECL *wine_version_func)(void); + +static wine_version_func wine_version; + +static void HandleSize( const WPARAM wParam, const LPARAM lParam) +{ + static WPARAM prev_wParam = (WPARAM)-99; + const unsigned n_xpixels = LOWORD (lParam); + const unsigned n_ypixels = HIWORD (lParam); + unsigned new_n_rows, new_n_cols; + + debug_printf( "WM_SIZE: wParam %x %d %d %d\n", (unsigned)wParam, + n_xpixels, n_ypixels, SP->resized); +/* if( wine_version) + printf( "Wine version: %s\n", wine_version( )); */ + + + if( wParam == SIZE_MINIMIZED ) + { + prev_wParam = SIZE_MINIMIZED; + return; + } + new_n_rows = n_ypixels / PDC_cyChar; + new_n_cols = n_xpixels / PDC_cxChar; + debug_printf( "Size was %d x %d; will be %d x %d\n", + PDC_n_rows, PDC_n_cols, new_n_rows, new_n_cols); + SP->resized = FALSE; + + /* If the window will have a different number of rows */ + /* or columns, we put KEY_RESIZE in the key queue. */ + /* We don't do this if */ + /* the resizing is the result of the window being */ + /* initialized, or as a result of PDC_resize_screen */ + /* being called. In the latter case, the user */ + /* presumably already knows the screen's been resized. */ + if( PDC_n_rows != (int)new_n_rows || PDC_n_cols != (int)new_n_cols) + { + PDC_n_cols = new_n_cols; + PDC_n_rows = new_n_rows; + debug_printf( "prev_wParam = %d; add_resize_key = %d\n", + (int)prev_wParam, add_resize_key); + if( prev_wParam != (WPARAM)-99 && add_resize_key) + { + /* don't add a key when the window is initialized */ + add_key_to_queue( KEY_RESIZE); + SP->resized = TRUE; + } + } + else if( wine_version) + return; + + add_resize_key = 1; + if( wParam == SIZE_RESTORED && + ( n_xpixels % PDC_cxChar || n_ypixels % PDC_cyChar)) + { + int new_xpixels = PDC_cxChar * PDC_n_cols; + int new_ypixels = PDC_cyChar * PDC_n_rows; + + adjust_window_size( &new_xpixels, &new_ypixels, + GetWindowLong( PDC_hWnd, GWL_STYLE), menu_shown); + debug_printf( "Irregular size\n"); + SetWindowPos( PDC_hWnd, 0, 0, 0, + new_xpixels, new_ypixels, + SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); + + } + + /* If the window has been restored from minimized form, */ + /* we should repaint. Otherwise, don't. */ + prev_wParam = wParam; +} + +static int HandleMouseMove( WPARAM wParam, LPARAM lParam) +{ + const int mouse_x = LOWORD( lParam) / PDC_cxChar; + const int mouse_y = HIWORD( lParam) / PDC_cyChar; + + return( add_mouse( 0, BUTTON_MOVED, mouse_x, mouse_y) != -1); +} + +static void HandlePaint( HWND hwnd ) +{ + PAINTSTRUCT ps; + HDC hdc; + RECT rect; + + GetUpdateRect( hwnd, &rect, FALSE); +/* printf( "In HandlePaint: %ld %ld, %ld %ld\n", + rect.left, rect.top, rect.right, rect.bottom); */ + + hdc = BeginPaint( hwnd, &ps); + + fix_up_edges( hdc, &rect); + + if( curscr && curscr->_y) + { + int i, x1, n_chars; + + x1 = rect.left / PDC_cxChar; + n_chars = rect.right / PDC_cxChar - x1 + 1; + if( n_chars > SP->cols - x1) + n_chars = SP->cols - x1; + if( n_chars > 0) + for( i = rect.top / PDC_cyChar; i <= rect.bottom / PDC_cyChar; i++) + if( i < SP->lines && curscr->_y[i]) + PDC_transform_line_given_hdc( hdc, i, x1, + n_chars, curscr->_y[i] + x1); + } + EndPaint( hwnd, &ps ); +} + +static bool key_already_handled = FALSE; + +static void HandleSyskeyDown( const WPARAM wParam, const LPARAM lParam, + int *ptr_modified_key_to_return ) +{ + const int shift_pressed = (GetKeyState( VK_SHIFT) & 0x8000); + const int ctrl_pressed = (GetKeyState( VK_CONTROL) & 0x8000); + const int alt_pressed = (GetKeyState( VK_MENU) & 0x8000); + const int extended = ((lParam & 0x01000000) != 0); + const int repeated = (int)( lParam >> 30) & 1; + const KPTAB *kptr = kptab + wParam; + int key = 0; + static int repeat_count; + + if( !repeated) + *ptr_modified_key_to_return = 0; + + if( repeated) + repeat_count++; + else + repeat_count = 0; + if( SP->return_key_modifiers && !repeated) + { /* See notes above this function */ + if( wParam == VK_SHIFT) + { + if( GetKeyState( VK_LSHIFT) & 0x8000) + *ptr_modified_key_to_return = KEY_SHIFT_L; + else if( GetKeyState( VK_RSHIFT) & 0x8000) + *ptr_modified_key_to_return = KEY_SHIFT_R; + else if(( HIWORD( lParam) & 0xff) == 0x36) + *ptr_modified_key_to_return = KEY_SHIFT_R; + else + *ptr_modified_key_to_return = KEY_SHIFT_L; + } + if( wParam == VK_CONTROL) + *ptr_modified_key_to_return = + (extended ? KEY_CONTROL_R : KEY_CONTROL_L); + if( wParam == VK_MENU) + *ptr_modified_key_to_return = + (extended ? KEY_ALT_R : KEY_ALT_L); + } + + if( !key) /* it's not a shift, ctl, alt handled above */ + { + if( extended && kptr->extended != 999) + kptr = ext_kptab + kptr->extended; + + if( alt_pressed) + key = kptr->alt; + else if( ctrl_pressed) + key = kptr->control; + else if( shift_pressed) + key = kptr->shift; + else + key = kptr->normal; + } + + /* On non-US keyboards, people hit Alt-Gr ("Alt-Ctrl" to */ + /* those on US keyboards) to get characters not otherwise */ + /* available: accented characters, local currency symbols, */ + /* etc. So we default to suppressing Alt-Ctrl-letter combos. */ + /* However, apps can set PDC_show_ctrl_alts if they know they're */ + /* running on a US keyboard layout (or other layout that doesn't */ + /* make special use of Ctrl-Alt... for example, I use the Dvorak */ + /* layout; it's fine with PDC_show_ctrl_alts = 1.) */ + if( key >= KEY_MIN && key <= KEY_MAX) + if( !ctrl_pressed || !alt_pressed || PDC_show_ctrl_alts) + { + add_key_to_queue( key); + if( wParam == VK_MULTIPLY || wParam == VK_DIVIDE + || wParam == VK_ADD || wParam == VK_SUBTRACT + || wParam == VK_RETURN) + if( !alt_pressed) + key_already_handled = TRUE; + } + SP->key_modifiers = 0; + /* Save the key modifiers if required. Do this first to allow to + detect e.g. a pressed CTRL key after a hit of NUMLOCK. */ + + if( alt_pressed) + SP->key_modifiers |= PDC_KEY_MODIFIER_ALT; + + if( shift_pressed) + SP->key_modifiers |= PDC_KEY_MODIFIER_SHIFT; + + if( ctrl_pressed) + SP->key_modifiers |= PDC_KEY_MODIFIER_CONTROL; + + if( GetKeyState( VK_NUMLOCK) & 1) + SP->key_modifiers |= PDC_KEY_MODIFIER_NUMLOCK; +} + +/* Blinking text is supposed to blink twice a second. Therefore, +HandleTimer( ) is called every .5 seconds. + + In truth, it's not so much 'blinking' as 'changing certain types of +text' that happens. If text is really blinking (i.e., PDC_set_blink(TRUE) +has been called), we need to flip that text. Or if text _was_ blinking +and we've just called PDC_set_blink(FALSE), all that text has to be +redrawn in 'standout' mode. Also, if PDC_set_line_color() has been +called, all text with left/right/under/over/strikeout lines needs to +be redrawn. + + Also, if we've switched from rendering bold text using a bold +font to showing it in intensified color, or vice versa, then all +bold text needs to be redrawn. + + So. After determining which attributes require redrawing (if any), +we run through all of 'curscr' and look for text with those attributes +set. If we find such text, we run it through PDC_transform_line. +(To speed matters up slightly, we skip over text at the start and end +of each line that lacks the desired attributes. We could conceivably +get more clever; as it stands, if the very first and very last +characters are blinking, we redraw the entire line, even though +everything in between may not require it. But it would probably be a +lot of code for little benefit.) + + Note that by default, we'll usually find that the line color hasn't +changed and the 'blink mode' is still FALSE. In that case, attr_to_seek +will be zero and the only thing we'll do here is to blink the cursor. */ + +static void HandleTimer( const WPARAM wParam ) +{ + int i; /* see WndProc() notes */ + static attr_t prev_termattrs; + static int prev_line_color = -1; + chtype attr_to_seek = 0; + + if( prev_line_color != SP->line_color) + attr_to_seek = A_ALL_LINES; + if( (SP->termattrs | prev_termattrs) & A_BLINK) + attr_to_seek |= A_BLINK; + if( (SP->termattrs ^ prev_termattrs) & A_BOLD) + attr_to_seek |= A_BOLD; + prev_line_color = SP->line_color; + prev_termattrs = SP->termattrs; + PDC_blink_state ^= 1; + if( attr_to_seek) + { + for( i = 0; i < SP->lines; i++) + { + if( curscr->_y[i]) + { + int j = 0, n_chars; + chtype *line = curscr->_y[i]; + + /* skip over starting text that isn't blinking: */ + while( j < SP->cols && !(*line & attr_to_seek)) + { + j++; + line++; + } + n_chars = SP->cols - j; + /* then skip over text at the end that's not blinking: */ + while( n_chars && !(line[n_chars - 1] & attr_to_seek)) + { + n_chars--; + } + if( n_chars) + PDC_transform_line( i, j, n_chars, line); + } +/* else + MessageBox( 0, "NULL _y[] found\n", "PDCurses", MB_OK); */ + } + } + if( SP->cursrow >=SP->lines || SP->curscol >= SP->cols + || SP->cursrow < 0 || SP->curscol < 0 + || !curscr->_y || !curscr->_y[SP->cursrow]) + debug_printf( "Cursor off-screen: %d %d, %d %d\n", + SP->cursrow, SP->curscol, SP->lines, SP->cols); + else if( PDC_CURSOR_IS_BLINKING) + PDC_transform_line( SP->cursrow, SP->curscol, 1, + curscr->_y[SP->cursrow] + SP->curscol); +} + + /* Options to enlarge/shrink the font are currently commented out. */ + +static HMENU set_menu( void) +{ + const HMENU hMenu = CreateMenu( ); +#ifdef PDC_WIDE + AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, L"Font"); +#else + AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, "Font"); +#endif + return( hMenu); +} + +INLINE void HandleMenuToggle( bool *ptr_ignore_resize) +{ + const bool is_zoomed = IsZoomed( PDC_hWnd); + HMENU hMenu; + + menu_shown ^= 1; + hMenu = GetSystemMenu( PDC_hWnd, FALSE); + CheckMenuItem( hMenu, WM_TOGGLE_MENU, MF_BYCOMMAND + | (menu_shown ? MF_CHECKED : MF_UNCHECKED)); + + if( !is_zoomed) + *ptr_ignore_resize = TRUE; + if( !menu_shown) + { + hMenu = GetMenu( PDC_hWnd); /* destroy existing menu */ + DestroyMenu( hMenu); + hMenu = CreateMenu( ); /* then set an empty menu */ + SetMenu( PDC_hWnd, hMenu); + } + else + { + SetMenu( PDC_hWnd, set_menu( )); + } + *ptr_ignore_resize = FALSE; + + if( !is_zoomed) + { + PDC_resize_screen( PDC_n_rows, PDC_n_cols ); + } + + InvalidateRect( PDC_hWnd, NULL, FALSE); +} + +INLINE uint64_t milliseconds_since_1970( void) +{ + FILETIME ft; + const uint64_t jd_1601 = 2305813; /* actually 2305813.5 */ + const uint64_t jd_1970 = 2440587; /* actually 2440587.5 */ + const uint64_t ten_million = 10000000; + const uint64_t diff = (jd_1970 - jd_1601) * ten_million * 86400; + uint64_t decimicroseconds_since_1970; /* i.e., time in units of 1e-7 seconds */ + + GetSystemTimeAsFileTime( &ft); + decimicroseconds_since_1970 = ((uint64_t)ft.dwLowDateTime | + ((uint64_t)ft.dwHighDateTime << 32)) - diff; + return( decimicroseconds_since_1970 / 10000); +} + +typedef struct +{ + int x, y; + int button, action; +} PDC_mouse_event; + +static int add_mouse( int button, const int action, const int x, const int y) +{ + static int n = 0; + static PDC_mouse_event e[10]; + static uint64_t prev_t = 0; + const uint64_t curr_t = milliseconds_since_1970( ); + const int timing_slop = 20; + bool within_timeout = (curr_t < prev_t + SP->mouse_wait + timing_slop); + static int mouse_state = 0; + static int prev_x, prev_y = -1; + const bool actually_moved = (x != prev_x || y != prev_y); + + if( action == BUTTON_MOVED && mouse_key_already_in_queue( )) + return( 0); + if( action == BUTTON_PRESSED) + mouse_state |= (1 << button); + else if( action == BUTTON_RELEASED) + mouse_state &= ~(1 << button); + if( button >= 0) + { + prev_x = x; + prev_y = y; + } + if( action == BUTTON_MOVED) + { + int i; +#ifdef TEMP_REMOVE + bool report_this_move = FALSE; +#endif + + if( !actually_moved) /* have to move to a new character cell, */ + return( -1); /* not just a new pixel */ + button = -1; /* assume no buttons down */ + for( i = 0; i < 9; i++) + if( (mouse_state >> i) & 1) + button = i; + if( button == -1 && !(SP->_trap_mbe & REPORT_MOUSE_POSITION)) + return( -1); +#ifdef TEMP_REMOVE + if( (SP->_trap_mbe & REPORT_MOUSE_POSITION) + || (button == 1 && (SP->_trap_mbe & BUTTON1_MOVED)) + || (button == 2 && (SP->_trap_mbe & BUTTON2_MOVED)) + || (button == 3 && (SP->_trap_mbe & BUTTON3_MOVED))) + report_this_move = TRUE; + debug_printf( "Move button %d, (%d %d) : %d\n", button, x, y, report_this_move); + if( !report_this_move) + return( -1); +#endif + } + + if( !within_timeout || action == BUTTON_MOVED) + while( n && !set_mouse( e->button - 1, e->action, e->x, e->y)) + { + n--; + memmove( e, e + 1, n * sizeof( PDC_mouse_event)); + } + if( action == BUTTON_MOVED) + if( !set_mouse( button - 1, action, x, y)) + return( n); + if( button < 0 && action != BUTTON_MOVED) + return( n); /* we're just checking for timed-out events */ + debug_printf( "Button %d, act %d, dt %ld : n %d\n", button, action, + (long)( curr_t - prev_t), n); + e[n].button = button; + e[n].action = action; + e[n].x = x; + e[n].y = y; + if( n) + { + int merged_act = 0; + + do + { + if( e[n - 1].button == e[n].button) + { + if( e[n - 1].action == BUTTON_PRESSED && e[n].action == BUTTON_RELEASED) + merged_act = BUTTON_CLICKED; + else if( e[n - 1].action == BUTTON_CLICKED && e[n].action == BUTTON_CLICKED) + merged_act = BUTTON_DOUBLE_CLICKED; + else if( e[n - 1].action == BUTTON_DOUBLE_CLICKED && e[n].action == BUTTON_CLICKED) + merged_act = BUTTON_TRIPLE_CLICKED; + if( merged_act) + { + n--; + e[n].action = merged_act; + } + } + } while( n && merged_act); + } + prev_t = curr_t; + n++; + return( n); +} + +/* Note that there are two types of WM_TIMER timer messages. One type +indicates that SP->mouse_wait milliseconds have elapsed since a mouse +button was pressed; that's handled as described in the above notes. +The other type, issued every half second, indicates that blinking +should take place. For these, HandleTimer() is called (see above). + + On WM_PAINT, we determine what parts of 'curscr' would be covered by +the update rectangle, and run those through PDC_transform_line. + + For determining left/right shift, alt, and control, I borrowed code +from SDL. Note that the Win32 version of PDCurses doesn't work correctly +here for Win9x; it just does GetKeyState( VK_LSHIFT), etc., which is +apparently not supported in Win9x. So no matter which shift (or alt or +Ctrl key) is hit, the right-hand variant is returned in that library. +The SDL handling, and hence the handling below, _does_ work on Win9x. +Note, though, that in Win9x, detection of the Shift keys is hardware +dependent; if you've an unusual keyboard, both Shift keys may be +detected as right, or both as left. */ + +#if defined(_WIN32) && defined(__GNUC__) +#define ALIGN_STACK __attribute__((force_align_arg_pointer)) +#else +#define ALIGN_STACK +#endif + +static LRESULT ALIGN_STACK CALLBACK WndProc (const HWND hwnd, + const UINT message, + const WPARAM wParam, + const LPARAM lParam) +{ + static int xbutton_pressed = 0; + static int modified_key_to_return = 0; + static bool ignore_resize = FALSE; + int button = -1, action = -1; + + PDC_hWnd = hwnd; + if( !hwnd) + debug_printf( "Null hWnd: msg %u, wParam %x, lParam %lx\n", + message, wParam, lParam); + + switch (message) + { + case WM_SIZING: + HandleSizing( wParam, lParam ); + return( TRUE ); + + case WM_SIZE: + /* If ignore_resize = 1, don't bother resizing; */ + /* the final window size has yet to be set */ + if( ignore_resize == FALSE) + HandleSize( wParam, lParam); + return 0 ; + + case WM_MOUSEWHEEL: + debug_printf( "Mouse wheel: %x %lx\n", wParam, lParam); + modified_key_to_return = 0; + set_mouse( VERTICAL_WHEEL_EVENT, (short)( HIWORD(wParam)), 0, 0); + break; + + case WM_MOUSEHWHEEL: + debug_printf( "Mouse horiz wheel: %x %lx\n", wParam, lParam); + modified_key_to_return = 0; + set_mouse( HORIZONTAL_WHEEL_EVENT, (short)( HIWORD(wParam)), 0, 0); + break; + + case WM_MOUSEMOVE: + if( HandleMouseMove( wParam, lParam)) + modified_key_to_return = 0; + break; + + case WM_LBUTTONDOWN: + button = 1; + action = BUTTON_PRESSED; + break; + + case WM_LBUTTONUP: + button = 1; + action = BUTTON_RELEASED; + break; + + case WM_RBUTTONDOWN: + button = 3; + action = BUTTON_PRESSED; + break; + + case WM_RBUTTONUP: + button = 3; + action = BUTTON_RELEASED; + break; + + case WM_MBUTTONDOWN: + button = 2; + action = BUTTON_PRESSED; + break; + + case WM_MBUTTONUP: + button = 2; + action = BUTTON_RELEASED; + break; + + case WM_XBUTTONDOWN: + button = ((wParam & MK_XBUTTON1) ? 3 : 4); + action = BUTTON_PRESSED; + xbutton_pressed = button; + break; + + case WM_XBUTTONUP: +#ifdef WRONG_WAY + /* You'd think we'd use the following line, wouldn't you? */ + /* But we can't, because an XBUTTONUP message doesn't actually */ + /* tell you which button was released! So we'll assume that */ + /* the released xbutton matches a pressed one; and we've kept */ + /* track of which buttons are currently pressed. */ + button = ((wParam & MK_XBUTTON1) ? 3 : 4); +#endif + button = xbutton_pressed; + action = BUTTON_RELEASED; + break; + + case WM_MOVE: + return 0 ; + + case WM_ERASEBKGND: /* no need to erase background; it'll */ + return( 0); /* all get painted over anyway */ + + /* The WM_PAINT routine is sort of "borrowed" from doupdate( ) from */ + /* refresh.c. I'm not entirely sure that this is what ought to be */ + /* done, though it does appear to work correctly. */ + case WM_PAINT: + if( hwnd && curscr ) + { + HandlePaint( hwnd ); + } + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if( wParam == VK_MENU && numpad_unicode_value) + { + modified_key_to_return = numpad_unicode_value; + numpad_unicode_value = 0; + SP->key_modifiers = 0; + } + if( modified_key_to_return ) + { + add_key_to_queue( modified_key_to_return ); + modified_key_to_return = 0; + } + break; + + case WM_CHAR: /* _Don't_ add Shift-Tab; it's handled elsewhere */ + if( wParam != 9 || !(GetKeyState( VK_SHIFT) & 0x8000)) + if( !key_already_handled) + add_key_to_queue( (int)wParam ); + key_already_handled = FALSE; + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if( wParam < 225 && wParam > 0 ) + { + HandleSyskeyDown( wParam, lParam, &modified_key_to_return ); + } + return 0 ; + + case WM_SYSCHAR: + return 0 ; + + case WM_TIMER: + if( wParam != TIMER_ID_FOR_BLINKING) + { + KillTimer( PDC_hWnd, (int)wParam); +// within_timeout = FALSE; + } + else if( SP && curscr && curscr->_y) + { + /* blink the blinking text */ + HandleTimer( wParam ); + } + break; + + case WM_CLOSE: + { + final_cleanup( ); + PDC_bDone = TRUE; + exit( 0); + } + return( 0); + + case WM_COMMAND: + case WM_SYSCOMMAND: + if( wParam == WM_EXIT_GRACELESSLY) + { + final_cleanup( ); + PDC_bDone = TRUE; + exit( 0); + } + else if( wParam == WM_ENLARGE_FONT || wParam == WM_SHRINK_FONT) + { + adjust_font_size( (wParam == WM_ENLARGE_FONT) ? 1 : -1); + return( 0); + } + else if( wParam == WM_CHOOSE_FONT) + { + if( PDC_choose_a_new_font( )) + adjust_font_size( 0); + return( 0); + } + else if( wParam == WM_TOGGLE_MENU) + { + HandleMenuToggle( &ignore_resize); + } + break; + + case WM_DESTROY: + PDC_LOG(("WM_DESTROY\n")); + PostQuitMessage (0) ; + PDC_bDone = TRUE; + return 0 ; + } + if( button != -1) + { + add_mouse( button, action, LOWORD( lParam) / PDC_cxChar, HIWORD( lParam) / PDC_cyChar); + if( action == BUTTON_PRESSED) + SetCapture( hwnd); + else + ReleaseCapture( ); +// SetTimer( hwnd, 0, SP->mouse_wait, NULL); + } + else + add_mouse( -1, -1, -1, -1); + return DefWindowProc( hwnd, message, wParam, lParam) ; +} + +/* https://msdn.microsoft.com/en-us/library/windows/desktop/dd162826(v=vs.85).aspx +The code at the above link provides general methods for positioning a window +on a multiple-display setup. The only instance we're using is the +MONITOR_WORKAREA one, which ensures that even if monitor geometry changes, +the window will still be entirely on-screen. + +These functions entered the Win32 API with Windows 2000. If +MONITOR_DEFAULTTONEAREST isn't defined, we shouldn't try to do this. */ + +#ifdef MONITOR_DEFAULTTONEAREST + +static void clip_or_center_rect_to_monitor( LPRECT prc) +{ + HMONITOR hMonitor; + MONITORINFO mi; + RECT rc; + const int w = prc->right - prc->left; + const int h = prc->bottom - prc->top; + + hMonitor = MonitorFromRect(prc, MONITOR_DEFAULTTONEAREST); + + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + rc = mi.rcMonitor; + + prc->left = max(rc.left, min(rc.right-w, prc->left)); + prc->top = max(rc.top, min(rc.bottom-h, prc->top)); + prc->right = prc->left + w; + prc->bottom = prc->top + h; +} + +static void clip_or_center_window_to_monitor( HWND hwnd) +{ + RECT rc; + + GetWindowRect(hwnd, &rc); + clip_or_center_rect_to_monitor(&rc); + SetWindowPos(hwnd, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); +} +#endif + +/* By default, the user cannot resize the window. This is because +many apps don't handle KEY_RESIZE, and one can get odd behavior +in such cases. There are two ways around this. If you call +PDC_set_resize_limits( ) before initwin( ), telling WinGUI exactly how +large/small the window can be, the window will be user-resizable. Or +you can set ttytype[0...3] to contain the resize limits. A call such as + +PDC_set_resize_limits( 42, 42, 135, 135); + + will result in the window being fixed at 42 lines by 135 columns. + +PDC_set_resize_limits( 20, 50, 70, 200); + + will mean the window can have 20 to 50 lines and 70 to 200 columns. +The user will be able to resize the window freely within those limits. +See 'newtest.c' (in the 'demos' folder) for an example. + + This function is used in only one place (PDC_scr_open( )), so +it's inlined. */ + +INLINE int set_up_window( void) +{ + /* create the dialog window */ + WNDCLASS wndclass ; + HMENU hMenu; + HANDLE hInstance = GetModuleHandleA( NULL); + int n_default_columns = 80; + int n_default_rows = 25; + int xsize, ysize, window_style; + int xloc = CW_USEDEFAULT; + int yloc = CW_USEDEFAULT; + TCHAR WindowTitle[MAX_PATH]; + const TCHAR *AppName = _T( "Curses_App"); + HICON icon; + static bool wndclass_has_been_registered = FALSE; + + if( !hInstance) + debug_printf( "No instance: %d\n", GetLastError( )); + originally_focussed_window = GetForegroundWindow( ); + debug_printf( "hInstance %x\nOriginal window %x\n", hInstance, originally_focussed_window); + /* set the window icon from the icon in the process */ + icon = get_app_icon(); + if( !icon ) + icon = LoadIcon( NULL, IDI_APPLICATION); + if( !wndclass_has_been_registered) + { + ATOM rval; + + wndclass.style = CS_VREDRAW ; + wndclass.lpfnWndProc = WndProc ; + wndclass.cbClsExtra = 0 ; + wndclass.cbWndExtra = 0 ; + wndclass.hInstance = hInstance ; + wndclass.hIcon = icon; + wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ; + wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; + wndclass.lpszMenuName = NULL ; + wndclass.lpszClassName = AppName ; + + rval = RegisterClass( &wndclass) ; + if( !rval) + { + const DWORD last_error = GetLastError( ); + + debug_printf( "RegisterClass failed: GetLastError = %lx\n", last_error); + return( -1); + } + wndclass_has_been_registered = TRUE; + } + + get_app_name( WindowTitle, MAX_PATH, TRUE); +#ifdef PDC_WIDE + debug_printf( "WindowTitle = '%ls'\n", WindowTitle); +#endif + + get_default_sizes_from_registry( &n_default_columns, &n_default_rows, &xloc, &yloc, + &menu_shown); + if( PDC_n_rows > 2 && PDC_n_cols > 2) + { + n_default_columns = PDC_n_cols; + n_default_rows = PDC_n_rows; + } + if( ttytype[1]) + PDC_set_resize_limits( (unsigned char)ttytype[0], + (unsigned char)ttytype[1], + (unsigned char)ttytype[2], + (unsigned char)ttytype[3]); + debug_printf( "Size %d x %d, loc %d x %d; menu %d\n", + n_default_columns, n_default_rows, xloc, yloc, menu_shown); + get_character_sizes( NULL, &PDC_cxChar, &PDC_cyChar); + + if( min_lines != max_lines || min_cols != max_cols) + window_style = ((n_default_columns == -1) ? + WS_MAXIMIZE | WS_OVERLAPPEDWINDOW : WS_OVERLAPPEDWINDOW); + else /* fixed-size window: looks "normal", but w/o a maximize box */ + window_style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + + if( n_default_columns == -1) + xsize = ysize = CW_USEDEFAULT; + else + { + keep_size_within_bounds( &n_default_rows, &n_default_columns); + xsize = PDC_cxChar * n_default_columns; + ysize = PDC_cyChar * n_default_rows; + adjust_window_size( &xsize, &ysize, window_style, menu_shown); + } + + PDC_hWnd = CreateWindow( AppName, WindowTitle, window_style, + xloc, yloc, + xsize, ysize, + NULL, (menu_shown ? set_menu( ) : NULL), + hInstance, NULL) ; + + if( !PDC_hWnd) + { + const DWORD last_error = GetLastError( ); + + debug_printf( "CreateWindow failed; GetLastError = %ld", last_error); + return( -2); + } + + hMenu = GetSystemMenu( PDC_hWnd, FALSE); + AppendMenu( hMenu, MF_STRING | (menu_shown ? MF_CHECKED : MF_UNCHECKED), WM_TOGGLE_MENU, _T( "Menu")); + AppendMenu( hMenu, MF_STRING, WM_CHOOSE_FONT, _T( "Choose Font")); + + debug_printf( "menu set\n"); + + ShowWindow (PDC_hWnd, + (n_default_columns == -1) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL); + debug_printf( "window shown\n"); + ValidateRect( PDC_hWnd, NULL); /* don't try repainting */ + UpdateWindow (PDC_hWnd) ; + debug_printf( "window updated\n"); + SetTimer( PDC_hWnd, TIMER_ID_FOR_BLINKING, 500, NULL); + debug_printf( "timer set\n"); + +#ifdef MONITOR_DEFAULTTONEAREST + /* if the window is off-screen, move it on screen. */ + clip_or_center_window_to_monitor( PDC_hWnd); +#endif + + return( 0); +} + +/* open the physical screen -- allocate SP, miscellaneous intialization, + and may save the existing screen for later restoration. + + Deciding on a for-real maximum screen size has proven difficult. + But there is really no particularly good reason to set such a maximum. + If one does, you get some tricky issues: suppose the user drags the + window to create a screen larger than MAX_LINES or MAX_COLUMNS? My + hope is to evade that problem by just setting those constants to be... + well... unrealistically large. */ + +#define MAX_LINES 50000 +#define MAX_COLUMNS 50000 + +int PDC_scr_open(void) +{ + HMODULE hntdll = GetModuleHandle( _T("ntdll.dll")); + + if( hntdll) + wine_version = (wine_version_func)GetProcAddress(hntdll, "wine_get_version"); + + PDC_LOG(("PDC_scr_open() - called\n")); + COLORS = PDC_MAXCOL; + if (!SP || PDC_init_palette( )) + return ERR; + + debug_printf( "colors alloc\n"); + SP->mouse_wait = PDC_CLICK_PERIOD; + SP->visibility = 0; /* no cursor, by default */ + SP->curscol = SP->cursrow = 0; + SP->audible = TRUE; + SP->mono = FALSE; + SP->termattrs = A_BOLD | A_COLOR | A_LEFTLINE | A_RIGHTLINE + | A_UNDERLINE | A_ITALIC + | A_DIM | A_REVERSE; + +#ifdef NO_LONGER_AVAILABLE + /* (Jan 2020 : the wmcbrine flavor lacks Xinitscr) */ + /* note: we parse the non-wide argc (see comment in header), + therefore using non-wide char handling here */ + if( argc && argv) /* store a copy of the input arguments */ + { + PDC_argc = argc; + PDC_argv = (char **)calloc( argc + 1, sizeof( char *)); + for( i = 0; i < argc; i++) + { + PDC_argv[i] = (char *)malloc( strlen( argv[i]) + 1); + strcpy( PDC_argv[i], argv[i]); + } + } +#endif + + if( set_up_window( )) + { + fprintf( stderr, "set_up_window failed\n"); + return ERR; + } + debug_printf( "Back from set_up_window\n"); + while( !PDC_get_rows( )) /* wait for screen to be drawn and */ + ; /* actual size to be determined */ + + debug_printf( "Back from PDC_get_rows\n"); + SP->lines = PDC_get_rows(); + SP->cols = PDC_get_columns(); + + if (SP->lines < 2 || SP->lines > MAX_LINES + || SP->cols < 2 || SP->cols > MAX_COLUMNS) + { + fprintf(stderr, "LINES value must be >= 2 and <= %d: got %d\n", + MAX_LINES, SP->lines); + fprintf(stderr, "COLS value must be >= 2 and <= %d: got %d\n", + MAX_COLUMNS, SP->cols); + + return ERR; + } + +/* PDC_reset_prog_mode(); doesn't do anything anyway */ + debug_printf( "...we're done\n"); + return OK; +} + +/* the core of resize_term() */ + +int PDC_resize_screen( int nlines, int ncols) +{ + if( !stdscr) /* window hasn't been created yet; we're */ + { /* specifying its size before doing so */ + PDC_n_rows = nlines; + PDC_n_cols = ncols; + return OK; + } + SP->resized = FALSE; + debug_printf( "Incoming: %d %d\n", nlines, ncols); + if( nlines >= 2 && ncols >= 2 && PDC_cxChar && PDC_cyChar && PDC_hWnd && + !IsZoomed( PDC_hWnd) /* && WaitResult == WAIT_OBJECT_0 */) + { + RECT rect, client_rect; + int new_width; + int new_height; + + GetWindowRect( PDC_hWnd, &rect); + GetClientRect( PDC_hWnd, &client_rect); + debug_printf( "Outgoing: %d %d\n", nlines, ncols); + new_width = ncols * PDC_cxChar; + new_height = nlines * PDC_cyChar; + + if( new_width != client_rect.right || new_height != client_rect.bottom) + { /* check to make sure size actually changed */ + add_resize_key = 0; + SetWindowPos( PDC_hWnd, 0, 0, 0, + new_width + (rect.right - rect.left) - client_rect.right, + new_height + (rect.bottom - rect.top) - client_rect.bottom, + SWP_NOMOVE | SWP_NOZORDER | SWP_SHOWWINDOW); + } + } + return OK; +} + +void PDC_reset_prog_mode(void) +{ + PDC_LOG(("PDC_reset_prog_mode() - called.\n")); +#ifdef NOT_CURRENTLY_IN_USE + if( PDC_bDone == FALSE && PDC_hWnd) + { + PDC_bDone = TRUE; + SetForegroundWindow( PDC_hWnd); + } +#endif +} + +void PDC_reset_shell_mode(void) +{ +} + +void PDC_restore_screen_mode(int i) +{ +} + +void PDC_save_screen_mode(int i) +{ +} + +/* NOTE: as with PDC_init_color() (see below), this function has to +redraw all text with color attribute 'pair' to match the newly-set +foreground and background colors. The loops to go through every character +in curscr, looking for those that need to be redrawn and ignoring +those at the front and start of each line, are very similar. */ + +static int get_pair( const chtype ch) +{ + return( (int)( (ch & A_COLOR) >> PDC_COLOR_SHIFT)); +} + +bool PDC_can_change_color(void) +{ + return TRUE; +} + +int PDC_color_content( short color, short *red, short *green, short *blue) +{ + COLORREF col = PDC_palette[color]; + + *red = DIVROUND(GetRValue(col) * 1000, 255); + *green = DIVROUND(GetGValue(col) * 1000, 255); + *blue = DIVROUND(GetBValue(col) * 1000, 255); + + return OK; +} + +/* We have an odd problem when changing colors with PDC_init_color(). On +palette-based systems, you just change the palette and the hardware takes +care of the rest. Here, though, we actually need to redraw any text that's +drawn in the specified color. So we gotta look at each character and see if +either the foreground or background matches the index that we're changing. +Then, that text gets redrawn. For speed/simplicity, the code looks for the +first and last character in each line that would be affected, then draws those +in between (frequently, this will be zero characters, i.e., no text on that +particular line happens to use the color index in question.) See similar code +above for PDC_init_pair(), to handle basically the same problem. */ + +static int color_used_for_this_char( const chtype c, const int idx) +{ + const int color = get_pair( c); + short fg, bg; + int rval; + + pair_content( color, &fg, &bg); + rval = (fg == idx || bg == idx); + return( rval); +} + +int PDC_init_color( short color, short red, short green, short blue) +{ + const COLORREF new_rgb = RGB(DIVROUND(red * 255, 1000), + DIVROUND(green * 255, 1000), + DIVROUND(blue * 255, 1000)); + + PDC_palette[color] = new_rgb; + + /* Possibly go through curscr and redraw everything with that color! */ + if( curscr && curscr->_y) + { + int i; + + for( i = 0; i < SP->lines; i++) + if( curscr->_y[i]) + { + int j = 0, n_chars; + chtype *line = curscr->_y[i]; + + /* skip over starting text that isn't of the desired color: */ + while( j < SP->cols + && !color_used_for_this_char( *line, color)) + { + j++; + line++; + } + n_chars = SP->cols - j; + /* then skip over text at the end that's not the right color: */ + while( n_chars && + !color_used_for_this_char( line[n_chars - 1], color)) + n_chars--; + if( n_chars) + PDC_transform_line( i, j, n_chars, line); + } + } + return OK; +} + +/* We initialize the first 256 values of the palette. Few programs will go + beyond that. */ + +static int PDC_init_palette( void) +{ + unsigned i; + + /* Initial 16 colors */ + for (i = 0; i < 16; ++i) + { + const int intensity = ((i & 8) ? 0xff : 0xc0); + + PDC_palette[i] = RGB( ((i & COLOR_RED) ? intensity : 0), + ((i & COLOR_GREEN) ? intensity : 0), + ((i & COLOR_BLUE) ? intensity : 0)); + } + + /* 6x6x6 cube */ + for (i = 0; i < 216; ++i) + { + unsigned r = i / 36; + unsigned g = (i / 6) % 6; + unsigned b = i % 6; + PDC_palette[i+16] = RGB( r ? r * 40 + 55 : 0, + g ? g * 40 + 55 : 0, + b ? b * 40 + 55 : 0); + } + + /* Gray scale */ + for (i = 232; i < 256; ++i) + { + const int intensity = (i - 232) * 10 + 8; + + PDC_palette[i] = RGB( intensity, intensity, intensity); + } + + return( 0); +} diff --git a/wingui/pdcsetsc.c b/wingui/pdcsetsc.c new file mode 100644 index 000000000..f33da598a --- /dev/null +++ b/wingui/pdcsetsc.c @@ -0,0 +1,110 @@ +/* Public Domain Curses */ + +#include "pdcwin.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_bold - - - + PDC_set_title - - - + +**man-end****************************************************************/ + + /* Note that the following is a line-for-line */ + /* copy of the SDL version of this function. */ +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) +{ +#ifdef PDC_WIDE + wchar_t wtitle[512]; +#endif + PDC_LOG(("PDC_set_title() - called:<%s>\n", title)); + +#ifdef PDC_WIDE + PDC_mbstowcs(wtitle, title, 511); + SetWindowTextW( PDC_hWnd, wtitle); +#else + SetWindowTextA( PDC_hWnd, title); +#endif +} + + /* If SP->termattrs & A_BLINK is on, then text with the A_BLINK */ + /* attribute will actually blink. Otherwise, such text will */ + /* be shown with higher color intensity (the R, G, and B values */ + /* are averaged with pure white). See pdcdisp.c for details of */ + /* how this is done. */ + /* Unlike on other PDCurses platforms, this doesn't require */ + /* decreasing the number of colors by half. Also, the choice */ + /* indicated by 'blinkon' will actually be respected, so OK is */ + /* always returned (most platforms don't actually support */ + /* blinking). */ + /* The default behavior is to not show A_BLINK text as */ + /* blinking, i.e., SP->termattrs & A_BLINK = 0. Blinking text */ + /* can be pretty annoying to some people. You should probably */ + /* call PDC_set_blink( TRUE) only if there is something to which */ + /* the user _must_ pay attention; say, "the nuclear reactor */ + /* is about to melt down". Otherwise, the bolder, brighter */ + /* text should be attention-getting enough. */ + +static int reset_attr( const attr_t attr, const bool attron) +{ + if (!SP) + return ERR; + if( attron) + SP->termattrs |= attr; + else + SP->termattrs &= ~attr; + return OK; +} + +int PDC_set_blink(bool blinkon) +{ + return( reset_attr( A_BLINK, blinkon)); +} + +int PDC_set_bold(bool boldon) +{ + return( reset_attr( A_BOLD, boldon)); +} diff --git a/wingui/pdcutil.c b/wingui/pdcutil.c new file mode 100644 index 000000000..a7418e59f --- /dev/null +++ b/wingui/pdcutil.c @@ -0,0 +1,45 @@ +/* Public Domain Curses */ + +#include "pdcwin.h" + +void PDC_beep(void) +{ + PDC_LOG(("PDC_beep() - called\n")); + +/* MessageBeep(MB_OK); */ + MessageBeep(0XFFFFFFFF); +} + +void PDC_napms(int ms) /* 'ms' = milli, _not_ microseconds! */ +{ + /* RR: keep GUI window responsive while PDCurses sleeps */ + MSG msg; + DWORD curr_ms = GetTickCount( ); + const DWORD milliseconds_sleep_limit = ms + curr_ms; + + PDC_LOG(("PDC_napms() - called: ms=%d\n", ms)); + + /* Pump all pending messages from WIN32 to the window handler */ + while( !PDC_bDone && curr_ms < milliseconds_sleep_limit ) + { + const DWORD max_sleep_ms = 50; /* check msgs 20 times/second */ + DWORD sleep_millisecs; + + while( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + curr_ms = GetTickCount( ); + sleep_millisecs = milliseconds_sleep_limit - curr_ms; + if( sleep_millisecs > max_sleep_ms) + sleep_millisecs = max_sleep_ms; + Sleep( sleep_millisecs); + curr_ms += sleep_millisecs; + } +} + +const char *PDC_sysname(void) +{ + return "WinGUI"; +} diff --git a/wingui/pdcwin.h b/wingui/pdcwin.h new file mode 100644 index 000000000..38626d6ce --- /dev/null +++ b/wingui/pdcwin.h @@ -0,0 +1,109 @@ +/* Public Domain Curses */ + +/* $Id: pdcwin.h,v 1.6 2008/07/13 06:36:32 wmcbrine Exp $ */ + +#ifdef PDC_WIDE + #if !defined( UNICODE) + # define UNICODE + #endif + #if !defined( _UNICODE) + # define _UNICODE + #endif +#endif + +#define WIN32_LEAN_AND_MEAN +#include + +#if defined( GS_8BIT_INDICES) && defined( PDC_WIDE) + /* We only need the 'fallback font' for the wide-char version, */ + /* and it'll only work in Win2000 or later (earlier versions */ + /* lack the necessary GetFontUnicodeRanges() function.) Comment */ + /* out the following line if you're dealing with a really old */ + /* system, or just don't want to use a fallback font. */ + #define USE_FALLBACK_FONT +#endif + +/* Windows.h will #define MOUSE_MOVED to 0x1, which is what the +Windows API expects. In Curses, MOUSE_MOVED is a macro indicating +whether the mouse has moved, so we'd get a compiler warning that +we're re-defining MOUSE_MOVED. That warning may be a Good Thing in +your software, letting you know: "Be careful; in a Curses program, +MOUSE_MOVED won't have the same meaning it does in a 'real' Windows +program." + + But in building WinGUI itself, we're expecting the Curses +meaning for MOUSE_MOVED, and the compiler warning would just be +a nuisance. So we'll #undefine MOUSE_MOVED before getting to the +Curses definition : */ + +#undef MOUSE_MOVED +#include + +extern int PDC_get_buffer_rows(void); + +/* The following probably ought to go into 'curses.h' at some point. */ +/* Or at least, not be here; this is a temporary place for them. */ + +/* WinGUI has some drastically extended cursor possibilities. The +following #defines can be used with curs_set(). For all other flavors +of PDCurses, only the invisible, "normal", and "intense" cursor +states will be recognized. In Win32, caret, half-block, central +block, cross, and outlined block cursors are available. + + By default, all such cursors will blink. However, in WinGUI, +the input value to curs_set() is treated as a two-byte value, with +the cursor blinking between those two states. For example, +curs_set( 0x0102) would blink between a "normal" (underline) cursor and +an "intense" (full-block) cursor. One can see that this behavior is +backward-compatible; for example, curs_set( 1) would blink between a +"normal" underline cursor and an invisible cursor. (But curs_set( 0x0101) +would result in a non-blinking underline cursor.) Note that one can use +the PDC_CURSOR macro for this, as in... + + curs_set( PDC_CURSOR( PDC_CURSOR_OUTLINE, PDC_CURSOR_INTENSE)); */ + +#define PDC_CURSOR_INVISIBLE 0x00 +#define PDC_CURSOR_NORMAL 0x01 +#define PDC_CURSOR_INTENSE 0x02 +#define PDC_CURSOR_OUTLINE 0x03 +#define PDC_CURSOR_CARET 0x04 +#define PDC_CURSOR_HALF_BLOCK 0x05 +#define PDC_CURSOR_CENTRAL_BLOCK 0x06 +#define PDC_CURSOR_CROSS 0x07 +#define PDC_CURSOR_OUTLINE_BLOCK 0x08 + +#define PDC_CURSOR( A, B) ((A)<<8 | (B)) +#define PDC_CURSOR_IS_BLINKING \ + ((SP->visibility >> 8) != (SP->visibility & 0xff)) + +#define PDC_REAL_ATTR_SHIFT 17 + +#define KEY_QUEUE_SIZE 30 + +extern int PDC_key_queue_low, PDC_key_queue_high; +extern int PDC_key_queue[KEY_QUEUE_SIZE]; + +extern HWND PDC_hWnd; +void PDC_transform_line_given_hdc( const HDC hdc, const int lineno, + int x, int len, const chtype *srcp); +int PDC_choose_a_new_font( void); /* pdcdisp.c */ +HFONT PDC_get_font_handle( const int is_bold); /* pdcdisp.c */ + +#ifdef USE_FALLBACK_FONT +extern GLYPHSET *PDC_unicode_range_data; +#endif /* #ifdef USE_FALLBACK_FONT */ + +extern TCHAR PDC_font_name[]; +extern int PDC_font_size; + +extern int PDC_cxChar, PDC_cyChar; + +extern int PDC_n_rows, PDC_n_cols; + +extern bool PDC_bDone; + +extern int PDC_blink_state; + +extern COLORREF PDC_palette[PDC_MAXCOL]; + +int debug_printf( const char *format, ...); /* pdcscrn.c */