diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2019-04-22 15:47:07 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2019-04-22 15:47:07 (GMT) |
commit | b195c291bad9f664e91ed5458ca45561c67874a5 (patch) | |
tree | e2072eea51f523a4f4de726a92e8dcf741c14337 /tk8.6/win | |
parent | 7e8909a08b8e425eeaa69085cbe86e848f2f5650 (diff) | |
download | blt-b195c291bad9f664e91ed5458ca45561c67874a5.zip blt-b195c291bad9f664e91ed5458ca45561c67874a5.tar.gz blt-b195c291bad9f664e91ed5458ca45561c67874a5.tar.bz2 |
backout tcl/tk 8.6.9
Diffstat (limited to 'tk8.6/win')
137 files changed, 50161 insertions, 0 deletions
diff --git a/tk8.6/win/Makefile.in b/tk8.6/win/Makefile.in new file mode 100644 index 0000000..7e48213 --- /dev/null +++ b/tk8.6/win/Makefile.in @@ -0,0 +1,748 @@ +# This file is a Makefile for Tk. If it has the name "Makefile.in" +# then it is a template for a Makefile; to generate the actual Makefile, +# run "./configure", which is a configuration script generated by the +# "autoconf" program (constructs like "@foo@" will get replaced in the +# actual Makefile. + +TCLVERSION = @TCL_VERSION@ +TCLPATCHL = @TCL_PATCH_LEVEL@ +VERSION = @TK_VERSION@ +PATCH_LEVEL = @TK_PATCH_LEVEL@ + +#---------------------------------------------------------------- +# Things you can change to personalize the Makefile for your own +# site (you can make these changes in either Makefile.in or +# Makefile, but changes to Makefile will get lost if you re-run +# the configuration script). +#---------------------------------------------------------------- + +# Default top-level directories in which to install architecture- +# specific files (exec_prefix) and machine-independent files such +# as scripts (prefix). The values specified here may be overridden +# at configure-time with the --exec-prefix and --prefix options +# to the "configure" script. + +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +includedir = @includedir@ +datarootdir = @datarootdir@ +mandir = @mandir@ + +# The following definition can be set to non-null for special systems +# like AFS with replication. It allows the pathnames used for installation +# to be different than those used for actually reference files at +# run-time. INSTALL_ROOT is prepended to $prefix and $exec_prefix +# when installing files. +INSTALL_ROOT = + +# Directory from which applications will reference the library of Tk +# scripts (note: you can set the TK_LIBRARY environment variable at +# run-time to override this value): +TK_LIBRARY = $(prefix)/lib/tk$(VERSION) + +# Path to use at runtime to refer to LIB_INSTALL_DIR: +LIB_RUNTIME_DIR = $(libdir) + +# Directory in which to install the program wish: +BIN_INSTALL_DIR = $(INSTALL_ROOT)$(bindir) + +# Directory in which to install the .a or .so binary for the Tk library: +LIB_INSTALL_DIR = $(INSTALL_ROOT)$(libdir) + +# Path name to use when installing library scripts: +SCRIPT_INSTALL_DIR = $(INSTALL_ROOT)$(TK_LIBRARY) + +# Directory in which to install the include file tk.h: +INCLUDE_INSTALL_DIR = $(INSTALL_ROOT)$(includedir) + +# Directory in which to (optionally) install the private tk headers: +PRIVATE_INCLUDE_INSTALL_DIR = $(INSTALL_ROOT)$(includedir) + +# Top-level directory for manual entries: +MAN_INSTALL_DIR = $(INSTALL_ROOT)$(mandir) + +# Directory in which to install manual entry for wish: +MAN1_INSTALL_DIR = $(MAN_INSTALL_DIR)/man1 + +# Directory in which to install manual entries for Tk's C library +# procedures: +MAN3_INSTALL_DIR = $(MAN_INSTALL_DIR)/man3 + +# Directory in which to install manual entries for the built-in +# Tk commands: +MANN_INSTALL_DIR = $(MAN_INSTALL_DIR)/mann + +# Libraries built with optimization switches have this additional extension +TK_DBGX = @TK_DBGX@ + +# Directory in which to install the pkgIndex.tcl file for loadable Tk +PKG_INSTALL_DIR = $(LIB_INSTALL_DIR)/tk$(VERSION)$(TK_DBGX) + +# Package index file for loadable Tk +PKG_INDEX = $(PKG_INSTALL_DIR)/pkgIndex.tcl + +# The directory containing the Tcl source and header files. +TCL_SRC_DIR = @TCL_SRC_DIR@ + +# The directory containing the Tcl library archive file appropriate +# for this version of Tk: +TCL_BIN_DIR = @TCL_BIN_DIR@ + +# The directory containing the Tcl sources and headers appropriate +# for this version of Tk ("srcdir" will be replaced or has already +# been replaced by the configure script): +TCL_GENERIC_DIR = @TCL_SRC_DIR@/generic + +# The directory containing the platform specific Tcl sources and headers +# appropriate for this version of Tk: +TCL_PLATFORM_DIR = @TCL_SRC_DIR@/win + +TCL_TOOL_DIR = @TCL_SRC_DIR@/tools + +# Converts a POSIX path to a Windows native path. +CYGPATH = @CYGPATH@ + +# The name of the Tcl library. +TCL_LIB_FILE = "$(shell $(CYGPATH) '@TCL_BIN_DIR@/@TCL_LIB_FILE@')" +TCL_STUB_LIB_FILE = "$(shell $(CYGPATH) '@TCL_BIN_DIR@/@TCL_STUB_LIB_FILE@')" + +SRC_DIR = @srcdir@ +ROOT_DIR = $(SRC_DIR)/.. +WIN_DIR = $(SRC_DIR) +UNIX_DIR = $(SRC_DIR)/../unix +GENERIC_DIR = $(SRC_DIR)/../generic +TTK_DIR = $(GENERIC_DIR)/ttk +BITMAP_DIR = $(ROOT_DIR)/bitmaps +XLIB_DIR = $(ROOT_DIR)/xlib +RC_DIR = $(WIN_DIR)/rc + +ROOT_DIR_NATIVE = $(shell $(CYGPATH) '$(ROOT_DIR)' | sed 's!\\!/!g') +WIN_DIR_NATIVE = $(shell $(CYGPATH) '$(WIN_DIR)' | sed 's!\\!/!g') +GENERIC_DIR_NATIVE = $(shell $(CYGPATH) '$(GENERIC_DIR)' | sed 's!\\!/!g') +BITMAP_DIR_NATIVE = $(ROOT_DIR_NATIVE)/bitmaps +XLIB_DIR_NATIVE = $(ROOT_DIR_NATIVE)/xlib +RC_DIR_NATIVE = $(WIN_DIR_NATIVE)/rc +TCL_GENERIC_NATIVE = $(shell $(CYGPATH) '$(TCL_GENERIC_DIR)' | sed 's!\\!/!g') +TCL_PLATFORM_NATIVE = $(shell $(CYGPATH) '$(TCL_PLATFORM_DIR)' | sed 's!\\!/!g') +TCL_SRC_DIR_NATIVE = $(shell $(CYGPATH) '$(TCL_SRC_DIR)' | sed 's!\\!/!g') + +DLLSUFFIX = @DLLSUFFIX@ +LIBSUFFIX = @LIBSUFFIX@ +EXESUFFIX = @EXESUFFIX@ + +TK_STUB_LIB_FILE = @TK_STUB_LIB_FILE@ +TK_LIB_FILE = @TK_LIB_FILE@ +TK_DLL_FILE = @TK_DLL_FILE@ +TEST_DLL_FILE = tktest$(VER)${DLLSUFFIX} +TEST_LIB_FILE = @LIBPREFIX@tktest$(VER)${LIBSUFFIX} + +SHARED_LIBRARIES = $(TK_DLL_FILE) $(TK_STUB_LIB_FILE) +STATIC_LIBRARIES = $(TK_LIB_FILE) + +WISH = wish$(VER)${EXESUFFIX} +TKTEST = tktest${EXEEXT} +CAT32 = cat32$(EXEEXT) +MAN2TCL = man2tcl$(EXEEXT) + +@SET_MAKE@ + +# Setting the VPATH variable to a list of paths will cause the +# makefile to look into these paths when resolving .c to .obj +# dependencies. + +VPATH = $(GENERIC_DIR):$(TTK_DIR):$(WIN_DIR):$(UNIX_DIR):$(XLIB_DIR):$(RC_DIR) + +# warning flags +CFLAGS_WARNING = @CFLAGS_WARNING@ + +# The default switches for optimization or debugging +CFLAGS_DEBUG = @CFLAGS_DEBUG@ +CFLAGS_OPTIMIZE = @CFLAGS_OPTIMIZE@ + +# The default switches for optimization or debugging +LDFLAGS_DEBUG = @LDFLAGS_DEBUG@ +LDFLAGS_OPTIMIZE = @LDFLAGS_OPTIMIZE@ + +# To change the compiler switches, for example to change from optimization to +# debugging symbols, change the following line: +#CFLAGS = $(CFLAGS_DEBUG) +#CFLAGS = $(CFLAGS_OPTIMIZE) +#CFLAGS = $(CFLAGS_DEBUG) $(CFLAGS_OPTIMIZE) +CFLAGS = @CFLAGS@ @CFLAGS_DEFAULT@ -DUNICODE -D_UNICODE -D_ATL_XP_TARGETING + +# Special compiler flags to use when building man2tcl on Windows. +MAN2TCLFLAGS = @MAN2TCLFLAGS@ + +AR = @AR@ +RANLIB = @RANLIB@ +CC = @CC@ +RC = @RC@ +RES = @RES@ +TK_RES = @TK_RES@ +AC_FLAGS = @EXTRA_CFLAGS@ @DEFS@ @TCL_DEFS@ +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ @LDFLAGS_DEFAULT@ +LDFLAGS_CONSOLE = @LDFLAGS_CONSOLE@ +LDFLAGS_WINDOW = @LDFLAGS_WINDOW@ +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +STLIB_LD = @STLIB_LD@ +SHLIB_LD = @SHLIB_LD@ +SHLIB_LD_LIBS = @SHLIB_LD_LIBS@ +SHLIB_CFLAGS = @SHLIB_CFLAGS@ +SHLIB_SUFFIX = @SHLIB_SUFFIX@ +VER = @TK_MAJOR_VERSION@@TK_MINOR_VERSION@ +DOTVER = @TK_MAJOR_VERSION@.@TK_MINOR_VERSION@ +LIBS = $(TCL_STUB_LIB_FILE) @LIBS@ @LIBS_GUI@ +RMDIR = rm -rf +MKDIR = mkdir -p +SHELL = @SHELL@ +RM = rm -f +COPY = cp + +BUILD_TCLSH = @BUILD_TCLSH@ + +# Tk does not used deprecated Tcl constructs so it should +# compile fine with -DTCL_NO_DEPRECATED. To remove its own +# set of deprecated code uncomment the second line. +NO_DEPRECATED_FLAGS = -DTCL_NO_DEPRECATED +#NO_DEPRECATED_FLAGS = -DTCL_NO_DEPRECATED -DTK_NO_DEPRECATED + +# TCL_EXE is the name of a tclsh executable that is available *BEFORE* +# running make for the first time. Certain build targets (make genstubs) +# need it to be available on the PATH. This executable should *NOT* be +# required just to do a normal build although it can be required to run +# make dist. +TCL_EXE = @TCLSH_PROG@ + +CC_SWITCHES = ${CFLAGS} ${CFLAGS_WARNING} ${SHLIB_CFLAGS} \ +-I"${GENERIC_DIR_NATIVE}" -I"${WIN_DIR_NATIVE}" \ +-I"${XLIB_DIR_NATIVE}" -I"${BITMAP_DIR_NATIVE}" \ +-I"${TCL_GENERIC_NATIVE}" -I"${TCL_PLATFORM_NATIVE}" \ +${AC_FLAGS} $(NO_DEPRECATED_FLAGS) -DUSE_TCL_STUBS + +CC_OBJNAME = @CC_OBJNAME@ +CC_EXENAME = @CC_EXENAME@ + +# Tk used to let the configure script choose which program to use +# for installing, but there are just too many different versions of +# "install" around; better to use the install-sh script that comes +# with the distribution, which is slower but guaranteed to work. + +INSTALL = cp +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} + +WISH_OBJS = \ + winMain.$(OBJEXT) + +TKTEST_OBJS = \ + tkSquare.$(OBJEXT) \ + tkTest.$(OBJEXT) \ + tkOldTest.$(OBJEXT) \ + tkWinTest.$(OBJEXT) + +XLIB_OBJS = \ + xcolors.$(OBJEXT) \ + xdraw.$(OBJEXT) \ + xgc.$(OBJEXT) \ + ximage.$(OBJEXT) \ + xutil.$(OBJEXT) + +TK_OBJS = \ + tkConsole.$(OBJEXT) \ + tkUnixMenubu.$(OBJEXT) \ + tkUnixScale.$(OBJEXT) \ + $(XLIB_OBJS) \ + tkWin3d.$(OBJEXT) \ + tkWin32Dll.$(OBJEXT) \ + tkWinButton.$(OBJEXT) \ + tkWinClipboard.$(OBJEXT) \ + tkWinColor.$(OBJEXT) \ + tkWinConfig.$(OBJEXT) \ + tkWinCursor.$(OBJEXT) \ + tkWinDialog.$(OBJEXT) \ + tkWinDraw.$(OBJEXT) \ + tkWinEmbed.$(OBJEXT) \ + tkWinFont.$(OBJEXT) \ + tkWinImage.$(OBJEXT) \ + tkWinInit.$(OBJEXT) \ + tkWinKey.$(OBJEXT) \ + tkWinMenu.$(OBJEXT) \ + tkWinPixmap.$(OBJEXT) \ + tkWinPointer.$(OBJEXT) \ + tkWinRegion.$(OBJEXT) \ + tkWinScrlbr.$(OBJEXT) \ + tkWinSend.$(OBJEXT) \ + tkWinSendCom.$(OBJEXT) \ + tkWinWindow.$(OBJEXT) \ + tkWinWm.$(OBJEXT) \ + tkWinX.$(OBJEXT) \ + stubs.$(OBJEXT) \ + tk3d.$(OBJEXT) \ + tkArgv.$(OBJEXT) \ + tkAtom.$(OBJEXT) \ + tkBind.$(OBJEXT) \ + tkBitmap.$(OBJEXT) \ + tkBusy.$(OBJEXT) \ + tkButton.$(OBJEXT) \ + tkCanvArc.$(OBJEXT) \ + tkCanvBmap.$(OBJEXT) \ + tkCanvImg.$(OBJEXT) \ + tkCanvLine.$(OBJEXT) \ + tkCanvPoly.$(OBJEXT) \ + tkCanvPs.$(OBJEXT) \ + tkCanvText.$(OBJEXT) \ + tkCanvUtil.$(OBJEXT) \ + tkCanvWind.$(OBJEXT) \ + tkCanvas.$(OBJEXT) \ + tkClipboard.$(OBJEXT) \ + tkCmds.$(OBJEXT) \ + tkColor.$(OBJEXT) \ + tkConfig.$(OBJEXT) \ + tkCursor.$(OBJEXT) \ + tkEntry.$(OBJEXT) \ + tkError.$(OBJEXT) \ + tkEvent.$(OBJEXT) \ + tkFileFilter.$(OBJEXT) \ + tkFocus.$(OBJEXT) \ + tkFont.$(OBJEXT) \ + tkFrame.$(OBJEXT) \ + tkGC.$(OBJEXT) \ + tkGeometry.$(OBJEXT) \ + tkGet.$(OBJEXT) \ + tkGrab.$(OBJEXT) \ + tkGrid.$(OBJEXT) \ + tkImage.$(OBJEXT) \ + tkImgBmap.$(OBJEXT) \ + tkImgGIF.$(OBJEXT) \ + tkImgPNG.$(OBJEXT) \ + tkImgPPM.$(OBJEXT) \ + tkImgPhoto.$(OBJEXT) \ + tkImgPhInstance.$(OBJEXT) \ + tkImgUtil.$(OBJEXT) \ + tkListbox.$(OBJEXT) \ + tkMacWinMenu.$(OBJEXT) \ + tkMain.$(OBJEXT) \ + tkMain2.$(OBJEXT) \ + tkMenu.$(OBJEXT) \ + tkMenubutton.$(OBJEXT) \ + tkMenuDraw.$(OBJEXT) \ + tkMessage.$(OBJEXT) \ + tkPanedWindow.$(OBJEXT) \ + tkObj.$(OBJEXT) \ + tkOldConfig.$(OBJEXT) \ + tkOption.$(OBJEXT) \ + tkPack.$(OBJEXT) \ + tkPlace.$(OBJEXT) \ + tkPointer.$(OBJEXT) \ + tkRectOval.$(OBJEXT) \ + tkScale.$(OBJEXT) \ + tkScrollbar.$(OBJEXT) \ + tkSelect.$(OBJEXT) \ + tkStyle.$(OBJEXT) \ + tkText.$(OBJEXT) \ + tkTextBTree.$(OBJEXT) \ + tkTextDisp.$(OBJEXT) \ + tkTextImage.$(OBJEXT) \ + tkTextIndex.$(OBJEXT) \ + tkTextMark.$(OBJEXT) \ + tkTextTag.$(OBJEXT) \ + tkTextWind.$(OBJEXT) \ + tkTrig.$(OBJEXT) \ + tkUndo.$(OBJEXT) \ + tkUtil.$(OBJEXT) \ + tkVisual.$(OBJEXT) \ + tkStubInit.$(OBJEXT) \ + tkWindow.$(OBJEXT) \ + $(TTK_OBJS) + +TTK_OBJS = \ + ttkWinMonitor.$(OBJEXT) \ + ttkWinTheme.$(OBJEXT) \ + ttkWinXPTheme.$(OBJEXT) \ + ttkBlink.$(OBJEXT) \ + ttkButton.$(OBJEXT) \ + ttkCache.$(OBJEXT) \ + ttkClamTheme.$(OBJEXT) \ + ttkClassicTheme.$(OBJEXT) \ + ttkDefaultTheme.$(OBJEXT) \ + ttkElements.$(OBJEXT) \ + ttkEntry.$(OBJEXT) \ + ttkFrame.$(OBJEXT) \ + ttkImage.$(OBJEXT) \ + ttkInit.$(OBJEXT) \ + ttkLabel.$(OBJEXT) \ + ttkLayout.$(OBJEXT) \ + ttkManager.$(OBJEXT) \ + ttkNotebook.$(OBJEXT) \ + ttkPanedwindow.$(OBJEXT) \ + ttkProgress.$(OBJEXT) \ + ttkScale.$(OBJEXT) \ + ttkScrollbar.$(OBJEXT) \ + ttkScroll.$(OBJEXT) \ + ttkSeparator.$(OBJEXT) \ + ttkSquare.$(OBJEXT) \ + ttkState.$(OBJEXT) \ + ttkTagSet.$(OBJEXT) \ + ttkTheme.$(OBJEXT) \ + ttkTrace.$(OBJEXT) \ + ttkTrack.$(OBJEXT) \ + ttkTreeview.$(OBJEXT) \ + ttkWidget.$(OBJEXT) \ + ttkStubInit.$(OBJEXT) + +STUB_OBJS = \ + tkStubLib.$(OBJEXT) \ + ttkStubLib.$(OBJEXT) + +TCL_DOCS = "$(TCL_SRC_DIR_NATIVE)/doc/*.[13n]" +TK_DOCS = "$(ROOT_DIR_NATIVE)/doc/*.[13n]" +CORE_DOCS = $(TCL_DOCS) $(TK_DOCS) + +DEMOPROGS = browse hello ixset rmt rolodex square tcolor timer widget + +SHELL_ENV = \ + @TCL_LIBRARY="$(TCL_SRC_DIR_NATIVE)/library"; export TCL_LIBRARY; \ + TK_LIBRARY="$(ROOT_DIR_NATIVE)/library"; export TK_LIBRARY; \ + PATH="$(TCL_BIN_DIR):$(PATH)"; export PATH; + +# Main targets. The default target -- all -- builds the binaries, +# performs any post processing on libraries or documents. + +all: binaries libraries doc + +binaries: @LIBRARIES@ $(WISH) + +libraries: + +$(ROOT_DIR)/doc/man.macros: + $(INSTALL_DATA) "$(TCL_SRC_DIR)/doc/man.macros" "$(ROOT_DIR)/doc/man.macros" + +doc: $(ROOT_DIR)/doc/man.macros + +winhelp: $(TCL_SRC_DIR)/tools/man2help.tcl $(MAN2TCL) + $(TCL_EXE) "$(TCL_SRC_DIR_NATIVE)/tools/man2help.tcl" tcl "$(VER)" $(CORE_DOCS) + $(COPY) "$(TCL_BIN_DIR)/tcl.hpj" ./ + hcw /c /e tcl.hpj + $(COPY) ./tcl$(VER).cnt ./TCL$(VER).HLP "$(TCL_SRC_DIR_NATIVE)/tools/" + +$(MAN2TCL): $(TCL_SRC_DIR)/tools/man2tcl.c + $(CC) $(CFLAGS_OPTIMIZE) $(MAN2TCLFLAGS) -o $(MAN2TCL) "$(TCL_SRC_DIR_NATIVE)/tools/man2tcl.c" + +# Specifying TESTFLAGS on the command line is the standard way to pass +# args to tcltest, ie: +# % make test TESTFLAGS="-verbose bps -file fileName.test" + +test: test-classic test-ttk + +test-classic: binaries $(TKTEST) $(TEST_DLL_FILE) $(CAT32) + $(SHELL_ENV) ./$(TKTEST) "$(ROOT_DIR_NATIVE)/tests/all.tcl" \ + $(TESTFLAGS) | ./$(CAT32) + +test-ttk: binaries $(TKTEST) $(TEST_DLL_FILE) $(CAT32) + $(SHELL_ENV) ./$(TKTEST) "$(ROOT_DIR_NATIVE)/tests/ttk/all.tcl" \ + $(TESTFLAGS) | ./$(CAT32) + +runtest: binaries $(TKTEST) $(TEST_DLL_FILE) + $(SHELL_ENV) ./$(TKTEST) $(TESTFLAGS) $(SCRIPT) + +# This target can be used to run wish from the build directory +# via `make shell` or `make shell SCRIPT=foo.tcl` +shell: binaries + $(SHELL_ENV) ./$(WISH) $(SCRIPT) + +demo: $(WISH) + $(SHELL_ENV) ./$(WISH) $(ROOT_DIR)/library/demos/widget + +# This target can be used to run wish inside either gdb or insight +gdb: binaries + @echo "set env TCL_LIBRARY=$(TCL_SRC_DIR_NATIVE)/library" > gdb.run + @echo "set env TK_LIBRARY=$(ROOT_DIR_NATIVE)/library" >> gdb.run + PATH="$(TCL_BIN_DIR):$(PATH)"; export PATH; \ + gdb ./$(WISH) --command=gdb.run + @$(RM) gdb.run + +install: all install-binaries install-libraries install-doc install-demos + +install-binaries: binaries + @for i in $(LIB_INSTALL_DIR) $(BIN_INSTALL_DIR) $(PKG_INSTALL_DIR); \ + do \ + if [ ! -d $$i ] ; then \ + echo "Making directory $$i"; \ + $(MKDIR) $$i; \ + chmod 755 $$i; \ + else true; \ + fi; \ + done; + @for i in $(TK_DLL_FILE) $(WISH); \ + do \ + if [ -f $$i ]; then \ + echo "Installing $$i to $(BIN_INSTALL_DIR)/"; \ + $(COPY) $$i "$(BIN_INSTALL_DIR)"; \ + fi; \ + done + @echo "Creating package index $(PKG_INDEX)"; + @$(RM) $(PKG_INDEX); + @(\ + echo "if {[catch {package present Tcl 8.6.0}]} return";\ + echo "if {(\$$::tcl_platform(platform) eq \"unix\") && ([info exists ::env(DISPLAY)]";\ + echo " || ([info exists ::argv] && (\"-display\" in \$$::argv)))} {";\ + echo " package ifneeded Tk $(VERSION)$(PATCH_LEVEL) [list load [file normalize [file join \$$dir .. .. bin libtk$(VERSION).dll]] Tk]";\ + echo "} else {";\ + echo " package ifneeded Tk $(VERSION)$(PATCH_LEVEL) [list load [file normalize [file join \$$dir .. .. bin $(TK_DLL_FILE)]] Tk]";\ + echo "}";\ + ) > $(PKG_INDEX); + @for i in tkConfig.sh $(TK_LIB_FILE) $(TK_STUB_LIB_FILE); \ + do \ + if [ -f $$i ]; then \ + echo "Installing $$i to $(LIB_INSTALL_DIR)/"; \ + $(COPY) $$i "$(LIB_INSTALL_DIR)"; \ + fi; \ + done + +install-libraries: libraries + @for i in $(INSTALL_ROOT)$(prefix)/lib \ + $(INCLUDE_INSTALL_DIR) $(INCLUDE_INSTALL_DIR)/X11 \ + $(SCRIPT_INSTALL_DIR) $(SCRIPT_INSTALL_DIR)/images \ + $(SCRIPT_INSTALL_DIR)/msgs $(SCRIPT_INSTALL_DIR)/ttk; \ + do \ + if [ ! -d $$i ] ; then \ + echo "Making directory $$i"; \ + $(MKDIR) $$i; \ + chmod 755 $$i; \ + else true; \ + fi; \ + done; + @echo "Installing header files to $(INCLUDE_INSTALL_DIR)/"; + @for i in $(GENERIC_DIR)/tk.h $(GENERIC_DIR)/tkPlatDecls.h \ + $(GENERIC_DIR)/tkIntXlibDecls.h $(GENERIC_DIR)/tkDecls.h ; \ + do \ + $(INSTALL_DATA) $$i $(INCLUDE_INSTALL_DIR); \ + done; + @for i in $(XLIB_DIR)/X11/*.h; \ + do \ + $(INSTALL_DATA) $$i $(INCLUDE_INSTALL_DIR)/X11; \ + done; + @echo "Installing library files to $(SCRIPT_INSTALL_DIR)"; + @for i in $(ROOT_DIR)/library/*.tcl $(ROOT_DIR)/library/tclIndex \ + $(UNIX_DIR)/tkAppInit.c; \ + do \ + $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR); \ + done; + @echo "Installing library ttk directory"; + @for i in $(ROOT_DIR)/library/ttk/*.tcl; \ + do \ + if [ -f $$i ] ; then \ + $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/ttk; \ + fi; \ + done; + @echo "Installing library images directory"; + @for i in $(ROOT_DIR)/library/images/*; \ + do \ + if [ -f $$i ] ; then \ + $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/images; \ + fi; \ + done; + @echo "Installing translation directory"; + @for i in $(ROOT_DIR)/library/msgs/*.msg; \ + do \ + if [ -f $$i ] ; then \ + $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/msgs; \ + fi; \ + done; + +install-demos: + @for i in $(INSTALL_ROOT)$(prefix)/lib $(SCRIPT_INSTALL_DIR) \ + $(SCRIPT_INSTALL_DIR)/demos \ + $(SCRIPT_INSTALL_DIR)/demos/images ; \ + do \ + if [ ! -d $$i ] ; then \ + echo "Making directory $$i"; \ + $(MKDIR) $$i; \ + chmod 755 $$i; \ + else true; \ + fi; \ + done; + @echo "Installing demos to $(SCRIPT_INSTALL_DIR)/demos/"; + @for i in $(ROOT_DIR)/library/demos/*; \ + do \ + if [ -f $$i ] ; then \ + sed -e '3 s|exec wish|exec wish$(VER)|' \ + $$i > $(SCRIPT_INSTALL_DIR)/demos/`basename $$i`; \ + fi; \ + done; + @for i in $(DEMOPROGS); \ + do \ + if test $$i = "square"; then \ + rm -f $(SCRIPT_INSTALL_DIR)/demos/$$i; \ + else \ + chmod 755 $(SCRIPT_INSTALL_DIR)/demos/$$i; \ + fi; \ + done; + @echo "Installing demo images"; + @for i in $(ROOT_DIR)/library/demos/images/*; \ + do \ + if [ -f $$i ] ; then \ + $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/demos/images; \ + fi; \ + done; + +install-doc: doc + +# Optional target to install private headers +install-private-headers: libraries + @for i in $(PRIVATE_INCLUDE_INSTALL_DIR); \ + do \ + if [ ! -d $$i ] ; then \ + echo "Making directory $$i"; \ + $(MKDIR) $$i; \ + chmod 755 $$i; \ + else true; \ + fi; \ + done; + @echo "Installing private header files to $(PRIVATE_INCLUDE_INSTALL_DIR)/"; + @for i in $(GENERIC_DIR)/tkInt.h $(GENERIC_DIR)/tkIntDecls.h \ + $(GENERIC_DIR)/tkIntPlatDecls.h $(GENERIC_DIR)/tkPort.h \ + $(WIN_DIR)/tkWinPort.h $(WIN_DIR)/tkWinInt.h $(WIN_DIR)/tkWin.h; \ + do \ + $(INSTALL_DATA) $$i $(PRIVATE_INCLUDE_INSTALL_DIR); \ + done; + +$(WISH): $(WISH_OBJS) @LIBRARIES@ $(TK_STUB_LIB_FILE) wish.$(RES) + $(CC) $(CFLAGS) $(WISH_OBJS) $(TK_LIB_FILE) \ + $(TK_STUB_LIB_FILE) $(TCL_LIB_FILE) $(LIBS) \ + wish.$(RES) $(CC_EXENAME) $(LDFLAGS_WINDOW) + @VC_MANIFEST_EMBED_EXE@ + +tktest: $(TKTEST) + +$(TKTEST): testMain.$(OBJEXT) $(TEST_DLL_FILE) @LIBRARIES@ $(TK_STUB_LIB_FILE) wish.$(RES) + $(CC) $(CFLAGS) testMain.$(OBJEXT) $(TEST_LIB_FILE) $(TK_LIB_FILE) \ + $(TK_STUB_LIB_FILE) $(TCL_LIB_FILE) $(LIBS) \ + wish.$(RES) $(CC_EXENAME) $(LDFLAGS_WINDOW) + @VC_MANIFEST_EMBED_EXE@ + +${TEST_DLL_FILE}: ${TKTEST_OBJS} ${TK_STUB_LIB_FILE} + @MAKE_DLL@ ${TKTEST_OBJS} $(TK_STUB_LIB_FILE) $(SHLIB_LD_LIBS) + +# Msys make requires this next rule for some reason. +$(TCL_SRC_DIR)/win/cat.c: + +cat32.${OBJEXT}: $(TCL_SRC_DIR)/win/cat.c + $(CC) -c $(CC_SWITCHES) "$(TCL_SRC_DIR)/win/cat.c" $(CC_OBJNAME) + +$(CAT32): cat32.${OBJEXT} + $(CC) $(CFLAGS) cat32.$(OBJEXT) $(CC_EXENAME) $(LIBS) $(LDFLAGS_CONSOLE) + +# The following targets are configured by autoconf to generate either +# a shared library or static library + +${TK_STUB_LIB_FILE}: ${STUB_OBJS} + @$(RM) ${TK_STUB_LIB_FILE} + @MAKE_STUB_LIB@ ${STUB_OBJS} + @POST_MAKE_LIB@ + +${TK_DLL_FILE}: ${TK_OBJS} $(TK_RES) + @$(RM) ${TK_DLL_FILE} + @MAKE_DLL@ ${TK_OBJS} $(TK_RES) $(SHLIB_LD_LIBS) + @VC_MANIFEST_EMBED_DLL@ + +${TK_LIB_FILE}: ${TK_OBJS} + @$(RM) ${TK_LIB_FILE} + @MAKE_LIB@ ${TK_OBJS} + @POST_MAKE_LIB@ + +# Special case object file targets + +winMain.$(OBJEXT): winMain.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ $(CC_OBJNAME) + +testMain.$(OBJEXT): winMain.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ -DTK_TEST $(CC_OBJNAME) + +tkTest.$(OBJEXT): tkTest.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ $(CC_OBJNAME) + +tkOldTest.$(OBJEXT): tkOldTest.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ $(CC_OBJNAME) + +tkWinTest.$(OBJEXT): tkWinTest.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ $(CC_OBJNAME) + +tkSquare.$(OBJEXT): tkSquare.c + $(CC) -c $(CC_SWITCHES) @DEPARG@ $(CC_OBJNAME) + +tkMain2.$(OBJEXT): tkMain.c + $(CC) -c $(CC_SWITCHES) -DBUILD_tk -DTK_ASCII_MAIN @DEPARG@ $(CC_OBJNAME) + +# Extra dependency info +tkConsole.$(OBJEXT): configure Makefile +tkMain.$(OBJEXT): configure Makefile +tkWindow.$(OBJEXT): configure Makefile + +# Add the object extension to the implicit rules. By default .obj is not +# automatically added. + +.SUFFIXES: .${OBJEXT} +.SUFFIXES: .$(RES) +.SUFFIXES: .rc + +# Implicit rule for all object files that will end up in the Tk library + +%.$(OBJEXT): %.c + $(CC) -c $(CC_SWITCHES) -DBUILD_tk -DBUILD_ttk @DEPARG@ $(CC_OBJNAME) + +.rc.$(RES): + $(RC) @RC_OUT@ $@ @RC_TYPE@ @RC_DEFINES@ @RC_INCLUDE@ "$(GENERIC_DIR_NATIVE)" @RC_INCLUDE@ "$(TCL_GENERIC_NATIVE)" @RC_INCLUDE@ "$(RC_DIR_NATIVE)" @DEPARG@ + +depend: + +cleanhelp: + $(RM) *.hlp *.cnt *.hpj *.GID *.rtf man2tcl${EXEEXT} + +clean: cleanhelp + $(RM) *.lib *.a *.exp *.dll *.res *.${OBJEXT} *~ \#* TAGS a.out + $(RM) $(WISH) $(TKTEST) $(CAT32) + $(RM) *.pch *.ilk *.pdb + +distclean: clean + $(RM) Makefile config.status config.cache config.log tkConfig.sh \ + wish.exe.manifest + +Makefile: $(SRC_DIR)/Makefile.in + ./config.status + +# +# Regenerate the stubs files. +# + +$(GENERIC_DIR)/tkStubInit.c: $(GENERIC_DIR)/tk.decls \ + $(GENERIC_DIR)/tkInt.decls + @echo "Warning: tkStubInit.c may be out of date." + @echo "Developers may want to run \"make genstubs\" to regenerate." + @echo "This warning can be safely ignored, do not report as a bug!" + +genstubs: + $(TCL_EXE) "$(TCL_TOOL_DIR)/genStubs.tcl" \ + "$(GENERIC_DIR_NATIVE)" \ + "$(GENERIC_DIR_NATIVE)/tk.decls" \ + "$(GENERIC_DIR_NATIVE)/tkInt.decls" + $(TCL_EXE) "$(TTK_DIR)/ttkGenStubs.tcl" \ + "$(TTK_DIR)" \ + "$(TTK_DIR)/ttk.decls" + +# +# The list of all the targets that do not correspond to real files. This stops +# 'make' from getting confused when someone makes an error in a rule. +# + +.PHONY: all binaries libraries doc tkLibObjs objs tktest-real test test-classic +.PHONY: test-ttk testlang runtest shell demo gdb install install-strip +.PHONY: install-binaries install-libraries install-demos install-doc +.PHONY: install-private-headers clean distclean depend genstubs checkstubs +.PHONY: checkuchar checkexports rpm dist alldist allpatch html html-tcl html-tk + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/tk8.6/win/README b/tk8.6/win/README new file mode 100644 index 0000000..8670446 --- /dev/null +++ b/tk8.6/win/README @@ -0,0 +1,21 @@ +Tk 8.6 for Windows + +Originally by Scott Stanton while at Sun Microsystems Labs + +This is the directory where you configure and compile the Windows +version of Tk. This directory also contains source files for Tk +that are specific to Microsoft Windows. The rest of this file +contains information specific to the Windows version of Tk. + +Please see the README and win/README files that come with the +associated Tcl release for an extensive set of pointers to +documentation. You will need to obtain and compile the +Tcl release before using the Tk source distrition. + +If you install the Tk sources next to the Tcl sources, then +the Tk Makefiles (e.g., makefile.vc for VC++) will properly +locate the necessary Tcl files. Otherwise you may need to +edit makefile.vc and adjust the path to Tcl accordingly. + +Information about compiling for windows is maintained at: + http://www.tcl.tk/doc/howto/compile.html diff --git a/tk8.6/win/aclocal.m4 b/tk8.6/win/aclocal.m4 new file mode 100644 index 0000000..bc7540d --- /dev/null +++ b/tk8.6/win/aclocal.m4 @@ -0,0 +1 @@ +builtin(include,tcl.m4) diff --git a/tk8.6/win/buildall.vc.bat b/tk8.6/win/buildall.vc.bat new file mode 100755 index 0000000..8f6803b --- /dev/null +++ b/tk8.6/win/buildall.vc.bat @@ -0,0 +1,107 @@ +@echo off
+
+:: This is an example batchfile for building everything. Please
+:: edit this (or make your own) for your needs and wants using
+:: the instructions for calling makefile.vc found in makefile.vc
+
+set SYMBOLS=
+
+:OPTIONS
+if "%1" == "/?" goto help
+if /i "%1" == "/help" goto help
+if %1.==symbols. goto SYMBOLS
+if %1.==debug. goto SYMBOLS
+goto OPTIONS_DONE
+
+:SYMBOLS
+ set SYMBOLS=symbols
+ shift
+ goto OPTIONS
+
+:OPTIONS_DONE
+
+:: reset errorlevel
+cd > nul
+
+:: You might have installed your developer studio to add itself to the
+:: path or have already run vcvars32.bat. Testing these envars proves
+:: cl.exe and friends are in your path.
+::
+if defined VCINSTALLDIR (goto :startBuilding)
+if defined MSDEVDIR (goto :startBuilding)
+if defined MSVCDIR (goto :startBuilding)
+if defined MSSDK (goto :startBuilding)
+if defined WINDOWSSDKDIR (goto :startBuilding)
+
+:: We need to run the development environment batch script that comes
+:: with developer studio (v4,5,6,7,etc...) All have it. This path
+:: might not be correct. You should call it yourself prior to running
+:: this batchfile.
+::
+call "C:\Program Files\Microsoft Developer Studio\vc98\bin\vcvars32.bat"
+if errorlevel 1 (goto no_vcvars)
+
+:startBuilding
+
+echo.
+echo Sit back and have a cup of coffee while this grinds through ;)
+echo You asked for *everything*, remember?
+echo.
+title Building Tk, please wait...
+
+
+:: makefile.vc uses this for its default anyways, but show its use here
+:: just to be explicit and convey understanding to the user. Setting
+:: the INSTALLDIR envar prior to running this batchfile affects all builds.
+::
+if "%INSTALLDIR%" == "" set INSTALLDIR=C:\Program Files\Tcl
+
+
+:: Where is the Tcl source directory?
+:: You can set the TCLDIR environment variable to your Tcl HEAD checkout
+if "%TCLDIR%" == "" set TCLDIR=..\..\tcl
+
+:: Build the normal stuff along with the help file.
+::
+set OPTS=none
+if not %SYMBOLS%.==. set OPTS=symbols
+nmake -nologo -f makefile.vc release htmlhelp OPTS=%OPTS% %1
+if errorlevel 1 goto error
+
+:: Build the static core and shell.
+::
+set OPTS=static,msvcrt
+if not %SYMBOLS%.==. set OPTS=symbols,static,msvcrt
+nmake -nologo -f makefile.vc shell OPTS=%OPTS% %1
+if errorlevel 1 goto error
+
+set OPTS=
+set SYMBOLS=
+goto end
+
+:error
+echo *** BOOM! ***
+goto end
+
+:no_vcvars
+echo vcvars32.bat was not run prior to this batchfile, nor are the MS tools in your path.
+goto out
+
+:help
+title buildall.vc.bat help message
+echo usage:
+echo %0 : builds Tk for all build types (do this first)
+echo %0 install : installs all the release builds (do this second)
+echo %0 symbols : builds Tk for all debugging build types
+echo %0 symbols install : install all the debug builds.
+echo.
+goto out
+
+:end
+title Building Tk, please wait... DONE!
+echo DONE!
+goto out
+
+:out
+pause
+title Command Prompt
diff --git a/tk8.6/win/configure b/tk8.6/win/configure new file mode 100755 index 0000000..15d509e --- /dev/null +++ b/tk8.6/win/configure @@ -0,0 +1,6176 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="../generic/tk.h" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP AR ac_ct_AR RANLIB ac_ct_RANLIB RC ac_ct_RC SET_MAKE TCL_THREADS TCL_VERSION TCL_BIN_DIR TCL_SRC_DIR TCL_LIB_FILE TCL_LIB_FLAG TCL_LIB_SPEC TCL_STUB_LIB_FILE TCL_STUB_LIB_FLAG TCL_STUB_LIB_SPEC TCL_DEFS CYGPATH CELIB_DIR DL_LIBS CFLAGS_DEBUG CFLAGS_OPTIMIZE CFLAGS_WARNING MAN2TCLFLAGS CFLAGS_DEFAULT LDFLAGS_DEFAULT VC_MANIFEST_EMBED_DLL VC_MANIFEST_EMBED_EXE BUILD_TCLSH TCLSH_PROG TK_WIN_VERSION MACHINE TK_VERSION TK_MAJOR_VERSION TK_MINOR_VERSION TK_PATCH_LEVEL TK_DBGX TK_LIB_FILE TK_DLL_FILE TK_STUB_LIB_FILE TK_STUB_LIB_FLAG TK_BUILD_STUB_LIB_SPEC TK_SRC_DIR TK_BIN_DIR TCL_MAJOR_VERSION TCL_MINOR_VERSION TCL_PATCH_LEVEL TCL_DBGX CFG_TK_SHARED_LIB_SUFFIX CFG_TK_UNSHARED_LIB_SUFFIX CFG_TK_EXPORT_FILE_SUFFIX EXTRA_CFLAGS DEPARG CC_OBJNAME CC_EXENAME LDFLAGS_DEBUG LDFLAGS_OPTIMIZE LDFLAGS_CONSOLE LDFLAGS_WINDOW TK_RES STLIB_LD SHLIB_LD SHLIB_LD_LIBS SHLIB_CFLAGS SHLIB_SUFFIX TK_SHARED_BUILD LIBS_GUI DLLSUFFIX LIBPREFIX LIBSUFFIX EXESUFFIX LIBRARIES MAKE_LIB MAKE_STUB_LIB POST_MAKE_LIB MAKE_DLL MAKE_EXE TK_LIB_FLAG TK_LIB_SPEC TK_BUILD_LIB_SPEC TK_STUB_LIB_SPEC TK_STUB_LIB_PATH TK_BUILD_STUB_LIB_PATH TK_CC_SEARCH_FLAGS TK_LD_SEARCH_FLAGS RC_OUT RC_TYPE RC_INCLUDE RC_DEFINE RC_DEFINES RES LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +ac_prev= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_option in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "enable_$ac_feature='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/-/_/g'` + eval "with_$ac_package=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-threads build with threads (default: on) + --enable-shared build and link with shared libraries (default: on) + --enable-64bit enable 64bit support (where applicable) + --enable-wince enable Win/CE support (where applicable) + --enable-symbols build with debugging symbols (default: off) + --enable-embedded-manifest + embed manifest if possible (default: yes) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-tcl directory containing tcl configuration + (tclConfig.sh) + --with-celib=DIR use Windows/CE support library from DIR + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have + headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd $ac_popdir + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo >confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + + + +# The following define is needed when building with Cygwin since newer +# versions of autoconf incorrectly set SHELL to /bin/bash instead of +# /bin/sh. The bash shell seems to suffer from some strange failures. +SHELL=/bin/sh + +TK_VERSION=8.6 +TK_MAJOR_VERSION=8 +TK_MINOR_VERSION=6 +TK_PATCH_LEVEL=".8" +VER=$TK_MAJOR_VERSION$TK_MINOR_VERSION + +#------------------------------------------------------------------------ +# Handle the --prefix=... option +#------------------------------------------------------------------------ + +if test "${prefix}" = "NONE"; then + prefix=/usr/local +fi +if test "${exec_prefix}" = "NONE"; then + exec_prefix=$prefix +fi +# libdir must be a fully qualified path (not ${exec_prefix}/lib) +eval libdir="$libdir" + +#------------------------------------------------------------------------ +# Standard compiler checks +#------------------------------------------------------------------------ + +# If the user did not set CFLAGS, set it now to keep +# the AC_PROG_CC macro from adding "-g -O2". +if test "${CFLAGS+set}" != "set" ; then + CFLAGS="" +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for inline" >&5 +echo $ECHO_N "checking for inline... $ECHO_C" >&6 +if test "${ac_cv_c_inline+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_c_inline=no +for ac_kw in inline __inline__ __inline; do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifndef __cplusplus +typedef int foo_t; +static $ac_kw foo_t static_foo () {return 0; } +$ac_kw foo_t foo () {return 0; } +#endif + +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_inline=$ac_kw; break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_inline" >&5 +echo "${ECHO_T}$ac_cv_c_inline" >&6 + + +case $ac_cv_c_inline in + inline | yes) ;; + *) + case $ac_cv_c_inline in + no) ac_val=;; + *) ac_val=$ac_cv_c_inline;; + esac + cat >>confdefs.h <<_ACEOF +#ifndef __cplusplus +#define inline $ac_val +#endif +_ACEOF + ;; +esac + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + exit(2); + exit (0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + echo "$as_me:$LINENO: result: $AR" >&5 +echo "${ECHO_T}$AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_AR+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="ar" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + echo "$as_me:$LINENO: result: $ac_ct_AR" >&5 +echo "${ECHO_T}$ac_ct_AR" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + AR=$ac_ct_AR +else + AR="$ac_cv_prog_AR" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + echo "$as_me:$LINENO: result: $RANLIB" >&5 +echo "${ECHO_T}$RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 +echo "${ECHO_T}$ac_ct_RANLIB" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RANLIB=$ac_ct_RANLIB +else + RANLIB="$ac_cv_prog_RANLIB" +fi + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args. +set dummy ${ac_tool_prefix}windres; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_RC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$RC"; then + ac_cv_prog_RC="$RC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RC="${ac_tool_prefix}windres" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +RC=$ac_cv_prog_RC +if test -n "$RC"; then + echo "$as_me:$LINENO: result: $RC" >&5 +echo "${ECHO_T}$RC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_RC"; then + ac_ct_RC=$RC + # Extract the first word of "windres", so it can be a program name with args. +set dummy windres; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_RC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_RC"; then + ac_cv_prog_ac_ct_RC="$ac_ct_RC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RC="windres" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_RC=$ac_cv_prog_ac_ct_RC +if test -n "$ac_ct_RC"; then + echo "$as_me:$LINENO: result: $ac_ct_RC" >&5 +echo "${ECHO_T}$ac_ct_RC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + RC=$ac_ct_RC +else + RC="$ac_cv_prog_RC" +fi + + +#-------------------------------------------------------------------- +# Checks to see if the make program sets the $MAKE variable. +#-------------------------------------------------------------------- + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + + +#-------------------------------------------------------------------- +# Determines the correct binary file extension (.o, .obj, .exe etc.) +#-------------------------------------------------------------------- + + + + +#-------------------------------------------------------------------- +# Check whether --enable-threads or --disable-threads was given. +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking for building with threads" >&5 +echo $ECHO_N "checking for building with threads... $ECHO_C" >&6 + # Check whether --enable-threads or --disable-threads was given. +if test "${enable_threads+set}" = set; then + enableval="$enable_threads" + tcl_ok=$enableval +else + tcl_ok=yes +fi; + + if test "$tcl_ok" = "yes"; then + echo "$as_me:$LINENO: result: yes (default)" >&5 +echo "${ECHO_T}yes (default)" >&6 + TCL_THREADS=1 + cat >>confdefs.h <<\_ACEOF +#define TCL_THREADS 1 +_ACEOF + + # USE_THREAD_ALLOC tells us to try the special thread-based + # allocator that significantly reduces lock contention + cat >>confdefs.h <<\_ACEOF +#define USE_THREAD_ALLOC 1 +_ACEOF + + else + TCL_THREADS=0 + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + fi + + + +#-------------------------------------------------------------------- +# The statements below define a collection of symbols related to +# building libtk as a shared library instead of a static library. +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking how to build libraries" >&5 +echo $ECHO_N "checking how to build libraries... $ECHO_C" >&6 + # Check whether --enable-shared or --disable-shared was given. +if test "${enable_shared+set}" = set; then + enableval="$enable_shared" + tcl_ok=$enableval +else + tcl_ok=yes +fi; + + if test "${enable_shared+set}" = set; then + enableval="$enable_shared" + tcl_ok=$enableval + else + tcl_ok=yes + fi + + if test "$tcl_ok" = "yes" ; then + echo "$as_me:$LINENO: result: shared" >&5 +echo "${ECHO_T}shared" >&6 + SHARED_BUILD=1 + else + echo "$as_me:$LINENO: result: static" >&5 +echo "${ECHO_T}static" >&6 + SHARED_BUILD=0 + +cat >>confdefs.h <<\_ACEOF +#define STATIC_BUILD 1 +_ACEOF + + fi + + +#-------------------------------------------------------------------- +# Locate and source the tclConfig.sh file. +#-------------------------------------------------------------------- + + + # + # Ok, lets find the tcl configuration + # First, look for one uninstalled. + # the alternative search directory is invoked by --with-tcl + # + + if test x"${no_tcl}" = x ; then + # we reset no_tcl in case something fails here + no_tcl=true + +# Check whether --with-tcl or --without-tcl was given. +if test "${with_tcl+set}" = set; then + withval="$with_tcl" + with_tclconfig="${withval}" +fi; + echo "$as_me:$LINENO: checking for Tcl configuration" >&5 +echo $ECHO_N "checking for Tcl configuration... $ECHO_C" >&6 + if test "${ac_cv_c_tclconfig+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + + # First check to see if --with-tcl was specified. + if test x"${with_tclconfig}" != x ; then + case "${with_tclconfig}" in + */tclConfig.sh ) + if test -f "${with_tclconfig}"; then + { echo "$as_me:$LINENO: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&5 +echo "$as_me: WARNING: --with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself" >&2;} + with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" + fi ;; + esac + if test -f "${with_tclconfig}/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" + else + { { echo "$as_me:$LINENO: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&5 +echo "$as_me: error: ${with_tclconfig} directory doesn't contain tclConfig.sh" >&2;} + { (exit 1); exit 1; }; } + fi + fi + + # then check for a private Tcl installation + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in \ + ../tcl \ + `ls -dr ../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ + `ls -dr ../tcl[8-9].[0-9] 2>/dev/null` \ + `ls -dr ../tcl[8-9].[0-9]* 2>/dev/null` \ + ../../tcl \ + `ls -dr ../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ + `ls -dr ../../tcl[8-9].[0-9] 2>/dev/null` \ + `ls -dr ../../tcl[8-9].[0-9]* 2>/dev/null` \ + ../../../tcl \ + `ls -dr ../../../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ + `ls -dr ../../../tcl[8-9].[0-9] 2>/dev/null` \ + `ls -dr ../../../tcl[8-9].[0-9]* 2>/dev/null` ; do + if test -f "$i/win/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + + # check in a few common install locations + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in `ls -d ${libdir} 2>/dev/null` \ + `ls -d ${exec_prefix}/lib 2>/dev/null` \ + `ls -d ${prefix}/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Tcl/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d /c/Tcl/lib 2>/dev/null` \ + `ls -d /c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d C:/Tcl/lib 2>/dev/null` \ + `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ + ; do + if test -f "$i/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i; pwd)`" + break + fi + done + fi + + # check in a few other private locations + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in \ + ${srcdir}/../tcl \ + `ls -dr ${srcdir}/../tcl[8-9].[0-9].[0-9]* 2>/dev/null` \ + `ls -dr ${srcdir}/../tcl[8-9].[0-9] 2>/dev/null` \ + `ls -dr ${srcdir}/../tcl[8-9].[0-9]* 2>/dev/null` ; do + if test -f "$i/win/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + +fi + + + if test x"${ac_cv_c_tclconfig}" = x ; then + TCL_BIN_DIR="# no Tcl configs found" + { { echo "$as_me:$LINENO: error: Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh" >&5 +echo "$as_me: error: Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh" >&2;} + { (exit 1); exit 1; }; } + else + no_tcl= + TCL_BIN_DIR="${ac_cv_c_tclconfig}" + echo "$as_me:$LINENO: result: found ${TCL_BIN_DIR}/tclConfig.sh" >&5 +echo "${ECHO_T}found ${TCL_BIN_DIR}/tclConfig.sh" >&6 + fi + fi + + + echo "$as_me:$LINENO: checking for existence of ${TCL_BIN_DIR}/tclConfig.sh" >&5 +echo $ECHO_N "checking for existence of ${TCL_BIN_DIR}/tclConfig.sh... $ECHO_C" >&6 + + if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then + echo "$as_me:$LINENO: result: loading" >&5 +echo "${ECHO_T}loading" >&6 + . "${TCL_BIN_DIR}/tclConfig.sh" + else + echo "$as_me:$LINENO: result: could not find ${TCL_BIN_DIR}/tclConfig.sh" >&5 +echo "${ECHO_T}could not find ${TCL_BIN_DIR}/tclConfig.sh" >&6 + fi + + # + # If the TCL_BIN_DIR is the build directory (not the install directory), + # then set the common variable name to the value of the build variables. + # For example, the variable TCL_LIB_SPEC will be set to the value + # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC + # instead of TCL_BUILD_LIB_SPEC since it will work with both an + # installed and uninstalled version of Tcl. + # + + if test -f $TCL_BIN_DIR/Makefile ; then + TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC} + TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC} + TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH} + fi + + # + # eval is required to do the TCL_DBGX substitution + # + + eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" + eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" + eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" + + eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" + eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" + eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" + + + + + + + + + + + + + + + + +if test "${TCL_MAJOR_VERSION}" != "${TK_MAJOR_VERSION}"; then + { { echo "$as_me:$LINENO: error: ${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}." >&5 +echo "$as_me: error: ${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}." >&2;} + { (exit 1); exit 1; }; } +fi +if test "${TCL_MINOR_VERSION}" -lt "${TK_MINOR_VERSION}"; then + { { echo "$as_me:$LINENO: error: ${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}." >&5 +echo "$as_me: error: ${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}." >&2;} + { (exit 1); exit 1; }; } +fi + +#-------------------------------------------------------------------- +# The statements below define a collection of compile flags. This +# macro depends on the value of SHARED_BUILD, and should be called +# after SC_ENABLE_SHARED checks the configure switches. +#-------------------------------------------------------------------- + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + # Step 0: Enable 64 bit support? + + echo "$as_me:$LINENO: checking if 64bit support is requested" >&5 +echo $ECHO_N "checking if 64bit support is requested... $ECHO_C" >&6 + # Check whether --enable-64bit or --disable-64bit was given. +if test "${enable_64bit+set}" = set; then + enableval="$enable_64bit" + do64bit=$enableval +else + do64bit=no +fi; + echo "$as_me:$LINENO: result: $do64bit" >&5 +echo "${ECHO_T}$do64bit" >&6 + + # Cross-compiling options for Windows/CE builds + + echo "$as_me:$LINENO: checking if Windows/CE build is requested" >&5 +echo $ECHO_N "checking if Windows/CE build is requested... $ECHO_C" >&6 + # Check whether --enable-wince or --disable-wince was given. +if test "${enable_wince+set}" = set; then + enableval="$enable_wince" + doWince=$enableval +else + doWince=no +fi; + echo "$as_me:$LINENO: result: $doWince" >&5 +echo "${ECHO_T}$doWince" >&6 + + echo "$as_me:$LINENO: checking for Windows/CE celib directory" >&5 +echo $ECHO_N "checking for Windows/CE celib directory... $ECHO_C" >&6 + +# Check whether --with-celib or --without-celib was given. +if test "${with_celib+set}" = set; then + withval="$with_celib" + CELIB_DIR=$withval +else + CELIB_DIR=NO_CELIB +fi; + echo "$as_me:$LINENO: result: $CELIB_DIR" >&5 +echo "${ECHO_T}$CELIB_DIR" >&6 + + # Set some defaults (may get changed below) + EXTRA_CFLAGS="" + +cat >>confdefs.h <<\_ACEOF +#define MODULE_SCOPE extern +_ACEOF + + + # Extract the first word of "cygpath", so it can be a program name with args. +set dummy cygpath; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CYGPATH+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CYGPATH"; then + ac_cv_prog_CYGPATH="$CYGPATH" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CYGPATH="cygpath -m" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + + test -z "$ac_cv_prog_CYGPATH" && ac_cv_prog_CYGPATH="echo" +fi +fi +CYGPATH=$ac_cv_prog_CYGPATH +if test -n "$CYGPATH"; then + echo "$as_me:$LINENO: result: $CYGPATH" >&5 +echo "${ECHO_T}$CYGPATH" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + + SHLIB_SUFFIX=".dll" + + # MACHINE is IX86 for LINK, but this is used by the manifest, + # which requires x86|amd64|ia64. + MACHINE="X86" + + if test "$GCC" = "yes"; then + + echo "$as_me:$LINENO: checking for cross-compile version of gcc" >&5 +echo $ECHO_N "checking for cross-compile version of gcc... $ECHO_C" >&6 +if test "${ac_cv_cross+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #ifndef _WIN32 + #error cross-compiler + #endif + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_cross=no +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_cross=yes +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_cross" >&5 +echo "${ECHO_T}$ac_cv_cross" >&6 + + if test "$ac_cv_cross" = "yes"; then + case "$do64bit" in + amd64|x64|yes) + CC="x86_64-w64-mingw32-gcc" + LD="x86_64-w64-mingw32-ld" + AR="x86_64-w64-mingw32-ar" + RANLIB="x86_64-w64-mingw32-ranlib" + RC="x86_64-w64-mingw32-windres" + ;; + *) + CC="i686-w64-mingw32-gcc" + LD="i686-w64-mingw32-ld" + AR="i686-w64-mingw32-ar" + RANLIB="i686-w64-mingw32-ranlib" + RC="i686-w64-mingw32-windres" + ;; + esac + fi + fi + + # Check for a bug in gcc's windres that causes the + # compile to fail when a Windows native path is + # passed into windres. The mingw toolchain requires + # Windows native paths while Cygwin should work + # with both. Avoid the bug by passing a POSIX + # path when using the Cygwin toolchain. + + if test "$GCC" = "yes" && test "$CYGPATH" != "echo" ; then + conftest=/tmp/conftest.rc + echo "STRINGTABLE BEGIN" > $conftest + echo "101 \"name\"" >> $conftest + echo "END" >> $conftest + + echo "$as_me:$LINENO: checking for Windows native path bug in windres" >&5 +echo $ECHO_N "checking for Windows native path bug in windres... $ECHO_C" >&6 + cyg_conftest=`$CYGPATH $conftest` + if { ac_try='$RC -o conftest.res.o $cyg_conftest' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } ; then + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + else + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + CYGPATH=echo + fi + conftest= + cyg_conftest= + fi + + if test "$CYGPATH" = "echo"; then + DEPARG='"$<"' + else + DEPARG='"$(shell $(CYGPATH) $<)"' + fi + + # set various compiler flags depending on whether we are using gcc or cl + + if test "${GCC}" = "yes" ; then + extra_cflags="-pipe" + extra_ldflags="-pipe -static-libgcc" + echo "$as_me:$LINENO: checking for mingw32 version of gcc" >&5 +echo $ECHO_N "checking for mingw32 version of gcc... $ECHO_C" >&6 +if test "${ac_cv_win32+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #ifdef _WIN32 + #error win32 + #endif + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_win32=no +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_win32=yes +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_win32" >&5 +echo "${ECHO_T}$ac_cv_win32" >&6 + if test "$ac_cv_win32" != "yes"; then + { { echo "$as_me:$LINENO: error: ${CC} cannot produce win32 executables." >&5 +echo "$as_me: error: ${CC} cannot produce win32 executables." >&2;} + { (exit 1); exit 1; }; } + fi + + hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -mwindows -municode -Dmain=xxmain" + echo "$as_me:$LINENO: checking for working -municode linker flag" >&5 +echo $ECHO_N "checking for working -municode linker flag... $ECHO_C" >&6 +if test "${ac_cv_municode+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #include <windows.h> + int APIENTRY wWinMain(HINSTANCE a, HINSTANCE b, LPWSTR c, int d) {return 0;} + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_municode=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_municode=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $ac_cv_municode" >&5 +echo "${ECHO_T}$ac_cv_municode" >&6 + CFLAGS=$hold_cflags + if test "$ac_cv_municode" = "yes" ; then + extra_ldflags="$extra_ldflags -municode" + else + extra_cflags="$extra_cflags -DTCL_BROKEN_MAINARGS" + fi + fi + + echo "$as_me:$LINENO: checking compiler flags" >&5 +echo $ECHO_N "checking compiler flags... $ECHO_C" >&6 + if test "${GCC}" = "yes" ; then + SHLIB_LD="" + SHLIB_LD_LIBS='${LIBS}' + LIBS="-lnetapi32 -lkernel32 -luser32 -ladvapi32 -luserenv -lws2_32" + # mingw needs to link ole32 and oleaut32 for [send], but MSVC doesn't + LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32" + STLIB_LD='${AR} cr' + RC_OUT=-o + RC_TYPE= + RC_INCLUDE=--include + RC_DEFINE=--define + RES=res.o + MAKE_LIB="\${STLIB_LD} \$@" + MAKE_STUB_LIB="\${STLIB_LD} \$@" + POST_MAKE_LIB="\${RANLIB} \$@" + MAKE_EXE="\${CC} -o \$@" + LIBPREFIX="lib" + + if test "${SHARED_BUILD}" = "0" ; then + # static + echo "$as_me:$LINENO: result: using static flags" >&5 +echo "${ECHO_T}using static flags" >&6 + runtime= + LIBRARIES="\${STATIC_LIBRARIES}" + EXESUFFIX="s\${DBGX}.exe" + else + # dynamic + echo "$as_me:$LINENO: result: using shared flags" >&5 +echo "${ECHO_T}using shared flags" >&6 + + # ad-hoc check to see if CC supports -shared. + if "${CC}" -shared 2>&1 | egrep ': -shared not supported' >/dev/null; then + { { echo "$as_me:$LINENO: error: ${CC} does not support the -shared option. + You will need to upgrade to a newer version of the toolchain." >&5 +echo "$as_me: error: ${CC} does not support the -shared option. + You will need to upgrade to a newer version of the toolchain." >&2;} + { (exit 1); exit 1; }; } + fi + + runtime= + # Add SHLIB_LD_LIBS to the Make rule, not here. + + EXESUFFIX="\${DBGX}.exe" + LIBRARIES="\${SHARED_LIBRARIES}" + fi + # Link with gcc since ld does not link to default libs like + # -luser32 and -lmsvcrt by default. + SHLIB_LD='${CC} -shared' + SHLIB_LD_LIBS='${LIBS}' + MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -o \$@ ${extra_ldflags} \ + -Wl,--out-implib,\$(patsubst %.dll,lib%.a,\$@)" + # DLLSUFFIX is separate because it is the building block for + # users of tclConfig.sh that may build shared or static. + DLLSUFFIX="\${DBGX}.dll" + LIBSUFFIX="\${DBGX}.a" + LIBFLAGSUFFIX="\${DBGX}" + SHLIB_SUFFIX=.dll + + EXTRA_CFLAGS="${extra_cflags}" + + CFLAGS_DEBUG=-g + CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" + CFLAGS_WARNING="-Wall -Wdeclaration-after-statement" + LDFLAGS_DEBUG= + LDFLAGS_OPTIMIZE= + + # Specify the CC output file names based on the target name + CC_OBJNAME="-o \$@" + CC_EXENAME="-o \$@" + + # Specify linker flags depending on the type of app being + # built -- Console vs. Window. + # + # ORIGINAL COMMENT: + # We need to pass -e _WinMain@16 so that ld will use + # WinMain() instead of main() as the entry point. We can't + # use autoconf to check for this case since it would need + # to run an executable and that does not work when + # cross compiling. Remove this -e workaround once we + # require a gcc that does not have this bug. + # + # MK NOTE: Tk should use a different mechanism. This causes + # interesting problems, such as wish dying at startup. + #LDFLAGS_WINDOW="-mwindows -e _WinMain@16 ${extra_ldflags}" + LDFLAGS_CONSOLE="-mconsole ${extra_ldflags}" + LDFLAGS_WINDOW="-mwindows ${extra_ldflags}" + + case "$do64bit" in + amd64|x64|yes) + MACHINE="AMD64" ; # assume AMD64 as default 64-bit build + echo "$as_me:$LINENO: result: Using 64-bit $MACHINE mode" >&5 +echo "${ECHO_T} Using 64-bit $MACHINE mode" >&6 + ;; + ia64) + MACHINE="IA64" + echo "$as_me:$LINENO: result: Using 64-bit $MACHINE mode" >&5 +echo "${ECHO_T} Using 64-bit $MACHINE mode" >&6 + ;; + *) + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #ifndef _WIN64 + #error 32-bit + #endif + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_win_64bit=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_win_64bit=no + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + if test "$tcl_win_64bit" = "yes" ; then + do64bit=amd64 + MACHINE="AMD64" + echo "$as_me:$LINENO: result: Using 64-bit $MACHINE mode" >&5 +echo "${ECHO_T} Using 64-bit $MACHINE mode" >&6 + fi + ;; + esac + else + if test "${SHARED_BUILD}" = "0" ; then + # static + echo "$as_me:$LINENO: result: using static flags" >&5 +echo "${ECHO_T}using static flags" >&6 + runtime=-MT + LIBRARIES="\${STATIC_LIBRARIES}" + EXESUFFIX="s\${DBGX}.exe" + else + # dynamic + echo "$as_me:$LINENO: result: using shared flags" >&5 +echo "${ECHO_T}using shared flags" >&6 + runtime=-MD + # Add SHLIB_LD_LIBS to the Make rule, not here. + LIBRARIES="\${SHARED_LIBRARIES}" + EXESUFFIX="\${DBGX}.exe" + case "x`echo \${VisualStudioVersion}`" in + x1[4-9]*) + lflags="${lflags} -nodefaultlib:libucrt.lib" + ;; + *) + ;; + esac + fi + MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -out:\$@" + # DLLSUFFIX is separate because it is the building block for + # users of tclConfig.sh that may build shared or static. + DLLSUFFIX="\${DBGX}.dll" + LIBSUFFIX="\${DBGX}.lib" + LIBFLAGSUFFIX="\${DBGX}" + + # This is a 2-stage check to make sure we have the 64-bit SDK + # We have to know where the SDK is installed. + # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs + if test "$do64bit" != "no" ; then + if test "x${MSSDK}x" = "xx" ; then + MSSDK="C:/Progra~1/Microsoft Platform SDK" + fi + MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` + PATH64="" + case "$do64bit" in + amd64|x64|yes) + MACHINE="AMD64" ; # assume AMD64 as default 64-bit build + PATH64="${MSSDK}/Bin/Win64/x86/AMD64" + ;; + ia64) + MACHINE="IA64" + PATH64="${MSSDK}/Bin/Win64" + ;; + esac + if test ! -d "${PATH64}" ; then + { echo "$as_me:$LINENO: WARNING: Could not find 64-bit $MACHINE SDK" >&5 +echo "$as_me: WARNING: Could not find 64-bit $MACHINE SDK" >&2;} + fi + echo "$as_me:$LINENO: result: Using 64-bit $MACHINE mode" >&5 +echo "${ECHO_T} Using 64-bit $MACHINE mode" >&6 + fi + + LIBS="netapi32.lib kernel32.lib user32.lib advapi32.lib userenv.lib ws2_32.lib" + + case "x`echo \${VisualStudioVersion}`" in + x1[4-9]*) + LIBS="$LIBS ucrt.lib" + ;; + *) + ;; + esac + + if test "$do64bit" != "no" ; then + # The space-based-path will work for the Makefile, but will + # not work if AC_TRY_COMPILE is called. TEA has the + # TEA_PATH_NOSPACE to avoid this issue. + # Check if _WIN64 is already recognized, and if so we don't + # need to modify CC. + echo "$as_me:$LINENO: checking whether _WIN64 is declared" >&5 +echo $ECHO_N "checking whether _WIN64 is declared... $ECHO_C" >&6 +if test "${ac_cv_have_decl__WIN64+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +#ifndef _WIN64 + char *p = (char *) _WIN64; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_have_decl__WIN64=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_have_decl__WIN64=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_have_decl__WIN64" >&5 +echo "${ECHO_T}$ac_cv_have_decl__WIN64" >&6 +if test $ac_cv_have_decl__WIN64 = yes; then + : +else + CC="\"${PATH64}/cl.exe\" -I\"${MSSDK}/Include\" \ + -I\"${MSSDK}/Include/crt\" \ + -I\"${MSSDK}/Include/crt/sys\"" +fi + + RC="\"${MSSDK}/bin/rc.exe\"" + CFLAGS_DEBUG="-nologo -Zi -Od ${runtime}d" + # Do not use -O2 for Win64 - this has proved buggy in code gen. + CFLAGS_OPTIMIZE="-nologo -O1 ${runtime}" + lflags="${lflags} -nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" + LINKBIN="\"${PATH64}/link.exe\"" + # Avoid 'unresolved external symbol __security_cookie' errors. + # c.f. http://support.microsoft.com/?id=894573 + LIBS="$LIBS bufferoverflowU.lib" + else + RC="rc" + # -Od - no optimization + # -WX - warnings as errors + CFLAGS_DEBUG="-nologo -Z7 -Od -WX ${runtime}d" + # -O2 - create fast code (/Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) + CFLAGS_OPTIMIZE="-nologo -O2 ${runtime}" + lflags="${lflags} -nologo" + LINKBIN="link" + fi + + if test "$doWince" != "no" ; then + # Set defaults for common evc4/PPC2003 setup + # Currently Tcl requires 300+, possibly 420+ for sockets + CEVERSION=420; # could be 211 300 301 400 420 ... + TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... + ARCH=ARM; # could be ARM MIPS X86EM ... + PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" + if test "$doWince" != "yes"; then + # If !yes then the user specified something + # Reset ARCH to allow user to skip specifying it + ARCH= + eval `echo $doWince | awk -F "," '{ \ + if (length($1)) { printf "CEVERSION=\"%s\"\n", $1; \ + if ($1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ + if (length($2)) { printf "TARGETCPU=\"%s\"\n", toupper($2) }; \ + if (length($3)) { printf "ARCH=\"%s\"\n", toupper($3) }; \ + if (length($4)) { printf "PLATFORM=\"%s\"\n", $4 }; \ + }'` + if test "x${ARCH}" = "x" ; then + ARCH=$TARGETCPU; + fi + fi + OSVERSION=WCE$CEVERSION; + if test "x${WCEROOT}" = "x" ; then + WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" + if test ! -d "${WCEROOT}" ; then + WCEROOT="C:/Program Files/Microsoft eMbedded Tools" + fi + fi + if test "x${SDKROOT}" = "x" ; then + SDKROOT="C:/Program Files/Windows CE Tools" + if test ! -d "${SDKROOT}" ; then + SDKROOT="C:/Windows CE Tools" + fi + fi + # The space-based-path will work for the Makefile, but will + # not work if AC_TRY_COMPILE is called. + WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` + SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` + CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` + if test ! -d "${CELIB_DIR}/inc"; then + { { echo "$as_me:$LINENO: error: Invalid celib directory \"${CELIB_DIR}\"" >&5 +echo "$as_me: error: Invalid celib directory \"${CELIB_DIR}\"" >&2;} + { (exit 1); exit 1; }; } + fi + if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"\ + -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then + { { echo "$as_me:$LINENO: error: could not find PocketPC SDK or target compiler to enable WinCE mode $CEVERSION,$TARGETCPU,$ARCH,$PLATFORM" >&5 +echo "$as_me: error: could not find PocketPC SDK or target compiler to enable WinCE mode $CEVERSION,$TARGETCPU,$ARCH,$PLATFORM" >&2;} + { (exit 1); exit 1; }; } + else + CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" + if test -d "${CEINCLUDE}/${TARGETCPU}" ; then + CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" + fi + CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" + fi + fi + + if test "$doWince" != "no" ; then + CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" + if test "${TARGETCPU}" = "X86"; then + CC="${CEBINROOT}/cl.exe" + else + CC="${CEBINROOT}/cl${ARCH}.exe" + fi + CC="\"${CC}\" -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" + RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" + arch=`echo ${ARCH} | awk '{print tolower($0)}'` + defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _DLL _WINDOWS" + for i in $defs ; do + cat >>confdefs.h <<_ACEOF +#define $i 1 +_ACEOF + + done +# if test "${ARCH}" = "X86EM"; then +# AC_DEFINE_UNQUOTED(_WIN32_WCE_EMULATION) +# fi + cat >>confdefs.h <<_ACEOF +#define _WIN32_WCE $CEVERSION +_ACEOF + + cat >>confdefs.h <<_ACEOF +#define UNDER_CE $CEVERSION +_ACEOF + + CFLAGS_DEBUG="-nologo -Zi -Od" + CFLAGS_OPTIMIZE="-nologo -O2" + lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` + lflags="-nodefaultlib -MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" + LINKBIN="\"${CEBINROOT}/link.exe\"" + + if test "${CEVERSION}" -lt 400 ; then + LIBS="coredll.lib corelibc.lib winsock.lib" + else + LIBS="coredll.lib corelibc.lib ws2.lib" + fi + # celib currently stuck at wce300 status + #LIBS="$LIBS \${CELIB_DIR}/wince-${ARCH}-pocket-${OSVERSION}-release/celib.lib" + LIBS="$LIBS \"\${CELIB_DIR}/wince-${ARCH}-pocket-wce300-release/celib.lib\"" + LIBS_GUI="commctrl.lib commdlg.lib" + else + LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib" + fi + + SHLIB_LD="${LINKBIN} -dll -incremental:no ${lflags}" + SHLIB_LD_LIBS='${LIBS}' + # link -lib only works when -lib is the first arg + STLIB_LD="${LINKBIN} -lib ${lflags}" + RC_OUT=-fo + RC_TYPE=-r + RC_INCLUDE=-i + RC_DEFINE=-d + RES=res + MAKE_LIB="\${STLIB_LD} -out:\$@" + MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\$@" + POST_MAKE_LIB= + MAKE_EXE="\${CC} -Fe\$@" + LIBPREFIX="" + + CFLAGS_DEBUG="${CFLAGS_DEBUG} -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE" + CFLAGS_OPTIMIZE="${CFLAGS_OPTIMIZE} -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE" + + EXTRA_CFLAGS="" + CFLAGS_WARNING="-W3" + LDFLAGS_DEBUG="-debug" + LDFLAGS_OPTIMIZE="-release" + + # Specify the CC output file names based on the target name + CC_OBJNAME="-Fo\$@" + CC_EXENAME="-Fe\"\$(shell \$(CYGPATH) '\$@')\"" + + # Specify linker flags depending on the type of app being + # built -- Console vs. Window. + if test "$doWince" != "no" -a "${TARGETCPU}" != "X86"; then + LDFLAGS_CONSOLE="-link ${lflags}" + LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} + else + LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" + LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" + fi + fi + + if test "$do64bit" != "no" ; then + cat >>confdefs.h <<\_ACEOF +#define TCL_CFG_DO64BIT 1 +_ACEOF + + fi + + if test "${GCC}" = "yes" ; then + echo "$as_me:$LINENO: checking for SEH support in compiler" >&5 +echo $ECHO_N "checking for SEH support in compiler... $ECHO_C" >&6 +if test "${tcl_cv_seh+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + tcl_cv_seh=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #undef WIN32_LEAN_AND_MEAN + + int main(int argc, char** argv) { + int a, b = 0; + __try { + a = 666 / b; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return 0; + } + return 1; + } + +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_seh=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +tcl_cv_seh=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + +fi +echo "$as_me:$LINENO: result: $tcl_cv_seh" >&5 +echo "${ECHO_T}$tcl_cv_seh" >&6 + if test "$tcl_cv_seh" = "no" ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_NO_SEH 1 +_ACEOF + + fi + + # + # Check to see if the excpt.h include file provided contains the + # definition for EXCEPTION_DISPOSITION; if not, which is the case + # with Cygwin's version as of 2002-04-10, define it to be int, + # sufficient for getting the current code to work. + # + echo "$as_me:$LINENO: checking for EXCEPTION_DISPOSITION support in include files" >&5 +echo $ECHO_N "checking for EXCEPTION_DISPOSITION support in include files... $ECHO_C" >&6 +if test "${tcl_cv_eh_disposition+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN + +int +main () +{ + + EXCEPTION_DISPOSITION x; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_eh_disposition=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_cv_eh_disposition=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $tcl_cv_eh_disposition" >&5 +echo "${ECHO_T}$tcl_cv_eh_disposition" >&6 + if test "$tcl_cv_eh_disposition" = "no" ; then + +cat >>confdefs.h <<\_ACEOF +#define EXCEPTION_DISPOSITION int +_ACEOF + + fi + + # Check to see if winnt.h defines CHAR, SHORT, and LONG + # even if VOID has already been #defined. The win32api + # used by mingw and cygwin is known to do this. + + echo "$as_me:$LINENO: checking for winnt.h that ignores VOID define" >&5 +echo $ECHO_N "checking for winnt.h that ignores VOID define... $ECHO_C" >&6 +if test "${tcl_cv_winnt_ignore_void+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + + #define VOID void + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #undef WIN32_LEAN_AND_MEAN + +int +main () +{ + + CHAR c; + SHORT s; + LONG l; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_winnt_ignore_void=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_cv_winnt_ignore_void=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $tcl_cv_winnt_ignore_void" >&5 +echo "${ECHO_T}$tcl_cv_winnt_ignore_void" >&6 + if test "$tcl_cv_winnt_ignore_void" = "yes" ; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WINNT_IGNORE_VOID 1 +_ACEOF + + fi + + # See if the compiler supports casting to a union type. + # This is used to stop gcc from printing a compiler + # warning when initializing a union member. + + echo "$as_me:$LINENO: checking for cast to union support" >&5 +echo $ECHO_N "checking for cast to union support... $ECHO_C" >&6 +if test "${tcl_cv_cast_to_union+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + union foo { int i; double d; }; + union foo f = (union foo) (int) 0; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_cast_to_union=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_cv_cast_to_union=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +fi +echo "$as_me:$LINENO: result: $tcl_cv_cast_to_union" >&5 +echo "${ECHO_T}$tcl_cv_cast_to_union" >&6 + if test "$tcl_cv_cast_to_union" = "yes"; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CAST_TO_UNION 1 +_ACEOF + + fi + fi + + # DL_LIBS is empty, but then we match the Unix version + + + + + + +#-------------------------------------------------------------------- +# man2tcl needs this so that it can use errno.h +#-------------------------------------------------------------------- + +if test "${ac_cv_header_errno_h+set}" = set; then + echo "$as_me:$LINENO: checking for errno.h" >&5 +echo $ECHO_N "checking for errno.h... $ECHO_C" >&6 +if test "${ac_cv_header_errno_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_errno_h" >&5 +echo "${ECHO_T}$ac_cv_header_errno_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking errno.h usability" >&5 +echo $ECHO_N "checking errno.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <errno.h> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking errno.h presence" >&5 +echo $ECHO_N "checking errno.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <errno.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: errno.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: errno.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: errno.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: errno.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: errno.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: errno.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: errno.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: errno.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: errno.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: errno.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: errno.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for errno.h" >&5 +echo $ECHO_N "checking for errno.h... $ECHO_C" >&6 +if test "${ac_cv_header_errno_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_errno_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_errno_h" >&5 +echo "${ECHO_T}$ac_cv_header_errno_h" >&6 + +fi +if test $ac_cv_header_errno_h = yes; then + : +else + MAN2TCLFLAGS="-DNO_ERRNO_H" +fi + + + + +#------------------------------------------- +# Check for _strtoi64 +#------------------------------------------- + +echo "$as_me:$LINENO: checking availability of _strtoi64" >&5 +echo $ECHO_N "checking availability of _strtoi64... $ECHO_C" >&6 +if test "${tcl_cv_strtoi64+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +int +main () +{ +_strtoi64(0,0,0) + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + tcl_cv_strtoi64=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +tcl_cv_strtoi64=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $tcl_cv_strtoi64" >&5 +echo "${ECHO_T}$tcl_cv_strtoi64" >&6 +if test $tcl_cv_strtoi64 = no; then + +cat >>confdefs.h <<\_ACEOF +#define NO_STRTOI64 1 +_ACEOF + +fi + +#-------------------------------------------------------------------- +# Windows XP theme engine header for Ttk +#-------------------------------------------------------------------- + +echo "$as_me:$LINENO: checking for uxtheme.h" >&5 +echo $ECHO_N "checking for uxtheme.h... $ECHO_C" >&6 +if test "${ac_cv_header_uxtheme_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <windows.h> + +#include <uxtheme.h> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_uxtheme_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_uxtheme_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_uxtheme_h" >&5 +echo "${ECHO_T}$ac_cv_header_uxtheme_h" >&6 +if test $ac_cv_header_uxtheme_h = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_UXTHEME_H 1 +_ACEOF + +else + { echo "$as_me:$LINENO: xpnative theme will be unavailable" >&5 +echo "$as_me: xpnative theme will be unavailable" >&6;} +fi + + +echo "$as_me:$LINENO: checking for vssym32.h" >&5 +echo $ECHO_N "checking for vssym32.h... $ECHO_C" >&6 +if test "${ac_cv_header_vssym32_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <windows.h> +#include <uxtheme.h> + +#include <vssym32.h> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_vssym32_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_vssym32_h=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_vssym32_h" >&5 +echo "${ECHO_T}$ac_cv_header_vssym32_h" >&6 +if test $ac_cv_header_vssym32_h = yes; then + cat >>confdefs.h <<\_ACEOF +#define HAVE_VSSYM32_H 1 +_ACEOF + +fi + + + +#-------------------------------------------------------------------- +# Set the default compiler switches based on the --enable-symbols +# option. This macro depends on C flags, and should be called +# after SC_CONFIG_CFLAGS macro is called. +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking for build with symbols" >&5 +echo $ECHO_N "checking for build with symbols... $ECHO_C" >&6 + # Check whether --enable-symbols or --disable-symbols was given. +if test "${enable_symbols+set}" = set; then + enableval="$enable_symbols" + tcl_ok=$enableval +else + tcl_ok=no +fi; +# FIXME: Currently, LDFLAGS_DEFAULT is not used, it should work like CFLAGS_DEFAULT. + if test "$tcl_ok" = "no"; then + CFLAGS_DEFAULT='$(CFLAGS_OPTIMIZE)' + LDFLAGS_DEFAULT='$(LDFLAGS_OPTIMIZE)' + DBGX="" + +cat >>confdefs.h <<\_ACEOF +#define NDEBUG 1 +_ACEOF + + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + + cat >>confdefs.h <<\_ACEOF +#define TCL_CFG_OPTIMIZED 1 +_ACEOF + + else + CFLAGS_DEFAULT='$(CFLAGS_DEBUG)' + LDFLAGS_DEFAULT='$(LDFLAGS_DEBUG)' + DBGX=g + if test "$tcl_ok" = "yes"; then + echo "$as_me:$LINENO: result: yes (standard debugging)" >&5 +echo "${ECHO_T}yes (standard debugging)" >&6 + fi + fi + + + + if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then + +cat >>confdefs.h <<\_ACEOF +#define TCL_MEM_DEBUG 1 +_ACEOF + + fi + + if test "$tcl_ok" = "compile" -o "$tcl_ok" = "all"; then + +cat >>confdefs.h <<\_ACEOF +#define TCL_COMPILE_DEBUG 1 +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define TCL_COMPILE_STATS 1 +_ACEOF + + fi + + if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then + if test "$tcl_ok" = "all"; then + echo "$as_me:$LINENO: result: enabled symbols mem compile debugging" >&5 +echo "${ECHO_T}enabled symbols mem compile debugging" >&6 + else + echo "$as_me:$LINENO: result: enabled $tcl_ok debugging" >&5 +echo "${ECHO_T}enabled $tcl_ok debugging" >&6 + fi + fi + + +TK_DBGX=${DBGX} + +#-------------------------------------------------------------------- +# Embed the manifest if we can determine how +#-------------------------------------------------------------------- + + + echo "$as_me:$LINENO: checking whether to embed manifest" >&5 +echo $ECHO_N "checking whether to embed manifest... $ECHO_C" >&6 + # Check whether --enable-embedded-manifest or --disable-embedded-manifest was given. +if test "${enable_embedded_manifest+set}" = set; then + enableval="$enable_embedded_manifest" + embed_ok=$enableval +else + embed_ok=yes +fi; + + VC_MANIFEST_EMBED_DLL= + VC_MANIFEST_EMBED_EXE= + result=no + if test "$embed_ok" = "yes" -a "${SHARED_BUILD}" = "1" \ + -a "$GCC" != "yes" ; then + # Add the magic to embed the manifest into the dll/exe + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#if defined(_MSC_VER) && _MSC_VER >= 1400 +print("manifest needed") +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "manifest needed" >/dev/null 2>&1; then + + # Could do a CHECK_PROG for mt, but should always be with MSVC8+ + # Could add 'if test -f' check, but manifest should be created + # in this compiler case + # Add in a manifest argument that may be specified + # XXX Needs improvement so that the test for existence accounts + # XXX for a provided (known) manifest + VC_MANIFEST_EMBED_DLL="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest wish.exe.manifest -outputresource:\$@\;2 ; fi" + VC_MANIFEST_EMBED_EXE="if test -f \$@.manifest ; then mt.exe -nologo -manifest \$@.manifest wish.exe.manifest -outputresource:\$@\;1 ; fi" + result=yes + if test "xwish.exe.manifest" != x ; then + result="yes (wish.exe.manifest)" + fi + +fi +rm -f conftest* + + fi + echo "$as_me:$LINENO: result: $result" >&5 +echo "${ECHO_T}$result" >&6 + + + + + + echo "$as_me:$LINENO: checking for tclsh in Tcl build directory" >&5 +echo $ECHO_N "checking for tclsh in Tcl build directory... $ECHO_C" >&6 + BUILD_TCLSH=${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT} + echo "$as_me:$LINENO: result: $BUILD_TCLSH" >&5 +echo "${ECHO_T}$BUILD_TCLSH" >&6 + + + + echo "$as_me:$LINENO: checking for tclsh" >&5 +echo $ECHO_N "checking for tclsh... $ECHO_C" >&6 + + if test "${ac_cv_path_tclsh+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + + search_path=`echo ${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/tclsh[8-9]*.exe 2> /dev/null` \ + `ls -r $dir/tclsh* 2> /dev/null` ; do + if test x"$ac_cv_path_tclsh" = x ; then + if test -f "$j" ; then + ac_cv_path_tclsh=$j + break + fi + fi + done + done + +fi + + + if test -f "$ac_cv_path_tclsh" ; then + TCLSH_PROG="$ac_cv_path_tclsh" + echo "$as_me:$LINENO: result: $TCLSH_PROG" >&5 +echo "${ECHO_T}$TCLSH_PROG" >&6 + else + # It is not an error if an installed version of Tcl can't be located. + TCLSH_PROG="" + echo "$as_me:$LINENO: result: No tclsh found on PATH" >&5 +echo "${ECHO_T}No tclsh found on PATH" >&6 + fi + + + +#------------------------------------------------------------------------ +# tkConfig.sh refers to this by a different name +#------------------------------------------------------------------------ + +TK_SHARED_BUILD=${SHARED_BUILD} + +#-------------------------------------------------------------------- +# Perform final evaluations of variables with possible substitutions. +#-------------------------------------------------------------------- + +TK_SHARED_LIB_SUFFIX="\${NODOT_VERSION}${DLLSUFFIX}" +TK_UNSHARED_LIB_SUFFIX="\${NODOT_VERSION}${LIBSUFFIX}" +TK_EXPORT_FILE_SUFFIX="\${NODOT_VERSION}${LIBSUFFIX}" + +eval "TK_SRC_DIR=\"`cd $srcdir/..; pwd`\"" + +eval "TK_DLL_FILE=tk$VER${DLLSUFFIX}" +eval "TK_LIB_FILE=${LIBPREFIX}tk$VER${LIBSUFFIX}" + +eval "TK_STUB_LIB_FILE=${LIBPREFIX}tkstub${VER}${LIBSUFFIX}" +# FIXME: All of this var junk needs to be done in tcl.m4 !!!! +# I left out the other vars that also need to get defined here. +# we also need to double check about spaces in path names +eval "TK_LIB_FLAG=\"-ltk${VER}${LIBFLAGSUFFIX}\"" +TK_LIB_SPEC="-L${libdir} ${TK_LIB_FLAG}" +TK_BUILD_LIB_SPEC="-L`pwd` ${TK_LIB_FLAG}" + +eval "TK_STUB_LIB_FLAG=\"-ltkstub${VER}${LIBFLAGSUFFIX}\"" +TK_BUILD_STUB_LIB_SPEC="-L`pwd` ${TK_STUB_LIB_FLAG}" + +TK_STUB_LIB_SPEC="-L${libdir} ${TK_STUB_LIB_FLAG}" +TK_STUB_LIB_PATH="${libdir}/${TK_STUB_LIB_FILE}" +TK_BUILD_STUB_LIB_PATH="`pwd`/${TK_STUB_LIB_FILE}" + +eval "DLLSUFFIX=${DLLSUFFIX}" +eval "LIBPREFIX=${LIBPREFIX}" +eval "LIBSUFFIX=${LIBSUFFIX}" +eval "EXESUFFIX=${EXESUFFIX}" + +CFG_TK_SHARED_LIB_SUFFIX=${TK_SHARED_LIB_SUFFIX} +CFG_TK_UNSHARED_LIB_SUFFIX=${TK_UNSHARED_LIB_SUFFIX} +CFG_TK_EXPORT_FILE_SUFFIX=${TK_EXPORT_FILE_SUFFIX} + +#-------------------------------------------------------------------- +# Adjust the defines for how the resources are built depending +# on symbols and static vs. shared. +#-------------------------------------------------------------------- + +if test ${SHARED_BUILD} = 0 -o "$TCL_NEEDS_EXP_FILE" = 0; then + if test "${DBGX}" = "d"; then + RC_DEFINES="${RC_DEFINE} STATIC_BUILD ${RC_DEFINE} DEBUG" + else + RC_DEFINES="${RC_DEFINE} STATIC_BUILD" + fi + TK_RES="" +else + if test "${DBGX}" = "d"; then + RC_DEFINES="${RC_DEFINE} DEBUG" + else + RC_DEFINES="" + fi + TK_RES='tk.$(RES)' +fi + +# The wish.exe.manifest requires these +# TK_WIN_VERSION is the 4 dotted pair Windows version format which needs +# the release level, and must account for interim release versioning +case "$TK_PATCH_LEVEL" in + *a*) TK_RELEASE_LEVEL=0 ;; + *b*) TK_RELEASE_LEVEL=1 ;; + *) TK_RELEASE_LEVEL=2 ;; +esac +TK_WIN_VERSION="$TK_VERSION.$TK_RELEASE_LEVEL.`echo $TK_PATCH_LEVEL | tr -d ab.`" + +# X86|AMD64|IA64 for manifest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# win/tcl.m4 doesn't set (LDFLAGS) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# undefined at this point for win + + + + + + + + + + + + ac_config_files="$ac_config_files Makefile tkConfig.sh wish.exe.manifest" +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +{ + (set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to <bug-autoconf@gnu.org>." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "tkConfig.sh" ) CONFIG_FILES="$CONFIG_FILES tkConfig.sh" ;; + "wish.exe.manifest" ) CONFIG_FILES="$CONFIG_FILES wish.exe.manifest" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@AR@,$AR,;t t +s,@ac_ct_AR@,$ac_ct_AR,;t t +s,@RANLIB@,$RANLIB,;t t +s,@ac_ct_RANLIB@,$ac_ct_RANLIB,;t t +s,@RC@,$RC,;t t +s,@ac_ct_RC@,$ac_ct_RC,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@TCL_THREADS@,$TCL_THREADS,;t t +s,@TCL_VERSION@,$TCL_VERSION,;t t +s,@TCL_BIN_DIR@,$TCL_BIN_DIR,;t t +s,@TCL_SRC_DIR@,$TCL_SRC_DIR,;t t +s,@TCL_LIB_FILE@,$TCL_LIB_FILE,;t t +s,@TCL_LIB_FLAG@,$TCL_LIB_FLAG,;t t +s,@TCL_LIB_SPEC@,$TCL_LIB_SPEC,;t t +s,@TCL_STUB_LIB_FILE@,$TCL_STUB_LIB_FILE,;t t +s,@TCL_STUB_LIB_FLAG@,$TCL_STUB_LIB_FLAG,;t t +s,@TCL_STUB_LIB_SPEC@,$TCL_STUB_LIB_SPEC,;t t +s,@TCL_DEFS@,$TCL_DEFS,;t t +s,@CYGPATH@,$CYGPATH,;t t +s,@CELIB_DIR@,$CELIB_DIR,;t t +s,@DL_LIBS@,$DL_LIBS,;t t +s,@CFLAGS_DEBUG@,$CFLAGS_DEBUG,;t t +s,@CFLAGS_OPTIMIZE@,$CFLAGS_OPTIMIZE,;t t +s,@CFLAGS_WARNING@,$CFLAGS_WARNING,;t t +s,@MAN2TCLFLAGS@,$MAN2TCLFLAGS,;t t +s,@CFLAGS_DEFAULT@,$CFLAGS_DEFAULT,;t t +s,@LDFLAGS_DEFAULT@,$LDFLAGS_DEFAULT,;t t +s,@VC_MANIFEST_EMBED_DLL@,$VC_MANIFEST_EMBED_DLL,;t t +s,@VC_MANIFEST_EMBED_EXE@,$VC_MANIFEST_EMBED_EXE,;t t +s,@BUILD_TCLSH@,$BUILD_TCLSH,;t t +s,@TCLSH_PROG@,$TCLSH_PROG,;t t +s,@TK_WIN_VERSION@,$TK_WIN_VERSION,;t t +s,@MACHINE@,$MACHINE,;t t +s,@TK_VERSION@,$TK_VERSION,;t t +s,@TK_MAJOR_VERSION@,$TK_MAJOR_VERSION,;t t +s,@TK_MINOR_VERSION@,$TK_MINOR_VERSION,;t t +s,@TK_PATCH_LEVEL@,$TK_PATCH_LEVEL,;t t +s,@TK_DBGX@,$TK_DBGX,;t t +s,@TK_LIB_FILE@,$TK_LIB_FILE,;t t +s,@TK_DLL_FILE@,$TK_DLL_FILE,;t t +s,@TK_STUB_LIB_FILE@,$TK_STUB_LIB_FILE,;t t +s,@TK_STUB_LIB_FLAG@,$TK_STUB_LIB_FLAG,;t t +s,@TK_BUILD_STUB_LIB_SPEC@,$TK_BUILD_STUB_LIB_SPEC,;t t +s,@TK_SRC_DIR@,$TK_SRC_DIR,;t t +s,@TK_BIN_DIR@,$TK_BIN_DIR,;t t +s,@TCL_MAJOR_VERSION@,$TCL_MAJOR_VERSION,;t t +s,@TCL_MINOR_VERSION@,$TCL_MINOR_VERSION,;t t +s,@TCL_PATCH_LEVEL@,$TCL_PATCH_LEVEL,;t t +s,@TCL_DBGX@,$TCL_DBGX,;t t +s,@CFG_TK_SHARED_LIB_SUFFIX@,$CFG_TK_SHARED_LIB_SUFFIX,;t t +s,@CFG_TK_UNSHARED_LIB_SUFFIX@,$CFG_TK_UNSHARED_LIB_SUFFIX,;t t +s,@CFG_TK_EXPORT_FILE_SUFFIX@,$CFG_TK_EXPORT_FILE_SUFFIX,;t t +s,@EXTRA_CFLAGS@,$EXTRA_CFLAGS,;t t +s,@DEPARG@,$DEPARG,;t t +s,@CC_OBJNAME@,$CC_OBJNAME,;t t +s,@CC_EXENAME@,$CC_EXENAME,;t t +s,@LDFLAGS_DEBUG@,$LDFLAGS_DEBUG,;t t +s,@LDFLAGS_OPTIMIZE@,$LDFLAGS_OPTIMIZE,;t t +s,@LDFLAGS_CONSOLE@,$LDFLAGS_CONSOLE,;t t +s,@LDFLAGS_WINDOW@,$LDFLAGS_WINDOW,;t t +s,@TK_RES@,$TK_RES,;t t +s,@STLIB_LD@,$STLIB_LD,;t t +s,@SHLIB_LD@,$SHLIB_LD,;t t +s,@SHLIB_LD_LIBS@,$SHLIB_LD_LIBS,;t t +s,@SHLIB_CFLAGS@,$SHLIB_CFLAGS,;t t +s,@SHLIB_SUFFIX@,$SHLIB_SUFFIX,;t t +s,@TK_SHARED_BUILD@,$TK_SHARED_BUILD,;t t +s,@LIBS_GUI@,$LIBS_GUI,;t t +s,@DLLSUFFIX@,$DLLSUFFIX,;t t +s,@LIBPREFIX@,$LIBPREFIX,;t t +s,@LIBSUFFIX@,$LIBSUFFIX,;t t +s,@EXESUFFIX@,$EXESUFFIX,;t t +s,@LIBRARIES@,$LIBRARIES,;t t +s,@MAKE_LIB@,$MAKE_LIB,;t t +s,@MAKE_STUB_LIB@,$MAKE_STUB_LIB,;t t +s,@POST_MAKE_LIB@,$POST_MAKE_LIB,;t t +s,@MAKE_DLL@,$MAKE_DLL,;t t +s,@MAKE_EXE@,$MAKE_EXE,;t t +s,@TK_LIB_FLAG@,$TK_LIB_FLAG,;t t +s,@TK_LIB_SPEC@,$TK_LIB_SPEC,;t t +s,@TK_BUILD_LIB_SPEC@,$TK_BUILD_LIB_SPEC,;t t +s,@TK_STUB_LIB_SPEC@,$TK_STUB_LIB_SPEC,;t t +s,@TK_STUB_LIB_PATH@,$TK_STUB_LIB_PATH,;t t +s,@TK_BUILD_STUB_LIB_PATH@,$TK_BUILD_STUB_LIB_PATH,;t t +s,@TK_CC_SEARCH_FLAGS@,$TK_CC_SEARCH_FLAGS,;t t +s,@TK_LD_SEARCH_FLAGS@,$TK_LD_SEARCH_FLAGS,;t t +s,@RC_OUT@,$RC_OUT,;t t +s,@RC_TYPE@,$RC_TYPE,;t t +s,@RC_INCLUDE@,$RC_INCLUDE,;t t +s,@RC_DEFINE@,$RC_DEFINE,;t t +s,@RC_DEFINES@,$RC_DEFINES,;t t +s,@RES@,$RES,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # Split the substitutions into bite-sized pieces for seds with + # small command number limits, like on Digital OSF/1 and HP-UX. + ac_max_sed_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # Line after last line for current file. + ac_more_lines=: + ac_sed_cmds= + while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + diff --git a/tk8.6/win/configure.in b/tk8.6/win/configure.in new file mode 100644 index 0000000..5ec7c35 --- /dev/null +++ b/tk8.6/win/configure.in @@ -0,0 +1,314 @@ +#! /bin/bash -norc +# This file is an input file used by the GNU "autoconf" program to +# generate the file "configure", which is run during Tk installation +# to configure the system for the local environment. + +AC_INIT(../generic/tk.h) +AC_PREREQ(2.59) + +# The following define is needed when building with Cygwin since newer +# versions of autoconf incorrectly set SHELL to /bin/bash instead of +# /bin/sh. The bash shell seems to suffer from some strange failures. +SHELL=/bin/sh + +TK_VERSION=8.6 +TK_MAJOR_VERSION=8 +TK_MINOR_VERSION=6 +TK_PATCH_LEVEL=".8" +VER=$TK_MAJOR_VERSION$TK_MINOR_VERSION + +#------------------------------------------------------------------------ +# Handle the --prefix=... option +#------------------------------------------------------------------------ + +if test "${prefix}" = "NONE"; then + prefix=/usr/local +fi +if test "${exec_prefix}" = "NONE"; then + exec_prefix=$prefix +fi +# libdir must be a fully qualified path (not ${exec_prefix}/lib) +eval libdir="$libdir" + +#------------------------------------------------------------------------ +# Standard compiler checks +#------------------------------------------------------------------------ + +# If the user did not set CFLAGS, set it now to keep +# the AC_PROG_CC macro from adding "-g -O2". +if test "${CFLAGS+set}" != "set" ; then + CFLAGS="" +fi + +AC_PROG_CC +AC_C_INLINE +AC_HEADER_STDC + +AC_CHECK_TOOL(AR, ar) +AC_CHECK_TOOL(RANLIB, ranlib) +AC_CHECK_TOOL(RC, windres) + +#-------------------------------------------------------------------- +# Checks to see if the make program sets the $MAKE variable. +#-------------------------------------------------------------------- + +AC_PROG_MAKE_SET + +#-------------------------------------------------------------------- +# Determines the correct binary file extension (.o, .obj, .exe etc.) +#-------------------------------------------------------------------- + +AC_OBJEXT +AC_EXEEXT + +#-------------------------------------------------------------------- +# Check whether --enable-threads or --disable-threads was given. +#-------------------------------------------------------------------- + +SC_ENABLE_THREADS + +#-------------------------------------------------------------------- +# The statements below define a collection of symbols related to +# building libtk as a shared library instead of a static library. +#-------------------------------------------------------------------- + +SC_ENABLE_SHARED + +#-------------------------------------------------------------------- +# Locate and source the tclConfig.sh file. +#-------------------------------------------------------------------- + +SC_PATH_TCLCONFIG($TK_PATCH_LEVEL) +SC_LOAD_TCLCONFIG + +if test "${TCL_MAJOR_VERSION}" != "${TK_MAJOR_VERSION}"; then + AC_MSG_ERROR([${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}.]) +fi +if test "${TCL_MINOR_VERSION}" -lt "${TK_MINOR_VERSION}"; then + AC_MSG_ERROR([${TCL_BIN_DIR}/tclConfig.sh is for Tcl ${TCL_VERSION}. +Tk ${TK_VERSION}${TK_PATCH_LEVEL} needs Tcl ${TK_VERSION}. +Use --with-tcl= option to indicate location of tclConfig.sh file for Tcl ${TK_VERSION}.]) +fi + +#-------------------------------------------------------------------- +# The statements below define a collection of compile flags. This +# macro depends on the value of SHARED_BUILD, and should be called +# after SC_ENABLE_SHARED checks the configure switches. +#-------------------------------------------------------------------- + +SC_CONFIG_CFLAGS + +#-------------------------------------------------------------------- +# man2tcl needs this so that it can use errno.h +#-------------------------------------------------------------------- + +AC_CHECK_HEADER(errno.h, , MAN2TCLFLAGS="-DNO_ERRNO_H") +AC_SUBST(MAN2TCLFLAGS) + +#------------------------------------------- +# Check for _strtoi64 +#------------------------------------------- + +AC_CACHE_CHECK([availability of _strtoi64], tcl_cv_strtoi64, [ + AC_TRY_LINK([#include <stdlib.h>], + [_strtoi64(0,0,0)], + tcl_cv_strtoi64=yes, tcl_cv_strtoi64=no)]) +if test $tcl_cv_strtoi64 = no; then + AC_DEFINE(NO_STRTOI64, 1, [Is _strtoi64 function available?]) +fi + +#-------------------------------------------------------------------- +# Windows XP theme engine header for Ttk +#-------------------------------------------------------------------- + +AC_CHECK_HEADER([uxtheme.h], [AC_DEFINE(HAVE_UXTHEME_H)], + [AC_MSG_NOTICE([xpnative theme will be unavailable])], + [#include <windows.h>]) +AC_CHECK_HEADER([vssym32.h], [AC_DEFINE(HAVE_VSSYM32_H)], [], + [#include <windows.h> +#include <uxtheme.h>]) + +#-------------------------------------------------------------------- +# Set the default compiler switches based on the --enable-symbols +# option. This macro depends on C flags, and should be called +# after SC_CONFIG_CFLAGS macro is called. +#-------------------------------------------------------------------- + +SC_ENABLE_SYMBOLS + +TK_DBGX=${DBGX} + +#-------------------------------------------------------------------- +# Embed the manifest if we can determine how +#-------------------------------------------------------------------- + +SC_EMBED_MANIFEST(wish.exe.manifest) + +SC_BUILD_TCLSH +SC_PROG_TCLSH + +#------------------------------------------------------------------------ +# tkConfig.sh refers to this by a different name +#------------------------------------------------------------------------ + +TK_SHARED_BUILD=${SHARED_BUILD} + +#-------------------------------------------------------------------- +# Perform final evaluations of variables with possible substitutions. +#-------------------------------------------------------------------- + +TK_SHARED_LIB_SUFFIX="\${NODOT_VERSION}${DLLSUFFIX}" +TK_UNSHARED_LIB_SUFFIX="\${NODOT_VERSION}${LIBSUFFIX}" +TK_EXPORT_FILE_SUFFIX="\${NODOT_VERSION}${LIBSUFFIX}" + +eval "TK_SRC_DIR=\"`cd $srcdir/..; pwd`\"" + +eval "TK_DLL_FILE=tk$VER${DLLSUFFIX}" +eval "TK_LIB_FILE=${LIBPREFIX}tk$VER${LIBSUFFIX}" + +eval "TK_STUB_LIB_FILE=${LIBPREFIX}tkstub${VER}${LIBSUFFIX}" +# FIXME: All of this var junk needs to be done in tcl.m4 !!!! +# I left out the other vars that also need to get defined here. +# we also need to double check about spaces in path names +eval "TK_LIB_FLAG=\"-ltk${VER}${LIBFLAGSUFFIX}\"" +TK_LIB_SPEC="-L${libdir} ${TK_LIB_FLAG}" +TK_BUILD_LIB_SPEC="-L`pwd` ${TK_LIB_FLAG}" + +eval "TK_STUB_LIB_FLAG=\"-ltkstub${VER}${LIBFLAGSUFFIX}\"" +TK_BUILD_STUB_LIB_SPEC="-L`pwd` ${TK_STUB_LIB_FLAG}" + +TK_STUB_LIB_SPEC="-L${libdir} ${TK_STUB_LIB_FLAG}" +TK_STUB_LIB_PATH="${libdir}/${TK_STUB_LIB_FILE}" +TK_BUILD_STUB_LIB_PATH="`pwd`/${TK_STUB_LIB_FILE}" + +eval "DLLSUFFIX=${DLLSUFFIX}" +eval "LIBPREFIX=${LIBPREFIX}" +eval "LIBSUFFIX=${LIBSUFFIX}" +eval "EXESUFFIX=${EXESUFFIX}" + +CFG_TK_SHARED_LIB_SUFFIX=${TK_SHARED_LIB_SUFFIX} +CFG_TK_UNSHARED_LIB_SUFFIX=${TK_UNSHARED_LIB_SUFFIX} +CFG_TK_EXPORT_FILE_SUFFIX=${TK_EXPORT_FILE_SUFFIX} + +#-------------------------------------------------------------------- +# Adjust the defines for how the resources are built depending +# on symbols and static vs. shared. +#-------------------------------------------------------------------- + +if test ${SHARED_BUILD} = 0 -o "$TCL_NEEDS_EXP_FILE" = 0; then + if test "${DBGX}" = "d"; then + RC_DEFINES="${RC_DEFINE} STATIC_BUILD ${RC_DEFINE} DEBUG" + else + RC_DEFINES="${RC_DEFINE} STATIC_BUILD" + fi + TK_RES="" +else + if test "${DBGX}" = "d"; then + RC_DEFINES="${RC_DEFINE} DEBUG" + else + RC_DEFINES="" + fi + TK_RES='tk.$(RES)' +fi + +# The wish.exe.manifest requires these +# TK_WIN_VERSION is the 4 dotted pair Windows version format which needs +# the release level, and must account for interim release versioning +case "$TK_PATCH_LEVEL" in + *a*) TK_RELEASE_LEVEL=0 ;; + *b*) TK_RELEASE_LEVEL=1 ;; + *) TK_RELEASE_LEVEL=2 ;; +esac +TK_WIN_VERSION="$TK_VERSION.$TK_RELEASE_LEVEL.`echo $TK_PATCH_LEVEL | tr -d ab.`" +AC_SUBST(TK_WIN_VERSION) +# X86|AMD64|IA64 for manifest +AC_SUBST(MACHINE) + +AC_SUBST(TK_VERSION) +AC_SUBST(TK_MAJOR_VERSION) +AC_SUBST(TK_MINOR_VERSION) +AC_SUBST(TK_PATCH_LEVEL) +AC_SUBST(TK_DBGX) +AC_SUBST(TK_LIB_FILE) +AC_SUBST(TK_DLL_FILE) +AC_SUBST(TK_STUB_LIB_FILE) +AC_SUBST(TK_STUB_LIB_FLAG) +AC_SUBST(TK_BUILD_STUB_LIB_SPEC) +AC_SUBST(TK_SRC_DIR) +AC_SUBST(TK_BIN_DIR) + +AC_SUBST(TCL_VERSION) +AC_SUBST(TCL_MAJOR_VERSION) +AC_SUBST(TCL_MINOR_VERSION) +AC_SUBST(TCL_PATCH_LEVEL) + +AC_SUBST(TCL_SRC_DIR) +AC_SUBST(TCL_BIN_DIR) +AC_SUBST(TCL_DBGX) +AC_SUBST(CFG_TK_SHARED_LIB_SUFFIX) +AC_SUBST(CFG_TK_UNSHARED_LIB_SUFFIX) +AC_SUBST(CFG_TK_EXPORT_FILE_SUFFIX) + +AC_SUBST(CFLAGS_DEFAULT) +AC_SUBST(EXTRA_CFLAGS) +AC_SUBST(CYGPATH) +AC_SUBST(DEPARG) +AC_SUBST(CC_OBJNAME) +AC_SUBST(CC_EXENAME) + +# win/tcl.m4 doesn't set (LDFLAGS) +AC_SUBST(LDFLAGS_DEFAULT) +AC_SUBST(LDFLAGS_DEBUG) +AC_SUBST(LDFLAGS_OPTIMIZE) +AC_SUBST(LDFLAGS_CONSOLE) +AC_SUBST(LDFLAGS_WINDOW) +AC_SUBST(AR) +AC_SUBST(RANLIB) +AC_SUBST(TK_RES) + +AC_SUBST(STLIB_LD) +AC_SUBST(SHLIB_LD) +AC_SUBST(SHLIB_LD_LIBS) +AC_SUBST(SHLIB_CFLAGS) +AC_SUBST(SHLIB_SUFFIX) +AC_SUBST(TK_SHARED_BUILD) + +AC_SUBST(LIBS) +AC_SUBST(LIBS_GUI) +AC_SUBST(DLLSUFFIX) +AC_SUBST(LIBPREFIX) +AC_SUBST(LIBSUFFIX) +AC_SUBST(EXESUFFIX) +AC_SUBST(LIBRARIES) +AC_SUBST(MAKE_LIB) +AC_SUBST(MAKE_STUB_LIB) +AC_SUBST(POST_MAKE_LIB) +AC_SUBST(MAKE_DLL) +AC_SUBST(MAKE_EXE) + +AC_SUBST(TK_LIB_FLAG) +AC_SUBST(TK_LIB_SPEC) +AC_SUBST(TK_BUILD_LIB_SPEC) +AC_SUBST(TK_STUB_LIB_SPEC) +AC_SUBST(TK_STUB_LIB_PATH) +AC_SUBST(TK_BUILD_STUB_LIB_PATH) + +# undefined at this point for win +AC_SUBST(TK_CC_SEARCH_FLAGS) +AC_SUBST(TK_LD_SEARCH_FLAGS) + +AC_SUBST(RC) +AC_SUBST(RC_OUT) +AC_SUBST(RC_TYPE) +AC_SUBST(RC_INCLUDE) +AC_SUBST(RC_DEFINE) +AC_SUBST(RC_DEFINES) +AC_SUBST(RES) + +AC_OUTPUT(Makefile tkConfig.sh wish.exe.manifest) + +dnl Local Variables: +dnl mode: autoconf; +dnl End: diff --git a/tk8.6/win/license.terms b/tk8.6/win/license.terms new file mode 100644 index 0000000..0126435 --- /dev/null +++ b/tk8.6/win/license.terms @@ -0,0 +1,40 @@ +This software is copyrighted by the Regents of the University of +California, Sun Microsystems, Inc., Scriptics Corporation, ActiveState +Corporation, Apple Inc. and other parties. The following terms apply to +all files associated with the software unless explicitly disclaimed in +individual files. + +The authors hereby grant permission to use, copy, modify, distribute, +and license this software and its documentation for any purpose, provided +that existing copyright notices are retained in all copies and that this +notice is included verbatim in any distributions. No written agreement, +license, or royalty fee is required for any of the authorized uses. +Modifications to this software may be copyrighted by their authors +and need not follow the licensing terms described here, provided that +the new terms are clearly indicated on the first page of each file where +they apply. + +IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY +FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY +DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE +IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE +NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR +MODIFICATIONS. + +GOVERNMENT USE: If you are acquiring this software on behalf of the +U.S. government, the Government shall have only "Restricted Rights" +in the software and related documentation as defined in the Federal +Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you +are acquiring the software on behalf of the Department of Defense, the +software shall be classified as "Commercial Computer Software" and the +Government shall have only "Restricted Rights" as defined in Clause +252.227-7013 (b) (3) of DFARs. Notwithstanding the foregoing, the +authors grant the U.S. Government and others acting in its behalf +permission to use and distribute the software in accordance with the +terms specified in this license. diff --git a/tk8.6/win/makefile.vc b/tk8.6/win/makefile.vc new file mode 100644 index 0000000..04c9757 --- /dev/null +++ b/tk8.6/win/makefile.vc @@ -0,0 +1,698 @@ +#------------------------------------------------------------- -*- makefile -*-
+# makefile.vc --
+#
+# Microsoft Visual C++ makefile for use with nmake.exe v1.62+ (VC++ 5.0+)
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# Copyright (c) 1995-1996 Sun Microsystems, Inc.
+# Copyright (c) 1998-2000 Ajuba Solutions.
+# Copyright (c) 2001-2005 ActiveState Corporation.
+# Copyright (c) 2001-2004 David Gravereaux.
+# Copyright (c) 2003-2008 Pat Thoyts.
+# Copyright (c) 2017 Ashok P. Nadkarni
+#------------------------------------------------------------------------------
+
+# General usage:
+# nmake [-nologo] -f makefile.vc [TARGET|MACRODEF [TARGET|MACRODEF] [...]]
+#
+# For MACRODEF, see TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md)
+# or examine Sections 6-8 in rules.vc. This makefile has the following
+# values for the OPTS macro in addition to the ones described there.
+# noxp = If you do not have the uxtheme.h header then you
+# cannot include support for XP themeing.
+# square = Include the demo square widget.
+#
+# Possible values for TARGET are:
+# release -- Builds the core, the shell and the dlls. (default)
+# dlls -- Just builds the windows extensions.
+# shell -- Just builds the shell and the core.
+# core -- Only builds the core [tkXX.(dll|lib)].
+# all -- Builds everything.
+# test -- Builds and runs the test suite.
+# tktest -- Just builds the binaries for the test suite.
+# install -- Installs the built binaries and libraries to $(INSTALLDIR)
+# as the root of the install tree.
+# cwish -- Builds a console version of wish.
+# tidy/clean/hose -- varying levels of cleaning.
+# genstubs -- Rebuilds the Stubs table and support files (dev only).
+# depend -- Generates an accurate set of source dependancies for this
+# makefile. Helpful to avoid problems when the sources are
+# refreshed and you rebuild, but can "overbuild" when common
+# headers like tkInt.h just get small changes.
+# htmlhelp -- Builds a Windows .chm help file for Tcl and Tk from the
+# troff manual pages found in $(ROOT)\doc. You need to
+# have installed the HTML Help Compiler package from Microsoft
+# to produce the .chm file.
+#
+# The steps to setup a Visual C++ environment depend on which
+# version of Visual Studio and/or the Windows SDK you are building
+# against and are not described here. The simplest method is generally
+# to start a command shell using one of the short cuts installed by
+# Visual Studio/Windows SDK for the appropriate target architecture.
+#
+# NOTE: For older (Visual C++ 6 or the 2003 SDK), to use the Platform
+# SDK (not expressly needed), run setenv.bat after vcvars32.bat
+# according to the instructions for it. This can also turn on the
+# 64-bit compiler, if your SDK has it.
+#
+# Examples:
+# Assumign Tcl sources lie in ../../tcl
+# c:\tcl_src\win\>nmake -f makefile.vc release
+# If Tcl sources are not in ../../tcl, use the TCLDIR macro to specify dir
+# c:\tcl_src\win\>nmake -f makefile.vc release TCLDIR=c:\src\tcl
+# Run the test suite
+# c:\tcl_src\win\>nmake -f makefile.vc test
+# Install Tk in location specified by INSTALLDIR macro
+# c:\tcl_src\win\>nmake -f makefile.vc install INSTALLDIR=c:\progra~1\tcl
+# Build release with PDF files
+# c:\tcl_src\win\>nmake -f makefile.vc release OPTS=pdbs
+# Build debug version
+# c:\tcl_src\win\>nmake -f makefile.vc release OPTS=symbols
+#
+###############################################################################
+
+# The PROJECT macro is used by rules.vc for generating appropriate
+# macros and rules.
+PROJECT = tk
+
+# Default target to build if no target is specified. If unspecified, the
+# rules.vc file will set up "all" as the target.
+DEFAULT_BUILD_TARGET = release
+
+# We have a custom resource file
+RCFILE = tk.rc
+
+# The rules.vc file does much of the hard work in terms of defining
+# the build configuration, macros, output directories etc.
+!include "rules-ext.vc"
+
+# TCLINSTALL is set to 1 by rules.vc to indicate we are building against
+# an installed Tcl and 0 if building against Tcl source. Tk needs the latter.
+!message TCLINSTALL=$(TCLINSTALL)
+!if $(TCLINSTALL)
+!message *** Warning: Tk requires the source distribution of Tcl to build from,
+!message *** at this time, sorry. Please set the TCLDIR macro to point to the
+!message *** Tcl sources.
+!endif
+
+# Extra makefile options processing for non-standard OPTS values ...
+!if "$(OPTS)" == "" || [nmakehlp -f "$(OPTS)" "none"]
+HAVE_UXTHEME_H = 1
+TTK_SQUARE_WIDGET = 0
+!else
+!if [nmakehlp -f $(OPTS) "noxp"]
+!message *** Exclude support for XP theme
+HAVE_UXTHEME_H = 0
+!else
+HAVE_UXTHEME_H = 1
+!endif
+!if [nmakehlp -f "$(OPTS)" "square"]
+!message *** Include ttk square demo widget
+TTK_SQUARE_WIDGET = 1
+!else
+TTK_SQUARE_WIDGET = 0
+!endif
+!endif
+
+WISHC = "$(OUT_DIR)\$(WISHNAMEPREFIX)c$(VERSION)$(SUFX).exe"
+
+TKTEST = "$(OUT_DIR)\$(PROJECT)test.exe"
+CAT32 = "$(OUT_DIR)\cat32.exe"
+
+WISHOBJS = \
+ $(TMP_DIR)\winMain.obj \
+!if $(TCL_USE_STATIC_PACKAGES)
+ $(TCLDDELIB) \
+ $(TCLREGLIB) \
+!endif
+ $(TMP_DIR)\wish.res
+
+TKTESTOBJS = \
+ $(TMP_DIR)\testMain.obj \
+ $(TMP_DIR)\tkSquare.obj \
+ $(TMP_DIR)\tkTest.obj \
+ $(TMP_DIR)\tkOldTest.obj \
+ $(TMP_DIR)\tkWinTest.obj
+
+XLIBOBJS = \
+ $(TMP_DIR)\xcolors.obj \
+ $(TMP_DIR)\xdraw.obj \
+ $(TMP_DIR)\xgc.obj \
+ $(TMP_DIR)\ximage.obj \
+ $(TMP_DIR)\xutil.obj
+
+TKOBJS = \
+ $(TMP_DIR)\tkConsole.obj \
+ $(TMP_DIR)\tkUnixMenubu.obj \
+ $(TMP_DIR)\tkUnixScale.obj \
+ $(XLIBOBJS) \
+ $(TMP_DIR)\tkWin3d.obj \
+ $(TMP_DIR)\tkWin32Dll.obj \
+ $(TMP_DIR)\tkWinButton.obj \
+ $(TMP_DIR)\tkWinClipboard.obj \
+ $(TMP_DIR)\tkWinColor.obj \
+ $(TMP_DIR)\tkWinConfig.obj \
+ $(TMP_DIR)\tkWinCursor.obj \
+ $(TMP_DIR)\tkWinDialog.obj \
+ $(TMP_DIR)\tkWinDraw.obj \
+ $(TMP_DIR)\tkWinEmbed.obj \
+ $(TMP_DIR)\tkWinFont.obj \
+ $(TMP_DIR)\tkWinImage.obj \
+ $(TMP_DIR)\tkWinInit.obj \
+ $(TMP_DIR)\tkWinKey.obj \
+ $(TMP_DIR)\tkWinMenu.obj \
+ $(TMP_DIR)\tkWinPixmap.obj \
+ $(TMP_DIR)\tkWinPointer.obj \
+ $(TMP_DIR)\tkWinRegion.obj \
+ $(TMP_DIR)\tkWinScrlbr.obj \
+ $(TMP_DIR)\tkWinSend.obj \
+ $(TMP_DIR)\tkWinSendCom.obj \
+ $(TMP_DIR)\tkWinWindow.obj \
+ $(TMP_DIR)\tkWinWm.obj \
+ $(TMP_DIR)\tkWinX.obj \
+ $(TMP_DIR)\stubs.obj \
+ $(TMP_DIR)\tk3d.obj \
+ $(TMP_DIR)\tkArgv.obj \
+ $(TMP_DIR)\tkAtom.obj \
+ $(TMP_DIR)\tkBind.obj \
+ $(TMP_DIR)\tkBitmap.obj \
+ $(TMP_DIR)\tkBusy.obj \
+ $(TMP_DIR)\tkButton.obj \
+ $(TMP_DIR)\tkCanvArc.obj \
+ $(TMP_DIR)\tkCanvBmap.obj \
+ $(TMP_DIR)\tkCanvImg.obj \
+ $(TMP_DIR)\tkCanvLine.obj \
+ $(TMP_DIR)\tkCanvPoly.obj \
+ $(TMP_DIR)\tkCanvPs.obj \
+ $(TMP_DIR)\tkCanvText.obj \
+ $(TMP_DIR)\tkCanvUtil.obj \
+ $(TMP_DIR)\tkCanvWind.obj \
+ $(TMP_DIR)\tkCanvas.obj \
+ $(TMP_DIR)\tkClipboard.obj \
+ $(TMP_DIR)\tkCmds.obj \
+ $(TMP_DIR)\tkColor.obj \
+ $(TMP_DIR)\tkConfig.obj \
+ $(TMP_DIR)\tkCursor.obj \
+ $(TMP_DIR)\tkEntry.obj \
+ $(TMP_DIR)\tkError.obj \
+ $(TMP_DIR)\tkEvent.obj \
+ $(TMP_DIR)\tkFileFilter.obj \
+ $(TMP_DIR)\tkFocus.obj \
+ $(TMP_DIR)\tkFont.obj \
+ $(TMP_DIR)\tkFrame.obj \
+ $(TMP_DIR)\tkGC.obj \
+ $(TMP_DIR)\tkGeometry.obj \
+ $(TMP_DIR)\tkGet.obj \
+ $(TMP_DIR)\tkGrab.obj \
+ $(TMP_DIR)\tkGrid.obj \
+ $(TMP_DIR)\tkImage.obj \
+ $(TMP_DIR)\tkImgBmap.obj \
+ $(TMP_DIR)\tkImgGIF.obj \
+ $(TMP_DIR)\tkImgPNG.obj \
+ $(TMP_DIR)\tkImgPPM.obj \
+ $(TMP_DIR)\tkImgPhoto.obj \
+ $(TMP_DIR)\tkImgPhInstance.obj \
+ $(TMP_DIR)\tkImgUtil.obj \
+ $(TMP_DIR)\tkListbox.obj \
+ $(TMP_DIR)\tkMacWinMenu.obj \
+ $(TMP_DIR)\tkMain.obj \
+ $(TMP_DIR)\tkMain2.obj \
+ $(TMP_DIR)\tkMenu.obj \
+ $(TMP_DIR)\tkMenubutton.obj \
+ $(TMP_DIR)\tkMenuDraw.obj \
+ $(TMP_DIR)\tkMessage.obj \
+ $(TMP_DIR)\tkPanedWindow.obj \
+ $(TMP_DIR)\tkObj.obj \
+ $(TMP_DIR)\tkOldConfig.obj \
+ $(TMP_DIR)\tkOption.obj \
+ $(TMP_DIR)\tkPack.obj \
+ $(TMP_DIR)\tkPlace.obj \
+ $(TMP_DIR)\tkPointer.obj \
+ $(TMP_DIR)\tkRectOval.obj \
+ $(TMP_DIR)\tkScale.obj \
+ $(TMP_DIR)\tkScrollbar.obj \
+ $(TMP_DIR)\tkSelect.obj \
+ $(TMP_DIR)\tkStyle.obj \
+ $(TMP_DIR)\tkText.obj \
+ $(TMP_DIR)\tkTextBTree.obj \
+ $(TMP_DIR)\tkTextDisp.obj \
+ $(TMP_DIR)\tkTextImage.obj \
+ $(TMP_DIR)\tkTextIndex.obj \
+ $(TMP_DIR)\tkTextMark.obj \
+ $(TMP_DIR)\tkTextTag.obj \
+ $(TMP_DIR)\tkTextWind.obj \
+ $(TMP_DIR)\tkTrig.obj \
+ $(TMP_DIR)\tkUndo.obj \
+ $(TMP_DIR)\tkUtil.obj \
+ $(TMP_DIR)\tkVisual.obj \
+ $(TMP_DIR)\tkStubInit.obj \
+ $(TMP_DIR)\tkWindow.obj \
+ $(TTK_OBJS) \
+!if !$(STATIC_BUILD)
+ $(TMP_DIR)\tk.res
+!endif
+
+TTK_OBJS = \
+ $(TMP_DIR)\ttkWinMonitor.obj \
+ $(TMP_DIR)\ttkWinTheme.obj \
+ $(TMP_DIR)\ttkWinXPTheme.obj \
+ $(TMP_DIR)\ttkBlink.obj \
+ $(TMP_DIR)\ttkButton.obj \
+ $(TMP_DIR)\ttkCache.obj \
+ $(TMP_DIR)\ttkClamTheme.obj \
+ $(TMP_DIR)\ttkClassicTheme.obj \
+ $(TMP_DIR)\ttkDefaultTheme.obj \
+ $(TMP_DIR)\ttkElements.obj \
+ $(TMP_DIR)\ttkEntry.obj \
+ $(TMP_DIR)\ttkFrame.obj \
+ $(TMP_DIR)\ttkImage.obj \
+ $(TMP_DIR)\ttkInit.obj \
+ $(TMP_DIR)\ttkLabel.obj \
+ $(TMP_DIR)\ttkLayout.obj \
+ $(TMP_DIR)\ttkManager.obj \
+ $(TMP_DIR)\ttkNotebook.obj \
+ $(TMP_DIR)\ttkPanedwindow.obj \
+ $(TMP_DIR)\ttkProgress.obj \
+ $(TMP_DIR)\ttkScale.obj \
+ $(TMP_DIR)\ttkScrollbar.obj \
+ $(TMP_DIR)\ttkScroll.obj \
+ $(TMP_DIR)\ttkSeparator.obj \
+ $(TMP_DIR)\ttkSquare.obj \
+ $(TMP_DIR)\ttkState.obj \
+ $(TMP_DIR)\ttkTagSet.obj \
+ $(TMP_DIR)\ttkTheme.obj \
+ $(TMP_DIR)\ttkTrace.obj \
+ $(TMP_DIR)\ttkTrack.obj \
+ $(TMP_DIR)\ttkTreeview.obj \
+ $(TMP_DIR)\ttkWidget.obj \
+ $(TMP_DIR)\ttkStubInit.obj
+
+TKSTUBOBJS = \
+ $(TMP_DIR)\tkStubLib.obj \
+ $(TMP_DIR)\ttkStubLib.obj
+
+### The following paths CANNOT have spaces in them as they appear on
+### the left side of implicit rules.
+XLIBDIR = $(ROOT)\xlib
+TTKDIR = $(ROOT)\generic\ttk
+BITMAPDIR = $(ROOT)\bitmaps
+
+# Additional include and C macro definitions for the implicit rules
+# defined in rules.vc
+PRJ_INCLUDES = -I"$(BITMAPDIR)" -I"$(XLIBDIR)"
+
+CONFIG_DEFS =-DSTDC_HEADERS=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_STAT_H=1 \
+ -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_MEMORY_H=1 \
+ -DHAVE_STRINGS_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 \
+ -DSUPPORT_CONFIG_EMBEDDED \
+!if $(HAVE_UXTHEME_H)
+ -DHAVE_UXTHEME_H=1 \
+!endif
+!if $(TTK_SQUARE_WIDGET)
+ -DTTK_SQUARE_WIDGET=1 \
+!endif
+
+PRJ_DEFINES = -DBUILD_ttk $(CONFIG_DEFS) -Dinline=__inline -D _CRT_SECURE_NO_DEPRECATE -D _CRT_NONSTDC_NO_DEPRECATE
+
+# Additional Link libraries needed beyond those in rules.vc
+PRJ_LIBS = netapi32.lib gdi32.lib user32.lib userenv.lib
+
+
+#---------------------------------------------------------------------
+# TkTest flags
+#---------------------------------------------------------------------
+
+!if "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!endif
+
+#---------------------------------------------------------------------
+# Project specific targets
+#---------------------------------------------------------------------
+
+release: setup $(TKSTUBLIB) $(WISH)
+all: release $(CAT32)
+core: setup $(TKSTUBLIB) $(TKLIB)
+cwish: $(WISHC)
+install: install-binaries install-libraries install-docs
+tktest: setup $(TKTEST) $(CAT32)
+
+setup: default-setup
+
+test: test-classic test-ttk
+
+test-classic: setup $(TKTEST) $(TKLIB) $(CAT32)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ $(DEBUGGER) $(TKTEST) "$(ROOT:\=/)/tests/all.tcl" $(TESTFLAGS) | $(CAT32)
+
+test-ttk: setup $(TKTEST) $(TKLIB) $(CAT32)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ $(DEBUGGER) $(TKTEST) "$(ROOT:\=/)/tests/ttk/all.tcl" $(TESTFLAGS) | $(CAT32)
+
+runtest: setup $(TKTEST) $(TKLIB) $(CAT32)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ $(DEBUGGER) $(TKTEST)
+
+rundemo: setup $(TKTEST) $(TKLIB) $(CAT32)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ $(TKTEST) $(ROOT:\=/)\library\demos\widget
+
+shell: setup $(WISH)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ $(DEBUGGER) $(WISH) <<
+ console show
+<<
+
+dbgshell: setup $(WISH)
+ @set TCL_LIBRARY=$(TCL_LIBRARY:\=/)
+ @set TK_LIBRARY=$(LIBDIR:\=/)
+ @set TCLLIBPATH=
+!if $(TCLINSTALL)
+ @set PATH=$(_TCLDIR)\bin;$(PATH)
+!else
+ @set PATH=$(_TCLDIR)\win\$(BUILDDIRTOP);$(PATH)
+!endif
+ windbg $(WISH)
+
+!if $(STATIC_BUILD)
+
+$(TKLIB): $(TKOBJS)
+ $(LIBCMD) @<<
+$**
+<<
+
+!else
+
+$(TKLIB): $(TKOBJS)
+ $(DLLCMD) @<<
+$**
+<<
+ $(_VC_MANIFEST_EMBED_DLL)
+ @if exist $*.exp del $*.exp
+
+$(TKIMPLIB): $(TKLIB)
+
+!endif # $(STATIC_BUILD)
+
+$(TKSTUBLIB): $(TKSTUBOBJS)
+ $(LIBCMD) -nodefaultlib $**
+
+
+$(WISH): $(WISHOBJS) $(TKSTUBLIB) $(TKIMPLIB)
+ $(GUIEXECMD) -stack:2300000 $**
+ $(_VC_MANIFEST_EMBED_EXE)
+
+
+$(WISHC): $(WISHOBJS) $(TKSTUBLIB) $(TKIMPLIB)
+ $(CONEXECMD) -stack:2300000 $**
+ $(_VC_MANIFEST_EMBED_EXE)
+
+
+$(TKTEST): $(TKTESTOBJS) $(TKSTUBLIB) $(TKIMPLIB)
+ $(GUIEXECMD) -stack:2300000 $**
+ $(_VC_MANIFEST_EMBED_EXE)
+
+
+$(CAT32): $(_TCLDIR)\win\cat.c
+ $(cc32) $(cflags) $(crt) -D_CRT_NONSTDC_NO_DEPRECATE -DCONSOLE -Fo$(TMP_DIR)\ $?
+ $(CONEXECMD) -DCONSOLE -stack:16384 $(TMP_DIR)\cat.obj
+ $(_VC_MANIFEST_EMBED_EXE)
+
+#---------------------------------------------------------------------
+# Regenerate the stubs files. [Development use only]
+#---------------------------------------------------------------------
+
+genstubs:
+!if !exist($(TCLSH))
+ @echo Build tclsh first!
+!else
+ set TCL_LIBRARY=$(TCL_LIBRARY)
+ $(TCLSH) $(_TCLDIR)\tools\genStubs.tcl $(GENERICDIR) \
+ $(GENERICDIR)\$(PROJECT).decls $(GENERICDIR)\$(PROJECT)Int.decls
+!endif
+
+
+#---------------------------------------------------------------------
+# Build the Windows HTML help file.
+#---------------------------------------------------------------------
+
+!if defined(PROCESSOR_ARCHITECTURE) && "$(PROCESSOR_ARCHITECTURE)" == "AMD64"
+HHC="%ProgramFiles(x86)%\HTML Help Workshop\hhc.exe"
+!else
+HHC="%ProgramFiles%\HTML Help Workshop\hhc.exe"
+!endif
+HTMLDIR=$(ROOT)\html
+HTMLBASE=TclTk$(TCL_VERSION)
+HHPFILE=$(HTMLDIR)\$(HTMLBASE).hhp
+CHMFILE=$(HTMLDIR)\$(HTMLBASE).chm
+
+htmlhelp: chmsetup $(CHMFILE)
+
+$(CHMFILE): $(DOCDIR)\*
+ @$(TCLSH) $(TCLTOOLSDIR)\tcltk-man2html.tcl
+ @echo Compiling HTML help project
+ @"$(HHC)" <<$(HHPFILE) >NUL
+[OPTIONS]
+Compatibility=1.1 or later
+Compiled file=$(HTMLBASE).chm
+Display compile progress=no
+Error log file=$(HTMLBASE).log
+Language=0x409 English (United States)
+Title=Tcl/Tk $(TCL_DOTVERSION) Help
+[FILES]
+contents.htm
+docs.css
+Keywords
+TclCmd
+TclLib
+TkCmd
+TkLib
+UserCmd
+<<
+
+chmsetup:
+ @if not exist $(HTMLDIR)\nul mkdir $(HTMLDIR)
+
+install-docs:
+!if exist("$(CHMFILE)")
+ @echo Installing compiled HTML help
+ @$(CPY) "$(CHMFILE)" "$(DOC_INSTALL_DIR)\"
+!endif
+# "emacs font-lock highlighting fix
+
+#---------------------------------------------------------------------
+# Special case object file targets
+#---------------------------------------------------------------------
+
+$(TMP_DIR)\testMain.obj: $(WINDIR)\winMain.c
+ $(cc32) $(appcflags_nostubs) -DTK_TEST \
+ -DTCL_USE_STATIC_PACKAGES=$(TCL_USE_STATIC_PACKAGES) \
+ -Fo$@ $?
+
+$(TMP_DIR)\tkTest.obj: $(GENERICDIR)\tkTest.c
+ $(cc32) $(appcflags_nostubs) -Fo$@ $?
+
+$(TMP_DIR)\tkOldTest.obj: $(GENERICDIR)\tkOldTest.c
+ $(cc32) $(appcflags_nostubs) -Fo$@ $?
+
+$(TMP_DIR)\tkWinTest.obj: $(WINDIR)\tkWinTest.c
+ $(cc32) $(appcflags_nostubs) -Fo$@ $?
+
+$(TMP_DIR)\tkSquare.obj: $(GENERICDIR)\tkSquare.c
+ $(cc32) $(appcflags_nostubs) -Fo$@ $?
+
+$(TMP_DIR)\winMain.obj: $(WINDIR)\winMain.c
+ $(cc32) $(appcflags_nostubs) \
+ -DTCL_USE_STATIC_PACKAGES=$(TCL_USE_STATIC_PACKAGES) \
+ -Fo$@ $?
+
+$(TMP_DIR)\tkMain2.obj: $(GENERICDIR)\tkMain.c
+ $(cc32) $(pkgcflags) -DTK_ASCII_MAIN -Fo$@ $?
+
+# The following objects are part of the stub library and should not
+# be built as DLL objects but none of the symbols should be exported
+# and no reference made to a C runtime.
+
+$(TMP_DIR)\tkStubLib.obj : $(GENERICDIR)\tkStubLib.c
+ $(cc32) $(stubscflags) -Fo$@ $?
+
+
+$(TMP_DIR)\wish.exe.manifest: $(WINDIR)\wish.exe.manifest.in
+ @nmakehlp -s << $** >$@
+@MACHINE@ $(MACHINE:IX86=X86)
+@TK_WIN_VERSION@ $(DOTVERSION).0.0
+<<
+
+#---------------------------------------------------------------------
+# Generate the source dependencies. Having dependency rules will
+# improve incremental build accuracy without having to resort to a
+# full rebuild just because some non-global header file like
+# tclCompile.h was changed. These rules aren't needed when building
+# from scratch.
+#---------------------------------------------------------------------
+
+depend:
+!if !exist($(TCLSH))
+ @echo Build tclsh first!
+!else
+ set TCL_LIBRARY=$(TCL_LIBRARY)
+ $(TCLSH) $(TCLTOOLSDIR:\=/)/mkdepend.tcl -vc32 -out:"$(OUT_DIR)\depend.mk" \
+ -passthru:"-DBUILD_tk $(TK_INCLUDES)" $(GENERICDIR),$$(GENERICDIR) \
+ $(WINDIR),$$(WINDIR) $(TTKDIR),$$(TTKDIR) $(XLIBDIR),$$(XLIBDIR) \
+ $(BITMAPDIR),$$(BITMAPDIR) @<<
+$(TKOBJS)
+<<
+!endif
+
+#---------------------------------------------------------------------
+# Dependency rules
+#---------------------------------------------------------------------
+
+$(TMP_DIR)\tk.res: \
+ $(RCDIR)\buttons.bmp \
+ $(RCDIR)\cursor*.cur \
+ $(RCDIR)\tk.ico
+
+!if exist("$(OUT_DIR)\depend.mk")
+!include "$(OUT_DIR)\depend.mk"
+!message *** Dependency rules in use.
+!else
+!message *** Dependency rules are not being used.
+!endif
+
+### add a spacer in the output
+!message
+
+#---------------------------------------------------------------------
+# Implicit rules
+#---------------------------------------------------------------------
+
+{$(XLIBDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(TTKDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(ROOT)\unix}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+$(TMP_DIR)\tk.res: $(TMP_DIR)\wish.exe.manifest
+$(TMP_DIR)\wish.res: $(TMP_DIR)\wish.exe.manifest
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
+
+
+#---------------------------------------------------------------------
+# Installation.
+#---------------------------------------------------------------------
+
+install-binaries:
+ @echo installing binaries
+ @$(CPY) "$(WISH)" "$(BIN_INSTALL_DIR)\"
+!if "$(TKLIB)" != "$(TKIMPLIB)"
+ @$(CPY) "$(TKLIB)" "$(BIN_INSTALL_DIR)\"
+!endif
+ @$(CPY) "$(TKIMPLIB)" "$(LIB_INSTALL_DIR)\"
+ @$(CPY) "$(TKSTUBLIB)" "$(LIB_INSTALL_DIR)\"
+!if !$(STATIC_BUILD)
+ @echo creating package index
+ @type << > $(OUT_DIR)\pkgIndex.tcl
+if {[catch {package present Tcl 8.6.0}]} { return }
+if {($$::tcl_platform(platform) eq "unix") && ([info exists ::env(DISPLAY)]
+ || ([info exists ::argv] && ("-display" in $$::argv)))} {
+ package ifneeded Tk $(TK_PATCH_LEVEL) [list load [file join $$dir .. .. bin libtk$(DOTVERSION).dll] Tk]
+} else {
+ package ifneeded Tk $(TK_PATCH_LEVEL) [list load [file join $$dir .. .. bin $(TKLIBNAME)] Tk]
+}
+<<
+ @$(CPY) $(OUT_DIR)\pkgIndex.tcl "$(SCRIPT_INSTALL_DIR)\"
+!endif
+
+#"
+
+install-libraries:
+ @echo installing Tk headers
+ @$(CPY) "$(GENERICDIR)\tk.h" "$(INCLUDE_INSTALL_DIR)\"
+ @$(CPY) "$(GENERICDIR)\tkDecls.h" "$(INCLUDE_INSTALL_DIR)\"
+ @$(CPY) "$(GENERICDIR)\tkPlatDecls.h" "$(INCLUDE_INSTALL_DIR)\"
+ @$(CPY) "$(GENERICDIR)\tkIntXlibDecls.h" "$(INCLUDE_INSTALL_DIR)\"
+ @$(CPY) "$(XLIBDIR)\X11\*.h" "$(INCLUDE_INSTALL_DIR)\X11\"
+ @echo installing script library
+ @$(CPY) "$(LIBDIR)\*" "$(SCRIPT_INSTALL_DIR)\"
+ @echo installing theme library
+ @$(CPY) "$(LIBDIR)\ttk\*" "$(SCRIPT_INSTALL_DIR)\ttk\"
+ @echo installing images
+ @$(CPY) "$(LIBDIR)\images\*" "$(SCRIPT_INSTALL_DIR)\images\"
+ @echo installing language files
+ @$(CPY) "$(LIBDIR)\msgs\*" "$(SCRIPT_INSTALL_DIR)\msgs\"
+ @echo installing demos
+ @$(CPY) "$(DEMODIR)\*" "$(DEMO_INSTALL_DIR)\"
+ @$(CPY) "$(DEMODIR)\images\*" "$(DEMO_INSTALL_DIR)\images\"
+
+#"
+
+#---------------------------------------------------------------------
+# Clean up
+#---------------------------------------------------------------------
+
+clean: default-clean
+realclean: hose
+hose: default-hose
+tidy:
+!if "$(TKLIB)" != "$(TKIMPLIB)"
+ @echo Removing $(TKLIB) ...
+ @if exist $(TKLIB) del $(TKLIB)
+!endif
+ @echo Removing $(TKIMPLIB) ...
+ @if exist $(TKIMPLIB) del $(TKIMPLIB)
+ @echo Removing $(WISH) ...
+ @if exist $(WISH) del $(WISH)
+ @echo Removing $(TKTEST) ...
+ @if exist $(TKTEST) del $(TKTEST)
+ @echo Removing $(TKSTUBLIB) ...
+ @if exist $(TKSTUBLIB) del $(TKSTUBLIB)
+
diff --git a/tk8.6/win/mkd.bat b/tk8.6/win/mkd.bat new file mode 100644 index 0000000..1bd5ccb --- /dev/null +++ b/tk8.6/win/mkd.bat @@ -0,0 +1,12 @@ +@echo off
+
+if exist %1\nul goto end
+
+md %1
+if errorlevel 1 goto end
+
+echo Created directory %1
+
+:end
+
+
diff --git a/tk8.6/win/nmakehlp.c b/tk8.6/win/nmakehlp.c new file mode 100644 index 0000000..025bb99 --- /dev/null +++ b/tk8.6/win/nmakehlp.c @@ -0,0 +1,814 @@ +/* + * ---------------------------------------------------------------------------- + * nmakehlp.c -- + * + * This is used to fix limitations within nmake and the environment. + * + * Copyright (c) 2002 by David Gravereaux. + * Copyright (c) 2006 by Pat Thoyts + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + * ---------------------------------------------------------------------------- + */ + +#define _CRT_SECURE_NO_DEPRECATE +#include <windows.h> +#define NO_SHLWAPI_GDI +#define NO_SHLWAPI_STREAM +#define NO_SHLWAPI_REG +#include <shlwapi.h> +#pragma comment (lib, "user32.lib") +#pragma comment (lib, "kernel32.lib") +#pragma comment (lib, "shlwapi.lib") +#include <stdio.h> +#include <math.h> + +/* + * This library is required for x64 builds with _some_ versions of MSVC + */ +#if defined(_M_IA64) || defined(_M_AMD64) +#if _MSC_VER >= 1400 && _MSC_VER < 1500 +#pragma comment(lib, "bufferoverflowU") +#endif +#endif + +/* ISO hack for dumb VC++ */ +#ifdef _MSC_VER +#define snprintf _snprintf +#endif + + +/* protos */ + +static int CheckForCompilerFeature(const char *option); +static int CheckForLinkerFeature(const char **options, int count); +static int IsIn(const char *string, const char *substring); +static int SubstituteFile(const char *substs, const char *filename); +static int QualifyPath(const char *path); +static int LocateDependency(const char *keyfile); +static const char *GetVersionFromFile(const char *filename, const char *match, int numdots); +static DWORD WINAPI ReadFromPipe(LPVOID args); + +/* globals */ + +#define CHUNK 25 +#define STATICBUFFERSIZE 1000 +typedef struct { + HANDLE pipe; + char buffer[STATICBUFFERSIZE]; +} pipeinfo; + +pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'}; +pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'}; + +/* + * exitcodes: 0 == no, 1 == yes, 2 == error + */ + +int +main( + int argc, + char *argv[]) +{ + char msg[300]; + DWORD dwWritten; + int chars; + char *s; + + /* + * Make sure children (cl.exe and link.exe) are kept quiet. + */ + + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); + + /* + * Make sure the compiler and linker aren't effected by the outside world. + */ + + SetEnvironmentVariable("CL", ""); + SetEnvironmentVariable("LINK", ""); + + if (argc > 1 && *argv[1] == '-') { + switch (*(argv[1]+1)) { + case 'c': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -c <compiler option>\n" + "Tests for whether cl.exe supports an option\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return CheckForCompilerFeature(argv[2]); + case 'l': + if (argc < 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -l <linker option> ?<mandatory option> ...?\n" + "Tests for whether link.exe supports an option\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return CheckForLinkerFeature(&argv[2], argc-2); + case 'f': + if (argc == 2) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -f <string> <substring>\n" + "Find a substring within another\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } else if (argc == 3) { + /* + * If the string is blank, there is no match. + */ + + return 0; + } else { + return IsIn(argv[2], argv[3]); + } + case 's': + if (argc == 2) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -s <substitutions file> <file>\n" + "Perform a set of string map type substutitions on a file\n" + "exitcodes: 0\n", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return SubstituteFile(argv[2], argv[3]); + case 'V': + if (argc != 4) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -V filename matchstring\n" + "Extract a version from a file:\n" + "eg: pkgIndex.tcl \"package ifneeded http\"", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 0; + } + s = GetVersionFromFile(argv[2], argv[3], *(argv[1]+2) - '0'); + if (s && *s) { + printf("%s\n", s); + return 0; + } else + return 1; /* Version not found. Return non-0 exit code */ + + case 'Q': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -Q path\n" + "Emit the fully qualified path\n" + "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return QualifyPath(argv[2]); + + case 'L': + if (argc != 3) { + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -L keypath\n" + "Emit the fully qualified path of directory containing keypath\n" + "exitcodes: 0 == success, 1 == not found, 2 == error\n", argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, + &dwWritten, NULL); + return 2; + } + return LocateDependency(argv[2]); + } + } + chars = snprintf(msg, sizeof(msg) - 1, + "usage: %s -c|-f|-l|-Q|-s|-V ...\n" + "This is a little helper app to equalize shell differences between WinNT and\n" + "Win9x and get nmake.exe to accomplish its job.\n", + argv[0]); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL); + return 2; +} + +static int +CheckForCompilerFeature( + const char *option) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa; + DWORD threadID; + char msg[300]; + BOOL ok; + HANDLE hProcess, h, pipeThreads[2]; + char cmdline[100]; + + hProcess = GetCurrentProcess(); + + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = FALSE; + + /* + * Create a non-inheritible pipe. + */ + + CreatePipe(&Out.pipe, &h, &sa, 0); + + /* + * Dupe the write side, make it inheritible, and close the original. + */ + + DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Same as above, but for the error side. + */ + + CreatePipe(&Err.pipe, &h, &sa, 0); + DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Base command line. + */ + + lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch "); + + /* + * Append our option for testing + */ + + lstrcat(cmdline, option); + + /* + * Filename to compile, which exists, but is nothing and empty. + */ + + lstrcat(cmdline, " .\\nul"); + + ok = CreateProcess( + NULL, /* Module name. */ + cmdline, /* Command line. */ + NULL, /* Process handle not inheritable. */ + NULL, /* Thread handle not inheritable. */ + TRUE, /* yes, inherit handles. */ + DETACHED_PROCESS, /* No console for you. */ + NULL, /* Use parent's environment block. */ + NULL, /* Use parent's starting directory. */ + &si, /* Pointer to STARTUPINFO structure. */ + &pi); /* Pointer to PROCESS_INFORMATION structure. */ + + if (!ok) { + DWORD err = GetLastError(); + int chars = snprintf(msg, sizeof(msg) - 1, + "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| + FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], + (300-chars), 0); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); + return 2; + } + + /* + * Close our references to the write handles that have now been inherited. + */ + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + WaitForInputIdle(pi.hProcess, 5000); + CloseHandle(pi.hThread); + + /* + * Start the pipe reader threads. + */ + + pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); + pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); + + /* + * Block waiting for the process to end. + */ + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + + /* + * Wait for our pipe to get done reading, should it be a little slow. + */ + + WaitForMultipleObjects(2, pipeThreads, TRUE, 500); + CloseHandle(pipeThreads[0]); + CloseHandle(pipeThreads[1]); + + /* + * Look for the commandline warning code in both streams. + * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002. + */ + + return !(strstr(Out.buffer, "D4002") != NULL + || strstr(Err.buffer, "D4002") != NULL + || strstr(Out.buffer, "D9002") != NULL + || strstr(Err.buffer, "D9002") != NULL + || strstr(Out.buffer, "D2021") != NULL + || strstr(Err.buffer, "D2021") != NULL); +} + +static int +CheckForLinkerFeature( + const char **options, + int count) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + SECURITY_ATTRIBUTES sa; + DWORD threadID; + char msg[300]; + BOOL ok; + HANDLE hProcess, h, pipeThreads[2]; + int i; + char cmdline[255]; + + hProcess = GetCurrentProcess(); + + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.dwFlags = STARTF_USESTDHANDLES; + si.hStdInput = INVALID_HANDLE_VALUE; + + ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + /* + * Create a non-inheritible pipe. + */ + + CreatePipe(&Out.pipe, &h, &sa, 0); + + /* + * Dupe the write side, make it inheritible, and close the original. + */ + + DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Same as above, but for the error side. + */ + + CreatePipe(&Err.pipe, &h, &sa, 0); + DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE, + DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + + /* + * Base command line. + */ + + lstrcpy(cmdline, "link.exe -nologo "); + + /* + * Append our option for testing. + */ + + for (i = 0; i < count; i++) { + lstrcat(cmdline, " \""); + lstrcat(cmdline, options[i]); + lstrcat(cmdline, "\""); + } + + ok = CreateProcess( + NULL, /* Module name. */ + cmdline, /* Command line. */ + NULL, /* Process handle not inheritable. */ + NULL, /* Thread handle not inheritable. */ + TRUE, /* yes, inherit handles. */ + DETACHED_PROCESS, /* No console for you. */ + NULL, /* Use parent's environment block. */ + NULL, /* Use parent's starting directory. */ + &si, /* Pointer to STARTUPINFO structure. */ + &pi); /* Pointer to PROCESS_INFORMATION structure. */ + + if (!ok) { + DWORD err = GetLastError(); + int chars = snprintf(msg, sizeof(msg) - 1, + "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS| + FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars], + (300-chars), 0); + WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, lstrlen(msg), &err,NULL); + return 2; + } + + /* + * Close our references to the write handles that have now been inherited. + */ + + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + WaitForInputIdle(pi.hProcess, 5000); + CloseHandle(pi.hThread); + + /* + * Start the pipe reader threads. + */ + + pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID); + pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID); + + /* + * Block waiting for the process to end. + */ + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + + /* + * Wait for our pipe to get done reading, should it be a little slow. + */ + + WaitForMultipleObjects(2, pipeThreads, TRUE, 500); + CloseHandle(pipeThreads[0]); + CloseHandle(pipeThreads[1]); + + /* + * Look for the commandline warning code in the stderr stream. + */ + + return !(strstr(Out.buffer, "LNK1117") != NULL || + strstr(Err.buffer, "LNK1117") != NULL || + strstr(Out.buffer, "LNK4044") != NULL || + strstr(Err.buffer, "LNK4044") != NULL || + strstr(Out.buffer, "LNK4224") != NULL || + strstr(Err.buffer, "LNK4224") != NULL); +} + +static DWORD WINAPI +ReadFromPipe( + LPVOID args) +{ + pipeinfo *pi = (pipeinfo *) args; + char *lastBuf = pi->buffer; + DWORD dwRead; + BOOL ok; + + again: + if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) { + CloseHandle(pi->pipe); + return (DWORD)-1; + } + ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L); + if (!ok || dwRead == 0) { + CloseHandle(pi->pipe); + return 0; + } + lastBuf += dwRead; + goto again; + + return 0; /* makes the compiler happy */ +} + +static int +IsIn( + const char *string, + const char *substring) +{ + return (strstr(string, substring) != NULL); +} + +/* + * GetVersionFromFile -- + * Looks for a match string in a file and then returns the version + * following the match where a version is anything acceptable to + * package provide or package ifneeded. + */ + +static const char * +GetVersionFromFile( + const char *filename, + const char *match, + int numdots) +{ + size_t cbBuffer = 100; + static char szBuffer[100]; + char *szResult = NULL; + FILE *fp = fopen(filename, "rt"); + + if (fp != NULL) { + /* + * Read data until we see our match string. + */ + + while (fgets(szBuffer, cbBuffer, fp) != NULL) { + LPSTR p, q; + + p = strstr(szBuffer, match); + if (p != NULL) { + /* + * Skip to first digit after the match. + */ + + p += strlen(match); + while (*p && !isdigit(*p)) { + ++p; + } + + /* + * Find ending whitespace. + */ + + q = p; + while (*q && (strchr("0123456789.ab", *q)) && ((!strchr(".ab", *q) + && (!strchr("ab", q[-1])) || --numdots))) { + ++q; + } + + memcpy(szBuffer, p, q - p); + szBuffer[q-p] = 0; + szResult = szBuffer; + break; + } + } + fclose(fp); + } + return szResult; +} + +/* + * List helpers for the SubstituteFile function + */ + +typedef struct list_item_t { + struct list_item_t *nextPtr; + char * key; + char * value; +} list_item_t; + +/* insert a list item into the list (list may be null) */ +static list_item_t * +list_insert(list_item_t **listPtrPtr, const char *key, const char *value) +{ + list_item_t *itemPtr = malloc(sizeof(list_item_t)); + if (itemPtr) { + itemPtr->key = strdup(key); + itemPtr->value = strdup(value); + itemPtr->nextPtr = NULL; + + while(*listPtrPtr) { + listPtrPtr = &(*listPtrPtr)->nextPtr; + } + *listPtrPtr = itemPtr; + } + return itemPtr; +} + +static void +list_free(list_item_t **listPtrPtr) +{ + list_item_t *tmpPtr, *listPtr = *listPtrPtr; + while (listPtr) { + tmpPtr = listPtr; + listPtr = listPtr->nextPtr; + free(tmpPtr->key); + free(tmpPtr->value); + free(tmpPtr); + } +} + +/* + * SubstituteFile -- + * As windows doesn't provide anything useful like sed and it's unreliable + * to use the tclsh you are building against (consider x-platform builds - + * eg compiling AMD64 target from IX86) we provide a simple substitution + * option here to handle autoconf style substitutions. + * The substitution file is whitespace and line delimited. The file should + * consist of lines matching the regular expression: + * \s*\S+\s+\S*$ + * + * Usage is something like: + * nmakehlp -S << $** > $@ + * @PACKAGE_NAME@ $(PACKAGE_NAME) + * @PACKAGE_VERSION@ $(PACKAGE_VERSION) + * << + */ + +static int +SubstituteFile( + const char *substitutions, + const char *filename) +{ + size_t cbBuffer = 1024; + static char szBuffer[1024], szCopy[1024]; + char *szResult = NULL; + list_item_t *substPtr = NULL; + FILE *fp, *sp; + + fp = fopen(filename, "rt"); + if (fp != NULL) { + + /* + * Build a list of substutitions from the first filename + */ + + sp = fopen(substitutions, "rt"); + if (sp != NULL) { + while (fgets(szBuffer, cbBuffer, sp) != NULL) { + unsigned char *ks, *ke, *vs, *ve; + ks = (unsigned char*)szBuffer; + while (ks && *ks && isspace(*ks)) ++ks; + ke = ks; + while (ke && *ke && !isspace(*ke)) ++ke; + vs = ke; + while (vs && *vs && isspace(*vs)) ++vs; + ve = vs; + while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve; + *ke = 0, *ve = 0; + list_insert(&substPtr, (char*)ks, (char*)vs); + } + fclose(sp); + } + + /* debug: dump the list */ +#ifdef _DEBUG + { + int n = 0; + list_item_t *p = NULL; + for (p = substPtr; p != NULL; p = p->nextPtr, ++n) { + fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value); + } + } +#endif + + /* + * Run the substitutions over each line of the input + */ + + while (fgets(szBuffer, cbBuffer, fp) != NULL) { + list_item_t *p = NULL; + for (p = substPtr; p != NULL; p = p->nextPtr) { + char *m = strstr(szBuffer, p->key); + if (m) { + char *cp, *op, *sp; + cp = szCopy; + op = szBuffer; + while (op != m) *cp++ = *op++; + sp = p->value; + while (sp && *sp) *cp++ = *sp++; + op += strlen(p->key); + while (*op) *cp++ = *op++; + *cp = 0; + memcpy(szBuffer, szCopy, sizeof(szCopy)); + } + } + printf(szBuffer); + } + + list_free(&substPtr); + } + fclose(fp); + return 0; +} + +/* + * QualifyPath -- + * + * This composes the current working directory with a provided path + * and returns the fully qualified and normalized path. + * Mostly needed to setup paths for testing. + */ + +static int +QualifyPath( + const char *szPath) +{ + char szCwd[MAX_PATH + 1]; + char szTmp[MAX_PATH + 1]; + char *p; + GetCurrentDirectory(MAX_PATH, szCwd); + while ((p = strchr(szPath, '/')) && *p) + *p = '\\'; + PathCombine(szTmp, szCwd, szPath); + PathCanonicalize(szCwd, szTmp); + printf("%s\n", szCwd); + return 0; +} + +/* + * Implements LocateDependency for a single directory. See that command + * for an explanation. + * Returns 0 if found after printing the directory. + * Returns 1 if not found but no errors. + * Returns 2 on any kind of error + * Basically, these are used as exit codes for the process. + */ +static int LocateDependencyHelper(const char *dir, const char *keypath) +{ + HANDLE hSearch; + char path[MAX_PATH+1]; + int dirlen, keylen, ret; + WIN32_FIND_DATA finfo; + + if (dir == NULL || keypath == NULL) + return 2; /* Have no real error reporting mechanism into nmake */ + dirlen = strlen(dir); + if ((dirlen + 3) > sizeof(path)) + return 2; + strncpy(path, dir, dirlen); + strncpy(path+dirlen, "\\*", 3); /* Including terminating \0 */ + keylen = strlen(keypath); + +#if 0 /* This function is not available in Visual C++ 6 */ + /* + * Use numerics 0 -> FindExInfoStandard, + * 1 -> FindExSearchLimitToDirectories, + * as these are not defined in Visual C++ 6 + */ + hSearch = FindFirstFileEx(path, 0, &finfo, 1, NULL, 0); +#else + hSearch = FindFirstFile(path, &finfo); +#endif + if (hSearch == INVALID_HANDLE_VALUE) + return 1; /* Not found */ + + /* Loop through all subdirs checking if the keypath is under there */ + ret = 1; /* Assume not found */ + do { + int sublen; + /* + * We need to check it is a directory despite the + * FindExSearchLimitToDirectories in the above call. See SDK docs + */ + if ((finfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0) + continue; + sublen = strlen(finfo.cFileName); + if ((dirlen+1+sublen+1+keylen+1) > sizeof(path)) + continue; /* Path does not fit, assume not matched */ + strncpy(path+dirlen+1, finfo.cFileName, sublen); + path[dirlen+1+sublen] = '\\'; + strncpy(path+dirlen+1+sublen+1, keypath, keylen+1); + if (PathFileExists(path)) { + /* Found a match, print to stdout */ + path[dirlen+1+sublen] = '\0'; + QualifyPath(path); + ret = 0; + break; + } + } while (FindNextFile(hSearch, &finfo)); + FindClose(hSearch); + return ret; +} + +/* + * LocateDependency -- + * + * Locates a dependency for a package. + * keypath - a relative path within the package directory + * that is used to confirm it is the correct directory. + * The search path for the package directory is currently only + * the parent and grandparent of the current working directory. + * If found, the command prints + * name_DIRPATH=<full path of located directory> + * and returns 0. If not found, does not print anything and returns 1. + */ +static int LocateDependency(const char *keypath) +{ + int i, ret; + static char *paths[] = {"..", "..\\..", "..\\..\\.."}; + + for (i = 0; i < (sizeof(paths)/sizeof(paths[0])); ++i) { + ret = LocateDependencyHelper(paths[i], keypath); + if (ret == 0) + return ret; + } + return ret; +} + + +/* + * Local variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * indent-tabs-mode: t + * tab-width: 8 + * End: + */ diff --git a/tk8.6/win/rc/buttons.bmp b/tk8.6/win/rc/buttons.bmp Binary files differnew file mode 100644 index 0000000..f37a4c9 --- /dev/null +++ b/tk8.6/win/rc/buttons.bmp diff --git a/tk8.6/win/rc/cursor00.cur b/tk8.6/win/rc/cursor00.cur Binary files differnew file mode 100644 index 0000000..0c58bfe --- /dev/null +++ b/tk8.6/win/rc/cursor00.cur diff --git a/tk8.6/win/rc/cursor02.cur b/tk8.6/win/rc/cursor02.cur Binary files differnew file mode 100644 index 0000000..46f0f11 --- /dev/null +++ b/tk8.6/win/rc/cursor02.cur diff --git a/tk8.6/win/rc/cursor04.cur b/tk8.6/win/rc/cursor04.cur Binary files differnew file mode 100644 index 0000000..1b3028d --- /dev/null +++ b/tk8.6/win/rc/cursor04.cur diff --git a/tk8.6/win/rc/cursor06.cur b/tk8.6/win/rc/cursor06.cur Binary files differnew file mode 100644 index 0000000..bb06edf --- /dev/null +++ b/tk8.6/win/rc/cursor06.cur diff --git a/tk8.6/win/rc/cursor08.cur b/tk8.6/win/rc/cursor08.cur Binary files differnew file mode 100644 index 0000000..d9f15f7 --- /dev/null +++ b/tk8.6/win/rc/cursor08.cur diff --git a/tk8.6/win/rc/cursor0a.cur b/tk8.6/win/rc/cursor0a.cur Binary files differnew file mode 100644 index 0000000..6bffda5 --- /dev/null +++ b/tk8.6/win/rc/cursor0a.cur diff --git a/tk8.6/win/rc/cursor0c.cur b/tk8.6/win/rc/cursor0c.cur Binary files differnew file mode 100644 index 0000000..a991da6 --- /dev/null +++ b/tk8.6/win/rc/cursor0c.cur diff --git a/tk8.6/win/rc/cursor0e.cur b/tk8.6/win/rc/cursor0e.cur Binary files differnew file mode 100644 index 0000000..6e0434d --- /dev/null +++ b/tk8.6/win/rc/cursor0e.cur diff --git a/tk8.6/win/rc/cursor10.cur b/tk8.6/win/rc/cursor10.cur Binary files differnew file mode 100644 index 0000000..c4f7809 --- /dev/null +++ b/tk8.6/win/rc/cursor10.cur diff --git a/tk8.6/win/rc/cursor12.cur b/tk8.6/win/rc/cursor12.cur Binary files differnew file mode 100644 index 0000000..0d0d667 --- /dev/null +++ b/tk8.6/win/rc/cursor12.cur diff --git a/tk8.6/win/rc/cursor14.cur b/tk8.6/win/rc/cursor14.cur Binary files differnew file mode 100644 index 0000000..c7de122 --- /dev/null +++ b/tk8.6/win/rc/cursor14.cur diff --git a/tk8.6/win/rc/cursor16.cur b/tk8.6/win/rc/cursor16.cur Binary files differnew file mode 100644 index 0000000..cfc08f2 --- /dev/null +++ b/tk8.6/win/rc/cursor16.cur diff --git a/tk8.6/win/rc/cursor18.cur b/tk8.6/win/rc/cursor18.cur Binary files differnew file mode 100644 index 0000000..95ed2ee --- /dev/null +++ b/tk8.6/win/rc/cursor18.cur diff --git a/tk8.6/win/rc/cursor1a.cur b/tk8.6/win/rc/cursor1a.cur Binary files differnew file mode 100644 index 0000000..ea51361 --- /dev/null +++ b/tk8.6/win/rc/cursor1a.cur diff --git a/tk8.6/win/rc/cursor1c.cur b/tk8.6/win/rc/cursor1c.cur Binary files differnew file mode 100644 index 0000000..6f10bfb --- /dev/null +++ b/tk8.6/win/rc/cursor1c.cur diff --git a/tk8.6/win/rc/cursor1e.cur b/tk8.6/win/rc/cursor1e.cur Binary files differnew file mode 100644 index 0000000..3272db1 --- /dev/null +++ b/tk8.6/win/rc/cursor1e.cur diff --git a/tk8.6/win/rc/cursor20.cur b/tk8.6/win/rc/cursor20.cur Binary files differnew file mode 100644 index 0000000..48080be --- /dev/null +++ b/tk8.6/win/rc/cursor20.cur diff --git a/tk8.6/win/rc/cursor22.cur b/tk8.6/win/rc/cursor22.cur Binary files differnew file mode 100644 index 0000000..2f8e912 --- /dev/null +++ b/tk8.6/win/rc/cursor22.cur diff --git a/tk8.6/win/rc/cursor24.cur b/tk8.6/win/rc/cursor24.cur Binary files differnew file mode 100644 index 0000000..87ba5b4 --- /dev/null +++ b/tk8.6/win/rc/cursor24.cur diff --git a/tk8.6/win/rc/cursor26.cur b/tk8.6/win/rc/cursor26.cur Binary files differnew file mode 100644 index 0000000..0b2dbd2 --- /dev/null +++ b/tk8.6/win/rc/cursor26.cur diff --git a/tk8.6/win/rc/cursor28.cur b/tk8.6/win/rc/cursor28.cur Binary files differnew file mode 100644 index 0000000..30550f9 --- /dev/null +++ b/tk8.6/win/rc/cursor28.cur diff --git a/tk8.6/win/rc/cursor2a.cur b/tk8.6/win/rc/cursor2a.cur Binary files differnew file mode 100644 index 0000000..5e75f16 --- /dev/null +++ b/tk8.6/win/rc/cursor2a.cur diff --git a/tk8.6/win/rc/cursor2c.cur b/tk8.6/win/rc/cursor2c.cur Binary files differnew file mode 100644 index 0000000..7be3494 --- /dev/null +++ b/tk8.6/win/rc/cursor2c.cur diff --git a/tk8.6/win/rc/cursor2e.cur b/tk8.6/win/rc/cursor2e.cur Binary files differnew file mode 100644 index 0000000..7a0bc69 --- /dev/null +++ b/tk8.6/win/rc/cursor2e.cur diff --git a/tk8.6/win/rc/cursor30.cur b/tk8.6/win/rc/cursor30.cur Binary files differnew file mode 100644 index 0000000..70ef4fd --- /dev/null +++ b/tk8.6/win/rc/cursor30.cur diff --git a/tk8.6/win/rc/cursor32.cur b/tk8.6/win/rc/cursor32.cur Binary files differnew file mode 100644 index 0000000..93b5c47 --- /dev/null +++ b/tk8.6/win/rc/cursor32.cur diff --git a/tk8.6/win/rc/cursor34.cur b/tk8.6/win/rc/cursor34.cur Binary files differnew file mode 100644 index 0000000..0fad3f1 --- /dev/null +++ b/tk8.6/win/rc/cursor34.cur diff --git a/tk8.6/win/rc/cursor36.cur b/tk8.6/win/rc/cursor36.cur Binary files differnew file mode 100644 index 0000000..fc8d4f6 --- /dev/null +++ b/tk8.6/win/rc/cursor36.cur diff --git a/tk8.6/win/rc/cursor38.cur b/tk8.6/win/rc/cursor38.cur Binary files differnew file mode 100644 index 0000000..4447d7d --- /dev/null +++ b/tk8.6/win/rc/cursor38.cur diff --git a/tk8.6/win/rc/cursor3a.cur b/tk8.6/win/rc/cursor3a.cur Binary files differnew file mode 100644 index 0000000..e3eda81 --- /dev/null +++ b/tk8.6/win/rc/cursor3a.cur diff --git a/tk8.6/win/rc/cursor3c.cur b/tk8.6/win/rc/cursor3c.cur Binary files differnew file mode 100644 index 0000000..6a3111d --- /dev/null +++ b/tk8.6/win/rc/cursor3c.cur diff --git a/tk8.6/win/rc/cursor3e.cur b/tk8.6/win/rc/cursor3e.cur Binary files differnew file mode 100644 index 0000000..fa6fe5b --- /dev/null +++ b/tk8.6/win/rc/cursor3e.cur diff --git a/tk8.6/win/rc/cursor40.cur b/tk8.6/win/rc/cursor40.cur Binary files differnew file mode 100644 index 0000000..f07bf4f --- /dev/null +++ b/tk8.6/win/rc/cursor40.cur diff --git a/tk8.6/win/rc/cursor42.cur b/tk8.6/win/rc/cursor42.cur Binary files differnew file mode 100644 index 0000000..387d5f0 --- /dev/null +++ b/tk8.6/win/rc/cursor42.cur diff --git a/tk8.6/win/rc/cursor44.cur b/tk8.6/win/rc/cursor44.cur Binary files differnew file mode 100644 index 0000000..190320c --- /dev/null +++ b/tk8.6/win/rc/cursor44.cur diff --git a/tk8.6/win/rc/cursor46.cur b/tk8.6/win/rc/cursor46.cur Binary files differnew file mode 100644 index 0000000..4f969d1 --- /dev/null +++ b/tk8.6/win/rc/cursor46.cur diff --git a/tk8.6/win/rc/cursor48.cur b/tk8.6/win/rc/cursor48.cur Binary files differnew file mode 100644 index 0000000..97e499a --- /dev/null +++ b/tk8.6/win/rc/cursor48.cur diff --git a/tk8.6/win/rc/cursor4a.cur b/tk8.6/win/rc/cursor4a.cur Binary files differnew file mode 100644 index 0000000..30febfa --- /dev/null +++ b/tk8.6/win/rc/cursor4a.cur diff --git a/tk8.6/win/rc/cursor4c.cur b/tk8.6/win/rc/cursor4c.cur Binary files differnew file mode 100644 index 0000000..3512a51 --- /dev/null +++ b/tk8.6/win/rc/cursor4c.cur diff --git a/tk8.6/win/rc/cursor4e.cur b/tk8.6/win/rc/cursor4e.cur Binary files differnew file mode 100644 index 0000000..28992c4 --- /dev/null +++ b/tk8.6/win/rc/cursor4e.cur diff --git a/tk8.6/win/rc/cursor50.cur b/tk8.6/win/rc/cursor50.cur Binary files differnew file mode 100644 index 0000000..7352420 --- /dev/null +++ b/tk8.6/win/rc/cursor50.cur diff --git a/tk8.6/win/rc/cursor52.cur b/tk8.6/win/rc/cursor52.cur Binary files differnew file mode 100644 index 0000000..435f99f --- /dev/null +++ b/tk8.6/win/rc/cursor52.cur diff --git a/tk8.6/win/rc/cursor54.cur b/tk8.6/win/rc/cursor54.cur Binary files differnew file mode 100644 index 0000000..54eb4f2 --- /dev/null +++ b/tk8.6/win/rc/cursor54.cur diff --git a/tk8.6/win/rc/cursor56.cur b/tk8.6/win/rc/cursor56.cur Binary files differnew file mode 100644 index 0000000..c808bd4 --- /dev/null +++ b/tk8.6/win/rc/cursor56.cur diff --git a/tk8.6/win/rc/cursor58.cur b/tk8.6/win/rc/cursor58.cur Binary files differnew file mode 100644 index 0000000..98b6a2f --- /dev/null +++ b/tk8.6/win/rc/cursor58.cur diff --git a/tk8.6/win/rc/cursor5a.cur b/tk8.6/win/rc/cursor5a.cur Binary files differnew file mode 100644 index 0000000..b00070e --- /dev/null +++ b/tk8.6/win/rc/cursor5a.cur diff --git a/tk8.6/win/rc/cursor5c.cur b/tk8.6/win/rc/cursor5c.cur Binary files differnew file mode 100644 index 0000000..a407b55 --- /dev/null +++ b/tk8.6/win/rc/cursor5c.cur diff --git a/tk8.6/win/rc/cursor5e.cur b/tk8.6/win/rc/cursor5e.cur Binary files differnew file mode 100644 index 0000000..ab3449f --- /dev/null +++ b/tk8.6/win/rc/cursor5e.cur diff --git a/tk8.6/win/rc/cursor60.cur b/tk8.6/win/rc/cursor60.cur Binary files differnew file mode 100644 index 0000000..dc61469 --- /dev/null +++ b/tk8.6/win/rc/cursor60.cur diff --git a/tk8.6/win/rc/cursor62.cur b/tk8.6/win/rc/cursor62.cur Binary files differnew file mode 100644 index 0000000..ff7c009 --- /dev/null +++ b/tk8.6/win/rc/cursor62.cur diff --git a/tk8.6/win/rc/cursor64.cur b/tk8.6/win/rc/cursor64.cur Binary files differnew file mode 100644 index 0000000..a6bdd0e --- /dev/null +++ b/tk8.6/win/rc/cursor64.cur diff --git a/tk8.6/win/rc/cursor66.cur b/tk8.6/win/rc/cursor66.cur Binary files differnew file mode 100644 index 0000000..269f772 --- /dev/null +++ b/tk8.6/win/rc/cursor66.cur diff --git a/tk8.6/win/rc/cursor68.cur b/tk8.6/win/rc/cursor68.cur Binary files differnew file mode 100644 index 0000000..27cfaf0 --- /dev/null +++ b/tk8.6/win/rc/cursor68.cur diff --git a/tk8.6/win/rc/cursor6a.cur b/tk8.6/win/rc/cursor6a.cur Binary files differnew file mode 100644 index 0000000..20f138e --- /dev/null +++ b/tk8.6/win/rc/cursor6a.cur diff --git a/tk8.6/win/rc/cursor6c.cur b/tk8.6/win/rc/cursor6c.cur Binary files differnew file mode 100644 index 0000000..d8fb0f1 --- /dev/null +++ b/tk8.6/win/rc/cursor6c.cur diff --git a/tk8.6/win/rc/cursor6e.cur b/tk8.6/win/rc/cursor6e.cur Binary files differnew file mode 100644 index 0000000..3a9b6b0 --- /dev/null +++ b/tk8.6/win/rc/cursor6e.cur diff --git a/tk8.6/win/rc/cursor70.cur b/tk8.6/win/rc/cursor70.cur Binary files differnew file mode 100644 index 0000000..e2d7673 --- /dev/null +++ b/tk8.6/win/rc/cursor70.cur diff --git a/tk8.6/win/rc/cursor72.cur b/tk8.6/win/rc/cursor72.cur Binary files differnew file mode 100644 index 0000000..471bcf0 --- /dev/null +++ b/tk8.6/win/rc/cursor72.cur diff --git a/tk8.6/win/rc/cursor74.cur b/tk8.6/win/rc/cursor74.cur Binary files differnew file mode 100644 index 0000000..176d2b0 --- /dev/null +++ b/tk8.6/win/rc/cursor74.cur diff --git a/tk8.6/win/rc/cursor76.cur b/tk8.6/win/rc/cursor76.cur Binary files differnew file mode 100644 index 0000000..34f402a --- /dev/null +++ b/tk8.6/win/rc/cursor76.cur diff --git a/tk8.6/win/rc/cursor78.cur b/tk8.6/win/rc/cursor78.cur Binary files differnew file mode 100644 index 0000000..70e25dd --- /dev/null +++ b/tk8.6/win/rc/cursor78.cur diff --git a/tk8.6/win/rc/cursor7a.cur b/tk8.6/win/rc/cursor7a.cur Binary files differnew file mode 100644 index 0000000..5ea95c4 --- /dev/null +++ b/tk8.6/win/rc/cursor7a.cur diff --git a/tk8.6/win/rc/cursor7c.cur b/tk8.6/win/rc/cursor7c.cur Binary files differnew file mode 100644 index 0000000..38036ab --- /dev/null +++ b/tk8.6/win/rc/cursor7c.cur diff --git a/tk8.6/win/rc/cursor7e.cur b/tk8.6/win/rc/cursor7e.cur Binary files differnew file mode 100644 index 0000000..4b24e50 --- /dev/null +++ b/tk8.6/win/rc/cursor7e.cur diff --git a/tk8.6/win/rc/cursor80.cur b/tk8.6/win/rc/cursor80.cur Binary files differnew file mode 100644 index 0000000..a3955a5 --- /dev/null +++ b/tk8.6/win/rc/cursor80.cur diff --git a/tk8.6/win/rc/cursor82.cur b/tk8.6/win/rc/cursor82.cur Binary files differnew file mode 100644 index 0000000..984cfba --- /dev/null +++ b/tk8.6/win/rc/cursor82.cur diff --git a/tk8.6/win/rc/cursor84.cur b/tk8.6/win/rc/cursor84.cur Binary files differnew file mode 100644 index 0000000..cd6807e --- /dev/null +++ b/tk8.6/win/rc/cursor84.cur diff --git a/tk8.6/win/rc/cursor86.cur b/tk8.6/win/rc/cursor86.cur Binary files differnew file mode 100644 index 0000000..52cb5c3 --- /dev/null +++ b/tk8.6/win/rc/cursor86.cur diff --git a/tk8.6/win/rc/cursor88.cur b/tk8.6/win/rc/cursor88.cur Binary files differnew file mode 100644 index 0000000..fdba0d6 --- /dev/null +++ b/tk8.6/win/rc/cursor88.cur diff --git a/tk8.6/win/rc/cursor8a.cur b/tk8.6/win/rc/cursor8a.cur Binary files differnew file mode 100644 index 0000000..369e71f --- /dev/null +++ b/tk8.6/win/rc/cursor8a.cur diff --git a/tk8.6/win/rc/cursor8c.cur b/tk8.6/win/rc/cursor8c.cur Binary files differnew file mode 100644 index 0000000..811a0c3 --- /dev/null +++ b/tk8.6/win/rc/cursor8c.cur diff --git a/tk8.6/win/rc/cursor8e.cur b/tk8.6/win/rc/cursor8e.cur Binary files differnew file mode 100644 index 0000000..a500a38 --- /dev/null +++ b/tk8.6/win/rc/cursor8e.cur diff --git a/tk8.6/win/rc/cursor90.cur b/tk8.6/win/rc/cursor90.cur Binary files differnew file mode 100644 index 0000000..3c655d5 --- /dev/null +++ b/tk8.6/win/rc/cursor90.cur diff --git a/tk8.6/win/rc/cursor92.cur b/tk8.6/win/rc/cursor92.cur Binary files differnew file mode 100644 index 0000000..4364b5d --- /dev/null +++ b/tk8.6/win/rc/cursor92.cur diff --git a/tk8.6/win/rc/cursor94.cur b/tk8.6/win/rc/cursor94.cur Binary files differnew file mode 100644 index 0000000..16e6b61 --- /dev/null +++ b/tk8.6/win/rc/cursor94.cur diff --git a/tk8.6/win/rc/cursor96.cur b/tk8.6/win/rc/cursor96.cur Binary files differnew file mode 100644 index 0000000..cecaea3 --- /dev/null +++ b/tk8.6/win/rc/cursor96.cur diff --git a/tk8.6/win/rc/cursor98.cur b/tk8.6/win/rc/cursor98.cur Binary files differnew file mode 100644 index 0000000..5cab68e --- /dev/null +++ b/tk8.6/win/rc/cursor98.cur diff --git a/tk8.6/win/rc/cursor9a.cur b/tk8.6/win/rc/cursor9a.cur Binary files differnew file mode 100644 index 0000000..048f06b --- /dev/null +++ b/tk8.6/win/rc/cursor9a.cur diff --git a/tk8.6/win/rc/lamp.bmp b/tk8.6/win/rc/lamp.bmp Binary files differnew file mode 100644 index 0000000..1e2f9d4 --- /dev/null +++ b/tk8.6/win/rc/lamp.bmp diff --git a/tk8.6/win/rc/tk.ico b/tk8.6/win/rc/tk.ico Binary files differnew file mode 100644 index 0000000..e254318 --- /dev/null +++ b/tk8.6/win/rc/tk.ico diff --git a/tk8.6/win/rc/tk.rc b/tk8.6/win/rc/tk.rc new file mode 100644 index 0000000..6a74be3 --- /dev/null +++ b/tk8.6/win/rc/tk.rc @@ -0,0 +1,74 @@ +// +// Version Resource Script +// + +#include <windows.h> +#include <tk.h> + +// +// build-up the name suffix that defines the type of build this is. +// +#if TCL_THREADS +#define SUFFIX_THREADS "t" +#else +#define SUFFIX_THREADS "" +#endif + +#if DEBUG && !UNCHECKED +#define SUFFIX_DEBUG "g" +#else +#define SUFFIX_DEBUG "" +#endif + +#define SUFFIX SUFFIX_THREADS SUFFIX_DEBUG + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL + PRODUCTVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL + FILEFLAGSMASK 0x3fL +#ifdef DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_DLL + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Tk DLL\0" + VALUE "OriginalFilename", "tk" STRINGIFY(TK_MAJOR_VERSION) STRINGIFY(TK_MINOR_VERSION) SUFFIX ".dll\0" + VALUE "CompanyName", "ActiveState Corporation\0" + VALUE "FileVersion", TK_PATCH_LEVEL + VALUE "LegalCopyright", "Copyright \251 2001 by ActiveState Corporation, et al\0" + VALUE "ProductName", "Tk " TK_VERSION " for Windows\0" + VALUE "ProductVersion", TK_PATCH_LEVEL + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +// +// Include the base resources. +// + +#include "tk_base.rc" + +// +// This enables themed scrollbars in XP by trying to use comctl32 v6. +// + +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "wish.exe.manifest" diff --git a/tk8.6/win/rc/tk_base.rc b/tk8.6/win/rc/tk_base.rc new file mode 100644 index 0000000..e6ab016 --- /dev/null +++ b/tk8.6/win/rc/tk_base.rc @@ -0,0 +1,140 @@ +// +// Base resources needed by Tk whether it's a DLL or a static library. +// + +#include <windows.h> + +// +// Tk Icon +// +// The BASE_NO_TK_ICON symbol can be defined to avoid +// creating an icon named "tk" in this resource file. +// The user can then create another icon named tk in +// another resource file and link both resource files. +// Tk will then use the custom icon instead of tk.ico. + +#ifndef BASE_NO_TK_ICON +tk ICON DISCARDABLE "tk.ico" +#endif + +#include <dlgs.h> + +FILEOPENORD DIALOG DISCARDABLE 36, 24, 218, 138 +STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Choose Directory" +FONT 8, "Helv" +BEGIN + LTEXT "Directory &name:",-1,8,6,118,9 + EDITTEXT edt10,8,26,144,12, WS_TABSTOP | ES_AUTOHSCROLL + LISTBOX lst2,8,40,144,64,LBS_SORT | LBS_OWNERDRAWFIXED | + LBS_HASSTRINGS | LBS_NOINTEGRALHEIGHT | + LBS_DISABLENOSCROLL | WS_VSCROLL | WS_TABSTOP + LTEXT "Dri&ves:",stc4,8,106,92,9 + COMBOBOX cmb2,8,115,144,68,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | + CBS_AUTOHSCROLL | CBS_SORT | CBS_HASSTRINGS | WS_BORDER | + WS_VSCROLL | WS_TABSTOP + DEFPUSHBUTTON "OK",1,160,6,50,14,WS_GROUP + PUSHBUTTON "Cancel",2,160,24,50,14,WS_GROUP + PUSHBUTTON "&Help",psh15,160,42,50,14,WS_GROUP + CHECKBOX "&Read only",chx1,160,66,50,12,WS_GROUP + PUSHBUTTON "Net&work...",psh14,160,115,50,14,WS_GROUP + + LTEXT "a",stc3,9,143,114,15 + EDITTEXT edt1,7,158,135,20,NOT WS_TABSTOP + LISTBOX lst1,8,205,134,42,LBS_NOINTEGRALHEIGHT + COMBOBOX cmb1,8,253,135,21,CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | + CBS_AUTOHSCROLL | CBS_SORT | CBS_HASSTRINGS | WS_BORDER | + WS_VSCROLL + +END + + +// +// Bitmaps +// + +buttons BITMAP DISCARDABLE "buttons.bmp" + +// +// Cursors +// + +X_cursor CURSOR DISCARDABLE "cursor00.cur" +arrow CURSOR DISCARDABLE "cursor02.cur" +based_arrow_down CURSOR DISCARDABLE "cursor04.cur" +based_arrow_up CURSOR DISCARDABLE "cursor06.cur" +boat CURSOR DISCARDABLE "cursor08.cur" +bogosity CURSOR DISCARDABLE "cursor0a.cur" +bottom_left_corner CURSOR DISCARDABLE "cursor0c.cur" +bottom_right_corner CURSOR DISCARDABLE "cursor0e.cur" +bottom_side CURSOR DISCARDABLE "cursor10.cur" +bottom_tee CURSOR DISCARDABLE "cursor12.cur" +box_spiral CURSOR DISCARDABLE "cursor14.cur" +center_ptr CURSOR DISCARDABLE "cursor16.cur" +circle CURSOR DISCARDABLE "cursor18.cur" +clock CURSOR DISCARDABLE "cursor1a.cur" +coffee_mug CURSOR DISCARDABLE "cursor1c.cur" +cross CURSOR DISCARDABLE "cursor1e.cur" +cross_reverse CURSOR DISCARDABLE "cursor20.cur" +crosshair CURSOR DISCARDABLE "cursor22.cur" +diamond_cross CURSOR DISCARDABLE "cursor24.cur" +dot CURSOR DISCARDABLE "cursor26.cur" +dotbox CURSOR DISCARDABLE "cursor28.cur" +double_arrow CURSOR DISCARDABLE "cursor2a.cur" +draft_large CURSOR DISCARDABLE "cursor2c.cur" +draft_small CURSOR DISCARDABLE "cursor2e.cur" +draped_box CURSOR DISCARDABLE "cursor30.cur" +exchange CURSOR DISCARDABLE "cursor32.cur" +fleur CURSOR DISCARDABLE "cursor34.cur" +gobbler CURSOR DISCARDABLE "cursor36.cur" +gumby CURSOR DISCARDABLE "cursor38.cur" +hand1 CURSOR DISCARDABLE "cursor3a.cur" +hand2 CURSOR DISCARDABLE "cursor3c.cur" +heart CURSOR DISCARDABLE "cursor3e.cur" +icon CURSOR DISCARDABLE "cursor40.cur" +iron_cross CURSOR DISCARDABLE "cursor42.cur" +left_ptr CURSOR DISCARDABLE "cursor44.cur" +left_side CURSOR DISCARDABLE "cursor46.cur" +left_tee CURSOR DISCARDABLE "cursor48.cur" +leftbutton CURSOR DISCARDABLE "cursor4a.cur" +ll_angle CURSOR DISCARDABLE "cursor4c.cur" +lr_angle CURSOR DISCARDABLE "cursor4e.cur" +man CURSOR DISCARDABLE "cursor50.cur" +middlebutton CURSOR DISCARDABLE "cursor52.cur" +mouse CURSOR DISCARDABLE "cursor54.cur" +pencil CURSOR DISCARDABLE "cursor56.cur" +pirate CURSOR DISCARDABLE "cursor58.cur" +plus CURSOR DISCARDABLE "cursor5a.cur" +question_arrow CURSOR DISCARDABLE "cursor5c.cur" +right_ptr CURSOR DISCARDABLE "cursor5e.cur" +right_side CURSOR DISCARDABLE "cursor60.cur" +right_tee CURSOR DISCARDABLE "cursor62.cur" +rightbutton CURSOR DISCARDABLE "cursor64.cur" +rtl_logo CURSOR DISCARDABLE "cursor66.cur" +sailboat CURSOR DISCARDABLE "cursor68.cur" +sb_down_arrow CURSOR DISCARDABLE "cursor6a.cur" +sb_h_double_arrow CURSOR DISCARDABLE "cursor6c.cur" +sb_left_arrow CURSOR DISCARDABLE "cursor6e.cur" +sb_right_arrow CURSOR DISCARDABLE "cursor70.cur" +sb_up_arrow CURSOR DISCARDABLE "cursor72.cur" +sb_v_double_arrow CURSOR DISCARDABLE "cursor74.cur" +shuttle CURSOR DISCARDABLE "cursor76.cur" +sizing CURSOR DISCARDABLE "cursor78.cur" +spider CURSOR DISCARDABLE "cursor7a.cur" +spraycan CURSOR DISCARDABLE "cursor7c.cur" +star CURSOR DISCARDABLE "cursor7e.cur" +target CURSOR DISCARDABLE "cursor80.cur" +tcross CURSOR DISCARDABLE "cursor82.cur" +top_left_arrow CURSOR DISCARDABLE "cursor84.cur" +top_left_corner CURSOR DISCARDABLE "cursor86.cur" +top_right_corner CURSOR DISCARDABLE "cursor88.cur" +top_side CURSOR DISCARDABLE "cursor8a.cur" +top_tee CURSOR DISCARDABLE "cursor8c.cur" +trek CURSOR DISCARDABLE "cursor8e.cur" +ul_angle CURSOR DISCARDABLE "cursor90.cur" +umbrella CURSOR DISCARDABLE "cursor92.cur" +ur_angle CURSOR DISCARDABLE "cursor94.cur" +watch CURSOR DISCARDABLE "cursor96.cur" +xterm CURSOR DISCARDABLE "cursor98.cur" +none CURSOR DISCARDABLE "cursor9a.cur" + diff --git a/tk8.6/win/rc/wish.ico b/tk8.6/win/rc/wish.ico Binary files differnew file mode 100644 index 0000000..5801fb8 --- /dev/null +++ b/tk8.6/win/rc/wish.ico diff --git a/tk8.6/win/rc/wish.rc b/tk8.6/win/rc/wish.rc new file mode 100644 index 0000000..53e02fa --- /dev/null +++ b/tk8.6/win/rc/wish.rc @@ -0,0 +1,87 @@ +// +// Version Resource Script +// + +#include <windows.h> +#include <tk.h> + +// +// build-up the name suffix that defines the type of build this is. +// +#if TCL_THREADS +#define SUFFIX_THREADS "t" +#else +#define SUFFIX_THREADS "" +#endif + +#if STATIC_BUILD +#define SUFFIX_STATIC "s" +#else +#define SUFFIX_STATIC "" +#endif + +#if DEBUG && !UNCHECKED +#define SUFFIX_DEBUG "g" +#else +#define SUFFIX_DEBUG "" +#endif + +#define SUFFIX SUFFIX_THREADS SUFFIX_STATIC SUFFIX_DEBUG + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL + PRODUCTVERSION TK_MAJOR_VERSION,TK_MINOR_VERSION,TK_RELEASE_LEVEL,TK_RELEASE_SERIAL + FILEFLAGSMASK 0x3fL +#ifdef DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "Wish Application\0" + VALUE "OriginalFilename", "wish" STRINGIFY(TK_MAJOR_VERSION) STRINGIFY(TK_MINOR_VERSION) SUFFIX ".exe\0" + VALUE "CompanyName", "ActiveState Corporation\0" + VALUE "FileVersion", TK_PATCH_LEVEL + VALUE "LegalCopyright", "Copyright \251 2000 by ActiveState Corporation, et al\0" + VALUE "ProductName", "Tk " TK_VERSION " for Windows\0" + VALUE "ProductVersion", TK_PATCH_LEVEL + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +// +// Icon +// +// The icon whose name or resource ID is lexigraphically first, is used +// as the application's icon. +// + +app ICON DISCARDABLE "wish.ico" + +#if STATIC_BUILD +#include "tk_base.rc" +#endif + +// +// This enables themed scrollbars in XP by trying to use comctl32 v6. +// + +#ifndef RT_MANIFEST +#define RT_MANIFEST 24 +#endif +#ifndef CREATEPROCESS_MANIFEST_RESOURCE_ID +#define CREATEPROCESS_MANIFEST_RESOURCE_ID 1 +#endif +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "wish.exe.manifest" diff --git a/tk8.6/win/rmd.bat b/tk8.6/win/rmd.bat new file mode 100644 index 0000000..820b76f --- /dev/null +++ b/tk8.6/win/rmd.bat @@ -0,0 +1,20 @@ +@echo off
+
+if not exist %1\nul goto end
+
+echo Removing directory %1
+
+if "%OS%" == "Windows_NT" goto winnt
+
+deltree /y %1
+if errorlevel 1 goto end
+goto success
+
+:winnt
+rmdir /s /q %1
+if errorlevel 1 goto end
+
+:success
+echo Deleted directory %1
+
+:end
diff --git a/tk8.6/win/rules-ext.vc b/tk8.6/win/rules-ext.vc new file mode 100644 index 0000000..ab86876 --- /dev/null +++ b/tk8.6/win/rules-ext.vc @@ -0,0 +1,118 @@ +# This file should only be included in makefiles for Tcl extensions, +# NOT in the makefile for Tcl itself. + +!ifndef _RULES_EXT_VC + +# We need to run from the directory the parent makefile is located in. +# nmake does not tell us what makefile was used to invoke it so parent +# makefile has to set the MAKEFILEVC macro or we just make a guess and +# warn if we think that is not the case. +!if "$(MAKEFILEVC)" == "" + +!if exist("$(PROJECT).vc") +MAKEFILEVC = $(PROJECT).vc +!elseif exist("makefile.vc") +MAKEFILEVC = makefile.vc +!endif +!endif # "$(MAKEFILEVC)" == "" + +!if !exist("$(MAKEFILEVC)") +MSG = ^ +You must run nmake from the directory containing the project makefile.^ +If you are doing that and getting this message, set the MAKEFILEVC^ +macro to the name of the project makefile. +!message WARNING: $(MSG) +!endif + +!if "$(PROJECT)" == "tcl" +!error The rules-ext.vc file is not intended for Tcl itself. +!endif + +# We extract version numbers using the nmakehlp program. For now use +# the local copy of nmakehlp. Once we locate Tcl, we will use that +# one if it is newer. +!if [$(CC) -nologo "nmakehlp.c" -link -subsystem:console > nul] +!endif + +# First locate the Tcl directory that we are working with. +!ifdef TCLDIR + +_RULESDIR = $(TCLDIR:/=\) + +!else + +# If an installation path is specified, that is also the Tcl directory. +# Also Tk never builds against an installed Tcl, it needs Tcl sources +!if defined(INSTALLDIR) && "$(PROJECT)" != "tk" +_RULESDIR=$(INSTALLDIR:/=\) +!else +# Locate Tcl sources +!if [echo _RULESDIR = \> nmakehlp.out] \ + || [nmakehlp -L generic\tcl.h >> nmakehlp.out] +_RULESDIR = ..\..\tcl +!else +!include nmakehlp.out +!endif + +!endif # defined(INSTALLDIR).... + +!endif # ifndef TCLDIR + +# Now look for the targets.vc file under the Tcl root. Note we check this +# file and not rules.vc because the latter also exists on older systems. +!if exist("$(_RULESDIR)\lib\nmake\targets.vc") # Building against installed Tcl +_RULESDIR = $(_RULESDIR)\lib\nmake +!elseif exist("$(_RULESDIR)\win\targets.vc") # Building against Tcl sources +_RULESDIR = $(_RULESDIR)\win +!else +# If we have not located Tcl's targets file, most likely we are compiling +# against an older version of Tcl and so must use our own support files. +_RULESDIR = . +!endif + +!if "$(_RULESDIR)" != "." +# Potentially using Tcl's support files. If this extension has its own +# nmake support files, need to compare the versions and pick newer. + +!if exist("rules.vc") # The extension has its own copy + +!if [echo TCL_RULES_MAJOR = \> versions.vc] \ + && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MAJOR >> versions.vc] +!endif +!if [echo TCL_RULES_MINOR = \>> versions.vc] \ + && [nmakehlp -V "$(_RULESDIR)\rules.vc" RULES_VERSION_MINOR >> versions.vc] +!endif + +!if [echo OUR_RULES_MAJOR = \>> versions.vc] \ + && [nmakehlp -V "rules.vc" RULES_VERSION_MAJOR >> versions.vc] +!endif +!if [echo OUR_RULES_MINOR = \>> versions.vc] \ + && [nmakehlp -V "rules.vc" RULES_VERSION_MINOR >> versions.vc] +!endif +!include versions.vc +# We have a newer version of the support files, use them +!if ($(TCL_RULES_MAJOR) != $(OUR_RULES_MAJOR)) || ($(TCL_RULES_MINOR) < $(OUR_RULES_MINOR)) +_RULESDIR = . +!endif + +!endif # if exist("rules.vc") + +!endif # if $(_RULESDIR) != "." + +# Let rules.vc know what copy of nmakehlp.c to use. +NMAKEHLPC = $(_RULESDIR)\nmakehlp.c + +# Get rid of our internal defines before calling rules.vc +!undef TCL_RULES_MAJOR +!undef TCL_RULES_MINOR +!undef OUR_RULES_MAJOR +!undef OUR_RULES_MINOR + +!if exist("$(_RULESDIR)\rules.vc") +!message *** Using $(_RULESDIR)\rules.vc +!include "$(_RULESDIR)\rules.vc" +!else +!error *** Could not locate rules.vc in $(_RULESDIR) +!endif + +!endif # _RULES_EXT_VC
\ No newline at end of file diff --git a/tk8.6/win/rules.vc b/tk8.6/win/rules.vc new file mode 100644 index 0000000..7fc51c1 --- /dev/null +++ b/tk8.6/win/rules.vc @@ -0,0 +1,1723 @@ +#------------------------------------------------------------- -*- makefile -*-
+# rules.vc --
+#
+# Part of the nmake based build system for Tcl and its extensions.
+# This file does all the hard work in terms of parsing build options,
+# compiler switches, defining common targets and macros. The Tcl makefile
+# directly includes this. Extensions include it via "rules-ext.vc".
+#
+# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for
+# detailed documentation.
+#
+# See the file "license.terms" for information on usage and redistribution
+# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+#
+# Copyright (c) 2001-2003 David Gravereaux.
+# Copyright (c) 2003-2008 Patrick Thoyts
+# Copyright (c) 2017 Ashok P. Nadkarni
+#------------------------------------------------------------------------------
+
+!ifndef _RULES_VC
+_RULES_VC = 1
+
+# The following macros define the version of the rules.vc nmake build system
+# For modifications that are not backward-compatible, you *must* change
+# the major version.
+RULES_VERSION_MAJOR = 1
+RULES_VERSION_MINOR = 0
+
+# The PROJECT macro must be defined by parent makefile.
+!if "$(PROJECT)" == ""
+!error *** Error: Macro PROJECT not defined! Please define it before including rules.vc
+!endif
+
+!if "$(PRJ_PACKAGE_TCLNAME)" == ""
+PRJ_PACKAGE_TCLNAME = $(PROJECT)
+!endif
+
+# Also special case Tcl and Tk to save some typing later
+DOING_TCL = 0
+DOING_TK = 0
+!if "$(PROJECT)" == "tcl"
+DOING_TCL = 1
+!elseif "$(PROJECT)" == "tk"
+DOING_TK = 1
+!endif
+
+!ifndef NEED_TK
+# Backwards compatibility
+!ifdef PROJECT_REQUIRES_TK
+NEED_TK = $(PROJECT_REQUIRES_TK)
+!else
+NEED_TK = 0
+!endif
+!endif
+
+!ifndef NEED_TCL_SOURCE
+NEED_TCL_SOURCE = 0
+!endif
+
+!ifdef NEED_TK_SOURCE
+!if $(NEED_TK_SOURCE)
+NEED_TK = 1
+!endif
+!else
+NEED_TK_SOURCE = 0
+!endif
+
+################################################################
+# Nmake is a pretty weak environment in syntax and capabilities
+# so this file is necessarily verbose. It's broken down into
+# the following parts.
+#
+# 0. Sanity check that compiler environment is set up and initialize
+# any built-in settings from the parent makefile
+# 1. First define the external tools used for compiling, copying etc.
+# as this is independent of everything else.
+# 2. Figure out our build structure in terms of the directory, whether
+# we are building Tcl or an extension, etc.
+# 3. Determine the compiler and linker versions
+# 4. Build the nmakehlp helper application
+# 5. Determine the supported compiler options and features
+# 6. Parse the OPTS macro value for user-specified build configuration
+# 7. Parse the STATS macro value for statistics instrumentation
+# 8. Parse the CHECKS macro for additional compilation checks
+# 9. Extract Tcl, and possibly Tk, version numbers from the headers
+# 10. Based on this selected configuration, construct the output
+# directory and file paths
+# 11. Construct the paths where the package is to be installed
+# 12. Set up the actual options passed to compiler and linker based
+# on the information gathered above.
+# 13. Define some standard build targets and implicit rules. These may
+# be optionally disabled by the parent makefile.
+# 14. (For extensions only.) Compare the configuration of the target
+# Tcl and the extensions and warn against discrepancies.
+#
+# One final note about the macro names used. They are as they are
+# for historical reasons. We would like legacy extensions to
+# continue to work with this make include file so be wary of
+# changing them for consistency or clarity.
+
+# 0. Sanity check compiler environment
+
+# Check to see we are configured to build with MSVC (MSDEVDIR, MSVCDIR or
+# VCINSTALLDIR) or with the MS Platform SDK (MSSDK or WindowsSDKDir)
+
+!if !defined(MSDEVDIR) && !defined(MSVCDIR) && !defined(VCINSTALLDIR) && !defined(MSSDK) && !defined(WINDOWSSDKDIR)
+MSG = ^
+Visual C++ compiler environment not initialized.
+!error $(MSG)
+!endif
+
+# We need to run from the directory the parent makefile is located in.
+# nmake does not tell us what makefile was used to invoke it so parent
+# makefile has to set the MAKEFILEVC macro or we just make a guess and
+# warn if we think that is not the case.
+!if "$(MAKEFILEVC)" == ""
+
+!if exist("$(PROJECT).vc")
+MAKEFILEVC = $(PROJECT).vc
+!elseif exist("makefile.vc")
+MAKEFILEVC = makefile.vc
+!endif
+!endif # "$(MAKEFILEVC)" == ""
+
+!if !exist("$(MAKEFILEVC)")
+MSG = ^
+You must run nmake from the directory containing the project makefile.^
+If you are doing that and getting this message, set the MAKEFILEVC^
+macro to the name of the project makefile.
+!message WARNING: $(MSG)
+!endif
+
+
+################################################################
+# 1. Define external programs being used
+
+#----------------------------------------------------------
+# Set the proper copy method to avoid overwrite questions
+# to the user when copying files and selecting the right
+# "delete all" method.
+#----------------------------------------------------------
+
+RMDIR = rmdir /S /Q
+CPY = xcopy /i /y >NUL
+CPYDIR = xcopy /e /i /y >NUL
+COPY = copy /y >NUL
+MKDIR = mkdir
+
+######################################################################
+# 2. Figure out our build environment in terms of what we're building.
+#
+# (a) Tcl itself
+# (b) Tk
+# (c) a Tcl extension using libraries/includes from an *installed* Tcl
+# (d) a Tcl extension using libraries/includes from Tcl source directory
+#
+# This last is needed because some extensions still need
+# some Tcl interfaces that are not publicly exposed.
+#
+# The fragment will set the following macros:
+# ROOT - root of this module sources
+# COMPATDIR - source directory that holds compatibility sources
+# DOCDIR - source directory containing documentation files
+# GENERICDIR - platform-independent source directory
+# WINDIR - Windows-specific source directory
+# TESTDIR - directory containing test files
+# TOOLSDIR - directory containing build tools
+# _TCLDIR - root of the Tcl installation OR the Tcl sources. Not set
+# when building Tcl itself.
+# _INSTALLDIR - native form of the installation path. For Tcl
+# this will be the root of the Tcl installation. For extensions
+# this will be the lib directory under the root.
+# TCLINSTALL - set to 1 if _TCLDIR refers to
+# headers and libraries from an installed Tcl, and 0 if built against
+# Tcl sources. Not set when building Tcl itself. Yes, not very well
+# named.
+# _TCL_H - native path to the tcl.h file
+#
+# If Tk is involved, also sets the following
+# _TKDIR - native form Tk installation OR Tk source. Not set if building
+# Tk itself.
+# TKINSTALL - set 1 if _TKDIR refers to installed Tk and 0 if Tk sources
+# _TK_H - native path to the tk.h file
+
+# Root directory for sources and assumed subdirectories
+ROOT = $(MAKEDIR)\..
+# The following paths CANNOT have spaces in them as they appear on the
+# left side of implicit rules.
+!ifndef COMPATDIR
+COMPATDIR = $(ROOT)\compat
+!endif
+!ifndef DOCDIR
+DOCDIR = $(ROOT)\doc
+!endif
+!ifndef GENERICDIR
+GENERICDIR = $(ROOT)\generic
+!endif
+!ifndef TOOLSDIR
+TOOLSDIR = $(ROOT)\tools
+!endif
+!ifndef TESTDIR
+TESTDIR = $(ROOT)\tests
+!endif
+!ifndef LIBDIR
+!if exist("$(ROOT)\library")
+LIBDIR = $(ROOT)\library
+!else
+LIBDIR = $(ROOT)\lib
+!endif
+!endif
+!ifndef DEMODIR
+!if exist("$(LIBDIR)\demos")
+DEMODIR = $(LIBDIR)\demos
+!else
+DEMODIR = $(ROOT)\demos
+!endif
+!endif # ifndef DEMODIR
+# Do NOT enclose WINDIR in a !ifndef because Windows always defines
+# WINDIR env var to point to c:\windows!
+# TBD - This is a potentially dangerous conflict, rename WINDIR to
+# something else
+WINDIR = $(ROOT)\win
+
+!ifndef RCDIR
+!if exist("$(WINDIR)\rc")
+RCDIR = $(WINDIR)\rc
+!else
+RCDIR = $(WINDIR)
+!endif
+!endif
+RCDIR = $(RCDIR:/=\)
+
+# The target directory where the built packages and binaries will be installed.
+# INSTALLDIR is the (optional) path specified by the user.
+# _INSTALLDIR is INSTALLDIR using the backslash separator syntax
+!ifdef INSTALLDIR
+### Fix the path separators.
+_INSTALLDIR = $(INSTALLDIR:/=\)
+!else
+### Assume the normal default.
+_INSTALLDIR = $(HOMEDRIVE)\Tcl
+!endif
+
+!if $(DOING_TCL)
+
+# BEGIN Case 2(a) - Building Tcl itself
+
+# Only need to define _TCL_H
+_TCL_H = ..\generic\tcl.h
+
+# END Case 2(a) - Building Tcl itself
+
+!elseif $(DOING_TK)
+
+# BEGIN Case 2(b) - Building Tk
+
+TCLINSTALL = 0 # Tk always builds against Tcl source, not an installed Tcl
+!if "$(TCLDIR)" == ""
+!if [echo TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+!endif # TCLDIR == ""
+
+_TCLDIR = $(TCLDIR:/=\)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!if !exist("$(_TCL_H)")
+!error Could not locate tcl.h. Please set the TCLDIR macro to point to the Tcl *source* directory.
+!endif
+
+_TK_H = ..\generic\tk.h
+
+# END Case 2(b) - Building Tk
+
+!else
+
+# BEGIN Case 2(c) or (d) - Building an extension other than Tk
+
+# If command line has specified Tcl location through TCLDIR, use it
+# else default to the INSTALLDIR setting
+!if "$(TCLDIR)" != ""
+
+_TCLDIR = $(TCLDIR:/=\)
+!if exist("$(_TCLDIR)\include\tcl.h") # Case 2(c) with TCLDIR defined
+TCLINSTALL = 1
+_TCL_H = $(_TCLDIR)\include\tcl.h
+!elseif exist("$(_TCLDIR)\generic\tcl.h") # Case 2(d) with TCLDIR defined
+TCLINSTALL = 0
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+!endif
+
+!else # # Case 2(c) for extensions with TCLDIR undefined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tcl.h") && !$(NEED_TCL_SOURCE)
+
+TCLINSTALL = 1
+TCLDIR = $(_INSTALLDIR)\..
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TCLDIR = $(_INSTALLDIR)\..
+_TCL_H = $(_TCLDIR)\include\tcl.h
+
+!else # exist(...) && ! $(NEED_TCL_SOURCE)
+
+!if [echo _TCLDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tcl.h >> nmakehlp.out]
+!error *** Could not locate Tcl source directory.
+!endif
+!include nmakehlp.out
+TCLINSTALL = 0
+TCLDIR = $(_TCLDIR)
+_TCL_H = $(_TCLDIR)\generic\tcl.h
+
+!endif # exist(...) && ! $(NEED_TCL_SOURCE)
+
+!endif # TCLDIR
+
+!ifndef _TCL_H
+MSG =^
+Failed to find tcl.h. The TCLDIR macro is set incorrectly or is not set and default path does not contain tcl.h.
+!error $(MSG)
+!endif
+
+# Now do the same to locate Tk headers and libs if project requires Tk
+!if $(NEED_TK)
+
+!if "$(TKDIR)" != ""
+
+_TKDIR = $(TKDIR:/=\)
+!if exist("$(_TKDIR)\include\tk.h")
+TKINSTALL = 1
+_TK_H = $(_TKDIR)\include\tk.h
+!elseif exist("$(_TKDIR)\generic\tk.h")
+TKINSTALL = 0
+_TK_H = $(_TKDIR)\generic\tk.h
+!endif
+
+!else # TKDIR not defined
+
+# Need to locate Tcl depending on whether it needs Tcl source or not.
+# If we don't, check the INSTALLDIR for an installed Tcl first
+
+!if exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+TKINSTALL = 1
+# NOTE: we will be resetting _INSTALLDIR to _INSTALLDIR/lib for extensions
+# later so the \.. accounts for the /lib
+_TKDIR = $(_INSTALLDIR)\..
+_TK_H = $(_TKDIR)\include\tk.h
+TKDIR = $(_TKDIR)
+
+!else # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!if [echo _TKDIR = \> nmakehlp.out] \
+ || [nmakehlp -L generic\tk.h >> nmakehlp.out]
+!error *** Could not locate Tk source directory.
+!endif
+!include nmakehlp.out
+TKINSTALL = 0
+TKDIR = $(_TKDIR)
+_TK_H = $(_TKDIR)\generic\tk.h
+
+!endif # exist("$(_INSTALLDIR)\include\tk.h") && !$(NEED_TK_SOURCE)
+
+!endif # TKDIR
+
+!ifndef _TK_H
+MSG =^
+Failed to find tk.h. The TKDIR macro is set incorrectly or is not set and default path does not contain tk.h.
+!error $(MSG)
+!endif
+
+!endif # NEED_TK
+
+!if $(NEED_TCL_SOURCE) && $(TCLINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tcl.^
+*** Please set the TCLDIR macro to point to the Tcl sources.
+!error $(MSG)
+!endif
+
+!if $(NEED_TK_SOURCE)
+!if $(TKINSTALL)
+MSG = ^
+*** Warning: This extension requires the source distribution of Tk.^
+*** Please set the TKDIR macro to point to the Tk sources.
+!error $(MSG)
+!endif
+!endif
+
+
+# If INSTALLDIR set to tcl installation root dir then reset to the
+# lib dir for installing extensions
+!if exist("$(_INSTALLDIR)\include\tcl.h")
+_INSTALLDIR=$(_INSTALLDIR)\lib
+!endif
+
+# END Case 2(c) or (d) - Building an extension
+!endif # if $(DOING_TCL)
+
+################################################################
+# 3. Determine compiler version and architecture
+# In this section, we figure out the compiler version and the
+# architecture for which we are building. This sets the
+# following macros:
+# VCVERSION - the internal compiler version as 1200, 1400, 1910 etc.
+# This is also printed by the compiler in dotted form 19.10 etc.
+# VCVER - the "marketing version", for example Visual C++ 6 for internal
+# compiler version 1200. This is kept only for legacy reasons as it
+# does not make sense for recent Microsoft compilers. Only used for
+# output directory names.
+# ARCH - set to IX86 or AMD64 depending on 32- or 64-bit target
+# NATIVE_ARCH - set to IX86 or AMD64 for the host machine
+# MACHINE - same as $(ARCH) - legacy
+# _VC_MANIFEST_EMBED_{DLL,EXE} - commands for embedding a manifest if needed
+# CFG_ENCODING - set to an character encoding.
+# TBD - this is passed to compiler as TCL_CFGVAL_ENCODING but can't
+# see where it is used
+
+cc32 = $(CC) # built-in default.
+link32 = link
+lib32 = lib
+rc32 = $(RC) # built-in default.
+
+#----------------------------------------------------------------
+# Figure out the compiler architecture and version by writing
+# the C macros to a file, preprocessing them with the C
+# preprocessor and reading back the created file
+
+_HASH=^#
+_VC_MANIFEST_EMBED_EXE=
+_VC_MANIFEST_EMBED_DLL=
+VCVER=0
+!if ![echo VCVERSION=_MSC_VER > vercl.x] \
+ && ![echo $(_HASH)if defined(_M_IX86) >> vercl.x] \
+ && ![echo ARCH=IX86 >> vercl.x] \
+ && ![echo $(_HASH)elif defined(_M_AMD64) >> vercl.x] \
+ && ![echo ARCH=AMD64 >> vercl.x] \
+ && ![echo $(_HASH)endif >> vercl.x] \
+ && ![$(cc32) -nologo -TC -P vercl.x 2>NUL]
+!include vercl.i
+!if $(VCVERSION) < 1900
+!if ![echo VCVER= ^\> vercl.vc] \
+ && ![set /a $(VCVERSION) / 100 - 6 >> vercl.vc]
+!include vercl.vc
+!endif
+!else
+# The simple calculation above does not apply to new Visual Studio releases
+# Keep the compiler version in its native form.
+VCVER = $(VCVERSION)
+!endif
+!endif
+
+!if ![del 2>NUL /q/f vercl.x vercl.i vercl.vc]
+!endif
+
+#----------------------------------------------------------------
+# The MACHINE macro is used by legacy makefiles so set it as well
+!ifdef MACHINE
+!if "$(MACHINE)" == "x86"
+!undef MACHINE
+MACHINE = IX86
+!elseif "$(MACHINE)" == "x64"
+!undef MACHINE
+MACHINE = AMD64
+!endif
+!if "$(MACHINE)" != "$(ARCH)"
+!error Specified MACHINE macro $(MACHINE) does not match detected target architecture $(ARCH).
+!endif
+!else
+MACHINE=$(ARCH)
+!endif
+
+#------------------------------------------------------------
+# Figure out the *host* architecture by reading the registry
+
+!if ![reg query HKLM\Hardware\Description\System\CentralProcessor\0 /v Identifier | findstr /i x86]
+NATIVE_ARCH=IX86
+!else
+NATIVE_ARCH=AMD64
+!endif
+
+# Since MSVC8 we must deal with manifest resources.
+!if $(VCVERSION) >= 1400
+_VC_MANIFEST_EMBED_EXE=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;1
+_VC_MANIFEST_EMBED_DLL=if exist $@.manifest mt -nologo -manifest $@.manifest -outputresource:$@;2
+!endif
+
+!ifndef CFG_ENCODING
+CFG_ENCODING = \"cp1252\"
+!endif
+
+################################################################
+# 4. Build the nmakehlp program
+# This is a helper app we need to overcome nmake's limiting
+# environment. We will call out to it to get various bits of
+# information about supported compiler options etc.
+#
+# Tcl itself will always use the nmakehlp.c program which is
+# in its own source. This is the "master" copy and kept updated.
+#
+# Extensions built against an installed Tcl will use the installed
+# copy of Tcl's nmakehlp.c if there is one and their own version
+# otherwise. In the latter case, they would also be using their own
+# rules.vc. Note that older versions of Tcl do not install nmakehlp.c
+# or rules.vc.
+#
+# Extensions built against Tcl sources will use the one from the Tcl source.
+#
+# When building an extension using a sufficiently new version of Tcl,
+# rules-ext.vc will define NMAKEHLPC appropriately to point to the
+# copy of nmakehlp.c to be used.
+
+!ifndef NMAKEHLPC
+# Default to the one in the current directory (the extension's own nmakehlp.c)
+NMAKEHLPC = nmakehlp.c
+
+!if !$(DOING_TCL)
+!if $(TCLINSTALL)
+!if exist("$(_TCLDIR)\lib\nmake\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\lib\nmake\nmakehlp.c
+!endif
+!else # ! $(TCLINSTALL)
+!if exist("$(_TCLDIR)\win\nmakehlp.c")
+NMAKEHLPC = $(_TCLDIR)\win\nmakehlp.c
+!endif
+!endif # $(TCLINSTALL)
+!endif # !$(DOING_TCL)
+
+!endif # NMAKEHLPC
+
+# We always build nmakehlp even if it exists since we do not know
+# what source it was built from.
+!message *** Using $(NMAKEHLPC)
+!if [$(cc32) -nologo "$(NMAKEHLPC)" -link -subsystem:console > nul]
+!endif
+
+################################################################
+# 5. Test for compiler features
+# Visual C++ compiler options have changed over the years. Check
+# which options are supported by the compiler in use.
+#
+# The following macros are set:
+# OPTIMIZATIONS - the compiler flags to be used for optimized builds
+# DEBUGFLAGS - the compiler flags to be used for debug builds
+# LINKERFLAGS - Flags passed to the linker
+#
+# Note that these are the compiler settings *available*, not those
+# that will be *used*. The latter depends on the OPTS macro settings
+# which we have not yet parsed.
+#
+# Also note that some of the flags in OPTIMIZATIONS are not really
+# related to optimization. They are placed there only for legacy reasons
+# as some extensions expect them to be included in that macro.
+
+# -Op improves float consistency. Note only needed for older compilers
+# Newer compilers do not need or support this option.
+!if [nmakehlp -c -Op]
+FPOPTS = -Op
+!endif
+
+# Strict floating point semantics - present in newer compilers in lieu of -Op
+!if [nmakehlp -c -fp:strict]
+FPOPTS = $(FPOPTS) -fp:strict
+!endif
+
+!if "$(MACHINE)" == "IX86"
+### test for pentium errata
+!if [nmakehlp -c -QI0f]
+!message *** Compiler has 'Pentium 0x0f fix'
+FPOPTS = $(FPOPTS) -QI0f
+!else
+!message *** Compiler does not have 'Pentium 0x0f fix'
+!endif
+!endif
+
+### test for optimizations
+# /O2 optimization includes /Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy as per
+# documentation. Note we do NOT want /Gs as that inserts a _chkstk
+# stack probe at *every* function entry, not just those with more than
+# a page of stack allocation resulting in a performance hit. However,
+# /O2 documentation is misleading as its stack probes are simply the
+# default page size locals allocation probes and not what is implied
+# by an explicit /Gs option.
+
+OPTIMIZATIONS = $(FPOPTS)
+
+!if [nmakehlp -c -O2]
+!message *** Compiler has 'Optimizations'
+OPTIMIZING = 1
+OPTIMIZATIONS = $(OPTIMIZATIONS) -O2
+!else
+# Legacy, really. All modern compilers support this
+!message *** Compiler does not have 'Optimizations'
+OPTIMIZING = 0
+!endif
+
+# Checks for buffer overflows in local arrays
+!if [nmakehlp -c -GS]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -GS
+!endif
+
+# Link time optimization. Note that this option (potentially) makes
+# generated libraries only usable by the specific VC++ version that
+# created it. Requires /LTCG linker option
+!if [nmakehlp -c -GL]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -GL
+CC_GL_OPT_ENABLED = 1
+!else
+# In newer compilers -GL and -YX are incompatible.
+!if [nmakehlp -c -YX]
+OPTIMIZATIONS = $(OPTIMIZATIONS) -YX
+!endif
+!endif # [nmakehlp -c -GL]
+
+DEBUGFLAGS = $(FPOPTS)
+
+# Run time error checks. Not available or valid in a release, non-debug build
+# RTC is for modern compilers, -GZ is legacy
+!if [nmakehlp -c -RTC1]
+DEBUGFLAGS = $(DEBUGFLAGS) -RTC1
+!elseif [nmakehlp -c -GZ]
+DEBUGFLAGS = $(DEBUGFLAGS) -GZ
+!endif
+
+#----------------------------------------------------------------
+# Linker flags
+
+# LINKER_TESTFLAGS are for internal use when we call nmakehlp to test
+# if the linker supports a specific option. Without these flags link will
+# return "LNK1561: entry point must be defined" error compiling from VS-IDE:
+# They are not passed through to the actual application / extension
+# link rules.
+!ifndef LINKER_TESTFLAGS
+LINKER_TESTFLAGS = /DLL /NOENTRY /OUT:nmakehlp.out
+!endif
+
+LINKERFLAGS =
+
+# If compiler has enabled link time optimization, linker must too with -ltcg
+!ifdef CC_GL_OPT_ENABLED
+!if [nmakehlp -l -ltcg $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS) -ltcg
+!endif
+!endif
+
+########################################################################
+# 6. Parse the OPTS macro to work out the requested build configuration.
+# Based on this, we will construct the actual switches to be passed to the
+# compiler and linker using the macros defined in the previous section.
+# The following macros are defined by this section based on OPTS
+# STATIC_BUILD - 0 -> Tcl is to be built as a shared library
+# 1 -> build as a static library and shell
+# TCL_THREADS - legacy but always 1 on Windows since winsock requires it.
+# DEBUG - 1 -> debug build, 0 -> release builds
+# SYMBOLS - 1 -> generate PDB's, 0 -> no PDB's
+# PROFILE - 1 -> generate profiling info, 0 -> no profiling
+# PGO - 1 -> profile based optimization, 0 -> no
+# MSVCRT - 1 -> link to dynamic C runtime even when building static Tcl build
+# 0 -> link to static C runtime for static Tcl build.
+# Does not impact shared Tcl builds (STATIC_BUILD == 0)
+# TCL_USE_STATIC_PACKAGES - 1 -> statically link the registry and dde extensions
+# in the Tcl shell. 0 -> keep them as shared libraries
+# Does not impact shared Tcl builds.
+# USE_THREAD_ALLOC - 1 -> Use a shared global free pool for allocation.
+# 0 -> Use the non-thread allocator.
+# UNCHECKED - 1 -> when doing a debug build with symbols, use the release
+# C runtime, 0 -> use the debug C runtime.
+# USE_STUBS - 1 -> compile to use stubs interfaces, 0 -> direct linking
+# CONFIG_CHECK - 1 -> check current build configuration against Tcl
+# configuration (ignored for Tcl itself)
+# Further, LINKERFLAGS are modified based on above.
+
+# Default values for all the above
+STATIC_BUILD = 0
+TCL_THREADS = 1
+DEBUG = 0
+SYMBOLS = 0
+PROFILE = 0
+PGO = 0
+MSVCRT = 1
+TCL_USE_STATIC_PACKAGES = 0
+USE_THREAD_ALLOC = 1
+UNCHECKED = 0
+CONFIG_CHECK = 1
+!if $(DOING_TCL)
+USE_STUBS = 0
+!else
+USE_STUBS = 1
+!endif
+
+# If OPTS is not empty AND does not contain "none" which turns off all OPTS
+# set the above macros based on OPTS content
+!if "$(OPTS)" != "" && ![nmakehlp -f "$(OPTS)" "none"]
+
+# OPTS are specified, parse them
+
+!if [nmakehlp -f $(OPTS) "static"]
+!message *** Doing static
+STATIC_BUILD = 1
+!endif
+
+!if [nmakehlp -f $(OPTS) "nostubs"]
+!message *** Not using stubs
+USE_STUBS = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "nomsvcrt"]
+!message *** Doing nomsvcrt
+MSVCRT = 0
+!else
+!if [nmakehlp -f $(OPTS) "msvcrt"]
+!message *** Doing msvcrt
+MSVCRT = 1
+!else
+!if !$(STATIC_BUILD)
+MSVCRT = 1
+!else
+MSVCRT = 0
+!endif
+!endif
+!endif # [nmakehlp -f $(OPTS) "nomsvcrt"]
+
+!if [nmakehlp -f $(OPTS) "staticpkg"] && $(STATIC_BUILD)
+!message *** Doing staticpkg
+TCL_USE_STATIC_PACKAGES = 1
+!else
+TCL_USE_STATIC_PACKAGES = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "nothreads"]
+!message *** Compile explicitly for non-threaded tcl
+TCL_THREADS = 0
+USE_THREAD_ALLOC= 0
+!else
+TCL_THREADS = 1
+USE_THREAD_ALLOC= 1
+!endif
+
+!if [nmakehlp -f $(OPTS) "symbols"]
+!message *** Doing symbols
+DEBUG = 1
+!else
+DEBUG = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "pdbs"]
+!message *** Doing pdbs
+SYMBOLS = 1
+!else
+SYMBOLS = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "profile"]
+!message *** Doing profile
+PROFILE = 1
+!else
+PROFILE = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "pgi"]
+!message *** Doing profile guided optimization instrumentation
+PGO = 1
+!elseif [nmakehlp -f $(OPTS) "pgo"]
+!message *** Doing profile guided optimization
+PGO = 2
+!else
+PGO = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "loimpact"]
+!message *** Warning: ignoring option "loimpact" - deprecated on modern Windows.
+!endif
+
+# TBD - should get rid of this option
+!if [nmakehlp -f $(OPTS) "thrdalloc"]
+!message *** Doing thrdalloc
+USE_THREAD_ALLOC = 1
+!endif
+
+!if [nmakehlp -f $(OPTS) "tclalloc"]
+USE_THREAD_ALLOC = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "unchecked"]
+!message *** Doing unchecked
+UNCHECKED = 1
+!else
+UNCHECKED = 0
+!endif
+
+!if [nmakehlp -f $(OPTS) "noconfigcheck"]
+CONFIG_CHECK = 1
+!else
+CONFIG_CHECK = 0
+!endif
+
+!endif # "$(OPTS)" != "" && ... parsing of OPTS
+
+# Set linker flags based on above
+
+!if $(PGO) > 1
+!if [nmakehlp -l -ltcg:pgoptimize $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pgoptimize
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!elseif $(PGO) > 0
+!if [nmakehlp -l -ltcg:pginstrument $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS:-ltcg=) -ltcg:pginstrument
+!else
+MSG=^
+This compiler does not support profile guided optimization.
+!error $(MSG)
+!endif
+!endif
+
+################################################################
+# 7. Parse the STATS macro to configure code instrumentation
+# The following macros are set by this section:
+# TCL_MEM_DEBUG - 1 -> enables memory allocation instrumentation
+# 0 -> disables
+# TCL_COMPILE_DEBUG - 1 -> enables byte compiler logging
+# 0 -> disables
+
+# Default both are off
+TCL_MEM_DEBUG = 0
+TCL_COMPILE_DEBUG = 0
+
+!if "$(STATS)" != "" && ![nmakehlp -f "$(STATS)" "none"]
+
+!if [nmakehlp -f $(STATS) "memdbg"]
+!message *** Doing memdbg
+TCL_MEM_DEBUG = 1
+!else
+TCL_MEM_DEBUG = 0
+!endif
+
+!if [nmakehlp -f $(STATS) "compdbg"]
+!message *** Doing compdbg
+TCL_COMPILE_DEBUG = 1
+!else
+TCL_COMPILE_DEBUG = 0
+!endif
+
+!endif
+
+####################################################################
+# 8. Parse the CHECKS macro to configure additional compiler checks
+# The following macros are set by this section:
+# WARNINGS - compiler switches that control the warnings level
+# TCL_NO_DEPRECATED - 1 -> disable support for deprecated functions
+# 0 -> enable deprecated functions
+
+# Defaults - Permit deprecated functions and warning level 3
+TCL_NO_DEPRECATED = 0
+WARNINGS = -W3
+
+!if "$(CHECKS)" != "" && ![nmakehlp -f "$(CHECKS)" "none"]
+
+!if [nmakehlp -f $(CHECKS) "nodep"]
+!message *** Doing nodep check
+TCL_NO_DEPRECATED = 1
+!endif
+
+!if [nmakehlp -f $(CHECKS) "fullwarn"]
+!message *** Doing full warnings check
+WARNINGS = -W4
+!if [nmakehlp -l -warn:3 $(LINKER_TESTFLAGS)]
+LINKERFLAGS = $(LINKERFLAGS) -warn:3
+!endif
+!endif
+
+!if [nmakehlp -f $(CHECKS) "64bit"] && [nmakehlp -c -Wp64]
+!message *** Doing 64bit portability warnings
+WARNINGS = $(WARNINGS) -Wp64
+!endif
+
+!endif
+
+################################################################
+# 9. Extract various version numbers
+# For Tcl and Tk, version numbers are extracted from tcl.h and tk.h
+# respectively. For extensions, versions are extracted from the
+# configure.in or configure.ac from the TEA configuration if it
+# exists, and unset otherwise.
+# Sets the following macros:
+# TCL_MAJOR_VERSION
+# TCL_MINOR_VERSION
+# TCL_PATCH_LEVEL
+# TCL_VERSION
+# TK_MAJOR_VERSION
+# TK_MINOR_VERSION
+# TK_PATCH_LEVEL
+# TK_VERSION
+# DOTVERSION - set as (for example) 2.5
+# VERSION - set as (for example 25)
+#--------------------------------------------------------------
+
+!if [echo REM = This file is generated from rules.vc > versions.vc]
+!endif
+!if [echo TCL_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TCL_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V "$(_TCL_H)" TCL_PATCH_LEVEL >> versions.vc]
+!endif
+
+!if defined(_TK_H)
+!if [echo TK_MAJOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MAJOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_MINOR_VERSION = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_MINOR_VERSION >> versions.vc]
+!endif
+!if [echo TK_PATCH_LEVEL = \>> versions.vc] \
+ && [nmakehlp -V $(_TK_H) TK_PATCH_LEVEL >> versions.vc]
+!endif
+!endif # _TK_H
+
+!include versions.vc
+
+TCL_VERSION = $(TCL_MAJOR_VERSION)$(TCL_MINOR_VERSION)
+TCL_DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+!if defined(_TK_H)
+TK_VERSION = $(TK_MAJOR_VERSION)$(TK_MINOR_VERSION)
+TK_DOTVERSION = $(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!endif
+
+# Set DOTVERSION and VERSION
+!if $(DOING_TCL)
+
+DOTVERSION = $(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+VERSION = $(TCL_VERSION)
+
+!elseif $(DOING_TK)
+
+DOTVERSION = $(TK_DOTVERSION)
+VERSION = $(TK_VERSION)
+
+!else # Doing a non-Tk extension
+
+# If parent makefile has not defined DOTVERSION, try to get it from TEA
+# first from a configure.in file, and then from configure.ac
+!ifndef DOTVERSION
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.in ^[$(PROJECT)^] >> versions.vc]
+!if [echo DOTVERSION = \> versions.vc] \
+ || [nmakehlp -V $(ROOT)\configure.ac ^[$(PROJECT)^] >> versions.vc]
+!error *** Could not figure out extension version. Please define DOTVERSION in parent makefile before including rules.vc.
+!endif
+!endif
+!include versions.vc
+!endif # DOTVERSION
+VERSION = $(DOTVERSION:.=)
+
+!endif # $(DOING_TCL) ... etc.
+
+################################################################
+# 10. Construct output directory and file paths
+# Figure-out how to name our intermediate and output directories.
+# In order to avoid inadvertent mixing of object files built using
+# different compilers, build configurations etc.,
+#
+# Naming convention (suffixes):
+# t = full thread support.
+# s = static library (as opposed to an import library)
+# g = linked to the debug enabled C run-time.
+# x = special static build when it links to the dynamic C run-time.
+#
+# The following macros are set in this section:
+# SUFX - the suffix to use for binaries based on above naming convention
+# BUILDDIRTOP - the toplevel default output directory
+# is of the form {Release,Debug}[_AMD64][_COMPILERVERSION]
+# TMP_DIR - directory where object files are created
+# OUT_DIR - directory where output executables are created
+# Both TMP_DIR and OUT_DIR are defaulted only if not defined by the
+# parent makefile (or command line). The default values are
+# based on BUILDDIRTOP.
+# STUBPREFIX - name of the stubs library for this project
+# PRJIMPLIB - output path of the generated project import library
+# PRJLIBNAME - name of generated project library
+# PRJLIB - output path of generated project library
+# PRJSTUBLIBNAME - name of the generated project stubs library
+# PRJSTUBLIB - output path of the generated project stubs library
+# RESFILE - output resource file (only if not static build)
+
+SUFX = tsgx
+
+!if $(DEBUG)
+BUILDDIRTOP = Debug
+!else
+BUILDDIRTOP = Release
+!endif
+
+!if "$(MACHINE)" != "IX86"
+BUILDDIRTOP =$(BUILDDIRTOP)_$(MACHINE)
+!endif
+!if $(VCVER) > 6
+BUILDDIRTOP =$(BUILDDIRTOP)_VC$(VCVER)
+!endif
+
+!if !$(DEBUG) || $(DEBUG) && $(UNCHECKED)
+SUFX = $(SUFX:g=)
+!endif
+
+TMP_DIRFULL = .\$(BUILDDIRTOP)\$(PROJECT)_ThreadedDynamicStaticX
+
+!if !$(STATIC_BUILD)
+TMP_DIRFULL = $(TMP_DIRFULL:Static=)
+SUFX = $(SUFX:s=)
+EXT = dll
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX = $(SUFX:x=)
+!else
+TMP_DIRFULL = $(TMP_DIRFULL:Dynamic=)
+EXT = lib
+!if !$(MSVCRT)
+TMP_DIRFULL = $(TMP_DIRFULL:X=)
+SUFX = $(SUFX:x=)
+!endif
+!endif
+
+!if !$(TCL_THREADS)
+TMP_DIRFULL = $(TMP_DIRFULL:Threaded=)
+SUFX = $(SUFX:t=)
+!endif
+
+!ifndef TMP_DIR
+TMP_DIR = $(TMP_DIRFULL)
+!ifndef OUT_DIR
+OUT_DIR = .\$(BUILDDIRTOP)
+!endif
+!else
+!ifndef OUT_DIR
+OUT_DIR = $(TMP_DIR)
+!endif
+!endif
+
+# Relative paths -> absolute
+!if [echo OUT_DIR = \> nmakehlp.out] \
+ || [nmakehlp -Q "$(OUT_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path OUT_DIR=$(OUT_DIR)
+!endif
+!if [echo TMP_DIR = \>> nmakehlp.out] \
+ || [nmakehlp -Q "$(TMP_DIR)" >> nmakehlp.out]
+!error *** Could not fully qualify path TMP_DIR=$(TMP_DIR)
+!endif
+!include nmakehlp.out
+
+# The name of the stubs library for the project being built
+STUBPREFIX = $(PROJECT)stub
+
+# Set up paths to various Tcl executables and libraries needed by extensions
+!if $(DOING_TCL)
+
+TCLSHNAME = $(PROJECT)sh$(TCL_VERSION)$(SUFX).exe
+TCLSH = $(OUT_DIR)\$(TCLSHNAME)
+TCLIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+TCLLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+TCLLIB = $(OUT_DIR)\$(TCLLIBNAME)
+
+TCLSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+TCLSTUBLIB = $(OUT_DIR)\$(TCLSTUBLIBNAME)
+TCL_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)"
+
+!else # ! $(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe
+!if !exist("$(TCLSH)") && $(TCL_THREADS)
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe
+!endif
+TCLSTUBLIB = $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib
+TCL_LIBRARY = $(_TCLDIR)\lib
+TCLREGLIB = $(_TCLDIR)\lib\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\lib\tcldde14$(SUFX:t=).lib
+TCLTOOLSDIR = \must\have\tcl\sources\to\build\this\target
+TCL_INCLUDES = -I"$(_TCLDIR)\include"
+
+!else # Building against Tcl sources
+
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX).exe
+!if !exist($(TCLSH)) && $(TCL_THREADS)
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)t$(SUFX).exe
+!endif
+TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib
+TCL_LIBRARY = $(_TCLDIR)\library
+TCLREGLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclreg13$(SUFX:t=).lib
+TCLDDELIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcldde14$(SUFX:t=).lib
+TCLTOOLSDIR = $(_TCLDIR)\tools
+TCL_INCLUDES = -I"$(_TCLDIR)\generic" -I"$(_TCLDIR)\win"
+
+!endif # TCLINSTALL
+
+tcllibs = "$(TCLSTUBLIB)" "$(TCLIMPLIB)"
+
+!endif # $(DOING_TCL)
+
+# We need a tclsh that will run on the host machine as part of the build.
+# IX86 runs on all architectures.
+!ifndef TCLSH_NATIVE
+!if "$(MACHINE)" == "IX86" || "$(MACHINE)" == "$(NATIVE_ARCH)"
+TCLSH_NATIVE = $(TCLSH)
+!else
+!error You must explicitly set TCLSH_NATIVE for cross-compilation
+!endif
+!endif
+
+# Do the same for Tk and Tk extensions that require the Tk libraries
+!if $(DOING_TK) || $(NEED_TK)
+WISHNAMEPREFIX = wish
+WISHNAME = $(WISHNAMEPREFIX)$(TK_VERSION)$(SUFX).exe
+TKLIBNAME = $(PROJECT)$(TK_VERSION)$(SUFX).$(EXT)
+TKSTUBLIBNAME = tkstub$(TK_VERSION).lib
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX).lib
+
+!if $(DOING_TK)
+WISH = $(OUT_DIR)\$(WISHNAME)
+TKSTUBLIB = $(OUT_DIR)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(OUT_DIR)\$(TKIMPLIBNAME)
+TKLIB = $(OUT_DIR)\$(TKLIBNAME)
+TK_INCLUDES = -I"$(WINDIR)" -I"$(GENERICDIR)"
+
+!else # effectively NEED_TK
+
+!if $(TKINSTALL) # Building against installed Tk
+WISH = $(_TKDIR)\bin\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\lib\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
+TK_INCLUDES = -I"$(_TKDIR)\include"
+!else # Building against Tk sources
+WISH = $(_TKDIR)\win\$(BUILDDIRTOP)\$(WISHNAME)
+TKSTUBLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKSTUBLIBNAME)
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
+TK_INCLUDES = -I"$(_TKDIR)\generic" -I"$(_TKDIR)\win" -I"$(_TKDIR)\xlib"
+!endif # TKINSTALL
+tklibs = "$(TKSTUBLIB)" "$(TKIMPLIB)"
+
+!endif # $(DOING_TK)
+!endif # $(DOING_TK) || $(NEED_TK)
+
+# Various output paths
+PRJIMPLIB = $(OUT_DIR)\$(PROJECT)$(VERSION)$(SUFX).lib
+PRJLIBNAME = $(PROJECT)$(VERSION)$(SUFX).$(EXT)
+PRJLIB = $(OUT_DIR)\$(PRJLIBNAME)
+
+PRJSTUBLIBNAME = $(STUBPREFIX)$(VERSION).lib
+PRJSTUBLIB = $(OUT_DIR)\$(PRJSTUBLIBNAME)
+
+# If extension parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+!if !$(DOING_TCL) && !$(DOING_TK) && !$(STATIC_BUILD)
+!ifdef RCFILE
+RESFILE = $(TMP_DIR)\$(RCFILE:.rc=.res)
+!else
+RESFILE = $(TMP_DIR)\$(PROJECT).res
+!endif
+!endif
+
+###################################################################
+# 11. Construct the paths for the installation directories
+# The following macros get defined in this section:
+# LIB_INSTALL_DIR - where libraries should be installed
+# BIN_INSTALL_DIR - where the executables should be installed
+# DOC_INSTALL_DIR - where documentation should be installed
+# SCRIPT_INSTALL_DIR - where scripts should be installed
+# INCLUDE_INSTALL_DIR - where C include files should be installed
+# DEMO_INSTALL_DIR - where demos should be installed
+# PRJ_INSTALL_DIR - where package will be installed (not set for tcl and tk)
+
+!if $(DOING_TCL) || $(DOING_TK)
+LIB_INSTALL_DIR = $(_INSTALLDIR)\lib
+BIN_INSTALL_DIR = $(_INSTALLDIR)\bin
+DOC_INSTALL_DIR = $(_INSTALLDIR)\doc
+!if $(DOING_TCL)
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TCL_MAJOR_VERSION).$(TCL_MINOR_VERSION)
+!else # DOING_TK
+SCRIPT_INSTALL_DIR = $(_INSTALLDIR)\lib\$(PROJECT)$(TK_MAJOR_VERSION).$(TK_MINOR_VERSION)
+!endif
+DEMO_INSTALL_DIR = $(SCRIPT_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_INSTALLDIR)\include
+
+!else # extension other than Tk
+
+PRJ_INSTALL_DIR = $(_INSTALLDIR)\$(PROJECT)$(DOTVERSION)
+LIB_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+BIN_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DOC_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+SCRIPT_INSTALL_DIR = $(PRJ_INSTALL_DIR)
+DEMO_INSTALL_DIR = $(PRJ_INSTALL_DIR)\demos
+INCLUDE_INSTALL_DIR = $(_TCLDIR)\include
+
+!endif
+
+###################################################################
+# 12. Set up actual options to be passed to the compiler and linker
+# Now we have all the information we need, set up the actual flags and
+# options that we will pass to the compiler and linker. The main
+# makefile should use these in combination with whatever other flags
+# and switches are specific to it.
+# The following macros are defined, names are for historical compatibility:
+# OPTDEFINES - /Dxxx C macro flags based on user-specified OPTS
+# COMPILERFLAGS - /Dxxx C macro flags independent of any configuration opttions
+# crt - Compiler switch that selects the appropriate C runtime
+# cdebug - Compiler switches related to debug AND optimizations
+# cwarn - Compiler switches that set warning levels
+# cflags - complete compiler switches (subsumes cdebug and cwarn)
+# ldebug - Linker switches controlling debug information and optimization
+# lflags - complete linker switches (subsumes ldebug) except subsystem type
+# dlllflags - complete linker switches to build DLLs (subsumes lflags)
+# conlflags - complete linker switches for console program (subsumes lflags)
+# guilflags - complete linker switches for GUI program (subsumes lflags)
+# baselibs - minimum Windows libraries required. Parent makefile can
+# define PRJ_LIBS before including rules.rc if additional libs are needed
+
+OPTDEFINES = -DTCL_CFGVAL_ENCODING=$(CFG_ENCODING) -DSTDC_HEADERS
+
+!if $(TCL_MEM_DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DTCL_MEM_DEBUG
+!endif
+!if $(TCL_COMPILE_DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DTCL_COMPILE_DEBUG -DTCL_COMPILE_STATS
+!endif
+!if $(TCL_THREADS)
+OPTDEFINES = $(OPTDEFINES) -DTCL_THREADS=1
+!if $(USE_THREAD_ALLOC)
+OPTDEFINES = $(OPTDEFINES) -DUSE_THREAD_ALLOC=1
+!endif
+!endif
+!if $(STATIC_BUILD)
+OPTDEFINES = $(OPTDEFINES) -DSTATIC_BUILD
+!endif
+!if $(TCL_NO_DEPRECATED)
+OPTDEFINES = $(OPTDEFINES) -DTCL_NO_DEPRECATED
+!endif
+
+!if $(USE_STUBS)
+# Note we do not define USE_TCL_STUBS even when building tk since some
+# test targets in tk do not use stubs
+!if ! $(DOING_TCL)
+USE_STUBS_DEFS = -DUSE_TCL_STUBS -DUSE_TCLOO_STUBS
+!if $(NEED_TK)
+USE_STUBS_DEFS = $(USE_STUBS_DEFS) -DUSE_TK_STUBS
+!endif
+!endif
+!endif # USE_STUBS
+
+!if !$(DEBUG)
+OPTDEFINES = $(OPTDEFINES) -DNDEBUG
+!if $(OPTIMIZING)
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_OPTIMIZED
+!endif
+!endif
+!if $(PROFILE)
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_PROFILED
+!endif
+!if "$(MACHINE)" == "AMD64"
+OPTDEFINES = $(OPTDEFINES) -DTCL_CFG_DO64BIT
+!endif
+!if $(VCVERSION) < 1300
+OPTDEFINES = $(OPTDEFINES) -DNO_STRTOI64
+!endif
+
+# _ATL_XP_TARGETING - Newer SDK's need this to build for XP
+COMPILERFLAGS = /D_ATL_XP_TARGETING
+
+# Following is primarily for the benefit of extensions. Tcl 8.5 builds
+# Tcl without /DUNICODE, while 8.6 builds with it defined. When building
+# an extension, it is advisable (but not mandated) to use the same Windows
+# API as the Tcl build. This is accordingly defaulted below. A particular
+# extension can override this by pre-definining USE_WIDECHAR_API.
+!ifndef USE_WIDECHAR_API
+!if $(TCL_VERSION) > 85
+USE_WIDECHAR_API = 1
+!else
+USE_WIDECHAR_API = 0
+!endif
+!endif
+
+!if $(USE_WIDECHAR_API)
+COMPILERFLAGS = $(COMPILERFLAGS) /DUNICODE /D_UNICODE
+!endif
+
+# Like the TEA system only set this non empty for non-Tk extensions
+# Note: some extensions use PACKAGE_NAME and others use PACKAGE_TCLNAME
+# so we pass both
+!if !$(DOING_TCL) && !$(DOING_TK)
+PKGNAMEFLAGS = -DPACKAGE_NAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ -DPACKAGE_TCLNAME="\"$(PRJ_PACKAGE_TCLNAME)\"" \
+ -DPACKAGE_VERSION="\"$(DOTVERSION)\"" \
+ -DMODULE_SCOPE=extern
+!endif
+
+# crt picks the C run time based on selected OPTS
+!if $(MSVCRT)
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MDd
+!else
+crt = -MD
+!endif
+!else
+!if $(DEBUG) && !$(UNCHECKED)
+crt = -MTd
+!else
+crt = -MT
+!endif
+!endif
+
+# cdebug includes compiler options for debugging as well as optimization.
+!if $(DEBUG)
+
+# In debugging mode, optimizations need to be disabled
+cdebug = -Zi -Od $(DEBUGFLAGS)
+
+!else
+
+cdebug = $(OPTIMIZATIONS)
+!if $(SYMBOLS)
+cdebug = $(cdebug) -Zi
+!endif
+
+!endif # $(DEBUG)
+
+# cwarn includes default warning levels.
+cwarn = $(WARNINGS)
+
+!if "$(MACHINE)" == "AMD64"
+# Disable pointer<->int warnings related to cast between different sizes
+# There are a gadzillion of these due to use of ClientData and
+# clutter up compiler
+# output increasing chance of a real warning getting lost. So disable them.
+# Eventually some day, Tcl will be 64-bit clean.
+cwarn = $(cwarn) -wd4311 -wd4312
+!endif
+
+### Common compiler options that are architecture specific
+!if "$(MACHINE)" == "ARM"
+carch = -D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE
+!else
+carch =
+!endif
+
+!if $(DEBUG)
+# Turn warnings into errors
+cwarn = $(cwarn) -WX
+!endif
+
+INCLUDES = $(TCL_INCLUDES) $(TK_INCLUDES) $(PRJ_INCLUDES)
+!if !$(DOING_TCL) && !$(DOING_TK)
+INCLUDES = $(INCLUDES) -I"$(GENERICDIR)" -I"$(WINDIR)" -I"$(COMPATDIR)"
+!endif
+
+# These flags are defined roughly in the order of the pre-reform
+# rules.vc/makefile.vc to help visually compare that the pre- and
+# post-reform build logs
+
+# cflags contains generic flags used for building practically all object files
+cflags = -nologo -c $(COMPILERFLAGS) $(carch) $(cwarn) -Fp$(TMP_DIR)^\ $(cdebug)
+
+# appcflags contains $(cflags) and flags for building the application
+# object files (e.g. tclsh, or wish) pkgcflags contains $(cflags) plus
+# flags used for building shared object files The two differ in the
+# BUILD_$(PROJECT) macro which should be defined only for the shared
+# library *implementation* and not for its caller interface
+
+appcflags = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES) $(USE_STUBS_DEFS)
+appcflags_nostubs = $(cflags) $(crt) $(INCLUDES) $(TCL_DEFINES) $(PRJ_DEFINES) $(OPTDEFINES)
+pkgcflags = $(appcflags) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)
+pkgcflags_nostubs = $(appcflags_nostubs) $(PKGNAMEFLAGS) -DBUILD_$(PROJECT)
+
+# stubscflags contains $(cflags) plus flags used for building a stubs
+# library for the package. Note: -DSTATIC_BUILD is defined in
+# $(OPTDEFINES) only if the OPTS configuration indicates a static
+# library. However the stubs library is ALWAYS static hence included
+# here irrespective of the OPTS setting.
+#
+# TBD - tclvfs has a comment that stubs libs should not be compiled with -GL
+# without stating why. Tcl itself compiled stubs libs with this flag.
+# so we do not remove it from cflags. -GL may prevent extensions
+# compiled with one VC version to fail to link against stubs library
+# compiled with another VC version. Check for this and fix accordingly.
+stubscflags = $(cflags) $(PKGNAMEFLAGS) $(PRJ_DEFINES) $(OPTDEFINES) -Zl -DSTATIC_BUILD $(INCLUDES)
+
+# Link flags
+
+!if $(DEBUG)
+ldebug = -debug -debugtype:cv
+!else
+ldebug = -release -opt:ref -opt:icf,3
+!if $(SYMBOLS)
+ldebug = $(ldebug) -debug -debugtype:cv
+!endif
+!endif
+
+# Note: Profiling is currently only possible with the Visual Studio Enterprise
+!if $(PROFILE)
+ldebug= $(ldebug) -profile
+!endif
+
+### Declarations common to all linker versions
+lflags = -nologo -machine:$(MACHINE) $(LINKERFLAGS) $(ldebug)
+
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+lflags = $(lflags) -nodefaultlib:libucrt.lib
+!endif
+
+# Old linkers (Visual C++ 6 in particular) will link for fast loading
+# on Win98. Since we do not support Win98 any more, we specify nowin98
+# as recommended for NT and later. However, this is only required by
+# IX86 on older compilers and only needed if we are not doing a static build.
+
+!if "$(MACHINE)" == "IX86" && !$(STATIC_BUILD)
+!if [nmakehlp -l -opt:nowin98 $(LINKER_TESTFLAGS)]
+# Align sections for PE size savings.
+lflags = $(lflags) -opt:nowin98
+!endif
+!endif
+
+dlllflags = $(lflags) -dll
+conlflags = $(lflags) -subsystem:console
+guilflags = $(lflags) -subsystem:windows
+
+# Libraries that are required for every image.
+# Extensions should define any additional libraries with $(PRJ_LIBS)
+winlibs = kernel32.lib advapi32.lib
+
+!if $(NEED_TK)
+winlibs = $(winlibs) gdi32.lib user32.lib uxtheme.lib
+!endif
+
+# Avoid 'unresolved external symbol __security_cookie' errors.
+# c.f. http://support.microsoft.com/?id=894573
+!if "$(MACHINE)" == "AMD64"
+!if $(VCVERSION) > 1399 && $(VCVERSION) < 1500
+winlibs = $(winlibs) bufferoverflowU.lib
+!endif
+!endif
+
+baselibs = $(winlibs) $(PRJ_LIBS)
+
+!if $(MSVCRT) && !($(DEBUG) && !$(UNCHECKED)) && $(VCVERSION) >= 1900
+baselibs = $(baselibs) ucrt.lib
+!endif
+
+################################################################
+# 13. Define standard commands, common make targets and implicit rules
+
+CCPKGCMD = $(cc32) $(pkgcflags) -Fo$(TMP_DIR)^\
+CCAPPCMD = $(cc32) $(appcflags) -Fo$(TMP_DIR)^\
+CCSTUBSCMD = $(cc32) $(stubscflags) -Fo$(TMP_DIR)^\
+
+LIBCMD = $(lib32) -nologo $(LINKERFLAGS) -out:$@
+DLLCMD = $(link32) $(dlllflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+
+CONEXECMD = $(link32) $(conlflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+GUIEXECMD = $(link32) $(guilflags) -out:$@ $(baselibs) $(tcllibs) $(tklibs)
+RESCMD = $(rc32) -fo $@ -r -i "$(GENERICDIR)" -i "$(TMP_DIR)" \
+ $(TCL_INCLUDES) \
+ -DDEBUG=$(DEBUG) -d UNCHECKED=$(UNCHECKED) \
+ -DCOMMAVERSION=$(DOTVERSION:.=,),0 \
+ -DDOTVERSION=\"$(DOTVERSION)\" \
+ -DVERSION=\"$(VERSION)\" \
+ -DSUFX=\"$(SUFX)\" \
+ -DPROJECT=\"$(PROJECT)\" \
+ -DPRJLIBNAME=\"$(PRJLIBNAME)\"
+
+!ifndef DEFAULT_BUILD_TARGET
+DEFAULT_BUILD_TARGET = $(PROJECT)
+!endif
+
+default-target: $(DEFAULT_BUILD_TARGET)
+
+default-pkgindex:
+ @echo package ifneeded $(PRJ_PACKAGE_TCLNAME) $(DOTVERSION) \
+ [list load [file join $$dir $(PRJLIBNAME)]] > $(OUT_DIR)\pkgIndex.tcl
+
+default-pkgindex-tea:
+ @if exist $(ROOT)\pkgIndex.tcl.in nmakehlp -s << $(ROOT)\pkgIndex.tcl.in > $(OUT_DIR)\pkgIndex.tcl
+@PACKAGE_VERSION@ $(DOTVERSION)
+@PACKAGE_NAME@ $(PRJ_PACKAGE_TCLNAME)
+@PACKAGE_TCLNAME@ $(PRJ_PACKAGE_TCLNAME)
+@PKG_LIB_FILE@ $(PRJLIBNAME)
+<<
+
+
+default-install: default-install-binaries default-install-libraries
+
+default-install-binaries: $(PRJLIB)
+ @echo Installing binaries to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+default-install-libraries: $(OUT_DIR)\pkgIndex.tcl
+ @echo Installing libraries to '$(SCRIPT_INSTALL_DIR)'
+ @if exist $(LIBDIR) $(CPY) $(LIBDIR)\*.tcl "$(SCRIPT_INSTALL_DIR)"
+ @echo Installing package index in '$(SCRIPT_INSTALL_DIR)'
+ @$(CPY) $(OUT_DIR)\pkgIndex.tcl $(SCRIPT_INSTALL_DIR)
+
+default-install-stubs:
+ @echo Installing stubs library to '$(SCRIPT_INSTALL_DIR)'
+ @if not exist "$(SCRIPT_INSTALL_DIR)" mkdir "$(SCRIPT_INSTALL_DIR)"
+ @$(CPY) $(PRJSTUBLIB) "$(SCRIPT_INSTALL_DIR)" >NUL
+
+default-install-docs-html:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.html" "$(DOCDIR)\*.css" "$(DOCDIR)\*.png") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-docs-n:
+ @echo Installing documentation files to '$(DOC_INSTALL_DIR)'
+ @if not exist "$(DOC_INSTALL_DIR)" mkdir "$(DOC_INSTALL_DIR)"
+ @if exist $(DOCDIR) for %f in ("$(DOCDIR)\*.n") do @$(COPY) %f "$(DOC_INSTALL_DIR)"
+
+default-install-demos:
+ @echo Installing demos to '$(DEMO_INSTALL_DIR)'
+ @if not exist "$(DEMO_INSTALL_DIR)" mkdir "$(DEMO_INSTALL_DIR)"
+ @if exist $(DEMODIR) $(CPYDIR) "$(DEMODIR)" "$(DEMO_INSTALL_DIR)"
+
+default-clean:
+ @echo Cleaning $(TMP_DIR)\* ...
+ @if exist $(TMP_DIR)\nul $(RMDIR) $(TMP_DIR)
+ @echo Cleaning $(WINDIR)\nmakehlp.obj, nmakehlp.exe ...
+ @if exist $(WINDIR)\nmakehlp.obj del $(WINDIR)\nmakehlp.obj
+ @if exist $(WINDIR)\nmakehlp.exe del $(WINDIR)\nmakehlp.exe
+ @if exist $(WINDIR)\nmakehlp.out del $(WINDIR)\nmakehlp.out
+ @echo Cleaning $(WINDIR)\nmhlp-out.txt ...
+ @if exist $(WINDIR)\nmhlp-out.txt del $(WINDIR)\nmhlp-out.txt
+ @echo Cleaning $(WINDIR)\_junk.pch ...
+ @if exist $(WINDIR)\_junk.pch del $(WINDIR)\_junk.pch
+ @echo Cleaning $(WINDIR)\vercl.x, vercl.i ...
+ @if exist $(WINDIR)\vercl.x del $(WINDIR)\vercl.x
+ @if exist $(WINDIR)\vercl.i del $(WINDIR)\vercl.i
+ @echo Cleaning $(WINDIR)\versions.vc, version.vc ...
+ @if exist $(WINDIR)\versions.vc del $(WINDIR)\versions.vc
+ @if exist $(WINDIR)\version.vc del $(WINDIR)\version.vc
+
+default-hose: default-clean
+ @echo Hosing $(OUT_DIR)\* ...
+ @if exist $(OUT_DIR)\nul $(RMDIR) $(OUT_DIR)
+
+# Only for backward compatibility
+default-distclean: default-hose
+
+default-setup:
+ @if not exist $(OUT_DIR)\nul mkdir $(OUT_DIR)
+ @if not exist $(TMP_DIR)\nul mkdir $(TMP_DIR)
+
+!if "$(TESTPAT)" != ""
+TESTFLAGS = $(TESTFLAGS) -file $(TESTPAT)
+!endif
+
+default-test: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ cd "$(TESTDIR)" && $(DEBUGGER) $(TCLSH) all.tcl $(TESTFLAGS)
+
+default-shell: default-setup $(PROJECT)
+ @set TCLLIBPATH=$(OUT_DIR:\=/)
+ @if exist $(LIBDIR) for %f in ("$(LIBDIR)\*.tcl") do @$(COPY) %f "$(OUT_DIR)"
+ $(DEBUGGER) $(TCLSH)
+
+# Generation of Windows version resource
+!ifdef RCFILE
+
+# Note: don't use $** in below rule because there may be other dependencies
+# and only the "master" rc must be passed to the resource compiler
+$(TMP_DIR)\$(PROJECT).res: $(RCDIR)\$(PROJECT).rc
+ $(RESCMD) $(RCDIR)\$(PROJECT).rc
+
+!else
+
+# If parent makefile has not defined a resource definition file,
+# we will generate one from standard template.
+$(TMP_DIR)\$(PROJECT).res: $(TMP_DIR)\$(PROJECT).rc
+
+$(TMP_DIR)\$(PROJECT).rc:
+ @$(COPY) << $(TMP_DIR)\$(PROJECT).rc
+#include <winver.h>
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION COMMAVERSION
+ PRODUCTVERSION COMMAVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "Tcl extension " PROJECT
+ VALUE "OriginalFilename", PRJLIBNAME
+ VALUE "FileVersion", DOTVERSION
+ VALUE "ProductName", "Package " PROJECT " for Tcl"
+ VALUE "ProductVersion", DOTVERSION
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+<<
+
+!endif # ifdef RCFILE
+
+!ifndef DISABLE_IMPLICIT_RULES
+DISABLE_IMPLICIT_RULES = 0
+!endif
+
+!if !$(DISABLE_IMPLICIT_RULES)
+# Implicit rule definitions - only for building library objects. For stubs and
+# main application, the master makefile should define explicit rules.
+
+{$(ROOT)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(WINDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(GENERICDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(COMPATDIR)}.c{$(TMP_DIR)}.obj::
+ $(CCPKGCMD) @<<
+$<
+<<
+
+{$(RCDIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(WINDIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+{$(TMP_DIR)}.rc{$(TMP_DIR)}.res:
+ $(RESCMD) $<
+
+.SUFFIXES:
+.SUFFIXES:.c .rc
+
+!endif
+
+################################################################
+# 14. Sanity check selected options against Tcl build options
+# When building an extension, certain configuration options should
+# match the ones used when Tcl was built. Here we check and
+# warn on a mismatch.
+!if ! $(DOING_TCL)
+
+!if $(TCLINSTALL) # Building against an installed Tcl
+!if exist("$(_TCLDIR)\lib\nmake\tcl.nmake")
+TCLNMAKECONFIG = "$(_TCLDIR)\lib\nmake\tcl.nmake"
+!endif
+!else # ! $(TCLINSTALL) - building against Tcl source
+!if exist("$(OUT_DIR)\tcl.nmake")
+TCLNMAKECONFIG = "$(OUT_DIR)\tcl.nmake"
+!endif
+!endif # TCLINSTALL
+
+!if $(CONFIG_CHECK)
+!ifdef TCLNMAKECONFIG
+!include $(TCLNMAKECONFIG)
+
+!if defined(CORE_MACHINE) && "$(CORE_MACHINE)" != "$(MACHINE)"
+!error ERROR: Build target ($(MACHINE)) does not match the Tcl library architecture ($(CORE_MACHINE)).
+!endif
+!if defined(CORE_USE_THREAD_ALLOC) && $(CORE_USE_THREAD_ALLOC) != $(USE_THREAD_ALLOC)
+!message WARNING: Value of USE_THREAD_ALLOC ($(USE_THREAD_ALLOC)) does not match its Tcl core value ($(CORE_USE_THREAD_ALLOC)).
+!endif
+!if defined(CORE_DEBUG) && $(CORE_DEBUG) != $(DEBUG)
+!message WARNING: Value of DEBUG ($(DEBUG)) does not match its Tcl library configuration ($(DEBUG)).
+!endif
+!endif
+
+!endif # TCLNMAKECONFIG
+
+!endif # ! $(DOING_TCL)
+
+
+#----------------------------------------------------------
+# Display stats being used.
+#----------------------------------------------------------
+
+!if !$(DOING_TCL)
+!message *** Building against Tcl at '$(_TCLDIR)'
+!endif
+!if !$(DOING_TK) && $(NEED_TK)
+!message *** Building against Tk at '$(_TKDIR)'
+!endif
+!message *** Intermediate directory will be '$(TMP_DIR)'
+!message *** Output directory will be '$(OUT_DIR)'
+!message *** Installation, if selected, will be in '$(_INSTALLDIR)'
+!message *** Suffix for binaries will be '$(SUFX)'
+!message *** Compiler version $(VCVER). Target machine is $(MACHINE)
+!message *** Host architecture is $(NATIVE_ARCH)
+
+!endif # ifdef _RULES_VC
diff --git a/tk8.6/win/stubs.c b/tk8.6/win/stubs.c new file mode 100644 index 0000000..1cf23ef --- /dev/null +++ b/tk8.6/win/stubs.c @@ -0,0 +1,474 @@ +#include "tk.h" + +/* + * Undocumented Xlib internal function + */ + +int +_XInitImageFuncPtrs( + XImage *image) +{ + return Success; +} + +/* + * From Xutil.h + */ + +void +XSetWMClientMachine( + Display *display, + Window w, + XTextProperty *text_prop) +{ +} + +Status +XStringListToTextProperty( + char **list, + int count, + XTextProperty *text_prop_return) +{ + return (Status) 0; +} + +/* + * From Xlib.h + */ + +int +XChangeProperty( + Display *display, + Window w, + Atom property, + Atom type, + int format, + int mode, + _Xconst unsigned char *data, + int nelements) +{ + return Success; +} + +Cursor +XCreateGlyphCursor( + Display *display, + Font source_font, + Font mask_font, + unsigned int source_char, + unsigned int mask_char, + XColor _Xconst *foreground_color, + XColor _Xconst *background_color) +{ + return 1; +} + +XIC +XCreateIC(XIM xim, ...) +{ + return NULL; +} + +Cursor +XCreatePixmapCursor( + Display *display, + Pixmap source, + Pixmap mask, + XColor *foreground_color, + XColor *background_color, + unsigned int x, + unsigned int y) +{ + return (Cursor) NULL; +} + +int +XDeleteProperty( + Display *display, + Window w, + Atom property) +{ + return Success; +} + +void +XDestroyIC( + XIC ic) +{ +} + +Bool +XFilterEvent( + XEvent *event, + Window window) +{ + return 0; +} + +int +XForceScreenSaver( + Display *display, + int mode) +{ + return Success; +} + +int +XFreeCursor( + Display *display, + Cursor cursor) +{ + return Success; +} + +GContext +XGContextFromGC( + GC gc) +{ + return (GContext) NULL; +} + +char * +XGetAtomName( + Display *display, + Atom atom) +{ + return NULL; +} + +int +XGetWindowAttributes( + Display *display, + Window w, + XWindowAttributes *window_attributes_return) +{ + return Success; +} + +Status +XGetWMColormapWindows( + Display *display, + Window w, + Window **windows_return, + int *count_return) +{ + return (Status) 0; +} + +int +XIconifyWindow( + Display *display, + Window w, + int screen_number) +{ + return Success; +} + +XHostAddress * +XListHosts( + Display *display, + int *nhosts_return, + Bool *state_return) +{ + return NULL; +} + +int +XLookupColor( + Display *display, + Colormap colormap, + _Xconst char *color_name, + XColor *exact_def_return, + XColor *screen_def_return) +{ + return Success; +} + +int +XNextEvent( + Display *display, + XEvent *event_return) +{ + return Success; +} + +int +XPutBackEvent( + Display *display, + XEvent *event) +{ + return Success; +} + +int +XQueryColors( + Display *display, + Colormap colormap, + XColor *defs_in_out, + int ncolors) +{ + return Success; +} + +int +XQueryTree( + Display *display, + Window w, + Window *root_return, + Window *parent_return, + Window **children_return, + unsigned int *nchildren_return) +{ + return Success; +} + +int +XRefreshKeyboardMapping( + XMappingEvent *event_map) +{ + return Success; +} + +Window +XRootWindow( + Display *display, + int screen_number) +{ + return (Window) NULL; +} + +int +XSelectInput( + Display *display, + Window w, + long event_mask) +{ + return Success; +} + +int +XSendEvent( + Display *display, + Window w, + Bool propagate, + long event_mask, + XEvent *event_send) +{ + return Success; +} + +int +XSetCommand( + Display *display, + Window w, + char **argv, + int argc) +{ + return Success; +} + +XErrorHandler +XSetErrorHandler( + XErrorHandler handler) +{ + return NULL; +} + +int +XSetIconName( + Display *display, + Window w, + _Xconst char *icon_name) +{ + return Success; +} + +int +XSetWindowBackground( + Display *display, + Window w, + unsigned long background_pixel) +{ + return Success; +} + +int +XSetWindowBackgroundPixmap( + Display *display, + Window w, + Pixmap background_pixmap) +{ + return Success; +} + +int +XSetWindowBorder( + Display *display, + Window w, + unsigned long border_pixel) +{ + return Success; +} + +int +XSetWindowBorderPixmap( + Display *display, + Window w, + Pixmap border_pixmap) +{ + return Success; +} + +int +XSetWindowBorderWidth( + Display *display, + Window w, + unsigned int width) +{ + return Success; +} + +int +XSetWindowColormap( + Display *display, + Window w, + Colormap colormap) +{ + return Success; +} + +Bool +XTranslateCoordinates( + Display *display, + Window src_w, + Window dest_w, + int src_x, + int src_y, + int *dest_x_return, + int *dest_y_return, + Window *child_return) +{ + return 0; +} + +int +XWindowEvent( + Display *display, + Window w, + long event_mask, + XEvent *event_return) +{ + return Success; +} + +int +XWithdrawWindow( + Display *display, + Window w, + int screen_number) +{ + return Success; +} + +int +XmbLookupString( + XIC ic, + XKeyPressedEvent *event, + char *buffer_return, + int bytes_buffer, + KeySym *keysym_return, + Status *status_return) +{ + return Success; +} + +int +XGetWindowProperty( + Display *display, + Window w, + Atom property, + long long_offset, + long long_length, + Bool delete, + Atom req_type, + Atom *actual_type_return, + int *actual_format_return, + unsigned long *nitems_return, + unsigned long *bytes_after_return, + unsigned char **prop_return) +{ + *actual_type_return = None; + *actual_format_return = 0; + *nitems_return = 0; + *bytes_after_return = 0; + *prop_return = NULL; + return BadValue; +} + +/* + * The following functions were implemented as macros under Windows. + */ + +int +XFlush( + Display *display) +{ + return 0; +} + +int +XGrabServer( + Display *display) +{ + return 0; +} + +int +XUngrabServer( + Display *display) +{ + return 0; +} + +int +XFree( + void *data) +{ + if ((data) != NULL) { + ckfree(data); + } + return 0; +} + +int +XNoOp( + Display *display) +{ + display->request++; + return 0; +} + +XAfterFunction +XSynchronize( + Display *display, + Bool bool) +{ + display->request++; + return NULL; +} + +int +XSync( + Display *display, + Bool bool) +{ + display->request++; + return 0; +} + +VisualID +XVisualIDFromVisual( + Visual *visual) +{ + return visual->visualid; +} diff --git a/tk8.6/win/targets.vc b/tk8.6/win/targets.vc new file mode 100644 index 0000000..312022d --- /dev/null +++ b/tk8.6/win/targets.vc @@ -0,0 +1,98 @@ +#------------------------------------------------------------- -*- makefile -*- +# targets.vc -- +# +# Part of the nmake based build system for Tcl and its extensions. +# This file defines some standard targets for the convenience of extensions +# and can be optionally included by the extension makefile. +# See TIP 477 (https://core.tcl.tk/tips/doc/trunk/tip/477.md) for docs. + +$(PROJECT): setup pkgindex $(PRJLIB) + +!ifdef PRJ_STUBOBJS +$(PROJECT): $(PRJSTUBLIB) +$(PRJSTUBLIB): $(PRJ_STUBOBJS) + $(LIBCMD) $** + +$(PRJ_STUBOBJS): + $(CCSTUBSCMD) %s +!endif # PRJ_STUBOBJS + +!ifdef PRJ_MANIFEST +$(PROJECT): $(PRJLIB).manifest +$(PRJLIB).manifest: $(PRJ_MANIFEST) + @nmakehlp -s << $** >$@ +@MACHINE@ $(MACHINE:IX86=X86) +<< +!endif + +!if "$(PROJECT)" != "tcl" && "$(PROJECT)" != "tk" +$(PRJLIB): $(PRJ_OBJS) $(RESFILE) +!if $(STATIC_BUILD) + $(LIBCMD) $** +!else + $(DLLCMD) $** + $(_VC_MANIFEST_EMBED_DLL) +!endif + -@del $*.exp +!endif + +!if "$(PRJ_HEADERS)" != "" && "$(PRJ_OBJS)" != "" +$(PRJ_OBJS): $(PRJ_HEADERS) +!endif + +# If parent makefile has defined stub objects, add their installation +# to the default install +!if "$(PRJ_STUBOBJS)" != "" +default-install: default-install-stubs +!endif + +# Unlike the other default targets, these cannot be in rules.vc because +# the executed command depends on existence of macro PRJ_HEADERS_PUBLIC +# that the parent makefile will not define until after including rules-ext.vc +!if "$(PRJ_HEADERS_PUBLIC)" != "" +default-install: default-install-headers +default-install-headers: + @echo Installing headers to '$(INCLUDE_INSTALL_DIR)' + @for %f in ($(PRJ_HEADERS_PUBLIC)) do @$(COPY) %f "$(INCLUDE_INSTALL_DIR)" +!endif + +!if "$(DISABLE_STANDARD_TARGETS)" == "" +DISABLE_STANDARD_TARGETS = 0 +!endif + +!if "$(DISABLE_TARGET_setup)" == "" +DISABLE_TARGET_setup = 0 +!endif +!if "$(DISABLE_TARGET_install)" == "" +DISABLE_TARGET_install = 0 +!endif +!if "$(DISABLE_TARGET_clean)" == "" +DISABLE_TARGET_clean = 0 +!endif +!if "$(DISABLE_TARGET_test)" == "" +DISABLE_TARGET_test = 0 +!endif +!if "$(DISABLE_TARGET_shell)" == "" +DISABLE_TARGET_shell = 0 +!endif + +!if !$(DISABLE_STANDARD_TARGETS) +!if !$(DISABLE_TARGET_setup) +setup: default-setup +!endif +!if !$(DISABLE_TARGET_install) +install: default-install +!endif +!if !$(DISABLE_TARGET_clean) +clean: default-clean +realclean: hose +hose: default-hose +distclean: realclean default-distclean +!endif +!if !$(DISABLE_TARGET_test) +test: default-test +!endif +!if !$(DISABLE_TARGET_shell) +shell: default-shell +!endif +!endif # DISABLE_STANDARD_TARGETS diff --git a/tk8.6/win/tcl.m4 b/tk8.6/win/tcl.m4 new file mode 100644 index 0000000..84f0dff --- /dev/null +++ b/tk8.6/win/tcl.m4 @@ -0,0 +1,1299 @@ +#------------------------------------------------------------------------ +# SC_PATH_TCLCONFIG -- +# +# Locate the tclConfig.sh file and perform a sanity check on +# the Tcl compile flags +# +# Arguments: +# none +# +# Results: +# +# Adds the following arguments to configure: +# --with-tcl=... +# +# Defines the following vars: +# TCL_BIN_DIR Full path to the directory containing +# the tclConfig.sh file +#------------------------------------------------------------------------ + +AC_DEFUN([SC_PATH_TCLCONFIG], [ + # + # Ok, lets find the tcl configuration + # First, look for one uninstalled. + # the alternative search directory is invoked by --with-tcl + # + + if test x"${no_tcl}" = x ; then + # we reset no_tcl in case something fails here + no_tcl=true + AC_ARG_WITH(tcl, + AC_HELP_STRING([--with-tcl], + [directory containing tcl configuration (tclConfig.sh)]), + with_tclconfig="${withval}") + AC_MSG_CHECKING([for Tcl configuration]) + AC_CACHE_VAL(ac_cv_c_tclconfig,[ + + # First check to see if --with-tcl was specified. + if test x"${with_tclconfig}" != x ; then + case "${with_tclconfig}" in + */tclConfig.sh ) + if test -f "${with_tclconfig}"; then + AC_MSG_WARN([--with-tcl argument should refer to directory containing tclConfig.sh, not to tclConfig.sh itself]) + with_tclconfig="`echo "${with_tclconfig}" | sed 's!/tclConfig\.sh$!!'`" + fi ;; + esac + if test -f "${with_tclconfig}/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd "${with_tclconfig}"; pwd)`" + else + AC_MSG_ERROR([${with_tclconfig} directory doesn't contain tclConfig.sh]) + fi + fi + + # then check for a private Tcl installation + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in \ + ../tcl \ + `ls -dr ../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../tcl[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../tcl \ + `ls -dr ../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../../tcl[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../../tcl \ + `ls -dr ../../../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../../../tcl[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../../../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/win/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + + # check in a few common install locations + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in `ls -d ${libdir} 2>/dev/null` \ + `ls -d ${exec_prefix}/lib 2>/dev/null` \ + `ls -d ${prefix}/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Tcl/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d /c/Tcl/lib 2>/dev/null` \ + `ls -d /c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d C:/Tcl/lib 2>/dev/null` \ + `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ + ; do + if test -f "$i/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i; pwd)`" + break + fi + done + fi + + # check in a few other private locations + if test x"${ac_cv_c_tclconfig}" = x ; then + for i in \ + ${srcdir}/../tcl \ + `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ${srcdir}/../tcl[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/win/tclConfig.sh" ; then + ac_cv_c_tclconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + ]) + + if test x"${ac_cv_c_tclconfig}" = x ; then + TCL_BIN_DIR="# no Tcl configs found" + AC_MSG_ERROR([Can't find Tcl configuration definitions. Use --with-tcl to specify a directory containing tclConfig.sh]) + else + no_tcl= + TCL_BIN_DIR="${ac_cv_c_tclconfig}" + AC_MSG_RESULT([found ${TCL_BIN_DIR}/tclConfig.sh]) + fi + fi +]) + +#------------------------------------------------------------------------ +# SC_PATH_TKCONFIG -- +# +# Locate the tkConfig.sh file +# +# Arguments: +# none +# +# Results: +# +# Adds the following arguments to configure: +# --with-tk=... +# +# Defines the following vars: +# TK_BIN_DIR Full path to the directory containing +# the tkConfig.sh file +#------------------------------------------------------------------------ + +AC_DEFUN([SC_PATH_TKCONFIG], [ + # + # Ok, lets find the tk configuration + # First, look for one uninstalled. + # the alternative search directory is invoked by --with-tk + # + + if test x"${no_tk}" = x ; then + # we reset no_tk in case something fails here + no_tk=true + AC_ARG_WITH(tk, + AC_HELP_STRING([--with-tk], + [directory containing tk configuration (tkConfig.sh)]), + with_tkconfig="${withval}") + AC_MSG_CHECKING([for Tk configuration]) + AC_CACHE_VAL(ac_cv_c_tkconfig,[ + + # First check to see if --with-tkconfig was specified. + if test x"${with_tkconfig}" != x ; then + case "${with_tkconfig}" in + */tkConfig.sh ) + if test -f "${with_tkconfig}"; then + AC_MSG_WARN([--with-tk argument should refer to directory containing tkConfig.sh, not to tkConfig.sh itself]) + with_tkconfig="`echo "${with_tkconfig}" | sed 's!/tkConfig\.sh$!!'`" + fi ;; + esac + if test -f "${with_tkconfig}/tkConfig.sh" ; then + ac_cv_c_tkconfig="`(cd "${with_tkconfig}"; pwd)`" + else + AC_MSG_ERROR([${with_tkconfig} directory doesn't contain tkConfig.sh]) + fi + fi + + # then check for a private Tk library + if test x"${ac_cv_c_tkconfig}" = x ; then + for i in \ + ../tk \ + `ls -dr ../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../tk[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../tk[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../tk \ + `ls -dr ../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../../tk[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../../tk[[8-9]].[[0-9]]* 2>/dev/null` \ + ../../../tk \ + `ls -dr ../../../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ../../../tk[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ../../../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/win/tkConfig.sh" ; then + ac_cv_c_tkconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + + # check in a few common install locations + if test x"${ac_cv_c_tkconfig}" = x ; then + for i in `ls -d ${libdir} 2>/dev/null` \ + `ls -d ${exec_prefix}/lib 2>/dev/null` \ + `ls -d ${prefix}/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Tcl/lib 2>/dev/null` \ + `ls -d /cygdrive/c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d /c/Tcl/lib 2>/dev/null` \ + `ls -d /c/Progra~1/Tcl/lib 2>/dev/null` \ + `ls -d C:/Tcl/lib 2>/dev/null` \ + `ls -d C:/Progra~1/Tcl/lib 2>/dev/null` \ + ; do + if test -f "$i/tkConfig.sh" ; then + ac_cv_c_tkconfig="`(cd $i; pwd)`" + break + fi + done + fi + + # check in a few other private locations + if test x"${ac_cv_c_tkconfig}" = x ; then + for i in \ + ${srcdir}/../tk \ + `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]].[[0-9]]* 2>/dev/null` \ + `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]] 2>/dev/null` \ + `ls -dr ${srcdir}/../tk[[8-9]].[[0-9]]* 2>/dev/null` ; do + if test -f "$i/win/tkConfig.sh" ; then + ac_cv_c_tkconfig="`(cd $i/win; pwd)`" + break + fi + done + fi + ]) + + if test x"${ac_cv_c_tkconfig}" = x ; then + TK_BIN_DIR="# no Tk configs found" + AC_MSG_ERROR([Can't find Tk configuration definitions. Use --with-tk to specify a directory containing tkConfig.sh]) + else + no_tk= + TK_BIN_DIR="${ac_cv_c_tkconfig}" + AC_MSG_RESULT([found ${TK_BIN_DIR}/tkConfig.sh]) + fi + fi +]) + +#------------------------------------------------------------------------ +# SC_LOAD_TCLCONFIG -- +# +# Load the tclConfig.sh file. +# +# Arguments: +# +# Requires the following vars to be set: +# TCL_BIN_DIR +# +# Results: +# +# Substitutes the following vars: +# TCL_BIN_DIR +# TCL_SRC_DIR +# TCL_LIB_FILE +# +#------------------------------------------------------------------------ + +AC_DEFUN([SC_LOAD_TCLCONFIG], [ + AC_MSG_CHECKING([for existence of ${TCL_BIN_DIR}/tclConfig.sh]) + + if test -f "${TCL_BIN_DIR}/tclConfig.sh" ; then + AC_MSG_RESULT([loading]) + . "${TCL_BIN_DIR}/tclConfig.sh" + else + AC_MSG_RESULT([could not find ${TCL_BIN_DIR}/tclConfig.sh]) + fi + + # + # If the TCL_BIN_DIR is the build directory (not the install directory), + # then set the common variable name to the value of the build variables. + # For example, the variable TCL_LIB_SPEC will be set to the value + # of TCL_BUILD_LIB_SPEC. An extension should make use of TCL_LIB_SPEC + # instead of TCL_BUILD_LIB_SPEC since it will work with both an + # installed and uninstalled version of Tcl. + # + + if test -f $TCL_BIN_DIR/Makefile ; then + TCL_LIB_SPEC=${TCL_BUILD_LIB_SPEC} + TCL_STUB_LIB_SPEC=${TCL_BUILD_STUB_LIB_SPEC} + TCL_STUB_LIB_PATH=${TCL_BUILD_STUB_LIB_PATH} + fi + + # + # eval is required to do the TCL_DBGX substitution + # + + eval "TCL_LIB_FILE=\"${TCL_LIB_FILE}\"" + eval "TCL_LIB_FLAG=\"${TCL_LIB_FLAG}\"" + eval "TCL_LIB_SPEC=\"${TCL_LIB_SPEC}\"" + + eval "TCL_STUB_LIB_FILE=\"${TCL_STUB_LIB_FILE}\"" + eval "TCL_STUB_LIB_FLAG=\"${TCL_STUB_LIB_FLAG}\"" + eval "TCL_STUB_LIB_SPEC=\"${TCL_STUB_LIB_SPEC}\"" + + AC_SUBST(TCL_VERSION) + AC_SUBST(TCL_BIN_DIR) + AC_SUBST(TCL_SRC_DIR) + + AC_SUBST(TCL_LIB_FILE) + AC_SUBST(TCL_LIB_FLAG) + AC_SUBST(TCL_LIB_SPEC) + + AC_SUBST(TCL_STUB_LIB_FILE) + AC_SUBST(TCL_STUB_LIB_FLAG) + AC_SUBST(TCL_STUB_LIB_SPEC) + + AC_SUBST(TCL_DEFS) +]) + +#------------------------------------------------------------------------ +# SC_LOAD_TKCONFIG -- +# +# Load the tkConfig.sh file +# +# Arguments: +# +# Requires the following vars to be set: +# TK_BIN_DIR +# +# Results: +# +# Sets the following vars that should be in tkConfig.sh: +# TK_BIN_DIR +#------------------------------------------------------------------------ + +AC_DEFUN([SC_LOAD_TKCONFIG], [ + AC_MSG_CHECKING([for existence of ${TK_BIN_DIR}/tkConfig.sh]) + + if test -f "${TK_BIN_DIR}/tkConfig.sh" ; then + AC_MSG_RESULT([loading]) + . "${TK_BIN_DIR}/tkConfig.sh" + else + AC_MSG_RESULT([could not find ${TK_BIN_DIR}/tkConfig.sh]) + fi + + + AC_SUBST(TK_BIN_DIR) + AC_SUBST(TK_SRC_DIR) + AC_SUBST(TK_LIB_FILE) +]) + +#------------------------------------------------------------------------ +# SC_ENABLE_SHARED -- +# +# Allows the building of shared libraries +# +# Arguments: +# none +# +# Results: +# +# Adds the following arguments to configure: +# --enable-shared=yes|no +# +# Defines the following vars: +# STATIC_BUILD Used for building import/export libraries +# on Windows. +# +# Sets the following vars: +# SHARED_BUILD Value of 1 or 0 +#------------------------------------------------------------------------ + +AC_DEFUN([SC_ENABLE_SHARED], [ + AC_MSG_CHECKING([how to build libraries]) + AC_ARG_ENABLE(shared, + [ --enable-shared build and link with shared libraries (default: on)], + [tcl_ok=$enableval], [tcl_ok=yes]) + + if test "${enable_shared+set}" = set; then + enableval="$enable_shared" + tcl_ok=$enableval + else + tcl_ok=yes + fi + + if test "$tcl_ok" = "yes" ; then + AC_MSG_RESULT([shared]) + SHARED_BUILD=1 + else + AC_MSG_RESULT([static]) + SHARED_BUILD=0 + AC_DEFINE(STATIC_BUILD, 1, [Is this a static build?]) + fi +]) + +#------------------------------------------------------------------------ +# SC_ENABLE_THREADS -- +# +# Specify if thread support should be enabled +# +# Arguments: +# none +# +# Results: +# +# Adds the following arguments to configure: +# --enable-threads=yes|no +# +# Defines the following vars: +# TCL_THREADS +#------------------------------------------------------------------------ + +AC_DEFUN([SC_ENABLE_THREADS], [ + AC_MSG_CHECKING(for building with threads) + AC_ARG_ENABLE(threads, [ --enable-threads build with threads (default: on)], + [tcl_ok=$enableval], [tcl_ok=yes]) + + if test "$tcl_ok" = "yes"; then + AC_MSG_RESULT([yes (default)]) + TCL_THREADS=1 + AC_DEFINE(TCL_THREADS) + # USE_THREAD_ALLOC tells us to try the special thread-based + # allocator that significantly reduces lock contention + AC_DEFINE(USE_THREAD_ALLOC) + else + TCL_THREADS=0 + AC_MSG_RESULT(no) + fi + AC_SUBST(TCL_THREADS) +]) + +#------------------------------------------------------------------------ +# SC_ENABLE_SYMBOLS -- +# +# Specify if debugging symbols should be used. +# Memory (TCL_MEM_DEBUG) and compile (TCL_COMPILE_DEBUG) debugging +# can also be enabled. +# +# Arguments: +# none +# +# Requires the following vars to be set in the Makefile: +# CFLAGS_DEBUG +# CFLAGS_OPTIMIZE +# +# Results: +# +# Adds the following arguments to configure: +# --enable-symbols +# +# Defines the following vars: +# CFLAGS_DEFAULT Sets to $(CFLAGS_DEBUG) if true +# Sets to $(CFLAGS_OPTIMIZE) if false +# LDFLAGS_DEFAULT Sets to $(LDFLAGS_DEBUG) if true +# Sets to $(LDFLAGS_OPTIMIZE) if false +# DBGX Debug library extension +# +#------------------------------------------------------------------------ + +AC_DEFUN([SC_ENABLE_SYMBOLS], [ + AC_MSG_CHECKING([for build with symbols]) + AC_ARG_ENABLE(symbols, [ --enable-symbols build with debugging symbols (default: off)], [tcl_ok=$enableval], [tcl_ok=no]) +# FIXME: Currently, LDFLAGS_DEFAULT is not used, it should work like CFLAGS_DEFAULT. + if test "$tcl_ok" = "no"; then + CFLAGS_DEFAULT='$(CFLAGS_OPTIMIZE)' + LDFLAGS_DEFAULT='$(LDFLAGS_OPTIMIZE)' + DBGX="" + AC_DEFINE(NDEBUG, 1, [Is no debugging enabled?]) + AC_MSG_RESULT([no]) + + AC_DEFINE(TCL_CFG_OPTIMIZED) + else + CFLAGS_DEFAULT='$(CFLAGS_DEBUG)' + LDFLAGS_DEFAULT='$(LDFLAGS_DEBUG)' + DBGX=g + if test "$tcl_ok" = "yes"; then + AC_MSG_RESULT([yes (standard debugging)]) + fi + fi + AC_SUBST(CFLAGS_DEFAULT) + AC_SUBST(LDFLAGS_DEFAULT) + + if test "$tcl_ok" = "mem" -o "$tcl_ok" = "all"; then + AC_DEFINE(TCL_MEM_DEBUG, 1, [Is memory debugging enabled?]) + fi + + if test "$tcl_ok" = "compile" -o "$tcl_ok" = "all"; then + AC_DEFINE(TCL_COMPILE_DEBUG, 1, [Is bytecode debugging enabled?]) + AC_DEFINE(TCL_COMPILE_STATS, 1, [Are bytecode statistics enabled?]) + fi + + if test "$tcl_ok" != "yes" -a "$tcl_ok" != "no"; then + if test "$tcl_ok" = "all"; then + AC_MSG_RESULT([enabled symbols mem compile debugging]) + else + AC_MSG_RESULT([enabled $tcl_ok debugging]) + fi + fi +]) + +#-------------------------------------------------------------------- +# SC_CONFIG_CFLAGS +# +# Try to determine the proper flags to pass to the compiler +# for building shared libraries and other such nonsense. +# +# NOTE: The backslashes in quotes below are substituted twice +# due to the fact that they are in a macro and then inlined +# in the final configure script. +# +# Arguments: +# none +# +# Results: +# +# Can the following vars: +# EXTRA_CFLAGS +# CFLAGS_DEBUG +# CFLAGS_OPTIMIZE +# CFLAGS_WARNING +# LDFLAGS_DEBUG +# LDFLAGS_OPTIMIZE +# LDFLAGS_CONSOLE +# LDFLAGS_WINDOW +# CC_OBJNAME +# CC_EXENAME +# CYGPATH +# STLIB_LD +# SHLIB_LD +# SHLIB_LD_LIBS +# LIBS +# AR +# RC +# RES +# +# MAKE_LIB +# MAKE_STUB_LIB +# MAKE_EXE +# MAKE_DLL +# +# LIBSUFFIX +# LIBFLAGSUFFIX +# LIBPREFIX +# LIBRARIES +# EXESUFFIX +# DLLSUFFIX +# +#-------------------------------------------------------------------- + +AC_DEFUN([SC_CONFIG_CFLAGS], [ + + # Step 0: Enable 64 bit support? + + AC_MSG_CHECKING([if 64bit support is requested]) + AC_ARG_ENABLE(64bit,[ --enable-64bit enable 64bit support (where applicable)], [do64bit=$enableval], [do64bit=no]) + AC_MSG_RESULT($do64bit) + + # Cross-compiling options for Windows/CE builds + + AC_MSG_CHECKING([if Windows/CE build is requested]) + AC_ARG_ENABLE(wince,[ --enable-wince enable Win/CE support (where applicable)], [doWince=$enableval], [doWince=no]) + AC_MSG_RESULT($doWince) + + AC_MSG_CHECKING([for Windows/CE celib directory]) + AC_ARG_WITH(celib,[ --with-celib=DIR use Windows/CE support library from DIR], + CELIB_DIR=$withval, CELIB_DIR=NO_CELIB) + AC_MSG_RESULT([$CELIB_DIR]) + + # Set some defaults (may get changed below) + EXTRA_CFLAGS="" + AC_DEFINE(MODULE_SCOPE, [extern], [No need to mark inidividual symbols as hidden]) + + AC_CHECK_PROG(CYGPATH, cygpath, cygpath -m, echo) + + SHLIB_SUFFIX=".dll" + + # MACHINE is IX86 for LINK, but this is used by the manifest, + # which requires x86|amd64|ia64. + MACHINE="X86" + + if test "$GCC" = "yes"; then + + AC_CACHE_CHECK(for cross-compile version of gcc, + ac_cv_cross, + AC_TRY_COMPILE([ + #ifndef _WIN32 + #error cross-compiler + #endif + ], [], + ac_cv_cross=no, + ac_cv_cross=yes) + ) + + if test "$ac_cv_cross" = "yes"; then + case "$do64bit" in + amd64|x64|yes) + CC="x86_64-w64-mingw32-gcc" + LD="x86_64-w64-mingw32-ld" + AR="x86_64-w64-mingw32-ar" + RANLIB="x86_64-w64-mingw32-ranlib" + RC="x86_64-w64-mingw32-windres" + ;; + *) + CC="i686-w64-mingw32-gcc" + LD="i686-w64-mingw32-ld" + AR="i686-w64-mingw32-ar" + RANLIB="i686-w64-mingw32-ranlib" + RC="i686-w64-mingw32-windres" + ;; + esac + fi + fi + + # Check for a bug in gcc's windres that causes the + # compile to fail when a Windows native path is + # passed into windres. The mingw toolchain requires + # Windows native paths while Cygwin should work + # with both. Avoid the bug by passing a POSIX + # path when using the Cygwin toolchain. + + if test "$GCC" = "yes" && test "$CYGPATH" != "echo" ; then + conftest=/tmp/conftest.rc + echo "STRINGTABLE BEGIN" > $conftest + echo "101 \"name\"" >> $conftest + echo "END" >> $conftest + + AC_MSG_CHECKING([for Windows native path bug in windres]) + cyg_conftest=`$CYGPATH $conftest` + if AC_TRY_COMMAND($RC -o conftest.res.o $cyg_conftest) ; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + CYGPATH=echo + fi + conftest= + cyg_conftest= + fi + + if test "$CYGPATH" = "echo"; then + DEPARG='"$<"' + else + DEPARG='"$(shell $(CYGPATH) $<)"' + fi + + # set various compiler flags depending on whether we are using gcc or cl + + if test "${GCC}" = "yes" ; then + extra_cflags="-pipe" + extra_ldflags="-pipe -static-libgcc" + AC_CACHE_CHECK(for mingw32 version of gcc, + ac_cv_win32, + AC_TRY_COMPILE([ + #ifdef _WIN32 + #error win32 + #endif + ], [], + ac_cv_win32=no, + ac_cv_win32=yes) + ) + if test "$ac_cv_win32" != "yes"; then + AC_MSG_ERROR([${CC} cannot produce win32 executables.]) + fi + + hold_cflags=$CFLAGS; CFLAGS="$CFLAGS -mwindows -municode -Dmain=xxmain" + AC_CACHE_CHECK(for working -municode linker flag, + ac_cv_municode, + AC_TRY_LINK([ + #include <windows.h> + int APIENTRY wWinMain(HINSTANCE a, HINSTANCE b, LPWSTR c, int d) {return 0;} + ], + [], + ac_cv_municode=yes, + ac_cv_municode=no) + ) + CFLAGS=$hold_cflags + if test "$ac_cv_municode" = "yes" ; then + extra_ldflags="$extra_ldflags -municode" + else + extra_cflags="$extra_cflags -DTCL_BROKEN_MAINARGS" + fi + fi + + AC_MSG_CHECKING([compiler flags]) + if test "${GCC}" = "yes" ; then + SHLIB_LD="" + SHLIB_LD_LIBS='${LIBS}' + LIBS="-lnetapi32 -lkernel32 -luser32 -ladvapi32 -luserenv -lws2_32" + # mingw needs to link ole32 and oleaut32 for [send], but MSVC doesn't + LIBS_GUI="-lgdi32 -lcomdlg32 -limm32 -lcomctl32 -lshell32 -luuid -lole32 -loleaut32" + STLIB_LD='${AR} cr' + RC_OUT=-o + RC_TYPE= + RC_INCLUDE=--include + RC_DEFINE=--define + RES=res.o + MAKE_LIB="\${STLIB_LD} \[$]@" + MAKE_STUB_LIB="\${STLIB_LD} \[$]@" + POST_MAKE_LIB="\${RANLIB} \[$]@" + MAKE_EXE="\${CC} -o \[$]@" + LIBPREFIX="lib" + + if test "${SHARED_BUILD}" = "0" ; then + # static + AC_MSG_RESULT([using static flags]) + runtime= + LIBRARIES="\${STATIC_LIBRARIES}" + EXESUFFIX="s\${DBGX}.exe" + else + # dynamic + AC_MSG_RESULT([using shared flags]) + + # ad-hoc check to see if CC supports -shared. + if "${CC}" -shared 2>&1 | egrep ': -shared not supported' >/dev/null; then + AC_MSG_ERROR([${CC} does not support the -shared option. + You will need to upgrade to a newer version of the toolchain.]) + fi + + runtime= + # Add SHLIB_LD_LIBS to the Make rule, not here. + + EXESUFFIX="\${DBGX}.exe" + LIBRARIES="\${SHARED_LIBRARIES}" + fi + # Link with gcc since ld does not link to default libs like + # -luser32 and -lmsvcrt by default. + SHLIB_LD='${CC} -shared' + SHLIB_LD_LIBS='${LIBS}' + MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -o \[$]@ ${extra_ldflags} \ + -Wl,--out-implib,\$(patsubst %.dll,lib%.a,\[$]@)" + # DLLSUFFIX is separate because it is the building block for + # users of tclConfig.sh that may build shared or static. + DLLSUFFIX="\${DBGX}.dll" + LIBSUFFIX="\${DBGX}.a" + LIBFLAGSUFFIX="\${DBGX}" + SHLIB_SUFFIX=.dll + + EXTRA_CFLAGS="${extra_cflags}" + + CFLAGS_DEBUG=-g + CFLAGS_OPTIMIZE="-O2 -fomit-frame-pointer" + CFLAGS_WARNING="-Wall -Wdeclaration-after-statement" + LDFLAGS_DEBUG= + LDFLAGS_OPTIMIZE= + + # Specify the CC output file names based on the target name + CC_OBJNAME="-o \[$]@" + CC_EXENAME="-o \[$]@" + + # Specify linker flags depending on the type of app being + # built -- Console vs. Window. + # + # ORIGINAL COMMENT: + # We need to pass -e _WinMain@16 so that ld will use + # WinMain() instead of main() as the entry point. We can't + # use autoconf to check for this case since it would need + # to run an executable and that does not work when + # cross compiling. Remove this -e workaround once we + # require a gcc that does not have this bug. + # + # MK NOTE: Tk should use a different mechanism. This causes + # interesting problems, such as wish dying at startup. + #LDFLAGS_WINDOW="-mwindows -e _WinMain@16 ${extra_ldflags}" + LDFLAGS_CONSOLE="-mconsole ${extra_ldflags}" + LDFLAGS_WINDOW="-mwindows ${extra_ldflags}" + + case "$do64bit" in + amd64|x64|yes) + MACHINE="AMD64" ; # assume AMD64 as default 64-bit build + AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) + ;; + ia64) + MACHINE="IA64" + AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) + ;; + *) + AC_TRY_COMPILE([ + #ifndef _WIN64 + #error 32-bit + #endif + ], [], + tcl_win_64bit=yes, + tcl_win_64bit=no + ) + if test "$tcl_win_64bit" = "yes" ; then + do64bit=amd64 + MACHINE="AMD64" + AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) + fi + ;; + esac + else + if test "${SHARED_BUILD}" = "0" ; then + # static + AC_MSG_RESULT([using static flags]) + runtime=-MT + LIBRARIES="\${STATIC_LIBRARIES}" + EXESUFFIX="s\${DBGX}.exe" + else + # dynamic + AC_MSG_RESULT([using shared flags]) + runtime=-MD + # Add SHLIB_LD_LIBS to the Make rule, not here. + LIBRARIES="\${SHARED_LIBRARIES}" + EXESUFFIX="\${DBGX}.exe" + case "x`echo \${VisualStudioVersion}`" in + x1[[4-9]]*) + lflags="${lflags} -nodefaultlib:libucrt.lib" + ;; + *) + ;; + esac + fi + MAKE_DLL="\${SHLIB_LD} \$(LDFLAGS) -out:\[$]@" + # DLLSUFFIX is separate because it is the building block for + # users of tclConfig.sh that may build shared or static. + DLLSUFFIX="\${DBGX}.dll" + LIBSUFFIX="\${DBGX}.lib" + LIBFLAGSUFFIX="\${DBGX}" + + # This is a 2-stage check to make sure we have the 64-bit SDK + # We have to know where the SDK is installed. + # This magic is based on MS Platform SDK for Win2003 SP1 - hobbs + if test "$do64bit" != "no" ; then + if test "x${MSSDK}x" = "xx" ; then + MSSDK="C:/Progra~1/Microsoft Platform SDK" + fi + MSSDK=`echo "$MSSDK" | sed -e 's!\\\!/!g'` + PATH64="" + case "$do64bit" in + amd64|x64|yes) + MACHINE="AMD64" ; # assume AMD64 as default 64-bit build + PATH64="${MSSDK}/Bin/Win64/x86/AMD64" + ;; + ia64) + MACHINE="IA64" + PATH64="${MSSDK}/Bin/Win64" + ;; + esac + if test ! -d "${PATH64}" ; then + AC_MSG_WARN([Could not find 64-bit $MACHINE SDK]) + fi + AC_MSG_RESULT([ Using 64-bit $MACHINE mode]) + fi + + LIBS="netapi32.lib kernel32.lib user32.lib advapi32.lib userenv.lib ws2_32.lib" + + case "x`echo \${VisualStudioVersion}`" in + x1[[4-9]]*) + LIBS="$LIBS ucrt.lib" + ;; + *) + ;; + esac + + if test "$do64bit" != "no" ; then + # The space-based-path will work for the Makefile, but will + # not work if AC_TRY_COMPILE is called. TEA has the + # TEA_PATH_NOSPACE to avoid this issue. + # Check if _WIN64 is already recognized, and if so we don't + # need to modify CC. + AC_CHECK_DECL([_WIN64], [], + [CC="\"${PATH64}/cl.exe\" -I\"${MSSDK}/Include\" \ + -I\"${MSSDK}/Include/crt\" \ + -I\"${MSSDK}/Include/crt/sys\""]) + RC="\"${MSSDK}/bin/rc.exe\"" + CFLAGS_DEBUG="-nologo -Zi -Od ${runtime}d" + # Do not use -O2 for Win64 - this has proved buggy in code gen. + CFLAGS_OPTIMIZE="-nologo -O1 ${runtime}" + lflags="${lflags} -nologo -MACHINE:${MACHINE} -LIBPATH:\"${MSSDK}/Lib/${MACHINE}\"" + LINKBIN="\"${PATH64}/link.exe\"" + # Avoid 'unresolved external symbol __security_cookie' errors. + # c.f. http://support.microsoft.com/?id=894573 + LIBS="$LIBS bufferoverflowU.lib" + else + RC="rc" + # -Od - no optimization + # -WX - warnings as errors + CFLAGS_DEBUG="-nologo -Z7 -Od -WX ${runtime}d" + # -O2 - create fast code (/Og /Oi /Ot /Oy /Ob2 /Gs /GF /Gy) + CFLAGS_OPTIMIZE="-nologo -O2 ${runtime}" + lflags="${lflags} -nologo" + LINKBIN="link" + fi + + if test "$doWince" != "no" ; then + # Set defaults for common evc4/PPC2003 setup + # Currently Tcl requires 300+, possibly 420+ for sockets + CEVERSION=420; # could be 211 300 301 400 420 ... + TARGETCPU=ARMV4; # could be ARMV4 ARM MIPS SH3 X86 ... + ARCH=ARM; # could be ARM MIPS X86EM ... + PLATFORM="Pocket PC 2003"; # or "Pocket PC 2002" + if test "$doWince" != "yes"; then + # If !yes then the user specified something + # Reset ARCH to allow user to skip specifying it + ARCH= + eval `echo $doWince | awk -F "," '{ \ + if (length([$]1)) { printf "CEVERSION=\"%s\"\n", [$]1; \ + if ([$]1 < 400) { printf "PLATFORM=\"Pocket PC 2002\"\n" } }; \ + if (length([$]2)) { printf "TARGETCPU=\"%s\"\n", toupper([$]2) }; \ + if (length([$]3)) { printf "ARCH=\"%s\"\n", toupper([$]3) }; \ + if (length([$]4)) { printf "PLATFORM=\"%s\"\n", [$]4 }; \ + }'` + if test "x${ARCH}" = "x" ; then + ARCH=$TARGETCPU; + fi + fi + OSVERSION=WCE$CEVERSION; + if test "x${WCEROOT}" = "x" ; then + WCEROOT="C:/Program Files/Microsoft eMbedded C++ 4.0" + if test ! -d "${WCEROOT}" ; then + WCEROOT="C:/Program Files/Microsoft eMbedded Tools" + fi + fi + if test "x${SDKROOT}" = "x" ; then + SDKROOT="C:/Program Files/Windows CE Tools" + if test ! -d "${SDKROOT}" ; then + SDKROOT="C:/Windows CE Tools" + fi + fi + # The space-based-path will work for the Makefile, but will + # not work if AC_TRY_COMPILE is called. + WCEROOT=`echo "$WCEROOT" | sed -e 's!\\\!/!g'` + SDKROOT=`echo "$SDKROOT" | sed -e 's!\\\!/!g'` + CELIB_DIR=`echo "$CELIB_DIR" | sed -e 's!\\\!/!g'` + if test ! -d "${CELIB_DIR}/inc"; then + AC_MSG_ERROR([Invalid celib directory "${CELIB_DIR}"]) + fi + if test ! -d "${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}"\ + -o ! -d "${WCEROOT}/EVC/${OSVERSION}/bin"; then + AC_MSG_ERROR([could not find PocketPC SDK or target compiler to enable WinCE mode [$CEVERSION,$TARGETCPU,$ARCH,$PLATFORM]]) + else + CEINCLUDE="${SDKROOT}/${OSVERSION}/${PLATFORM}/include" + if test -d "${CEINCLUDE}/${TARGETCPU}" ; then + CEINCLUDE="${CEINCLUDE}/${TARGETCPU}" + fi + CELIBPATH="${SDKROOT}/${OSVERSION}/${PLATFORM}/Lib/${TARGETCPU}" + fi + fi + + if test "$doWince" != "no" ; then + CEBINROOT="${WCEROOT}/EVC/${OSVERSION}/bin" + if test "${TARGETCPU}" = "X86"; then + CC="${CEBINROOT}/cl.exe" + else + CC="${CEBINROOT}/cl${ARCH}.exe" + fi + CC="\"${CC}\" -I\"${CELIB_DIR}/inc\" -I\"${CEINCLUDE}\"" + RC="\"${WCEROOT}/Common/EVC/bin/rc.exe\"" + arch=`echo ${ARCH} | awk '{print tolower([$]0)}'` + defs="${ARCH} _${ARCH}_ ${arch} PALM_SIZE _MT _DLL _WINDOWS" + for i in $defs ; do + AC_DEFINE_UNQUOTED($i) + done +# if test "${ARCH}" = "X86EM"; then +# AC_DEFINE_UNQUOTED(_WIN32_WCE_EMULATION) +# fi + AC_DEFINE_UNQUOTED(_WIN32_WCE, $CEVERSION) + AC_DEFINE_UNQUOTED(UNDER_CE, $CEVERSION) + CFLAGS_DEBUG="-nologo -Zi -Od" + CFLAGS_OPTIMIZE="-nologo -O2" + lversion=`echo ${CEVERSION} | sed -e 's/\(.\)\(..\)/\1\.\2/'` + lflags="-nodefaultlib -MACHINE:${ARCH} -LIBPATH:\"${CELIBPATH}\" -subsystem:windowsce,${lversion} -nologo" + LINKBIN="\"${CEBINROOT}/link.exe\"" + AC_SUBST(CELIB_DIR) + if test "${CEVERSION}" -lt 400 ; then + LIBS="coredll.lib corelibc.lib winsock.lib" + else + LIBS="coredll.lib corelibc.lib ws2.lib" + fi + # celib currently stuck at wce300 status + #LIBS="$LIBS \${CELIB_DIR}/wince-${ARCH}-pocket-${OSVERSION}-release/celib.lib" + LIBS="$LIBS \"\${CELIB_DIR}/wince-${ARCH}-pocket-wce300-release/celib.lib\"" + LIBS_GUI="commctrl.lib commdlg.lib" + else + LIBS_GUI="gdi32.lib comdlg32.lib imm32.lib comctl32.lib shell32.lib uuid.lib" + fi + + SHLIB_LD="${LINKBIN} -dll -incremental:no ${lflags}" + SHLIB_LD_LIBS='${LIBS}' + # link -lib only works when -lib is the first arg + STLIB_LD="${LINKBIN} -lib ${lflags}" + RC_OUT=-fo + RC_TYPE=-r + RC_INCLUDE=-i + RC_DEFINE=-d + RES=res + MAKE_LIB="\${STLIB_LD} -out:\[$]@" + MAKE_STUB_LIB="\${STLIB_LD} -nodefaultlib -out:\[$]@" + POST_MAKE_LIB= + MAKE_EXE="\${CC} -Fe\[$]@" + LIBPREFIX="" + + CFLAGS_DEBUG="${CFLAGS_DEBUG} -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE" + CFLAGS_OPTIMIZE="${CFLAGS_OPTIMIZE} -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE" + + EXTRA_CFLAGS="" + CFLAGS_WARNING="-W3" + LDFLAGS_DEBUG="-debug" + LDFLAGS_OPTIMIZE="-release" + + # Specify the CC output file names based on the target name + CC_OBJNAME="-Fo\[$]@" + CC_EXENAME="-Fe\"\$(shell \$(CYGPATH) '\[$]@')\"" + + # Specify linker flags depending on the type of app being + # built -- Console vs. Window. + if test "$doWince" != "no" -a "${TARGETCPU}" != "X86"; then + LDFLAGS_CONSOLE="-link ${lflags}" + LDFLAGS_WINDOW=${LDFLAGS_CONSOLE} + else + LDFLAGS_CONSOLE="-link -subsystem:console ${lflags}" + LDFLAGS_WINDOW="-link -subsystem:windows ${lflags}" + fi + fi + + if test "$do64bit" != "no" ; then + AC_DEFINE(TCL_CFG_DO64BIT) + fi + + if test "${GCC}" = "yes" ; then + AC_CACHE_CHECK(for SEH support in compiler, + tcl_cv_seh, + AC_TRY_RUN([ + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #undef WIN32_LEAN_AND_MEAN + + int main(int argc, char** argv) { + int a, b = 0; + __try { + a = 666 / b; + } + __except (EXCEPTION_EXECUTE_HANDLER) { + return 0; + } + return 1; + } + ], + tcl_cv_seh=yes, + tcl_cv_seh=no, + tcl_cv_seh=no) + ) + if test "$tcl_cv_seh" = "no" ; then + AC_DEFINE(HAVE_NO_SEH, 1, + [Defined when mingw does not support SEH]) + fi + + # + # Check to see if the excpt.h include file provided contains the + # definition for EXCEPTION_DISPOSITION; if not, which is the case + # with Cygwin's version as of 2002-04-10, define it to be int, + # sufficient for getting the current code to work. + # + AC_CACHE_CHECK(for EXCEPTION_DISPOSITION support in include files, + tcl_cv_eh_disposition, + AC_TRY_COMPILE([ +# define WIN32_LEAN_AND_MEAN +# include <windows.h> +# undef WIN32_LEAN_AND_MEAN + ],[ + EXCEPTION_DISPOSITION x; + ], + tcl_cv_eh_disposition=yes, + tcl_cv_eh_disposition=no) + ) + if test "$tcl_cv_eh_disposition" = "no" ; then + AC_DEFINE(EXCEPTION_DISPOSITION, int, + [Defined when cygwin/mingw does not support EXCEPTION DISPOSITION]) + fi + + # Check to see if winnt.h defines CHAR, SHORT, and LONG + # even if VOID has already been #defined. The win32api + # used by mingw and cygwin is known to do this. + + AC_CACHE_CHECK(for winnt.h that ignores VOID define, + tcl_cv_winnt_ignore_void, + AC_TRY_COMPILE([ + #define VOID void + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #undef WIN32_LEAN_AND_MEAN + ], [ + CHAR c; + SHORT s; + LONG l; + ], + tcl_cv_winnt_ignore_void=yes, + tcl_cv_winnt_ignore_void=no) + ) + if test "$tcl_cv_winnt_ignore_void" = "yes" ; then + AC_DEFINE(HAVE_WINNT_IGNORE_VOID, 1, + [Defined when cygwin/mingw ignores VOID define in winnt.h]) + fi + + # See if the compiler supports casting to a union type. + # This is used to stop gcc from printing a compiler + # warning when initializing a union member. + + AC_CACHE_CHECK(for cast to union support, + tcl_cv_cast_to_union, + AC_TRY_COMPILE([], + [ + union foo { int i; double d; }; + union foo f = (union foo) (int) 0; + ], + tcl_cv_cast_to_union=yes, + tcl_cv_cast_to_union=no) + ) + if test "$tcl_cv_cast_to_union" = "yes"; then + AC_DEFINE(HAVE_CAST_TO_UNION, 1, + [Defined when compiler supports casting to union type.]) + fi + fi + + # DL_LIBS is empty, but then we match the Unix version + AC_SUBST(DL_LIBS) + AC_SUBST(CFLAGS_DEBUG) + AC_SUBST(CFLAGS_OPTIMIZE) + AC_SUBST(CFLAGS_WARNING) +]) + +#------------------------------------------------------------------------ +# SC_WITH_TCL -- +# +# Location of the Tcl build directory. +# +# Arguments: +# none +# +# Results: +# +# Adds the following arguments to configure: +# --with-tcl=... +# +# Defines the following vars: +# TCL_BIN_DIR Full path to the tcl build dir. +#------------------------------------------------------------------------ + +AC_DEFUN([SC_WITH_TCL], [ + if test -d ../../tcl8.6$1/win; then + TCL_BIN_DEFAULT=../../tcl8.6$1/win + else + TCL_BIN_DEFAULT=../../tcl8.6/win + fi + + AC_ARG_WITH(tcl, [ --with-tcl=DIR use Tcl 8.6 binaries from DIR], + TCL_BIN_DIR=$withval, TCL_BIN_DIR=`cd $TCL_BIN_DEFAULT; pwd`) + if test ! -d $TCL_BIN_DIR; then + AC_MSG_ERROR(Tcl directory $TCL_BIN_DIR does not exist) + fi + if test ! -f $TCL_BIN_DIR/Makefile; then + AC_MSG_ERROR(There is no Makefile in $TCL_BIN_DIR: perhaps you did not specify the Tcl *build* directory (not the toplevel Tcl directory) or you forgot to configure Tcl?) + else + echo "building against Tcl binaries in: $TCL_BIN_DIR" + fi + AC_SUBST(TCL_BIN_DIR) +]) + +#------------------------------------------------------------------------ +# SC_PROG_TCLSH +# Locate a tclsh shell installed on the system path. This macro +# will only find a Tcl shell that already exists on the system. +# It will not find a Tcl shell in the Tcl build directory or +# a Tcl shell that has been installed from the Tcl build directory. +# If a Tcl shell can't be located on the PATH, then TCLSH_PROG will +# be set to "". Extensions should take care not to create Makefile +# rules that are run by default and depend on TCLSH_PROG. An +# extension can't assume that an executable Tcl shell exists at +# build time. +# +# Arguments +# none +# +# Results +# Subst's the following values: +# TCLSH_PROG +#------------------------------------------------------------------------ + +AC_DEFUN([SC_PROG_TCLSH], [ + AC_MSG_CHECKING([for tclsh]) + + AC_CACHE_VAL(ac_cv_path_tclsh, [ + search_path=`echo ${PATH} | sed -e 's/:/ /g'` + for dir in $search_path ; do + for j in `ls -r $dir/tclsh[[8-9]]*.exe 2> /dev/null` \ + `ls -r $dir/tclsh* 2> /dev/null` ; do + if test x"$ac_cv_path_tclsh" = x ; then + if test -f "$j" ; then + ac_cv_path_tclsh=$j + break + fi + fi + done + done + ]) + + if test -f "$ac_cv_path_tclsh" ; then + TCLSH_PROG="$ac_cv_path_tclsh" + AC_MSG_RESULT($TCLSH_PROG) + else + # It is not an error if an installed version of Tcl can't be located. + TCLSH_PROG="" + AC_MSG_RESULT([No tclsh found on PATH]) + fi + AC_SUBST(TCLSH_PROG) +]) + +#------------------------------------------------------------------------ +# SC_BUILD_TCLSH +# Determine the fully qualified path name of the tclsh executable +# in the Tcl build directory. This macro will correctly determine +# the name of the tclsh executable even if tclsh has not yet +# been built in the build directory. The build tclsh must be used +# when running tests from an extension build directory. It is not +# correct to use the TCLSH_PROG in cases like this. +# +# Arguments +# none +# +# Results +# Subst's the following values: +# BUILD_TCLSH +#------------------------------------------------------------------------ + +AC_DEFUN([SC_BUILD_TCLSH], [ + AC_MSG_CHECKING([for tclsh in Tcl build directory]) + BUILD_TCLSH=${TCL_BIN_DIR}/tclsh${TCL_MAJOR_VERSION}${TCL_MINOR_VERSION}${TCL_DBGX}${EXEEXT} + AC_MSG_RESULT($BUILD_TCLSH) + AC_SUBST(BUILD_TCLSH) +]) + +#-------------------------------------------------------------------- +# SC_TCL_CFG_ENCODING TIP #59 +# +# Declare the encoding to use for embedded configuration information. +# +# Arguments: +# None. +# +# Results: +# Might append to the following vars: +# DEFS (implicit) +# +# Will define the following vars: +# TCL_CFGVAL_ENCODING +# +#-------------------------------------------------------------------- + +AC_DEFUN([SC_TCL_CFG_ENCODING], [ + AC_ARG_WITH(encoding, [ --with-encoding encoding for configuration values], with_tcencoding=${withval}) + + if test x"${with_tcencoding}" != x ; then + AC_DEFINE_UNQUOTED(TCL_CFGVAL_ENCODING,"${with_tcencoding}") + else + # Default encoding on windows is not "iso8859-1" + AC_DEFINE(TCL_CFGVAL_ENCODING,"cp1252") + fi +]) + +#-------------------------------------------------------------------- +# SC_EMBED_MANIFEST +# +# Figure out if we can embed the manifest where necessary +# +# Arguments: +# An optional manifest to merge into DLL/EXE. +# +# Results: +# Will define the following vars: +# VC_MANIFEST_EMBED_DLL +# VC_MANIFEST_EMBED_EXE +# +#-------------------------------------------------------------------- + +AC_DEFUN([SC_EMBED_MANIFEST], [ + AC_MSG_CHECKING(whether to embed manifest) + AC_ARG_ENABLE(embedded-manifest, + AC_HELP_STRING([--enable-embedded-manifest], + [embed manifest if possible (default: yes)]), + [embed_ok=$enableval], [embed_ok=yes]) + + VC_MANIFEST_EMBED_DLL= + VC_MANIFEST_EMBED_EXE= + result=no + if test "$embed_ok" = "yes" -a "${SHARED_BUILD}" = "1" \ + -a "$GCC" != "yes" ; then + # Add the magic to embed the manifest into the dll/exe + AC_EGREP_CPP([manifest needed], [ +#if defined(_MSC_VER) && _MSC_VER >= 1400 +print("manifest needed") +#endif + ], [ + # Could do a CHECK_PROG for mt, but should always be with MSVC8+ + # Could add 'if test -f' check, but manifest should be created + # in this compiler case + # Add in a manifest argument that may be specified + # XXX Needs improvement so that the test for existence accounts + # XXX for a provided (known) manifest + VC_MANIFEST_EMBED_DLL="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest $1 -outputresource:\[$]@\;2 ; fi" + VC_MANIFEST_EMBED_EXE="if test -f \[$]@.manifest ; then mt.exe -nologo -manifest \[$]@.manifest $1 -outputresource:\[$]@\;1 ; fi" + result=yes + if test "x$1" != x ; then + result="yes ($1)" + fi + ]) + fi + AC_MSG_RESULT([$result]) + AC_SUBST(VC_MANIFEST_EMBED_DLL) + AC_SUBST(VC_MANIFEST_EMBED_EXE) +]) diff --git a/tk8.6/win/tkConfig.sh.in b/tk8.6/win/tkConfig.sh.in new file mode 100644 index 0000000..c511312 --- /dev/null +++ b/tk8.6/win/tkConfig.sh.in @@ -0,0 +1,87 @@ +# tkConfig.sh -- +# +# This shell script (for sh) is generated automatically by Tk's +# configure script. It will create shell variables for most of +# the configuration options discovered by the configure script. +# This script is intended to be included by the configure scripts +# for Tk extensions so that they don't have to figure this all +# out for themselves. This file does not duplicate information +# already provided by tclConfig.sh, so you may need to use that +# file in addition to this one. +# +# The information in this file is specific to a single platform. + +TK_DLL_FILE="@TK_DLL_FILE@" + +# Tk's version number. +TK_VERSION='@TK_VERSION@' +TK_MAJOR_VERSION='@TK_MAJOR_VERSION@' +TK_MINOR_VERSION='@TK_MINOR_VERSION@' +TK_PATCH_LEVEL='@TK_PATCH_LEVEL@' + +# -D flags for use with the C compiler. +TK_DEFS='@DEFS@' + +# Flag, 1: we built a shared lib, 0 we didn't +TK_SHARED_BUILD=@TK_SHARED_BUILD@ + +# This indicates if Tk was build with debugging symbols +TK_DBGX=@TK_DBGX@ + +# The name of the Tk library (may be either a .a file or a shared library): +TK_LIB_FILE='@TK_LIB_FILE@' + +# Additional libraries to use when linking Tk. +TK_LIBS='@LIBS@ @LIBS_GUI@' + +# Top-level directory in which Tcl's platform-independent files are +# installed. +TK_PREFIX='@prefix@' + +# Top-level directory in which Tcl's platform-specific files (e.g. +# executables) are installed. +TK_EXEC_PREFIX='@exec_prefix@' + +# -l flag to pass to the linker to pick up the Tcl library +TK_LIB_FLAG='@TK_LIB_FLAG@' + +# String to pass to linker to pick up the Tk library from its +# build directory. +TK_BUILD_LIB_SPEC='@TK_BUILD_LIB_SPEC@' + +# String to pass to linker to pick up the Tk library from its +# installed directory. +TK_LIB_SPEC='@TK_LIB_SPEC@' + +# Location of the top-level source directory from which Tk was built. +# This is the directory that contains a README file as well as +# subdirectories such as generic, unix, etc. If Tk was compiled in a +# different place than the directory containing the source files, this +# points to the location of the sources, not the location where Tk was +# compiled. +TK_SRC_DIR='@TK_SRC_DIR@' + +# Needed if you want to make a 'fat' shared library library +# containing tk objects or link a different wish. +TK_CC_SEARCH_FLAGS='@TK_CC_SEARCH_FLAGS@' +TK_LD_SEARCH_FLAGS='@TK_LD_SEARCH_FLAGS@' + +# The name of the Tk stub library (.a): +TK_STUB_LIB_FILE='@TK_STUB_LIB_FILE@' + +# -l flag to pass to the linker to pick up the Tk stub library +TK_STUB_LIB_FLAG='@TK_STUB_LIB_FLAG@' + +# String to pass to linker to pick up the Tk stub library from its +# build directory. +TK_BUILD_STUB_LIB_SPEC='@TK_BUILD_STUB_LIB_SPEC@' + +# String to pass to linker to pick up the Tk stub library from its +# installed directory. +TK_STUB_LIB_SPEC='@TK_STUB_LIB_SPEC@' + +# Path to the Tk stub library in the build directory. +TK_BUILD_STUB_LIB_PATH='@TK_BUILD_STUB_LIB_PATH@' + +# Path to the Tk stub library in the install directory. +TK_STUB_LIB_PATH='@TK_STUB_LIB_PATH@' diff --git a/tk8.6/win/tkWin.h b/tk8.6/win/tkWin.h new file mode 100644 index 0000000..4d278d7 --- /dev/null +++ b/tk8.6/win/tkWin.h @@ -0,0 +1,81 @@ +/* + * tkWin.h -- + * + * Declarations of public types and interfaces that are only + * available under Windows. + * + * Copyright (c) 1996-1997 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef _TKWIN +#define _TKWIN + +/* + * We must specify the lower version we intend to support. In particular + * the SystemParametersInfo API doesn't like to receive structures that + * are larger than it expects which affects the font assignments. + * + * WINVER = 0x0500 means Windows 2000 and above + */ + +#ifndef WINVER +#define WINVER 0x0500 +#endif +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#ifndef _TK +#include <tk.h> +#endif + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN + +/* + * The following messages are used to communicate between a Tk toplevel + * and its container window. A Tk container may not be able to provide + * service to all of the following requests at the moment. But an embedded + * Tk window will send out these requests to support external Tk container + * application. + */ + +#define TK_CLAIMFOCUS (WM_USER) /* an embedded window requests to focus */ +#define TK_GEOMETRYREQ (WM_USER+1) /* an embedded window requests to change size */ +#define TK_ATTACHWINDOW (WM_USER+2) /* an embedded window requests to attach */ +#define TK_DETACHWINDOW (WM_USER+3) /* an embedded window requests to detach */ +#define TK_MOVEWINDOW (WM_USER+4) /* an embedded window requests to move */ +#define TK_RAISEWINDOW (WM_USER+5) /* an embedded window requests to raise */ +#define TK_ICONIFY (WM_USER+6) /* an embedded window requests to iconify */ +#define TK_DEICONIFY (WM_USER+7) /* an embedded window requests to deiconify */ +#define TK_WITHDRAW (WM_USER+8) /* an embedded window requests to withdraw */ +#define TK_GETFRAMEWID (WM_USER+9) /* an embedded window requests a frame window id */ +#define TK_OVERRIDEREDIRECT (WM_USER+10) /* an embedded window requests to overrideredirect */ +#define TK_SETMENU (WM_USER+11) /* an embedded window requests to setup menu */ +#define TK_STATE (WM_USER+12) /* an embedded window sets/gets state */ +#define TK_INFO (WM_USER+13) /* an embedded window requests a container's info */ + +/* + * The following are sub-messages (wParam) for TK_INFO. An embedded window may + * send a TK_INFO message with one of the sub-messages to query a container + * for verification and availability + */ +#define TK_CONTAINER_VERIFY 0x01 +#define TK_CONTAINER_ISAVAILABLE 0x02 + + +/* + *-------------------------------------------------------------- + * + * Exported procedures defined for the Windows platform only. + * + *-------------------------------------------------------------- + */ + +#include "tkPlatDecls.h" + +#endif /* _TKWIN */ diff --git a/tk8.6/win/tkWin32Dll.c b/tk8.6/win/tkWin32Dll.c new file mode 100644 index 0000000..3c3d83a --- /dev/null +++ b/tk8.6/win/tkWin32Dll.c @@ -0,0 +1,296 @@ +/* + * tkWin32Dll.c -- + * + * This file contains a stub dll entry point. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#ifndef STATIC_BUILD + +#ifdef HAVE_NO_SEH + +/* + * Unlike Borland and Microsoft, we don't register exception handlers by + * pushing registration records onto the runtime stack. Instead, we register + * them by creating an TCLEXCEPTION_REGISTRATION within the activation record. + */ + +typedef struct TCLEXCEPTION_REGISTRATION { + struct TCLEXCEPTION_REGISTRATION *link; + EXCEPTION_DISPOSITION (*handler)( + struct _EXCEPTION_RECORD*, void*, struct _CONTEXT*, void*); + void *ebp; + void *esp; + int status; +} TCLEXCEPTION_REGISTRATION; + +/* + * Need to add noinline flag to DllMain declaration so that gcc -O3 does not + * inline asm code into DllEntryPoint and cause a compile time error because + * of redefined local labels. + */ + +BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, + LPVOID reserved) __attribute__ ((noinline)); + +#else /* !HAVE_NO_SEH */ + +/* + * The following declaration is for the VC++ DLL entry point. + */ + +BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, + LPVOID reserved); +#endif /* HAVE_NO_SEH */ + +/* + *---------------------------------------------------------------------- + * + * DllEntryPoint -- + * + * This wrapper function is used by Borland to invoke the initialization + * code for Tk. It simply calls the DllMain routine. + * + * Results: + * See DllMain. + * + * Side effects: + * See DllMain. + * + *---------------------------------------------------------------------- + */ + +BOOL APIENTRY +DllEntryPoint( + HINSTANCE hInst, /* Library instance handle. */ + DWORD reason, /* Reason this function is being called. */ + LPVOID reserved) /* Not used. */ +{ + return DllMain(hInst, reason, reserved); +} + +/* + *---------------------------------------------------------------------- + * + * DllMain -- + * + * DLL entry point. It is only necessary to specify our dll here so that + * resources are found correctly. Otherwise Tk will initialize and clean + * up after itself through other methods, in order to be consistent + * whether the build is static or dynamic. + * + * Results: + * Always TRUE. + * + * Side effects: + * This might call some synchronization functions, but MSDN documentation + * states: "Waiting on synchronization objects in DllMain can cause a + * deadlock." + * + *---------------------------------------------------------------------- + */ + +BOOL APIENTRY +DllMain( + HINSTANCE hInstance, + DWORD reason, + LPVOID reserved) +{ +#ifdef HAVE_NO_SEH + TCLEXCEPTION_REGISTRATION registration; +#endif + + /* + * If we are attaching to the DLL from a new process, tell Tk about the + * hInstance to use. + */ + + switch (reason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstance); + TkWinSetHINSTANCE(hInstance); + break; + + case DLL_PROCESS_DETACH: + /* + * Protect the call to TkFinalize in an SEH block. We can't be + * guarenteed Tk is always being unloaded from a stable condition. + */ + +#ifdef HAVE_NO_SEH +# ifdef __WIN64 + __asm__ __volatile__ ( + + /* + * Construct an TCLEXCEPTION_REGISTRATION to protect the call to + * TkFinalize + */ + + "leaq %[registration], %%rdx" "\n\t" + "movq %%gs:0, %%rax" "\n\t" + "movq %%rax, 0x0(%%rdx)" "\n\t" /* link */ + "leaq 1f, %%rax" "\n\t" + "movq %%rax, 0x8(%%rdx)" "\n\t" /* handler */ + "movq %%rbp, 0x10(%%rdx)" "\n\t" /* rbp */ + "movq %%rsp, 0x18(%%rdx)" "\n\t" /* rsp */ + "movl %[error], 0x20(%%rdx)" "\n\t" /* status */ + + /* + * Link the TCLEXCEPTION_REGISTRATION on the chain + */ + + "movq %%rdx, %%gs:0" "\n\t" + + /* + * Call TkFinalize + */ + + "movq $0x0, 0x0(%%esp)" "\n\t" + "call TkFinalize" "\n\t" + + /* + * Come here on a normal exit. Recover the TCLEXCEPTION_REGISTRATION + * and store a TCL_OK status + */ + + "movq %%gs:0, %%rdx" "\n\t" + "movl %[ok], %%eax" "\n\t" + "movl %%eax, 0x20(%%rdx)" "\n\t" + "jmp 2f" "\n" + + /* + * Come here on an exception. Get the TCLEXCEPTION_REGISTRATION that + * we previously put on the chain. + */ + + "1:" "\t" + "movq %%gs:0, %%rdx" "\n\t" + "movq 0x10(%%rdx), %%rdx" "\n\t" + + /* + * Come here however we exited. Restore context from the + * TCLEXCEPTION_REGISTRATION in case the stack is unbalanced. + */ + + "2:" "\t" + "movq 0x18(%%rdx), %%rsp" "\n\t" + "movq 0x10(%%rdx), %%rbp" "\n\t" + "movq 0x0(%%rdx), %%rax" "\n\t" + "movq %%rax, %%gs:0" "\n\t" + + : + /* No outputs */ + : + [registration] "m" (registration), + [ok] "i" (TCL_OK), + [error] "i" (TCL_ERROR) + : + "%rax", "%rbx", "%rcx", "%rdx", "%rsi", "%rdi", "memory" + ); + +# else + __asm__ __volatile__ ( + + /* + * Construct an TCLEXCEPTION_REGISTRATION to protect the call to + * TkFinalize + */ + + "leal %[registration], %%edx" "\n\t" + "movl %%fs:0, %%eax" "\n\t" + "movl %%eax, 0x0(%%edx)" "\n\t" /* link */ + "leal 1f, %%eax" "\n\t" + "movl %%eax, 0x4(%%edx)" "\n\t" /* handler */ + "movl %%ebp, 0x8(%%edx)" "\n\t" /* ebp */ + "movl %%esp, 0xc(%%edx)" "\n\t" /* esp */ + "movl %[error], 0x10(%%edx)" "\n\t" /* status */ + + /* + * Link the TCLEXCEPTION_REGISTRATION on the chain + */ + + "movl %%edx, %%fs:0" "\n\t" + + /* + * Call TkFinalize + */ + + "movl $0x0, 0x0(%%esp)" "\n\t" + "call _TkFinalize" "\n\t" + + /* + * Come here on a normal exit. Recover the TCLEXCEPTION_REGISTRATION + * and store a TCL_OK status + */ + + "movl %%fs:0, %%edx" "\n\t" + "movl %[ok], %%eax" "\n\t" + "movl %%eax, 0x10(%%edx)" "\n\t" + "jmp 2f" "\n" + + /* + * Come here on an exception. Get the TCLEXCEPTION_REGISTRATION that + * we previously put on the chain. + */ + + "1:" "\t" + "movl %%fs:0, %%edx" "\n\t" + "movl 0x8(%%edx), %%edx" "\n" + + + /* + * Come here however we exited. Restore context from the + * TCLEXCEPTION_REGISTRATION in case the stack is unbalanced. + */ + + "2:" "\t" + "movl 0xc(%%edx), %%esp" "\n\t" + "movl 0x8(%%edx), %%ebp" "\n\t" + "movl 0x0(%%edx), %%eax" "\n\t" + "movl %%eax, %%fs:0" "\n\t" + + : + /* No outputs */ + : + [registration] "m" (registration), + [ok] "i" (TCL_OK), + [error] "i" (TCL_ERROR) + : + "%eax", "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory" + ); + +# endif +#else /* HAVE_NO_SEH */ + __try { + /* + * Run and remove our exit handlers, if they haven't already been + * run. Just in case we are being unloaded prior to Tcl (it can + * happen), we won't leave any dangling pointers hanging around + * for when Tcl gets unloaded later. + */ + + TkFinalize(NULL); + } __except (EXCEPTION_EXECUTE_HANDLER) { + /* empty handler body. */ + } +#endif + + break; + } + return TRUE; +} + +#endif /* !STATIC_BUILD */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWin3d.c b/tk8.6/win/tkWin3d.c new file mode 100644 index 0000000..d3c443d --- /dev/null +++ b/tk8.6/win/tkWin3d.c @@ -0,0 +1,573 @@ +/* + * tkWin3d.c -- + * + * This file contains the platform specific routines for drawing 3D + * borders in the Windows 95 style. + * + * Copyright (c) 1996 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tk3d.h" + +/* + * This structure is used to keep track of the extra colors used by Windows 3D + * borders. + */ + +typedef struct { + TkBorder info; + XColor *light2ColorPtr; /* System3dLight */ + XColor *dark2ColorPtr; /* System3dDarkShadow */ +} WinBorder; + +/* + *---------------------------------------------------------------------- + * + * TkpGetBorder -- + * + * This function allocates a new TkBorder structure. + * + * Results: + * Returns a newly allocated TkBorder. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkBorder * +TkpGetBorder(void) +{ + WinBorder *borderPtr = ckalloc(sizeof(WinBorder)); + + borderPtr->light2ColorPtr = NULL; + borderPtr->dark2ColorPtr = NULL; + return (TkBorder *) borderPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpFreeBorder -- + * + * This function frees any colors allocated by the platform specific part + * of this module. + * + * Results: + * None. + * + * Side effects: + * May deallocate some colors. + * + *---------------------------------------------------------------------- + */ + +void +TkpFreeBorder( + TkBorder *borderPtr) +{ + WinBorder *winBorderPtr = (WinBorder *) borderPtr; + if (winBorderPtr->light2ColorPtr) { + Tk_FreeColor(winBorderPtr->light2ColorPtr); + } + if (winBorderPtr->dark2ColorPtr) { + Tk_FreeColor(winBorderPtr->dark2ColorPtr); + } +} + +/* + *-------------------------------------------------------------- + * + * Tk_3DVerticalBevel -- + * + * This procedure draws a vertical bevel along one side of an object. The + * bevel is always rectangular in shape: + * ||| + * ||| + * ||| + * ||| + * ||| + * ||| + * An appropriate shadow color is chosen for the bevel based on the + * leftBevel and relief arguments. Normally this procedure is called + * first, then Tk_3DHorizontalBevel is called next to draw neat corners. + * + * Results: + * None. + * + * Side effects: + * Graphics are drawn in drawable. + * + *-------------------------------------------------------------- + */ + +void +Tk_3DVerticalBevel( + Tk_Window tkwin, /* Window for which border was allocated. */ + Drawable drawable, /* X window or pixmap in which to draw. */ + Tk_3DBorder border, /* Token for border to draw. */ + int x, int y, int width, int height, + /* Area of vertical bevel. */ + int leftBevel, /* Non-zero means this bevel forms the left + * side of the object; 0 means it forms the + * right side. */ + int relief) /* Kind of bevel to draw. For example, + * TK_RELIEF_RAISED means interior of object + * should appear higher than exterior. */ +{ + TkBorder *borderPtr = (TkBorder *) border; + int left, right; + Display *display = Tk_Display(tkwin); + TkWinDCState state; + HDC dc = TkWinGetDrawableDC(display, drawable, &state); + int half; + + if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) { + TkpGetShadows(borderPtr, tkwin); + } + + switch (relief) { + case TK_RELIEF_RAISED: + left = (leftBevel) + ? borderPtr->lightGC->foreground + : borderPtr->darkGC->foreground; + right = (leftBevel) + ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel + : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel; + break; + case TK_RELIEF_SUNKEN: + left = (leftBevel) + ? borderPtr->darkGC->foreground + : ((WinBorder *)borderPtr)->light2ColorPtr->pixel; + right = (leftBevel) + ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel + : borderPtr->lightGC->foreground; + break; + case TK_RELIEF_RIDGE: + left = borderPtr->lightGC->foreground; + right = borderPtr->darkGC->foreground; + break; + case TK_RELIEF_GROOVE: + left = borderPtr->darkGC->foreground; + right = borderPtr->lightGC->foreground; + break; + case TK_RELIEF_FLAT: + left = right = borderPtr->bgGC->foreground; + break; + case TK_RELIEF_SOLID: + default: + left = right = RGB(0,0,0); + break; + } + half = width/2; + if (leftBevel && (width & 1)) { + half++; + } + TkWinFillRect(dc, x, y, half, height, left); + TkWinFillRect(dc, x+half, y, width-half, height, right); + TkWinReleaseDrawableDC(drawable, dc, &state); +} + +/* + *-------------------------------------------------------------- + * + * Tk_3DHorizontalBevel -- + * + * This procedure draws a horizontal bevel along one side of an object. + * The bevel has mitered corners (depending on leftIn and rightIn + * arguments). + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void +Tk_3DHorizontalBevel( + Tk_Window tkwin, /* Window for which border was allocated. */ + Drawable drawable, /* X window or pixmap in which to draw. */ + Tk_3DBorder border, /* Token for border to draw. */ + int x, int y, int width, int height, + /* Bounding box of area of bevel. Height gives + * width of border. */ + int leftIn, int rightIn, /* Describes whether the left and right edges + * of the bevel angle in or out as they go + * down. For example, if "leftIn" is true, the + * left side of the bevel looks like this: + * ___________ + * __________ + * _________ + * ________ + */ + int topBevel, /* Non-zero means this bevel forms the top + * side of the object; 0 means it forms the + * bottom side. */ + int relief) /* Kind of bevel to draw. For example, + * TK_RELIEF_RAISED means interior of object + * should appear higher than exterior. */ +{ + TkBorder *borderPtr = (TkBorder *) border; + Display *display = Tk_Display(tkwin); + int bottom, halfway, x1, x2, x1Delta, x2Delta; + TkWinDCState state; + HDC dc = TkWinGetDrawableDC(display, drawable, &state); + int topColor, bottomColor; + + if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) { + TkpGetShadows(borderPtr, tkwin); + } + + /* + * Compute a GC for the top half of the bevel and a GC for the bottom half + * (they're the same in many cases). + */ + + switch (relief) { + case TK_RELIEF_RAISED: + topColor = (topBevel) + ? borderPtr->lightGC->foreground + : borderPtr->darkGC->foreground; + bottomColor = (topBevel) + ? ((WinBorder *)borderPtr)->light2ColorPtr->pixel + : ((WinBorder *)borderPtr)->dark2ColorPtr->pixel; + break; + case TK_RELIEF_SUNKEN: + topColor = (topBevel) + ? borderPtr->darkGC->foreground + : ((WinBorder *)borderPtr)->light2ColorPtr->pixel; + bottomColor = (topBevel) + ? ((WinBorder *)borderPtr)->dark2ColorPtr->pixel + : borderPtr->lightGC->foreground; + break; + case TK_RELIEF_RIDGE: + topColor = borderPtr->lightGC->foreground; + bottomColor = borderPtr->darkGC->foreground; + break; + case TK_RELIEF_GROOVE: + topColor = borderPtr->darkGC->foreground; + bottomColor = borderPtr->lightGC->foreground; + break; + case TK_RELIEF_FLAT: + topColor = bottomColor = borderPtr->bgGC->foreground; + break; + case TK_RELIEF_SOLID: + default: + topColor = bottomColor = RGB(0,0,0); + } + + /* + * Compute various other geometry-related stuff. + */ + + if (leftIn) { + x1 = x+1; + } else { + x1 = x+height-1; + } + x2 = x+width; + if (rightIn) { + x2--; + } else { + x2 -= height; + } + x1Delta = (leftIn) ? 1 : -1; + x2Delta = (rightIn) ? -1 : 1; + halfway = y + height/2; + if (topBevel && (height & 1)) { + halfway++; + } + bottom = y + height; + + /* + * Draw one line for each y-coordinate covered by the bevel. + */ + + for ( ; y < bottom; y++) { + /* + * In some weird cases (such as large border widths for skinny + * rectangles) x1 can be >= x2. Don't draw the lines in these cases. + */ + + if (x1 < x2) { + TkWinFillRect(dc, x1, y, x2-x1, 1, + (y < halfway) ? topColor : bottomColor); + } + x1 += x1Delta; + x2 += x2Delta; + } + TkWinReleaseDrawableDC(drawable, dc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetShadows -- + * + * This procedure computes the shadow colors for a 3-D border and fills + * in the corresponding fields of the Border structure. It's called + * lazily, so that the colors aren't allocated until something is + * actually drawn with them. That way, if a border is only used for flat + * backgrounds the shadow colors will never be allocated. + * + * Results: + * None. + * + * Side effects: + * The lightGC and darkGC fields in borderPtr get filled in, if they + * weren't already. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetShadows( + TkBorder *borderPtr, /* Information about border. */ + Tk_Window tkwin) /* Window where border will be used for + * drawing. */ +{ + XColor lightColor, darkColor; + int tmp1, tmp2; + int r, g, b; + XGCValues gcValues; + + if (borderPtr->lightGC != None) { + return; + } + + /* + * Handle the special case of the default system colors. + */ + + if ((TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_3DFACE) + || (TkWinIndexOfColor(borderPtr->bgColorPtr) == COLOR_WINDOW)) { + borderPtr->darkColorPtr = Tk_GetColor(NULL, tkwin, + Tk_GetUid("SystemButtonShadow")); + gcValues.foreground = borderPtr->darkColorPtr->pixel; + borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + borderPtr->lightColorPtr = Tk_GetColor(NULL, tkwin, + Tk_GetUid("SystemButtonHighlight")); + gcValues.foreground = borderPtr->lightColorPtr->pixel; + borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColor(NULL, tkwin, + Tk_GetUid("System3dDarkShadow")); + ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColor(NULL, tkwin, + Tk_GetUid("System3dLight")); + return; + } + darkColor.red = 0; + darkColor.green = 0; + darkColor.blue = 0; + ((WinBorder*)borderPtr)->dark2ColorPtr = Tk_GetColorByValue(tkwin, + &darkColor); + lightColor = *(borderPtr->bgColorPtr); + ((WinBorder*)borderPtr)->light2ColorPtr = Tk_GetColorByValue(tkwin, + &lightColor); + + /* + * First, handle the case of a color display with lots of colors. The + * shadow colors get computed using whichever formula results in the + * greatest change in color: + * 1. Lighter shadow is half-way to white, darker shadow is half way to + * dark. + * 2. Lighter shadow is 40% brighter than background, darker shadow is 40% + * darker than background. + */ + + if (Tk_Depth(tkwin) >= 6) { + /* + * This is a color display with lots of colors. For the dark shadow, + * cut 40% from each of the background color components. But if the + * background is already very dark, make the dark color a little + * lighter than the background by increasing each color component + * 1/4th of the way to MAX_INTENSITY. + * + * For the light shadow, boost each component by 40% or half-way to + * white, whichever is greater (the first approach works better for + * unsaturated colors, the second for saturated ones). But if the + * background is already very bright, instead choose a slightly darker + * color for the light shadow by reducing each color component by 10%. + * + * Compute the colors using integers, not using lightColor.red etc.: + * these are shorts and may have problems with integer overflow. + */ + + /* + * Compute the dark shadow color + */ + + r = (int) borderPtr->bgColorPtr->red; + g = (int) borderPtr->bgColorPtr->green; + b = (int) borderPtr->bgColorPtr->blue; + + if (r*0.5*r + g*1.0*g + b*0.28*b < MAX_INTENSITY*0.05*MAX_INTENSITY) { + darkColor.red = (MAX_INTENSITY + 3*r)/4; + darkColor.green = (MAX_INTENSITY + 3*g)/4; + darkColor.blue = (MAX_INTENSITY + 3*b)/4; + } else { + darkColor.red = (60 * r)/100; + darkColor.green = (60 * g)/100; + darkColor.blue = (60 * b)/100; + } + + /* + * Allocate the dark shadow color and its GC + */ + + borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor); + gcValues.foreground = borderPtr->darkColorPtr->pixel; + borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + + /* + * Compute the light shadow color + */ + + if (g > MAX_INTENSITY*0.95) { + lightColor.red = (90 * r)/100; + lightColor.green = (90 * g)/100; + lightColor.blue = (90 * b)/100; + } else { + tmp1 = (14 * r)/10; + if (tmp1 > MAX_INTENSITY) { + tmp1 = MAX_INTENSITY; + } + tmp2 = (MAX_INTENSITY + r)/2; + lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2; + tmp1 = (14 * g)/10; + if (tmp1 > MAX_INTENSITY) { + tmp1 = MAX_INTENSITY; + } + tmp2 = (MAX_INTENSITY + g)/2; + lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2; + tmp1 = (14 * b)/10; + if (tmp1 > MAX_INTENSITY) { + tmp1 = MAX_INTENSITY; + } + tmp2 = (MAX_INTENSITY + b)/2; + lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2; + } + + /* + * Allocate the light shadow color and its GC + */ + + borderPtr->lightColorPtr = Tk_GetColorByValue(tkwin, &lightColor); + gcValues.foreground = borderPtr->lightColorPtr->pixel; + borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + return; + } + + if (borderPtr->shadow == None) { + borderPtr->shadow = Tk_GetBitmap((Tcl_Interp *) NULL, tkwin, + Tk_GetUid("gray50")); + if (borderPtr->shadow == None) { + Tcl_Panic("TkpGetShadows couldn't allocate bitmap for border"); + } + } + if (borderPtr->visual->map_entries > 2) { + /* + * This isn't a monochrome display, but the colormap either ran out of + * entries or didn't have very many to begin with. Generate the light + * shadows with a white stipple and the dark shadows with a black + * stipple. + */ + + gcValues.foreground = borderPtr->bgColorPtr->pixel; + gcValues.background = BlackPixelOfScreen(borderPtr->screen); + gcValues.stipple = borderPtr->shadow; + gcValues.fill_style = FillOpaqueStippled; + borderPtr->darkGC = Tk_GetGC(tkwin, + GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); + gcValues.foreground = WhitePixelOfScreen(borderPtr->screen); + gcValues.background = borderPtr->bgColorPtr->pixel; + borderPtr->lightGC = Tk_GetGC(tkwin, + GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); + return; + } + + /* + * This is just a measly monochrome display, hardly even worth its + * existence on this earth. Make one shadow a 50% stipple and the other + * the opposite of the background. + */ + + gcValues.foreground = WhitePixelOfScreen(borderPtr->screen); + gcValues.background = BlackPixelOfScreen(borderPtr->screen); + gcValues.stipple = borderPtr->shadow; + gcValues.fill_style = FillOpaqueStippled; + borderPtr->lightGC = Tk_GetGC(tkwin, + GCForeground|GCBackground|GCStipple|GCFillStyle, &gcValues); + if (borderPtr->bgColorPtr->pixel + == WhitePixelOfScreen(borderPtr->screen)) { + gcValues.foreground = BlackPixelOfScreen(borderPtr->screen); + borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + } else { + borderPtr->darkGC = borderPtr->lightGC; + borderPtr->lightGC = Tk_GetGC(tkwin, GCForeground, &gcValues); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetBorderPixels -- + * + * This routine returns the 5 COLORREFs used to draw a given 3d border. + * + * Results: + * Returns the colors in the specified array. + * + * Side effects: + * May cause the remaining colors to be allocated. + * + *---------------------------------------------------------------------- + */ + +COLORREF +TkWinGetBorderPixels( + Tk_Window tkwin, + Tk_3DBorder border, + int which) /* One of TK_3D_FLAT_GC, TK_3D_LIGHT_GC, + * TK_3D_DARK_GC, TK_3D_LIGHT2, TK_3D_DARK2 */ +{ + WinBorder *borderPtr = (WinBorder *) border; + + if (borderPtr->info.lightGC == None) { + TkpGetShadows(&borderPtr->info, tkwin); + } + switch (which) { + case TK_3D_FLAT_GC: + return borderPtr->info.bgColorPtr->pixel; + case TK_3D_LIGHT_GC: + if (borderPtr->info.lightColorPtr == NULL) { + return WhitePixelOfScreen(borderPtr->info.screen); + } + return borderPtr->info.lightColorPtr->pixel; + case TK_3D_DARK_GC: + if (borderPtr->info.darkColorPtr == NULL) { + return BlackPixelOfScreen(borderPtr->info.screen); + } + return borderPtr->info.darkColorPtr->pixel; + case TK_3D_LIGHT2: + return borderPtr->light2ColorPtr->pixel; + case TK_3D_DARK2: + return borderPtr->dark2ColorPtr->pixel; + } + return 0; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinButton.c b/tk8.6/win/tkWinButton.c new file mode 100644 index 0000000..e46bcb3 --- /dev/null +++ b/tk8.6/win/tkWinButton.c @@ -0,0 +1,1307 @@ +/* + * tkWinButton.c -- + * + * This file implements the Windows specific portion of the button + * widgets. + * + * Copyright (c) 1996-1998 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#define OEMRESOURCE +#include "tkWinInt.h" +#include "tkButton.h" + +/* + * These macros define the base style flags for the different button types. + */ + +#define LABEL_STYLE (BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS) +#define PUSH_STYLE (LABEL_STYLE | BS_PUSHBUTTON) +#define CHECK_STYLE (LABEL_STYLE | BS_CHECKBOX) +#define RADIO_STYLE (LABEL_STYLE | BS_RADIOBUTTON) + +/* + * Declaration of Windows specific button structure. + */ + +typedef struct WinButton { + TkButton info; /* Generic button info. */ + WNDPROC oldProc; /* Old window procedure. */ + HWND hwnd; /* Current window handle. */ + Pixmap pixmap; /* Bitmap for rendering the button. */ + DWORD style; /* Window style flags. */ +} WinButton; + +/* + * The following macro reverses the order of RGB bytes to convert between + * RGBQUAD and COLORREF values. + */ + +#define FlipColor(rgb) (RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb))) + +/* + * The following enumeration defines the meaning of the palette entries in the + * "buttons" image used to draw checkbox and radiobutton indicators. + */ + +enum { + PAL_CHECK = 0, + PAL_TOP_OUTER = 1, + PAL_BOTTOM_OUTER = 2, + PAL_BOTTOM_INNER = 3, + PAL_INTERIOR = 4, + PAL_TOP_INNER = 5, + PAL_BACKGROUND = 6 +}; + +/* + * Cached information about the boxes bitmap, and the default border width for + * a button in string form for use in Tk_OptionSpec for the various button + * widget classes. + */ + +typedef struct ThreadSpecificData { + BITMAPINFOHEADER *boxesPtr; /* Information about the bitmap. */ + DWORD *boxesPalette; /* Pointer to color palette. */ + LPSTR boxesBits; /* Pointer to bitmap data. */ + DWORD boxHeight; /* Height of each sub-image. */ + DWORD boxWidth; /* Width of each sub-image. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Declarations for functions defined in this file. + */ + +static LRESULT CALLBACK ButtonProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); +static Window CreateProc(Tk_Window tkwin, Window parent, + ClientData instanceData); +static void InitBoxes(void); + +/* + * The class procedure table for the button widgets. + */ + +const Tk_ClassProcs tkpButtonProcs = { + sizeof(Tk_ClassProcs), /* size */ + TkButtonWorldChanged, /* worldChangedProc */ + CreateProc, /* createProc */ + NULL /* modalProc */ +}; + + +/* + *---------------------------------------------------------------------- + * + * InitBoxes -- + * + * This function load the Tk 3d button bitmap. "buttons" is a 16 color + * bitmap that is laid out such that the top row contains the 4 checkbox + * images, and the bottom row contains the radio button images. Note that + * the bitmap is stored in bottom-up format. Also, the first seven + * palette entries are used to identify the different parts of the + * bitmaps so we can do the appropriate color mappings based on the + * current button colors. + * + * Results: + * None. + * + * Side effects: + * Loads the "buttons" resource. + * + *---------------------------------------------------------------------- + */ + +static void +InitBoxes(void) +{ + /* + * For DLLs like Tk, the HINSTANCE is the same as the HMODULE. + */ + + HMODULE module = (HINSTANCE) Tk_GetHINSTANCE(); + HRSRC hrsrc; + HGLOBAL hblk; + LPBITMAPINFOHEADER newBitmap; + DWORD size; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + hrsrc = FindResource(module, TEXT("buttons"), RT_BITMAP); + if (hrsrc == NULL) { + Tcl_Panic("FindResource() failed for buttons bitmap resource, " + "resources in tk_base.rc must be linked into Tk dll or static executable"); + } else { + hblk = LoadResource(module, hrsrc); + tsdPtr->boxesPtr = (LPBITMAPINFOHEADER)LockResource(hblk); + } + + /* + * Copy the DIBitmap into writable memory. + */ + + if (tsdPtr->boxesPtr != NULL && !(tsdPtr->boxesPtr->biWidth % 4) + && !(tsdPtr->boxesPtr->biHeight % 2)) { + size = tsdPtr->boxesPtr->biSize + (1 << tsdPtr->boxesPtr->biBitCount) + * sizeof(RGBQUAD) + tsdPtr->boxesPtr->biSizeImage; + newBitmap = ckalloc(size); + memcpy(newBitmap, tsdPtr->boxesPtr, size); + tsdPtr->boxesPtr = newBitmap; + tsdPtr->boxWidth = tsdPtr->boxesPtr->biWidth / 4; + tsdPtr->boxHeight = tsdPtr->boxesPtr->biHeight / 2; + tsdPtr->boxesPalette = (DWORD*) (((LPSTR) tsdPtr->boxesPtr) + + tsdPtr->boxesPtr->biSize); + tsdPtr->boxesBits = ((LPSTR) tsdPtr->boxesPalette) + + ((1 << tsdPtr->boxesPtr->biBitCount) * sizeof(RGBQUAD)); + } else { + tsdPtr->boxesPtr = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpButtonSetDefaults -- + * + * This procedure is invoked before option tables are created for + * buttons. It modifies some of the default values to match the current + * values defined for this platform. + * + * Results: + * Some of the default values in *specPtr are modified. + * + * Side effects: + * Updates some of. + * + *---------------------------------------------------------------------- + */ + +void +TkpButtonSetDefaults() +{ + int width = GetSystemMetrics(SM_CXEDGE); + if (width > 0) { + sprintf(tkDefButtonBorderWidth, "%d", width); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpCreateButton -- + * + * Allocate a new TkButton structure. + * + * Results: + * Returns a newly allocated TkButton structure. + * + * Side effects: + * Registers an event handler for the widget. + * + *---------------------------------------------------------------------- + */ + +TkButton * +TkpCreateButton( + Tk_Window tkwin) +{ + WinButton *butPtr; + + butPtr = ckalloc(sizeof(WinButton)); + butPtr->hwnd = NULL; + return (TkButton *) butPtr; +} + +/* + *---------------------------------------------------------------------- + * + * CreateProc -- + * + * This function creates a new Button control, subclasses the instance, + * and generates a new Window object. + * + * Results: + * Returns the newly allocated Window object, or None on failure. + * + * Side effects: + * Causes a new Button control to come into existence. + * + *---------------------------------------------------------------------- + */ + +static Window +CreateProc( + Tk_Window tkwin, /* Token for window. */ + Window parentWin, /* Parent of new window. */ + ClientData instanceData) /* Button instance data. */ +{ + Window window; + HWND parent; + const TCHAR *class; + WinButton *butPtr = (WinButton *)instanceData; + + parent = Tk_GetHWND(parentWin); + if (butPtr->info.type == TYPE_LABEL) { + class = TEXT("STATIC"); + butPtr->style = SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; + } else { + class = TEXT("BUTTON"); + butPtr->style = BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS; + } + butPtr->hwnd = CreateWindow(class, NULL, butPtr->style, + Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), + parent, NULL, Tk_GetHINSTANCE(), NULL); + SetWindowPos(butPtr->hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + butPtr->oldProc = (WNDPROC)SetWindowLongPtr(butPtr->hwnd, GWLP_WNDPROC, + (LONG_PTR) ButtonProc); + + window = Tk_AttachHWND(tkwin, butPtr->hwnd); + return window; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyButton -- + * + * Free data structures associated with the button control. + * + * Results: + * None. + * + * Side effects: + * Restores the default control state. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyButton( + TkButton *butPtr) +{ + WinButton *winButPtr = (WinButton *)butPtr; + HWND hwnd = winButPtr->hwnd; + + if (hwnd) { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) winButPtr->oldProc); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDisplayButton -- + * + * This procedure is invoked to display a button widget. It is normally + * invoked as an idle handler. + * + * Results: + * None. + * + * Side effects: + * Information appears on the screen. The REDRAW_PENDING flag is cleared. + * + *---------------------------------------------------------------------- + */ + +void +TkpDisplayButton( + ClientData clientData) /* Information about widget. */ +{ + TkWinDCState state; + HDC dc; + register TkButton *butPtr = (TkButton *) clientData; + GC gc; + Tk_3DBorder border; + Pixmap pixmap; + int x = 0; /* Initialization only needed to stop compiler + * warning. */ + int y, relief; + register Tk_Window tkwin = butPtr->tkwin; + int width = 0, height = 0, haveImage = 0, haveText = 0, drawRing = 0; + RECT rect; + int defaultWidth; /* Width of default ring. */ + int offset; /* 0 means this is a label widget. 1 means it + * is a flavor of button, so we offset the + * text to make the button appear to move up + * and down as the relief changes. */ + int textXOffset = 0, textYOffset = 0; + /* Text offsets for use with compound buttons + * and focus ring. */ + int imageWidth, imageHeight; + int imageXOffset = 0, imageYOffset = 0; + /* Image information that will be used to + * restrict disabled pixmap as well. */ + DWORD *boxesPalette; + + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + boxesPalette= tsdPtr->boxesPalette; + butPtr->flags &= ~REDRAW_PENDING; + if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) { + return; + } + + border = butPtr->normalBorder; + if ((butPtr->state == STATE_DISABLED) && (butPtr->disabledFg != NULL)) { + gc = butPtr->disabledGC; + } else if ((butPtr->state == STATE_ACTIVE) + && !Tk_StrictMotif(butPtr->tkwin)) { + gc = butPtr->activeTextGC; + border = butPtr->activeBorder; + } else { + gc = butPtr->normalTextGC; + } + if ((butPtr->flags & SELECTED) && (butPtr->state != STATE_ACTIVE) + && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) { + border = butPtr->selectBorder; + } + + /* + * Override the relief specified for the button if this is a checkbutton + * or radiobutton and there's no indicator. The new relief is as follows: + * If the button is select --> "sunken" + * If relief==overrelief --> relief + * Otherwise --> overrelief + * + * The effect we are trying to achieve is as follows: + * + * value mouse-over? --> relief + * ------- ------------ -------- + * off no flat + * off yes raised + * on no sunken + * on yes sunken + * + * This is accomplished by configuring the checkbutton or radiobutton like + * this: + * + * -indicatoron 0 -overrelief raised -offrelief flat + * + * Bindings (see library/button.tcl) will copy the -overrelief into + * -relief on mouseover. Hence, we can tell if we are in mouse-over by + * comparing relief against overRelief. This is an aweful kludge, but it + * gives use the desired behavior while keeping the code backwards + * compatible. + */ + + relief = butPtr->relief; + if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { + if (butPtr->flags & SELECTED) { + relief = TK_RELIEF_SUNKEN; + } else if (butPtr->overRelief != relief) { + relief = butPtr->offRelief; + } + } + + /* + * Compute width of default ring and offset for pushed buttons. + */ + + if (butPtr->type == TYPE_BUTTON) { + defaultWidth = ((butPtr->defaultState == DEFAULT_ACTIVE) + ? butPtr->highlightWidth : 0); + offset = 1; + } else { + defaultWidth = 0; + if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) { + offset = 1; + } else { + offset = 0; + } + } + + /* + * In order to avoid screen flashes, this procedure redraws the button in + * a pixmap, then copies the pixmap to the screen in a single operation. + * This means that there's no point in time where the on-sreen image has + * been cleared. + */ + + pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin), + Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin)); + Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin), + Tk_Height(tkwin), 0, TK_RELIEF_FLAT); + + /* + * Display image or bitmap or text for button. + */ + + if (butPtr->image != None) { + Tk_SizeOfImage(butPtr->image, &width, &height); + haveImage = 1; + } else if (butPtr->bitmap != None) { + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height); + haveImage = 1; + } + imageWidth = width; + imageHeight = height; + + haveText = (butPtr->textWidth != 0 && butPtr->textHeight != 0); + + if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { + int fullWidth = 0, fullHeight = 0; + + switch ((enum compound) butPtr->compound) { + case COMPOUND_TOP: + case COMPOUND_BOTTOM: + /* + * Image is above or below text. + */ + + if (butPtr->compound == COMPOUND_TOP) { + textYOffset = height + butPtr->padY; + } else { + imageYOffset = butPtr->textHeight + butPtr->padY; + } + fullHeight = height + butPtr->textHeight + butPtr->padY; + fullWidth = (width > butPtr->textWidth ? width : + butPtr->textWidth); + textXOffset = (fullWidth - butPtr->textWidth)/2; + imageXOffset = (fullWidth - width)/2; + break; + + case COMPOUND_LEFT: + case COMPOUND_RIGHT: + /* + * Image is left or right of text. + */ + + if (butPtr->compound == COMPOUND_LEFT) { + textXOffset = width + butPtr->padX; + } else { + imageXOffset = butPtr->textWidth + butPtr->padX; + } + fullWidth = butPtr->textWidth + butPtr->padX + width; + fullHeight = (height > butPtr->textHeight ? height : + butPtr->textHeight); + textYOffset = (fullHeight - butPtr->textHeight)/2; + imageYOffset = (fullHeight - height)/2; + break; + + case COMPOUND_CENTER: + /* + * Image and text are superimposed. + */ + + fullWidth = (width > butPtr->textWidth ? width : + butPtr->textWidth); + fullHeight = (height > butPtr->textHeight ? height : + butPtr->textHeight); + textXOffset = (fullWidth - butPtr->textWidth)/2; + imageXOffset = (fullWidth - width)/2; + textYOffset = (fullHeight - butPtr->textHeight)/2; + imageYOffset = (fullHeight - height)/2; + break; + case COMPOUND_NONE: + break; + } + TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, + butPtr->indicatorSpace + fullWidth, fullHeight, &x, &y); + x += butPtr->indicatorSpace; + + if (relief == TK_RELIEF_SUNKEN) { + x += offset; + y += offset; + } + imageXOffset += x; + imageYOffset += y; + if (butPtr->image != NULL) { + if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) { + Tk_RedrawImage(butPtr->selectImage, 0, 0, + width, height, pixmap, imageXOffset, imageYOffset); + } else if ((butPtr->tristateImage != NULL) + && (butPtr->flags & TRISTATED)) { + Tk_RedrawImage(butPtr->tristateImage, 0, 0, + width, height, pixmap, imageXOffset, imageYOffset); + } else { + Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, + imageXOffset, imageYOffset); + } + } else { + XSetClipOrigin(butPtr->display, gc, imageXOffset, imageYOffset); + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, + 0, 0, (unsigned int) width, (unsigned int) height, + imageXOffset, imageYOffset, 1); + XSetClipOrigin(butPtr->display, gc, 0, 0); + } + if ((butPtr->state == STATE_DISABLED) && + (butPtr->disabledFg != NULL)) { + COLORREF oldFgColor = gc->foreground; + + if (gc->background == GetSysColor(COLOR_BTNFACE)) { + gc->foreground = GetSysColor(COLOR_3DHILIGHT); + Tk_DrawTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x + textXOffset + 1, + y + textYOffset + 1, 0, -1); + Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x + textXOffset + 1, + y + textYOffset + 1, + butPtr->underline); + gc->foreground = oldFgColor; + } + } + + Tk_DrawTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x + textXOffset, y + textYOffset, 0, -1); + Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x + textXOffset, y + textYOffset, + butPtr->underline); + height = fullHeight; + drawRing = 1; + } else { + if (haveImage) { + TkComputeAnchor(butPtr->anchor, tkwin, 0, 0, + butPtr->indicatorSpace + width, height, &x, &y); + x += butPtr->indicatorSpace; + + if (relief == TK_RELIEF_SUNKEN) { + x += offset; + y += offset; + } + imageXOffset += x; + imageYOffset += y; + if (butPtr->image != NULL) { + if ((butPtr->selectImage != NULL) && + (butPtr->flags & SELECTED)) { + Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, + pixmap, imageXOffset, imageYOffset); + } else if ((butPtr->tristateImage != NULL) && + (butPtr->flags & TRISTATED)) { + Tk_RedrawImage(butPtr->tristateImage, 0, 0, width, height, + pixmap, imageXOffset, imageYOffset); + } else { + Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap, + imageXOffset, imageYOffset); + } + } else { + XSetClipOrigin(butPtr->display, gc, x, y); + XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0, + (unsigned int) width, (unsigned int) height, x, y, 1); + XSetClipOrigin(butPtr->display, gc, 0, 0); + } + } else { + TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY, + butPtr->indicatorSpace + butPtr->textWidth, + butPtr->textHeight, &x, &y); + + x += butPtr->indicatorSpace; + + if (relief == TK_RELIEF_SUNKEN) { + x += offset; + y += offset; + } + if ((butPtr->state == STATE_DISABLED) && + (butPtr->disabledFg != NULL)) { + COLORREF oldFgColor = gc->foreground; + if (gc->background == GetSysColor(COLOR_BTNFACE)) { + gc->foreground = GetSysColor(COLOR_3DHILIGHT); + Tk_DrawTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, + x + 1, y + 1, 0, -1); + Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x + 1, y + 1, butPtr->underline); + gc->foreground = oldFgColor; + } + } + Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout, + x, y, 0, -1); + Tk_UnderlineTextLayout(butPtr->display, pixmap, gc, + butPtr->textLayout, x, y, butPtr->underline); + + height = butPtr->textHeight; + drawRing = 1; + } + } + + /* + * Draw the focus ring. If this is a push button then we need to put it + * around the inner edge of the border, otherwise we put it around the + * text. The text offsets are only non-zero when this is a compound + * button. + */ + + if (drawRing && butPtr->flags & GOT_FOCUS && butPtr->type != TYPE_LABEL) { + dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); + if (butPtr->type == TYPE_BUTTON || !butPtr->indicatorOn) { + rect.top = butPtr->borderWidth + 1 + defaultWidth; + rect.left = rect.top; + rect.right = Tk_Width(tkwin) - rect.left; + rect.bottom = Tk_Height(tkwin) - rect.top; + } else { + rect.top = y-1 + textYOffset; + rect.left = x-1 + textXOffset; + rect.right = x+butPtr->textWidth + 1 + textXOffset; + rect.bottom = y+butPtr->textHeight + 2 + textYOffset; + } + SetTextColor(dc, gc->foreground); + SetBkColor(dc, gc->background); + DrawFocusRect(dc, &rect); + TkWinReleaseDrawableDC(pixmap, dc, &state); + } + + y += height/2; + + /* + * Draw the indicator for check buttons and radio buttons. At this point x + * and y refer to the top-left corner of the text or image or bitmap. + */ + + if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn + && tsdPtr->boxesPtr) { + int xSrc, ySrc; + + x -= butPtr->indicatorSpace; + y -= butPtr->indicatorDiameter / 2; + + xSrc = (butPtr->flags & (SELECTED|TRISTATED)) ? tsdPtr->boxWidth : 0; + if (butPtr->state == STATE_ACTIVE) { + xSrc += tsdPtr->boxWidth*2; + } + ySrc = (butPtr->type == TYPE_RADIO_BUTTON) ? 0 : tsdPtr->boxHeight; + + /* + * Update the palette in the boxes bitmap to reflect the current + * button colors. Note that this code relies on the layout of the + * bitmap's palette. Also, all of the colors used to draw the bitmap + * must be in the palette that is selected into the DC of the + * offscreen pixmap. This requires that the static colors be placed + * into the palette. + */ + + if ((butPtr->state == STATE_DISABLED) + && (butPtr->disabledFg == NULL)) { + boxesPalette[PAL_CHECK] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_DARK_GC)); + } else { + boxesPalette[PAL_CHECK] = FlipColor(gc->foreground); + } + boxesPalette[PAL_TOP_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_DARK_GC)); + boxesPalette[PAL_TOP_INNER] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_DARK2)); + boxesPalette[PAL_BOTTOM_INNER] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_LIGHT2)); + boxesPalette[PAL_BOTTOM_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_LIGHT_GC)); + if ((butPtr->state == STATE_DISABLED) || (butPtr->flags & TRISTATED)) { + boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_LIGHT2)); + } else if (butPtr->selectBorder != NULL) { + boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin, + butPtr->selectBorder, TK_3D_FLAT_GC)); + } else { + boxesPalette[PAL_INTERIOR] = FlipColor(GetSysColor(COLOR_WINDOW)); + } + boxesPalette[PAL_BACKGROUND] = FlipColor(TkWinGetBorderPixels(tkwin, + border, TK_3D_FLAT_GC)); + + dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); + StretchDIBits(dc, x, y, (int)tsdPtr->boxWidth, (int)tsdPtr->boxHeight, + xSrc, ySrc, (int)tsdPtr->boxWidth, (int)tsdPtr->boxHeight, + tsdPtr->boxesBits, (LPBITMAPINFO) tsdPtr->boxesPtr, + DIB_RGB_COLORS, SRCCOPY); + TkWinReleaseDrawableDC(pixmap, dc, &state); + } + + /* + * If the button is disabled with a stipple rather than a special + * foreground color, generate the stippled effect. If the widget is + * selected and we use a different background color when selected, must + * temporarily modify the GC so the stippling is the right color. + */ + + if ((butPtr->state == STATE_DISABLED) + && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) { + if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn + && (butPtr->selectBorder != NULL)) { + XSetForeground(butPtr->display, butPtr->stippleGC, + Tk_3DBorderColor(butPtr->selectBorder)->pixel); + } + + /* + * Stipple the whole button if no disabledFg was specified, otherwise + * restrict stippling only to displayed image + */ + + if (butPtr->disabledFg == NULL) { + XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, 0, 0, + (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin)); + } else { + XFillRectangle(butPtr->display, pixmap, butPtr->stippleGC, + imageXOffset, imageYOffset, + (unsigned) imageWidth, (unsigned) imageHeight); + } + if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn + && (butPtr->selectBorder != NULL)) { + XSetForeground(butPtr->display, butPtr->stippleGC, + Tk_3DBorderColor(butPtr->normalBorder)->pixel); + } + } + + /* + * Draw the border and traversal highlight last. This way, if the button's + * contents overflow they'll be covered up by the border. + */ + + if (relief != TK_RELIEF_FLAT) { + Tk_Draw3DRectangle(tkwin, pixmap, border, + defaultWidth, defaultWidth, + Tk_Width(tkwin) - 2*defaultWidth, + Tk_Height(tkwin) - 2*defaultWidth, + butPtr->borderWidth, relief); + } + if (defaultWidth != 0) { + dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state); + TkWinFillRect(dc, 0, 0, Tk_Width(tkwin), defaultWidth, + (int) butPtr->highlightColorPtr->pixel); + TkWinFillRect(dc, 0, 0, defaultWidth, Tk_Height(tkwin), + (int) butPtr->highlightColorPtr->pixel); + TkWinFillRect(dc, 0, Tk_Height(tkwin) - defaultWidth, + Tk_Width(tkwin), defaultWidth, + (int) butPtr->highlightColorPtr->pixel); + TkWinFillRect(dc, Tk_Width(tkwin) - defaultWidth, 0, + defaultWidth, Tk_Height(tkwin), + (int) butPtr->highlightColorPtr->pixel); + TkWinReleaseDrawableDC(pixmap, dc, &state); + } + + if (butPtr->flags & GOT_FOCUS) { + Tk_SetCaretPos(tkwin, x, y, 0 /* not used */); + } + + /* + * Copy the information from the off-screen pixmap onto the screen, then + * delete the pixmap. + */ + + XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin), + butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin), + (unsigned) Tk_Height(tkwin), 0, 0); + Tk_FreePixmap(butPtr->display, pixmap); +} + +/* + *---------------------------------------------------------------------- + * + * TkpComputeButtonGeometry -- + * + * After changes in a button's text or bitmap, this procedure recomputes + * the button's geometry and passes this information along to the + * geometry manager for the window. + * + * Results: + * None. + * + * Side effects: + * The button's window may change size. + * + *---------------------------------------------------------------------- + */ + +void +TkpComputeButtonGeometry( + register TkButton *butPtr) /* Button whose geometry may have changed. */ +{ + int txtWidth, txtHeight; /* Width and height of text */ + int imgWidth, imgHeight; /* Width and height of image */ + int width = 0, height = 0; /* Width and height of button */ + int haveImage, haveText; + int avgWidth; + int minWidth; + /* Vertical and horizontal dialog units size in pixels. */ + double vDLU, hDLU; + Tk_FontMetrics fm; + + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (butPtr->highlightWidth < 0) { + butPtr->highlightWidth = 0; + } + butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth; + butPtr->indicatorSpace = 0; + + if (!tsdPtr->boxesPtr) { + InitBoxes(); + } + + /* + * Figure out image metrics. + */ + + if (butPtr->image != NULL) { + Tk_SizeOfImage(butPtr->image, &imgWidth, &imgHeight); + haveImage = 1; + } else if (butPtr->bitmap != None) { + Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, + &imgWidth, &imgHeight); + haveImage = 1; + } else { + imgWidth = 0; + imgHeight = 0; + haveImage = 0; + } + + /* + * Figure out font metrics (even if we don't have text because we need + * DLUs (based on font, not text) for some spacing calculations below). + */ + + Tk_FreeTextLayout(butPtr->textLayout); + butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont, + Tcl_GetString(butPtr->textPtr), -1, butPtr->wrapLength, + butPtr->justify, 0, &butPtr->textWidth, &butPtr->textHeight); + + txtWidth = butPtr->textWidth; + txtHeight = butPtr->textHeight; + haveText = (*(Tcl_GetString(butPtr->textPtr)) != '\0'); + avgWidth = (Tk_TextWidth(butPtr->tkfont, + "abcdefghijklmnopqurstuvwzyABCDEFGHIJKLMNOPQURSTUVWZY", + 52) + 26) / 52; + Tk_GetFontMetrics(butPtr->tkfont, &fm); + + /* + * Compute dialog units for layout calculations. + */ + + hDLU = avgWidth / 4.0; + vDLU = fm.linespace / 8.0; + + /* + * First, let's try to compute button size "by the book" (See "Microsoft + * Windows User Experience" (ISBN 0-7356-0566-1), Chapter 14 - Visual + * Design, Section 4 - Layout (page 448)). + * + * Note, that Tk "buttons" are Microsoft "Command buttons", Tk + * "checkbuttons" are Microsoft "check boxes", Tk "radiobuttons" are + * Microsoft "option buttons", and Tk "labels" are Microsoft "text + * labels". + */ + + /* + * Set width and height by button type; See User Experience table, p449. + * These are text-based measurements, even if the text is "". If there is + * an image, height will get set again later. + */ + + switch (butPtr->type) { + case TYPE_BUTTON: + /* + * First compute the minimum width of the button in characters. MWUE + * says that the button should be 50 DLUs. We allow 6 DLUs padding + * left and right. (There is no rule but this is consistent with the + * fact that button text is 8 DLUs high and buttons are 14 DLUs high.) + * + * The width is specified in characters. A character is, by + * definition, 4 DLUs wide. 11 char * 4 DLU is 44 DLU + 6 DLU padding + * = 50 DLU. Therefore, width = -11 -> MWUE compliant buttons. + */ + + if (butPtr->width < 0) { + minWidth = -(butPtr->width); /* Min width in chars */ + width = avgWidth * minWidth; /* Allow for characters */ + width += (int)(0.5 + (6 * hDLU)); /* Add for padding */ + } + + /* + * If shrink-wrapping was requested (width = 0) or if the text is + * wider than the default button width, adjust the button width up to + * suit. + */ + + if (butPtr->width == 0 + || (txtWidth + (int)(0.5 + (6 * hDLU)) > width)) { + width = txtWidth + (int)(0.5 + (6 * hDLU)); + } + + /* + * The User Experience says 14 DLUs. Since text is, by definition, 8 + * DLU/line, this allows for multi-line text while working perfectly + * for single-line text. + */ + + height = txtHeight + (int)(0.5 + (6 * vDLU)); + + /* + * The above includes 6 DLUs of padding which should include defaults + * of 1 pixel of highlightwidth, 2 pixels of borderwidth, 1 pixel of + * padding and 1 pixel of extra inset on each side. Those will be + * added later so reduce width and height now to compensate. + */ + + width -= 10; + height -= 10; + + if (!haveImage) { + /* + * Extra inset for the focus ring. + */ + + butPtr->inset += 1; + } + break; + + case TYPE_LABEL: + /* + * The User Experience says, "as wide as needed". + */ + + width = txtWidth; + + /* + * The User Experience says, "8 (DLUs) per line of text". Since text + * is, by definition, 8 DLU/line, this allows for multi-line text + * while working perfectly for single-line text. + */ + + if (txtHeight) { + height = txtHeight; + } else { + /* + * If there's no text, we want the height to be one linespace. + */ + height = fm.linespace; + } + break; + + case TYPE_RADIO_BUTTON: + case TYPE_CHECK_BUTTON: { + /* + * See note for TYPE_LABEL. + */ + + width = txtWidth; + + /* + * The User Experience says 10 DLUs. (Is that one DLU above and below + * for the focus ring?) See note above about multi-line text and 8 + * DLU/line. + */ + + height = txtHeight + (int)(0.5 + (2.0 * vDLU)); + + /* + * The above includes 2 DLUs of padding which should include defaults + * of 1 pixel of highlightwidth, 0 pixels of borderwidth, and 1 pixel + * of padding on each side. Those will be added later so reduce height + * now to compensate. + */ + + height -= 4; + + /* + * Extra inset for the focus ring. + */ + butPtr->inset += 1; + break; + } + }/* switch */ + + /* + * At this point, the width and height are correct for a Tk text button, + * excluding padding and inset, but we have to allow for compound buttons. + * The image may be above, below, left, or right of the text. + */ + + /* + * If the button is compound (i.e., it shows both an image and text), the + * new geometry is a combination of the image and text geometry. We only + * honor the compound bit if the button has both text and an image, + * because otherwise it is not really a compound button. + */ + + if (butPtr->compound != COMPOUND_NONE && haveImage && haveText) { + switch ((enum compound) butPtr->compound) { + case COMPOUND_TOP: + case COMPOUND_BOTTOM: + /* + * Image is above or below text. + */ + + if (imgWidth > width) { + width = imgWidth; + } + height += imgHeight + butPtr->padY; + break; + + case COMPOUND_LEFT: + case COMPOUND_RIGHT: + + /* + * Image is left or right of text. + * + * Only increase width of button if image doesn't fit in slack + * space of default button width + */ + + if ((imgWidth + txtWidth + butPtr->padX) > width) { + width = imgWidth + txtWidth + butPtr->padX; + } + + if (imgHeight > height) { + height = imgHeight; + } + break; + + case COMPOUND_CENTER: + /* + * Image and text are superimposed. + */ + + if (imgWidth > width) { + width = imgWidth; + } + if (imgHeight > height) { + height = imgHeight; + } + break; + case COMPOUND_NONE: + break; + } /* switch */ + + /* + * Fix up for minimum width. + */ + + if (butPtr->width < 0) { + /* + * minWidth in pixels (because there's an image. + */ + + minWidth = -(butPtr->width); + if (width < minWidth) { + width = minWidth; + } + } else if (butPtr->width > 0) { + width = butPtr->width; + } + + if (butPtr->height > 0) { + height = butPtr->height; + } + + width += 2*butPtr->padX; + height += 2*butPtr->padY; + } else if (haveImage) { + if (butPtr->width > 0) { + width = butPtr->width; + } else { + width = imgWidth; + } + if (butPtr->height > 0) { + height = butPtr->height; + } else { + height = imgHeight; + } + } else { + /* + * No image. May or may not be text. May or may not be compound. + */ + + /* + * butPtr->width is in characters. We need to allow for that many + * characters on the face, not in the over-all button width + */ + + if (butPtr->width > 0) { + width = butPtr->width * avgWidth; + } + + /* + * butPtr->height is in lines of text. We need to allow for that many + * lines on the face, not in the over-all button height. + */ + + if (butPtr->height > 0) { + height = butPtr->height * fm.linespace; + + /* + * Make the same adjustments as above to get same height for e.g. + * a one line text with -height 0 or 1. [Bug #565485] + */ + + switch (butPtr->type) { + case TYPE_BUTTON: { + height += (int)(0.5 + (6 * vDLU)) - 10; + break; + } + case TYPE_RADIO_BUTTON: + case TYPE_CHECK_BUTTON: { + height += (int)(0.5 + (2.0 * vDLU)) - 4; + break; + } + } + } + + width += 2 * butPtr->padX; + height += 2 * butPtr->padY; + } + + /* + * Fix up width and height for indicator sizing and spacing. + */ + + if (butPtr->type == TYPE_RADIO_BUTTON + || butPtr->type == TYPE_CHECK_BUTTON) { + if (butPtr->indicatorOn) { + butPtr->indicatorDiameter = tsdPtr->boxHeight; + + /* + * Make sure we can see the whole indicator, even if the text or + * image is very small. + */ + + if (height < butPtr->indicatorDiameter) { + height = butPtr->indicatorDiameter; + } + + /* + * There is no rule for space between the indicator and the text + * (the two are atomic on 'Windows) but the User Experience page + * 451 says leave 3 hDLUs between "text labels and their + * associated controls". + */ + + butPtr->indicatorSpace = butPtr->indicatorDiameter + + (int)(0.5 + (3.0 * hDLU)); + width += butPtr->indicatorSpace; + } + } + + /* + * Inset is always added to the size. + */ + + width += 2 * butPtr->inset; + height += 2 * butPtr->inset; + + Tk_GeometryRequest(butPtr->tkwin, width, height); + Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset); +} + +/* + *---------------------------------------------------------------------- + * + * ButtonProc -- + * + * This function is call by Windows whenever an event occurs on a button + * control created by Tk. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * May generate events. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +ButtonProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + WinButton *butPtr; + Tk_Window tkwin = Tk_HWNDToWindow(hwnd); + + if (tkwin == NULL) { + Tcl_Panic("ButtonProc called on an invalid HWND"); + } + butPtr = (WinButton *)((TkWindow*)tkwin)->instanceData; + + switch(message) { + case WM_ERASEBKGND: + return 0; + + case BM_GETCHECK: + if (((butPtr->info.type == TYPE_CHECK_BUTTON) + || (butPtr->info.type == TYPE_RADIO_BUTTON)) + && butPtr->info.indicatorOn) { + return (butPtr->info.flags & SELECTED) + ? BST_CHECKED : BST_UNCHECKED; + } + return 0; + + case BM_GETSTATE: { + DWORD state = 0; + + if (((butPtr->info.type == TYPE_CHECK_BUTTON) + || (butPtr->info.type == TYPE_RADIO_BUTTON)) + && butPtr->info.indicatorOn) { + state = (butPtr->info.flags & SELECTED) + ? BST_CHECKED : BST_UNCHECKED; + } + if (butPtr->info.flags & GOT_FOCUS) { + state |= BST_FOCUS; + } + return state; + } + case WM_ENABLE: + break; + + case WM_PAINT: { + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + EndPaint(hwnd, &ps); + TkpDisplayButton((ClientData)butPtr); + + /* + * Special note: must cancel any existing idle handler for + * TkpDisplayButton; it's no longer needed, and TkpDisplayButton + * cleared the REDRAW_PENDING flag. + */ + + Tcl_CancelIdleCall(TkpDisplayButton, (ClientData)butPtr); + return 0; + } + case BN_CLICKED: { + /* + * OOPS: chromium fires WM_NULL regularly to ping if plugin is still + * alive. When using an external window (i.e. via the tcl plugin), this + * causes all buttons to fire once a second, so we need to make sure + * that we are not dealing with the chromium life check. + */ + if (wParam != 0 || lParam != 0) { + int code; + Tcl_Interp *interp = butPtr->info.interp; + + if (butPtr->info.state != STATE_DISABLED) { + Tcl_Preserve((ClientData)interp); + code = TkInvokeButton((TkButton*)butPtr); + if (code != TCL_OK && code != TCL_CONTINUE + && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (button invoke)"); + Tcl_BackgroundException(interp, code); + } + Tcl_Release((ClientData)interp); + } + Tcl_ServiceAll(); + return 0; + } + } + + default: + if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { + return result; + } + } + return DefWindowProc(hwnd, message, wParam, lParam); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinClipboard.c b/tk8.6/win/tkWinClipboard.c new file mode 100644 index 0000000..03c0cde --- /dev/null +++ b/tk8.6/win/tkWinClipboard.c @@ -0,0 +1,478 @@ +/* + * tkWinClipboard.c -- + * + * This file contains functions for managing the clipboard. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkSelect.h" +#include <shlobj.h> /* for DROPFILES */ + +static void UpdateClipboard(HWND hwnd); + +/* + *---------------------------------------------------------------------- + * + * TkSelGetSelection -- + * + * Retrieve the specified selection from another process. For now, only + * fetching XA_STRING from CLIPBOARD is supported. Eventually other types + * should be allowed. + * + * Results: + * The return value is a standard Tcl return value. If an error occurs + * (such as no selection exists) then an error message is left in the + * interp's result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkSelGetSelection( + Tcl_Interp *interp, /* Interpreter to use for reporting errors. */ + Tk_Window tkwin, /* Window on whose behalf to retrieve the + * selection (determines display from which to + * retrieve). */ + Atom selection, /* Selection to retrieve. */ + Atom target, /* Desired form in which selection is to be + * returned. */ + Tk_GetSelProc *proc, /* Procedure to call to process the selection, + * once it has been retrieved. */ + ClientData clientData) /* Arbitrary value to pass to proc. */ +{ + char *data, *destPtr; + Tcl_DString ds; + HGLOBAL handle; + Tcl_Encoding encoding; + int result, locale, noBackslash = 0; + + if ((selection != Tk_InternAtom(tkwin, "CLIPBOARD")) + || (target != XA_STRING) + || !OpenClipboard(NULL)) { + goto error; + } + + /* + * Attempt to get the data in Unicode form if available as this is less + * work that CF_TEXT. + */ + + result = TCL_ERROR; + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) { + handle = GetClipboardData(CF_UNICODETEXT); + if (!handle) { + CloseClipboard(); + goto error; + } + data = GlobalLock(handle); + Tcl_DStringInit(&ds); + Tcl_UniCharToUtfDString((Tcl_UniChar *)data, + Tcl_UniCharLen((Tcl_UniChar *)data), &ds); + GlobalUnlock(handle); + } else if (IsClipboardFormatAvailable(CF_TEXT)) { + /* + * Determine the encoding to use to convert this text. + */ + + if (IsClipboardFormatAvailable(CF_LOCALE)) { + handle = GetClipboardData(CF_LOCALE); + if (!handle) { + CloseClipboard(); + goto error; + } + + /* + * Get the locale identifier, determine the proper code page to + * use, and find the corresponding encoding. + */ + + Tcl_DStringInit(&ds); + Tcl_DStringAppend(&ds, "cp######", -1); + data = GlobalLock(handle); + + /* + * Even though the documentation claims that GetLocaleInfo expects + * an LCID, on Windows 9x it really seems to expect a LanguageID. + */ + + locale = LANGIDFROMLCID(*((int*)data)); + GetLocaleInfoA(locale, LOCALE_IDEFAULTANSICODEPAGE, + Tcl_DStringValue(&ds)+2, Tcl_DStringLength(&ds)-2); + GlobalUnlock(handle); + + encoding = Tcl_GetEncoding(NULL, Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + } else { + encoding = NULL; + } + + /* + * Fetch the text and convert it to UTF. + */ + + handle = GetClipboardData(CF_TEXT); + if (!handle) { + if (encoding) { + Tcl_FreeEncoding(encoding); + } + CloseClipboard(); + goto error; + } + data = GlobalLock(handle); + Tcl_ExternalToUtfDString(encoding, data, -1, &ds); + GlobalUnlock(handle); + if (encoding) { + Tcl_FreeEncoding(encoding); + } + } else if (IsClipboardFormatAvailable(CF_HDROP)) { + DROPFILES *drop; + + handle = GetClipboardData(CF_HDROP); + if (!handle) { + CloseClipboard(); + goto error; + } + Tcl_DStringInit(&ds); + drop = (DROPFILES *) GlobalLock(handle); + if (drop->fWide) { + WCHAR *fname = (WCHAR *) ((char *) drop + drop->pFiles); + Tcl_DString dsTmp; + int count = 0, len; + + while (*fname != 0) { + if (count) { + Tcl_DStringAppend(&ds, "\n", 1); + } + len = Tcl_UniCharLen((Tcl_UniChar *) fname); + Tcl_DStringInit(&dsTmp); + Tcl_UniCharToUtfDString((Tcl_UniChar *) fname, len, &dsTmp); + Tcl_DStringAppend(&ds, Tcl_DStringValue(&dsTmp), + Tcl_DStringLength(&dsTmp)); + Tcl_DStringFree(&dsTmp); + fname += len + 1; + count++; + } + noBackslash = (count > 0); + } + GlobalUnlock(handle); + } else { + CloseClipboard(); + goto error; + } + + /* + * Translate CR/LF to LF. + */ + + data = destPtr = Tcl_DStringValue(&ds); + while (*data) { + if (data[0] == '\r' && data[1] == '\n') { + data++; + } else if (noBackslash && data[0] == '\\') { + data++; + *destPtr++ = '/'; + } else { + *destPtr++ = *data++; + } + } + *destPtr = '\0'; + + /* + * Pass the data off to the selection procedure. + */ + + result = proc(clientData, interp, Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + CloseClipboard(); + return result; + + error: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s selection doesn't exist or form \"%s\" not defined", + Tk_GetAtomName(tkwin, selection), Tk_GetAtomName(tkwin, target))); + Tcl_SetErrorCode(interp, "TK", "SELECTION", "EXISTS", NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * TkSetSelectionOwner -- + * + * This function claims ownership of the specified selection. If the + * selection is CLIPBOARD, then we empty the system clipboard. + * + * Results: + * None. + * + * Side effects: + * Empties the system clipboard, and claims ownership. + * + *---------------------------------------------------------------------- + */ + +int +XSetSelectionOwner( + Display *display, + Atom selection, + Window owner, + Time time) +{ + HWND hwnd = owner ? TkWinGetHWND(owner) : NULL; + Tk_Window tkwin; + + /* + * This is a gross hack because the Tk_InternAtom interface is broken. It + * expects a Tk_Window, even though it only needs a Tk_Display. + */ + + tkwin = (Tk_Window) TkGetMainInfoList()->winPtr; + + if (selection == Tk_InternAtom(tkwin, "CLIPBOARD")) { + /* + * Only claim and empty the clipboard if we aren't already the owner + * of the clipboard. + */ + + if (GetClipboardOwner() != hwnd) { + UpdateClipboard(hwnd); + } + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinClipboardRender -- + * + * This function supplies the contents of the clipboard in response to a + * WM_RENDERFORMAT message. + * + * Results: + * None. + * + * Side effects: + * Sets the contents of the clipboard. + * + *---------------------------------------------------------------------- + */ + +void +TkWinClipboardRender( + TkDisplay *dispPtr, + UINT format) +{ + TkClipboardTarget *targetPtr; + TkClipboardBuffer *cbPtr; + HGLOBAL handle; + char *buffer, *p, *rawText, *endPtr; + int length; + Tcl_DString ds; + + for (targetPtr = dispPtr->clipTargetPtr; targetPtr != NULL; + targetPtr = targetPtr->nextPtr) { + if (targetPtr->type == XA_STRING) { + break; + } + } + + /* + * Count the number of newlines so we can add space for them in the + * resulting string. + */ + + length = 0; + if (targetPtr != NULL) { + for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL; + cbPtr = cbPtr->nextPtr) { + length += cbPtr->length; + for (p = cbPtr->buffer, endPtr = p + cbPtr->length; + p < endPtr; p++) { + if (*p == '\n') { + length++; + } + } + } + } + + /* + * Copy the data and change EOL characters. + */ + + buffer = rawText = ckalloc(length + 1); + if (targetPtr != NULL) { + for (cbPtr = targetPtr->firstBufferPtr; cbPtr != NULL; + cbPtr = cbPtr->nextPtr) { + for (p = cbPtr->buffer, endPtr = p + cbPtr->length; + p < endPtr; p++) { + if (*p == '\n') { + *buffer++ = '\r'; + } + *buffer++ = *p; + } + } + } + *buffer = '\0'; + + /* + * Depending on the platform, turn the data into Unicode or the system + * encoding before placing it on the clipboard. + */ + +#ifdef UNICODE + Tcl_DStringInit(&ds); + Tcl_WinUtfToTChar(rawText, -1, &ds); + ckfree(rawText); + handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, + (unsigned) Tcl_DStringLength(&ds) + 2); + if (!handle) { + Tcl_DStringFree(&ds); + return; + } + buffer = GlobalLock(handle); + memcpy(buffer, Tcl_DStringValue(&ds), + (unsigned) Tcl_DStringLength(&ds) + 2); + GlobalUnlock(handle); + Tcl_DStringFree(&ds); + SetClipboardData(CF_UNICODETEXT, handle); +#else + Tcl_UtfToExternalDString(NULL, rawText, -1, &ds); + ckfree(rawText); + handle = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE, + (unsigned) Tcl_DStringLength(&ds) + 1); + if (!handle) { + Tcl_DStringFree(&ds); + return; + } + buffer = GlobalLock(handle); + memcpy(buffer, Tcl_DStringValue(&ds), + (unsigned) Tcl_DStringLength(&ds) + 1); + GlobalUnlock(handle); + Tcl_DStringFree(&ds); + SetClipboardData(CF_TEXT, handle); +#endif +} + +/* + *---------------------------------------------------------------------- + * + * TkSelUpdateClipboard -- + * + * This function is called to force the clipboard to be updated after new + * data is added. + * + * Results: + * None. + * + * Side effects: + * Clears the current contents of the clipboard. + * + *---------------------------------------------------------------------- + */ + +void +TkSelUpdateClipboard( + TkWindow *winPtr, + TkClipboardTarget *targetPtr) +{ + HWND hwnd = TkWinGetHWND(winPtr->window); + UpdateClipboard(hwnd); +} + +/* + *---------------------------------------------------------------------- + * + * UpdateClipboard -- + * + * Take ownership of the clipboard, clear it, and indicate to the system + * the supported formats. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateClipboard( + HWND hwnd) +{ + TkWinUpdatingClipboard(TRUE); + OpenClipboard(hwnd); + EmptyClipboard(); + + SetClipboardData(CF_UNICODETEXT, NULL); + CloseClipboard(); + TkWinUpdatingClipboard(FALSE); +} + +/* + *-------------------------------------------------------------- + * + * TkSelEventProc -- + * + * This procedure is invoked whenever a selection-related event occurs. + * + * Results: + * None. + * + * Side effects: + * Lots: depends on the type of event. + * + *-------------------------------------------------------------- + */ + +void +TkSelEventProc( + Tk_Window tkwin, /* Window for which event was targeted. */ + register XEvent *eventPtr) /* X event: either SelectionClear, + * SelectionRequest, or SelectionNotify. */ +{ + if (eventPtr->type == SelectionClear) { + TkSelClearSelection(tkwin, eventPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkSelPropProc -- + * + * This procedure is invoked when property-change events occur on windows + * not known to the toolkit. This is a stub function under Windows. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkSelPropProc( + register XEvent *eventPtr) /* X PropertyChange event. */ +{ +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinColor.c b/tk8.6/win/tkWinColor.c new file mode 100644 index 0000000..ba9815c --- /dev/null +++ b/tk8.6/win/tkWinColor.c @@ -0,0 +1,589 @@ +/* + * tkWinColor.c -- + * + * Functions to map color names to system color values. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * Copyright (c) 1994 Software Research Associates, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkColor.h" + +/* + * The following structure is used to keep track of each color that is + * allocated by this module. + */ + +typedef struct WinColor { + TkColor info; /* Generic color information. */ + int index; /* Index for GetSysColor(), -1 if color is not + * a "live" system color. */ +} WinColor; + +/* + * The sysColors array contains the names and index values for the Windows + * indirect system color names. In use, all of the names will have the string + * "System" prepended, but we omit it in the table to save space. + */ + +typedef struct { + const char *name; + int index; +} SystemColorEntry; + +static const SystemColorEntry sysColors[] = { + {"3dDarkShadow", COLOR_3DDKSHADOW}, + {"3dLight", COLOR_3DLIGHT}, + {"ActiveBorder", COLOR_ACTIVEBORDER}, + {"ActiveCaption", COLOR_ACTIVECAPTION}, + {"AppWorkspace", COLOR_APPWORKSPACE}, + {"Background", COLOR_BACKGROUND}, + {"ButtonFace", COLOR_BTNFACE}, + {"ButtonHighlight", COLOR_BTNHIGHLIGHT}, + {"ButtonShadow", COLOR_BTNSHADOW}, + {"ButtonText", COLOR_BTNTEXT}, + {"CaptionText", COLOR_CAPTIONTEXT}, + {"DisabledText", COLOR_GRAYTEXT}, + {"GrayText", COLOR_GRAYTEXT}, + {"Highlight", COLOR_HIGHLIGHT}, + {"HighlightText", COLOR_HIGHLIGHTTEXT}, + {"InactiveBorder", COLOR_INACTIVEBORDER}, + {"InactiveCaption", COLOR_INACTIVECAPTION}, + {"InactiveCaptionText", COLOR_INACTIVECAPTIONTEXT}, + {"InfoBackground", COLOR_INFOBK}, + {"InfoText", COLOR_INFOTEXT}, + {"Menu", COLOR_MENU}, + {"MenuText", COLOR_MENUTEXT}, + {"Scrollbar", COLOR_SCROLLBAR}, + {"Window", COLOR_WINDOW}, + {"WindowFrame", COLOR_WINDOWFRAME}, + {"WindowText", COLOR_WINDOWTEXT} +}; + +/* + * Forward declarations for functions defined later in this file. + */ + +static int FindSystemColor(const char *name, XColor *colorPtr, + int *indexPtr); + +/* + *---------------------------------------------------------------------- + * + * FindSystemColor -- + * + * This routine finds the color entry that corresponds to the specified + * color. + * + * Results: + * Returns non-zero on success. The RGB values of the XColor will be + * initialized to the proper values on success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +FindSystemColor( + const char *name, /* Color name. */ + XColor *colorPtr, /* Where to store results. */ + int *indexPtr) /* Out parameter to store color index. */ +{ + int l, u, r, i; + int index; + + /* + * Perform a binary search on the sorted array of colors. + */ + + l = 0; + u = (sizeof(sysColors) / sizeof(sysColors[0])) - 1; + while (l <= u) { + i = (l + u) / 2; + r = strcasecmp(name, sysColors[i].name); + if (r == 0) { + break; + } else if (r < 0) { + u = i-1; + } else { + l = i+1; + } + } + if (l > u) { + return 0; + } + + *indexPtr = index = sysColors[i].index; + colorPtr->pixel = GetSysColor(index); + + /* + * x257 is (value<<8 + value) to get the properly bit shifted and padded + * value. [Bug: 4919] + */ + + colorPtr->red = GetRValue(colorPtr->pixel) * 257; + colorPtr->green = GetGValue(colorPtr->pixel) * 257; + colorPtr->blue = GetBValue(colorPtr->pixel) * 257; + colorPtr->flags = DoRed|DoGreen|DoBlue; + colorPtr->pad = 0; + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetColor -- + * + * Allocate a new TkColor for the color with the given name. + * + * Results: + * Returns a newly allocated TkColor, or NULL on failure. + * + * Side effects: + * May invalidate the colormap cache associated with tkwin upon + * allocating a new colormap entry. Allocates a new TkColor structure. + * + *---------------------------------------------------------------------- + */ + +TkColor * +TkpGetColor( + Tk_Window tkwin, /* Window in which color will be used. */ + Tk_Uid name) /* Name of color to allocated (in form + * suitable for passing to XParseColor). */ +{ + WinColor *winColPtr; + XColor color; + int index = -1; /* -1 indicates that this is not an indirect + * system color. */ + + /* + * Check to see if it is a system color or an X color string. If the color + * is found, allocate a new WinColor and store the XColor and the system + * color index. + */ + + if (((strncasecmp(name, "system", 6) == 0) + && FindSystemColor(name+6, &color, &index)) + || TkParseColor(Tk_Display(tkwin), Tk_Colormap(tkwin), name, + &color)) { + winColPtr = ckalloc(sizeof(WinColor)); + winColPtr->info.color = color; + winColPtr->index = index; + + XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin), + &winColPtr->info.color); + return (TkColor *) winColPtr; + } + return (TkColor *) NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetColorByValue -- + * + * Given a desired set of red-green-blue intensities for a color, locate + * a pixel value to use to draw that color in a given window. + * + * Results: + * The return value is a pointer to an TkColor structure that indicates + * the closest red, blue, and green intensities available to those + * specified in colorPtr, and also specifies a pixel value to use to draw + * in that color. + * + * Side effects: + * May invalidate the colormap cache for the specified window. Allocates + * a new TkColor structure. + * + *---------------------------------------------------------------------- + */ + +TkColor * +TkpGetColorByValue( + Tk_Window tkwin, /* Window in which color will be used. */ + XColor *colorPtr) /* Red, green, and blue fields indicate + * desired color. */ +{ + WinColor *tkColPtr = ckalloc(sizeof(WinColor)); + + tkColPtr->info.color.red = colorPtr->red; + tkColPtr->info.color.green = colorPtr->green; + tkColPtr->info.color.blue = colorPtr->blue; + tkColPtr->info.color.pixel = 0; + tkColPtr->index = -1; + XAllocColor(Tk_Display(tkwin), Tk_Colormap(tkwin), &tkColPtr->info.color); + return (TkColor *) tkColPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpFreeColor -- + * + * Release the specified color back to the system. + * + * Results: + * None + * + * Side effects: + * Invalidates the colormap cache for the colormap associated with the + * given color. + * + *---------------------------------------------------------------------- + */ + +void +TkpFreeColor( + TkColor *tkColPtr) /* Color to be released. Must have been + * allocated by TkpGetColor or + * TkpGetColorByValue. */ +{ + Screen *screen = tkColPtr->screen; + + XFreeColors(DisplayOfScreen(screen), tkColPtr->colormap, + &tkColPtr->color.pixel, 1, 0L); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinIndexOfColor -- + * + * Given a color, return the system color index that was used to create + * the color. + * + * Results: + * If the color was allocated using a system indirect color name, then + * the corresponding GetSysColor() index is returned. Otherwise, -1 is + * returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkWinIndexOfColor( + XColor *colorPtr) +{ + register WinColor *winColPtr = (WinColor *) colorPtr; + if (winColPtr->info.magic == COLOR_MAGIC) { + return winColPtr->index; + } + return -1; +} + +/* + *---------------------------------------------------------------------- + * + * XAllocColor -- + * + * Find the closest available color to the specified XColor. + * + * Results: + * Updates the color argument and returns 1 on success. Otherwise returns + * 0. + * + * Side effects: + * Allocates a new color in the palette. + * + *---------------------------------------------------------------------- + */ + +int +XAllocColor( + Display *display, + Colormap colormap, + XColor *color) +{ + TkWinColormap *cmap = (TkWinColormap *) colormap; + PALETTEENTRY entry, closeEntry; + HDC dc = GetDC(NULL); + + entry.peRed = (color->red) >> 8; + entry.peGreen = (color->green) >> 8; + entry.peBlue = (color->blue) >> 8; + entry.peFlags = 0; + + if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { + unsigned long sizePalette = GetDeviceCaps(dc, SIZEPALETTE); + UINT newPixel, closePixel; + int new; + size_t refCount; + Tcl_HashEntry *entryPtr; + UINT index; + + /* + * Find the nearest existing palette entry. + */ + + newPixel = RGB(entry.peRed, entry.peGreen, entry.peBlue); + index = GetNearestPaletteIndex(cmap->palette, newPixel); + GetPaletteEntries(cmap->palette, index, 1, &closeEntry); + closePixel = RGB(closeEntry.peRed, closeEntry.peGreen, + closeEntry.peBlue); + + /* + * If this is not a duplicate, allocate a new entry. Note that we may + * get values for index that are above the current size of the + * palette. This happens because we don't shrink the size of the + * palette object when we deallocate colors so there may be stale + * values that match in the upper slots. We should ignore those values + * and just put the new color in as if the colors had not matched. + */ + + if ((index >= cmap->size) || (newPixel != closePixel)) { + if (cmap->size == sizePalette) { + color->red = closeEntry.peRed * 257; + color->green = closeEntry.peGreen * 257; + color->blue = closeEntry.peBlue * 257; + entry = closeEntry; + if (index >= cmap->size) { + OutputDebugStringA("XAllocColor: Colormap is bigger than we thought"); + } + } else { + cmap->size++; + ResizePalette(cmap->palette, cmap->size); + SetPaletteEntries(cmap->palette, cmap->size - 1, 1, &entry); + } + } + + color->pixel = PALETTERGB(entry.peRed, entry.peGreen, entry.peBlue); + entryPtr = Tcl_CreateHashEntry(&cmap->refCounts, + INT2PTR(color->pixel), &new); + if (new) { + refCount = 1; + } else { + refCount = (size_t)Tcl_GetHashValue(entryPtr) + 1; + } + Tcl_SetHashValue(entryPtr, (void *)refCount); + } else { + /* + * Determine what color will actually be used on non-colormap systems. + */ + + color->pixel = GetNearestColor(dc, + RGB(entry.peRed, entry.peGreen, entry.peBlue)); + color->red = GetRValue(color->pixel) * 257; + color->green = GetGValue(color->pixel) * 257; + color->blue = GetBValue(color->pixel) * 257; + } + + ReleaseDC(NULL, dc); + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * XFreeColors -- + * + * Deallocate a block of colors. + * + * Results: + * None. + * + * Side effects: + * Removes entries for the current palette and compacts the remaining + * set. + * + *---------------------------------------------------------------------- + */ + +int +XFreeColors( + Display *display, + Colormap colormap, + unsigned long *pixels, + int npixels, + unsigned long planes) +{ + TkWinColormap *cmap = (TkWinColormap *) colormap; + COLORREF cref; + UINT count, index; + size_t refCount; + int i; + PALETTEENTRY entry, *entries; + Tcl_HashEntry *entryPtr; + HDC dc = GetDC(NULL); + + /* + * We don't have to do anything for non-palette devices. + */ + + if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { + /* + * This is really slow for large values of npixels. + */ + + for (i = 0; i < npixels; i++) { + entryPtr = Tcl_FindHashEntry(&cmap->refCounts, INT2PTR(pixels[i])); + if (!entryPtr) { + Tcl_Panic("Tried to free a color that isn't allocated"); + } + refCount = (size_t)Tcl_GetHashValue(entryPtr) - 1; + if (refCount == 0) { + cref = pixels[i] & 0x00ffffff; + index = GetNearestPaletteIndex(cmap->palette, cref); + GetPaletteEntries(cmap->palette, index, 1, &entry); + if (cref == RGB(entry.peRed, entry.peGreen, entry.peBlue)) { + count = cmap->size - index; + entries = ckalloc(sizeof(PALETTEENTRY) * count); + GetPaletteEntries(cmap->palette, index+1, count, entries); + SetPaletteEntries(cmap->palette, index, count, entries); + ckfree(entries); + cmap->size--; + } else { + Tcl_Panic("Tried to free a color that isn't allocated"); + } + Tcl_DeleteHashEntry(entryPtr); + } else { + Tcl_SetHashValue(entryPtr, (size_t)refCount); + } + } + } + ReleaseDC(NULL, dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XCreateColormap -- + * + * Allocate a new colormap. + * + * Results: + * Returns a newly allocated colormap. + * + * Side effects: + * Allocates an empty palette and color list. + * + *---------------------------------------------------------------------- + */ + +Colormap +XCreateColormap( + Display *display, + Window w, + Visual *visual, + int alloc) +{ + char logPalBuf[sizeof(LOGPALETTE) + 256 * sizeof(PALETTEENTRY)]; + LOGPALETTE *logPalettePtr; + PALETTEENTRY *entryPtr; + TkWinColormap *cmap; + Tcl_HashEntry *hashPtr; + int new; + UINT i; + HPALETTE sysPal; + + /* + * Allocate a starting palette with all of the reserved colors. + */ + + logPalettePtr = (LOGPALETTE *) logPalBuf; + logPalettePtr->palVersion = 0x300; + sysPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE); + logPalettePtr->palNumEntries = GetPaletteEntries(sysPal, 0, 256, + logPalettePtr->palPalEntry); + + cmap = ckalloc(sizeof(TkWinColormap)); + cmap->size = logPalettePtr->palNumEntries; + cmap->stale = 0; + cmap->palette = CreatePalette(logPalettePtr); + + /* + * Add hash entries for each of the static colors. + */ + + Tcl_InitHashTable(&cmap->refCounts, TCL_ONE_WORD_KEYS); + for (i = 0; i < logPalettePtr->palNumEntries; i++) { + entryPtr = logPalettePtr->palPalEntry + i; + hashPtr = Tcl_CreateHashEntry(&cmap->refCounts, INT2PTR(PALETTERGB( + entryPtr->peRed, entryPtr->peGreen, entryPtr->peBlue)), &new); + Tcl_SetHashValue(hashPtr, INT2PTR(1)); + } + + return (Colormap)cmap; +} + +/* + *---------------------------------------------------------------------- + * + * XFreeColormap -- + * + * Frees the resources associated with the given colormap. + * + * Results: + * None. + * + * Side effects: + * Deletes the palette associated with the colormap. Note that the + * palette must not be selected into a device context when this occurs. + * + *---------------------------------------------------------------------- + */ + +int +XFreeColormap( + Display *display, + Colormap colormap) +{ + TkWinColormap *cmap = (TkWinColormap *) colormap; + + if (!DeleteObject(cmap->palette)) { + Tcl_Panic("Unable to free colormap, palette is still selected"); + } + Tcl_DeleteHashTable(&cmap->refCounts); + ckfree(cmap); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinSelectPalette -- + * + * This function sets up the specified device context with a given + * palette. If the palette is stale, it realizes it in the background + * unless the palette is the current global palette. + * + * Results: + * Returns the previous palette selected into the device context. + * + * Side effects: + * May change the system palette. + * + *---------------------------------------------------------------------- + */ + +HPALETTE +TkWinSelectPalette( + HDC dc, + Colormap colormap) +{ + TkWinColormap *cmap = (TkWinColormap *) colormap; + HPALETTE oldPalette; + + oldPalette = SelectPalette(dc, cmap->palette, + (cmap->palette == TkWinGetSystemPalette()) ? FALSE : TRUE); + RealizePalette(dc); + return oldPalette; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinConfig.c b/tk8.6/win/tkWinConfig.c new file mode 100644 index 0000000..aeb9405 --- /dev/null +++ b/tk8.6/win/tkWinConfig.c @@ -0,0 +1,64 @@ +/* + * tkWinConfig.c -- + * + * This module implements the Windows system defaults for the + * configuration package. + * + * Copyright (c) 1997 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + + +/* + *---------------------------------------------------------------------- + * + * TkpGetSystemDefault -- + * + * Given a dbName and className for a configuration option, return a + * string representation of the option. + * + * Results: + * Returns a Tk_Uid that is the string identifier that identifies this + * option. Returns NULL if there are no system defaults that match this + * pair. + * + * Side effects: + * None, once the package is initialized. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TkpGetSystemDefault( + Tk_Window tkwin, /* A window to use. */ + const char *dbName, /* The option database name. */ + const char *className) /* The name of the option class. */ +{ + Tcl_Obj *valueObjPtr; + Tk_Uid classUid; + + if (tkwin == NULL) { + return NULL; + } + + valueObjPtr = NULL; + classUid = Tk_Class(tkwin); + + if (strcmp(classUid, "Menu") == 0) { + valueObjPtr = TkWinGetMenuSystemDefault(tkwin, dbName, className); + } + + return valueObjPtr; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinCursor.c b/tk8.6/win/tkWinCursor.c new file mode 100644 index 0000000..622ba4d --- /dev/null +++ b/tk8.6/win/tkWinCursor.c @@ -0,0 +1,272 @@ +/* + * tkWinCursor.c -- + * + * This file contains Win32 specific cursor related routines. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * The following data structure contains the system specific data necessary to + * control Windows cursors. + */ + +typedef struct { + TkCursor info; /* Generic cursor info used by tkCursor.c */ + HCURSOR winCursor; /* Win32 cursor handle. */ + int system; /* 1 if cursor is a system cursor, else 0. */ +} TkWinCursor; + +/* + * The HAND cursor is only present when WINVER >= 0x0500. If this is not + * available at runtime, it will default to the unix-style cursor. + */ + +#ifndef IDC_HAND +#define IDC_HAND MAKEINTRESOURCE(32649) +#endif +#ifndef IDC_HELP +#define IDC_HELP MAKEINTRESOURCE(32651) +#endif + +/* + * The table below is used to map from the name of a predefined cursor to its + * resource identifier. + */ + +static struct CursorName { + const char *name; + LPCTSTR id; +} cursorNames[] = { + {"starting", IDC_APPSTARTING}, + {"arrow", IDC_ARROW}, + {"ibeam", IDC_IBEAM}, + {"icon", IDC_ICON}, + {"no", IDC_NO}, + {"size", IDC_SIZEALL}, + {"size_ne_sw", IDC_SIZENESW}, + {"size_ns", IDC_SIZENS}, + {"size_nw_se", IDC_SIZENWSE}, + {"size_we", IDC_SIZEWE}, + {"uparrow", IDC_UPARROW}, + {"wait", IDC_WAIT}, + {"crosshair", IDC_CROSS}, + {"fleur", IDC_SIZEALL}, + {"sb_v_double_arrow", IDC_SIZENS}, + {"sb_h_double_arrow", IDC_SIZEWE}, + {"center_ptr", IDC_UPARROW}, + {"watch", IDC_WAIT}, + {"xterm", IDC_IBEAM}, + {"hand2", IDC_HAND}, + {"question_arrow", IDC_HELP}, + {NULL, 0} +}; + +/* + * The default cursor is used whenever no other cursor has been specified. + */ + +#define TK_DEFAULT_CURSOR IDC_ARROW + +/* + *---------------------------------------------------------------------- + * + * TkGetCursorByName -- + * + * Retrieve a system cursor by name. + * + * Results: + * Returns a new cursor, or NULL on errors. + * + * Side effects: + * Allocates a new cursor. + * + *---------------------------------------------------------------------- + */ + +TkCursor * +TkGetCursorByName( + Tcl_Interp *interp, /* Interpreter to use for error reporting. */ + Tk_Window tkwin, /* Window in which cursor will be used. */ + Tk_Uid string) /* Description of cursor. See manual entry for + * details on legal syntax. */ +{ + struct CursorName *namePtr; + TkWinCursor *cursorPtr; + int argc; + const char **argv = NULL; + + /* + * All cursor names are valid lists of one element (for + * Unix-compatability), even unadorned system cursor names. + */ + + if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) { + return NULL; + } + if (argc == 0) { + goto badCursorSpec; + } + + cursorPtr = ckalloc(sizeof(TkWinCursor)); + cursorPtr->info.cursor = (Tk_Cursor) cursorPtr; + cursorPtr->winCursor = NULL; + cursorPtr->system = 0; + + if (argv[0][0] == '@') { + /* + * Check for system cursor of type @<filename>, where only the name is + * allowed. This accepts any of: + * -cursor @/winnt/cursors/globe.ani + * -cursor @C:/Winnt/cursors/E_arrow.cur + * -cursor {@C:/Program\ Files/Cursors/bart.ani} + * -cursor {{@C:/Program Files/Cursors/bart.ani}} + * -cursor [list @[file join "C:/Program Files" Cursors bart.ani]] + */ + + if (Tcl_IsSafe(interp)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "can't get cursor from a file in a safe interpreter",-1)); + Tcl_SetErrorCode(interp, "TK", "SAFE", "CURSOR_FILE", NULL); + ckfree(argv); + ckfree(cursorPtr); + return NULL; + } + cursorPtr->winCursor = LoadCursorFromFileA(&(argv[0][1])); + } else { + /* + * Check for the cursor in the system cursor set. + */ + + for (namePtr = cursorNames; namePtr->name != NULL; namePtr++) { + if (strcmp(namePtr->name, argv[0]) == 0) { + cursorPtr->winCursor = LoadCursor(NULL, namePtr->id); + break; + } + } + + if (cursorPtr->winCursor == NULL) { + /* + * Hmm, it is not in the system cursor set. Check to see if it is + * one of our application resources. + */ + + cursorPtr->winCursor = LoadCursorA(Tk_GetHINSTANCE(), argv[0]); + } else { + cursorPtr->system = 1; + } + } + + if (cursorPtr->winCursor == NULL) { + ckfree(cursorPtr); + badCursorSpec: + ckfree(argv); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad cursor spec \"%s\"", string)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "CURSOR", NULL); + return NULL; + } + ckfree(argv); + return (TkCursor *) cursorPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkCreateCursorFromData -- + * + * Creates a cursor from the source and mask bits. + * + * Results: + * Returns a new cursor, or NULL on errors. + * + * Side effects: + * Allocates a new cursor. + * + *---------------------------------------------------------------------- + */ + +TkCursor * +TkCreateCursorFromData( + Tk_Window tkwin, /* Window in which cursor will be used. */ + const char *source, /* Bitmap data for cursor shape. */ + const char *mask, /* Bitmap data for cursor mask. */ + int width, int height, /* Dimensions of cursor. */ + int xHot, int yHot, /* Location of hot-spot in cursor. */ + XColor fgColor, /* Foreground color for cursor. */ + XColor bgColor) /* Background color for cursor. */ +{ + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TkpFreeCursor -- + * + * This procedure is called to release a cursor allocated by + * TkGetCursorByName. + * + * Results: + * None. + * + * Side effects: + * The cursor data structure is deallocated. + * + *---------------------------------------------------------------------- + */ + +void +TkpFreeCursor( + TkCursor *cursorPtr) +{ + /* TkWinCursor *winCursorPtr = (TkWinCursor *) cursorPtr; */ +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetCursor -- + * + * Set the global cursor. If the cursor is None, then use the default Tk + * cursor. + * + * Results: + * None. + * + * Side effects: + * Changes the mouse cursor. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetCursor( + TkpCursor cursor) +{ + HCURSOR hcursor; + TkWinCursor *winCursor = (TkWinCursor *) cursor; + + if (winCursor == NULL || winCursor->winCursor == NULL) { + hcursor = LoadCursor(NULL, TK_DEFAULT_CURSOR); + } else { + hcursor = winCursor->winCursor; + } + + if (hcursor != NULL) { + SetCursor(hcursor); + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinDefault.h b/tk8.6/win/tkWinDefault.h new file mode 100644 index 0000000..f389075 --- /dev/null +++ b/tk8.6/win/tkWinDefault.h @@ -0,0 +1,536 @@ +/* + * tkWinDefault.h -- + * + * This file defines the defaults for all options for all of + * the Tk widgets. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef _TKWINDEFAULT +#define _TKWINDEFAULT + +/* + * The definitions below provide symbolic names for the default colors. + * NORMAL_BG - Normal background color. + * ACTIVE_BG - Background color when widget is active. + * SELECT_BG - Background color for selected text. + * TROUGH - Background color for troughs in scales and scrollbars. + * INDICATOR - Color for indicator when button is selected. + * DISABLED - Foreground color when widget is disabled. + */ + +#define BLACK "Black" +#define WHITE "White" + +#define NORMAL_BG "SystemButtonFace" +#define NORMAL_FG "SystemButtonText" +#define ACTIVE_BG NORMAL_BG +#define TEXT_FG "SystemWindowText" +#define SELECT_BG "SystemHighlight" +#define SELECT_FG "SystemHighlightText" +#define TROUGH "SystemScrollbar" +#define INDICATOR "SystemWindow" +#define DISABLED "SystemDisabledText" +#define MENU_BG "SystemMenu" +#define MENU_FG "SystemMenuText" +#define HIGHLIGHT "SystemWindowFrame" + +/* + * Defaults for labels, buttons, checkbuttons, and radiobuttons: + */ + +#define DEF_BUTTON_ANCHOR "center" +#define DEF_BUTTON_ACTIVE_BG_COLOR NORMAL_BG +#define DEF_BUTTON_ACTIVE_BG_MONO BLACK +#define DEF_BUTTON_ACTIVE_FG_COLOR NORMAL_FG +#define DEF_CHKRAD_ACTIVE_FG_COLOR TEXT_FG +#define DEF_BUTTON_ACTIVE_FG_MONO WHITE +#define DEF_BUTTON_BG_COLOR NORMAL_BG +#define DEF_BUTTON_BG_MONO WHITE +#define DEF_BUTTON_BITMAP "" +#define DEF_BUTTON_BORDER_WIDTH "2" +#define DEF_BUTTON_CURSOR "" +#define DEF_BUTTON_COMMAND "" +#define DEF_BUTTON_COMPOUND "none" +#define DEF_BUTTON_DEFAULT "disabled" +#define DEF_BUTTON_DISABLED_FG_COLOR DISABLED +#define DEF_BUTTON_DISABLED_FG_MONO "" +#define DEF_BUTTON_FG NORMAL_FG +#define DEF_CHKRAD_FG TEXT_FG +#define DEF_BUTTON_FONT "TkDefaultFont" +#define DEF_BUTTON_HEIGHT "0" +#define DEF_BUTTON_HIGHLIGHT_BG_COLOR DEF_BUTTON_BG_COLOR +#define DEF_BUTTON_HIGHLIGHT_BG_MONO DEF_BUTTON_BG_MONO +#define DEF_BUTTON_HIGHLIGHT HIGHLIGHT +#define DEF_LABEL_HIGHLIGHT_WIDTH "0" +#define DEF_BUTTON_HIGHLIGHT_WIDTH "1" +#define DEF_BUTTON_IMAGE ((char *) NULL) +#define DEF_BUTTON_INDICATOR "1" +#define DEF_BUTTON_JUSTIFY "center" +#define DEF_BUTTON_OFF_VALUE "0" +#define DEF_BUTTON_ON_VALUE "1" +#define DEF_BUTTON_OVER_RELIEF "" +#define DEF_BUTTON_PADX "1" +#define DEF_LABCHKRAD_PADX "1" +#define DEF_BUTTON_PADY "1" +#define DEF_LABCHKRAD_PADY "1" +#define DEF_BUTTON_RELIEF "raised" +#define DEF_LABCHKRAD_RELIEF "flat" +#define DEF_BUTTON_REPEAT_DELAY "0" +#define DEF_BUTTON_REPEAT_INTERVAL "0" +#define DEF_BUTTON_SELECT_COLOR INDICATOR +#define DEF_BUTTON_SELECT_MONO BLACK +#define DEF_BUTTON_SELECT_IMAGE ((char *) NULL) +#define DEF_BUTTON_STATE "normal" +#define DEF_LABEL_TAKE_FOCUS "0" +#define DEF_BUTTON_TAKE_FOCUS ((char *) NULL) +#define DEF_BUTTON_TEXT "" +#define DEF_BUTTON_TEXT_VARIABLE "" +#define DEF_BUTTON_TRISTATE_VALUE "" +#define DEF_BUTTON_UNDERLINE "-1" +#define DEF_BUTTON_VALUE "" +#define DEF_BUTTON_WIDTH "0" +#define DEF_BUTTON_WRAP_LENGTH "0" +#define DEF_RADIOBUTTON_VARIABLE "selectedButton" +#define DEF_CHECKBUTTON_VARIABLE "" + +/* + * Defaults for canvases: + */ + +#define DEF_CANVAS_BG_COLOR NORMAL_BG +#define DEF_CANVAS_BG_MONO WHITE +#define DEF_CANVAS_BORDER_WIDTH "0" +#define DEF_CANVAS_CLOSE_ENOUGH "1" +#define DEF_CANVAS_CONFINE "1" +#define DEF_CANVAS_CURSOR "" +#define DEF_CANVAS_HEIGHT "7c" +#define DEF_CANVAS_HIGHLIGHT_BG NORMAL_BG +#define DEF_CANVAS_HIGHLIGHT HIGHLIGHT +#define DEF_CANVAS_HIGHLIGHT_WIDTH "2" +#define DEF_CANVAS_INSERT_BG NORMAL_FG +#define DEF_CANVAS_INSERT_BD_COLOR "0" +#define DEF_CANVAS_INSERT_BD_MONO "0" +#define DEF_CANVAS_INSERT_OFF_TIME "300" +#define DEF_CANVAS_INSERT_ON_TIME "600" +#define DEF_CANVAS_INSERT_WIDTH "2" +#define DEF_CANVAS_RELIEF "flat" +#define DEF_CANVAS_SCROLL_REGION "" +#define DEF_CANVAS_SELECT_COLOR SELECT_BG +#define DEF_CANVAS_SELECT_MONO BLACK +#define DEF_CANVAS_SELECT_BD_COLOR "1" +#define DEF_CANVAS_SELECT_BD_MONO "0" +#define DEF_CANVAS_SELECT_FG_COLOR SELECT_FG +#define DEF_CANVAS_SELECT_FG_MONO WHITE +#define DEF_CANVAS_TAKE_FOCUS ((char *) NULL) +#define DEF_CANVAS_WIDTH "10c" +#define DEF_CANVAS_X_SCROLL_CMD "" +#define DEF_CANVAS_X_SCROLL_INCREMENT "0" +#define DEF_CANVAS_Y_SCROLL_CMD "" +#define DEF_CANVAS_Y_SCROLL_INCREMENT "0" + +/* + * Defaults for entries: + */ + +#define DEF_ENTRY_BG_COLOR "SystemWindow" +#define DEF_ENTRY_BG_MONO WHITE +#define DEF_ENTRY_BORDER_WIDTH "1" +#define DEF_ENTRY_CURSOR "xterm" +#define DEF_ENTRY_DISABLED_BG_COLOR "SystemButtonFace" +#define DEF_ENTRY_DISABLED_BG_MONO WHITE +#define DEF_ENTRY_DISABLED_FG DISABLED +#define DEF_ENTRY_EXPORT_SELECTION "1" +#define DEF_ENTRY_FONT "TkTextFont" +#define DEF_ENTRY_FG TEXT_FG +#define DEF_ENTRY_HIGHLIGHT_BG NORMAL_BG +#define DEF_ENTRY_HIGHLIGHT HIGHLIGHT +#define DEF_ENTRY_HIGHLIGHT_WIDTH "0" +#define DEF_ENTRY_INSERT_BG TEXT_FG +#define DEF_ENTRY_INSERT_BD_COLOR "0" +#define DEF_ENTRY_INSERT_BD_MONO "0" +#define DEF_ENTRY_INSERT_OFF_TIME "300" +#define DEF_ENTRY_INSERT_ON_TIME "600" +#define DEF_ENTRY_INSERT_WIDTH "2" +#define DEF_ENTRY_JUSTIFY "left" +#define DEF_ENTRY_READONLY_BG_COLOR "SystemButtonFace" +#define DEF_ENTRY_READONLY_BG_MONO WHITE +#define DEF_ENTRY_RELIEF "sunken" +#define DEF_ENTRY_SCROLL_COMMAND "" +#define DEF_ENTRY_SELECT_COLOR SELECT_BG +#define DEF_ENTRY_SELECT_MONO BLACK +#define DEF_ENTRY_SELECT_BD_COLOR "0" +#define DEF_ENTRY_SELECT_BD_MONO "0" +#define DEF_ENTRY_SELECT_FG_COLOR SELECT_FG +#define DEF_ENTRY_SELECT_FG_MONO WHITE +#define DEF_ENTRY_SHOW ((char *) NULL) +#define DEF_ENTRY_STATE "normal" +#define DEF_ENTRY_TAKE_FOCUS ((char *) NULL) +#define DEF_ENTRY_TEXT_VARIABLE "" +#define DEF_ENTRY_WIDTH "20" + +/* + * Defaults for frames: + */ + +#define DEF_FRAME_BG_COLOR NORMAL_BG +#define DEF_FRAME_BG_MONO WHITE +#define DEF_FRAME_BORDER_WIDTH "0" +#define DEF_FRAME_CLASS "Frame" +#define DEF_FRAME_COLORMAP "" +#define DEF_FRAME_CONTAINER "0" +#define DEF_FRAME_CURSOR "" +#define DEF_FRAME_HEIGHT "0" +#define DEF_FRAME_HIGHLIGHT_BG NORMAL_BG +#define DEF_FRAME_HIGHLIGHT HIGHLIGHT +#define DEF_FRAME_HIGHLIGHT_WIDTH "0" +#define DEF_FRAME_PADX "0" +#define DEF_FRAME_PADY "0" +#define DEF_FRAME_RELIEF "flat" +#define DEF_FRAME_TAKE_FOCUS "0" +#define DEF_FRAME_VISUAL "" +#define DEF_FRAME_WIDTH "0" + +/* + * Defaults for labelframes: + */ + +#define DEF_LABELFRAME_BORDER_WIDTH "2" +#define DEF_LABELFRAME_CLASS "Labelframe" +#define DEF_LABELFRAME_RELIEF "groove" +#define DEF_LABELFRAME_FG NORMAL_FG +#define DEF_LABELFRAME_FONT "TkDefaultFont" +#define DEF_LABELFRAME_TEXT "" +#define DEF_LABELFRAME_LABELANCHOR "nw" + +/* + * Defaults for listboxes: + */ + +#define DEF_LISTBOX_ACTIVE_STYLE "underline" +#define DEF_LISTBOX_BG_COLOR "SystemWindow" +#define DEF_LISTBOX_BG_MONO WHITE +#define DEF_LISTBOX_BORDER_WIDTH "1" +#define DEF_LISTBOX_CURSOR "" +#define DEF_LISTBOX_DISABLED_FG DISABLED +#define DEF_LISTBOX_EXPORT_SELECTION "1" +#define DEF_LISTBOX_FONT "TkDefaultFont" +#define DEF_LISTBOX_FG NORMAL_FG +#define DEF_LISTBOX_HEIGHT "10" +#define DEF_LISTBOX_HIGHLIGHT_BG NORMAL_BG +#define DEF_LISTBOX_HIGHLIGHT HIGHLIGHT +#define DEF_LISTBOX_HIGHLIGHT_WIDTH "1" +#define DEF_LISTBOX_JUSTIFY "left" +#define DEF_LISTBOX_RELIEF "sunken" +#define DEF_LISTBOX_SCROLL_COMMAND "" +#define DEF_LISTBOX_LIST_VARIABLE "" +#define DEF_LISTBOX_SELECT_COLOR SELECT_BG +#define DEF_LISTBOX_SELECT_MONO BLACK +#define DEF_LISTBOX_SELECT_BD "0" +#define DEF_LISTBOX_SELECT_FG_COLOR SELECT_FG +#define DEF_LISTBOX_SELECT_FG_MONO WHITE +#define DEF_LISTBOX_SELECT_MODE "browse" +#define DEF_LISTBOX_SET_GRID "0" +#define DEF_LISTBOX_STATE "normal" +#define DEF_LISTBOX_TAKE_FOCUS ((char *) NULL) +#define DEF_LISTBOX_WIDTH "20" + +/* + * Defaults for individual entries of menus: + */ + +#define DEF_MENU_ENTRY_ACTIVE_BG ((char *) NULL) +#define DEF_MENU_ENTRY_ACTIVE_FG ((char *) NULL) +#define DEF_MENU_ENTRY_ACCELERATOR ((char *) NULL) +#define DEF_MENU_ENTRY_BG ((char *) NULL) +#define DEF_MENU_ENTRY_BITMAP None +#define DEF_MENU_ENTRY_COLUMN_BREAK "0" +#define DEF_MENU_ENTRY_COMMAND ((char *) NULL) +#define DEF_MENU_ENTRY_COMPOUND "none" +#define DEF_MENU_ENTRY_FG ((char *) NULL) +#define DEF_MENU_ENTRY_FONT ((char *) NULL) +#define DEF_MENU_ENTRY_HIDE_MARGIN "0" +#define DEF_MENU_ENTRY_IMAGE ((char *) NULL) +#define DEF_MENU_ENTRY_INDICATOR "1" +#define DEF_MENU_ENTRY_LABEL ((char *) NULL) +#define DEF_MENU_ENTRY_MENU ((char *) NULL) +#define DEF_MENU_ENTRY_OFF_VALUE "0" +#define DEF_MENU_ENTRY_ON_VALUE "1" +#define DEF_MENU_ENTRY_SELECT_IMAGE ((char *) NULL) +#define DEF_MENU_ENTRY_STATE "normal" +#define DEF_MENU_ENTRY_VALUE ((char *) NULL) +#define DEF_MENU_ENTRY_CHECK_VARIABLE ((char *) NULL) +#define DEF_MENU_ENTRY_RADIO_VARIABLE "selectedButton" +#define DEF_MENU_ENTRY_SELECT ((char *) NULL) +#define DEF_MENU_ENTRY_UNDERLINE "-1" + +/* + * Defaults for menus overall: + */ + +#define DEF_MENU_ACTIVE_BG_COLOR SELECT_BG +#define DEF_MENU_ACTIVE_BG_MONO BLACK +#define DEF_MENU_ACTIVE_BORDER_WIDTH "0" +#define DEF_MENU_ACTIVE_FG_COLOR SELECT_FG +#define DEF_MENU_ACTIVE_FG_MONO WHITE +#define DEF_MENU_BG_COLOR MENU_BG +#define DEF_MENU_BG_MONO WHITE +#define DEF_MENU_BORDER_WIDTH "0" +#define DEF_MENU_CURSOR "arrow" +#define DEF_MENU_DISABLED_FG_COLOR DISABLED +#define DEF_MENU_DISABLED_FG_MONO "" +#define DEF_MENU_FONT "TkMenuFont" +#define DEF_MENU_FG MENU_FG +#define DEF_MENU_POST_COMMAND "" +#define DEF_MENU_RELIEF "flat" +#define DEF_MENU_SELECT_COLOR MENU_FG +#define DEF_MENU_SELECT_MONO BLACK +#define DEF_MENU_TAKE_FOCUS "0" +#define DEF_MENU_TEAROFF "1" +#define DEF_MENU_TEAROFF_CMD ((char *) NULL) +#define DEF_MENU_TITLE "" +#define DEF_MENU_TYPE "normal" + +/* + * Defaults for menubuttons: + */ + +#define DEF_MENUBUTTON_ANCHOR "center" +#define DEF_MENUBUTTON_ACTIVE_BG_COLOR ACTIVE_BG +#define DEF_MENUBUTTON_ACTIVE_BG_MONO BLACK +#define DEF_MENUBUTTON_ACTIVE_FG_COLOR NORMAL_FG +#define DEF_MENUBUTTON_ACTIVE_FG_MONO WHITE +#define DEF_MENUBUTTON_BG_COLOR NORMAL_BG +#define DEF_MENUBUTTON_BG_MONO WHITE +#define DEF_MENUBUTTON_BITMAP "" +#define DEF_MENUBUTTON_BORDER_WIDTH "1" +#define DEF_MENUBUTTON_CURSOR "" +#define DEF_MENUBUTTON_DIRECTION "below" +#define DEF_MENUBUTTON_DISABLED_FG_COLOR DISABLED +#define DEF_MENUBUTTON_DISABLED_FG_MONO "" +#define DEF_MENUBUTTON_FONT "TkDefaultFont" +#define DEF_MENUBUTTON_FG NORMAL_FG +#define DEF_MENUBUTTON_HEIGHT "0" +#define DEF_MENUBUTTON_HIGHLIGHT_BG_COLOR DEF_MENUBUTTON_BG_COLOR +#define DEF_MENUBUTTON_HIGHLIGHT_BG_MONO DEF_MENUBUTTON_BG_MONO +#define DEF_MENUBUTTON_HIGHLIGHT HIGHLIGHT +#define DEF_MENUBUTTON_HIGHLIGHT_WIDTH "0" +#define DEF_MENUBUTTON_IMAGE ((char *) NULL) +#define DEF_MENUBUTTON_INDICATOR "0" +#define DEF_MENUBUTTON_JUSTIFY "center" +#define DEF_MENUBUTTON_MENU "" +#define DEF_MENUBUTTON_PADX "4p" +#define DEF_MENUBUTTON_PADY "3p" +#define DEF_MENUBUTTON_RELIEF "flat" +#define DEF_MENUBUTTON_STATE "normal" +#define DEF_MENUBUTTON_TAKE_FOCUS "0" +#define DEF_MENUBUTTON_TEXT "" +#define DEF_MENUBUTTON_TEXT_VARIABLE "" +#define DEF_MENUBUTTON_UNDERLINE "-1" +#define DEF_MENUBUTTON_WIDTH "0" +#define DEF_MENUBUTTON_WRAP_LENGTH "0" + +/* + * Defaults for messages: + */ + +#define DEF_MESSAGE_ANCHOR "center" +#define DEF_MESSAGE_ASPECT "150" +#define DEF_MESSAGE_BG_COLOR NORMAL_BG +#define DEF_MESSAGE_BG_MONO WHITE +#define DEF_MESSAGE_BORDER_WIDTH "1" +#define DEF_MESSAGE_CURSOR "" +#define DEF_MESSAGE_FG NORMAL_FG +#define DEF_MESSAGE_FONT "TkDefaultFont" +#define DEF_MESSAGE_HIGHLIGHT_BG NORMAL_BG +#define DEF_MESSAGE_HIGHLIGHT HIGHLIGHT +#define DEF_MESSAGE_HIGHLIGHT_WIDTH "0" +#define DEF_MESSAGE_JUSTIFY "left" +#define DEF_MESSAGE_PADX "-1" +#define DEF_MESSAGE_PADY "-1" +#define DEF_MESSAGE_RELIEF "flat" +#define DEF_MESSAGE_TAKE_FOCUS "0" +#define DEF_MESSAGE_TEXT "" +#define DEF_MESSAGE_TEXT_VARIABLE "" +#define DEF_MESSAGE_WIDTH "0" + +/* + * Defaults for panedwindows + */ + +#define DEF_PANEDWINDOW_BG_COLOR NORMAL_BG +#define DEF_PANEDWINDOW_BG_MONO WHITE +#define DEF_PANEDWINDOW_BORDERWIDTH "1" +#define DEF_PANEDWINDOW_CURSOR "" +#define DEF_PANEDWINDOW_HANDLEPAD "8" +#define DEF_PANEDWINDOW_HANDLESIZE "8" +#define DEF_PANEDWINDOW_HEIGHT "" +#define DEF_PANEDWINDOW_OPAQUERESIZE "1" +#define DEF_PANEDWINDOW_ORIENT "horizontal" +#define DEF_PANEDWINDOW_PROXYBORDER "2" +#define DEF_PANEDWINDOW_RELIEF "flat" +#define DEF_PANEDWINDOW_SASHCURSOR "" +#define DEF_PANEDWINDOW_SASHPAD "0" +#define DEF_PANEDWINDOW_SASHRELIEF "flat" +#define DEF_PANEDWINDOW_SASHWIDTH "3" +#define DEF_PANEDWINDOW_SHOWHANDLE "0" +#define DEF_PANEDWINDOW_WIDTH "" + +/* + * Defaults for panedwindow panes + */ + +#define DEF_PANEDWINDOW_PANE_AFTER "" +#define DEF_PANEDWINDOW_PANE_BEFORE "" +#define DEF_PANEDWINDOW_PANE_HEIGHT "" +#define DEF_PANEDWINDOW_PANE_MINSIZE "0" +#define DEF_PANEDWINDOW_PANE_PADX "0" +#define DEF_PANEDWINDOW_PANE_PADY "0" +#define DEF_PANEDWINDOW_PANE_STICKY "nsew" +#define DEF_PANEDWINDOW_PANE_WIDTH "" +#define DEF_PANEDWINDOW_PANE_HIDE "0" +#define DEF_PANEDWINDOW_PANE_STRETCH "last" + +/* + * Defaults for scales: + */ + +#define DEF_SCALE_ACTIVE_BG_COLOR ACTIVE_BG +#define DEF_SCALE_ACTIVE_BG_MONO BLACK +#define DEF_SCALE_BG_COLOR NORMAL_BG +#define DEF_SCALE_BG_MONO WHITE +#define DEF_SCALE_BIG_INCREMENT "0" +#define DEF_SCALE_BORDER_WIDTH "1" +#define DEF_SCALE_COMMAND "" +#define DEF_SCALE_CURSOR "" +#define DEF_SCALE_DIGITS "0" +#define DEF_SCALE_FONT "TkDefaultFont" +#define DEF_SCALE_FG_COLOR NORMAL_FG +#define DEF_SCALE_FG_MONO BLACK +#define DEF_SCALE_FROM "0" +#define DEF_SCALE_HIGHLIGHT_BG_COLOR DEF_SCALE_BG_COLOR +#define DEF_SCALE_HIGHLIGHT_BG_MONO DEF_SCALE_BG_MONO +#define DEF_SCALE_HIGHLIGHT HIGHLIGHT +#define DEF_SCALE_HIGHLIGHT_WIDTH "2" +#define DEF_SCALE_LABEL "" +#define DEF_SCALE_LENGTH "100" +#define DEF_SCALE_ORIENT "vertical" +#define DEF_SCALE_RELIEF "flat" +#define DEF_SCALE_REPEAT_DELAY "300" +#define DEF_SCALE_REPEAT_INTERVAL "100" +#define DEF_SCALE_RESOLUTION "1" +#define DEF_SCALE_TROUGH_COLOR TROUGH +#define DEF_SCALE_TROUGH_MONO WHITE +#define DEF_SCALE_SHOW_VALUE "1" +#define DEF_SCALE_SLIDER_LENGTH "30" +#define DEF_SCALE_SLIDER_RELIEF "raised" +#define DEF_SCALE_STATE "normal" +#define DEF_SCALE_TAKE_FOCUS ((char *) NULL) +#define DEF_SCALE_TICK_INTERVAL "0" +#define DEF_SCALE_TO "100" +#define DEF_SCALE_VARIABLE "" +#define DEF_SCALE_WIDTH "15" + +/* + * Defaults for scrollbars: + */ + +#define DEF_SCROLLBAR_ACTIVE_BG_COLOR ACTIVE_BG +#define DEF_SCROLLBAR_ACTIVE_BG_MONO BLACK +#define DEF_SCROLLBAR_ACTIVE_RELIEF "raised" +#define DEF_SCROLLBAR_BG_COLOR NORMAL_BG +#define DEF_SCROLLBAR_BG_MONO WHITE +#define DEF_SCROLLBAR_BORDER_WIDTH "0" +#define DEF_SCROLLBAR_COMMAND "" +#define DEF_SCROLLBAR_CURSOR "" +#define DEF_SCROLLBAR_EL_BORDER_WIDTH "-1" +#define DEF_SCROLLBAR_HIGHLIGHT_BG NORMAL_BG +#define DEF_SCROLLBAR_HIGHLIGHT HIGHLIGHT +#define DEF_SCROLLBAR_HIGHLIGHT_WIDTH "0" +#define DEF_SCROLLBAR_JUMP "0" +#define DEF_SCROLLBAR_ORIENT "vertical" +#define DEF_SCROLLBAR_RELIEF "sunken" +#define DEF_SCROLLBAR_REPEAT_DELAY "300" +#define DEF_SCROLLBAR_REPEAT_INTERVAL "100" +#define DEF_SCROLLBAR_TAKE_FOCUS ((char *) NULL) +#define DEF_SCROLLBAR_TROUGH_COLOR TROUGH +#define DEF_SCROLLBAR_TROUGH_MONO WHITE +#define DEF_SCROLLBAR_WIDTH "10" + +/* + * Defaults for texts: + */ + +#define DEF_TEXT_AUTO_SEPARATORS "1" +#define DEF_TEXT_BG_COLOR "SystemWindow" +#define DEF_TEXT_BG_MONO WHITE +#define DEF_TEXT_BLOCK_CURSOR "0" +#define DEF_TEXT_BORDER_WIDTH "1" +#define DEF_TEXT_CURSOR "xterm" +#define DEF_TEXT_FG TEXT_FG +#define DEF_TEXT_EXPORT_SELECTION "1" +#define DEF_TEXT_FONT "TkFixedFont" +#define DEF_TEXT_HEIGHT "24" +#define DEF_TEXT_HIGHLIGHT_BG NORMAL_BG +#define DEF_TEXT_HIGHLIGHT HIGHLIGHT +#define DEF_TEXT_HIGHLIGHT_WIDTH "0" +#define DEF_TEXT_INSERT_BG TEXT_FG +#define DEF_TEXT_INSERT_BD_COLOR "0" +#define DEF_TEXT_INSERT_BD_MONO "0" +#define DEF_TEXT_INSERT_OFF_TIME "300" +#define DEF_TEXT_INSERT_ON_TIME "600" +#define DEF_TEXT_INSERT_UNFOCUSSED "none" +#define DEF_TEXT_INSERT_WIDTH "2" +#define DEF_TEXT_MAX_UNDO "0" +#define DEF_TEXT_PADX "1" +#define DEF_TEXT_PADY "1" +#define DEF_TEXT_RELIEF "sunken" +#define DEF_TEXT_INACTIVE_SELECT_COLOR NULL +#define DEF_TEXT_SELECT_COLOR SELECT_BG +#define DEF_TEXT_SELECT_MONO BLACK +#define DEF_TEXT_SELECT_BD_COLOR "0" +#define DEF_TEXT_SELECT_BD_MONO "0" +#define DEF_TEXT_SELECT_FG_COLOR SELECT_FG +#define DEF_TEXT_SELECT_FG_MONO WHITE +#define DEF_TEXT_SELECT_RELIEF "flat" +#define DEF_TEXT_SET_GRID "0" +#define DEF_TEXT_SPACING1 "0" +#define DEF_TEXT_SPACING2 "0" +#define DEF_TEXT_SPACING3 "0" +#define DEF_TEXT_STATE "normal" +#define DEF_TEXT_TABS "" +#define DEF_TEXT_TABSTYLE "tabular" +#define DEF_TEXT_TAKE_FOCUS ((char *) NULL) +#define DEF_TEXT_UNDO "0" +#define DEF_TEXT_WIDTH "80" +#define DEF_TEXT_WRAP "char" +#define DEF_TEXT_XSCROLL_COMMAND "" +#define DEF_TEXT_YSCROLL_COMMAND "" + +/* + * Defaults for canvas text: + */ + +#define DEF_CANVTEXT_FONT "TkDefaultFont" + +/* + * Defaults for toplevels (most of the defaults for frames also apply + * to toplevels): + */ + +#define DEF_TOPLEVEL_CLASS "Toplevel" +#define DEF_TOPLEVEL_MENU "" +#define DEF_TOPLEVEL_SCREEN "" +#define DEF_TOPLEVEL_USE "" + +/* + * Defaults for busy windows: + */ + +#define DEF_BUSY_CURSOR "wait" + +#endif /* _TKWINDEFAULT */ diff --git a/tk8.6/win/tkWinDialog.c b/tk8.6/win/tkWinDialog.c new file mode 100644 index 0000000..f97d813 --- /dev/null +++ b/tk8.6/win/tkWinDialog.c @@ -0,0 +1,3623 @@ +/* + * tkWinDialog.c -- + * + * Contains the Windows implementation of the common dialog boxes. + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkFileFilter.h" +#include "tkFont.h" + +#include <commdlg.h> /* includes common dialog functionality */ +#include <dlgs.h> /* includes common dialog template defines */ +#include <cderr.h> /* includes the common dialog error codes */ + +#include <shlobj.h> /* includes SHBrowseForFolder */ + +#ifdef _MSC_VER +# pragma comment (lib, "shell32.lib") +# pragma comment (lib, "comdlg32.lib") +# pragma comment (lib, "uuid.lib") +#endif + +/* These needed for compilation with VC++ 5.2 */ +/* XXX - remove these since need at least VC 6 */ +#ifndef BIF_EDITBOX +#define BIF_EDITBOX 0x10 +#endif + +#ifndef BIF_VALIDATE +#define BIF_VALIDATE 0x0020 +#endif + +/* This "new" dialog style is now actually the "old" dialog style post-Vista */ +#ifndef BIF_NEWDIALOGSTYLE +#define BIF_NEWDIALOGSTYLE 0x0040 +#endif + +#ifndef BFFM_VALIDATEFAILED +#ifdef UNICODE +#define BFFM_VALIDATEFAILED 4 +#else +#define BFFM_VALIDATEFAILED 3 +#endif +#endif /* BFFM_VALIDATEFAILED */ + +typedef struct ThreadSpecificData { + int debugFlag; /* Flags whether we should output debugging + * information while displaying a builtin + * dialog. */ + Tcl_Interp *debugInterp; /* Interpreter to used for debugging. */ + UINT WM_LBSELCHANGED; /* Holds a registered windows event used for + * communicating between the Directory Chooser + * dialog and its hook proc. */ + HHOOK hMsgBoxHook; /* Hook proc for tk_messageBox and the */ + HICON hSmallIcon; /* icons used by a parent to be used in */ + HICON hBigIcon; /* the message box */ + int newFileDialogsState; +#define FDLG_STATE_INIT 0 /* Uninitialized */ +#define FDLG_STATE_USE_NEW 1 /* Use the new dialogs */ +#define FDLG_STATE_USE_OLD 2 /* Use the old dialogs */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * The following structures are used by Tk_MessageBoxCmd() to parse arguments + * and return results. + */ + +static const TkStateMap iconMap[] = { + {MB_ICONERROR, "error"}, + {MB_ICONINFORMATION, "info"}, + {MB_ICONQUESTION, "question"}, + {MB_ICONWARNING, "warning"}, + {-1, NULL} +}; + +static const TkStateMap typeMap[] = { + {MB_ABORTRETRYIGNORE, "abortretryignore"}, + {MB_OK, "ok"}, + {MB_OKCANCEL, "okcancel"}, + {MB_RETRYCANCEL, "retrycancel"}, + {MB_YESNO, "yesno"}, + {MB_YESNOCANCEL, "yesnocancel"}, + {-1, NULL} +}; + +static const TkStateMap buttonMap[] = { + {IDABORT, "abort"}, + {IDRETRY, "retry"}, + {IDIGNORE, "ignore"}, + {IDOK, "ok"}, + {IDCANCEL, "cancel"}, + {IDNO, "no"}, + {IDYES, "yes"}, + {-1, NULL} +}; + +static const int buttonFlagMap[] = { + MB_DEFBUTTON1, MB_DEFBUTTON2, MB_DEFBUTTON3, MB_DEFBUTTON4 +}; + +static const struct {int type; int btnIds[3];} allowedTypes[] = { + {MB_ABORTRETRYIGNORE, {IDABORT, IDRETRY, IDIGNORE}}, + {MB_OK, {IDOK, -1, -1 }}, + {MB_OKCANCEL, {IDOK, IDCANCEL, -1 }}, + {MB_RETRYCANCEL, {IDRETRY, IDCANCEL, -1 }}, + {MB_YESNO, {IDYES, IDNO, -1 }}, + {MB_YESNOCANCEL, {IDYES, IDNO, IDCANCEL}} +}; + +#define NUM_TYPES (sizeof(allowedTypes) / sizeof(allowedTypes[0])) + +/* + * Abstract trivial differences between Win32 and Win64. + */ + +#define TkWinGetHInstance(from) \ + ((HINSTANCE) GetWindowLongPtr((from), GWLP_HINSTANCE)) +#define TkWinGetUserData(from) \ + GetWindowLongPtr((from), GWLP_USERDATA) +#define TkWinSetUserData(to,what) \ + SetWindowLongPtr((to), GWLP_USERDATA, (LPARAM)(what)) + +/* + * The value of TK_MULTI_MAX_PATH dictates how many files can be retrieved + * with tk_get*File -multiple 1. It must be allocated on the stack, so make it + * large enough but not too large. - hobbs + * + * The data is stored as <dir>\0<file1>\0<file2>\0...<fileN>\0\0. Since + * MAX_PATH == 260 on Win2K/NT, *40 is ~10Kbytes. + */ + +#define TK_MULTI_MAX_PATH (MAX_PATH*40) + +/* + * The following structure is used to pass information between the directory + * chooser function, Tk_ChooseDirectoryObjCmd(), and its dialog hook proc. + */ + +typedef struct { + TCHAR initDir[MAX_PATH]; /* Initial folder to use */ + TCHAR retDir[MAX_PATH]; /* Returned folder to use */ + Tcl_Interp *interp; + int mustExist; /* True if file must exist to return from + * callback */ +} ChooseDir; + +/* + * The following structure is used to pass information between GetFileName + * function and OFN dialog hook procedures. [Bug 2896501, Patch 2898255] + */ + +typedef struct OFNData { + Tcl_Interp *interp; /* Interp, used only if debug is turned on, + * for setting the "tk_dialog" variable. */ + int dynFileBufferSize; /* Dynamic filename buffer size, stored to + * avoid shrinking and expanding the buffer + * when selection changes */ + TCHAR *dynFileBuffer; /* Dynamic filename buffer */ +} OFNData; + +/* + * The following structure is used to gather options used by various + * file dialogs + */ +typedef struct OFNOpts { + Tk_Window tkwin; /* Owner window for dialog */ + Tcl_Obj *extObj; /* Default extension */ + Tcl_Obj *titleObj; /* Title for dialog */ + Tcl_Obj *filterObj; /* File type filter list */ + Tcl_Obj *typeVariableObj; /* Variable in which to store type selected */ + Tcl_Obj *initialTypeObj; /* Initial value of above, or NULL */ + Tcl_DString utfDirString; /* Initial dir */ + int multi; /* Multiple selection enabled */ + int confirmOverwrite; /* Confirm before overwriting */ + int mustExist; /* Used only for */ + int forceXPStyle; /* XXX - Force XP style even on newer systems */ + TCHAR file[TK_MULTI_MAX_PATH]; /* File name + XXX - fixed size because it was so + historically. Why not malloc'ed ? + XXX - also, TCHAR should really be WCHAR + because TkWinGetUnicodeEncoding is always + UCS2. + */ +} OFNOpts; + +/* Define the operation for which option parsing is to be done. */ +enum OFNOper { + OFN_FILE_SAVE, /* tk_getOpenFile */ + OFN_FILE_OPEN, /* tk_getSaveFile */ + OFN_DIR_CHOOSE /* tk_chooseDirectory */ +}; + + +/* + * The following definitions are required when using older versions of + * Visual C++ (like 6.0) and possibly MingW. Those headers do not contain + * required definitions for interfaces new to Vista that we need for + * the new file dialogs. Duplicating definitions is OK because they + * should forever remain unchanged. + * + * XXX - is there a better/easier way to use new data definitions with + * older compilers? Should we prefix definitions with Tcl_ instead + * of using the same names as in the SDK? + */ +#ifndef __IShellItem_INTERFACE_DEFINED__ +# define __IShellItem_INTERFACE_DEFINED__ +#ifdef __MSVCRT__ +typedef struct IShellItem IShellItem; + +typedef enum __MIDL_IShellItem_0001 { + SIGDN_NORMALDISPLAY = 0,SIGDN_PARENTRELATIVEPARSING = 0x80018001,SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8001c001, + SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,SIGDN_PARENTRELATIVEEDITING = 0x80031001,SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000, + SIGDN_FILESYSPATH = 0x80058000,SIGDN_URL = 0x80068000 +} SIGDN; + +typedef DWORD SICHINTF; + +typedef struct IShellItemVtbl +{ + BEGIN_INTERFACE + + HRESULT (STDMETHODCALLTYPE *QueryInterface)(IShellItem *, REFIID, void **); + ULONG (STDMETHODCALLTYPE *AddRef)(IShellItem *); + ULONG (STDMETHODCALLTYPE *Release)(IShellItem *); + HRESULT (STDMETHODCALLTYPE *BindToHandler)(IShellItem *, IBindCtx *, REFGUID, REFIID, void **); + HRESULT (STDMETHODCALLTYPE *GetParent)(IShellItem *, IShellItem **); + HRESULT (STDMETHODCALLTYPE *GetDisplayName)(IShellItem *, SIGDN, LPOLESTR *); + HRESULT (STDMETHODCALLTYPE *GetAttributes)(IShellItem *, SFGAOF, SFGAOF *); + HRESULT (STDMETHODCALLTYPE *Compare)(IShellItem *, IShellItem *, SICHINTF, int *); + + END_INTERFACE +} IShellItemVtbl; +struct IShellItem { + CONST_VTBL struct IShellItemVtbl *lpVtbl; +}; +#endif +#endif + +#ifndef __IShellItemArray_INTERFACE_DEFINED__ +#define __IShellItemArray_INTERFACE_DEFINED__ + +typedef enum SIATTRIBFLAGS { + SIATTRIBFLAGS_AND = 0x1, + SIATTRIBFLAGS_OR = 0x2, + SIATTRIBFLAGS_APPCOMPAT = 0x3, + SIATTRIBFLAGS_MASK = 0x3, + SIATTRIBFLAGS_ALLITEMS = 0x4000 +} SIATTRIBFLAGS; +#ifdef __MSVCRT__ +typedef ULONG SFGAOF; +#endif /* __MSVCRT__ */ +typedef struct IShellItemArray IShellItemArray; +typedef struct IShellItemArrayVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IShellItemArray *, REFIID riid,void **ppvObject); + ULONG ( STDMETHODCALLTYPE *AddRef )(IShellItemArray *); + ULONG ( STDMETHODCALLTYPE *Release )(IShellItemArray *); + HRESULT ( STDMETHODCALLTYPE *BindToHandler )(IShellItemArray *, + IBindCtx *, REFGUID, REFIID, void **); + /* flags is actually is enum GETPROPERTYSTOREFLAGS */ + HRESULT ( STDMETHODCALLTYPE *GetPropertyStore )( + IShellItemArray *, int, REFIID, void **); + /* keyType actually REFPROPERTYKEY */ + HRESULT ( STDMETHODCALLTYPE *GetPropertyDescriptionList )( + IShellItemArray *, void *, REFIID, void **); + HRESULT ( STDMETHODCALLTYPE *GetAttributes )(IShellItemArray *, + SIATTRIBFLAGS, SFGAOF, SFGAOF *); + HRESULT ( STDMETHODCALLTYPE *GetCount )( + IShellItemArray *, DWORD *); + HRESULT ( STDMETHODCALLTYPE *GetItemAt )( + IShellItemArray *, DWORD, IShellItem **); + /* ppenumShellItems actually (IEnumShellItems **) */ + HRESULT ( STDMETHODCALLTYPE *EnumItems )( + IShellItemArray *, void **); + + END_INTERFACE +} IShellItemArrayVtbl; + +struct IShellItemArray { + CONST_VTBL struct IShellItemArrayVtbl *lpVtbl; +}; + +#endif /* __IShellItemArray_INTERFACE_DEFINED__ */ + +/* + * Older compilers do not define these CLSIDs so we do so here under + * a slightly different name so as to not clash with the definitions + * in new compilers + */ +static const CLSID ClsidFileOpenDialog = { + 0xDC1C5A9C, 0xE88A, 0X4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7} +}; +static const CLSID ClsidFileSaveDialog = { + 0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B} +}; +static const IID IIDIFileOpenDialog = { + 0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60} +}; +static const IID IIDIFileSaveDialog = { + 0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB} +}; +static const IID IIDIShellItem = { + 0x43826D1E, 0xE718, 0x42EE, {0xBC, 0x55, 0xA1, 0xE2, 0x61, 0xC3, 0x7B, 0xFE} +}; + +#ifdef __IFileDialog_INTERFACE_DEFINED__ +# define TCLCOMDLG_FILTERSPEC COMDLG_FILTERSPEC +#else + +/* Forward declarations for structs that are referenced but not used */ +typedef struct IPropertyStore IPropertyStore; +typedef struct IPropertyDescriptionList IPropertyDescriptionList; +typedef struct IFileOperationProgressSink IFileOperationProgressSink; +typedef enum FDAP { + FDAP_BOTTOM = 0, + FDAP_TOP = 1 +} FDAP; + +typedef struct { + LPCWSTR pszName; + LPCWSTR pszSpec; +} TCLCOMDLG_FILTERSPEC; + +enum _FILEOPENDIALOGOPTIONS { + FOS_OVERWRITEPROMPT = 0x2, + FOS_STRICTFILETYPES = 0x4, + FOS_NOCHANGEDIR = 0x8, + FOS_PICKFOLDERS = 0x20, + FOS_FORCEFILESYSTEM = 0x40, + FOS_ALLNONSTORAGEITEMS = 0x80, + FOS_NOVALIDATE = 0x100, + FOS_ALLOWMULTISELECT = 0x200, + FOS_PATHMUSTEXIST = 0x800, + FOS_FILEMUSTEXIST = 0x1000, + FOS_CREATEPROMPT = 0x2000, + FOS_SHAREAWARE = 0x4000, + FOS_NOREADONLYRETURN = 0x8000, + FOS_NOTESTFILECREATE = 0x10000, + FOS_HIDEMRUPLACES = 0x20000, + FOS_HIDEPINNEDPLACES = 0x40000, + FOS_NODEREFERENCELINKS = 0x100000, + FOS_DONTADDTORECENT = 0x2000000, + FOS_FORCESHOWHIDDEN = 0x10000000, + FOS_DEFAULTNOMINIMODE = 0x20000000, + FOS_FORCEPREVIEWPANEON = 0x40000000 +} ; +typedef DWORD FILEOPENDIALOGOPTIONS; + +typedef struct IFileDialog IFileDialog; +typedef struct IFileDialogVtbl +{ + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileDialog *, REFIID, void **); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileDialog *); + ULONG ( STDMETHODCALLTYPE *Release )( IFileDialog *); + HRESULT ( STDMETHODCALLTYPE *Show )( IFileDialog *, HWND); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileDialog *, + UINT, const TCLCOMDLG_FILTERSPEC *); + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )(IFileDialog *, UINT); + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )(IFileDialog *, UINT *); + /* XXX - Actually pfde is IFileDialogEvents* but we do not use + this call and do not want to define IFileDialogEvents as that + pulls in a whole bunch of other stuff. */ + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileDialog *, void *, DWORD *); + HRESULT ( STDMETHODCALLTYPE *Unadvise )(IFileDialog *, DWORD); + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileDialog *, FILEOPENDIALOGOPTIONS); + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileDialog *, FILEOPENDIALOGOPTIONS *); + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileDialog *, LPWSTR *); + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileDialog *, IShellItem *, FDAP); + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileDialog *, HRESULT); + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileDialog *, REFGUID); + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( IFileDialog *); + /* pFilter actually IShellItemFilter. But deprecated in Win7 AND we do + not use it anyways. So define as void* */ + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileDialog *, void *); + + END_INTERFACE +} IFileDialogVtbl; + +struct IFileDialog { + CONST_VTBL struct IFileDialogVtbl *lpVtbl; +}; + + +typedef struct IFileSaveDialog IFileSaveDialog; +typedef struct IFileSaveDialogVtbl { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileSaveDialog *, REFIID, void **); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileSaveDialog *); + ULONG ( STDMETHODCALLTYPE *Release )( IFileSaveDialog *); + HRESULT ( STDMETHODCALLTYPE *Show )( + IFileSaveDialog *, HWND); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileSaveDialog * this, + UINT, const TCLCOMDLG_FILTERSPEC *); + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileSaveDialog *, UINT); + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( + IFileSaveDialog *, UINT *); + /* Actually pfde is IFileSaveDialogEvents* */ + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileSaveDialog *, void *, DWORD *); + HRESULT ( STDMETHODCALLTYPE *Unadvise )( IFileSaveDialog *, DWORD); + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileSaveDialog *, FILEOPENDIALOGOPTIONS); + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileSaveDialog *, FILEOPENDIALOGOPTIONS *); + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileSaveDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileSaveDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileSaveDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileSaveDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileSaveDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileSaveDialog *, LPWSTR *); + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileSaveDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileSaveDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileSaveDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileSaveDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileSaveDialog *, IShellItem *, FDAP); + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileSaveDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileSaveDialog *, HRESULT); + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileSaveDialog *, REFGUID); + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( IFileSaveDialog *); + /* pFilter Actually IShellItemFilter* */ + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileSaveDialog *, void *); + HRESULT ( STDMETHODCALLTYPE *SetSaveAsItem )( + IFileSaveDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *SetProperties )( + IFileSaveDialog *, IPropertyStore *); + HRESULT ( STDMETHODCALLTYPE *SetCollectedProperties )( + IFileSaveDialog *, IPropertyDescriptionList *, BOOL); + HRESULT ( STDMETHODCALLTYPE *GetProperties )( + IFileSaveDialog *, IPropertyStore **); + HRESULT ( STDMETHODCALLTYPE *ApplyProperties )( + IFileSaveDialog *, IShellItem *, IPropertyStore *, + HWND, IFileOperationProgressSink *); + + END_INTERFACE + +} IFileSaveDialogVtbl; + +struct IFileSaveDialog { + CONST_VTBL struct IFileSaveDialogVtbl *lpVtbl; +}; + +typedef struct IFileOpenDialog IFileOpenDialog; +typedef struct IFileOpenDialogVtbl { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE *QueryInterface )( + IFileOpenDialog *, REFIID, void **); + ULONG ( STDMETHODCALLTYPE *AddRef )( IFileOpenDialog *); + ULONG ( STDMETHODCALLTYPE *Release )( IFileOpenDialog *); + HRESULT ( STDMETHODCALLTYPE *Show )( IFileOpenDialog *, HWND); + HRESULT ( STDMETHODCALLTYPE *SetFileTypes )( IFileOpenDialog *, + UINT, const TCLCOMDLG_FILTERSPEC *); + HRESULT ( STDMETHODCALLTYPE *SetFileTypeIndex )( + IFileOpenDialog *, UINT); + HRESULT ( STDMETHODCALLTYPE *GetFileTypeIndex )( + IFileOpenDialog *, UINT *); + /* Actually pfde is IFileDialogEvents* */ + HRESULT ( STDMETHODCALLTYPE *Advise )( + IFileOpenDialog *, void *, DWORD *); + HRESULT ( STDMETHODCALLTYPE *Unadvise )( IFileOpenDialog *, DWORD); + HRESULT ( STDMETHODCALLTYPE *SetOptions )( + IFileOpenDialog *, FILEOPENDIALOGOPTIONS); + HRESULT ( STDMETHODCALLTYPE *GetOptions )( + IFileOpenDialog *, FILEOPENDIALOGOPTIONS *); + HRESULT ( STDMETHODCALLTYPE *SetDefaultFolder )( + IFileOpenDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *SetFolder )( + IFileOpenDialog *, IShellItem *); + HRESULT ( STDMETHODCALLTYPE *GetFolder )( + IFileOpenDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *GetCurrentSelection )( + IFileOpenDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *SetFileName )( + IFileOpenDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetFileName )( + IFileOpenDialog *, LPWSTR *); + HRESULT ( STDMETHODCALLTYPE *SetTitle )( + IFileOpenDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetOkButtonLabel )( + IFileOpenDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *SetFileNameLabel )( + IFileOpenDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *GetResult )( + IFileOpenDialog *, IShellItem **); + HRESULT ( STDMETHODCALLTYPE *AddPlace )( + IFileOpenDialog *, IShellItem *, FDAP); + HRESULT ( STDMETHODCALLTYPE *SetDefaultExtension )( + IFileOpenDialog *, LPCWSTR); + HRESULT ( STDMETHODCALLTYPE *Close )( IFileOpenDialog *, HRESULT); + HRESULT ( STDMETHODCALLTYPE *SetClientGuid )( + IFileOpenDialog *, REFGUID); + HRESULT ( STDMETHODCALLTYPE *ClearClientData )( + IFileOpenDialog *); + HRESULT ( STDMETHODCALLTYPE *SetFilter )( + IFileOpenDialog *, + /* pFilter is actually IShellItemFilter */ + void *); + HRESULT ( STDMETHODCALLTYPE *GetResults )( + IFileOpenDialog *, IShellItemArray **); + HRESULT ( STDMETHODCALLTYPE *GetSelectedItems )( + IFileOpenDialog *, IShellItemArray **); + + END_INTERFACE +} IFileOpenDialogVtbl; + +struct IFileOpenDialog +{ + CONST_VTBL struct IFileOpenDialogVtbl *lpVtbl; +}; + +#endif /* __IFileDialog_INTERFACE_DEFINED__ */ + +/* + * Definitions of functions used only in this file. + */ + +static UINT APIENTRY ChooseDirectoryValidateProc(HWND hdlg, UINT uMsg, + LPARAM wParam, LPARAM lParam); +static UINT CALLBACK ColorDlgHookProc(HWND hDlg, UINT uMsg, WPARAM wParam, + LPARAM lParam); +static void CleanupOFNOptions(OFNOpts *optsPtr); +static int ParseOFNOptions(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[], enum OFNOper oper, OFNOpts *optsPtr); +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper); +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper); +static int GetFileName(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[], enum OFNOper oper); +static int MakeFilterVista(Tcl_Interp *interp, OFNOpts *optsPtr, + DWORD *countPtr, TCLCOMDLG_FILTERSPEC **dlgFilterPtrPtr, + DWORD *defaultFilterIndexPtr); +static void FreeFilterVista(DWORD count, TCLCOMDLG_FILTERSPEC *dlgFilterPtr); +static int MakeFilter(Tcl_Interp *interp, Tcl_Obj *valuePtr, + Tcl_DString *dsPtr, Tcl_Obj *initialPtr, + int *indexPtr); +static UINT APIENTRY OFNHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, + LPARAM lParam); +static LRESULT CALLBACK MsgBoxCBTProc(int nCode, WPARAM wParam, LPARAM lParam); +static void SetTkDialog(ClientData clientData); +static const char *ConvertExternalFilename(TCHAR *filename, + Tcl_DString *dsPtr); +static void LoadShellProcs(void); + + +/* Definitions of dynamically loaded Win32 calls */ +typedef HRESULT (STDAPICALLTYPE SHCreateItemFromParsingNameProc)( + PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); +struct ShellProcPointers { + SHCreateItemFromParsingNameProc *SHCreateItemFromParsingName; +} ShellProcs; + + +/* + *------------------------------------------------------------------------- + * + * LoadShellProcs -- + * + * Some shell functions are not available on older versions of + * Windows. This function dynamically loads them and stores pointers + * to them in ShellProcs. Any function that is not available has + * the corresponding pointer set to NULL. + * + * Note this call never fails. Unavailability of a function is not + * a reason for failure. Caller should check whether a particular + * function pointer is NULL or not. Once loaded a function stays + * forever loaded. + * + * XXX - we load the function pointers into global memory. This implies + * there is a potential (however small) for race conditions between + * threads. However, Tk is in any case meant to be loaded in exactly + * one thread so this should not be an issue and saves us from + * unnecessary bookkeeping. + * + * Return value: + * None. + * + * Side effects: + * ShellProcs is populated. + *------------------------------------------------------------------------- + */ +static void LoadShellProcs() +{ + static HMODULE shell32_handle = NULL; + + if (shell32_handle != NULL) + return; /* We have already been through here. */ + + shell32_handle = GetModuleHandle(TEXT("shell32.dll")); + if (shell32_handle == NULL) /* Should never happen but check anyways. */ + return; + + ShellProcs.SHCreateItemFromParsingName = + (SHCreateItemFromParsingNameProc*) GetProcAddress(shell32_handle, + "SHCreateItemFromParsingName"); +} + + +/* + *------------------------------------------------------------------------- + * + * EatSpuriousMessageBugFix -- + * + * In the file open/save dialog, double clicking on a list item causes + * the dialog box to close, but an unwanted WM_LBUTTONUP message is sent + * to the window underneath. If the window underneath happens to be a + * windows control (eg a button) then it will be activated by accident. + * + * This problem does not occur in dialog boxes, because windows must do + * some special processing to solve the problem. (separate message + * processing functions are used to cope with keyboard navigation of + * controls.) + * + * Here is one solution. After returning, we flush all mouse events + * for 1/4 second. In 8.6.5 and earlier, the code used to + * poll the message queue consuming WM_LBUTTONUP messages. + * On seeing a WM_LBUTTONDOWN message, it would exit early, since the user + * must be doing something new. However this early exit does not work + * on Vista and later because the Windows sends both BUTTONDOWN and + * BUTTONUP after the DBLCLICK instead of just BUTTONUP as on XP. + * Rather than try and figure out version specific sequences, we + * ignore all mouse events in that interval. + * + * This fix only works for the current application, so the problem will + * still occur if the open dialog happens to be over another applications + * button. However this is a fairly rare occurrance. + * + * Results: + * None. + * + * Side effects: + * Consumes unwanted mouse related messages. + * + *------------------------------------------------------------------------- + */ + +static void +EatSpuriousMessageBugFix(void) +{ + MSG msg; + DWORD nTime = GetTickCount() + 250; + + while (GetTickCount() < nTime) { + PeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE); + } +} + +/* + *------------------------------------------------------------------------- + * + * TkWinDialogDebug -- + * + * Function to turn on/off debugging support for common dialogs under + * windows. The variable "tk_debug" is set to the identifier of the + * dialog window when the modal dialog window pops up and it is safe to + * send messages to the dialog. + * + * Results: + * None. + * + * Side effects: + * This variable only makes sense if just one dialog is up at a time. + * + *------------------------------------------------------------------------- + */ + +void +TkWinDialogDebug( + int debug) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + tsdPtr->debugFlag = debug; +} + +/* + *------------------------------------------------------------------------- + * + * Tk_ChooseColorObjCmd -- + * + * This function implements the color dialog box for the Windows + * platform. See the user documentation for details on what it does. + * + * Results: + * See user documentation. + * + * Side effects: + * A dialog window is created the first time this function is called. + * This window is not destroyed and will be reused the next time the + * application invokes the "tk_chooseColor" command. + * + *------------------------------------------------------------------------- + */ + +int +Tk_ChooseColorObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tk_Window tkwin = clientData, parent; + HWND hWnd; + int i, oldMode, winCode, result; + CHOOSECOLOR chooseColor; + static int inited = 0; + static COLORREF dwCustColors[16]; + static long oldColor; /* the color selected last time */ + static const char *const optionStrings[] = { + "-initialcolor", "-parent", "-title", NULL + }; + enum options { + COLOR_INITIAL, COLOR_PARENT, COLOR_TITLE + }; + + result = TCL_OK; + if (inited == 0) { + /* + * dwCustColors stores the custom color which the user can modify. We + * store these colors in a static array so that the next time the + * color dialog pops up, the same set of custom colors remain in the + * dialog. + */ + + for (i = 0; i < 16; i++) { + dwCustColors[i] = RGB(255-i * 10, i, i * 10); + } + oldColor = RGB(0xa0, 0xa0, 0xa0); + inited = 1; + } + + parent = tkwin; + chooseColor.lStructSize = sizeof(CHOOSECOLOR); + chooseColor.hwndOwner = NULL; + chooseColor.hInstance = NULL; + chooseColor.rgbResult = oldColor; + chooseColor.lpCustColors = dwCustColors; + chooseColor.Flags = CC_RGBINIT | CC_FULLOPEN | CC_ENABLEHOOK; + chooseColor.lCustData = (LPARAM) NULL; + chooseColor.lpfnHook = (LPOFNHOOKPROC) ColorDlgHookProc; + chooseColor.lpTemplateName = (LPTSTR) interp; + + for (i = 1; i < objc; i += 2) { + int index; + const char *string; + Tcl_Obj *optionPtr, *valuePtr; + + optionPtr = objv[i]; + valuePtr = objv[i + 1]; + + if (Tcl_GetIndexFromObjStruct(interp, optionPtr, optionStrings, + sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { + return TCL_ERROR; + } + if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value for \"%s\" missing", Tcl_GetString(optionPtr))); + Tcl_SetErrorCode(interp, "TK", "COLORDIALOG", "VALUE", NULL); + return TCL_ERROR; + } + + string = Tcl_GetString(valuePtr); + switch ((enum options) index) { + case COLOR_INITIAL: { + XColor *colorPtr; + + colorPtr = Tk_GetColor(interp, tkwin, string); + if (colorPtr == NULL) { + return TCL_ERROR; + } + chooseColor.rgbResult = RGB(colorPtr->red / 0x100, + colorPtr->green / 0x100, colorPtr->blue / 0x100); + break; + } + case COLOR_PARENT: + parent = Tk_NameToWindow(interp, string, tkwin); + if (parent == NULL) { + return TCL_ERROR; + } + break; + case COLOR_TITLE: + chooseColor.lCustData = (LPARAM) string; + break; + } + } + + Tk_MakeWindowExist(parent); + chooseColor.hwndOwner = NULL; + hWnd = Tk_GetHWND(Tk_WindowId(parent)); + chooseColor.hwndOwner = hWnd; + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + winCode = ChooseColor(&chooseColor); + (void) Tcl_SetServiceMode(oldMode); + + /* + * Ensure that hWnd is enabled, because it can happen that we have updated + * the wrapper of the parent, which causes us to leave this child disabled + * (Windows loses sync). + */ + + EnableWindow(hWnd, 1); + + /* + * Clear the interp result since anything may have happened during the + * modal loop. + */ + + Tcl_ResetResult(interp); + + /* + * 3. Process the result of the dialog + */ + + if (winCode) { + /* + * User has selected a color + */ + + Tcl_SetObjResult(interp, Tcl_ObjPrintf("#%02x%02x%02x", + GetRValue(chooseColor.rgbResult), + GetGValue(chooseColor.rgbResult), + GetBValue(chooseColor.rgbResult))); + oldColor = chooseColor.rgbResult; + result = TCL_OK; + } + + return result; +} + +/* + *------------------------------------------------------------------------- + * + * ColorDlgHookProc -- + * + * Provides special handling of messages for the Color common dialog box. + * Used to set the title when the dialog first appears. + * + * Results: + * The return value is 0 if the default dialog box function should handle + * the message, non-zero otherwise. + * + * Side effects: + * Changes the title of the dialog window. + * + *---------------------------------------------------------------------- + */ + +static UINT CALLBACK +ColorDlgHookProc( + HWND hDlg, /* Handle to the color dialog. */ + UINT uMsg, /* Type of message. */ + WPARAM wParam, /* First message parameter. */ + LPARAM lParam) /* Second message parameter. */ +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + const char *title; + CHOOSECOLOR *ccPtr; + + if (WM_INITDIALOG == uMsg) { + + /* + * Set the title string of the dialog. + */ + + ccPtr = (CHOOSECOLOR *) lParam; + title = (const char *) ccPtr->lCustData; + + if ((title != NULL) && (title[0] != '\0')) { + Tcl_DString ds; + + SetWindowText(hDlg, Tcl_WinUtfToTChar(title,-1,&ds)); + Tcl_DStringFree(&ds); + } + if (tsdPtr->debugFlag) { + tsdPtr->debugInterp = (Tcl_Interp *) ccPtr->lpTemplateName; + Tcl_DoWhenIdle(SetTkDialog, hDlg); + } + return TRUE; + } + return FALSE; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetOpenFileCmd -- + * + * This function implements the "open file" dialog box for the Windows + * platform. See the user documentation for details on what it does. + * + * Results: + * See user documentation. + * + * Side effects: + * A dialog window is created the first this function is called. + * + *---------------------------------------------------------------------- + */ + +int +Tk_GetOpenFileObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return GetFileName(clientData, interp, objc, objv, OFN_FILE_OPEN); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetSaveFileCmd -- + * + * Same as Tk_GetOpenFileCmd but opens a "save file" dialog box + * instead + * + * Results: + * Same as Tk_GetOpenFileCmd. + * + * Side effects: + * Same as Tk_GetOpenFileCmd. + * + *---------------------------------------------------------------------- + */ + +int +Tk_GetSaveFileObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + return GetFileName(clientData, interp, objc, objv, OFN_FILE_SAVE); +} + +/* + *---------------------------------------------------------------------- + * + * CleanupOFNOptions -- + * + * Cleans up any storage allocated by ParseOFNOptions + * + * Results: + * None. + * + * Side effects: + * Releases resources held by *optsPtr + *---------------------------------------------------------------------- + */ +static void CleanupOFNOptions(OFNOpts *optsPtr) +{ + Tcl_DStringFree(&optsPtr->utfDirString); +} + + + +/* + *---------------------------------------------------------------------- + * + * ParseOFNOptions -- + * + * Option parsing for tk_get{Open,Save}File + * + * Results: + * TCL_OK on success, TCL_ERROR otherwise + * + * Side effects: + * Returns option values in *optsPtr. Note these may include string + * pointers into objv[] + *---------------------------------------------------------------------- + */ + +static int +ParseOFNOptions( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument objects. */ + enum OFNOper oper, /* 1 for Open, 0 for Save */ + OFNOpts *optsPtr) /* Output, uninitialized on entry */ +{ + int i; + Tcl_DString ds; + enum options { + FILE_DEFAULT, FILE_TYPES, FILE_INITDIR, FILE_INITFILE, FILE_PARENT, + FILE_TITLE, FILE_TYPEVARIABLE, FILE_MULTIPLE, FILE_CONFIRMOW, + FILE_MUSTEXIST, + }; + struct Options { + const char *name; + enum options value; + }; + static const struct Options saveOptions[] = { + {"-confirmoverwrite", FILE_CONFIRMOW}, + {"-defaultextension", FILE_DEFAULT}, + {"-filetypes", FILE_TYPES}, + {"-initialdir", FILE_INITDIR}, + {"-initialfile", FILE_INITFILE}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {"-typevariable", FILE_TYPEVARIABLE}, + {NULL, FILE_DEFAULT/*ignored*/ } + }; + static const struct Options openOptions[] = { + {"-defaultextension", FILE_DEFAULT}, + {"-filetypes", FILE_TYPES}, + {"-initialdir", FILE_INITDIR}, + {"-initialfile", FILE_INITFILE}, + {"-multiple", FILE_MULTIPLE}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {"-typevariable", FILE_TYPEVARIABLE}, + {NULL, FILE_DEFAULT/*ignored*/ } + }; + static const struct Options dirOptions[] = { + {"-initialdir", FILE_INITDIR}, + {"-mustexist", FILE_MUSTEXIST}, + {"-parent", FILE_PARENT}, + {"-title", FILE_TITLE}, + {NULL, FILE_DEFAULT/*ignored*/ } + }; + + const struct Options *options = NULL; + + switch (oper) { + case OFN_FILE_SAVE: options = saveOptions; break; + case OFN_DIR_CHOOSE: options = dirOptions; break; + case OFN_FILE_OPEN: options = openOptions; break; + } + + ZeroMemory(optsPtr, sizeof(*optsPtr)); + // optsPtr->forceXPStyle = 1; + optsPtr->tkwin = clientData; + optsPtr->confirmOverwrite = 1; /* By default we ask for confirmation */ + Tcl_DStringInit(&optsPtr->utfDirString); + optsPtr->file[0] = 0; + + for (i = 1; i < objc; i += 2) { + int index; + const char *string; + Tcl_Obj *valuePtr; + + if (Tcl_GetIndexFromObjStruct(interp, objv[i], options, + sizeof(struct Options), "option", 0, &index) != TCL_OK) { + /* + * XXX -xpstyle is explicitly checked for as it is undocumented + * and we do not want it to show in option error messages. + */ + if (strcmp(Tcl_GetString(objv[i]), "-xpstyle")) + goto error_return; + if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("value for \"-xpstyle\" missing", -1)); + Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "VALUE", NULL); + goto error_return; + } + if (Tcl_GetBooleanFromObj(interp, objv[i+1], + &optsPtr->forceXPStyle) != TCL_OK) + goto error_return; + + continue; + + } else if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value for \"%s\" missing", options[index].name)); + Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "VALUE", NULL); + goto error_return; + } + + valuePtr = objv[i + 1]; + string = Tcl_GetString(valuePtr); + switch (options[index].value) { + case FILE_DEFAULT: + optsPtr->extObj = valuePtr; + break; + case FILE_TYPES: + optsPtr->filterObj = valuePtr; + break; + case FILE_INITDIR: + Tcl_DStringFree(&optsPtr->utfDirString); + if (Tcl_TranslateFileName(interp, string, + &optsPtr->utfDirString) == NULL) + goto error_return; + break; + case FILE_INITFILE: + if (Tcl_TranslateFileName(interp, string, &ds) == NULL) + goto error_return; + Tcl_UtfToExternal(NULL, TkWinGetUnicodeEncoding(), + Tcl_DStringValue(&ds), Tcl_DStringLength(&ds), 0, NULL, + (char *) &optsPtr->file[0], sizeof(optsPtr->file), + NULL, NULL, NULL); + Tcl_DStringFree(&ds); + break; + case FILE_PARENT: + optsPtr->tkwin = Tk_NameToWindow(interp, string, clientData); + if (optsPtr->tkwin == NULL) + goto error_return; + break; + case FILE_TITLE: + optsPtr->titleObj = valuePtr; + break; + case FILE_TYPEVARIABLE: + optsPtr->typeVariableObj = valuePtr; + optsPtr->initialTypeObj = Tcl_ObjGetVar2(interp, valuePtr, + NULL, TCL_GLOBAL_ONLY); + break; + case FILE_MULTIPLE: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->multi) != TCL_OK) + goto error_return; + break; + case FILE_CONFIRMOW: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->confirmOverwrite) != TCL_OK) + goto error_return; + break; + case FILE_MUSTEXIST: + if (Tcl_GetBooleanFromObj(interp, valuePtr, + &optsPtr->mustExist) != TCL_OK) + goto error_return; + break; + } + } + + return TCL_OK; + +error_return: /* interp should already hold error */ + /* On error, we need to clean up anything we might have allocated */ + CleanupOFNOptions(optsPtr); + return TCL_ERROR; + +} + + +/* + *---------------------------------------------------------------------- + * VistaFileDialogsAvailable + * + * Checks whether the new (Vista) file dialogs can be used on + * the system. + * + * Returns: + * 1 if new dialogs are available, 0 otherwise + * + * Side effects: + * Loads required procedures dynamically if available. + * If new dialogs are available, COM is also initialized. + *---------------------------------------------------------------------- + */ +static int VistaFileDialogsAvailable() +{ + HRESULT hr; + IFileDialog *fdlgPtr = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->newFileDialogsState == FDLG_STATE_INIT) { + tsdPtr->newFileDialogsState = FDLG_STATE_USE_OLD; + LoadShellProcs(); + if (ShellProcs.SHCreateItemFromParsingName != NULL) { + hr = CoInitialize(0); + /* XXX - need we schedule CoUninitialize at thread shutdown ? */ + + /* Ensure all COM interfaces we use are available */ + if (SUCCEEDED(hr)) { + hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, (void **) &fdlgPtr); + if (SUCCEEDED(hr)) { + fdlgPtr->lpVtbl->Release(fdlgPtr); + hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, + (void **) &fdlgPtr); + if (SUCCEEDED(hr)) { + fdlgPtr->lpVtbl->Release(fdlgPtr); + + /* Looks like we have all we need */ + tsdPtr->newFileDialogsState = FDLG_STATE_USE_NEW; + } + } + } + } + } + + return (tsdPtr->newFileDialogsState == FDLG_STATE_USE_NEW); +} + +/* + *---------------------------------------------------------------------- + * + * GetFileNameVista -- + * + * Displays the new file dialogs on Vista and later. + * This function must generally not be called unless the + * tsdPtr->newFileDialogsState is FDLG_STATE_USE_NEW but if + * it is, it will just pass the call to the older GetFileNameXP + * + * Results: + * TCL_OK - dialog was successfully displayed, results returned in interp + * TCL_ERROR - error return + * + * Side effects: + * Dialogs is displayed + *---------------------------------------------------------------------- + */ +static int GetFileNameVista(Tcl_Interp *interp, OFNOpts *optsPtr, + enum OFNOper oper) +{ + HRESULT hr; + HWND hWnd; + DWORD flags, nfilters, defaultFilterIndex; + TCLCOMDLG_FILTERSPEC *filterPtr = NULL; + IFileDialog *fdlgIf = NULL; + IShellItem *dirIf = NULL; + LPWSTR wstr; + Tcl_Obj *resultObj = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + int oldMode; + + if (tsdPtr->newFileDialogsState != FDLG_STATE_USE_NEW) { + Tcl_Panic("Internal error: GetFileNameVista: IFileDialog API not available"); + return TCL_ERROR; + } + + /* + * At this point new interfaces are supposed to be available. + * fdlgIf is actually a IFileOpenDialog or IFileSaveDialog + * both of which inherit from IFileDialog. We use the common + * IFileDialog interface for the most part, casting only for + * type-specific calls. + */ + Tk_MakeWindowExist(optsPtr->tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(optsPtr->tkwin)); + + /* + * The only validation we need to do w.r.t caller supplied data + * is the filter specification so do that before creating + */ + if (MakeFilterVista(interp, optsPtr, &nfilters, &filterPtr, + &defaultFilterIndex) != TCL_OK) + return TCL_ERROR; + + /* + * Beyond this point, do not just return on error as there will be + * resources that need to be released/freed. + */ + + if (oper == OFN_FILE_OPEN || oper == OFN_DIR_CHOOSE) + hr = CoCreateInstance(&ClsidFileOpenDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileOpenDialog, (void **) &fdlgIf); + else + hr = CoCreateInstance(&ClsidFileSaveDialog, NULL, + CLSCTX_INPROC_SERVER, &IIDIFileSaveDialog, (void **) &fdlgIf); + + if (FAILED(hr)) + goto vamoose; + + /* + * Get current settings first because we want to preserve existing + * settings like whether to show hidden files etc. based on the + * user's existing preference + */ + hr = fdlgIf->lpVtbl->GetOptions(fdlgIf, &flags); + if (FAILED(hr)) + goto vamoose; + + if (filterPtr) { + /* + * Causes -filetypes {{All *}} -defaultextension ext to return + * foo.ext.ext when foo is typed into the entry box + * flags |= FOS_STRICTFILETYPES; + */ + hr = fdlgIf->lpVtbl->SetFileTypes(fdlgIf, nfilters, filterPtr); + if (FAILED(hr)) + goto vamoose; + hr = fdlgIf->lpVtbl->SetFileTypeIndex(fdlgIf, defaultFilterIndex); + if (FAILED(hr)) + goto vamoose; + } + + /* Flags are equivalent to those we used in the older API */ + + /* + * Following flags must be set irrespective of original setting + * XXX - should FOS_NOVALIDATE be there ? Note FOS_NOVALIDATE has different + * semantics than OFN_NOVALIDATE in the old API. + */ + flags |= + FOS_FORCEFILESYSTEM | /* Only want files, not other shell items */ + FOS_NOVALIDATE | /* Don't check for access denied etc. */ + FOS_PATHMUSTEXIST; /* The *directory* path must exist */ + + + if (oper == OFN_DIR_CHOOSE) { + flags |= FOS_PICKFOLDERS; + if (optsPtr->mustExist) + flags |= FOS_FILEMUSTEXIST; /* XXX - check working */ + } else + flags &= ~ FOS_PICKFOLDERS; + + if (optsPtr->multi) + flags |= FOS_ALLOWMULTISELECT; + else + flags &= ~FOS_ALLOWMULTISELECT; + + if (optsPtr->confirmOverwrite) + flags |= FOS_OVERWRITEPROMPT; + else + flags &= ~FOS_OVERWRITEPROMPT; + + hr = fdlgIf->lpVtbl->SetOptions(fdlgIf, flags); + if (FAILED(hr)) + goto vamoose; + + if (optsPtr->extObj != NULL) { + Tcl_DString ds; + const char *src; + + src = Tcl_GetString(optsPtr->extObj); + wstr = (LPWSTR) Tcl_WinUtfToTChar(src, optsPtr->extObj->length, &ds); + if (wstr[0] == L'.') + ++wstr; + hr = fdlgIf->lpVtbl->SetDefaultExtension(fdlgIf, wstr); + Tcl_DStringFree(&ds); + if (FAILED(hr)) + goto vamoose; + } + + if (optsPtr->titleObj != NULL) { + Tcl_DString ds; + const char *src; + + src = Tcl_GetString(optsPtr->titleObj); + wstr = (LPWSTR) Tcl_WinUtfToTChar(src, optsPtr->titleObj->length, &ds); + hr = fdlgIf->lpVtbl->SetTitle(fdlgIf, wstr); + Tcl_DStringFree(&ds); + if (FAILED(hr)) + goto vamoose; + } + + if (optsPtr->file[0]) { + hr = fdlgIf->lpVtbl->SetFileName(fdlgIf, optsPtr->file); + if (FAILED(hr)) + goto vamoose; + } + + if (Tcl_DStringValue(&optsPtr->utfDirString)[0] != '\0') { + Tcl_Obj *normPath, *iniDirPath; + iniDirPath = Tcl_NewStringObj(Tcl_DStringValue(&optsPtr->utfDirString), -1); + Tcl_IncrRefCount(iniDirPath); + normPath = Tcl_FSGetNormalizedPath(interp, iniDirPath); + /* XXX - Note on failures do not raise error, simply ignore ini dir */ + if (normPath) { + const WCHAR *nativePath; + Tcl_IncrRefCount(normPath); + nativePath = Tcl_FSGetNativePath(normPath); /* Points INTO normPath*/ + if (nativePath) { + hr = ShellProcs.SHCreateItemFromParsingName( + nativePath, NULL, + &IIDIShellItem, (void **) &dirIf); + if (SUCCEEDED(hr)) { + /* Note we use SetFolder, not SetDefaultFolder - see MSDN */ + fdlgIf->lpVtbl->SetFolder(fdlgIf, dirIf); /* Ignore errors */ + } + } + Tcl_DecrRefCount(normPath); /* ALSO INVALIDATES nativePath !! */ + } + Tcl_DecrRefCount(iniDirPath); + } + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + hr = fdlgIf->lpVtbl->Show(fdlgIf, hWnd); + Tcl_SetServiceMode(oldMode); + EatSpuriousMessageBugFix(); + + /* + * Ensure that hWnd is enabled, because it can happen that we have updated + * the wrapper of the parent, which causes us to leave this child disabled + * (Windows loses sync). + */ + + if (hWnd) + EnableWindow(hWnd, 1); + + /* + * Clear interp result since it might have been set during the modal loop. + * http://core.tcl.tk/tk/tktview/4a0451f5291b3c9168cc560747dae9264e1d2ef6 + */ + Tcl_ResetResult(interp); + + if (SUCCEEDED(hr)) { + if ((oper == OFN_FILE_OPEN) && optsPtr->multi) { + IShellItemArray *multiIf; + DWORD dw, count; + IFileOpenDialog *fodIf = (IFileOpenDialog *) fdlgIf; + hr = fodIf->lpVtbl->GetResults(fodIf, &multiIf); + if (SUCCEEDED(hr)) { + Tcl_Obj *multiObj; + hr = multiIf->lpVtbl->GetCount(multiIf, &count); + multiObj = Tcl_NewListObj(count, NULL); + if (SUCCEEDED(hr)) { + IShellItem *itemIf; + for (dw = 0; dw < count; ++dw) { + hr = multiIf->lpVtbl->GetItemAt(multiIf, dw, &itemIf); + if (FAILED(hr)) + break; + hr = itemIf->lpVtbl->GetDisplayName(itemIf, + SIGDN_FILESYSPATH, &wstr); + if (SUCCEEDED(hr)) { + Tcl_DString fnds; + + ConvertExternalFilename(wstr, &fnds); + CoTaskMemFree(wstr); + Tcl_ListObjAppendElement( + interp, multiObj, + Tcl_NewStringObj(Tcl_DStringValue(&fnds), + Tcl_DStringLength(&fnds))); + Tcl_DStringFree(&fnds); + } + itemIf->lpVtbl->Release(itemIf); + if (FAILED(hr)) + break; + } + } + multiIf->lpVtbl->Release(multiIf); + if (SUCCEEDED(hr)) + resultObj = multiObj; + else + Tcl_DecrRefCount(multiObj); + } + } else { + IShellItem *resultIf; + hr = fdlgIf->lpVtbl->GetResult(fdlgIf, &resultIf); + if (SUCCEEDED(hr)) { + hr = resultIf->lpVtbl->GetDisplayName(resultIf, SIGDN_FILESYSPATH, + &wstr); + if (SUCCEEDED(hr)) { + Tcl_DString fnds; + + ConvertExternalFilename(wstr, &fnds); + resultObj = Tcl_NewStringObj(Tcl_DStringValue(&fnds), + Tcl_DStringLength(&fnds)); + CoTaskMemFree(wstr); + Tcl_DStringFree(&fnds); + } + resultIf->lpVtbl->Release(resultIf); + } + } + if (SUCCEEDED(hr)) { + if (filterPtr && optsPtr->typeVariableObj) { + UINT ftix; + + hr = fdlgIf->lpVtbl->GetFileTypeIndex(fdlgIf, &ftix); + if (SUCCEEDED(hr)) { + /* Note ftix is a 1-based index */ + if (ftix > 0 && ftix <= nfilters) { + Tcl_DString ftds; + Tcl_Obj *ftobj; + + Tcl_WinTCharToUtf(filterPtr[ftix-1].pszName, -1, &ftds); + ftobj = Tcl_NewStringObj(Tcl_DStringValue(&ftds), + Tcl_DStringLength(&ftds)); + Tcl_ObjSetVar2(interp, optsPtr->typeVariableObj, NULL, + ftobj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG); + Tcl_DStringFree(&ftds); + } + } + } + } + } else { + if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)) + hr = 0; /* User cancelled, return empty string */ + } + +vamoose: /* (hr != 0) => error */ + if (dirIf) + dirIf->lpVtbl->Release(dirIf); + if (fdlgIf) + fdlgIf->lpVtbl->Release(fdlgIf); + + if (filterPtr) + FreeFilterVista(nfilters, filterPtr); + + if (hr == 0) { + if (resultObj) /* May be NULL if user cancelled */ + Tcl_SetObjResult(interp, resultObj); + return TCL_OK; + } else { + if (resultObj) + Tcl_DecrRefCount(resultObj); + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); + return TCL_ERROR; + } +} + + +/* + *---------------------------------------------------------------------- + * + * GetFileNameXP -- + * + * Displays the old pre-Vista file dialogs. + * + * Results: + * TCL_OK - if dialog was successfully displayed + * TCL_ERROR - error return + * + * Side effects: + * See user documentation. + *---------------------------------------------------------------------- + */ +static int GetFileNameXP(Tcl_Interp *interp, OFNOpts *optsPtr, enum OFNOper oper) +{ + OPENFILENAME ofn; + OFNData ofnData; + int cdlgerr; + int filterIndex = 0, result = TCL_ERROR, winCode, oldMode; + HWND hWnd; + Tcl_DString utfFilterString, ds; + Tcl_DString extString, filterString, dirString, titleString; + const char *str; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + ZeroMemory(&ofnData, sizeof(OFNData)); + Tcl_DStringInit(&utfFilterString); + Tcl_DStringInit(&dirString); /* XXX - original code was missing this + leaving dirString uninitialized for + the unlikely code path where cwd failed */ + + if (MakeFilter(interp, optsPtr->filterObj, &utfFilterString, + optsPtr->initialTypeObj, &filterIndex) != TCL_OK) { + goto end; + } + + Tk_MakeWindowExist(optsPtr->tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(optsPtr->tkwin)); + + ZeroMemory(&ofn, sizeof(OPENFILENAME)); + ofn.lStructSize = sizeof(OPENFILENAME); + ofn.hwndOwner = hWnd; + ofn.hInstance = TkWinGetHInstance(ofn.hwndOwner); + ofn.lpstrFile = optsPtr->file; + ofn.nMaxFile = TK_MULTI_MAX_PATH; + ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR + | OFN_EXPLORER| OFN_ENABLEHOOK| OFN_ENABLESIZING; + ofn.lpfnHook = (LPOFNHOOKPROC) OFNHookProc; + ofn.lCustData = (LPARAM) &ofnData; + + if (oper != OFN_FILE_SAVE) { + ofn.Flags |= OFN_FILEMUSTEXIST; + } else if (optsPtr->confirmOverwrite) { + ofn.Flags |= OFN_OVERWRITEPROMPT; + } + if (tsdPtr->debugFlag != 0) { + ofnData.interp = interp; + } + if (optsPtr->multi != 0) { + ofn.Flags |= OFN_ALLOWMULTISELECT; + + /* + * Starting buffer size. The buffer will be expanded by the OFN dialog + * procedure when necessary + */ + + ofnData.dynFileBufferSize = 512; + ofnData.dynFileBuffer = ckalloc(512 * sizeof(TCHAR)); + } + + if (optsPtr->extObj != NULL) { + str = Tcl_GetString(optsPtr->extObj); + if (str[0] == '.') + ++str; + Tcl_WinUtfToTChar(str, -1, &extString); + ofn.lpstrDefExt = (TCHAR *) Tcl_DStringValue(&extString); + } + + Tcl_WinUtfToTChar(Tcl_DStringValue(&utfFilterString), + Tcl_DStringLength(&utfFilterString), &filterString); + ofn.lpstrFilter = (TCHAR *) Tcl_DStringValue(&filterString); + ofn.nFilterIndex = filterIndex; + + if (Tcl_DStringValue(&optsPtr->utfDirString)[0] != '\0') { + Tcl_WinUtfToTChar(Tcl_DStringValue(&optsPtr->utfDirString), + Tcl_DStringLength(&optsPtr->utfDirString), &dirString); + } else { + /* + * NT 5.0 changed the meaning of lpstrInitialDir, so we have to ensure + * that we set the [pwd] if the user didn't specify anything else. + */ + + Tcl_DString cwd; + + Tcl_DStringFree(&optsPtr->utfDirString); + if ((Tcl_GetCwd(interp, &optsPtr->utfDirString) == NULL) || + (Tcl_TranslateFileName(interp, + Tcl_DStringValue(&optsPtr->utfDirString), &cwd) == NULL)) { + Tcl_ResetResult(interp); + } else { + Tcl_WinUtfToTChar(Tcl_DStringValue(&cwd), + Tcl_DStringLength(&cwd), &dirString); + } + Tcl_DStringFree(&cwd); + } + ofn.lpstrInitialDir = (TCHAR *) Tcl_DStringValue(&dirString); + + if (optsPtr->titleObj != NULL) { + Tcl_WinUtfToTChar(Tcl_GetString(optsPtr->titleObj), -1, &titleString); + ofn.lpstrTitle = (TCHAR *) Tcl_DStringValue(&titleString); + } + + /* + * Popup the dialog. + */ + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + if (oper != OFN_FILE_SAVE) { + winCode = GetOpenFileName(&ofn); + } else { + winCode = GetSaveFileName(&ofn); + } + Tcl_SetServiceMode(oldMode); + EatSpuriousMessageBugFix(); + + /* + * Ensure that hWnd is enabled, because it can happen that we have updated + * the wrapper of the parent, which causes us to leave this child disabled + * (Windows loses sync). + */ + + EnableWindow(hWnd, 1); + + /* + * Clear the interp result since anything may have happened during the + * modal loop. + */ + + Tcl_ResetResult(interp); + + /* + * Process the results. + * + * Use the CommDlgExtendedError() function to retrieve the error code. + * This function can return one of about two dozen codes; most of these + * indicate some sort of gross system failure (insufficient memory, bad + * window handles, etc.). Most of the error codes will be ignored; as we + * find we want more specific error messages for particular errors, we can + * extend the code as needed. + */ + + cdlgerr = CommDlgExtendedError(); + + /* + * We now allow FNERR_BUFFERTOOSMALL when multiselection is enabled. The + * filename buffer has been dynamically allocated by the OFN dialog + * procedure to accomodate all selected files. + */ + + if ((winCode != 0) + || ((cdlgerr == FNERR_BUFFERTOOSMALL) + && (ofn.Flags & OFN_ALLOWMULTISELECT))) { + int gotFilename = 0; /* Flag for tracking whether we have any + * filename at all. For details, see + * http://stackoverflow.com/q/9227859/301832 + */ + + if (ofn.Flags & OFN_ALLOWMULTISELECT) { + /* + * The result in dynFileBuffer contains many items, separated by + * NUL characters. It is terminated with two nulls in a row. The + * first element is the directory path. + */ + + TCHAR *files = ofnData.dynFileBuffer; + Tcl_Obj *returnList = Tcl_NewObj(); + int count = 0; + + /* + * Get directory. + */ + + ConvertExternalFilename(files, &ds); + + while (*files != '\0') { + while (*files != '\0') { + files++; + } + files++; + if (*files != '\0') { + Tcl_Obj *fullnameObj; + Tcl_DString filenameBuf; + + count++; + ConvertExternalFilename(files, &filenameBuf); + + fullnameObj = Tcl_NewStringObj(Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds)); + Tcl_AppendToObj(fullnameObj, "/", -1); + Tcl_AppendToObj(fullnameObj, Tcl_DStringValue(&filenameBuf), + Tcl_DStringLength(&filenameBuf)); + gotFilename = 1; + Tcl_DStringFree(&filenameBuf); + Tcl_ListObjAppendElement(NULL, returnList, fullnameObj); + } + } + + if (count == 0) { + /* + * Only one file was returned. + */ + + Tcl_ListObjAppendElement(NULL, returnList, + Tcl_NewStringObj(Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds))); + gotFilename |= (Tcl_DStringLength(&ds) > 0); + } + Tcl_SetObjResult(interp, returnList); + Tcl_DStringFree(&ds); + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + ConvertExternalFilename(ofn.lpstrFile, &ds), -1)); + gotFilename = (Tcl_DStringLength(&ds) > 0); + Tcl_DStringFree(&ds); + } + result = TCL_OK; + if ((ofn.nFilterIndex > 0) && gotFilename && optsPtr->typeVariableObj + && optsPtr->filterObj) { + int listObjc, count; + Tcl_Obj **listObjv = NULL; + Tcl_Obj **typeInfo = NULL; + + if (Tcl_ListObjGetElements(interp, optsPtr->filterObj, + &listObjc, &listObjv) != TCL_OK) { + result = TCL_ERROR; + } else if (Tcl_ListObjGetElements(interp, + listObjv[ofn.nFilterIndex - 1], &count, + &typeInfo) != TCL_OK) { + result = TCL_ERROR; + } else { + /* + * BUGFIX for d43a10ce2fed950e00890049f3c273f2cdd12583 + * The original code was broken because it passed typeinfo[0] + * directly into Tcl_ObjSetVar2. In the case of typeInfo[0] + * pointing into a list which is also referenced by + * typeVariableObj, TOSV2 shimmers the object into + * variable intrep which loses the list representation. + * This invalidates typeInfo[0] which is freed but + * nevertheless stored as the value of the variable. + */ + Tcl_Obj *selFilterObj = typeInfo[0]; + Tcl_IncrRefCount(selFilterObj); + if (Tcl_ObjSetVar2(interp, optsPtr->typeVariableObj, NULL, + selFilterObj, TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG) == NULL) { + result = TCL_ERROR; + } + Tcl_DecrRefCount(selFilterObj); + } + } + } else if (cdlgerr == FNERR_INVALIDFILENAME) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "invalid filename \"%s\"", + ConvertExternalFilename(ofn.lpstrFile, &ds))); + Tcl_SetErrorCode(interp, "TK", "FILEDIALOG", "INVALID_FILENAME", + NULL); + Tcl_DStringFree(&ds); + } else { + result = TCL_OK; + } + + if (ofn.lpstrTitle != NULL) { + Tcl_DStringFree(&titleString); + } + if (ofn.lpstrInitialDir != NULL) { + /* XXX - huh? lpstrInitialDir is set from Tcl_DStringValue which + can never return NULL */ + Tcl_DStringFree(&dirString); + } + Tcl_DStringFree(&filterString); + if (ofn.lpstrDefExt != NULL) { + Tcl_DStringFree(&extString); + } + +end: + Tcl_DStringFree(&utfFilterString); + if (ofnData.dynFileBuffer != NULL) { + ckfree(ofnData.dynFileBuffer); + ofnData.dynFileBuffer = NULL; + } + + return result; +} + + +/* + *---------------------------------------------------------------------- + * + * GetFileName -- + * + * Calls GetOpenFileName() or GetSaveFileName(). + * + * Results: + * See user documentation. + * + * Side effects: + * See user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +GetFileName( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[], /* Argument objects. */ + enum OFNOper oper) /* 1 to call GetOpenFileName(), 0 to call + * GetSaveFileName(). */ +{ + OFNOpts ofnOpts; + int result; + + result = ParseOFNOptions(clientData, interp, objc, objv, oper, &ofnOpts); + if (result != TCL_OK) + return result; + + if (VistaFileDialogsAvailable() && ! ofnOpts.forceXPStyle) + result = GetFileNameVista(interp, &ofnOpts, oper); + else + result = GetFileNameXP(interp, &ofnOpts, oper); + + CleanupOFNOptions(&ofnOpts); + return result; +} + + +/* + *------------------------------------------------------------------------- + * + * OFNHookProc -- + * + * Dialog box hook function. This is used to sets the "tk_dialog" + * variable for test/debugging when the dialog is ready to receive + * messages. When multiple file selection is enabled this function + * is used to process the list of names. + * + * Results: + * Returns 0 to allow default processing of messages to occur. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static UINT APIENTRY +OFNHookProc( + HWND hdlg, /* Handle to child dialog window. */ + UINT uMsg, /* Message identifier */ + WPARAM wParam, /* Message parameter */ + LPARAM lParam) /* Message parameter */ +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + OPENFILENAME *ofnPtr; + OFNData *ofnData; + + if (uMsg == WM_INITDIALOG) { + TkWinSetUserData(hdlg, lParam); + } else if (uMsg == WM_NOTIFY) { + OFNOTIFY *notifyPtr = (OFNOTIFY *) lParam; + + /* + * This is weird... or not. The CDN_FILEOK is NOT sent when the + * selection exceeds declared buffer size (the nMaxFile member of the + * OPENFILENAME struct passed to GetOpenFileName function). So, we + * have to rely on the most recent CDN_SELCHANGE then. Unfortunately + * this means, that gathering the selected filenames happens twice + * when they fit into the declared buffer. Luckily, it's not frequent + * operation so it should not incur any noticeable delay. See [Bug + * 2987995] + */ + + if (notifyPtr->hdr.code == CDN_FILEOK || + notifyPtr->hdr.code == CDN_SELCHANGE) { + int dirsize, selsize; + TCHAR *buffer; + int buffersize; + + /* + * Change of selection. Unscramble the unholy mess that's in the + * selection buffer, resizing it if necessary. + */ + + ofnPtr = notifyPtr->lpOFN; + ofnData = (OFNData *) ofnPtr->lCustData; + buffer = ofnData->dynFileBuffer; + hdlg = GetParent(hdlg); + + selsize = (int) SendMessage(hdlg, CDM_GETSPEC, 0, 0); + dirsize = (int) SendMessage(hdlg, CDM_GETFOLDERPATH, 0, 0); + buffersize = (selsize + dirsize + 1); + + /* + * Just empty the buffer if dirsize indicates an error. [Bug + * 3071836] + */ + + if ((selsize > 1) && (dirsize > 0)) { + if (ofnData->dynFileBufferSize < buffersize) { + buffer = ckrealloc(buffer, buffersize * sizeof(TCHAR)); + ofnData->dynFileBufferSize = buffersize; + ofnData->dynFileBuffer = buffer; + } + + SendMessage(hdlg, CDM_GETFOLDERPATH, dirsize, (LPARAM) buffer); + buffer += dirsize; + + SendMessage(hdlg, CDM_GETSPEC, selsize, (LPARAM) buffer); + + /* + * If there are multiple files, delete the quotes and change + * every second quote to NULL terminator + */ + + if (buffer[0] == '"') { + BOOL findquote = TRUE; + TCHAR *tmp = buffer; + + while (*buffer != '\0') { + if (findquote) { + if (*buffer == '"') { + findquote = FALSE; + } + buffer++; + } else { + if (*buffer == '"') { + findquote = TRUE; + *buffer = '\0'; + } + *tmp++ = *buffer++; + } + } + *tmp = '\0'; /* Second NULL terminator. */ + } else { + + /* + * Replace directory terminating NULL with a with a backslash, + * but only if not an absolute path. + */ + + Tcl_DString tmpfile; + ConvertExternalFilename(buffer, &tmpfile); + if (TCL_PATH_ABSOLUTE == + Tcl_GetPathType(Tcl_DStringValue(&tmpfile))) { + /* re-get the full path to the start of the buffer */ + buffer = (TCHAR *) ofnData->dynFileBuffer; + SendMessage(hdlg, CDM_GETSPEC, selsize, (LPARAM) buffer); + } else { + *(buffer-1) = '\\'; + } + buffer[selsize] = '\0'; /* Second NULL terminator. */ + Tcl_DStringFree(&tmpfile); + } + } else { + /* + * Nothing is selected, so just empty the string. + */ + + if (buffer != NULL) { + *buffer = '\0'; + } + } + } + } else if (uMsg == WM_WINDOWPOSCHANGED) { + /* + * This message is delivered at the right time to enable Tk to set the + * debug information. Unhooks itself so it won't set the debug + * information every time it gets a WM_WINDOWPOSCHANGED message. + */ + + ofnPtr = (OPENFILENAME *) TkWinGetUserData(hdlg); + if (ofnPtr != NULL) { + ofnData = (OFNData *) ofnPtr->lCustData; + if (ofnData->interp != NULL) { + hdlg = GetParent(hdlg); + tsdPtr->debugInterp = ofnData->interp; + Tcl_DoWhenIdle(SetTkDialog, hdlg); + } + TkWinSetUserData(hdlg, NULL); + } + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * MakeFilter -- + * + * Allocate a buffer to store the filters in a format understood by + * Windows. + * + * Results: + * A standard TCL return value. + * + * Side effects: + * ofnPtr->lpstrFilter is modified. + * + *---------------------------------------------------------------------- + */ + +static int +MakeFilter( + Tcl_Interp *interp, /* Current interpreter. */ + Tcl_Obj *valuePtr, /* Value of the -filetypes option */ + Tcl_DString *dsPtr, /* Filled with windows filter string. */ + Tcl_Obj *initialPtr, /* Initial type name */ + int *indexPtr) /* Index of initial type in filter string */ +{ + char *filterStr; + char *p; + const char *initial = NULL; + int pass; + int ix = 0; /* index counter */ + FileFilterList flist; + FileFilter *filterPtr; + + if (initialPtr) { + initial = Tcl_GetString(initialPtr); + } + TkInitFileFilters(&flist); + if (TkGetFileFilters(interp, &flist, valuePtr, 1) != TCL_OK) { + return TCL_ERROR; + } + + if (flist.filters == NULL) { + /* + * Use "All Files (*.*) as the default filter if none is specified + */ + const char *defaultFilter = "All Files (*.*)"; + + p = filterStr = ckalloc(30); + + strcpy(p, defaultFilter); + p+= strlen(defaultFilter); + + *p++ = '\0'; + *p++ = '*'; + *p++ = '.'; + *p++ = '*'; + *p++ = '\0'; + *p++ = '\0'; + *p = '\0'; + + } else { + size_t len; + + if (valuePtr == NULL) { + len = 0; + } else { + (void) Tcl_GetString(valuePtr); + len = valuePtr->length; + } + + /* + * We format the filetype into a string understood by Windows: {"Text + * Documents" {.doc .txt} {TEXT}} becomes "Text Documents + * (*.doc,*.txt)\0*.doc;*.txt\0" + * + * See the Windows OPENFILENAME manual page for details on the filter + * string format. + */ + + /* + * Since we may only add asterisks (*) to the filter, we need at most + * twice the size of the string to format the filter + */ + + filterStr = ckalloc(len * 3); + + for (filterPtr = flist.filters, p = filterStr; filterPtr; + filterPtr = filterPtr->next) { + const char *sep; + FileFilterClause *clausePtr; + + /* + * Check initial index for match, set *indexPtr. Filter index is 1 + * based so increment first + */ + + ix++; + if (indexPtr && initial + && (strcmp(initial, filterPtr->name) == 0)) { + *indexPtr = ix; + } + + /* + * First, put in the name of the file type. + */ + + strcpy(p, filterPtr->name); + p+= strlen(filterPtr->name); + *p++ = ' '; + *p++ = '('; + + for (pass = 1; pass <= 2; pass++) { + /* + * In the first pass, we format the extensions in the name + * field. In the second pass, we format the extensions in the + * filter pattern field + */ + + sep = ""; + for (clausePtr=filterPtr->clauses;clausePtr; + clausePtr=clausePtr->next) { + GlobPattern *globPtr; + + for (globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + strcpy(p, sep); + p += strlen(sep); + strcpy(p, globPtr->pattern); + p += strlen(globPtr->pattern); + + if (pass == 1) { + sep = ","; + } else { + sep = ";"; + } + } + } + if (pass == 1) { + *p ++ = ')'; + } + *p++ = '\0'; + } + } + + /* + * Windows requires the filter string to be ended by two NULL + * characters. + */ + + *p++ = '\0'; + *p = '\0'; + } + + Tcl_DStringAppend(dsPtr, filterStr, (int) (p - filterStr)); + ckfree(filterStr); + + TkFreeFileFilters(&flist); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * FreeFilterVista + * + * Frees storage previously allocated by MakeFilterVista. + * count is the number of elements in dlgFilterPtr[] + */ +static void FreeFilterVista(DWORD count, TCLCOMDLG_FILTERSPEC *dlgFilterPtr) +{ + if (dlgFilterPtr != NULL) { + DWORD dw; + for (dw = 0; dw < count; ++dw) { + if (dlgFilterPtr[dw].pszName != NULL) + ckfree(dlgFilterPtr[dw].pszName); + if (dlgFilterPtr[dw].pszSpec != NULL) + ckfree(dlgFilterPtr[dw].pszSpec); + } + ckfree(dlgFilterPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * MakeFilterVista -- + * + * Returns file type filters in a format required + * by the Vista file dialogs. + * + * Results: + * A standard TCL return value. + * + * Side effects: + * Various values are returned through the parameters as + * described in the comments below. + *---------------------------------------------------------------------- + */ +static int MakeFilterVista( + Tcl_Interp *interp, /* Current interpreter. */ + OFNOpts *optsPtr, /* Caller specified options */ + DWORD *countPtr, /* Will hold number of filters */ + TCLCOMDLG_FILTERSPEC **dlgFilterPtrPtr, /* Will hold pointer to filter array. + Set to NULL if no filters specified. + Must be freed by calling + FreeFilterVista */ + DWORD *initialIndexPtr) /* Will hold index of default type */ +{ + TCLCOMDLG_FILTERSPEC *dlgFilterPtr; + const char *initial = NULL; + FileFilterList flist; + FileFilter *filterPtr; + DWORD initialIndex = 0; + Tcl_DString ds, patterns; + int i; + + if (optsPtr->filterObj == NULL) { + *dlgFilterPtrPtr = NULL; + *countPtr = 0; + return TCL_OK; + } + + if (optsPtr->initialTypeObj) + initial = Tcl_GetString(optsPtr->initialTypeObj); + + TkInitFileFilters(&flist); + if (TkGetFileFilters(interp, &flist, optsPtr->filterObj, 1) != TCL_OK) + return TCL_ERROR; + + if (flist.filters == NULL) { + *dlgFilterPtrPtr = NULL; + *countPtr = 0; + return TCL_OK; + } + + Tcl_DStringInit(&ds); + Tcl_DStringInit(&patterns); + dlgFilterPtr = ckalloc(flist.numFilters * sizeof(*dlgFilterPtr)); + + for (i = 0, filterPtr = flist.filters; + filterPtr; + filterPtr = filterPtr->next, ++i) { + const char *sep; + FileFilterClause *clausePtr; + int nbytes; + + /* Check if this entry should be shown as the default */ + if (initial && strcmp(initial, filterPtr->name) == 0) + initialIndex = i+1; /* Windows filter indices are 1-based */ + + /* First stash away the text description of the pattern */ + Tcl_WinUtfToTChar(filterPtr->name, -1, &ds); + nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ + nbytes += sizeof(WCHAR); /* Terminating \0 */ + dlgFilterPtr[i].pszName = ckalloc(nbytes); + memmove((void *) dlgFilterPtr[i].pszName, Tcl_DStringValue(&ds), nbytes); + Tcl_DStringFree(&ds); + + /* + * Loop through and join patterns with a ";" Each "clause" + * corresponds to a single textual description (called typename) + * in the tk_getOpenFile docs. Each such typename may occur + * multiple times and all these form a single filter entry + * with one clause per occurence. Further each clause may specify + * multiple patterns. Hence the nested loop here. + */ + sep = ""; + for (clausePtr=filterPtr->clauses ; clausePtr; + clausePtr=clausePtr->next) { + GlobPattern *globPtr; + for (globPtr = clausePtr->patterns; globPtr; + globPtr = globPtr->next) { + Tcl_DStringAppend(&patterns, sep, -1); + Tcl_DStringAppend(&patterns, globPtr->pattern, -1); + sep = ";"; + } + } + + /* Again we need a Unicode form of the string */ + Tcl_WinUtfToTChar(Tcl_DStringValue(&patterns), -1, &ds); + nbytes = Tcl_DStringLength(&ds); /* # bytes, not Unicode chars */ + nbytes += sizeof(WCHAR); /* Terminating \0 */ + dlgFilterPtr[i].pszSpec = ckalloc(nbytes); + memmove((void *)dlgFilterPtr[i].pszSpec, Tcl_DStringValue(&ds), nbytes); + Tcl_DStringFree(&ds); + Tcl_DStringFree(&patterns); + } + + if (initialIndex == 0) + initialIndex = 1; /* If no default, show first entry */ + *initialIndexPtr = initialIndex; + *dlgFilterPtrPtr = dlgFilterPtr; + *countPtr = flist.numFilters; + + TkFreeFileFilters(&flist); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * Tk_ChooseDirectoryObjCmd -- + * + * This function implements the "tk_chooseDirectory" dialog box for the + * Windows platform. See the user documentation for details on what it + * does. Uses the newer SHBrowseForFolder explorer type interface. + * + * Results: + * See user documentation. + * + * Side effects: + * A modal dialog window is created. Tcl_SetServiceMode() is called to + * allow background events to be processed + * + *---------------------------------------------------------------------- + * + * The function tk_chooseDirectory pops up a dialog box for the user to select + * a directory. The following option-value pairs are possible as command line + * arguments: + * + * -initialdir dirname + * + * Specifies that the directories in directory should be displayed when the + * dialog pops up. If this parameter is not specified, then the directories in + * the current working directory are displayed. If the parameter specifies a + * relative path, the return value will convert the relative path to an + * absolute path. This option may not always work on the Macintosh. This is + * not a bug. Rather, the General Controls control panel on the Mac allows the + * end user to override the application default directory. + * + * -parent window + * + * Makes window the logical parent of the dialog. The dialog is displayed on + * top of its parent window. + * + * -title titleString + * + * Specifies a string to display as the title of the dialog box. If this + * option is not specified, then a default title will be displayed. + * + * -mustexist boolean + * + * Specifies whether the user may specify non-existant directories. If this + * parameter is true, then the user may only select directories that already + * exist. The default value is false. + * + * New Behaviour: + * + * - If mustexist = 0 and a user entered folder does not exist, a prompt will + * pop-up asking if the user wants another chance to change it. The old + * dialog just returned the bogus entry. On mustexist = 1, the entries MUST + * exist before exiting the box with OK. + * + * Bugs: + * + * - If valid abs directory name is entered into the entry box and Enter + * pressed, the box will close returning the name. This is inconsistent when + * entering relative names or names with forward slashes, which are + * invalidated then corrected in the callback. After correction, the box is + * held open to allow further modification by the user. + * + * - Not sure how to implement localization of message prompts. + * + * - -title is really -message. + * + *---------------------------------------------------------------------- + */ + +int +Tk_ChooseDirectoryObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + TCHAR path[MAX_PATH]; + int oldMode, result; + LPCITEMIDLIST pidl; /* Returned by browser */ + BROWSEINFO bInfo; /* Used by browser */ + ChooseDir cdCBData; /* Structure to pass back and forth */ + LPMALLOC pMalloc; /* Used by shell */ + HWND hWnd; + TCHAR saveDir[MAX_PATH]; + Tcl_DString titleString; /* Title */ + Tcl_DString tempString; /* temporary */ + Tcl_Obj *objPtr; + OFNOpts ofnOpts; + const char *utfDir; + + result = ParseOFNOptions(clientData, interp, objc, objv, + OFN_DIR_CHOOSE, &ofnOpts); + if (result != TCL_OK) + return result; + + /* Use new dialogs if available */ + if (VistaFileDialogsAvailable() && ! ofnOpts.forceXPStyle) { + result = GetFileNameVista(interp, &ofnOpts, OFN_DIR_CHOOSE); + CleanupOFNOptions(&ofnOpts); + return result; + } + + /* Older dialogs */ + + path[0] = '\0'; + ZeroMemory(&cdCBData, sizeof(ChooseDir)); + cdCBData.interp = interp; + cdCBData.mustExist = ofnOpts.mustExist; + + utfDir = Tcl_DStringValue(&ofnOpts.utfDirString); + if (utfDir[0] != '\0') { + const TCHAR *uniStr; + + Tcl_WinUtfToTChar(Tcl_DStringValue(&ofnOpts.utfDirString), -1, + &tempString); + uniStr = (TCHAR *) Tcl_DStringValue(&tempString); + + /* Convert possible relative path to full path to keep dialog happy. */ + + GetFullPathName(uniStr, MAX_PATH, saveDir, NULL); + _tcsncpy(cdCBData.initDir, saveDir, MAX_PATH); + } + + /* XXX - rest of this (original) code has no error checks at all. */ + + /* + * Get ready to call the browser + */ + + Tk_MakeWindowExist(ofnOpts.tkwin); + hWnd = Tk_GetHWND(Tk_WindowId(ofnOpts.tkwin)); + + /* + * Setup the parameters used by SHBrowseForFolder + */ + + bInfo.hwndOwner = hWnd; + bInfo.pszDisplayName = path; + bInfo.pidlRoot = NULL; + if (_tcslen(cdCBData.initDir) == 0) { + GetCurrentDirectory(MAX_PATH, cdCBData.initDir); + } + bInfo.lParam = (LPARAM) &cdCBData; + + if (ofnOpts.titleObj != NULL) { + Tcl_WinUtfToTChar(Tcl_GetString(ofnOpts.titleObj), -1, &titleString); + bInfo.lpszTitle = (LPTSTR) Tcl_DStringValue(&titleString); + } else { + bInfo.lpszTitle = TEXT("Please choose a directory, then select OK."); + } + + /* + * Set flags to add edit box, status text line and use the new ui. Allow + * override with magic variable (ignore errors in retrieval). See + * http://msdn.microsoft.com/en-us/library/bb773205(VS.85).aspx for + * possible flag values. + */ + + bInfo.ulFlags = BIF_EDITBOX | BIF_STATUSTEXT | BIF_RETURNFSANCESTORS + | BIF_VALIDATE | BIF_NEWDIALOGSTYLE; + objPtr = Tcl_GetVar2Ex(interp, "::tk::winChooseDirFlags", NULL, + TCL_GLOBAL_ONLY); + if (objPtr != NULL) { + int flags; + Tcl_GetIntFromObj(NULL, objPtr, &flags); + bInfo.ulFlags = flags; + } + + /* + * Callback to handle events + */ + + bInfo.lpfn = (BFFCALLBACK) ChooseDirectoryValidateProc; + + /* + * Display dialog in background and process result. We look to give the + * user a chance to change their mind on an invalid folder if mustexist is + * 0. + */ + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + GetCurrentDirectory(MAX_PATH, saveDir); + if (SHGetMalloc(&pMalloc) == NOERROR) { + /* + * XXX - MSDN says CoInitialize must have been called before + * SHBrowseForFolder can be used but don't see that called anywhere. + */ + pidl = SHBrowseForFolder(&bInfo); + + /* + * This is a fix for Windows 2000, which seems to modify the folder + * name buffer even when the dialog is canceled (in this case the + * buffer contains garbage). See [Bug #3002230] + */ + + path[0] = '\0'; + + /* + * Null for cancel button or invalid dir, otherwise valid. + */ + + if (pidl != NULL) { + if (!SHGetPathFromIDList(pidl, path)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "error: not a file system folder", -1)); + Tcl_SetErrorCode(interp, "TK", "DIRDIALOG", "PSEUDO", NULL); + } + pMalloc->lpVtbl->Free(pMalloc, (void *) pidl); + } else if (_tcslen(cdCBData.retDir) > 0) { + _tcscpy(path, cdCBData.retDir); + } + pMalloc->lpVtbl->Release(pMalloc); + } + SetCurrentDirectory(saveDir); + Tcl_SetServiceMode(oldMode); + + /* + * Ensure that hWnd is enabled, because it can happen that we have updated + * the wrapper of the parent, which causes us to leave this child disabled + * (Windows loses sync). + */ + + EnableWindow(hWnd, 1); + + /* + * Change the pathname to the Tcl "normalized" pathname, where back + * slashes are used instead of forward slashes + */ + + Tcl_ResetResult(interp); + if (*path) { + Tcl_DString ds; + + Tcl_SetObjResult(interp, Tcl_NewStringObj( + ConvertExternalFilename(path, &ds), -1)); + Tcl_DStringFree(&ds); + } + + CleanupOFNOptions(&ofnOpts); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * ChooseDirectoryValidateProc -- + * + * Hook function called by the explorer ChooseDirectory dialog when + * events occur. It is used to validate the text entry the user may have + * entered. + * + * Results: + * Returns 0 to allow default processing of message, or 1 to tell default + * dialog function not to close. + * + *---------------------------------------------------------------------- + */ + +static UINT APIENTRY +ChooseDirectoryValidateProc( + HWND hwnd, + UINT message, + LPARAM lParam, + LPARAM lpData) +{ + TCHAR selDir[MAX_PATH]; + ChooseDir *chooseDirSharedData = (ChooseDir *) lpData; + Tcl_DString tempString; + Tcl_DString initDirString; + TCHAR string[MAX_PATH]; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->debugFlag) { + tsdPtr->debugInterp = (Tcl_Interp *) chooseDirSharedData->interp; + Tcl_DoWhenIdle(SetTkDialog, hwnd); + } + chooseDirSharedData->retDir[0] = '\0'; + switch (message) { + case BFFM_VALIDATEFAILED: + /* + * First save and check to see if it is a valid path name, if so then + * make that path the one shown in the window. Otherwise, it failed + * the check and should be treated as such. Use + * Set/GetCurrentDirectory which allows relative path names and names + * with forward slashes. Use Tcl_TranslateFileName to make sure names + * like ~ are converted correctly. + */ + + Tcl_WinTCharToUtf((TCHAR *) lParam, -1, &initDirString); + if (Tcl_TranslateFileName(chooseDirSharedData->interp, + Tcl_DStringValue(&initDirString), &tempString) == NULL) { + /* + * Should we expose the error (in the interp result) to the user + * at this point? + */ + + chooseDirSharedData->retDir[0] = '\0'; + return 1; + } + Tcl_DStringFree(&initDirString); + Tcl_WinUtfToTChar(Tcl_DStringValue(&tempString), -1, &initDirString); + Tcl_DStringFree(&tempString); + _tcsncpy(string, (TCHAR *) Tcl_DStringValue(&initDirString), + MAX_PATH); + Tcl_DStringFree(&initDirString); + + if (SetCurrentDirectory(string) == 0) { + + /* + * Get the full path name to the user entry, at this point it does + * not exist so see if it is supposed to. Otherwise just return + * it. + */ + + GetFullPathName(string, MAX_PATH, + chooseDirSharedData->retDir, NULL); + if (chooseDirSharedData->mustExist) { + /* + * User HAS to select a valid directory. + */ + + wsprintf(selDir, TEXT("Directory '%s' does not exist,\n") + TEXT("please select or enter an existing directory."), + chooseDirSharedData->retDir); + MessageBox(NULL, selDir, NULL, MB_ICONEXCLAMATION|MB_OK); + chooseDirSharedData->retDir[0] = '\0'; + return 1; + } + } else { + /* + * Changed to new folder OK, return immediatly with the current + * directory in utfRetDir. + */ + + GetCurrentDirectory(MAX_PATH, chooseDirSharedData->retDir); + return 0; + } + return 0; + + case BFFM_SELCHANGED: + /* + * Set the status window to the currently selected path and enable the + * OK button if a file system folder, otherwise disable the OK button + * for things like server names. Perhaps a new switch + * -enablenonfolders can be used to allow non folders to be selected. + * + * Not called when user changes edit box directly. + */ + + if (SHGetPathFromIDList((LPITEMIDLIST) lParam, selDir)) { + SendMessage(hwnd, BFFM_SETSTATUSTEXT, 0, (LPARAM) selDir); + // enable the OK button + SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM) 1); + } else { + // disable the OK button + SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM) 0); + } + UpdateWindow(hwnd); + return 1; + + case BFFM_INITIALIZED: { + /* + * Directory browser intializing - tell it where to start from, user + * specified parameter. + */ + + TCHAR *initDir = chooseDirSharedData->initDir; + + SetCurrentDirectory(initDir); + + if (*initDir == '\\') { + /* + * BFFM_SETSELECTION only understands UNC paths as pidls, so + * convert path to pidl using IShellFolder interface. + */ + + LPMALLOC pMalloc; + LPSHELLFOLDER psfFolder; + + if (SUCCEEDED(SHGetMalloc(&pMalloc))) { + if (SUCCEEDED(SHGetDesktopFolder(&psfFolder))) { + LPITEMIDLIST pidlMain; + ULONG ulCount, ulAttr; + + if (SUCCEEDED(psfFolder->lpVtbl->ParseDisplayName( + psfFolder, hwnd, NULL, (TCHAR *) + initDir, &ulCount,&pidlMain,&ulAttr)) + && (pidlMain != NULL)) { + SendMessage(hwnd, BFFM_SETSELECTION, FALSE, + (LPARAM) pidlMain); + pMalloc->lpVtbl->Free(pMalloc, pidlMain); + } + psfFolder->lpVtbl->Release(psfFolder); + } + pMalloc->lpVtbl->Release(pMalloc); + } + } else { + SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM) initDir); + } + SendMessage(hwnd, BFFM_ENABLEOK, 0, (LPARAM) 1); + break; + } + + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_MessageBoxObjCmd -- + * + * This function implements the MessageBox window for the Windows + * platform. See the user documentation for details on what it does. + * + * Results: + * See user documentation. + * + * Side effects: + * None. The MessageBox window will be destroy before this function + * returns. + * + *---------------------------------------------------------------------- + */ + +int +Tk_MessageBoxObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tk_Window tkwin = clientData, parent; + HWND hWnd; + Tcl_Obj *messageObj, *titleObj, *detailObj, *tmpObj; + int defaultBtn, icon, type; + int i, oldMode, winCode; + UINT flags; + static const char *const optionStrings[] = { + "-default", "-detail", "-icon", "-message", + "-parent", "-title", "-type", NULL + }; + enum options { + MSG_DEFAULT, MSG_DETAIL, MSG_ICON, MSG_MESSAGE, + MSG_PARENT, MSG_TITLE, MSG_TYPE + }; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + Tcl_DString titleBuf, tmpBuf; + const WCHAR *titlePtr, *tmpPtr; + const char *src; + + defaultBtn = -1; + detailObj = NULL; + icon = MB_ICONINFORMATION; + messageObj = NULL; + parent = tkwin; + titleObj = NULL; + type = MB_OK; + + for (i = 1; i < objc; i += 2) { + int index; + Tcl_Obj *optionPtr, *valuePtr; + + optionPtr = objv[i]; + valuePtr = objv[i + 1]; + + if (Tcl_GetIndexFromObjStruct(interp, optionPtr, optionStrings, + sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) { + return TCL_ERROR; + } + if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value for \"%s\" missing", Tcl_GetString(optionPtr))); + Tcl_SetErrorCode(interp, "TK", "MSGBOX", "VALUE", NULL); + return TCL_ERROR; + } + + switch ((enum options) index) { + case MSG_DEFAULT: + defaultBtn = TkFindStateNumObj(interp, optionPtr, buttonMap, + valuePtr); + if (defaultBtn < 0) { + return TCL_ERROR; + } + break; + + case MSG_DETAIL: + detailObj = valuePtr; + break; + + case MSG_ICON: + icon = TkFindStateNumObj(interp, optionPtr, iconMap, valuePtr); + if (icon < 0) { + return TCL_ERROR; + } + break; + + case MSG_MESSAGE: + messageObj = valuePtr; + break; + + case MSG_PARENT: + parent = Tk_NameToWindow(interp, Tcl_GetString(valuePtr), tkwin); + if (parent == NULL) { + return TCL_ERROR; + } + break; + + case MSG_TITLE: + titleObj = valuePtr; + break; + + case MSG_TYPE: + type = TkFindStateNumObj(interp, optionPtr, typeMap, valuePtr); + if (type < 0) { + return TCL_ERROR; + } + break; + } + } + + while (!Tk_IsTopLevel(parent)) { + parent = Tk_Parent(parent); + } + Tk_MakeWindowExist(parent); + hWnd = Tk_GetHWND(Tk_WindowId(parent)); + + flags = 0; + if (defaultBtn >= 0) { + int defaultBtnIdx = -1; + + for (i = 0; i < (int) NUM_TYPES; i++) { + if (type == allowedTypes[i].type) { + int j; + + for (j = 0; j < 3; j++) { + if (allowedTypes[i].btnIds[j] == defaultBtn) { + defaultBtnIdx = j; + break; + } + } + if (defaultBtnIdx < 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "invalid default button \"%s\"", + TkFindStateString(buttonMap, defaultBtn))); + Tcl_SetErrorCode(interp, "TK", "MSGBOX", "DEFAULT", NULL); + return TCL_ERROR; + } + break; + } + } + flags = buttonFlagMap[defaultBtnIdx]; + } + + flags |= icon | type | MB_TASKMODAL | MB_SETFOREGROUND; + + tmpObj = messageObj ? Tcl_DuplicateObj(messageObj) + : Tcl_NewUnicodeObj(NULL, 0); + Tcl_IncrRefCount(tmpObj); + if (detailObj) { + const Tcl_UniChar twoNL[] = { '\n', '\n' }; + + Tcl_AppendUnicodeToObj(tmpObj, twoNL, 2); + Tcl_AppendObjToObj(tmpObj, detailObj); + } + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + + /* + * MessageBoxW exists for all platforms. Use it to allow unicode error + * message to be displayed correctly where possible by the OS. + * + * In order to have the parent window icon reflected in a MessageBox, we + * have to create a hook that will trigger when the MessageBox is being + * created. + */ + + tsdPtr->hSmallIcon = TkWinGetIcon(parent, ICON_SMALL); + tsdPtr->hBigIcon = TkWinGetIcon(parent, ICON_BIG); + tsdPtr->hMsgBoxHook = SetWindowsHookEx(WH_CBT, MsgBoxCBTProc, NULL, + GetCurrentThreadId()); + src = Tcl_GetString(tmpObj); + tmpPtr = Tcl_WinUtfToTChar(src, tmpObj->length, &tmpBuf); + if (titleObj != NULL) { + src = Tcl_GetString(titleObj); + titlePtr = Tcl_WinUtfToTChar(src, titleObj->length, &titleBuf); + } else { + titlePtr = L""; + Tcl_DStringInit(&titleBuf); + } + winCode = MessageBox(hWnd, tmpPtr, titlePtr, flags); + Tcl_DStringFree(&titleBuf); + Tcl_DStringFree(&tmpBuf); + UnhookWindowsHookEx(tsdPtr->hMsgBoxHook); + (void) Tcl_SetServiceMode(oldMode); + + /* + * Ensure that hWnd is enabled, because it can happen that we have updated + * the wrapper of the parent, which causes us to leave this child disabled + * (Windows loses sync). + */ + + EnableWindow(hWnd, 1); + + Tcl_DecrRefCount(tmpObj); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + TkFindStateString(buttonMap, winCode), -1)); + return TCL_OK; +} + +static LRESULT CALLBACK +MsgBoxCBTProc( + int nCode, + WPARAM wParam, + LPARAM lParam) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (nCode == HCBT_CREATEWND) { + /* + * Window owned by our task is being created. Since the hook is + * installed just before the MessageBox call and removed after the + * MessageBox call, the window being created is either the message box + * or one of its controls. Check that the class is WC_DIALOG to ensure + * that it's the one we want. + */ + + LPCBT_CREATEWND lpcbtcreate = (LPCBT_CREATEWND) lParam; + + if (WC_DIALOG == lpcbtcreate->lpcs->lpszClass) { + HWND hwnd = (HWND) wParam; + + SendMessage(hwnd, WM_SETICON, ICON_SMALL, + (LPARAM) tsdPtr->hSmallIcon); + SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM) tsdPtr->hBigIcon); + } + } + + /* + * Call the next hook proc, if there is one + */ + + return CallNextHookEx(tsdPtr->hMsgBoxHook, nCode, wParam, lParam); +} + +/* + * ---------------------------------------------------------------------- + * + * SetTkDialog -- + * + * Records the HWND for a native dialog in the 'tk_dialog' variable so + * that the test-suite can operate on the correct dialog window. Use of + * this is enabled when a test program calls TkWinDialogDebug by calling + * the test command 'tkwinevent debug 1'. + * + * ---------------------------------------------------------------------- + */ + +static void +SetTkDialog( + ClientData clientData) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + char buf[32]; + + sprintf(buf, "0x%p", (HWND) clientData); + Tcl_SetVar2(tsdPtr->debugInterp, "tk_dialog", NULL, buf, TCL_GLOBAL_ONLY); +} + +/* + * Factored out a common pattern in use in this file. + */ + +static const char * +ConvertExternalFilename( + TCHAR *filename, + Tcl_DString *dsPtr) +{ + char *p; + + Tcl_WinTCharToUtf(filename, -1, dsPtr); + for (p = Tcl_DStringValue(dsPtr); *p != '\0'; p++) { + /* + * Change the pathname to the Tcl "normalized" pathname, where back + * slashes are used instead of forward slashes + */ + + if (*p == '\\') { + *p = '/'; + } + } + return Tcl_DStringValue(dsPtr); +} + +/* + * ---------------------------------------------------------------------- + * + * GetFontObj -- + * + * Convert a windows LOGFONT into a Tk font description. + * + * Result: + * A list containing a Tk font description. + * + * ---------------------------------------------------------------------- + */ + +static Tcl_Obj * +GetFontObj( + HDC hdc, + LOGFONT *plf) +{ + Tcl_DString ds; + Tcl_Obj *resObj; + int pt = 0; + + resObj = Tcl_NewListObj(0, NULL); + Tcl_WinTCharToUtf(plf->lfFaceName, -1, &ds); + Tcl_ListObjAppendElement(NULL, resObj, + Tcl_NewStringObj(Tcl_DStringValue(&ds), -1)); + Tcl_DStringFree(&ds); + pt = -MulDiv(plf->lfHeight, 72, GetDeviceCaps(hdc, LOGPIXELSY)); + Tcl_ListObjAppendElement(NULL, resObj, Tcl_NewIntObj(pt)); + if (plf->lfWeight >= 700) { + Tcl_ListObjAppendElement(NULL, resObj, Tcl_NewStringObj("bold", -1)); + } + if (plf->lfItalic) { + Tcl_ListObjAppendElement(NULL, resObj, + Tcl_NewStringObj("italic", -1)); + } + if (plf->lfUnderline) { + Tcl_ListObjAppendElement(NULL, resObj, + Tcl_NewStringObj("underline", -1)); + } + if (plf->lfStrikeOut) { + Tcl_ListObjAppendElement(NULL, resObj, + Tcl_NewStringObj("overstrike", -1)); + } + return resObj; +} + +static void +ApplyLogfont( + Tcl_Interp *interp, + Tcl_Obj *cmdObj, + HDC hdc, + LOGFONT *logfontPtr) +{ + int objc; + Tcl_Obj **objv, **tmpv; + + Tcl_ListObjGetElements(NULL, cmdObj, &objc, &objv); + tmpv = ckalloc(sizeof(Tcl_Obj *) * (objc + 2)); + memcpy(tmpv, objv, sizeof(Tcl_Obj *) * objc); + tmpv[objc] = GetFontObj(hdc, logfontPtr); + TkBackgroundEvalObjv(interp, objc+1, tmpv, TCL_EVAL_GLOBAL); + ckfree(tmpv); +} + +/* + * ---------------------------------------------------------------------- + * + * HookProc -- + * + * Font selection hook. If the user selects Apply on the dialog, we call + * the applyProc script with the currently selected font as arguments. + * + * ---------------------------------------------------------------------- + */ + +typedef struct HookData { + Tcl_Interp *interp; + Tcl_Obj *titleObj; + Tcl_Obj *cmdObj; + Tcl_Obj *parentObj; + Tcl_Obj *fontObj; + HWND hwnd; + Tk_Window parent; +} HookData; + +static UINT_PTR CALLBACK +HookProc( + HWND hwndDlg, + UINT msg, + WPARAM wParam, + LPARAM lParam) +{ + CHOOSEFONT *pcf = (CHOOSEFONT *) lParam; + HWND hwndCtrl; + static HookData *phd = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (WM_INITDIALOG == msg && lParam != 0) { + phd = (HookData *) pcf->lCustData; + phd->hwnd = hwndDlg; + if (tsdPtr->debugFlag) { + tsdPtr->debugInterp = phd->interp; + Tcl_DoWhenIdle(SetTkDialog, hwndDlg); + } + if (phd->titleObj != NULL) { + Tcl_DString title; + + Tcl_WinUtfToTChar(Tcl_GetString(phd->titleObj), -1, &title); + if (Tcl_DStringLength(&title) > 0) { + SetWindowText(hwndDlg, (LPCTSTR) Tcl_DStringValue(&title)); + } + Tcl_DStringFree(&title); + } + + /* + * Disable the colour combobox (0x473) and its label (0x443). + */ + + hwndCtrl = GetDlgItem(hwndDlg, 0x443); + if (IsWindow(hwndCtrl)) { + EnableWindow(hwndCtrl, FALSE); + } + hwndCtrl = GetDlgItem(hwndDlg, 0x473); + if (IsWindow(hwndCtrl)) { + EnableWindow(hwndCtrl, FALSE); + } + TkSendVirtualEvent(phd->parent, "TkFontchooserVisibility", NULL); + return 1; /* we handled the message */ + } + + if (WM_DESTROY == msg) { + phd->hwnd = NULL; + TkSendVirtualEvent(phd->parent, "TkFontchooserVisibility", NULL); + return 0; + } + + /* + * Handle apply button by calling the provided command script as a + * background evaluation (ie: errors dont come back here). + */ + + if (WM_COMMAND == msg && LOWORD(wParam) == 1026) { + LOGFONT lf = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0}}; + HDC hdc = GetDC(hwndDlg); + + SendMessage(hwndDlg, WM_CHOOSEFONT_GETLOGFONT, 0, (LPARAM) &lf); + if (phd && phd->cmdObj) { + ApplyLogfont(phd->interp, phd->cmdObj, hdc, &lf); + } + if (phd && phd->parent) { + TkSendVirtualEvent(phd->parent, "TkFontchooserFontChanged", NULL); + } + return 1; + } + return 0; /* pass on for default processing */ +} + +/* + * Helper for the FontchooserConfigure command to return the current value of + * any of the options (which may be NULL in the structure) + */ + +enum FontchooserOption { + FontchooserParent, FontchooserTitle, FontchooserFont, FontchooserCmd, + FontchooserVisible +}; + +static Tcl_Obj * +FontchooserCget( + HookData *hdPtr, + int optionIndex) +{ + Tcl_Obj *resObj = NULL; + + switch(optionIndex) { + case FontchooserParent: + if (hdPtr->parentObj) { + resObj = hdPtr->parentObj; + } else { + resObj = Tcl_NewStringObj(".", 1); + } + break; + case FontchooserTitle: + if (hdPtr->titleObj) { + resObj = hdPtr->titleObj; + } else { + resObj = Tcl_NewStringObj("", 0); + } + break; + case FontchooserFont: + if (hdPtr->fontObj) { + resObj = hdPtr->fontObj; + } else { + resObj = Tcl_NewStringObj("", 0); + } + break; + case FontchooserCmd: + if (hdPtr->cmdObj) { + resObj = hdPtr->cmdObj; + } else { + resObj = Tcl_NewStringObj("", 0); + } + break; + case FontchooserVisible: + resObj = Tcl_NewBooleanObj(hdPtr->hwnd && IsWindow(hdPtr->hwnd)); + break; + default: + resObj = Tcl_NewStringObj("", 0); + } + return resObj; +} + +/* + * ---------------------------------------------------------------------- + * + * FontchooserConfigureCmd -- + * + * Implementation of the 'tk fontchooser configure' ensemble command. See + * the user documentation for what it does. + * + * Results: + * See the user documentation. + * + * Side effects: + * Per-interp data structure may be modified + * + * ---------------------------------------------------------------------- + */ + +static int +FontchooserConfigureCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + Tk_Window tkwin = clientData; + HookData *hdPtr = NULL; + int i, r = TCL_OK; + static const char *const optionStrings[] = { + "-parent", "-title", "-font", "-command", "-visible", NULL + }; + + hdPtr = Tcl_GetAssocData(interp, "::tk::fontchooser", NULL); + + /* + * With no arguments we return all the options in a dict. + */ + + if (objc == 1) { + Tcl_Obj *keyObj, *valueObj; + Tcl_Obj *dictObj = Tcl_NewDictObj(); + + for (i = 0; r == TCL_OK && optionStrings[i] != NULL; ++i) { + keyObj = Tcl_NewStringObj(optionStrings[i], -1); + valueObj = FontchooserCget(hdPtr, i); + r = Tcl_DictObjPut(interp, dictObj, keyObj, valueObj); + } + if (r == TCL_OK) { + Tcl_SetObjResult(interp, dictObj); + } + return r; + } + + for (i = 1; i < objc; i += 2) { + int optionIndex; + + if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings, + sizeof(char *), "option", 0, &optionIndex) != TCL_OK) { + return TCL_ERROR; + } + if (objc == 2) { + /* + * If one option and no arg - return the current value. + */ + + Tcl_SetObjResult(interp, FontchooserCget(hdPtr, optionIndex)); + return TCL_OK; + } + if (i + 1 == objc) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "value for \"%s\" missing", Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TK", "FONTDIALOG", "VALUE", NULL); + return TCL_ERROR; + } + switch (optionIndex) { + case FontchooserVisible: { + static const char *msg = "cannot change read-only option " + "\"-visible\": use the show or hide command"; + + Tcl_SetObjResult(interp, Tcl_NewStringObj(msg, -1)); + Tcl_SetErrorCode(interp, "TK", "FONTDIALOG", "READONLY", NULL); + return TCL_ERROR; + } + case FontchooserParent: { + Tk_Window parent = Tk_NameToWindow(interp, + Tcl_GetString(objv[i+1]), tkwin); + + if (parent == None) { + return TCL_ERROR; + } + if (hdPtr->parentObj) { + Tcl_DecrRefCount(hdPtr->parentObj); + } + hdPtr->parentObj = objv[i+1]; + if (Tcl_IsShared(hdPtr->parentObj)) { + hdPtr->parentObj = Tcl_DuplicateObj(hdPtr->parentObj); + } + Tcl_IncrRefCount(hdPtr->parentObj); + break; + } + case FontchooserTitle: + if (hdPtr->titleObj) { + Tcl_DecrRefCount(hdPtr->titleObj); + } + hdPtr->titleObj = objv[i+1]; + if (Tcl_IsShared(hdPtr->titleObj)) { + hdPtr->titleObj = Tcl_DuplicateObj(hdPtr->titleObj); + } + Tcl_IncrRefCount(hdPtr->titleObj); + break; + case FontchooserFont: + if (hdPtr->fontObj) { + Tcl_DecrRefCount(hdPtr->fontObj); + } + (void)Tcl_GetString(objv[i+1]); + if (objv[i+1]->length) { + hdPtr->fontObj = objv[i+1]; + if (Tcl_IsShared(hdPtr->fontObj)) { + hdPtr->fontObj = Tcl_DuplicateObj(hdPtr->fontObj); + } + Tcl_IncrRefCount(hdPtr->fontObj); + } else { + hdPtr->fontObj = NULL; + } + break; + case FontchooserCmd: + if (hdPtr->cmdObj) { + Tcl_DecrRefCount(hdPtr->cmdObj); + } + (void)Tcl_GetString(objv[i+1]); + if (objv[i+1]->length) { + hdPtr->cmdObj = objv[i+1]; + if (Tcl_IsShared(hdPtr->cmdObj)) { + hdPtr->cmdObj = Tcl_DuplicateObj(hdPtr->cmdObj); + } + Tcl_IncrRefCount(hdPtr->cmdObj); + } else { + hdPtr->cmdObj = NULL; + } + break; + } + } + return TCL_OK; +} + +/* + * ---------------------------------------------------------------------- + * + * FontchooserShowCmd -- + * + * Implements the 'tk fontchooser show' ensemble command. The per-interp + * configuration data for the dialog is held in an interp associated + * structure. + * + * Calls the Win32 FontChooser API which provides a modal dialog. See + * HookProc where we make a few changes to the dialog and set some + * additional state. + * + * ---------------------------------------------------------------------- + */ + +static int +FontchooserShowCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + Tcl_DString ds; + Tk_Window tkwin = clientData, parent; + CHOOSEFONT cf; + LOGFONT lf; + HDC hdc; + HookData *hdPtr; + int r = TCL_OK, oldMode = 0; + + hdPtr = Tcl_GetAssocData(interp, "::tk::fontchooser", NULL); + + parent = tkwin; + if (hdPtr->parentObj) { + parent = Tk_NameToWindow(interp, Tcl_GetString(hdPtr->parentObj), + tkwin); + if (parent == None) { + return TCL_ERROR; + } + } + + Tk_MakeWindowExist(parent); + + ZeroMemory(&cf, sizeof(CHOOSEFONT)); + ZeroMemory(&lf, sizeof(LOGFONT)); + lf.lfCharSet = DEFAULT_CHARSET; + cf.lStructSize = sizeof(CHOOSEFONT); + cf.hwndOwner = Tk_GetHWND(Tk_WindowId(parent)); + cf.lpLogFont = &lf; + cf.nFontType = SCREEN_FONTTYPE; + cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_ENABLEHOOK; + cf.rgbColors = RGB(0,0,0); + cf.lpfnHook = HookProc; + cf.lCustData = (INT_PTR) hdPtr; + hdPtr->interp = interp; + hdPtr->parent = parent; + hdc = GetDC(cf.hwndOwner); + + if (hdPtr->fontObj != NULL) { + TkFont *fontPtr; + Tk_Font f = Tk_AllocFontFromObj(interp, tkwin, hdPtr->fontObj); + + if (f == NULL) { + return TCL_ERROR; + } + fontPtr = (TkFont *) f; + cf.Flags |= CF_INITTOLOGFONTSTRUCT; + Tcl_WinUtfToTChar(fontPtr->fa.family, -1, &ds); + _tcsncpy(lf.lfFaceName, (TCHAR *)Tcl_DStringValue(&ds), + LF_FACESIZE-1); + Tcl_DStringFree(&ds); + lf.lfFaceName[LF_FACESIZE-1] = 0; + lf.lfHeight = -MulDiv((int)(TkFontGetPoints(tkwin, fontPtr->fa.size) + 0.5), + GetDeviceCaps(hdc, LOGPIXELSY), 72); + if (fontPtr->fa.weight == TK_FW_BOLD) { + lf.lfWeight = FW_BOLD; + } + if (fontPtr->fa.slant != TK_FS_ROMAN) { + lf.lfItalic = TRUE; + } + if (fontPtr->fa.underline) { + lf.lfUnderline = TRUE; + } + if (fontPtr->fa.overstrike) { + lf.lfStrikeOut = TRUE; + } + Tk_FreeFont(f); + } + + if (TCL_OK == r && hdPtr->cmdObj != NULL) { + int len = 0; + + r = Tcl_ListObjLength(interp, hdPtr->cmdObj, &len); + if (len > 0) { + cf.Flags |= CF_APPLY; + } + } + + if (TCL_OK == r) { + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + if (ChooseFont(&cf)) { + if (hdPtr->cmdObj) { + ApplyLogfont(hdPtr->interp, hdPtr->cmdObj, hdc, &lf); + } + if (hdPtr->parent) { + TkSendVirtualEvent(hdPtr->parent, "TkFontchooserFontChanged", NULL); + } + } + Tcl_SetServiceMode(oldMode); + EnableWindow(cf.hwndOwner, 1); + } + + ReleaseDC(cf.hwndOwner, hdc); + return r; +} + +/* + * ---------------------------------------------------------------------- + * + * FontchooserHideCmd -- + * + * Implementation of the 'tk fontchooser hide' ensemble. See the user + * documentation for details. + * As the Win32 FontChooser function is always modal all we do here is + * destroy the dialog + * + * ---------------------------------------------------------------------- + */ + +static int +FontchooserHideCmd( + ClientData clientData, /* Main window */ + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + HookData *hdPtr = Tcl_GetAssocData(interp, "::tk::fontchooser", NULL); + + if (hdPtr->hwnd && IsWindow(hdPtr->hwnd)) { + EndDialog(hdPtr->hwnd, 0); + } + return TCL_OK; +} + +/* + * ---------------------------------------------------------------------- + * + * DeleteHookData -- + * + * Clean up the font chooser configuration data when the interp is + * destroyed. + * + * ---------------------------------------------------------------------- + */ + +static void +DeleteHookData(ClientData clientData, Tcl_Interp *interp) +{ + HookData *hdPtr = clientData; + + if (hdPtr->parentObj) { + Tcl_DecrRefCount(hdPtr->parentObj); + } + if (hdPtr->fontObj) { + Tcl_DecrRefCount(hdPtr->fontObj); + } + if (hdPtr->titleObj) { + Tcl_DecrRefCount(hdPtr->titleObj); + } + if (hdPtr->cmdObj) { + Tcl_DecrRefCount(hdPtr->cmdObj); + } + ckfree(hdPtr); +} + +/* + * ---------------------------------------------------------------------- + * + * TkInitFontchooser -- + * + * Associate the font chooser configuration data with the Tcl + * interpreter. There is one font chooser per interp. + * + * ---------------------------------------------------------------------- + */ + +MODULE_SCOPE const TkEnsemble tkFontchooserEnsemble[]; +const TkEnsemble tkFontchooserEnsemble[] = { + { "configure", FontchooserConfigureCmd, NULL }, + { "show", FontchooserShowCmd, NULL }, + { "hide", FontchooserHideCmd, NULL }, + { NULL, NULL, NULL } +}; + +int +TkInitFontchooser(Tcl_Interp *interp, ClientData clientData) +{ + HookData *hdPtr = ckalloc(sizeof(HookData)); + + memset(hdPtr, 0, sizeof(HookData)); + Tcl_SetAssocData(interp, "::tk::fontchooser", DeleteHookData, hdPtr); + return TCL_OK; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinDraw.c b/tk8.6/win/tkWinDraw.c new file mode 100644 index 0000000..e13a5e5 --- /dev/null +++ b/tk8.6/win/tkWinDraw.c @@ -0,0 +1,1523 @@ +/* + * tkWinDraw.c -- + * + * This file contains the Xlib emulation functions pertaining to actually + * drawing objects on a window. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * Copyright (c) 1994 Software Research Associates, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * These macros convert between X's bizarre angle units to radians. + */ + +#define XAngleToRadians(a) ((double)(a) / 64 * PI / 180); + +/* + * Translation table between X gc functions and Win32 raster op modes. + */ + +const int tkpWinRopModes[] = { + R2_BLACK, /* GXclear */ + R2_MASKPEN, /* GXand */ + R2_MASKPENNOT, /* GXandReverse */ + R2_COPYPEN, /* GXcopy */ + R2_MASKNOTPEN, /* GXandInverted */ + R2_NOT, /* GXnoop */ + R2_XORPEN, /* GXxor */ + R2_MERGEPEN, /* GXor */ + R2_NOTMERGEPEN, /* GXnor */ + R2_NOTXORPEN, /* GXequiv */ + R2_NOT, /* GXinvert */ + R2_MERGEPENNOT, /* GXorReverse */ + R2_NOTCOPYPEN, /* GXcopyInverted */ + R2_MERGENOTPEN, /* GXorInverted */ + R2_NOTMASKPEN, /* GXnand */ + R2_WHITE /* GXset */ +}; + +/* + * Translation table between X gc functions and Win32 BitBlt op modes. Some of + * the operations defined in X don't have names, so we have to construct new + * opcodes for those functions. This is arcane and probably not all that + * useful, but at least it's accurate. + */ + +#define NOTSRCAND (DWORD)0x00220326 /* dest = (NOT source) AND dest */ +#define NOTSRCINVERT (DWORD)0x00990066 /* dest = (NOT source) XOR dest */ +#define SRCORREVERSE (DWORD)0x00DD0228 /* dest = source OR (NOT dest) */ +#define SRCNAND (DWORD)0x007700E6 /* dest = NOT (source AND dest) */ + +const int tkpWinBltModes[] = { + BLACKNESS, /* GXclear */ + SRCAND, /* GXand */ + SRCERASE, /* GXandReverse */ + SRCCOPY, /* GXcopy */ + NOTSRCAND, /* GXandInverted */ + PATCOPY, /* GXnoop */ + SRCINVERT, /* GXxor */ + SRCPAINT, /* GXor */ + NOTSRCERASE, /* GXnor */ + NOTSRCINVERT, /* GXequiv */ + DSTINVERT, /* GXinvert */ + SRCORREVERSE, /* GXorReverse */ + NOTSRCCOPY, /* GXcopyInverted */ + MERGEPAINT, /* GXorInverted */ + SRCNAND, /* GXnand */ + WHITENESS /* GXset */ +}; + +/* + * The following raster op uses the source bitmap as a mask for the pattern. + * This is used to draw in a foreground color but leave the background color + * transparent. + */ + +#define MASKPAT 0x00E20746 /* dest = (src & pat) | (!src & dst) */ + +/* + * The following two raster ops are used to copy the foreground and background + * bits of a source pattern as defined by a stipple used as the pattern. + */ + +#define COPYFG 0x00CA0749 /* dest = (pat & src) | (!pat & dst) */ +#define COPYBG 0x00AC0744 /* dest = (!pat & src) | (pat & dst) */ + +/* + * Macros used later in the file. + */ +#ifndef MIN +# define MIN(a,b) ((a>b) ? b : a) +# define MAX(a,b) ((a<b) ? b : a) +#endif + +/* + * The followng typedef is used to pass Windows GDI drawing functions. + */ + +typedef BOOL (CALLBACK *WinDrawFunc)(HDC dc, const POINT *points, int npoints); + +typedef struct ThreadSpecificData { + POINT *winPoints; /* Array of points that is reused. */ + int nWinPoints; /* Current size of point array. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Forward declarations for functions defined in this file: + */ + +static POINT * ConvertPoints(XPoint *points, int npoints, int mode, + RECT *bbox); +static int DrawOrFillArc(Display *display, Drawable d, GC gc, + int x, int y, unsigned int width, + unsigned int height, int start, int extent, + int fill); +static void RenderObject(HDC dc, GC gc, XPoint* points, + int npoints, int mode, HPEN pen, WinDrawFunc func); +static HPEN SetUpGraphicsPort(GC gc); + +/* + *---------------------------------------------------------------------- + * + * TkWinGetDrawableDC -- + * + * Retrieve the DC from a drawable. + * + * Results: + * Returns the window DC for windows. Returns a new memory DC for + * pixmaps. + * + * Side effects: + * Sets up the palette for the device context, and saves the old device + * context state in the passed in TkWinDCState structure. + * + *---------------------------------------------------------------------- + */ + +HDC +TkWinGetDrawableDC( + Display *display, + Drawable d, + TkWinDCState *state) +{ + HDC dc; + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + Colormap cmap; + + if (twdPtr->type == TWD_WINDOW) { + TkWindow *winPtr = twdPtr->window.winPtr; + + dc = GetDC(twdPtr->window.handle); + if (winPtr == NULL) { + cmap = DefaultColormap(display, DefaultScreen(display)); + } else { + cmap = winPtr->atts.colormap; + } + } else if (twdPtr->type == TWD_WINDC) { + dc = twdPtr->winDC.hdc; + cmap = DefaultColormap(display, DefaultScreen(display)); + } else { + dc = CreateCompatibleDC(NULL); + SelectObject(dc, twdPtr->bitmap.handle); + cmap = twdPtr->bitmap.colormap; + } + state->palette = TkWinSelectPalette(dc, cmap); + state->bkmode = GetBkMode(dc); + return dc; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinReleaseDrawableDC -- + * + * Frees the resources associated with a drawable's DC. + * + * Results: + * None. + * + * Side effects: + * Restores the old bitmap handle to the memory DC for pixmaps. + * + *---------------------------------------------------------------------- + */ + +void +TkWinReleaseDrawableDC( + Drawable d, + HDC dc, + TkWinDCState *state) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + + SetBkMode(dc, state->bkmode); + SelectPalette(dc, state->palette, TRUE); + RealizePalette(dc); + if (twdPtr->type == TWD_WINDOW) { + ReleaseDC(TkWinGetHWND(d), dc); + } else if (twdPtr->type == TWD_BITMAP) { + DeleteDC(dc); + } +} + +/* + *---------------------------------------------------------------------- + * + * ConvertPoints -- + * + * Convert an array of X points to an array of Win32 points. + * + * Results: + * Returns the converted array of POINTs. + * + * Side effects: + * Allocates a block of memory in thread local storage that should not be + * freed. + * + *---------------------------------------------------------------------- + */ + +static POINT * +ConvertPoints( + XPoint *points, + int npoints, + int mode, /* CoordModeOrigin or CoordModePrevious. */ + RECT *bbox) /* Bounding box of points. */ +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + int i; + + /* + * To avoid paying the cost of a malloc on every drawing routine, we reuse + * the last array if it is large enough. + */ + + if (npoints > tsdPtr->nWinPoints) { + if (tsdPtr->winPoints != NULL) { + ckfree(tsdPtr->winPoints); + } + tsdPtr->winPoints = ckalloc(sizeof(POINT) * npoints); + if (tsdPtr->winPoints == NULL) { + tsdPtr->nWinPoints = -1; + return NULL; + } + tsdPtr->nWinPoints = npoints; + } + + bbox->left = bbox->right = points[0].x; + bbox->top = bbox->bottom = points[0].y; + + if (mode == CoordModeOrigin) { + for (i = 0; i < npoints; i++) { + tsdPtr->winPoints[i].x = points[i].x; + tsdPtr->winPoints[i].y = points[i].y; + bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x); + bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x); + bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y); + bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y); + } + } else { + tsdPtr->winPoints[0].x = points[0].x; + tsdPtr->winPoints[0].y = points[0].y; + for (i = 1; i < npoints; i++) { + tsdPtr->winPoints[i].x = tsdPtr->winPoints[i-1].x + points[i].x; + tsdPtr->winPoints[i].y = tsdPtr->winPoints[i-1].y + points[i].y; + bbox->left = MIN(bbox->left, tsdPtr->winPoints[i].x); + bbox->right = MAX(bbox->right, tsdPtr->winPoints[i].x); + bbox->top = MIN(bbox->top, tsdPtr->winPoints[i].y); + bbox->bottom = MAX(bbox->bottom, tsdPtr->winPoints[i].y); + } + } + return tsdPtr->winPoints; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyArea -- + * + * Copies data from one drawable to another using block transfer + * routines. + * + * Results: + * None. + * + * Side effects: + * Data is moved from a window or bitmap to a second window or bitmap. + * + *---------------------------------------------------------------------- + */ + +int +XCopyArea( + Display *display, + Drawable src, + Drawable dest, + GC gc, + int src_x, int src_y, + unsigned int width, unsigned int height, + int dest_x, int dest_y) +{ + HDC srcDC, destDC; + TkWinDCState srcState, destState; + TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask; + + srcDC = TkWinGetDrawableDC(display, src, &srcState); + + if (src != dest) { + destDC = TkWinGetDrawableDC(display, dest, &destState); + } else { + destDC = srcDC; + } + + if (clipPtr && clipPtr->type == TKP_CLIP_REGION) { + SelectClipRgn(destDC, (HRGN) clipPtr->value.region); + OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin); + } + + BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC, + src_x, src_y, (DWORD) tkpWinBltModes[gc->function]); + + SelectClipRgn(destDC, NULL); + + if (src != dest) { + TkWinReleaseDrawableDC(dest, destDC, &destState); + } + TkWinReleaseDrawableDC(src, srcDC, &srcState); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XCopyPlane -- + * + * Copies a bitmap from a source drawable to a destination drawable. The + * plane argument specifies which bit plane of the source contains the + * bitmap. Note that this implementation ignores the gc->function. + * + * Results: + * None. + * + * Side effects: + * Changes the destination drawable. + * + *---------------------------------------------------------------------- + */ + +int +XCopyPlane( + Display *display, + Drawable src, + Drawable dest, + GC gc, + int src_x, int src_y, + unsigned int width, unsigned int height, + int dest_x, int dest_y, + unsigned long plane) +{ + HDC srcDC, destDC; + TkWinDCState srcState, destState; + HBRUSH bgBrush, fgBrush, oldBrush; + TkpClipMask *clipPtr = (TkpClipMask*)gc->clip_mask; + + display->request++; + + if (plane != 1) { + Tcl_Panic("Unexpected plane specified for XCopyPlane"); + } + + srcDC = TkWinGetDrawableDC(display, src, &srcState); + + if (src != dest) { + destDC = TkWinGetDrawableDC(display, dest, &destState); + } else { + destDC = srcDC; + } + + if (clipPtr == NULL || clipPtr->type == TKP_CLIP_REGION) { + /* + * Case 1: opaque bitmaps. Windows handles the conversion from one bit + * to multiple bits by setting 0 to the foreground color, and 1 to the + * background color (seems backwards, but there you are). + */ + + if (clipPtr && clipPtr->type == TKP_CLIP_REGION) { + SelectClipRgn(destDC, (HRGN) clipPtr->value.region); + OffsetClipRgn(destDC, gc->clip_x_origin, gc->clip_y_origin); + } + + SetBkMode(destDC, OPAQUE); + SetBkColor(destDC, gc->foreground); + SetTextColor(destDC, gc->background); + BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC, + src_x, src_y, SRCCOPY); + + SelectClipRgn(destDC, NULL); + } else if (clipPtr->type == TKP_CLIP_PIXMAP) { + if (clipPtr->value.pixmap == src) { + + /* + * Case 2: transparent bitmaps are handled by setting the + * destination to the foreground color whenever the source pixel + * is set. + */ + + fgBrush = CreateSolidBrush(gc->foreground); + oldBrush = SelectObject(destDC, fgBrush); + SetBkColor(destDC, RGB(255,255,255)); + SetTextColor(destDC, RGB(0,0,0)); + BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, srcDC, + src_x, src_y, MASKPAT); + SelectObject(destDC, oldBrush); + DeleteObject(fgBrush); + } else { + + /* + * Case 3: two arbitrary bitmaps. Copy the source rectangle into a + * color pixmap. Use the result as a brush when copying the clip + * mask into the destination. + */ + + HDC memDC, maskDC; + HBITMAP bitmap; + TkWinDCState maskState; + + fgBrush = CreateSolidBrush(gc->foreground); + bgBrush = CreateSolidBrush(gc->background); + maskDC = TkWinGetDrawableDC(display, clipPtr->value.pixmap, + &maskState); + memDC = CreateCompatibleDC(destDC); + bitmap = CreateBitmap((int) width, (int) height, 1, 1, NULL); + SelectObject(memDC, bitmap); + + /* + * Set foreground bits. We create a new bitmap containing (source + * AND mask), then use it to set the foreground color into the + * destination. + */ + + BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y, + SRCCOPY); + BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC, + dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin, + SRCAND); + oldBrush = SelectObject(destDC, fgBrush); + BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC, + 0, 0, MASKPAT); + + /* + * Set background bits. Same as foreground, except we use ((NOT + * source) AND mask) and the background brush. + */ + + BitBlt(memDC, 0, 0, (int) width, (int) height, srcDC, src_x, src_y, + NOTSRCCOPY); + BitBlt(memDC, 0, 0, (int) width, (int) height, maskDC, + dest_x - gc->clip_x_origin, dest_y - gc->clip_y_origin, + SRCAND); + SelectObject(destDC, bgBrush); + BitBlt(destDC, dest_x, dest_y, (int) width, (int) height, memDC, + 0, 0, MASKPAT); + + TkWinReleaseDrawableDC(clipPtr->value.pixmap, maskDC, &maskState); + SelectObject(destDC, oldBrush); + DeleteDC(memDC); + DeleteObject(bitmap); + DeleteObject(fgBrush); + DeleteObject(bgBrush); + } + } + if (src != dest) { + TkWinReleaseDrawableDC(dest, destDC, &destState); + } + TkWinReleaseDrawableDC(src, srcDC, &srcState); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkPutImage, XPutImage -- + * + * Copies a subimage from an in-memory image to a rectangle of of the + * specified drawable. + * + * Results: + * None. + * + * Side effects: + * Draws the image on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +TkPutImage( + unsigned long *colors, /* Array of pixel values used by this image. + * May be NULL. */ + int ncolors, /* Number of colors used, or 0. */ + Display *display, + Drawable d, /* Destination drawable. */ + GC gc, + XImage *image, /* Source image. */ + int src_x, int src_y, /* Offset of subimage. */ + int dest_x, int dest_y, /* Position of subimage origin in drawable. */ + unsigned int width, unsigned int height) + /* Dimensions of subimage. */ +{ + HDC dc, dcMem; + TkWinDCState state; + BITMAPINFO *infoPtr; + HBITMAP bitmap; + char *data; + + display->request++; + + dc = TkWinGetDrawableDC(display, d, &state); + SetROP2(dc, tkpWinRopModes[gc->function]); + dcMem = CreateCompatibleDC(dc); + + if (image->bits_per_pixel == 1) { + /* + * If the image isn't in the right format, we have to copy it into a + * new buffer in MSBFirst and word-aligned format. + */ + + if ((image->bitmap_bit_order != MSBFirst) + || (image->bitmap_pad != sizeof(WORD))) { + data = TkAlignImageData(image, sizeof(WORD), MSBFirst); + bitmap = CreateBitmap(image->width, image->height, 1, 1, data); + ckfree(data); + } else { + bitmap = CreateBitmap(image->width, image->height, 1, 1, + image->data); + } + SetTextColor(dc, gc->foreground); + SetBkColor(dc, gc->background); + } else { + int i, usePalette; + + /* + * Do not use a palette for TrueColor images. + */ + + usePalette = (image->bits_per_pixel < 16); + + if (usePalette) { + infoPtr = ckalloc(sizeof(BITMAPINFOHEADER) + + sizeof(RGBQUAD)*ncolors); + } else { + infoPtr = ckalloc(sizeof(BITMAPINFOHEADER)); + } + + infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + infoPtr->bmiHeader.biWidth = image->width; + infoPtr->bmiHeader.biHeight = -image->height; /* Top-down order */ + infoPtr->bmiHeader.biPlanes = 1; + infoPtr->bmiHeader.biBitCount = image->bits_per_pixel; + infoPtr->bmiHeader.biCompression = BI_RGB; + infoPtr->bmiHeader.biSizeImage = 0; + infoPtr->bmiHeader.biXPelsPerMeter = 0; + infoPtr->bmiHeader.biYPelsPerMeter = 0; + infoPtr->bmiHeader.biClrImportant = 0; + + if (usePalette) { + infoPtr->bmiHeader.biClrUsed = ncolors; + for (i = 0; i < ncolors; i++) { + infoPtr->bmiColors[i].rgbBlue = GetBValue(colors[i]); + infoPtr->bmiColors[i].rgbGreen = GetGValue(colors[i]); + infoPtr->bmiColors[i].rgbRed = GetRValue(colors[i]); + infoPtr->bmiColors[i].rgbReserved = 0; + } + } else { + infoPtr->bmiHeader.biClrUsed = 0; + } + bitmap = CreateDIBitmap(dc, &infoPtr->bmiHeader, CBM_INIT, + image->data, infoPtr, DIB_RGB_COLORS); + ckfree(infoPtr); + } + if (!bitmap) { + Tcl_Panic("Fail to allocate bitmap"); + DeleteDC(dcMem); + TkWinReleaseDrawableDC(d, dc, &state); + return BadValue; + } + bitmap = SelectObject(dcMem, bitmap); + BitBlt(dc, dest_x, dest_y, (int) width, (int) height, dcMem, src_x, src_y, + SRCCOPY); + DeleteObject(SelectObject(dcMem, bitmap)); + DeleteDC(dcMem); + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +int +XPutImage( + Display *display, + Drawable d, /* Destination drawable. */ + GC gc, + XImage *image, /* Source image. */ + int src_x, int src_y, /* Offset of subimage. */ + int dest_x, int dest_y, /* Position of subimage origin in drawable. */ + unsigned int width, unsigned int height) + /* Dimensions of subimage. */ +{ + return TkPutImage(NULL, 0, display, d, gc, image, + src_x, src_y, dest_x, dest_y, width, height); +} + +/* + *---------------------------------------------------------------------- + * + * XFillRectangles -- + * + * Fill multiple rectangular areas in the given drawable. + * + * Results: + * None. + * + * Side effects: + * Draws onto the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XFillRectangles( + Display *display, + Drawable d, + GC gc, + XRectangle *rectangles, + int nrectangles) +{ + HDC dc; + RECT rect; + TkWinDCState state; + HBRUSH brush, oldBrush; + + if (d == None) { + return BadDrawable; + } + + dc = TkWinGetDrawableDC(display, d, &state); + SetROP2(dc, tkpWinRopModes[gc->function]); + brush = CreateSolidBrush(gc->foreground); + + if ((gc->fill_style == FillStippled + || gc->fill_style == FillOpaqueStippled) + && gc->stipple != None) { + TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple; + HBRUSH stipple; + HBITMAP oldBitmap, bitmap; + HDC dcMem; + HBRUSH bgBrush = CreateSolidBrush(gc->background); + + if (twdPtr->type != TWD_BITMAP) { + Tcl_Panic("unexpected drawable type in stipple"); + } + + /* + * Select stipple pattern into destination dc. + */ + + stipple = CreatePatternBrush(twdPtr->bitmap.handle); + SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL); + oldBrush = SelectObject(dc, stipple); + dcMem = CreateCompatibleDC(dc); + + /* + * For each rectangle, create a drawing surface which is the size of + * the rectangle and fill it with the background color. Then merge the + * result with the stipple pattern. + */ + + while (nrectangles-- > 0) { + bitmap = CreateCompatibleBitmap(dc, rectangles[0].width, + rectangles[0].height); + oldBitmap = SelectObject(dcMem, bitmap); + rect.left = 0; + rect.top = 0; + rect.right = rectangles[0].width; + rect.bottom = rectangles[0].height; + FillRect(dcMem, &rect, brush); + BitBlt(dc, rectangles[0].x, rectangles[0].y, rectangles[0].width, + rectangles[0].height, dcMem, 0, 0, COPYFG); + if (gc->fill_style == FillOpaqueStippled) { + FillRect(dcMem, &rect, bgBrush); + BitBlt(dc, rectangles[0].x, rectangles[0].y, + rectangles[0].width, rectangles[0].height, dcMem, + 0, 0, COPYBG); + } + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + ++rectangles; + } + + DeleteDC(dcMem); + SelectObject(dc, oldBrush); + DeleteObject(stipple); + DeleteObject(bgBrush); + } else { + if (gc->function == GXcopy) { + while (nrectangles-- > 0) { + rect.left = rectangles[0].x; + rect.right = rect.left + rectangles[0].width; + rect.top = rectangles[0].y; + rect.bottom = rect.top + rectangles[0].height; + FillRect(dc, &rect, brush); + ++rectangles; + } + } else { + HPEN newPen = CreatePen(PS_NULL, 0, gc->foreground); + HPEN oldPen = SelectObject(dc, newPen); + oldBrush = SelectObject(dc, brush); + + while (nrectangles-- > 0) { + Rectangle(dc, rectangles[0].x, rectangles[0].y, + rectangles[0].x + rectangles[0].width + 1, + rectangles[0].y + rectangles[0].height + 1); + ++rectangles; + } + + SelectObject(dc, oldBrush); + SelectObject(dc, oldPen); + DeleteObject(newPen); + } + } + DeleteObject(brush); + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * MakeAndStrokePath -- + * + * This function draws a shape using a list of points, a stipple pattern, + * and the specified drawing function. It does it through creation of a + * so-called 'path' (see GDI documentation on MSDN). + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ +static void +MakeAndStrokePath( + HDC dc, + POINT *winPoints, + int npoints, + WinDrawFunc func) /* Name of the Windows GDI drawing function: + this is either Polyline or Polygon. */ +{ + BeginPath(dc); + func(dc, winPoints, npoints); + /* + * In the case of closed polylines, the first and last points + * are the same. We want miter or bevel join be rendered also + * at this point, this needs telling the Windows GDI that the + * path is closed. + */ + if (func == Polyline) { + if ((winPoints[0].x == winPoints[npoints-1].x) && + (winPoints[0].y == winPoints[npoints-1].y)) { + CloseFigure(dc); + } + EndPath(dc); + StrokePath(dc); + } else { + EndPath(dc); + StrokeAndFillPath(dc); + } +} + +/* + *---------------------------------------------------------------------- + * + * RenderObject -- + * + * This function draws a shape using a list of points, a stipple pattern, + * and the specified drawing function. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +RenderObject( + HDC dc, + GC gc, + XPoint *points, + int npoints, + int mode, + HPEN pen, + WinDrawFunc func) +{ + RECT rect = {0,0,0,0}; + HPEN oldPen; + HBRUSH oldBrush; + POINT *winPoints = ConvertPoints(points, npoints, mode, &rect); + + if ((gc->fill_style == FillStippled + || gc->fill_style == FillOpaqueStippled) + && gc->stipple != None) { + + TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple; + HDC dcMem; + LONG width, height; + HBITMAP oldBitmap; + int i; + HBRUSH oldMemBrush; + + if (twdPtr->type != TWD_BITMAP) { + Tcl_Panic("unexpected drawable type in stipple"); + } + + /* + * Grow the bounding box enough to account for line width. + */ + + rect.left -= gc->line_width; + rect.top -= gc->line_width; + rect.right += gc->line_width; + rect.bottom += gc->line_width; + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + /* + * Select stipple pattern into destination dc. + */ + + SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL); + oldBrush = SelectObject(dc, CreatePatternBrush(twdPtr->bitmap.handle)); + + /* + * Create temporary drawing surface containing a copy of the + * destination equal in size to the bounding box of the object. + */ + + dcMem = CreateCompatibleDC(dc); + oldBitmap = SelectObject(dcMem, CreateCompatibleBitmap(dc, width, + height)); + oldPen = SelectObject(dcMem, pen); + BitBlt(dcMem, 0, 0, width, height, dc, rect.left, rect.top, SRCCOPY); + + /* + * Translate the object for rendering in the temporary drawing + * surface. + */ + + for (i = 0; i < npoints; i++) { + winPoints[i].x -= rect.left; + winPoints[i].y -= rect.top; + } + + /* + * Draw the object in the foreground color and copy it to the + * destination wherever the pattern is set. + */ + + SetPolyFillMode(dcMem, (gc->fill_rule == EvenOddRule) ? ALTERNATE + : WINDING); + oldMemBrush = SelectObject(dcMem, CreateSolidBrush(gc->foreground)); + MakeAndStrokePath(dcMem, winPoints, npoints, func); + BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, COPYFG); + + /* + * If we are rendering an opaque stipple, then draw the polygon in the + * background color and copy it to the destination wherever the + * pattern is clear. + */ + + if (gc->fill_style == FillOpaqueStippled) { + DeleteObject(SelectObject(dcMem, + CreateSolidBrush(gc->background))); + MakeAndStrokePath(dcMem, winPoints, npoints, func); + BitBlt(dc, rect.left, rect.top, width, height, dcMem, 0, 0, + COPYBG); + } + + SelectObject(dcMem, oldPen); + DeleteObject(SelectObject(dcMem, oldMemBrush)); + DeleteObject(SelectObject(dcMem, oldBitmap)); + DeleteDC(dcMem); + } else { + oldPen = SelectObject(dc, pen); + oldBrush = SelectObject(dc, CreateSolidBrush(gc->foreground)); + SetROP2(dc, tkpWinRopModes[gc->function]); + + SetPolyFillMode(dc, (gc->fill_rule == EvenOddRule) ? ALTERNATE + : WINDING); + MakeAndStrokePath(dc, winPoints, npoints, func); + SelectObject(dc, oldPen); + } + DeleteObject(SelectObject(dc, oldBrush)); +} + +/* + *---------------------------------------------------------------------- + * + * XDrawLines -- + * + * Draw connected lines. + * + * Results: + * None. + * + * Side effects: + * Renders a series of connected lines. + * + *---------------------------------------------------------------------- + */ + +int +XDrawLines( + Display *display, + Drawable d, + GC gc, + XPoint *points, + int npoints, + int mode) +{ + HPEN pen; + TkWinDCState state; + HDC dc; + + if (d == None) { + return BadDrawable; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + pen = SetUpGraphicsPort(gc); + SetBkMode(dc, TRANSPARENT); + RenderObject(dc, gc, points, npoints, mode, pen, Polyline); + DeleteObject(pen); + + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XFillPolygon -- + * + * Draws a filled polygon. + * + * Results: + * None. + * + * Side effects: + * Draws a filled polygon on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XFillPolygon( + Display *display, + Drawable d, + GC gc, + XPoint *points, + int npoints, + int shape, + int mode) +{ + HPEN pen; + TkWinDCState state; + HDC dc; + + if (d == None) { + return BadDrawable; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + pen = GetStockObject(NULL_PEN); + RenderObject(dc, gc, points, npoints, mode, pen, Polygon); + + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XDrawRectangle, XDrawRectangles -- + * + * Draws a rectangle. + * + * Results: + * None. + * + * Side effects: + * Draws a rectangle on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XDrawRectangle( + Display *display, + Drawable d, + GC gc, + int x, int y, + unsigned int width, unsigned int height) +{ + HPEN pen, oldPen; + TkWinDCState state; + HBRUSH oldBrush; + HDC dc; + + if (d == None) { + return BadDrawable; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + pen = SetUpGraphicsPort(gc); + SetBkMode(dc, TRANSPARENT); + oldPen = SelectObject(dc, pen); + oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); + SetROP2(dc, tkpWinRopModes[gc->function]); + + Rectangle(dc, x, y, (int) x+width+1, (int) y+height+1); + + DeleteObject(SelectObject(dc, oldPen)); + SelectObject(dc, oldBrush); + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +int +XDrawRectangles( + Display *display, + Drawable d, + GC gc, + XRectangle rects[], + int nrects) +{ + int ret = Success; + + while (nrects-- > 0) { + ret = XDrawRectangle(display, d, gc, rects[0].x, rects[0].y, + rects[0].width, rects[0].height); + if (ret != Success) { + break; + } + ++rects; + } + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * XDrawArc, XDrawArcs -- + * + * Draw an arc. + * + * Results: + * None. + * + * Side effects: + * Draws an arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XDrawArc( + Display *display, + Drawable d, + GC gc, + int x, int y, + unsigned int width, unsigned int height, + int start, int extent) +{ + display->request++; + + return DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 0); +} + +int +XDrawArcs( + Display *display, + Drawable d, + GC gc, + XArc *arcs, + int narcs) +{ + int ret = Success; + + display->request++; + + while (narcs-- > 0) { + ret = DrawOrFillArc(display, d, gc, arcs[0].x, arcs[0].y, + arcs[0].width, arcs[0].height, + arcs[0].angle1, arcs[0].angle2, 0); + if (ret != Success) { + break; + } + ++arcs; + } + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * XFillArc, XFillArcs -- + * + * Draw a filled arc. + * + * Results: + * None. + * + * Side effects: + * Draws a filled arc on the specified drawable. + * + *---------------------------------------------------------------------- + */ + +int +XFillArc( + Display *display, + Drawable d, + GC gc, + int x, int y, + unsigned int width, unsigned int height, + int start, int extent) +{ + display->request++; + + return DrawOrFillArc(display, d, gc, x, y, width, height, start, extent, 1); +} + +int +XFillArcs( + Display *display, + Drawable d, + GC gc, + XArc *arcs, + int narcs) +{ + int ret = Success; + + display->request++; + + while (narcs-- > 0) { + ret = DrawOrFillArc(display, d, gc, arcs[0].x, arcs[0].y, + arcs[0].width, arcs[0].height, + arcs[0].angle1, arcs[0].angle2, 1); + if (ret != Success) { + break; + } + ++arcs; + } + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * DrawOrFillArc -- + * + * This function handles the rendering of drawn or filled arcs and + * chords. + * + * Results: + * None. + * + * Side effects: + * Renders the requested arc. + * + *---------------------------------------------------------------------- + */ + +static int +DrawOrFillArc( + Display *display, + Drawable d, + GC gc, + int x, int y, /* left top */ + unsigned int width, unsigned int height, + int start, /* start: three-o'clock (deg*64) */ + int extent, /* extent: relative (deg*64) */ + int fill) /* ==0 draw, !=0 fill */ +{ + HDC dc; + HBRUSH brush, oldBrush; + HPEN pen, oldPen; + TkWinDCState state; + int clockwise = (extent < 0); /* non-zero if clockwise */ + int xstart, ystart, xend, yend; + double radian_start, radian_end, xr, yr; + + if (d == None) { + return BadDrawable; + } + + dc = TkWinGetDrawableDC(display, d, &state); + + SetROP2(dc, tkpWinRopModes[gc->function]); + + /* + * Compute the absolute starting and ending angles in normalized radians. + * Swap the start and end if drawing clockwise. + */ + + start = start % (64*360); + if (start < 0) { + start += (64*360); + } + extent = (start+extent) % (64*360); + if (extent < 0) { + extent += (64*360); + } + if (clockwise) { + int tmp = start; + start = extent; + extent = tmp; + } + radian_start = XAngleToRadians(start); + radian_end = XAngleToRadians(extent); + + /* + * Now compute points on the radial lines that define the starting and + * ending angles. Be sure to take into account that the y-coordinate + * system is inverted. + */ + + xr = x + width / 2.0; + yr = y + height / 2.0; + xstart = (int)((xr + cos(radian_start)*width/2.0) + 0.5); + ystart = (int)((yr + sin(-radian_start)*height/2.0) + 0.5); + xend = (int)((xr + cos(radian_end)*width/2.0) + 0.5); + yend = (int)((yr + sin(-radian_end)*height/2.0) + 0.5); + + /* + * Now draw a filled or open figure. Note that we have to increase the + * size of the bounding box by one to account for the difference in pixel + * definitions between X and Windows. + */ + + pen = SetUpGraphicsPort(gc); + oldPen = SelectObject(dc, pen); + if (!fill) { + /* + * Note that this call will leave a gap of one pixel at the end of the + * arc for thin arcs. We can't use ArcTo because it's only supported + * under Windows NT. + */ + + SetBkMode(dc, TRANSPARENT); + Arc(dc, x, y, (int) (x+width+1), (int) (y+height+1), xstart, ystart, + xend, yend); + } else { + brush = CreateSolidBrush(gc->foreground); + oldBrush = SelectObject(dc, brush); + if (gc->arc_mode == ArcChord) { + Chord(dc, x, y, (int) (x+width+1), (int) (y+height+1), + xstart, ystart, xend, yend); + } else if (gc->arc_mode == ArcPieSlice) { + Pie(dc, x, y, (int) (x+width+1), (int) (y+height+1), + xstart, ystart, xend, yend); + } + DeleteObject(SelectObject(dc, oldBrush)); + } + DeleteObject(SelectObject(dc, oldPen)); + TkWinReleaseDrawableDC(d, dc, &state); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * SetUpGraphicsPort -- + * + * Set up the graphics port from the given GC. + * + * Results: + * None. + * + * Side effects: + * The current port is adjusted. + * + *---------------------------------------------------------------------- + */ + +static HPEN +SetUpGraphicsPort( + GC gc) +{ + DWORD style; + + if (gc->line_style == LineOnOffDash) { + unsigned char *p = (unsigned char *) &(gc->dashes); + /* pointer to the dash-list */ + + /* + * Below is a simple translation of serveral dash patterns to valid + * windows pen types. Far from complete, but I don't know how to do it + * better. Any ideas: <mailto:j.nijtmans@chello.nl> + */ + + if (p[1] && p[2]) { + if (!p[3] || p[4]) { + style = PS_DASHDOTDOT; /* -.. */ + } else { + style = PS_DASHDOT; /* -. */ + } + } else { + if (p[0] > (4 * gc->line_width)) { + style = PS_DASH; /* - */ + } else { + style = PS_DOT; /* . */ + } + } + } else { + style = PS_SOLID; + } + if (gc->line_width < 2) { + return CreatePen((int) style, gc->line_width, gc->foreground); + } else { + LOGBRUSH lb; + + lb.lbStyle = BS_SOLID; + lb.lbColor = gc->foreground; + lb.lbHatch = 0; + + style |= PS_GEOMETRIC; + switch (gc->cap_style) { + case CapNotLast: + case CapButt: + style |= PS_ENDCAP_FLAT; + break; + case CapRound: + style |= PS_ENDCAP_ROUND; + break; + default: + style |= PS_ENDCAP_SQUARE; + break; + } + switch (gc->join_style) { + case JoinMiter: + style |= PS_JOIN_MITER; + break; + case JoinRound: + style |= PS_JOIN_ROUND; + break; + default: + style |= PS_JOIN_BEVEL; + break; + } + return ExtCreatePen(style, (DWORD) gc->line_width, &lb, 0, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkScrollWindow -- + * + * Scroll a rectangle of the specified window and accumulate a damage + * region. + * + * Results: + * Returns 0 if the scroll genereated no additional damage. Otherwise, + * sets the region that needs to be repainted after scrolling and returns + * 1. + * + * Side effects: + * Scrolls the bits in the window. + * + *---------------------------------------------------------------------- + */ + +int +TkScrollWindow( + Tk_Window tkwin, /* The window to be scrolled. */ + GC gc, /* GC for window to be scrolled. */ + int x, int y, int width, int height, + /* Position rectangle to be scrolled. */ + int dx, int dy, /* Distance rectangle should be moved. */ + TkRegion damageRgn) /* Region to accumulate damage in. */ +{ + HWND hwnd = TkWinGetHWND(Tk_WindowId(tkwin)); + RECT scrollRect; + + scrollRect.left = x; + scrollRect.top = y; + scrollRect.right = x + width; + scrollRect.bottom = y + height; + return (ScrollWindowEx(hwnd, dx, dy, &scrollRect, NULL, (HRGN) damageRgn, + NULL, 0) == NULLREGION) ? 0 : 1; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinFillRect -- + * + * This routine fills a rectangle with the foreground color from the + * specified GC ignoring all other GC values. This is the fastest way to + * fill a drawable with a solid color. + * + * Results: + * None. + * + * Side effects: + * Modifies the contents of the DC drawing surface. + * + *---------------------------------------------------------------------- + */ + +void +TkWinFillRect( + HDC dc, + int x, int y, int width, int height, + int pixel) +{ + RECT rect; + COLORREF oldColor; + + rect.left = x; + rect.top = y; + rect.right = x + width; + rect.bottom = y + height; + oldColor = SetBkColor(dc, (COLORREF)pixel); + SetBkMode(dc, OPAQUE); + ExtTextOut(dc, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + SetBkColor(dc, oldColor); +} + +/* + *---------------------------------------------------------------------- + * + * TkpDrawHighlightBorder -- + * + * This function draws a rectangular ring around the outside of a widget + * to indicate that it has received the input focus. + * + * On Windows, we just draw the simple inset ring. On other sytems, e.g. + * the Mac, the focus ring is a little more complicated, so we need this + * abstraction. + * + * Results: + * None. + * + * Side effects: + * A rectangle "width" pixels wide is drawn in "drawable", corresponding + * to the outer area of "tkwin". + * + *---------------------------------------------------------------------- + */ + +void +TkpDrawHighlightBorder( + Tk_Window tkwin, + GC fgGC, + GC bgGC, + int highlightWidth, + Drawable drawable) +{ + TkDrawInsetFocusHighlight(tkwin, fgGC, highlightWidth, drawable, 0); +} + +/* + *---------------------------------------------------------------------- + * + * TkpDrawFrame -- + * + * This function draws the rectangular frame area. + * + * Results: + * None. + * + * Side effects: + * Draws inside the tkwin area. + * + *---------------------------------------------------------------------- + */ + +void +TkpDrawFrame( + Tk_Window tkwin, + Tk_3DBorder border, + int highlightWidth, + int borderWidth, + int relief) +{ + Tk_Fill3DRectangle(tkwin, Tk_WindowId(tkwin), border, highlightWidth, + highlightWidth, Tk_Width(tkwin) - 2 * highlightWidth, + Tk_Height(tkwin) - 2 * highlightWidth, borderWidth, relief); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinEmbed.c b/tk8.6/win/tkWinEmbed.c new file mode 100644 index 0000000..8bfd295 --- /dev/null +++ b/tk8.6/win/tkWinEmbed.c @@ -0,0 +1,1118 @@ +/* + * tkWinEmbed.c -- + * + * This file contains platform specific procedures for Windows platforms + * to provide basic operations needed for application embedding (where + * one application can use as its main window an internal window from + * another application). + * + * Copyright (c) 1996-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * One of the following structures exists for each container in this + * application. It keeps track of the container window and its associated + * embedded window. + */ + +typedef struct Container { + HWND parentHWnd; /* Windows HWND to the parent window */ + TkWindow *parentPtr; /* Tk's information about the container or + * NULL if the container isn't in this + * process. */ + HWND embeddedHWnd; /* Windows HWND to the embedded window. */ + TkWindow *embeddedPtr; /* Tk's information about the embedded window, + * or NULL if the embedded application isn't + * in this process. */ + HWND embeddedMenuHWnd; /* Tk's embedded menu window handler. */ + struct Container *nextPtr; /* Next in list of all containers in this + * process. */ +} Container; + +typedef struct ThreadSpecificData { + Container *firstContainerPtr; + /* First in list of all containers managed by + * this process. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +static void ContainerEventProc(ClientData clientData, + XEvent *eventPtr); +static void EmbedGeometryRequest(Container *containerPtr, + int width, int height); +static void EmbedWindowDeleted(TkWindow *winPtr); +static void Tk_MapEmbeddedWindow(TkWindow* winPtr); +HWND Tk_GetEmbeddedHWnd(TkWindow* winPtr); + +/* + *---------------------------------------------------------------------- + * + * TkWinCleanupContainerList -- + * + * Finalizes the list of containers. + * + * Results: + * None. + * + * Side effects: + * Releases memory occupied by containers of embedded windows. + * + *---------------------------------------------------------------------- + */ + +void +TkWinCleanupContainerList(void) +{ + Container *nextPtr; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + for (; tsdPtr->firstContainerPtr != NULL; + tsdPtr->firstContainerPtr = nextPtr) { + nextPtr = tsdPtr->firstContainerPtr->nextPtr; + ckfree(tsdPtr->firstContainerPtr); + } + tsdPtr->firstContainerPtr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TkpTestembedCmd -- + * + * Test command for the embedding facility. + * + * Results: + * Always returns TCL_OK. + * + * Side effects: + * Currently it does not do anything. + * + *---------------------------------------------------------------------- + */ + + /* ARGSUSED */ +int +TkpTestembedCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_DetachEmbeddedWindow -- + * + * This function detaches an embedded window + * + * Results: + * No return value. Detach the embedded window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static +void +Tk_DetachEmbeddedWindow( + TkWindow *winPtr, /* an embedded window */ + BOOL detachFlag) /* a flag of truely detaching */ +{ + TkpWinToplevelDetachWindow(winPtr); + if(detachFlag) { + TkpWinToplevelOverrideRedirect(winPtr, 0); + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_MapEmbeddedWindow -- + * + * This function is required for mapping an embedded window during idle. + * The input winPtr must be preserved using Tcl_Preserve before call this + * function and will be released by this function. + * + * Results: + * No return value. Map the embedded window if it is not dead. + * + * Side effects: + * The embedded window may change its state as the container's. + * + *---------------------------------------------------------------------- + */ + +static +void Tk_MapEmbeddedWindow( + TkWindow *winPtr) /* Top-level window that's about to be + * mapped. */ +{ + if(!(winPtr->flags & TK_ALREADY_DEAD)) { + HWND hwnd = (HWND)winPtr->privatePtr; + int state = SendMessage(hwnd, TK_STATE, -1, -1) - 1; + + if (state < 0 || state > 3) { + state = NormalState; + } + + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) { + /* empty body */ + } + + TkpWmSetState(winPtr, state); + TkWmMapWindow(winPtr); + } + Tcl_Release((ClientData)winPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkpUseWindow -- + * + * This procedure causes a Tk window to use a given Windows handle for a + * window as its underlying window, rather than a new Windows window + * being created automatically. It is invoked by an embedded application + * to specify the window in which the application is embedded. + * + * This procedure uses a simple attachment protocol by sending TK_INFO + * messages to the window to use with two sub messages: + * + * TK_CONTAINER_VERIFY - if a window handles this message, it should + * return either a (long)hwnd for a container or a -(long)hwnd + * for a non-container. + * + * TK_CONTAINER_ISAVAILABLE - a container window should return either + * a TRUE (non-zero) if it is available for use or a FALSE (zero) + * othersize. + * + * The TK_INFO messages are required in order to verify if the window to + * use is a valid container. Without an id verification, an invalid + * window attachment may cause unexpected crashes/panics (bug 1096074). + * Additional sub messages may be definded/used in future for other + * needs. + * + * We do not enforce the above protocol for the reason of backward + * compatibility. If the window to use is unable to handle TK_INFO + * messages (e.g., legacy Tk container applications before 8.5), a dialog + * box with a warning message pops up and the user is asked to confirm if + * the attachment should proceed. However, we may have to enforce it in + * future. + * + * Results: + * The return value is normally TCL_OK. If an error occurred (such as if + * the argument does not identify a legal Windows window handle or it is + * already in use or a cancel button is pressed by a user in confirming + * the use window as a Tk container) the return value is TCL_ERROR and an + * error message is left in the the interp's result if interp is not + * NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkpUseWindow( + Tcl_Interp *interp, /* If not NULL, used for error reporting if + * string is bogus. */ + Tk_Window tkwin, /* Tk window that does not yet have an + * associated X window. */ + const char *string) /* String identifying an X window to use for + * tkwin; must be an integer value. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + int id; + HWND hwnd; +/* + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); +*/ + +/* + if (winPtr->window != None) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "can't modify container after widget is created", -1)); + Tcl_SetErrorCode(interp, "TK", "EMBED", "POST_CREATE", NULL); + return TCL_ERROR; + } +*/ + + if (strcmp(string, "") == 0) { + if (winPtr->flags & TK_EMBEDDED) { + Tk_DetachEmbeddedWindow(winPtr, TRUE); + } + return TCL_OK; + } + + if ( +#ifdef _WIN64 + (sscanf(string, "0x%p", &hwnd) != 1) && +#endif + Tcl_GetInt(interp, string, (int *) &hwnd) != TCL_OK) { + return TCL_ERROR; + } + if ((HWND)winPtr->privatePtr == hwnd) { + return TCL_OK; + } + + /* + * Check if the window is a valid handle. If it is invalid, return + * TCL_ERROR and potentially leave an error message in the interp's + * result. + */ + + if (!IsWindow(hwnd)) { + if (interp != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" doesn't exist", string)); + Tcl_SetErrorCode(interp, "TK", "EMBED", "EXIST", NULL); + } + return TCL_ERROR; + } + + id = SendMessage(hwnd, TK_INFO, TK_CONTAINER_VERIFY, 0); + if (id == PTR2INT(hwnd)) { + if (!SendMessage(hwnd, TK_INFO, TK_CONTAINER_ISAVAILABLE, 0)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "The container is already in use", -1)); + Tcl_SetErrorCode(interp, "TK", "EMBED", "IN_USE", NULL); + return TCL_ERROR; + } + } else if (id == -PTR2INT(hwnd)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "the window to use is not a Tk container", -1)); + Tcl_SetErrorCode(interp, "TK", "EMBED", "CONTAINER", NULL); + return TCL_ERROR; + } else { + /* + * Proceed if the user decide to do so because it can be a legacy + * container application. However we may have to return a TCL_ERROR in + * order to avoid bug 1096074 in future. + */ + + char msg[256]; + + sprintf(msg, "Unable to get information of window \"%.80s\". Attach to this\nwindow may have unpredictable results if it is not a valid container.\n\nPress Ok to proceed or Cancel to abort attaching.", string); + if (IDCANCEL == MessageBoxA(hwnd, msg, "Tk Warning", + MB_OKCANCEL | MB_ICONWARNING)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "Operation has been canceled", -1)); + Tcl_SetErrorCode(interp, "TK", "EMBED", "CANCEL", NULL); + return TCL_ERROR; + } + } + + Tk_DetachEmbeddedWindow(winPtr, FALSE); + + /* + * Store the parent window in the platform private data slot so + * TkWmMapWindow can use it when creating the wrapper window. + */ + + winPtr->privatePtr = (struct TkWindowPrivate*) hwnd; + winPtr->flags |= TK_EMBEDDED; + winPtr->flags &= ~(TK_MAPPED); + + /* + * Preserve the winPtr and create an idle handler to map the embedded + * window. + */ + + Tcl_Preserve((ClientData) winPtr); + Tcl_DoWhenIdle((Tcl_IdleProc*) Tk_MapEmbeddedWindow, (ClientData) winPtr); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpMakeContainer -- + * + * This procedure is called to indicate that a particular window will be + * a container for an embedded application. This changes certain aspects + * of the window's behavior, such as whether it will receive events + * anymore. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpMakeContainer( + Tk_Window tkwin) +{ + TkWindow *winPtr = (TkWindow *) tkwin; + Container *containerPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * Register the window as a container so that, for example, we can find + * out later if the embedded app. is in the same process. + */ + + Tk_MakeWindowExist(tkwin); + containerPtr = ckalloc(sizeof(Container)); + containerPtr->parentPtr = winPtr; + containerPtr->parentHWnd = Tk_GetHWND(Tk_WindowId(tkwin)); + containerPtr->embeddedHWnd = NULL; + containerPtr->embeddedPtr = NULL; + containerPtr->embeddedMenuHWnd = NULL; + containerPtr->nextPtr = tsdPtr->firstContainerPtr; + tsdPtr->firstContainerPtr = containerPtr; + winPtr->flags |= TK_CONTAINER; + + /* + * Unlike in tkUnixEmbed.c, we don't make any requests for events in the + * embedded window here. Now we just allow the embedding of another TK + * application into TK windows. When the embedded window makes a request, + * that will be done by sending to the container window a WM_USER message, + * which will be intercepted by TkWinContainerProc. + * + * We need to get structure events of the container itself, though. + */ + + Tk_CreateEventHandler(tkwin, StructureNotifyMask, + ContainerEventProc, (ClientData) containerPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinEmbeddedEventProc -- + * + * This procedure is invoked by the Tk event dispatcher when various + * useful events are received for the *children* of a container window. + * It forwards relevant information, such as geometry requests, from the + * events into the container's application. + * + * Results: + * None. + * + * Side effects: + * Depends on the event. For example, when ConfigureRequest events occur, + * geometry information gets set for the container window. + * + *---------------------------------------------------------------------- + */ + +LRESULT +TkWinEmbeddedEventProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + int result = 1; + Container *containerPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * Find the Container structure associated with the parent window. + */ + + for (containerPtr = tsdPtr->firstContainerPtr; + containerPtr && containerPtr->parentHWnd != hwnd; + containerPtr = containerPtr->nextPtr) { + /* empty loop body */ + } + + if (containerPtr) { + TkWindow *topwinPtr = NULL; + if(Tk_IsTopLevel(containerPtr->parentPtr)) { + topwinPtr = containerPtr->parentPtr; + } + switch (message) { + case TK_INFO: + /* + * An embedded window may send this message for container + * verification and availability before attach. + * + * wParam - a sub message + * + * TK_CONTAINER_ISAVAILABLE - if the container is available + * for use? + * result = 1 for yes and 0 for no; + * + * TK_CONTAINER_VERIFY - request the container to verify its + * identification + * result = (long)hwnd if this window is a container + * -(long)hwnd otherwise + * + * lParam - N/A + */ + + switch(wParam) { + case TK_CONTAINER_ISAVAILABLE: + result = containerPtr->embeddedHWnd == NULL? 1:0; + break; + case TK_CONTAINER_VERIFY: + result = PTR2INT(containerPtr->parentHWnd); + break; + default: + result = 0; + } + break; + + case TK_ATTACHWINDOW: + /* + * An embedded window (either from this application or from + * another application) is trying to attach to this container. We + * attach it only if this container is not yet containing any + * window. + * + * wParam - a handle of an embedded window + * lParam - N/A + * + * An embedded window may send this message with a wParam of NULL + * to test if a window is able to provide embedding service. The + * container returns its window handle for accepting the + * attachment and identifying itself or a zero for being already + * in use. + * + * Return value: + * 0 - the container is unable to be used. + * hwnd - the container is ready to be used. + */ + if (containerPtr->embeddedHWnd == NULL) { + if (wParam) { + TkWindow *winPtr = (TkWindow *) + Tk_HWNDToWindow((HWND) wParam); + if (winPtr) { + winPtr->flags |= TK_BOTH_HALVES; + containerPtr->embeddedPtr = winPtr; + containerPtr->parentPtr->flags |= TK_BOTH_HALVES; + } + containerPtr->embeddedHWnd = (HWND)wParam; + } + result = PTR2INT(containerPtr->parentHWnd); + } else { + result = 0; + } + break; + + case TK_DETACHWINDOW: + /* + * An embedded window notifies the container that it is detached. + * The container should clearn the related variables and redraw + * its window. + * + * wParam - N/A + * lParam - N/A + * + * Return value: + * 0 - the message is not processed. + * others - the message is processed. + */ + + containerPtr->embeddedMenuHWnd = NULL; + containerPtr->embeddedHWnd = NULL; + containerPtr->parentPtr->flags &= ~TK_BOTH_HALVES; + if (topwinPtr) { + TkWinSetMenu((Tk_Window) topwinPtr, 0); + } + InvalidateRect(hwnd, NULL, TRUE); + break; + + case TK_GEOMETRYREQ: + /* + * An embedded window requests a window size change. + * + * wParam - window width + * lParam - window height + * + * Return value: + * 0 - the message is not processed. + * others - the message is processed. + */ + + EmbedGeometryRequest(containerPtr, (int)wParam, lParam); + break; + + case TK_RAISEWINDOW: + /* + * An embedded window requests to change its Z-order. + * + * wParam - a window handle as a z-order stack reference + * lParam - a flag of above-below: 0 - above; 1 or others: - below + * + * Return value: + * 0 - the message is not processed. + * others - the message is processed. + */ + + TkWinSetWindowPos(GetParent(containerPtr->parentHWnd), + (HWND)wParam, (int)lParam); + break; + + case TK_GETFRAMEWID: + /* + * An embedded window requests to get the frame window's id. + * + * wParam - N/A + * lParam - N/A + * + * Return vlaue: + * + * A handle of the frame window. If it is not availble, a zero is + * returned. + */ + if (topwinPtr) { + result = PTR2INT(GetParent(containerPtr->parentHWnd)); + } else { + topwinPtr = containerPtr->parentPtr; + while (!(topwinPtr->flags & TK_TOP_HIERARCHY)) { + topwinPtr = topwinPtr->parentPtr; + } + if (topwinPtr && topwinPtr->window) { + result = PTR2INT(GetParent(Tk_GetHWND(topwinPtr->window))); + } else { + result = 0; + } + } + break; + + case TK_CLAIMFOCUS: + /* + * An embedded window requests a focus. + * + * wParam - a flag of forcing focus + * lParam - N/A + * + * Return value: + * 0 - the message is not processed + * 1 - the message is processed + */ + + if (!SetFocus(containerPtr->embeddedHWnd) && wParam) { + /* + * forcing focus TBD + */ + } + break; + + case TK_WITHDRAW: + /* + * An embedded window requests withdraw. + * + * wParam - N/A + * lParam - N/A + * + * Return value + * 0 - the message is not processed + * 1 - the message is processed + */ + + if (topwinPtr) { + TkpWinToplevelWithDraw(topwinPtr); + } else { + result = 0; + } + break; + + case TK_ICONIFY: + /* + * An embedded window requests iconification. + * + * wParam - N/A + * lParam - N/A + * + * Return value + * 0 - the message is not processed + * 1 - the message is processed + */ + + if (topwinPtr) { + TkpWinToplevelIconify(topwinPtr); + } else { + result = 0; + } + break; + + case TK_DEICONIFY: + /* + * An embedded window requests deiconification. + * + * wParam - N/A + * lParam - N/A + * + * Return value + * 0 - the message is not processed + * 1 - the message is processed + */ + if (topwinPtr) { + TkpWinToplevelDeiconify(topwinPtr); + } else { + result = 0; + } + break; + + case TK_MOVEWINDOW: + /* + * An embedded window requests to move position if both wParam and + * lParam are greater or equal to 0. + * wParam - x value of the frame's upper left + * lParam - y value of the frame's upper left + * + * Otherwise an embedded window requests the current position + * + * Return value: an encoded window position in a 32bit long, i.e, + * ((x << 16) & 0xffff0000) | (y & 0xffff) + * + * Only a toplevel container may move the embedded. + */ + + result = TkpWinToplevelMove(containerPtr->parentPtr, + wParam, lParam); + break; + + case TK_OVERRIDEREDIRECT: + /* + * An embedded window request overrideredirect. + * + * wParam + * 0 - add a frame if there is no one + * 1 - remove the frame if there is a one + * < 0 - query the current overrideredirect value + * + * lParam - N/A + * + * Return value: + * 1 + the current value of overrideredirect if the container is a + * toplevel. Otherwise 0. + */ + if (topwinPtr) { + result = 1 + TkpWinToplevelOverrideRedirect(topwinPtr, wParam); + } else { + result = 0; + } + break; + + case TK_SETMENU: + /* + * An embedded requests to set a menu. + * + * wParam - a menu handle + * lParam - a menu window handle + * + * Return value: + * 1 - the message is processed + * 0 - the message is not processed + */ + if (topwinPtr) { + containerPtr->embeddedMenuHWnd = (HWND)lParam; + TkWinSetMenu((Tk_Window)topwinPtr, (HMENU)wParam); + } else { + result = 0; + } + break; + + case TK_STATE: + /* + * An embedded window request set/get state services. + * + * wParam - service directive + * 0 - 3 for setting state + * 0 - withdrawn state + * 1 - normal state + * 2 - zoom state + * 3 - icon state + * others for gettting state + * + * lParam - N/A + * + * Return value + * 1 + the current state or 0 if the container is not a toplevel + */ + + if (topwinPtr) { + if (wParam <= 3) { + TkpWmSetState(topwinPtr, wParam); + } + result = 1+TkpWmGetState(topwinPtr); + } else { + result = 0; + } + break; + + /* + * Return 0 since the current Tk container implementation is + * unable to provide following services. + */ + default: + result = 0; + break; + } + } else { + if ((message == TK_INFO) && (wParam == TK_CONTAINER_VERIFY)) { + /* + * Reply the message sender: this is not a Tk container + */ + + return -PTR2INT(hwnd); + } else { + result = 0; + } + } + + return result; +} + +/* + *---------------------------------------------------------------------- + * + * EmbedGeometryRequest -- + * + * This procedure is invoked when an embedded application requests a + * particular size. It processes the request (which may or may not + * actually resize the window) and reflects the results back to the + * embedded application. + * + * Results: + * None. + * + * Side effects: + * If we deny the child's size change request, a Configure event is + * synthesized to let the child know that the size is the same as it used + * to be. Events get processed while we're waiting for the geometry + * managers to do their thing. + * + *---------------------------------------------------------------------- + */ + +void +EmbedGeometryRequest( + Container *containerPtr, /* Information about the container window. */ + int width, int height) /* Size that the child has requested. */ +{ + TkWindow *winPtr = containerPtr->parentPtr; + + /* + * Forward the requested size into our geometry management hierarchy via + * the container window. We need to send a Configure event back to the + * embedded application even if we decide not to resize the window; to + * make this happen, process all idle event handlers synchronously here + * (so that the geometry managers have had a chance to do whatever they + * want to do), and if the window's size didn't change then generate a + * configure event. + */ + + Tk_GeometryRequest((Tk_Window)winPtr, width, height); + + if (containerPtr->embeddedHWnd != NULL) { + while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) { + /* Empty loop body. */ + } + + SetWindowPos(containerPtr->embeddedHWnd, NULL, 0, 0, + winPtr->changes.width, winPtr->changes.height, SWP_NOZORDER); + } +} + +/* + *---------------------------------------------------------------------- + * + * ContainerEventProc -- + * + * This procedure is invoked by the Tk event dispatcher when various + * useful events are received for the container window. + * + * Results: + * None. + * + * Side effects: + * Depends on the event. For example, when ConfigureRequest events occur, + * geometry information gets set for the container window. + * + *---------------------------------------------------------------------- + */ + +static void +ContainerEventProc( + ClientData clientData, /* Token for container window. */ + XEvent *eventPtr) /* ResizeRequest event. */ +{ + Container *containerPtr = (Container *)clientData; + Tk_Window tkwin = (Tk_Window)containerPtr->parentPtr; + + if (eventPtr->type == ConfigureNotify) { + /* + * Resize the embedded window, if there is any. + */ + + if (containerPtr->embeddedHWnd) { + SetWindowPos(containerPtr->embeddedHWnd, NULL, 0, 0, + Tk_Width(tkwin), Tk_Height(tkwin), SWP_NOZORDER); + } + } else if (eventPtr->type == DestroyNotify) { + /* + * The container is gone, remove it from the list. + */ + + EmbedWindowDeleted(containerPtr->parentPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetOtherWindow -- + * + * If both the container and embedded window are in the same process, + * this procedure will return either one, given the other. + * + * Results: + * If winPtr is a container, the return value is the token for the + * embedded window, and vice versa. If the "other" window isn't in this + * process, NULL is returned. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkWindow * +TkpGetOtherWindow( + TkWindow *winPtr) /* Tk's structure for a container or embedded + * window. */ +{ + Container *containerPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL; + containerPtr = containerPtr->nextPtr) { + if (containerPtr->embeddedPtr == winPtr) { + return containerPtr->parentPtr; + } else if (containerPtr->parentPtr == winPtr) { + return containerPtr->embeddedPtr; + } + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetEmbeddedHWnd -- + * + * This function returns the embedded window id. + * + * Results: + * If winPtr is a container, the return value is the HWND for the + * embedded window. Otherwise it returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HWND +Tk_GetEmbeddedHWnd( + TkWindow *winPtr) +{ + Container *containerPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL; + containerPtr = containerPtr->nextPtr) { + if (containerPtr->parentPtr == winPtr) { + return containerPtr->embeddedHWnd; + } + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetEmbeddedMenuHWND -- + * + * This function returns the embedded menu window id. + * + * Results: + * If winPtr is a container, the return value is the HWND for the + * embedded menu window. Otherwise it returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HWND +Tk_GetEmbeddedMenuHWND( + Tk_Window tkwin) +{ + TkWindow *winPtr = (TkWindow*)tkwin; + Container *containerPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + for (containerPtr = tsdPtr->firstContainerPtr; containerPtr != NULL; + containerPtr = containerPtr->nextPtr) { + if (containerPtr->parentPtr == winPtr) { + return containerPtr->embeddedMenuHWnd; + } + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * TkpClaimFocus -- + * + * This procedure is invoked when someone asks or the input focus to be + * put on a window in an embedded application, but the application + * doesn't currently have the focus. It requests the input focus from the + * container application. + * + * Results: + * None. + * + * Side effects: + * The input focus may change. + * + *---------------------------------------------------------------------- + */ + +void +TkpClaimFocus( + TkWindow *topLevelPtr, /* Top-level window containing desired focus + * window; should be embedded. */ + int force) /* One means that the container should claim + * the focus if it doesn't currently have + * it. */ +{ + HWND hwnd = GetParent(Tk_GetHWND(topLevelPtr->window)); + SendMessage(hwnd, TK_CLAIMFOCUS, (WPARAM) force, 0); +} + +/* + *---------------------------------------------------------------------- + * + * TkpRedirectKeyEvent -- + * + * This procedure is invoked when a key press or release event arrives + * for an application that does not believe it owns the input focus. + * This can happen because of embedding; for example, X can send an event + * to an embedded application when the real focus window is in the + * container application and is an ancestor of the container. This + * procedure's job is to forward the event back to the application where + * it really belongs. + * + * Results: + * None. + * + * Side effects: + * The event may get sent to a different application. + * + *---------------------------------------------------------------------- + */ + +void +TkpRedirectKeyEvent( + TkWindow *winPtr, /* Window to which the event was originally + * reported. */ + XEvent *eventPtr) /* X event to redirect (should be KeyPress or + * KeyRelease). */ +{ + /* not implemented */ +} + +/* + *---------------------------------------------------------------------- + * + * EmbedWindowDeleted -- + * + * This procedure is invoked when a window involved in embedding (as + * either the container or the embedded application) is destroyed. It + * cleans up the Container structure for the window. + * + * Results: + * None. + * + * Side effects: + * A Container structure may be freed. + * + *---------------------------------------------------------------------- + */ + +static void +EmbedWindowDeleted( + TkWindow *winPtr) /* Tk's information about window that was + * deleted. */ +{ + Container *containerPtr, *prevPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * Find the Container structure for this window work. Delete the + * information about the embedded application and free the container's + * record. The main container may be null. [Bug #476176] + */ + + prevPtr = NULL; + containerPtr = tsdPtr->firstContainerPtr; + if (containerPtr == NULL) return; + while (1) { + if (containerPtr->embeddedPtr == winPtr) { + containerPtr->embeddedHWnd = NULL; + containerPtr->embeddedPtr = NULL; + break; + } + if (containerPtr->parentPtr == winPtr) { + SendMessage(containerPtr->embeddedHWnd, WM_CLOSE, 0, 0); + containerPtr->parentPtr = NULL; + containerPtr->embeddedPtr = NULL; + break; + } + prevPtr = containerPtr; + containerPtr = containerPtr->nextPtr; + if (containerPtr == NULL) { + return; + } + } + if ((containerPtr->embeddedPtr == NULL) + && (containerPtr->parentPtr == NULL)) { + if (prevPtr == NULL) { + tsdPtr->firstContainerPtr = containerPtr->nextPtr; + } else { + prevPtr->nextPtr = containerPtr->nextPtr; + } + ckfree(containerPtr); + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinFont.c b/tk8.6/win/tkWinFont.c new file mode 100644 index 0000000..d67ea66 --- /dev/null +++ b/tk8.6/win/tkWinFont.c @@ -0,0 +1,2908 @@ +/* + * tkWinFont.c -- + * + * Contains the Windows implementation of the platform-independant font + * package interface. + * + * Copyright (c) 1994 Software Research Associates, Inc. + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-1999 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkFont.h" + +/* + * The following structure represents a font family. It is assumed that all + * screen fonts constructed from the same "font family" share certain + * properties; all screen fonts with the same "font family" point to a shared + * instance of this structure. The most important shared property is the + * character existence metrics, used to determine if a screen font can display + * a given Unicode character. + * + * Under Windows, a "font family" is uniquely identified by its face name. + */ + +#define FONTMAP_SHIFT 10 + +#define FONTMAP_PAGES (1 << (sizeof(Tcl_UniChar)*8 - FONTMAP_SHIFT)) +#define FONTMAP_BITSPERPAGE (1 << FONTMAP_SHIFT) + +typedef struct FontFamily { + struct FontFamily *nextPtr; /* Next in list of all known font families. */ + size_t refCount; /* How many SubFonts are referring to this + * FontFamily. When the refCount drops to + * zero, this FontFamily may be freed. */ + /* + * Key. + */ + + Tk_Uid faceName; /* Face name key for this FontFamily. */ + + /* + * Derived properties. + */ + + Tcl_Encoding encoding; /* Encoding for this font family. */ + int isSymbolFont; /* Non-zero if this is a symbol font. */ + int isWideFont; /* 1 if this is a double-byte font, 0 + * otherwise. */ + BOOL (WINAPI *textOutProc)(HDC hdc, int x, int y, TCHAR *str, int len); + /* The procedure to use to draw text after it + * has been converted from UTF-8 to the + * encoding of this font. */ + BOOL (WINAPI *getTextExtentPoint32Proc)(HDC, TCHAR *, int, LPSIZE); + /* The procedure to use to measure text after + * it has been converted from UTF-8 to the + * encoding of this font. */ + + char *fontMap[FONTMAP_PAGES]; + /* Two-level sparse table used to determine + * quickly if the specified character exists. + * As characters are encountered, more pages + * in this table are dynamically added. The + * contents of each page is a bitmask + * consisting of FONTMAP_BITSPERPAGE bits, + * representing whether this font can be used + * to display the given character at the + * corresponding bit position. The high bits + * of the character are used to pick which + * page of the table is used. */ + + /* + * Cached Truetype font info. + */ + + int segCount; /* The length of the following arrays. */ + USHORT *startCount; /* Truetype information about the font, */ + USHORT *endCount; /* indicating which characters this font can + * display (malloced). The format of this + * information is (relatively) compact, but + * would take longer to search than indexing + * into the fontMap[][] table. */ +} FontFamily; + +/* + * The following structure encapsulates an individual screen font. A font + * object is made up of however many SubFonts are necessary to display a + * stream of multilingual characters. + */ + +typedef struct SubFont { + char **fontMap; /* Pointer to font map from the FontFamily, + * cached here to save a dereference. */ + HFONT hFont0; /* The specific screen font that will be used + * when displaying/measuring chars belonging + * to the FontFamily. */ + FontFamily *familyPtr; /* The FontFamily for this SubFont. */ + HFONT hFontAngled; + double angle; +} SubFont; + +/* + * The following structure represents Windows' implementation of a font + * object. + */ + +#define SUBFONT_SPACE 3 +#define BASE_CHARS 128 + +typedef struct WinFont { + TkFont font; /* Stuff used by generic font package. Must be + * first in structure. */ + SubFont staticSubFonts[SUBFONT_SPACE]; + /* Builtin space for a limited number of + * SubFonts. */ + int numSubFonts; /* Length of following array. */ + SubFont *subFontArray; /* Array of SubFonts that have been loaded in + * order to draw/measure all the characters + * encountered by this font so far. All fonts + * start off with one SubFont initialized by + * AllocFont() from the original set of font + * attributes. Usually points to + * staticSubFonts, but may point to malloced + * space if there are lots of SubFonts. */ + HWND hwnd; /* Toplevel window of application that owns + * this font, used for getting HDC for + * offscreen measurements. */ + int pixelSize; /* Original pixel size used when font was + * constructed. */ + int widths[BASE_CHARS]; /* Widths of first 128 chars in the base font, + * for handling common case. The base font is + * always used to draw characters between + * 0x0000 and 0x007f. */ +} WinFont; + +/* + * The following structure is passed as the LPARAM when calling the font + * enumeration procedure to determine if a font can support the given + * character. + */ + +typedef struct CanUse { + HDC hdc; + WinFont *fontPtr; + Tcl_DString *nameTriedPtr; + int ch; + SubFont *subFontPtr; + SubFont **subFontPtrPtr; +} CanUse; + +/* + * The following structure is used to map between the Tcl strings that + * represent the system fonts and the numbers used by Windows. + */ + +static const TkStateMap systemMap[] = { + {ANSI_FIXED_FONT, "ansifixed"}, + {ANSI_FIXED_FONT, "fixed"}, + {ANSI_VAR_FONT, "ansi"}, + {DEVICE_DEFAULT_FONT, "device"}, + {DEFAULT_GUI_FONT, "defaultgui"}, + {OEM_FIXED_FONT, "oemfixed"}, + {SYSTEM_FIXED_FONT, "systemfixed"}, + {SYSTEM_FONT, "system"}, + {-1, NULL} +}; + +typedef struct ThreadSpecificData { + FontFamily *fontFamilyList; /* The list of font families that are + * currently loaded. As screen fonts are + * loaded, this list grows to hold information + * about what characters exist in each font + * family. */ + Tcl_HashTable uidTable; +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Information cached about the system at startup time. + */ + +static Tcl_Encoding systemEncoding; + +/* + * Procedures used only in this file. + */ + +static FontFamily * AllocFontFamily(HDC hdc, HFONT hFont, int base); +static SubFont * CanUseFallback(HDC hdc, WinFont *fontPtr, + const char *fallbackName, int ch, + SubFont **subFontPtrPtr); +static SubFont * CanUseFallbackWithAliases(HDC hdc, WinFont *fontPtr, + const char *faceName, int ch, + Tcl_DString *nameTriedPtr, + SubFont **subFontPtrPtr); +static int FamilyExists(HDC hdc, const char *faceName); +static const char * FamilyOrAliasExists(HDC hdc, const char *faceName); +static SubFont * FindSubFontForChar(WinFont *fontPtr, int ch, + SubFont **subFontPtrPtr); +static void FontMapInsert(SubFont *subFontPtr, int ch); +static void FontMapLoadPage(SubFont *subFontPtr, int row); +static int FontMapLookup(SubFont *subFontPtr, int ch); +static void FreeFontFamily(FontFamily *familyPtr); +static HFONT GetScreenFont(const TkFontAttributes *faPtr, + const char *faceName, int pixelSize, + double angle); +static void InitFont(Tk_Window tkwin, HFONT hFont, + int overstrike, WinFont *tkFontPtr); +static inline void InitSubFont(HDC hdc, HFONT hFont, int base, + SubFont *subFontPtr); +static int CreateNamedSystemLogFont(Tcl_Interp *interp, + Tk_Window tkwin, const char* name, + LOGFONT* logFontPtr); +static int CreateNamedSystemFont(Tcl_Interp *interp, + Tk_Window tkwin, const char* name, HFONT hFont); +static int LoadFontRanges(HDC hdc, HFONT hFont, + USHORT **startCount, USHORT **endCount, + int *symbolPtr); +static void MultiFontTextOut(HDC hdc, WinFont *fontPtr, + const char *source, int numBytes, int x, int y, + double angle); +static void ReleaseFont(WinFont *fontPtr); +static inline void ReleaseSubFont(SubFont *subFontPtr); +static int SeenName(const char *name, Tcl_DString *dsPtr); +static inline HFONT SelectFont(HDC hdc, WinFont *fontPtr, + SubFont *subFontPtr, double angle); +static inline void SwapLong(PULONG p); +static inline void SwapShort(USHORT *p); +static int CALLBACK WinFontCanUseProc(ENUMLOGFONT *lfPtr, + NEWTEXTMETRIC *tmPtr, int fontType, + LPARAM lParam); +static int CALLBACK WinFontExistProc(ENUMLOGFONT *lfPtr, + NEWTEXTMETRIC *tmPtr, int fontType, + LPARAM lParam); +static int CALLBACK WinFontFamilyEnumProc(ENUMLOGFONT *lfPtr, + NEWTEXTMETRIC *tmPtr, int fontType, + LPARAM lParam); + +/* + *------------------------------------------------------------------------- + * + * TkpFontPkgInit -- + * + * This procedure is called when an application is created. It + * initializes all the structures that are used by the platform-dependent + * code on a per application basis. + * + * Results: + * None. + * + * Side effects: + * + * None. + * + *------------------------------------------------------------------------- + */ + +void +TkpFontPkgInit( + TkMainInfo *mainPtr) /* The application being created. */ +{ + systemEncoding = TkWinGetUnicodeEncoding(); + TkWinSetupSystemFonts(mainPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetNativeFont -- + * + * Map a platform-specific native font name to a TkFont. + * + * Results: + * The return value is a pointer to a TkFont that represents the native + * font. If a native font by the given name could not be found, the + * return value is NULL. + * + * Every call to this procedure returns a new TkFont structure, even if + * the name has already been seen before. The caller should call + * TkpDeleteFont() when the font is no longer needed. + * + * The caller is responsible for initializing the memory associated with + * the generic TkFont when this function returns and releasing the + * contents of the generic TkFont before calling TkpDeleteFont(). + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetNativeFont( + Tk_Window tkwin, /* For display where font will be used. */ + const char *name) /* Platform-specific font name. */ +{ + int object; + WinFont *fontPtr; + + object = TkFindStateNum(NULL, NULL, systemMap, name); + if (object < 0) { + return NULL; + } + + tkwin = (Tk_Window) ((TkWindow *) tkwin)->mainPtr->winPtr; + fontPtr = ckalloc(sizeof(WinFont)); + InitFont(tkwin, GetStockObject(object), 0, fontPtr); + + return (TkFont *) fontPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * CreateNamedSystemFont -- + * + * This function registers a Windows logical font description with the Tk + * named font mechanism. + * + * Side effects: + * A new named font is added to the Tk font registry. + * + *--------------------------------------------------------------------------- + */ + +static int +CreateNamedSystemLogFont( + Tcl_Interp *interp, + Tk_Window tkwin, + const char* name, + LOGFONT* logFontPtr) +{ + HFONT hFont; + int r; + + hFont = CreateFontIndirect(logFontPtr); + r = CreateNamedSystemFont(interp, tkwin, name, hFont); + DeleteObject((HGDIOBJ)hFont); + return r; +} + +/* + *--------------------------------------------------------------------------- + * + * CreateNamedSystemFont -- + * + * This function registers a Windows font with the Tk named font + * mechanism. + * + * Side effects: + * A new named font is added to the Tk font registry. + * + *--------------------------------------------------------------------------- + */ + +static int +CreateNamedSystemFont( + Tcl_Interp *interp, + Tk_Window tkwin, + const char* name, + HFONT hFont) +{ + WinFont winfont; + int r; + + TkDeleteNamedFont(NULL, tkwin, name); + InitFont(tkwin, hFont, 0, &winfont); + r = TkCreateNamedFont(interp, tkwin, name, &winfont.font.fa); + TkpDeleteFont((TkFont *)&winfont); + return r; +} + +/* + *--------------------------------------------------------------------------- + * + * TkWinSystemFonts -- + * + * Create some platform specific named fonts that to give access to the + * system fonts. These are all defined for the Windows desktop + * parameters. + * + *--------------------------------------------------------------------------- + */ + +void +TkWinSetupSystemFonts( + TkMainInfo *mainPtr) +{ + Tcl_Interp *interp; + Tk_Window tkwin; + const TkStateMap *mapPtr; + NONCLIENTMETRICS ncMetrics; + ICONMETRICS iconMetrics; + HFONT hFont; + + interp = (Tcl_Interp *) mainPtr->interp; + tkwin = (Tk_Window) mainPtr->winPtr; + + /* force this for now */ + if (((TkWindow *) tkwin)->mainPtr == NULL) { + ((TkWindow *) tkwin)->mainPtr = mainPtr; + } + + /* + * If this API call fails then we will fallback to setting these named + * fonts from script in ttk/fonts.tcl. So far I've only seen it fail when + * WINVER has been defined for a higher platform than we are running on. + * (i.e. WINVER=0x0600 and running on XP). + */ + + ZeroMemory(&ncMetrics, sizeof(ncMetrics)); + ncMetrics.cbSize = sizeof(ncMetrics); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, + sizeof(ncMetrics), &ncMetrics, 0)) { + CreateNamedSystemLogFont(interp, tkwin, "TkDefaultFont", + &ncMetrics.lfMessageFont); + CreateNamedSystemLogFont(interp, tkwin, "TkHeadingFont", + &ncMetrics.lfMessageFont); + CreateNamedSystemLogFont(interp, tkwin, "TkTextFont", + &ncMetrics.lfMessageFont); + CreateNamedSystemLogFont(interp, tkwin, "TkMenuFont", + &ncMetrics.lfMenuFont); + CreateNamedSystemLogFont(interp, tkwin, "TkTooltipFont", + &ncMetrics.lfStatusFont); + CreateNamedSystemLogFont(interp, tkwin, "TkCaptionFont", + &ncMetrics.lfCaptionFont); + CreateNamedSystemLogFont(interp, tkwin, "TkSmallCaptionFont", + &ncMetrics.lfSmCaptionFont); + } + + iconMetrics.cbSize = sizeof(iconMetrics); + if (SystemParametersInfo(SPI_GETICONMETRICS, sizeof(iconMetrics), + &iconMetrics, 0)) { + CreateNamedSystemLogFont(interp, tkwin, "TkIconFont", + &iconMetrics.lfFont); + } + + /* + * Identify an available fixed font. Equivalent to ANSI_FIXED_FONT but + * more reliable on Russian Windows. + */ + + { + LOGFONT lfFixed = { + 0, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + 0, 0, DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN, TEXT("") + }; + long pointSize, dpi; + HDC hdc = GetDC(NULL); + dpi = GetDeviceCaps(hdc, LOGPIXELSY); + pointSize = -MulDiv(ncMetrics.lfMessageFont.lfHeight, 72, dpi); + lfFixed.lfHeight = -MulDiv(pointSize+1, dpi, 72); + ReleaseDC(NULL, hdc); + CreateNamedSystemLogFont(interp, tkwin, "TkFixedFont", &lfFixed); + } + + /* + * Setup the remaining standard Tk font names as named fonts. + */ + + for (mapPtr = systemMap; mapPtr->strKey != NULL; mapPtr++) { + hFont = (HFONT) GetStockObject(mapPtr->numKey); + CreateNamedSystemFont(interp, tkwin, mapPtr->strKey, hFont); + } +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFromAttributes -- + * + * Given a desired set of attributes for a font, find a font with the + * closest matching attributes. + * + * Results: + * The return value is a pointer to a TkFont that represents the font + * with the desired attributes. If a font with the desired attributes + * could not be constructed, some other font will be substituted + * automatically. NULL is never returned. + * + * Every call to this procedure returns a new TkFont structure, even if + * the specified attributes have already been seen before. The caller + * should call TkpDeleteFont() to free the platform- specific data when + * the font is no longer needed. + * + * The caller is responsible for initializing the memory associated with + * the generic TkFont when this function returns and releasing the + * contents of the generic TkFont before calling TkpDeleteFont(). + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +TkFont * +TkpGetFontFromAttributes( + TkFont *tkFontPtr, /* If non-NULL, store the information in this + * existing TkFont structure, rather than + * allocating a new structure to hold the + * font; the existing contents of the font + * will be released. If NULL, a new TkFont + * structure is allocated. */ + Tk_Window tkwin, /* For display where font will be used. */ + const TkFontAttributes *faPtr) + /* Set of attributes to match. */ +{ + int i, j; + HDC hdc; + HWND hwnd; + HFONT hFont; + Window window; + WinFont *fontPtr; + const char *const *const *fontFallbacks; + Tk_Uid faceName, fallback, actualName; + + tkwin = (Tk_Window) ((TkWindow *) tkwin)->mainPtr->winPtr; + window = Tk_WindowId(tkwin); + hwnd = (window == None) ? NULL : TkWinGetHWND(window); + hdc = GetDC(hwnd); + + /* + * Algorithm to get the closest font name to the one requested. + * + * try fontname + * try all aliases for fontname + * foreach fallback for fontname + * try the fallback + * try all aliases for the fallback + */ + + faceName = faPtr->family; + if (faceName != NULL) { + actualName = FamilyOrAliasExists(hdc, faceName); + if (actualName != NULL) { + faceName = actualName; + goto found; + } + fontFallbacks = TkFontGetFallbacks(); + for (i = 0; fontFallbacks[i] != NULL; i++) { + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + if (strcasecmp(faceName, fallback) == 0) { + break; + } + } + if (fallback != NULL) { + for (j = 0; (fallback = fontFallbacks[i][j]) != NULL; j++) { + actualName = FamilyOrAliasExists(hdc, fallback); + if (actualName != NULL) { + faceName = actualName; + goto found; + } + } + } + } + } + + found: + ReleaseDC(hwnd, hdc); + + hFont = GetScreenFont(faPtr, faceName, + (int)(TkFontGetPixels(tkwin, faPtr->size) + 0.5), 0.0); + if (tkFontPtr == NULL) { + fontPtr = ckalloc(sizeof(WinFont)); + } else { + fontPtr = (WinFont *) tkFontPtr; + ReleaseFont(fontPtr); + } + InitFont(tkwin, hFont, faPtr->overstrike, fontPtr); + + return (TkFont *) fontPtr; +} + +/* + *--------------------------------------------------------------------------- + * + * TkpDeleteFont -- + * + * Called to release a font allocated by TkpGetNativeFont() or + * TkpGetFontFromAttributes(). The caller should have already released + * the fields of the TkFont that are used exclusively by the generic + * TkFont code. + * + * Results: + * None. + * + * Side effects: + * TkFont is deallocated. + * + *--------------------------------------------------------------------------- + */ + +void +TkpDeleteFont( + TkFont *tkFontPtr) /* Token of font to be deleted. */ +{ + WinFont *fontPtr; + + fontPtr = (WinFont *) tkFontPtr; + ReleaseFont(fontPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpGetFontFamilies, WinFontFamilyEnumProc -- + * + * Return information about the font families that are available on the + * display of the given window. + * + * Results: + * Modifies interp's result object to hold a list of all the available + * font families. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +void +TkpGetFontFamilies( + Tcl_Interp *interp, /* Interp to hold result. */ + Tk_Window tkwin) /* For display to query. */ +{ + HDC hdc; + HWND hwnd; + Window window; + Tcl_Obj *resultObj; + + window = Tk_WindowId(tkwin); + hwnd = (window == None) ? NULL : TkWinGetHWND(window); + hdc = GetDC(hwnd); + resultObj = Tcl_NewObj(); + + /* + * On any version NT, there may fonts with international names. Use the + * NT-only Unicode version of EnumFontFamilies to get the font names. If + * we used the ANSI version on a non-internationalized version of NT, we + * would get font names with '?' replacing all the international + * characters. + * + * On a non-internationalized verson of 95, fonts with international names + * are not allowed, so the ANSI version of EnumFontFamilies will work. On + * an internationalized version of 95, there may be fonts with + * international names; the ANSI version will work, fetching the name in + * the system code page. Can't use the Unicode version of EnumFontFamilies + * because it only exists under NT. + */ + + EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontFamilyEnumProc, + (LPARAM) resultObj); + ReleaseDC(hwnd, hdc); + Tcl_SetObjResult(interp, resultObj); +} + +static int CALLBACK +WinFontFamilyEnumProc( + ENUMLOGFONT *lfPtr, /* Logical-font data. */ + NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */ + int fontType, /* Type of font (not used). */ + LPARAM lParam) /* Result object to hold result. */ +{ + char *faceName = (char *) lfPtr->elfLogFont.lfFaceName; + Tcl_Obj *resultObj = (Tcl_Obj *) lParam; + Tcl_DString faceString; + + Tcl_ExternalToUtfDString(systemEncoding, faceName, -1, &faceString); + Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj( + Tcl_DStringValue(&faceString), Tcl_DStringLength(&faceString))); + Tcl_DStringFree(&faceString); + return 1; +} + +/* + *------------------------------------------------------------------------- + * + * TkpGetSubFonts -- + * + * A function used by the testing package for querying the actual screen + * fonts that make up a font object. + * + * Results: + * Modifies interp's result object to hold a list containing the names of + * the screen fonts that make up the given font object. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +void +TkpGetSubFonts( + Tcl_Interp *interp, /* Interp to hold result. */ + Tk_Font tkfont) /* Font object to query. */ +{ + int i; + WinFont *fontPtr; + FontFamily *familyPtr; + Tcl_Obj *resultPtr, *strPtr; + + resultPtr = Tcl_NewObj(); + fontPtr = (WinFont *) tkfont; + for (i = 0; i < fontPtr->numSubFonts; i++) { + familyPtr = fontPtr->subFontArray[i].familyPtr; + strPtr = Tcl_NewStringObj(familyPtr->faceName, -1); + Tcl_ListObjAppendElement(NULL, resultPtr, strPtr); + } + Tcl_SetObjResult(interp, resultPtr); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetFontAttrsForChar -- + * + * Retrieve the font attributes of the actual font used to render a given + * character. + * + * Results: + * None. + * + * Side effects: + * The font attributes are stored in *faPtr. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetFontAttrsForChar( + Tk_Window tkwin, /* Window on the font's display */ + Tk_Font tkfont, /* Font to query */ + int c, /* Character of interest */ + TkFontAttributes *faPtr) /* Output: Font attributes */ +{ + WinFont *fontPtr = (WinFont *) tkfont; + /* Structure describing the logical font */ + HDC hdc = GetDC(fontPtr->hwnd); + /* GDI device context */ + SubFont *lastSubFontPtr = &fontPtr->subFontArray[0]; + /* Pointer to subfont array in case + * FindSubFontForChar needs to fix up the + * memory allocation */ + SubFont *thisSubFontPtr = + FindSubFontForChar(fontPtr, c, &lastSubFontPtr); + /* Pointer to the subfont to use for the given + * character */ + FontFamily *familyPtr = thisSubFontPtr->familyPtr; + HFONT oldfont; /* Saved font from the device context */ + TEXTMETRIC tm; /* Font metrics of the selected subfont */ + + /* + * Get the font attributes. + */ + + oldfont = SelectObject(hdc, thisSubFontPtr->hFont0); + GetTextMetrics(hdc, &tm); + SelectObject(hdc, oldfont); + ReleaseDC(fontPtr->hwnd, hdc); + faPtr->family = familyPtr->faceName; + faPtr->size = TkFontGetPoints(tkwin, + (double)(tm.tmInternalLeading - tm.tmHeight)); + faPtr->weight = (tm.tmWeight > FW_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL; + faPtr->slant = tm.tmItalic ? TK_FS_ITALIC : TK_FS_ROMAN; + faPtr->underline = (tm.tmUnderlined != 0); + faPtr->overstrike = fontPtr->font.fa.overstrike; +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_MeasureChars -- + * + * Determine the number of bytes from the string that will fit in the + * given horizontal span. The measurement is done under the assumption + * that Tk_DrawChars() will be used to actually display the characters. + * + * Results: + * The return value is the number of bytes from source that fit into the + * span that extends from 0 to maxLength. *lengthPtr is filled with the + * x-coordinate of the right edge of the last character that did fit. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +Tk_MeasureChars( + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. */ + int numBytes, /* Maximum number of bytes to consider from + * source string. */ + int maxLength, /* If >= 0, maxLength specifies the longest + * permissible line length in pixels; don't + * consider any character that would cross + * this x-position. If < 0, then line length + * is unbounded and the flags argument is + * ignored. */ + int flags, /* Various flag bits OR-ed together: + * TK_PARTIAL_OK means include the last char + * which only partially fits on this line. + * TK_WHOLE_WORDS means stop on a word + * boundary, if possible. TK_AT_LEAST_ONE + * means return at least one character (or at + * least the first partial word in case + * TK_WHOLE_WORDS is also set) even if no + * characters (words) fit. */ + int *lengthPtr) /* Filled with x-location just after the + * terminating character. */ +{ + HDC hdc; + HFONT oldFont; + WinFont *fontPtr; + int curX, moretomeasure; + int ch; + SIZE size; + FontFamily *familyPtr; + Tcl_DString runString; + SubFont *thisSubFontPtr, *lastSubFontPtr; + const char *p, *end, *next = NULL, *start; + + if (numBytes == 0) { + *lengthPtr = 0; + return 0; + } + + fontPtr = (WinFont *) tkfont; + + hdc = GetDC(fontPtr->hwnd); + lastSubFontPtr = &fontPtr->subFontArray[0]; + oldFont = SelectObject(hdc, lastSubFontPtr->hFont0); + + /* + * A three step process: + * 1. Find a contiguous range of characters that can all be represented by + * a single screen font. + * 2. Convert those chars to the encoding of that font. + * 3. Measure converted chars. + */ + + moretomeasure = 0; + curX = 0; + start = source; + end = start + numBytes; + for (p = start; p < end; ) { + next = p + TkUtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); + if (thisSubFontPtr != lastSubFontPtr) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, start, + (int) (p - start), &runString); + size.cx = 0; + familyPtr->getTextExtentPoint32Proc(hdc, + (TCHAR *)Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + Tcl_DStringFree(&runString); + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + break; + } + curX += size.cx; + lastSubFontPtr = thisSubFontPtr; + start = p; + + SelectObject(hdc, lastSubFontPtr->hFont0); + } + p = next; + } + + if (!moretomeasure) { + /* + * We get here if the previous loop was just finished normally, + * without a break. Just measure the last run and that's it. + */ + + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, start, + (int) (p - start), &runString); + size.cx = 0; + familyPtr->getTextExtentPoint32Proc(hdc, (TCHAR *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + Tcl_DStringFree(&runString); + if (maxLength >= 0 && (curX+size.cx) > maxLength) { + moretomeasure = 1; + } else { + curX += size.cx; + p = end; + } + } + + if (moretomeasure) { + /* + * We get here if the measurement of the last run was over the + * maxLength limit. We need to restart this run and do it char by + * char, but always in context with the previous text to account for + * kerning (especially italics). + */ + + char buf[16]; + int dstWrote; + int lastSize = 0; + + familyPtr = lastSubFontPtr->familyPtr; + Tcl_DStringInit(&runString); + for (p = start; p < end; ) { + next = p + TkUtfToUniChar(p, &ch); + Tcl_UtfToExternal(NULL, familyPtr->encoding, p, + (int) (next - p), 0, NULL, buf, sizeof(buf), NULL, + &dstWrote, NULL); + Tcl_DStringAppend(&runString,buf,dstWrote); + size.cx = 0; + familyPtr->getTextExtentPoint32Proc(hdc, + (TCHAR *) Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + if ((curX+size.cx) > maxLength) { + break; + } + lastSize = size.cx; + p = next; + } + Tcl_DStringFree(&runString); + + /* + * "p" points to the first character that doesn't fit in the desired + * span. Look at the flags to figure out whether to include this next + * character. + */ + + if ((p < end) && (((flags & TK_PARTIAL_OK) && (curX != maxLength)) + || ((p==source) && (flags&TK_AT_LEAST_ONE) && (curX==0)))) { + /* + * Include the first character that didn't quite fit in the + * desired span. The width returned will include the width of that + * extra character. + */ + + p = next; + curX += size.cx; + } else { + curX += lastSize; + } + } + + SelectObject(hdc, oldFont); + ReleaseDC(fontPtr->hwnd, hdc); + + if ((flags & TK_WHOLE_WORDS) && (p < end)) { + /* + * Scan the string for the last word break and than repeat the whole + * procedure without the maxLength limit or any flags. + */ + + const char *lastWordBreak = NULL; + int ch2; + + end = p; + p = source; + ch = ' '; + while (p < end) { + next = p + TkUtfToUniChar(p, &ch2); + if ((ch != ' ') && (ch2 == ' ')) { + lastWordBreak = p; + } + p = next; + ch = ch2; + } + + if (lastWordBreak != NULL) { + return Tk_MeasureChars(tkfont, source, lastWordBreak-source, + -1, 0, lengthPtr); + } + if (flags & TK_AT_LEAST_ONE) { + p = end; + } else { + p = source; + curX = 0; + } + } + + *lengthPtr = curX; + return p - source; +} + +/* + *--------------------------------------------------------------------------- + * + * TkpMeasureCharsInContext -- + * + * Determine the number of bytes from the string that will fit in the + * given horizontal span. The measurement is done under the assumption + * that TkpDrawCharsInContext() will be used to actually display the + * characters. + * + * This one is almost the same as Tk_MeasureChars(), but with access to + * all the characters on the line for context. On Windows this context + * isn't consulted, so we just call Tk_MeasureChars(). + * + * Results: + * The return value is the number of bytes from source that fit into the + * span that extends from 0 to maxLength. *lengthPtr is filled with the + * x-coordinate of the right edge of the last character that did fit. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +int +TkpMeasureCharsInContext( + Tk_Font tkfont, /* Font in which characters will be drawn. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. */ + int numBytes, /* Maximum number of bytes to consider from + * source string in all. */ + int rangeStart, /* Index of first byte to measure. */ + int rangeLength, /* Length of range to measure in bytes. */ + int maxLength, /* If >= 0, maxLength specifies the longest + * permissible line length; don't consider any + * character that would cross this x-position. + * If < 0, then line length is unbounded and + * the flags argument is ignored. */ + int flags, /* Various flag bits OR-ed together: + * TK_PARTIAL_OK means include the last char + * which only partially fit on this line. + * TK_WHOLE_WORDS means stop on a word + * boundary, if possible. TK_AT_LEAST_ONE + * means return at least one character even if + * no characters fit. TK_ISOLATE_END means + * that the last character should not be + * considered in context with the rest of the + * string (used for breaking lines). */ + int *lengthPtr) /* Filled with x-location just after the + * terminating character. */ +{ + (void) numBytes; /*unused*/ + return Tk_MeasureChars(tkfont, source + rangeStart, rangeLength, + maxLength, flags, lengthPtr); +} + +/* + *--------------------------------------------------------------------------- + * + * Tk_DrawChars -- + * + * Draw a string of characters on the screen. + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +Tk_DrawChars( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + int x, int y) /* Coordinates at which to place origin of + * string when drawing. */ +{ + HDC dc; + WinFont *fontPtr; + TkWinDCState state; + + fontPtr = (WinFont *) gc->font; + display->request++; + + if (drawable == None) { + return; + } + + dc = TkWinGetDrawableDC(display, drawable, &state); + + SetROP2(dc, tkpWinRopModes[gc->function]); + + if ((gc->clip_mask != None) && + ((TkpClipMask *) gc->clip_mask)->type == TKP_CLIP_REGION) { + SelectClipRgn(dc, (HRGN)((TkpClipMask *)gc->clip_mask)->value.region); + } + + if ((gc->fill_style == FillStippled + || gc->fill_style == FillOpaqueStippled) + && gc->stipple != None) { + TkWinDrawable *twdPtr = (TkWinDrawable *) gc->stipple; + HBRUSH oldBrush, stipple; + HBITMAP oldBitmap, bitmap; + HDC dcMem; + TEXTMETRIC tm; + SIZE size; + + if (twdPtr->type != TWD_BITMAP) { + Tcl_Panic("unexpected drawable type in stipple"); + } + + /* + * Select stipple pattern into destination dc. + */ + + dcMem = CreateCompatibleDC(dc); + + stipple = CreatePatternBrush(twdPtr->bitmap.handle); + SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL); + oldBrush = SelectObject(dc, stipple); + + SetTextAlign(dcMem, TA_LEFT | TA_BASELINE); + SetTextColor(dcMem, gc->foreground); + SetBkMode(dcMem, TRANSPARENT); + SetBkColor(dcMem, RGB(0, 0, 0)); + + /* + * Compute the bounding box and create a compatible bitmap. + */ + + GetTextExtentPointA(dcMem, source, numBytes, &size); + GetTextMetrics(dcMem, &tm); + size.cx -= tm.tmOverhang; + bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy); + oldBitmap = SelectObject(dcMem, bitmap); + + /* + * The following code is tricky because fonts are rendered in multiple + * colors. First we draw onto a black background and copy the white + * bits. Then we draw onto a white background and copy the black bits. + * Both the foreground and background bits of the font are ANDed with + * the stipple pattern as they are copied. + */ + + PatBlt(dcMem, 0, 0, size.cx, size.cy, BLACKNESS); + MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0); + BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, 0xEA02E9); + PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS); + MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0); + BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, 0x8A0E06); + + /* + * Destroy the temporary bitmap and restore the device context. + */ + + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + DeleteDC(dcMem); + SelectObject(dc, oldBrush); + DeleteObject(stipple); + } else if (gc->function == GXcopy) { + SetTextAlign(dc, TA_LEFT | TA_BASELINE); + SetTextColor(dc, gc->foreground); + SetBkMode(dc, TRANSPARENT); + MultiFontTextOut(dc, fontPtr, source, numBytes, x, y, 0.0); + } else { + HBITMAP oldBitmap, bitmap; + HDC dcMem; + TEXTMETRIC tm; + SIZE size; + + dcMem = CreateCompatibleDC(dc); + + SetTextAlign(dcMem, TA_LEFT | TA_BASELINE); + SetTextColor(dcMem, gc->foreground); + SetBkMode(dcMem, TRANSPARENT); + SetBkColor(dcMem, RGB(0, 0, 0)); + + /* + * Compute the bounding box and create a compatible bitmap. + */ + + GetTextExtentPointA(dcMem, source, numBytes, &size); + GetTextMetrics(dcMem, &tm); + size.cx -= tm.tmOverhang; + bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy); + oldBitmap = SelectObject(dcMem, bitmap); + + MultiFontTextOut(dcMem, fontPtr, source, numBytes, 0, tm.tmAscent, + 0.0); + BitBlt(dc, x, y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, (DWORD) tkpWinBltModes[gc->function]); + + /* + * Destroy the temporary bitmap and restore the device context. + */ + + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + DeleteDC(dcMem); + } + TkWinReleaseDrawableDC(drawable, dc, &state); +} + +void +TkDrawAngledChars( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + double x, double y, /* Coordinates at which to place origin of + * string when drawing. */ + double angle) +{ + HDC dc; + WinFont *fontPtr; + TkWinDCState state; + + fontPtr = (WinFont *) gc->font; + display->request++; + + if (drawable == None) { + return; + } + + dc = TkWinGetDrawableDC(display, drawable, &state); + + SetROP2(dc, tkpWinRopModes[gc->function]); + + if ((gc->clip_mask != None) && + ((TkpClipMask *) gc->clip_mask)->type == TKP_CLIP_REGION) { + SelectClipRgn(dc, (HRGN)((TkpClipMask *)gc->clip_mask)->value.region); + } + + if ((gc->fill_style == FillStippled + || gc->fill_style == FillOpaqueStippled) + && gc->stipple != None) { + TkWinDrawable *twdPtr = (TkWinDrawable *)gc->stipple; + HBRUSH oldBrush, stipple; + HBITMAP oldBitmap, bitmap; + HDC dcMem; + TEXTMETRIC tm; + SIZE size; + + if (twdPtr->type != TWD_BITMAP) { + Tcl_Panic("unexpected drawable type in stipple"); + } + + /* + * Select stipple pattern into destination dc. + */ + + dcMem = CreateCompatibleDC(dc); + + stipple = CreatePatternBrush(twdPtr->bitmap.handle); + SetBrushOrgEx(dc, gc->ts_x_origin, gc->ts_y_origin, NULL); + oldBrush = SelectObject(dc, stipple); + + SetTextAlign(dcMem, TA_LEFT | TA_BASELINE); + SetTextColor(dcMem, gc->foreground); + SetBkMode(dcMem, TRANSPARENT); + SetBkColor(dcMem, RGB(0, 0, 0)); + + /* + * Compute the bounding box and create a compatible bitmap. + */ + + GetTextExtentPointA(dcMem, source, numBytes, &size); + GetTextMetrics(dcMem, &tm); + size.cx -= tm.tmOverhang; + bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy); + oldBitmap = SelectObject(dcMem, bitmap); + + /* + * The following code is tricky because fonts are rendered in multiple + * colors. First we draw onto a black background and copy the white + * bits. Then we draw onto a white background and copy the black bits. + * Both the foreground and background bits of the font are ANDed with + * the stipple pattern as they are copied. + */ + + PatBlt(dcMem, 0, 0, size.cx, size.cy, BLACKNESS); + MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle); + BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, 0xEA02E9); + PatBlt(dcMem, 0, 0, size.cx, size.cy, WHITENESS); + MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle); + BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, 0x8A0E06); + + /* + * Destroy the temporary bitmap and restore the device context. + */ + + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + DeleteDC(dcMem); + SelectObject(dc, oldBrush); + DeleteObject(stipple); + } else if (gc->function == GXcopy) { + SetTextAlign(dc, TA_LEFT | TA_BASELINE); + SetTextColor(dc, gc->foreground); + SetBkMode(dc, TRANSPARENT); + MultiFontTextOut(dc, fontPtr, source, numBytes, (int)x, (int)y, angle); + } else { + HBITMAP oldBitmap, bitmap; + HDC dcMem; + TEXTMETRIC tm; + SIZE size; + + dcMem = CreateCompatibleDC(dc); + + SetTextAlign(dcMem, TA_LEFT | TA_BASELINE); + SetTextColor(dcMem, gc->foreground); + SetBkMode(dcMem, TRANSPARENT); + SetBkColor(dcMem, RGB(0, 0, 0)); + + /* + * Compute the bounding box and create a compatible bitmap. + */ + + GetTextExtentPointA(dcMem, source, numBytes, &size); + GetTextMetrics(dcMem, &tm); + size.cx -= tm.tmOverhang; + bitmap = CreateCompatibleBitmap(dc, size.cx, size.cy); + oldBitmap = SelectObject(dcMem, bitmap); + + MultiFontTextOut(dcMem, fontPtr, source, numBytes, 0, tm.tmAscent, + angle); + BitBlt(dc, (int)x, (int)y - tm.tmAscent, size.cx, size.cy, dcMem, + 0, 0, (DWORD) tkpWinBltModes[gc->function]); + + /* + * Destroy the temporary bitmap and restore the device context. + */ + + SelectObject(dcMem, oldBitmap); + DeleteObject(bitmap); + DeleteDC(dcMem); + } + TkWinReleaseDrawableDC(drawable, dc, &state); +} + +/* + *--------------------------------------------------------------------------- + * + * TkpDrawCharsInContext -- + * + * Draw a string of characters on the screen like Tk_DrawChars(), but + * with access to all the characters on the line for context. On Windows + * this context isn't consulted, so we just call Tk_DrawChars(). + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. + * + *--------------------------------------------------------------------------- + */ + +void +TkpDrawCharsInContext( + Display *display, /* Display on which to draw. */ + Drawable drawable, /* Window or pixmap in which to draw. */ + GC gc, /* Graphics context for drawing characters. */ + Tk_Font tkfont, /* Font in which characters will be drawn; + * must be the same as font used in GC. */ + const char *source, /* UTF-8 string to be displayed. Need not be + * '\0' terminated. All Tk meta-characters + * (tabs, control characters, and newlines) + * should be stripped out of the string that + * is passed to this function. If they are not + * stripped out, they will be displayed as + * regular printing characters. */ + int numBytes, /* Number of bytes in string. */ + int rangeStart, /* Index of first byte to draw. */ + int rangeLength, /* Length of range to draw in bytes. */ + int x, int y) /* Coordinates at which to place origin of the + * whole (not just the range) string when + * drawing. */ +{ + int widthUntilStart; + + (void) numBytes; /*unused*/ + + Tk_MeasureChars(tkfont, source, rangeStart, -1, 0, &widthUntilStart); + Tk_DrawChars(display, drawable, gc, tkfont, source + rangeStart, + rangeLength, x+widthUntilStart, y); +} + +/* + *------------------------------------------------------------------------- + * + * MultiFontTextOut -- + * + * Helper function for Tk_DrawChars. Draws characters, using the various + * screen fonts in fontPtr to draw multilingual characters. Note: No + * bidirectional support. + * + * Results: + * None. + * + * Side effects: + * Information gets drawn on the screen. Contents of fontPtr may be + * modified if more subfonts were loaded in order to draw all the + * multilingual characters in the given string. + * + *------------------------------------------------------------------------- + */ + +static void +MultiFontTextOut( + HDC hdc, /* HDC to draw into. */ + WinFont *fontPtr, /* Contains set of fonts to use when drawing + * following string. */ + const char *source, /* Potentially multilingual UTF-8 string. */ + int numBytes, /* Length of string in bytes. */ + int x, int y, /* Coordinates at which to place origin of + * string when drawing. */ + double angle) +{ + int ch; + SIZE size; + HFONT oldFont; + FontFamily *familyPtr; + Tcl_DString runString; + const char *p, *end, *next; + SubFont *lastSubFontPtr, *thisSubFontPtr; + TEXTMETRIC tm; + + lastSubFontPtr = &fontPtr->subFontArray[0]; + oldFont = SelectFont(hdc, fontPtr, lastSubFontPtr, angle); + GetTextMetrics(hdc, &tm); + + end = source + numBytes; + for (p = source; p < end; ) { + next = p + TkUtfToUniChar(p, &ch); + thisSubFontPtr = FindSubFontForChar(fontPtr, ch, &lastSubFontPtr); + + /* + * The drawing API has a limit of 32767 pixels in one go. + * To avoid spending time on a rare case we do not measure each char, + * instead we limit to drawing chunks of 200 bytes since that works + * well in practice. + */ + + if ((thisSubFontPtr != lastSubFontPtr) || (p-source > 200)) { + if (p > source) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, + (int) (p - source), &runString); + familyPtr->textOutProc(hdc, x-(tm.tmOverhang/2), y, + (TCHAR *)Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString)>>familyPtr->isWideFont); + familyPtr->getTextExtentPoint32Proc(hdc, + (TCHAR *)Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont, + &size); + x += size.cx; + Tcl_DStringFree(&runString); + } + lastSubFontPtr = thisSubFontPtr; + source = p; + SelectFont(hdc, fontPtr, lastSubFontPtr, angle); + GetTextMetrics(hdc, &tm); + } + p = next; + } + if (p > source) { + familyPtr = lastSubFontPtr->familyPtr; + Tcl_UtfToExternalDString(familyPtr->encoding, source, + (int) (p - source), &runString); + familyPtr->textOutProc(hdc, x-(tm.tmOverhang/2), y, + (TCHAR *)Tcl_DStringValue(&runString), + Tcl_DStringLength(&runString) >> familyPtr->isWideFont); + Tcl_DStringFree(&runString); + } + SelectObject(hdc, oldFont); +} + +static inline HFONT +SelectFont( + HDC hdc, + WinFont *fontPtr, + SubFont *subFontPtr, + double angle) +{ + if (angle == 0.0) { + return SelectObject(hdc, subFontPtr->hFont0); + } else if (angle == subFontPtr->angle) { + return SelectObject(hdc, subFontPtr->hFontAngled); + } else { + if (subFontPtr->hFontAngled) { + DeleteObject(subFontPtr->hFontAngled); + } + subFontPtr->hFontAngled = GetScreenFont(&fontPtr->font.fa, + subFontPtr->familyPtr->faceName, fontPtr->pixelSize, angle); + if (subFontPtr->hFontAngled == NULL) { + return SelectObject(hdc, subFontPtr->hFont0); + } + subFontPtr->angle = angle; + return SelectObject(hdc, subFontPtr->hFontAngled); + } +} + +/* + *--------------------------------------------------------------------------- + * + * InitFont -- + * + * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes(). + * Initializes the memory for a new WinFont that wraps the + * platform-specific data. + * + * The caller is responsible for initializing the fields of the WinFont + * that are used exclusively by the generic TkFont code, and for + * releasing those fields before calling TkpDeleteFont(). + * + * Results: + * Fills the WinFont structure. + * + * Side effects: + * Memory allocated. + * + *--------------------------------------------------------------------------- + */ + +static void +InitFont( + Tk_Window tkwin, /* Main window of interp in which font will be + * used, for getting HDC. */ + HFONT hFont, /* Windows token for font. */ + int overstrike, /* The overstrike attribute of logfont used to + * allocate this font. For some reason, the + * TEXTMETRICs may contain incorrect info in + * the tmStruckOut field. */ + WinFont *fontPtr) /* Filled with information constructed from + * the above arguments. */ +{ + HDC hdc; + HWND hwnd; + HFONT oldFont; + TEXTMETRIC tm; + Window window; + TkFontMetrics *fmPtr; + Tcl_Encoding encoding; + Tcl_DString faceString; + TkFontAttributes *faPtr; + TCHAR buf[LF_FACESIZE]; + + window = Tk_WindowId(tkwin); + hwnd = (window == None) ? NULL : TkWinGetHWND(window); + hdc = GetDC(hwnd); + oldFont = SelectObject(hdc, hFont); + + GetTextMetrics(hdc, &tm); + + /* + * On any version NT, there may fonts with international names. Use the + * NT-only Unicode version of GetTextFace to get the font's name. If we + * used the ANSI version on a non-internationalized version of NT, we + * would get a font name with '?' replacing all the international + * characters. + * + * On a non-internationalized verson of 95, fonts with international names + * are not allowed, so the ANSI version of GetTextFace will work. On an + * internationalized version of 95, there may be fonts with international + * names; the ANSI version will work, fetching the name in the + * international system code page. Can't use the Unicode version of + * GetTextFace because it only exists under NT. + */ + + GetTextFace(hdc, LF_FACESIZE, buf); + Tcl_ExternalToUtfDString(systemEncoding, (char *) buf, -1, &faceString); + + fontPtr->font.fid = (Font) fontPtr; + fontPtr->hwnd = hwnd; + fontPtr->pixelSize = tm.tmHeight - tm.tmInternalLeading; + + faPtr = &fontPtr->font.fa; + faPtr->family = Tk_GetUid(Tcl_DStringValue(&faceString)); + + faPtr->size = + TkFontGetPoints(tkwin, (double)-(fontPtr->pixelSize)); + faPtr->weight = + (tm.tmWeight > FW_MEDIUM) ? TK_FW_BOLD : TK_FW_NORMAL; + faPtr->slant = (tm.tmItalic != 0) ? TK_FS_ITALIC : TK_FS_ROMAN; + faPtr->underline = (tm.tmUnderlined != 0) ? 1 : 0; + faPtr->overstrike = overstrike; + + fmPtr = &fontPtr->font.fm; + fmPtr->ascent = tm.tmAscent; + fmPtr->descent = tm.tmDescent; + fmPtr->maxWidth = tm.tmMaxCharWidth; + fmPtr->fixed = !(tm.tmPitchAndFamily & TMPF_FIXED_PITCH); + + fontPtr->numSubFonts = 1; + fontPtr->subFontArray = fontPtr->staticSubFonts; + InitSubFont(hdc, hFont, 1, &fontPtr->subFontArray[0]); + + encoding = fontPtr->subFontArray[0].familyPtr->encoding; + if (encoding == TkWinGetUnicodeEncoding()) { + GetCharWidth(hdc, 0, BASE_CHARS - 1, fontPtr->widths); + } else { + GetCharWidthA(hdc, 0, BASE_CHARS - 1, fontPtr->widths); + } + Tcl_DStringFree(&faceString); + + SelectObject(hdc, oldFont); + ReleaseDC(hwnd, hdc); +} + +/* + *------------------------------------------------------------------------- + * + * ReleaseFont -- + * + * Called to release the windows-specific contents of a TkFont. The + * caller is responsible for freeing the memory used by the font itself. + * + * Results: + * None. + * + * Side effects: + * Memory is freed. + * + *--------------------------------------------------------------------------- + */ + +static void +ReleaseFont( + WinFont *fontPtr) /* The font to delete. */ +{ + int i; + + for (i = 0; i < fontPtr->numSubFonts; i++) { + ReleaseSubFont(&fontPtr->subFontArray[i]); + } + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree(fontPtr->subFontArray); + } +} + +/* + *------------------------------------------------------------------------- + * + * InitSubFont -- + * + * Wrap a screen font and load the FontFamily that represents it. Used to + * prepare a SubFont so that characters can be mapped from UTF-8 to the + * charset of the font. + * + * Results: + * The subFontPtr is filled with information about the font. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static inline void +InitSubFont( + HDC hdc, /* HDC in which font can be selected. */ + HFONT hFont, /* The screen font. */ + int base, /* Non-zero if this SubFont is being used as + * the base font for a font object. */ + SubFont *subFontPtr) /* Filled with SubFont constructed from above + * attributes. */ +{ + subFontPtr->hFont0 = hFont; + subFontPtr->familyPtr = AllocFontFamily(hdc, hFont, base); + subFontPtr->fontMap = subFontPtr->familyPtr->fontMap; + subFontPtr->hFontAngled = NULL; + subFontPtr->angle = 0.0; +} + +/* + *------------------------------------------------------------------------- + * + * ReleaseSubFont -- + * + * Called to release the contents of a SubFont. The caller is responsible + * for freeing the memory used by the SubFont itself. + * + * Results: + * None. + * + * Side effects: + * Memory and resources are freed. + * + *--------------------------------------------------------------------------- + */ + +static inline void +ReleaseSubFont( + SubFont *subFontPtr) /* The SubFont to delete. */ +{ + DeleteObject(subFontPtr->hFont0); + if (subFontPtr->hFontAngled) { + DeleteObject(subFontPtr->hFontAngled); + } + FreeFontFamily(subFontPtr->familyPtr); +} + +/* + *------------------------------------------------------------------------- + * + * AllocFontFamily -- + * + * Find the FontFamily structure associated with the given font name. The + * information should be stored by the caller in a SubFont and used when + * determining if that SubFont supports a character. + * + * Cannot use the string name used to construct the font as the key, + * because the capitalization may not be canonical. Therefore use the + * face name actually retrieved from the font metrics as the key. + * + * Results: + * A pointer to a FontFamily. The reference count in the FontFamily is + * automatically incremented. When the SubFont is released, the reference + * count is decremented. When no SubFont is using this FontFamily, it may + * be deleted. + * + * Side effects: + * A new FontFamily structure will be allocated if this font family has + * not been seen. TrueType character existence metrics are loaded into + * the FontFamily structure. + * + *------------------------------------------------------------------------- + */ + +static FontFamily * +AllocFontFamily( + HDC hdc, /* HDC in which font can be selected. */ + HFONT hFont, /* Screen font whose FontFamily is to be + * returned. */ + int base) /* Non-zero if this font family is to be used + * in the base font of a font object. */ +{ + Tk_Uid faceName; + FontFamily *familyPtr; + Tcl_DString faceString; + Tcl_Encoding encoding; + TCHAR buf[LF_FACESIZE]; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + hFont = SelectObject(hdc, hFont); + GetTextFace(hdc, LF_FACESIZE, buf); + Tcl_ExternalToUtfDString(systemEncoding, (char *) buf, -1, &faceString); + faceName = Tk_GetUid(Tcl_DStringValue(&faceString)); + Tcl_DStringFree(&faceString); + hFont = SelectObject(hdc, hFont); + + familyPtr = tsdPtr->fontFamilyList; + for ( ; familyPtr != NULL; familyPtr = familyPtr->nextPtr) { + if (familyPtr->faceName == faceName) { + familyPtr->refCount++; + return familyPtr; + } + } + + familyPtr = ckalloc(sizeof(FontFamily)); + memset(familyPtr, 0, sizeof(FontFamily)); + familyPtr->nextPtr = tsdPtr->fontFamilyList; + tsdPtr->fontFamilyList = familyPtr; + + /* + * Set key for this FontFamily. + */ + + familyPtr->faceName = faceName; + + /* + * An initial refCount of 2 means that FontFamily information will persist + * even when the SubFont that loaded the FontFamily is released. Change it + * to 1 to cause FontFamilies to be unloaded when not in use. + */ + + familyPtr->refCount = 2; + + familyPtr->segCount = LoadFontRanges(hdc, hFont, &familyPtr->startCount, + &familyPtr->endCount, &familyPtr->isSymbolFont); + + encoding = NULL; + if (familyPtr->isSymbolFont != 0) { + /* + * Symbol fonts are handled specially. For instance, Unicode 0393 + * (GREEK CAPITAL GAMMA) must be mapped to Symbol character 0047 + * (GREEK CAPITAL GAMMA), because the Symbol font doesn't have a GREEK + * CAPITAL GAMMA at location 0393. If Tk interpreted the Symbol font + * using the Unicode encoding, it would decide that the Symbol font + * has no GREEK CAPITAL GAMMA, because the Symbol encoding (of course) + * reports that character 0393 doesn't exist. + * + * With non-symbol Windows fonts, such as Times New Roman, if the font + * has a GREEK CAPITAL GAMMA, it will be found in the correct Unicode + * location (0393); the GREEK CAPITAL GAMMA will not be off hiding at + * some other location. + */ + + encoding = Tcl_GetEncoding(NULL, faceName); + } + + if (encoding == NULL) { + encoding = Tcl_GetEncoding(NULL, "unicode"); + familyPtr->textOutProc = + (BOOL (WINAPI *)(HDC, int, int, TCHAR *, int)) TextOutW; + familyPtr->getTextExtentPoint32Proc = + (BOOL (WINAPI *)(HDC, TCHAR *, int, LPSIZE)) GetTextExtentPoint32W; + familyPtr->isWideFont = 1; + } else { + familyPtr->textOutProc = + (BOOL (WINAPI *)(HDC, int, int, TCHAR *, int)) TextOutA; + familyPtr->getTextExtentPoint32Proc = + (BOOL (WINAPI *)(HDC, TCHAR *, int, LPSIZE)) GetTextExtentPoint32A; + familyPtr->isWideFont = 0; + } + + familyPtr->encoding = encoding; + + return familyPtr; +} + +/* + *------------------------------------------------------------------------- + * + * FreeFontFamily -- + * + * Called to free a FontFamily when the SubFont is finished using it. + * Frees the contents of the FontFamily and the memory used by the + * FontFamily itself. + * + * Results: + * None. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static void +FreeFontFamily( + FontFamily *familyPtr) /* The FontFamily to delete. */ +{ + int i; + FontFamily **familyPtrPtr; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (familyPtr == NULL) { + return; + } + if (familyPtr->refCount-- > 1) { + return; + } + for (i = 0; i < FONTMAP_PAGES; i++) { + if (familyPtr->fontMap[i] != NULL) { + ckfree(familyPtr->fontMap[i]); + } + } + if (familyPtr->startCount != NULL) { + ckfree(familyPtr->startCount); + } + if (familyPtr->endCount != NULL) { + ckfree(familyPtr->endCount); + } + if (familyPtr->encoding != TkWinGetUnicodeEncoding()) { + Tcl_FreeEncoding(familyPtr->encoding); + } + + /* + * Delete from list. + */ + + for (familyPtrPtr = &tsdPtr->fontFamilyList; ; ) { + if (*familyPtrPtr == familyPtr) { + *familyPtrPtr = familyPtr->nextPtr; + break; + } + familyPtrPtr = &(*familyPtrPtr)->nextPtr; + } + + ckfree(familyPtr); +} + +/* + *------------------------------------------------------------------------- + * + * FindSubFontForChar -- + * + * Determine which screen font is necessary to use to display the given + * character. If the font object does not have a screen font that can + * display the character, another screen font may be loaded into the font + * object, following a set of preferred fallback rules. + * + * Results: + * The return value is the SubFont to use to display the given character. + * + * Side effects: + * The contents of fontPtr are modified to cache the results of the + * lookup and remember any SubFonts that were dynamically loaded. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +FindSubFontForChar( + WinFont *fontPtr, /* The font object with which the character + * will be displayed. */ + int ch, /* The Unicode character to be displayed. */ + SubFont **subFontPtrPtr) /* Pointer to var to be fixed up if we + * reallocate the subfont table. */ +{ + HDC hdc; + int i, j, k; + CanUse canUse; + const char *const *aliases; + const char *const *anyFallbacks; + const char *const *const *fontFallbacks; + const char *fallbackName; + SubFont *subFontPtr; + Tcl_DString ds; + + + if ((ch < BASE_CHARS) || (ch >= 0x10000)) { + return &fontPtr->subFontArray[0]; + } + + for (i = 0; i < fontPtr->numSubFonts; i++) { + if (FontMapLookup(&fontPtr->subFontArray[i], ch)) { + return &fontPtr->subFontArray[i]; + } + } + + /* + * Keep track of all face names that we check, so we don't check some name + * multiple times if it can be reached by multiple paths. + */ + + Tcl_DStringInit(&ds); + hdc = GetDC(fontPtr->hwnd); + + aliases = TkFontGetAliasList(fontPtr->font.fa.family); + + fontFallbacks = TkFontGetFallbacks(); + for (i = 0; fontFallbacks[i] != NULL; i++) { + for (j = 0; fontFallbacks[i][j] != NULL; j++) { + fallbackName = fontFallbacks[i][j]; + if (strcasecmp(fallbackName, fontPtr->font.fa.family) == 0) { + /* + * If the base font has a fallback... + */ + + goto tryfallbacks; + } else if (aliases != NULL) { + /* + * Or if an alias for the base font has a fallback... + */ + + for (k = 0; aliases[k] != NULL; k++) { + if (strcasecmp(aliases[k], fallbackName) == 0) { + goto tryfallbacks; + } + } + } + } + continue; + + /* + * ...then see if we can use one of the fallbacks, or an alias for one + * of the fallbacks. + */ + + tryfallbacks: + for (j = 0; fontFallbacks[i][j] != NULL; j++) { + fallbackName = fontFallbacks[i][j]; + subFontPtr = CanUseFallbackWithAliases(hdc, fontPtr, fallbackName, + ch, &ds, subFontPtrPtr); + if (subFontPtr != NULL) { + goto end; + } + } + } + + /* + * See if we can use something from the global fallback list. + */ + + anyFallbacks = TkFontGetGlobalClass(); + for (i = 0; anyFallbacks[i] != NULL; i++) { + fallbackName = anyFallbacks[i]; + subFontPtr = CanUseFallbackWithAliases(hdc, fontPtr, fallbackName, + ch, &ds, subFontPtrPtr); + if (subFontPtr != NULL) { + goto end; + } + } + + /* + * Try all face names available in the whole system until we find one that + * can be used. + */ + + canUse.hdc = hdc; + canUse.fontPtr = fontPtr; + canUse.nameTriedPtr = &ds; + canUse.ch = ch; + canUse.subFontPtr = NULL; + canUse.subFontPtrPtr = subFontPtrPtr; + EnumFontFamilies(hdc, NULL, (FONTENUMPROC) WinFontCanUseProc, + (LPARAM) &canUse); + subFontPtr = canUse.subFontPtr; + + end: + Tcl_DStringFree(&ds); + + if (subFontPtr == NULL) { + /* + * No font can display this character. We will use the base font and + * have it display the "unknown" character. + */ + + subFontPtr = &fontPtr->subFontArray[0]; + FontMapInsert(subFontPtr, ch); + } + ReleaseDC(fontPtr->hwnd, hdc); + return subFontPtr; +} + +static int CALLBACK +WinFontCanUseProc( + ENUMLOGFONT *lfPtr, /* Logical-font data. */ + NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */ + int fontType, /* Type of font (not used). */ + LPARAM lParam) /* Result object to hold result. */ +{ + int ch; + HDC hdc; + WinFont *fontPtr; + CanUse *canUsePtr; + char *fallbackName; + SubFont *subFontPtr; + Tcl_DString faceString; + Tcl_DString *nameTriedPtr; + + canUsePtr = (CanUse *) lParam; + ch = canUsePtr->ch; + hdc = canUsePtr->hdc; + fontPtr = canUsePtr->fontPtr; + nameTriedPtr = canUsePtr->nameTriedPtr; + + fallbackName = (char *) lfPtr->elfLogFont.lfFaceName; + Tcl_ExternalToUtfDString(systemEncoding, fallbackName, -1, &faceString); + fallbackName = Tcl_DStringValue(&faceString); + + if (SeenName(fallbackName, nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(hdc, fontPtr, fallbackName, ch, + canUsePtr->subFontPtrPtr); + if (subFontPtr != NULL) { + canUsePtr->subFontPtr = subFontPtr; + Tcl_DStringFree(&faceString); + return 0; + } + } + Tcl_DStringFree(&faceString); + return 1; +} + +/* + *------------------------------------------------------------------------- + * + * FontMapLookup -- + * + * See if the screen font can display the given character. + * + * Results: + * The return value is 0 if the screen font cannot display the character, + * non-zero otherwise. + * + * Side effects: + * New pages are added to the font mapping cache whenever the character + * belongs to a page that hasn't been seen before. When a page is loaded, + * information about all the characters on that page is stored, not just + * for the single character in question. + * + *------------------------------------------------------------------------- + */ + +static int +FontMapLookup( + SubFont *subFontPtr, /* Contains font mapping cache to be queried + * and possibly updated. */ + int ch) /* Character to be tested. */ +{ + int row, bitOffset; + + row = ch >> FONTMAP_SHIFT; + if (subFontPtr->fontMap[row] == NULL) { + FontMapLoadPage(subFontPtr, row); + } + bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); + return (subFontPtr->fontMap[row][bitOffset >> 3] >> (bitOffset & 7)) & 1; +} + +/* + *------------------------------------------------------------------------- + * + * FontMapInsert -- + * + * Tell the font mapping cache that the given screen font should be used + * to display the specified character. This is called when no font on the + * system can be be found that can display that character; we lie to the + * font and tell it that it can display the character, otherwise we would + * end up re-searching the entire fallback hierarchy every time that + * character was seen. + * + * Results: + * None. + * + * Side effects: + * New pages are added to the font mapping cache whenever the character + * belongs to a page that hasn't been seen before. When a page is loaded, + * information about all the characters on that page is stored, not just + * for the single character in question. + * + *------------------------------------------------------------------------- + */ + +static void +FontMapInsert( + SubFont *subFontPtr, /* Contains font mapping cache to be + * updated. */ + int ch) /* Character to be added to cache. */ +{ + int row, bitOffset; + + row = ch >> FONTMAP_SHIFT; + if (subFontPtr->fontMap[row] == NULL) { + FontMapLoadPage(subFontPtr, row); + } + bitOffset = ch & (FONTMAP_BITSPERPAGE - 1); + subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7); +} + +/* + *------------------------------------------------------------------------- + * + * FontMapLoadPage -- + * + * Load information about all the characters on a given page. This + * information consists of one bit per character that indicates whether + * the associated HFONT can (1) or cannot (0) display the characters on + * the page. + * + * Results: + * None. + * + * Side effects: + * Mempry allocated. + * + *------------------------------------------------------------------------- + */ + +static void +FontMapLoadPage( + SubFont *subFontPtr, /* Contains font mapping cache to be + * updated. */ + int row) /* Index of the page to be loaded into the + * cache. */ +{ + FontFamily *familyPtr; + Tcl_Encoding encoding; + char src[XMaxTransChars], buf[16]; + USHORT *startCount, *endCount; + int i, j, bitOffset, end, segCount; + + subFontPtr->fontMap[row] = ckalloc(FONTMAP_BITSPERPAGE / 8); + memset(subFontPtr->fontMap[row], 0, FONTMAP_BITSPERPAGE / 8); + + familyPtr = subFontPtr->familyPtr; + encoding = familyPtr->encoding; + + if (familyPtr->encoding == TkWinGetUnicodeEncoding()) { + /* + * Font is Unicode. Few fonts are going to have all characters, so + * examine the TrueType character existence metrics to determine what + * characters actually exist in this font. + */ + + segCount = familyPtr->segCount; + startCount = familyPtr->startCount; + endCount = familyPtr->endCount; + + j = 0; + end = (row + 1) << FONTMAP_SHIFT; + for (i = row << FONTMAP_SHIFT; i < end; i++) { + for ( ; j < segCount; j++) { + if (endCount[j] >= i) { + if (startCount[j] <= i) { + bitOffset = i & (FONTMAP_BITSPERPAGE - 1); + subFontPtr->fontMap[row][bitOffset >> 3] |= + 1 << (bitOffset & 7); + } + break; + } + } + } + } else if (familyPtr->isSymbolFont) { + /* + * Assume that a symbol font with a known encoding has all the + * characters that its encoding claims it supports. + * + * The test for "encoding == unicodeEncoding" must occur before this + * case, to catch all symbol fonts (such as {Comic Sans MS} or + * Wingdings) for which we don't have encoding information; those + * symbol fonts are treated as if they were in the Unicode encoding + * and their symbolic character existence metrics are treated as if + * they were Unicode character existence metrics. This way, although + * we don't know the proper Unicode -> symbol font mapping, we can + * install the symbol font as the base font and access its glyphs. + */ + + end = (row + 1) << FONTMAP_SHIFT; + for (i = row << FONTMAP_SHIFT; i < end; i++) { + if (Tcl_UtfToExternal(NULL, encoding, src, + Tcl_UniCharToUtf(i, src), TCL_ENCODING_STOPONERROR, NULL, + buf, sizeof(buf), NULL, NULL, NULL) != TCL_OK) { + continue; + } + bitOffset = i & (FONTMAP_BITSPERPAGE - 1); + subFontPtr->fontMap[row][bitOffset >> 3] |= 1 << (bitOffset & 7); + } + } +} + +/* + *--------------------------------------------------------------------------- + * + * CanUseFallbackWithAliases -- + * + * Helper function for FindSubFontForChar. Determine if the specified + * face name (or an alias of the specified face name) can be used to + * construct a screen font that can display the given character. + * + * Results: + * See CanUseFallback(). + * + * Side effects: + * If the name and/or one of its aliases was rejected, the rejected + * string is recorded in nameTriedPtr so that it won't be tried again. + * + *--------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallbackWithAliases( + HDC hdc, /* HDC in which font can be selected. */ + WinFont *fontPtr, /* The font object that will own the new + * screen font. */ + const char *faceName, /* Desired face name for new screen font. */ + int ch, /* The Unicode character that the new screen + * font must be able to display. */ + Tcl_DString *nameTriedPtr, /* Records face names that have already been + * tried. It is possible for the same face + * name to be queried multiple times when + * trying to find a suitable screen font. */ + SubFont **subFontPtrPtr) /* Variable to fixup if we reallocate the + * array of subfonts. */ +{ + int i; + const char *const *aliases; + SubFont *subFontPtr; + + if (SeenName(faceName, nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(hdc, fontPtr, faceName, ch, subFontPtrPtr); + if (subFontPtr != NULL) { + return subFontPtr; + } + } + aliases = TkFontGetAliasList(faceName); + if (aliases != NULL) { + for (i = 0; aliases[i] != NULL; i++) { + if (SeenName(aliases[i], nameTriedPtr) == 0) { + subFontPtr = CanUseFallback(hdc, fontPtr, aliases[i], ch, + subFontPtrPtr); + if (subFontPtr != NULL) { + return subFontPtr; + } + } + } + } + return NULL; +} + +/* + *--------------------------------------------------------------------------- + * + * SeenName -- + * + * Used to determine we have already tried and rejected the given face + * name when looking for a screen font that can support some Unicode + * character. + * + * Results: + * The return value is 0 if this face name has not already been seen, + * non-zero otherwise. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static int +SeenName( + const char *name, /* The name to check. */ + Tcl_DString *dsPtr) /* Contains names that have already been + * seen. */ +{ + const char *seen, *end; + + seen = Tcl_DStringValue(dsPtr); + end = seen + Tcl_DStringLength(dsPtr); + while (seen < end) { + if (strcasecmp(seen, name) == 0) { + return 1; + } + seen += strlen(seen) + 1; + } + Tcl_DStringAppend(dsPtr, name, (int) (strlen(name) + 1)); + return 0; +} + +/* + *------------------------------------------------------------------------- + * + * CanUseFallback -- + * + * If the specified screen font has not already been loaded into the font + * object, determine if it can display the given character. + * + * Results: + * The return value is a pointer to a newly allocated SubFont, owned by + * the font object. This SubFont can be used to display the given + * character. The SubFont represents the screen font with the base set of + * font attributes from the font object, but using the specified font + * name. NULL is returned if the font object already holds a reference to + * the specified physical font or if the specified physical font cannot + * display the given character. + * + * Side effects: + * The font object's subFontArray is updated to contain a reference to + * the newly allocated SubFont. + * + *------------------------------------------------------------------------- + */ + +static SubFont * +CanUseFallback( + HDC hdc, /* HDC in which font can be selected. */ + WinFont *fontPtr, /* The font object that will own the new + * screen font. */ + const char *faceName, /* Desired face name for new screen font. */ + int ch, /* The Unicode character that the new screen + * font must be able to display. */ + SubFont **subFontPtrPtr) /* Variable to fix-up if we realloc the array + * of subfonts. */ +{ + int i; + HFONT hFont; + SubFont subFont; + + if (FamilyExists(hdc, faceName) == 0) { + return NULL; + } + + /* + * Skip all fonts we've already used. + */ + + for (i = 0; i < fontPtr->numSubFonts; i++) { + if (faceName == fontPtr->subFontArray[i].familyPtr->faceName) { + return NULL; + } + } + + /* + * Load this font and see if it has the desired character. + */ + + hFont = GetScreenFont(&fontPtr->font.fa, faceName, fontPtr->pixelSize, + 0.0); + InitSubFont(hdc, hFont, 0, &subFont); + if (((ch < 256) && (subFont.familyPtr->isSymbolFont)) + || (FontMapLookup(&subFont, ch) == 0)) { + /* + * Don't use a symbol font as a fallback font for characters below + * 256. + */ + + ReleaseSubFont(&subFont); + return NULL; + } + + if (fontPtr->numSubFonts >= SUBFONT_SPACE) { + SubFont *newPtr; + + newPtr = ckalloc(sizeof(SubFont) * (fontPtr->numSubFonts + 1)); + memcpy(newPtr, fontPtr->subFontArray, + fontPtr->numSubFonts * sizeof(SubFont)); + if (fontPtr->subFontArray != fontPtr->staticSubFonts) { + ckfree(fontPtr->subFontArray); + } + + /* + * Fix up the variable pointed to by subFontPtrPtr so it still points + * into the live array. [Bug 618872] + */ + + *subFontPtrPtr = newPtr + (*subFontPtrPtr - fontPtr->subFontArray); + fontPtr->subFontArray = newPtr; + } + fontPtr->subFontArray[fontPtr->numSubFonts] = subFont; + fontPtr->numSubFonts++; + return &fontPtr->subFontArray[fontPtr->numSubFonts - 1]; +} + +/* + *--------------------------------------------------------------------------- + * + * GetScreenFont -- + * + * Given the name and other attributes, construct an HFONT. This is where + * all the alias and fallback substitution bottoms out. + * + * Results: + * The screen font that corresponds to the attributes. + * + * Side effects: + * None. + * + *--------------------------------------------------------------------------- + */ + +static HFONT +GetScreenFont( + const TkFontAttributes *faPtr, + /* Desired font attributes for new HFONT. */ + const char *faceName, /* Overrides font family specified in font + * attributes. */ + int pixelSize, /* Overrides size specified in font + * attributes. */ + double angle) /* What is the desired orientation of the + * font. */ +{ + Tcl_DString ds; + HFONT hFont; + LOGFONT lf; + + memset(&lf, 0, sizeof(lf)); + lf.lfHeight = -pixelSize; + lf.lfWidth = 0; + lf.lfEscapement = ROUND16(angle * 10); + lf.lfOrientation = ROUND16(angle * 10); + lf.lfWeight = (faPtr->weight == TK_FW_NORMAL) ? FW_NORMAL : FW_BOLD; + lf.lfItalic = faPtr->slant; + lf.lfUnderline = faPtr->underline; + lf.lfStrikeOut = faPtr->overstrike; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfOutPrecision = OUT_TT_PRECIS; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfQuality = DEFAULT_QUALITY; + lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + + Tcl_UtfToExternalDString(systemEncoding, faceName, -1, &ds); + _tcsncpy(lf.lfFaceName, (TCHAR *)Tcl_DStringValue(&ds), LF_FACESIZE-1); + Tcl_DStringFree(&ds); + lf.lfFaceName[LF_FACESIZE-1] = 0; + hFont = CreateFontIndirect(&lf); + return hFont; +} + +/* + *------------------------------------------------------------------------- + * + * FamilyExists, FamilyOrAliasExists, WinFontExistsProc -- + * + * Determines if any physical screen font exists on the system with the + * given family name. If the family exists, then it should be possible to + * construct some physical screen font with that family name. + * + * Results: + * The return value is 0 if the specified font family does not exist, + * non-zero otherwise. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static int +FamilyExists( + HDC hdc, /* HDC in which font family will be used. */ + const char *faceName) /* Font family to query. */ +{ + int result; + Tcl_DString faceString; + + /* + * Just immediately rule out the following fonts, because they look so + * ugly on windows. The caller's fallback mechanism will cause the + * corresponding appropriate TrueType fonts to be selected. + */ + + if (strcasecmp(faceName, "Courier") == 0) { + return 0; + } + if (strcasecmp(faceName, "Times") == 0) { + return 0; + } + if (strcasecmp(faceName, "Helvetica") == 0) { + return 0; + } + + Tcl_UtfToExternalDString(systemEncoding, faceName, -1, &faceString); + + /* + * If the family exists, WinFontExistProc() will be called and + * EnumFontFamilies() will return whatever WinFontExistProc() returns. If + * the family doesn't exist, EnumFontFamilies() will just return a + * non-zero value. + */ + + result = EnumFontFamilies(hdc, (TCHAR*) Tcl_DStringValue(&faceString), + (FONTENUMPROC) WinFontExistProc, 0); + Tcl_DStringFree(&faceString); + return (result == 0); +} + +static const char * +FamilyOrAliasExists( + HDC hdc, + const char *faceName) +{ + const char *const *aliases; + int i; + + if (FamilyExists(hdc, faceName) != 0) { + return faceName; + } + aliases = TkFontGetAliasList(faceName); + if (aliases != NULL) { + for (i = 0; aliases[i] != NULL; i++) { + if (FamilyExists(hdc, aliases[i]) != 0) { + return aliases[i]; + } + } + } + return NULL; +} + +static int CALLBACK +WinFontExistProc( + ENUMLOGFONT *lfPtr, /* Logical-font data. */ + NEWTEXTMETRIC *tmPtr, /* Physical-font data (not used). */ + int fontType, /* Type of font (not used). */ + LPARAM lParam) /* EnumFontData to hold result. */ +{ + return 0; +} + +/* + * The following data structures are used when querying a TrueType font file + * to determine which characters the font supports. + */ + +#pragma pack(1) /* Structures are byte aligned in file. */ + +#define CMAPHEX 0x636d6170 /* Key for character map resource. */ + +typedef struct CMAPTABLE { + USHORT version; /* Table version number (0). */ + USHORT numTables; /* Number of encoding tables following. */ +} CMAPTABLE; + +typedef struct ENCODINGTABLE { + USHORT platform; /* Platform for which data is targeted. 3 + * means data is for Windows. */ + USHORT encoding; /* How characters in font are encoded. 1 means + * that the following subtable is keyed based + * on Unicode. */ + ULONG offset; /* Byte offset from beginning of CMAPTABLE to + * the subtable for this encoding. */ +} ENCODINGTABLE; + +typedef struct ANYTABLE { + USHORT format; /* Format number. */ + USHORT length; /* The actual length in bytes of this + * subtable. */ + USHORT version; /* Version number (starts at 0). */ +} ANYTABLE; + +typedef struct BYTETABLE { + USHORT format; /* Format number is set to 0. */ + USHORT length; /* The actual length in bytes of this + * subtable. */ + USHORT version; /* Version number (starts at 0). */ + BYTE glyphIdArray[256]; /* Array that maps up to 256 single-byte char + * codes to glyph indices. */ +} BYTETABLE; + +typedef struct SUBHEADER { + USHORT firstCode; /* First valid low byte for subHeader. */ + USHORT entryCount; /* Number valid low bytes for subHeader. */ + SHORT idDelta; /* Constant adder to get base glyph index. */ + USHORT idRangeOffset; /* Byte offset from here to appropriate + * glyphIndexArray. */ +} SUBHEADER; + +typedef struct HIBYTETABLE { + USHORT format; /* Format number is set to 2. */ + USHORT length; /* The actual length in bytes of this + * subtable. */ + USHORT version; /* Version number (starts at 0). */ + USHORT subHeaderKeys[256]; /* Maps high bytes to subHeaders: value is + * subHeader index * 8. */ +#if 0 + SUBHEADER subHeaders[]; /* Variable-length array of SUBHEADERs. */ + USHORT glyphIndexArray[]; /* Variable-length array containing subarrays + * used for mapping the low byte of 2-byte + * characters. */ +#endif +} HIBYTETABLE; + +typedef struct SEGMENTTABLE { + USHORT format; /* Format number is set to 4. */ + USHORT length; /* The actual length in bytes of this + * subtable. */ + USHORT version; /* Version number (starts at 0). */ + USHORT segCountX2; /* 2 x segCount. */ + USHORT searchRange; /* 2 x (2**floor(log2(segCount))). */ + USHORT entrySelector; /* log2(searchRange/2). */ + USHORT rangeShift; /* 2 x segCount - searchRange. */ +#if 0 + USHORT endCount[segCount] /* End characterCode for each segment. */ + USHORT reservedPad; /* Set to 0. */ + USHORT startCount[segCount];/* Start character code for each segment. */ + USHORT idDelta[segCount]; /* Delta for all character in segment. */ + USHORT idRangeOffset[segCount]; /* Offsets into glyphIdArray or 0. */ + USHORT glyphIdArray[] /* Glyph index array. */ +#endif +} SEGMENTTABLE; + +typedef struct TRIMMEDTABLE { + USHORT format; /* Format number is set to 6. */ + USHORT length; /* The actual length in bytes of this + * subtable. */ + USHORT version; /* Version number (starts at 0). */ + USHORT firstCode; /* First character code of subrange. */ + USHORT entryCount; /* Number of character codes in subrange. */ +#if 0 + USHORT glyphIdArray[]; /* Array of glyph index values for + * character codes in the range. */ +#endif +} TRIMMEDTABLE; + +typedef union SUBTABLE { + ANYTABLE any; + BYTETABLE byte; + HIBYTETABLE hiByte; + SEGMENTTABLE segment; + TRIMMEDTABLE trimmed; +} SUBTABLE; + +#pragma pack() + +/* + *------------------------------------------------------------------------- + * + * LoadFontRanges -- + * + * Given an HFONT, get the information about the characters that this + * font can display. + * + * Results: + * If the font has no Unicode character information, the return value is + * 0 and *startCountPtr and *endCountPtr are filled with NULL. Otherwise, + * *startCountPtr and *endCountPtr are set to pointers to arrays of + * TrueType character existence information and the return value is the + * length of the arrays (the two arrays are always the same length as + * each other). + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static int +LoadFontRanges( + HDC hdc, /* HDC into which font can be selected. */ + HFONT hFont, /* HFONT to query. */ + USHORT **startCountPtr, /* Filled with malloced pointer to character + * range information. */ + USHORT **endCountPtr, /* Filled with malloced pointer to character + * range information. */ + int *symbolPtr) + { + int n, i, swapped, offset, cbData, segCount; + DWORD cmapKey; + USHORT *startCount, *endCount; + CMAPTABLE cmapTable; + ENCODINGTABLE encTable; + SUBTABLE subTable; + char *s; + + segCount = 0; + startCount = NULL; + endCount = NULL; + *symbolPtr = 0; + + hFont = SelectObject(hdc, hFont); + + i = 0; + s = (char *) &i; + *s = '\1'; + swapped = 0; + + if (i == 1) { + swapped = 1; + } + + cmapKey = CMAPHEX; + if (swapped) { + SwapLong(&cmapKey); + } + + n = GetFontData(hdc, cmapKey, 0, &cmapTable, sizeof(cmapTable)); + if (n != (int) GDI_ERROR) { + if (swapped) { + SwapShort(&cmapTable.numTables); + } + for (i = 0; i < cmapTable.numTables; i++) { + offset = sizeof(cmapTable) + i * sizeof(encTable); + GetFontData(hdc, cmapKey, (DWORD) offset, &encTable, + sizeof(encTable)); + if (swapped) { + SwapShort(&encTable.platform); + SwapShort(&encTable.encoding); + SwapLong(&encTable.offset); + } + if (encTable.platform != 3) { + /* + * Not Microsoft encoding. + */ + + continue; + } + if (encTable.encoding == 0) { + *symbolPtr = 1; + } else if (encTable.encoding != 1) { + continue; + } + + GetFontData(hdc, cmapKey, (DWORD) encTable.offset, &subTable, + sizeof(subTable)); + if (swapped) { + SwapShort(&subTable.any.format); + } + if (subTable.any.format == 4) { + if (swapped) { + SwapShort(&subTable.segment.segCountX2); + } + segCount = subTable.segment.segCountX2 / 2; + cbData = segCount * sizeof(USHORT); + + startCount = ckalloc(cbData); + endCount = ckalloc(cbData); + + offset = encTable.offset + sizeof(subTable.segment); + GetFontData(hdc, cmapKey, (DWORD) offset, endCount, cbData); + offset += cbData + sizeof(USHORT); + GetFontData(hdc, cmapKey, (DWORD) offset, startCount, cbData); + if (swapped) { + for (i = 0; i < segCount; i++) { + SwapShort(&endCount[i]); + SwapShort(&startCount[i]); + } + } + if (*symbolPtr != 0) { + /* + * Empirically determined: When a symbol font is loaded, + * the character existence metrics obtained from the + * system are mildly wrong. If the real range of the + * symbol font is from 0020 to 00FE, then the metrics are + * reported as F020 to F0FE. When we load a symbol font, + * we must fix the character existence metrics. + * + * Symbol fonts should only use the symbol encoding for + * 8-bit characters [note Bug: 2406] + */ + + for (i = 0; i < segCount; i++) { + if (((startCount[i] & 0xff00) == 0xf000) + && ((endCount[i] & 0xff00) == 0xf000)) { + startCount[i] &= 0xff; + endCount[i] &= 0xff; + } + } + } + } + } + } else if (GetTextCharset(hdc) == ANSI_CHARSET) { + /* + * Bitmap font. We should also support ranges for the other *_CHARSET + * values. + */ + + segCount = 1; + cbData = segCount * sizeof(USHORT); + startCount = ckalloc(cbData); + endCount = ckalloc(cbData); + startCount[0] = 0x0000; + endCount[0] = 0x00ff; + } + SelectObject(hdc, hFont); + + *startCountPtr = startCount; + *endCountPtr = endCount; + return segCount; +} + +/* + *------------------------------------------------------------------------- + * + * SwapShort, SwapLong -- + * + * Helper functions to convert the data loaded from TrueType font files + * to Intel byte ordering. + * + * Results: + * Bytes of input value are swapped and stored back in argument. + * + * Side effects: + * None. + * + *------------------------------------------------------------------------- + */ + +static inline void +SwapShort( + PUSHORT p) +{ + *p = (SHORT)(HIBYTE(*p) + (LOBYTE(*p) << 8)); +} + +static inline void +SwapLong( + PULONG p) +{ + ULONG temp; + + temp = (LONG) ((BYTE) *p); + temp <<= 8; + *p >>=8; + + temp += (LONG) ((BYTE) *p); + temp <<= 8; + *p >>=8; + + temp += (LONG) ((BYTE) *p); + temp <<= 8; + *p >>=8; + + temp += (LONG) ((BYTE) *p); + *p = temp; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinImage.c b/tk8.6/win/tkWinImage.c new file mode 100644 index 0000000..d61b84a --- /dev/null +++ b/tk8.6/win/tkWinImage.c @@ -0,0 +1,696 @@ +/* + * tkWinImage.c -- + * + * This file contains routines for manipulation full-color images. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +static int DestroyImage(XImage* data); +static unsigned long ImageGetPixel(XImage *image, int x, int y); +static int PutPixel(XImage *image, int x, int y, + unsigned long pixel); + +/* + *---------------------------------------------------------------------- + * + * DestroyImage -- + * + * This is a trivial wrapper around ckfree to make it possible to pass + * ckfree as a pointer. + * + * Results: + * None. + * + * Side effects: + * Deallocates the image. + * + *---------------------------------------------------------------------- + */ + +static int +DestroyImage( + XImage *imagePtr) /* Image to free. */ +{ + if (imagePtr) { + if (imagePtr->data) { + ckfree(imagePtr->data); + } + ckfree(imagePtr); + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * ImageGetPixel -- + * + * Get a single pixel from an image. + * + * Results: + * Returns the 32 bit pixel value. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static unsigned long +ImageGetPixel( + XImage *image, + int x, int y) +{ + unsigned long pixel = 0; + unsigned char *srcPtr = (unsigned char *) &(image->data[(y * image->bytes_per_line) + + ((x * image->bits_per_pixel) / NBBY)]); + + switch (image->bits_per_pixel) { + case 32: + case 24: + pixel = RGB(srcPtr[2], srcPtr[1], srcPtr[0]); + break; + case 16: + pixel = RGB(((((WORD*)srcPtr)[0]) >> 7) & 0xf8, + ((((WORD*)srcPtr)[0]) >> 2) & 0xf8, + ((((WORD*)srcPtr)[0]) << 3) & 0xf8); + break; + case 8: + pixel = srcPtr[0]; + break; + case 4: + pixel = ((x%2) ? (*srcPtr) : ((*srcPtr) >> 4)) & 0x0f; + break; + case 1: + pixel = ((*srcPtr) & (0x80 >> (x%8))) ? 1 : 0; + break; + } + return pixel; +} + +/* + *---------------------------------------------------------------------- + * + * PutPixel -- + * + * Set a single pixel in an image. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +PutPixel( + XImage *image, + int x, int y, + unsigned long pixel) +{ + unsigned char *destPtr = (unsigned char *) &(image->data[(y * image->bytes_per_line) + + ((x * image->bits_per_pixel) / NBBY)]); + + switch (image->bits_per_pixel) { + case 32: + /* + * Pixel is DWORD: 0x00BBGGRR + */ + + destPtr[3] = 0; + case 24: + /* + * Pixel is triplet: 0xBBGGRR. + */ + + destPtr[0] = (unsigned char) GetBValue(pixel); + destPtr[1] = (unsigned char) GetGValue(pixel); + destPtr[2] = (unsigned char) GetRValue(pixel); + break; + case 16: + /* + * Pixel is WORD: 5-5-5 (R-G-B) + */ + + (*(WORD*)destPtr) = ((GetRValue(pixel) & 0xf8) << 7) + | ((GetGValue(pixel) & 0xf8) <<2) + | ((GetBValue(pixel) & 0xf8) >> 3); + break; + case 8: + /* + * Pixel is 8-bit index into color table. + */ + + (*destPtr) = (unsigned char) pixel; + break; + case 4: + /* + * Pixel is 4-bit index in MSBFirst order. + */ + + if (x%2) { + (*destPtr) = (unsigned char) (((*destPtr) & 0xf0) + | (pixel & 0x0f)); + } else { + (*destPtr) = (unsigned char) (((*destPtr) & 0x0f) + | ((pixel << 4) & 0xf0)); + } + break; + case 1: { + /* + * Pixel is bit in MSBFirst order. + */ + + int mask = (0x80 >> (x%8)); + + if (pixel) { + (*destPtr) |= mask; + } else { + (*destPtr) &= ~mask; + } + break; + } + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * XCreateImage -- + * + * Allocates storage for a new XImage. + * + * Results: + * Returns a newly allocated XImage. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +XImage * +XCreateImage( + Display *display, + Visual *visual, + unsigned int depth, + int format, + int offset, + char *data, + unsigned int width, + unsigned int height, + int bitmap_pad, + int bytes_per_line) +{ + XImage* imagePtr = ckalloc(sizeof(XImage)); + imagePtr->width = width; + imagePtr->height = height; + imagePtr->xoffset = offset; + imagePtr->format = format; + imagePtr->data = data; + imagePtr->byte_order = LSBFirst; + imagePtr->bitmap_unit = 8; + imagePtr->bitmap_bit_order = LSBFirst; + imagePtr->bitmap_pad = bitmap_pad; + imagePtr->bits_per_pixel = depth; + imagePtr->depth = depth; + + /* + * Under Windows, bitmap_pad must be on an LONG data-type boundary. + */ + +#define LONGBITS (sizeof(LONG) * 8) + + bitmap_pad = (bitmap_pad + LONGBITS - 1) / LONGBITS * LONGBITS; + + /* + * Round to the nearest bitmap_pad boundary. + */ + + if (bytes_per_line) { + imagePtr->bytes_per_line = bytes_per_line; + } else { + imagePtr->bytes_per_line = (((depth * width) + + (bitmap_pad - 1)) >> 3) & ~((bitmap_pad >> 3) - 1); + } + + imagePtr->red_mask = 0; + imagePtr->green_mask = 0; + imagePtr->blue_mask = 0; + + imagePtr->f.put_pixel = PutPixel; + imagePtr->f.get_pixel = ImageGetPixel; + imagePtr->f.destroy_image = DestroyImage; + imagePtr->f.create_image = NULL; + imagePtr->f.sub_image = NULL; + imagePtr->f.add_pixel = NULL; + + return imagePtr; +} + +/* + *---------------------------------------------------------------------- + * + * XGetImageZPixmap -- + * + * This function copies data from a pixmap or window into an XImage. This + * handles the ZPixmap case only. + * + * Results: + * Returns a newly allocated image containing the data from the given + * rectangle of the given drawable. + * + * Side effects: + * None. + * + * This procedure is adapted from the XGetImage implementation in TkNT. That + * code is Copyright (c) 1994 Software Research Associates, Inc. + * + *---------------------------------------------------------------------- + */ + +static XImage * +XGetImageZPixmap( + Display *display, + Drawable d, + int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, + int format) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + XImage *ret_image; + HDC hdc, hdcMem; + HBITMAP hbmp, hbmpPrev; + BITMAPINFO *bmInfo = NULL; + HPALETTE hPal, hPalPrev1 = 0, hPalPrev2 = 0; + int size; + unsigned int n; + unsigned int depth; + unsigned char *data; + TkWinDCState state; + BOOL ret; + + if (format != ZPixmap) { + TkpDisplayWarning("Only ZPixmap types are implemented", + "XGetImageZPixmap Failure"); + return NULL; + } + + hdc = TkWinGetDrawableDC(display, d, &state); + + /* + * Need to do a Blt operation to copy into a new bitmap. + */ + + hbmp = CreateCompatibleBitmap(hdc, (int) width, (int) height); + hdcMem = CreateCompatibleDC(hdc); + hbmpPrev = SelectObject(hdcMem, hbmp); + hPal = state.palette; + if (hPal) { + hPalPrev1 = SelectPalette(hdcMem, hPal, FALSE); + n = RealizePalette(hdcMem); + if (n > 0) { + UpdateColors(hdcMem); + } + hPalPrev2 = SelectPalette(hdc, hPal, FALSE); + n = RealizePalette(hdc); + if (n > 0) { + UpdateColors(hdc); + } + } + + ret = BitBlt(hdcMem, 0, 0, (int) width, (int) height, hdc, x, y, SRCCOPY); + if (hPal) { + SelectPalette(hdc, hPalPrev2, FALSE); + } + SelectObject(hdcMem, hbmpPrev); + TkWinReleaseDrawableDC(d, hdc, &state); + if (ret == FALSE) { + ret_image = NULL; + goto cleanup; + } + if (twdPtr->type == TWD_WINDOW) { + depth = Tk_Depth((Tk_Window) twdPtr->window.winPtr); + } else { + depth = twdPtr->bitmap.depth; + } + + size = sizeof(BITMAPINFO); + if (depth <= 8) { + size += sizeof(unsigned short) * (1 << depth); + } + bmInfo = ckalloc(size); + + bmInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmInfo->bmiHeader.biWidth = width; + bmInfo->bmiHeader.biHeight = -(int) height; + bmInfo->bmiHeader.biPlanes = 1; + bmInfo->bmiHeader.biBitCount = depth; + bmInfo->bmiHeader.biCompression = BI_RGB; + bmInfo->bmiHeader.biSizeImage = 0; + bmInfo->bmiHeader.biXPelsPerMeter = 0; + bmInfo->bmiHeader.biYPelsPerMeter = 0; + bmInfo->bmiHeader.biClrUsed = 0; + bmInfo->bmiHeader.biClrImportant = 0; + + if (depth == 1) { + unsigned char *p, *pend; + + GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); + data = ckalloc(bmInfo->bmiHeader.biSizeImage); + if (!data) { + /* printf("Failed to allocate data area for XImage.\n"); */ + ret_image = NULL; + goto cleanup; + } + ret_image = XCreateImage(display, NULL, depth, ZPixmap, 0, (char *) data, + width, height, 32, (int) ((width + 31) >> 3) & ~1); + if (ret_image == NULL) { + ckfree(data); + goto cleanup; + } + + /* + * Get the BITMAP info into the Image. + */ + + if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, + DIB_PAL_COLORS) == 0) { + ckfree(ret_image->data); + ckfree(ret_image); + ret_image = NULL; + goto cleanup; + } + p = data; + pend = data + bmInfo->bmiHeader.biSizeImage; + while (p < pend) { + *p = ~*p; + p++; + } + } else if (depth == 8) { + unsigned short *palette; + unsigned int i; + unsigned char *p; + + GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_PAL_COLORS); + data = ckalloc(bmInfo->bmiHeader.biSizeImage); + if (!data) { + /* printf("Failed to allocate data area for XImage.\n"); */ + ret_image = NULL; + goto cleanup; + } + ret_image = XCreateImage(display, NULL, 8, ZPixmap, 0, (char *) data, + width, height, 8, (int) width); + if (ret_image == NULL) { + ckfree(data); + goto cleanup; + } + + /* + * Get the BITMAP info into the Image. + */ + + if (GetDIBits(hdcMem, hbmp, 0, height, data, bmInfo, + DIB_PAL_COLORS) == 0) { + ckfree(ret_image->data); + ckfree(ret_image); + ret_image = NULL; + goto cleanup; + } + p = data; + palette = (unsigned short *) bmInfo->bmiColors; + for (i = 0; i < bmInfo->bmiHeader.biSizeImage; i++, p++) { + *p = (unsigned char) palette[*p]; + } + } else if (depth == 16) { + GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); + data = ckalloc(bmInfo->bmiHeader.biSizeImage); + if (!data) { + /* printf("Failed to allocate data area for XImage.\n"); */ + ret_image = NULL; + goto cleanup; + } + ret_image = XCreateImage(display, NULL, 16, ZPixmap, 0, (char *) data, + width, height, 16, 0 /* will be calc'ed from bitmap_pad */); + if (ret_image == NULL) { + ckfree(data); + goto cleanup; + } + + /* + * Get the BITMAP info directly into the Image. + */ + + if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, + DIB_RGB_COLORS) == 0) { + ckfree(ret_image->data); + ckfree(ret_image); + ret_image = NULL; + goto cleanup; + } + } else { + GetDIBits(hdcMem, hbmp, 0, height, NULL, bmInfo, DIB_RGB_COLORS); + data = ckalloc(width * height * 4); + if (!data) { + /* printf("Failed to allocate data area for XImage.\n"); */ + ret_image = NULL; + goto cleanup; + } + ret_image = XCreateImage(display, NULL, 32, ZPixmap, 0, (char *) data, + width, height, 0, (int) width * 4); + if (ret_image == NULL) { + ckfree(data); + goto cleanup; + } + + if (depth <= 24) { + /* + * This used to handle 16 and 24 bpp, but now just handles 24. It + * can likely be optimized for that. -- hobbs + */ + + unsigned char *smallBitData, *smallBitBase, *bigBitData; + unsigned int byte_width, h, w; + + byte_width = ((width * 3 + 3) & ~(unsigned)3); + smallBitBase = ckalloc(byte_width * height); + if (!smallBitBase) { + ckfree(ret_image->data); + ckfree(ret_image); + ret_image = NULL; + goto cleanup; + } + smallBitData = smallBitBase; + + /* + * Get the BITMAP info into the Image. + */ + + if (GetDIBits(hdcMem, hbmp, 0, height, smallBitData, bmInfo, + DIB_RGB_COLORS) == 0) { + ckfree(ret_image->data); + ckfree(ret_image); + ckfree(smallBitBase); + ret_image = NULL; + goto cleanup; + } + + /* + * Copy the 24 Bit Pixmap to a 32-Bit one. + */ + + for (h = 0; h < height; h++) { + bigBitData = (unsigned char *) ret_image->data + h * ret_image->bytes_per_line; + smallBitData = smallBitBase + h * byte_width; + + for (w = 0; w < width; w++) { + *bigBitData++ = ((*smallBitData++)); + *bigBitData++ = ((*smallBitData++)); + *bigBitData++ = ((*smallBitData++)); + *bigBitData++ = 0; + } + } + + /* + * Free the Device contexts, and the Bitmap. + */ + + ckfree(smallBitBase); + } else { + /* + * Get the BITMAP info directly into the Image. + */ + + if (GetDIBits(hdcMem, hbmp, 0, height, ret_image->data, bmInfo, + DIB_RGB_COLORS) == 0) { + ckfree(ret_image->data); + ckfree(ret_image); + ret_image = NULL; + goto cleanup; + } + } + } + + cleanup: + if (bmInfo) { + ckfree(bmInfo); + } + if (hPal) { + SelectPalette(hdcMem, hPalPrev1, FALSE); + } + DeleteDC(hdcMem); + DeleteObject(hbmp); + + return ret_image; +} + +/* + *---------------------------------------------------------------------- + * + * XGetImage -- + * + * This function copies data from a pixmap or window into an XImage. + * + * Results: + * Returns a newly allocated image containing the data from the given + * rectangle of the given drawable. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +XImage * +XGetImage( + Display* display, + Drawable d, + int x, int y, + unsigned int width, unsigned int height, + unsigned long plane_mask, + int format) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + XImage *imagePtr; + HDC dc; + + display->request++; + + if (twdPtr == NULL) { + /* + * Avoid unmapped windows or bad drawables + */ + + return NULL; + } + + if (twdPtr->type != TWD_BITMAP) { + /* + * This handles TWD_WINDOW or TWD_WINDC, always creating a 32bit + * image. If the window being copied isn't visible (unmapped or + * obscured), we quietly stop copying (no user error). The user will + * see black where the widget should be. This branch is likely + * followed in favor of XGetImageZPixmap as postscript printed widgets + * require RGB data. + */ + + TkWinDCState state; + unsigned int xx, yy, size; + COLORREF pixel; + + dc = TkWinGetDrawableDC(display, d, &state); + + imagePtr = XCreateImage(display, NULL, 32, format, 0, NULL, + width, height, 32, 0); + size = imagePtr->bytes_per_line * imagePtr->height; + imagePtr->data = ckalloc(size); + ZeroMemory(imagePtr->data, size); + + for (yy = 0; yy < height; yy++) { + for (xx = 0; xx < width; xx++) { + pixel = GetPixel(dc, x+(int)xx, y+(int)yy); + if (pixel == CLR_INVALID) { + break; + } + PutPixel(imagePtr, (int) xx, (int) yy, pixel); + } + } + + TkWinReleaseDrawableDC(d, dc, &state); + } else if (format == ZPixmap) { + /* + * This actually handles most TWD_WINDOW requests, but it varies from + * the above in that it really does a screen capture of an area, which + * is consistent with the Unix behavior, but does not appear to handle + * all bit depths correctly. -- hobbs + */ + + imagePtr = XGetImageZPixmap(display, d, x, y, + width, height, plane_mask, format); + } else { + const char *errMsg = NULL; + char infoBuf[sizeof(BITMAPINFO) + sizeof(RGBQUAD)]; + BITMAPINFO *infoPtr = (BITMAPINFO*)infoBuf; + + if (twdPtr->bitmap.handle == NULL) { + errMsg = "XGetImage: not implemented for empty bitmap handles"; + } else if (format != XYPixmap) { + errMsg = "XGetImage: not implemented for format != XYPixmap"; + } else if (plane_mask != 1) { + errMsg = "XGetImage: not implemented for plane_mask != 1"; + } + if (errMsg != NULL) { + /* + * Do a soft warning for the unsupported XGetImage types. + */ + + TkpDisplayWarning(errMsg, "XGetImage Failure"); + return NULL; + } + + imagePtr = XCreateImage(display, NULL, 1, XYBitmap, 0, NULL, + width, height, 32, 0); + imagePtr->data = ckalloc(imagePtr->bytes_per_line * imagePtr->height); + + dc = GetDC(NULL); + + GetDIBits(dc, twdPtr->bitmap.handle, 0, height, NULL, + infoPtr, DIB_RGB_COLORS); + + infoPtr->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + infoPtr->bmiHeader.biWidth = width; + infoPtr->bmiHeader.biHeight = -(LONG)height; + infoPtr->bmiHeader.biPlanes = 1; + infoPtr->bmiHeader.biBitCount = 1; + infoPtr->bmiHeader.biCompression = BI_RGB; + infoPtr->bmiHeader.biSizeImage = 0; + infoPtr->bmiHeader.biXPelsPerMeter = 0; + infoPtr->bmiHeader.biYPelsPerMeter = 0; + infoPtr->bmiHeader.biClrUsed = 0; + infoPtr->bmiHeader.biClrImportant = 0; + + GetDIBits(dc, twdPtr->bitmap.handle, 0, height, imagePtr->data, + infoPtr, DIB_RGB_COLORS); + ReleaseDC(NULL, dc); + } + + return imagePtr; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinInit.c b/tk8.6/win/tkWinInit.c new file mode 100644 index 0000000..4c18399 --- /dev/null +++ b/tk8.6/win/tkWinInit.c @@ -0,0 +1,223 @@ +/* + * tkWinInit.c -- + * + * This file contains Windows-specific interpreter initialization + * functions. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + + +/* + *---------------------------------------------------------------------- + * + * TkpInit -- + * + * Performs Windows-specific interpreter initialization related to the + * tk_library variable. + * + * Results: + * A standard Tcl completion code (TCL_OK or TCL_ERROR). Also leaves + * information in the interp's result. + * + * Side effects: + * Sets "tk_library" Tcl variable, runs "tk.tcl" script. + * + *---------------------------------------------------------------------- + */ + +int +TkpInit( + Tcl_Interp *interp) +{ + /* + * This is necessary for static initialization, and is ok otherwise + * because TkWinXInit flips a static bit to do its work just once. + */ + + TkWinXInit(Tk_GetHINSTANCE()); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetAppName -- + * + * Retrieves the name of the current application from a platform specific + * location. For Windows, the application name is the root of the tail of + * the path contained in the tcl variable argv0. + * + * Results: + * Returns the application name in the given Tcl_DString. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpGetAppName( + Tcl_Interp *interp, + Tcl_DString *namePtr) /* A previously initialized Tcl_DString. */ +{ + int argc, namelength; + const char **argv = NULL, *name, *p; + + name = Tcl_GetVar2(interp, "argv0", NULL, TCL_GLOBAL_ONLY); + namelength = -1; + if (name != NULL) { + Tcl_SplitPath(name, &argc, &argv); + if (argc > 0) { + name = argv[argc-1]; + p = strrchr(name, '.'); + if (p != NULL) { + namelength = p - name; + } + } else { + name = NULL; + } + } + if ((name == NULL) || (*name == 0)) { + name = "tk"; + namelength = -1; + } + Tcl_DStringAppend(namePtr, name, namelength); + if (argv != NULL) { + ckfree(argv); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDisplayWarning -- + * + * This routines is called from Tk_Main to display warning messages that + * occur during startup. + * + * Results: + * None. + * + * Side effects: + * Displays a message box. + * + *---------------------------------------------------------------------- + */ + +void +TkpDisplayWarning( + const char *msg, /* Message to be displayed. */ + const char *title) /* Title of warning. */ +{ +#define TK_MAX_WARN_LEN 1024 + WCHAR titleString[TK_MAX_WARN_LEN]; + WCHAR *msgString; /* points to titleString, just after title, leaving space for ": " */ + int len; /* size of title, including terminating NULL */ + + /* If running on Cygwin and we have a stderr channel, use it. */ +#if !defined(STATIC_BUILD) + if (tclStubsPtr->reserved9) { + Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR); + if (errChannel) { + Tcl_WriteChars(errChannel, title, -1); + Tcl_WriteChars(errChannel, ": ", 2); + Tcl_WriteChars(errChannel, msg, -1); + Tcl_WriteChars(errChannel, "\n", 1); + return; + } + } +#endif /* !STATIC_BUILD */ + + len = MultiByteToWideChar(CP_UTF8, 0, title, -1, titleString, TK_MAX_WARN_LEN); + msgString = &titleString[len + 1]; + titleString[TK_MAX_WARN_LEN - 1] = L'\0'; + MultiByteToWideChar(CP_UTF8, 0, msg, -1, msgString, (TK_MAX_WARN_LEN - 1) - len); + /* + * Truncate MessageBox string if it is too long to not overflow the screen + * and cause possible oversized window error. + */ + if (titleString[TK_MAX_WARN_LEN - 1] != L'\0') { + memcpy(titleString + (TK_MAX_WARN_LEN - 5), L" ...", 5 * sizeof(WCHAR)); + } + if (IsDebuggerPresent()) { + titleString[len - 1] = L':'; + titleString[len] = L' '; + OutputDebugStringW(titleString); + } else { + titleString[len - 1] = L'\0'; + MessageBoxW(NULL, msgString, titleString, + MB_OK | MB_ICONEXCLAMATION | MB_SYSTEMMODAL + | MB_SETFOREGROUND | MB_TOPMOST); + } +} + +/* + * ---------------------------------------------------------------------- + * + * Win32ErrorObj -- + * + * Returns a string object containing text from a COM or Win32 error code + * + * Results: + * A Tcl_Obj containing the Win32 error message. + * + * Side effects: + * Removed the error message from the COM threads error object. + * + * ---------------------------------------------------------------------- + */ + +Tcl_Obj* +TkWin32ErrorObj( + HRESULT hrError) +{ + LPTSTR lpBuffer = NULL, p = NULL; + TCHAR sBuffer[30]; + Tcl_Obj* errPtr = NULL; +#ifdef _UNICODE + Tcl_DString ds; +#endif + + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD)hrError, + LANG_NEUTRAL, (LPTSTR)&lpBuffer, 0, NULL); + + if (lpBuffer == NULL) { + lpBuffer = sBuffer; + wsprintf(sBuffer, TEXT("Error Code: %08lX"), hrError); + } + + if ((p = _tcsrchr(lpBuffer, TEXT('\r'))) != NULL) { + *p = TEXT('\0'); + } + +#ifdef _UNICODE + Tcl_WinTCharToUtf(lpBuffer, (int)wcslen(lpBuffer) * sizeof (WCHAR), &ds); + errPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); +#else + errPtr = Tcl_NewStringObj(lpBuffer, (int)strlen(lpBuffer)); +#endif /* _UNICODE */ + + if (lpBuffer != sBuffer) { + LocalFree((HLOCAL)lpBuffer); + } + + return errPtr; +} + + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinInt.h b/tk8.6/win/tkWinInt.h new file mode 100644 index 0000000..0e2c844 --- /dev/null +++ b/tk8.6/win/tkWinInt.h @@ -0,0 +1,256 @@ +/* + * tkWinInt.h -- + * + * This file contains declarations that are shared among the + * Windows-specific parts of Tk, but aren't used by the rest of Tk. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef _TKWININT +#define _TKWININT + +#ifndef _TKINT +#include "tkInt.h" +#endif + +/* + * Include platform specific public interfaces. + */ + +#ifndef _TKWIN +#include "tkWin.h" +#endif + +/* + * Define constants missing from older Win32 SDK header files. + */ + +#ifndef WS_EX_TOOLWINDOW +#define WS_EX_TOOLWINDOW 0x00000080L +#endif +#ifndef SPI_SETKEYBOARDCUES +#define SPI_SETKEYBOARDCUES 0x100B +#endif + +/* + * The TkWinDCState is used to save the state of a device context so that it + * can be restored later. + */ + +typedef struct TkWinDCState { + HPALETTE palette; + int bkmode; +} TkWinDCState; + +/* + * The TkWinDrawable is the internal implementation of an X Drawable (either a + * Window or a Pixmap). The following constants define the valid Drawable + * types. + */ + +#define TWD_BITMAP 1 +#define TWD_WINDOW 2 +#define TWD_WINDC 3 + +typedef struct { + int type; + HWND handle; + TkWindow *winPtr; +} TkWinWindow; + +typedef struct { + int type; + HBITMAP handle; + Colormap colormap; + int depth; +} TkWinBitmap; + +typedef struct { + int type; + HDC hdc; +}TkWinDC; + +typedef union { + int type; + TkWinWindow window; + TkWinBitmap bitmap; + TkWinDC winDC; +} TkWinDrawable; + +/* + * The following macros are used to retrieve internal values from a Drawable. + */ + +#define TkWinGetHWND(w) (((TkWinDrawable *) w)->window.handle) +#define TkWinGetWinPtr(w) (((TkWinDrawable *) w)->window.winPtr) +#define TkWinGetHBITMAP(w) (((TkWinDrawable *) w)->bitmap.handle) +#define TkWinGetColormap(w) (((TkWinDrawable *) w)->bitmap.colormap) +#define TkWinGetHDC(w) (((TkWinDrawable *) w)->winDC.hdc) + +/* + * The following structure is used to encapsulate palette information. + */ + +typedef struct { + HPALETTE palette; /* Palette handle used when drawing. */ + UINT size; /* Number of entries in the palette. */ + int stale; /* 1 if palette needs to be realized, + * otherwise 0. If the palette is stale, then + * an idle handler is scheduled to realize the + * palette. */ + Tcl_HashTable refCounts; /* Hash table of palette entry reference + * counts indexed by pixel value. */ +} TkWinColormap; + +/* + * The following macro retrieves the Win32 palette from a colormap. + */ + +#define TkWinGetPalette(colormap) (((TkWinColormap *) colormap)->palette) + +/* + * The following macros define the class names for Tk Window types. + */ + +#define TK_WIN_TOPLEVEL_CLASS_NAME TEXT("TkTopLevel") +#define TK_WIN_CHILD_CLASS_NAME TEXT("TkChild") + +/* + * The following variable is a translation table between X gc functions and + * Win32 raster and BitBlt op modes. + */ + +MODULE_SCOPE const int tkpWinRopModes[]; +MODULE_SCOPE const int tkpWinBltModes[]; + +/* + * The following defines are used with TkWinGetBorderPixels to get the extra 2 + * border colors from a Tk_3DBorder. + */ + +#define TK_3D_LIGHT2 TK_3D_DARK_GC+1 +#define TK_3D_DARK2 TK_3D_DARK_GC+2 + +/* + * Internal functions used by more than one source file. + */ + +#include "tkIntPlatDecls.h" + +/* + * Special proc needed as tsd accessor function between + * tkWinX.c:GenerateXEvent and tkWinClipboard.c:UpdateClipboard + */ + +MODULE_SCOPE void TkWinUpdatingClipboard(int mode); + +/* + * Used by tkWinDialog.c to associate the right icon with tk_messageBox + */ + +MODULE_SCOPE HICON TkWinGetIcon(Tk_Window tkw, DWORD iconsize); + +/* + * Used by tkWinX.c on for certain system display change messages and cleanup + * up containers + */ + +MODULE_SCOPE void TkWinDisplayChanged(Display *display); +MODULE_SCOPE void TkWinCleanupContainerList(void); + +/* + * Used by tkWinWm.c for embedded menu handling. May become public. + */ + +MODULE_SCOPE HWND Tk_GetMenuHWND(Tk_Window tkwin); +MODULE_SCOPE HWND Tk_GetEmbeddedMenuHWND(Tk_Window tkwin); + +/* + * The following allows us to cache these encoding for multiple functions. + */ + + +MODULE_SCOPE Tcl_Encoding TkWinGetKeyInputEncoding(void); +MODULE_SCOPE Tcl_Encoding TkWinGetUnicodeEncoding(void); +MODULE_SCOPE void TkWinSetupSystemFonts(TkMainInfo *mainPtr); + +/* + * Values returned by TkWinGetPlatformTheme. + */ + +#define TK_THEME_WIN_CLASSIC 1 +#define TK_THEME_WIN_XP 2 + +/* + * The following is implemented in tkWinWm and used by tkWinEmbed.c + */ + +MODULE_SCOPE void TkpWinToplevelWithDraw(TkWindow *winPtr); +MODULE_SCOPE void TkpWinToplevelIconify(TkWindow *winPtr); +MODULE_SCOPE void TkpWinToplevelDeiconify(TkWindow *winPtr); +MODULE_SCOPE long TkpWinToplevelIsControlledByWm(TkWindow *winPtr); +MODULE_SCOPE long TkpWinToplevelMove(TkWindow *winPtr, int x, int y); +MODULE_SCOPE long TkpWinToplevelOverrideRedirect(TkWindow *winPtr, + int reqValue); +MODULE_SCOPE void TkpWinToplevelDetachWindow(TkWindow *winPtr); +MODULE_SCOPE int TkpWmGetState(TkWindow *winPtr); + +/* + * Common routines used in Windows implementation + */ +MODULE_SCOPE Tcl_Obj * TkWin32ErrorObj(HRESULT hrError); + + +/* + * The following functions are not present in old versions of Windows + * API headers but are used in the Tk source to ensure 64bit + * compatibility. + */ + +#ifndef GetClassLongPtr +# define GetClassLongPtrA GetClassLongA +# define GetClassLongPtrW GetClassLongW +# define SetClassLongPtrA SetClassLongA +# define SetClassLongPtrW SetClassLongW +# ifdef UNICODE +# define GetClassLongPtr GetClassLongPtrW +# define SetClassLongPtr SetClassLongPtrW +# else +# define GetClassLongPtr GetClassLongPtrA +# define SetClassLongPtr SetClassLongPtrA +# endif /* !UNICODE */ +#endif /* !GetClassLongPtr */ +#ifndef GCLP_HICON +# define GCLP_HICON GCL_HICON +#endif /* !GCLP_HICON */ +#ifndef GCLP_HICONSM +# define GCLP_HICONSM (-34) +#endif /* !GCLP_HICONSM */ + +#ifndef GetWindowLongPtr +# define GetWindowLongPtrA GetWindowLongA +# define GetWindowLongPtrW GetWindowLongW +# define SetWindowLongPtrA SetWindowLongA +# define SetWindowLongPtrW SetWindowLongW +# ifdef UNICODE +# define GetWindowLongPtr GetWindowLongPtrW +# define SetWindowLongPtr SetWindowLongPtrW +# else +# define GetWindowLongPtr GetWindowLongPtrW +# define SetWindowLongPtr SetWindowLongPtrW +# endif /* !UNICODE */ +#endif /* !GetWindowLongPtr */ +#ifndef GWLP_WNDPROC +#define GWLP_WNDPROC GWL_WNDPROC +#define GWLP_HINSTANCE GWL_HINSTANCE +#define GWLP_HWNDPARENT GWL_HWNDPARENT +#define GWLP_USERDATA GWL_USERDATA +#define GWLP_ID GWL_ID +#endif /* !GWLP_WNDPROC */ + +#endif /* _TKWININT */ diff --git a/tk8.6/win/tkWinKey.c b/tk8.6/win/tkWinKey.c new file mode 100644 index 0000000..357a804 --- /dev/null +++ b/tk8.6/win/tkWinKey.c @@ -0,0 +1,750 @@ +/* + * tkWinKey.c -- + * + * This file contains X emulation routines for keyboard related + * functions. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * The keymap table holds mappings of Windows keycodes to X keysyms. If + * Windows ever comes along and changes the value of their keycodes, this will + * break all kinds of things. However, this table lookup is much faster than + * the alternative, in which we walked a list of keycodes looking for a match. + * Since this lookup is performed for every Windows keypress event, it seems + * like a worthwhile improvement to use the table. + */ + +#define MAX_KEYCODE 179 /* VK_MEDIA_PLAY_PAUSE is the last entry in our table below */ +/* cf. https://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx */ + +static const KeySym keymap[] = { + NoSymbol, NoSymbol, NoSymbol, XK_Cancel, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, XK_BackSpace, XK_Tab, + NoSymbol, NoSymbol, XK_Clear, XK_Return, NoSymbol, + NoSymbol, XK_Shift_L, XK_Control_L, XK_Alt_L, XK_Pause, + XK_Caps_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_Escape, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_space, XK_Prior, XK_Next, + XK_End, XK_Home, XK_Left, XK_Up, XK_Right, + XK_Down, XK_Select, XK_Print, XK_Execute, NoSymbol, + XK_Insert, XK_Delete, XK_Help, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, XK_Win_L, XK_Win_R, XK_App, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, XK_F1, XK_F2, XK_F3, + XK_F4, XK_F5, XK_F6, XK_F7, XK_F8, + XK_F9, XK_F10, XK_F11, XK_F12, XK_F13, + XK_F14, XK_F15, XK_F16, XK_F17, XK_F18, + XK_F19, XK_F20, XK_F21, XK_F22, XK_F23, + XK_F24, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, XK_Num_Lock, + XK_Scroll_Lock, NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*150 0x96*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*155 0x9b*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*160 0xa0*/ + NoSymbol, NoSymbol, NoSymbol, NoSymbol, NoSymbol, /*165 0xa5*/ + NoSymbol, NoSymbol, NoSymbol, XK_XF86AudioMute, XK_XF86AudioLowerVolume, /*170 0xaa*/ + XK_XF86AudioRaiseVolume, XK_XF86AudioNext, XK_XF86AudioPrev, XK_XF86AudioStop, XK_XF86AudioPlay /*175 0xaf*/ +}; + +/* + * Prototypes for local functions defined in this file: + */ + +static KeySym KeycodeToKeysym(unsigned int keycode, + int state, int noascii); + +/* + *---------------------------------------------------------------------- + * + * TkpGetString -- + * + * Retrieve the UTF string equivalent for the given keyboard event. + * + * Results: + * Returns the UTF string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +const char * +TkpGetString( + TkWindow *winPtr, /* Window where event occurred: needed to get + * input context. */ + XEvent *eventPtr, /* X keyboard event. */ + Tcl_DString *dsPtr) /* Uninitialized or empty string to hold + * result. */ +{ + XKeyEvent *keyEv = &eventPtr->xkey; + char buf[6]; + int len; + + Tcl_DStringInit(dsPtr); + if (keyEv->send_event == -1) { + if (keyEv->nbytes > 0) { + Tcl_ExternalToUtfDString(TkWinGetKeyInputEncoding(), + keyEv->trans_chars, keyEv->nbytes, dsPtr); + } + } else if (keyEv->send_event == -3) { + + /* + * Special case for WM_UNICHAR and win2000 multi-lingal IME input + */ + + len = TkUniCharToUtf(keyEv->keycode, buf); + Tcl_DStringAppend(dsPtr, buf, len); + } else { + /* + * This is an event generated from generic code. It has no nchars or + * trans_chars members. + */ + + KeySym keysym = KeycodeToKeysym(keyEv->keycode, keyEv->state, 0); + + if (((keysym != NoSymbol) && (keysym > 0) && (keysym < 256)) + || (keysym == XK_Return) || (keysym == XK_Tab)) { + len = Tcl_UniCharToUtf((Tcl_UniChar) (keysym & 255), buf); + Tcl_DStringAppend(dsPtr, buf, len); + } + } + return Tcl_DStringValue(dsPtr); +} + +/* + *---------------------------------------------------------------------- + * + * XKeycodeToKeysym -- + * + * Translate from a system-dependent keycode to a system-independent + * keysym. + * + * Results: + * Returns the translated keysym, or NoSymbol on failure. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeySym +XKeycodeToKeysym( + Display *display, + unsigned int keycode, + int index) +{ + int state = 0; + + if (index & 0x01) { + state |= ShiftMask; + } + return KeycodeToKeysym(keycode, state, 0); +} + +/* + *---------------------------------------------------------------------- + * + * KeycodeToKeysym -- + * + * Translate from a system-dependent keycode to a system-independent + * keysym. + * + * Results: + * Returns the translated keysym, or NoSymbol on failure. + * + * Side effects: + * It may affect the internal state of the keyboard, such as remembered + * dead key or lock indicator lamps. + * + *---------------------------------------------------------------------- + */ + +static KeySym +KeycodeToKeysym( + unsigned int keycode, + int state, + int noascii) +{ + BYTE keys[256]; + int result, deadkey, shift; + TCHAR buf[4]; + unsigned int scancode = MapVirtualKey(keycode, 0); + + /* + * Do not run keycodes of lock keys through ToUnicode(). One of ToUnicode()'s + * side effects is to handle the lights on the keyboard, and we don't want + * to mess that up. + */ + + if (noascii || keycode == VK_CAPITAL || keycode == VK_SCROLL || + keycode == VK_NUMLOCK) { + goto skipToUnicode; + } + + /* + * Use MapVirtualKey() to detect some dead keys. + */ + + if (MapVirtualKey(keycode, 2) > 0x7fffUL) { + return XK_Multi_key; + } + + /* + * Set up a keyboard with correct modifiers + */ + + memset(keys, 0, 256); + if (state & ShiftMask) { + keys[VK_SHIFT] = 0x80; + } + if (state & ControlMask) { + keys[VK_CONTROL] = 0x80; + } + if (state & Mod2Mask) { + keys[VK_MENU] = 0x80; + } + + /* + * Make sure all lock button info is correct so we don't mess up the + * lights. + */ + + if (state & LockMask) { + keys[VK_CAPITAL] = 1; + } + if (state & Mod3Mask) { + keys[VK_SCROLL] = 1; + } + if (state & Mod1Mask) { + keys[VK_NUMLOCK] = 1; + } + + result = ToUnicode(keycode, scancode, keys, buf, 4, 0); + + if (result < 0) { + /* + * Win95/98: This was a dead char, which is now remembered by the + * keyboard. Call ToUnicode() again to forget it. + * WinNT: This was a dead char, overwriting any previously remembered + * key. Calling ToUnicode() again does not affect anything. + */ + + ToUnicode(keycode, scancode, keys, buf, 4, 0); + return XK_Multi_key; + } + + if (result == 2) { + /* + * This was a dead char, and there were one previously remembered by + * the keyboard. Call ToUnicode() again with proper parameters to + * restore it. + * + * Get information about the old char + */ + + deadkey = VkKeyScan(buf[0]); + shift = deadkey >> 8; + deadkey &= 255; + scancode = MapVirtualKey(deadkey, 0); + + /* + * Set up a keyboard with proper modifier keys + */ + + memset(keys, 0, 256); + if (shift & 1) { + keys[VK_SHIFT] = 0x80; + } + if (shift & 2) { + keys[VK_CONTROL] = 0x80; + } + if (shift & 4) { + keys[VK_MENU] = 0x80; + } + ToUnicode(deadkey, scancode, keys, buf, 4, 0); + return XK_Multi_key; + } + + /* + * Keycode mapped to a valid Unicode character. Since the keysyms for + * alphanumeric characters map onto Unicode, we just return it. + * + * We treat 0x7F as a special case mostly for backwards compatibility. In + * versions of Tk<=8.2, Control-Backspace returned "XK_BackSpace" as the X + * Keysym. This was due to the fact that we did not initialize the keys + * array properly when we passed it to ToUnicode, above. We had previously + * not been setting the state bit for the Control key. When we fixed that, + * we found that Control-Backspace on Windows is interpreted as ASCII-127 + * (0x7F), which corresponds to the Delete key. + * + * Upon discovering this, we realized we had two choices: return XK_Delete + * or return XK_BackSpace. If we returned XK_Delete, that could be + * considered "more correct" (although the correctness would be dependent + * on whether you believe that ToUnicode is doing the right thing in that + * case); however, this would break backwards compatibility, and worse, it + * would limit application programmers; they would effectively be unable + * to bind to <Control-Backspace> on Windows. We therefore chose instead + * to return XK_BackSpace (handled here by letting the code "fall-through" + * to the return statement below, which works because the keycode for this + * event is VK_BACKSPACE, and the keymap table maps that keycode to + * XK_BackSpace). + */ + + if (result == 1 && buf[0] >= 0x20 && buf[0] != 0x7F) { + return (KeySym) buf[0]; + } + + /* + * Keycode is a non-alphanumeric key, so we have to do the lookup. + */ + + skipToUnicode: + if (keycode > MAX_KEYCODE) { + return NoSymbol; + } + switch (keycode) { + /* + * Windows only gives us an undifferentiated VK_CONTROL code (for + * example) when either Control key is pressed. To distinguish between + * left and right, we use the Extended flag. Indeed, the right Control + * and Alt (aka Menu) keys are such extended keys (which their left + * counterparts are not). + * Regarding the shift case, Windows does not set the Extended flag for + * the neither the left nor the right shift key. As a consequence another + * way to distinguish between the two keys is to query the state of one + * of the two to determine which was actually pressed. So if the keycode + * indicates Shift, do this extra test. If the right-side key was + * pressed, return the appropriate keycode. Otherwise, we fall through + * and rely on the keymap table to hold the correct keysym value. + * Note: this little trick only works for KeyPress, not for KeyRelease, + * for reasons stated in bug [2945130] + */ + + case VK_CONTROL: + if (state & EXTENDED_MASK) { + return XK_Control_R; + } + break; + case VK_SHIFT: + if (GetKeyState(VK_RSHIFT) & 0x80) { + return XK_Shift_R; + } + break; + case VK_MENU: + if (state & EXTENDED_MASK) { + return XK_Alt_R; + } + break; + } + return keymap[keycode]; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetKeySym -- + * + * Given an X KeyPress or KeyRelease event, map the keycode in the event + * into a KeySym. + * + * Results: + * The return value is the KeySym corresponding to eventPtr, or NoSymbol + * if no matching Keysym could be found. + * + * Side effects: + * In the first call for a given display, keycode-to-KeySym maps get + * loaded. + * + *---------------------------------------------------------------------- + */ + +KeySym +TkpGetKeySym( + TkDisplay *dispPtr, /* Display in which to map keycode. */ + XEvent *eventPtr) /* Description of X event. */ +{ + KeySym sym; + int state = eventPtr->xkey.state; + + /* + * Refresh the mapping information if it's stale + */ + + if (dispPtr->bindInfoStale) { + TkpInitKeymapInfo(dispPtr); + } + + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + + /* + * Special handling: if this is a ctrl-alt or shifted key, and there is no + * keysym defined, try without the modifiers. + */ + + if ((sym == NoSymbol) && ((state & ControlMask) || (state & Mod2Mask))) { + state &= ~(ControlMask | Mod2Mask); + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + } + if ((sym == NoSymbol) && (state & ShiftMask)) { + state &= ~ShiftMask; + sym = KeycodeToKeysym(eventPtr->xkey.keycode, state, 0); + } + return sym; +} + +/* + *-------------------------------------------------------------- + * + * TkpInitKeymapInfo -- + * + * This function is invoked to scan keymap information to recompute stuff + * that's important for binding, such as the modifier key (if any) that + * corresponds to "mode switch". + * + * Results: + * None. + * + * Side effects: + * Keymap-related information in dispPtr is updated. + * + *-------------------------------------------------------------- + */ + +void +TkpInitKeymapInfo( + TkDisplay *dispPtr) /* Display for which to recompute keymap + * information. */ +{ + XModifierKeymap *modMapPtr; + KeyCode *codePtr; + KeySym keysym; + int count, i, j, max, arraySize; +#define KEYCODE_ARRAY_SIZE 20 + + dispPtr->bindInfoStale = 0; + modMapPtr = XGetModifierMapping(dispPtr->display); + + /* + * Check the keycodes associated with the Lock modifier. If any of them is + * associated with the XK_Shift_Lock modifier, then Lock has to be + * interpreted as Shift Lock, not Caps Lock. + */ + + dispPtr->lockUsage = LU_IGNORE; + codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex; + for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) { + if (*codePtr == 0) { + continue; + } + keysym = KeycodeToKeysym(*codePtr, 0, 1); + if (keysym == XK_Shift_Lock) { + dispPtr->lockUsage = LU_SHIFT; + break; + } + if (keysym == XK_Caps_Lock) { + dispPtr->lockUsage = LU_CAPS; + break; + } + } + + /* + * Look through the keycodes associated with modifiers to see if the the + * "mode switch", "meta", or "alt" keysyms are associated with any + * modifiers. If so, remember their modifier mask bits. + */ + + dispPtr->modeModMask = 0; + dispPtr->metaModMask = 0; + dispPtr->altModMask = 0; + codePtr = modMapPtr->modifiermap; + max = 8*modMapPtr->max_keypermod; + for (i = 0; i < max; i++, codePtr++) { + if (*codePtr == 0) { + continue; + } + keysym = KeycodeToKeysym(*codePtr, 0, 1); + if (keysym == XK_Mode_switch) { + dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) { + dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) { + dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod); + } + } + + /* + * Create an array of the keycodes for all modifier keys. + */ + + if (dispPtr->modKeyCodes != NULL) { + ckfree(dispPtr->modKeyCodes); + } + dispPtr->numModKeyCodes = 0; + arraySize = KEYCODE_ARRAY_SIZE; + dispPtr->modKeyCodes = ckalloc(KEYCODE_ARRAY_SIZE * sizeof(KeyCode)); + for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) { + if (*codePtr == 0) { + continue; + } + + /* + * Make sure that the keycode isn't already in the array. + */ + + for (j = 0; j < dispPtr->numModKeyCodes; j++) { + if (dispPtr->modKeyCodes[j] == *codePtr) { + goto nextModCode; + } + } + if (dispPtr->numModKeyCodes >= arraySize) { + KeyCode *new; + + /* + * Ran out of space in the array; grow it. + */ + + arraySize *= 2; + new = ckalloc(arraySize * sizeof(KeyCode)); + memcpy(new, dispPtr->modKeyCodes, + dispPtr->numModKeyCodes * sizeof(KeyCode)); + ckfree(dispPtr->modKeyCodes); + dispPtr->modKeyCodes = new; + } + dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr; + dispPtr->numModKeyCodes++; + nextModCode: continue; + } + XFreeModifiermap(modMapPtr); +} + +/* + * When mapping from a keysym to a keycode, need information about the + * modifier state that should be used so that when they call XKeycodeToKeysym + * taking into account the xkey.state, they will get back the original keysym. + */ + +void +TkpSetKeycodeAndState( + Tk_Window tkwin, + KeySym keySym, + XEvent *eventPtr) +{ + int i; + SHORT result; + int shift; + + eventPtr->xkey.keycode = 0; + if (keySym == NoSymbol) { + return; + } + + /* + * We check our private map first for a virtual keycode, as VkKeyScan will + * return values that don't map to X for the "extended" Syms. This may be + * due to just casting problems below, but this works. + */ + + for (i = 0; i <= MAX_KEYCODE; i++) { + if (keymap[i] == keySym) { + eventPtr->xkey.keycode = i; + return; + } + } + if (keySym >= 0x20) { + result = VkKeyScan((TCHAR) keySym); + if (result != -1) { + shift = result >> 8; + if (shift & 1) + eventPtr->xkey.state |= ShiftMask; + if (shift & 2) + eventPtr->xkey.state |= ControlMask; + if (shift & 4) + eventPtr->xkey.state |= Mod2Mask; + eventPtr->xkey.keycode = (KeyCode) (result & 0xff); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * XKeysymToKeycode -- + * + * Translate a keysym back into a keycode. + * + * Results: + * Returns the keycode that would generate the specified keysym. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeyCode +XKeysymToKeycode( + Display *display, + KeySym keysym) +{ + int i; + SHORT result; + + /* + * We check our private map first for a virtual keycode, as VkKeyScan will + * return values that don't map to X for the "extended" Syms. This may be + * due to just casting problems below, but this works. + */ + + if (keysym == NoSymbol) { + return 0; + } + for (i = 0; i <= MAX_KEYCODE; i++) { + if (keymap[i] == keysym) { + return ((KeyCode) i); + } + } + if (keysym >= 0x20) { + result = VkKeyScan((TCHAR) keysym); + if (result != -1) { + return (KeyCode) (result & 0xff); + } + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * XGetModifierMapping -- + * + * Fetch the current keycodes used as modifiers. + * + * Results: + * Returns a new modifier map. + * + * Side effects: + * Allocates a new modifier map data structure. + * + *---------------------------------------------------------------------- + */ + +XModifierKeymap * +XGetModifierMapping( + Display *display) +{ + XModifierKeymap *map = ckalloc(sizeof(XModifierKeymap)); + + map->max_keypermod = 1; + map->modifiermap = ckalloc(sizeof(KeyCode) * 8); + map->modifiermap[ShiftMapIndex] = VK_SHIFT; + map->modifiermap[LockMapIndex] = VK_CAPITAL; + map->modifiermap[ControlMapIndex] = VK_CONTROL; + map->modifiermap[Mod1MapIndex] = VK_NUMLOCK; + map->modifiermap[Mod2MapIndex] = VK_MENU; + map->modifiermap[Mod3MapIndex] = VK_SCROLL; + map->modifiermap[Mod4MapIndex] = 0; + map->modifiermap[Mod5MapIndex] = 0; + return map; +} + +/* + *---------------------------------------------------------------------- + * + * XFreeModifiermap -- + * + * Deallocate a modifier map that was created by XGetModifierMapping. + * + * Results: + * None. + * + * Side effects: + * Frees the datastructure referenced by modmap. + * + *---------------------------------------------------------------------- + */ + +int +XFreeModifiermap( + XModifierKeymap *modmap) +{ + ckfree(modmap->modifiermap); + ckfree(modmap); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XStringToKeysym -- + * + * Translate a keysym name to the matching keysym. + * + * Results: + * Returns the keysym. Since this is already handled by Tk's + * StringToKeysym function, we just return NoSymbol. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +KeySym +XStringToKeysym( + _Xconst char *string) +{ + return NoSymbol; +} + +/* + *---------------------------------------------------------------------- + * + * XKeysymToString -- + * + * Convert a keysym to character form. + * + * Results: + * Returns NULL, since Tk will have handled this already. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +XKeysymToString( + KeySym keysym) +{ + return NULL; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinMenu.c b/tk8.6/win/tkWinMenu.c new file mode 100644 index 0000000..5dc8f8a --- /dev/null +++ b/tk8.6/win/tkWinMenu.c @@ -0,0 +1,3396 @@ +/* + * tkWinMenu.c -- + * + * This module implements the Windows platform-specific features of + * menus. + * + * Copyright (c) 1996-1998 by Sun Microsystems, Inc. + * Copyright (c) 1998-1999 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#define OEMRESOURCE +#include "tkWinInt.h" +#include "tkMenu.h" + +/* + * The class of the window for popup menus. + */ + +#define MENU_CLASS_NAME TEXT("MenuWindowClass") +#define EMBEDDED_MENU_CLASS_NAME TEXT("EmbeddedMenuWindowClass") + +/* + * Used to align a windows bitmap inside a rectangle + */ + +#define ALIGN_BITMAP_LEFT 0x00000001 +#define ALIGN_BITMAP_RIGHT 0x00000002 +#define ALIGN_BITMAP_TOP 0x00000004 +#define ALIGN_BITMAP_BOTTOM 0x00000008 + + +/* + * Platform-specific menu flags: + * + * MENU_SYSTEM_MENU Non-zero means that the Windows menu handle was + * retrieved with GetSystemMenu and needs to be disposed + * of specially. + * MENU_RECONFIGURE_PENDING + * Non-zero means that an idle handler has been set up to + * reconfigure the Windows menu handle for this menu. + */ + +#define MENU_SYSTEM_MENU MENU_PLATFORM_FLAG1 +#define MENU_RECONFIGURE_PENDING MENU_PLATFORM_FLAG2 + +/* + * ODS_NOACCEL flag forbids drawing accelerator cues (i.e. underlining labels) + * on Windows 2000 and above. The ODS_NOACCEL define is missing from mingw32 + * headers and undefined for _WIN32_WINNT < 0x0500 in Microsoft SDK. We might + * check for _WIN32_WINNT here, but I think it's not needed, as checking for + * this flag does no harm on even on NT: reserved bits should be zero, and in + * fact they are. + */ + +#ifndef ODS_NOACCEL +#define ODS_NOACCEL 0x100 +#endif +#ifndef SPI_GETKEYBOARDCUES +#define SPI_GETKEYBOARDCUES 0x100A +#endif +#ifndef WM_UPDATEUISTATE +#define WM_UPDATEUISTATE 0x0128 +#endif +#ifndef UIS_SET +#define UIS_SET 1 +#endif +#ifndef UIS_CLEAR +#define UIS_CLEAR 2 +#endif +#ifndef UISF_HIDEACCEL +#define UISF_HIDEACCEL 2 +#endif + +#ifndef WM_UNINITMENUPOPUP +#define WM_UNINITMENUPOPUP 0x0125 +#endif + +static int indicatorDimensions[2]; + /* The dimensions of the indicator space in a + * menu entry. Calculated at init time to save + * time. */ + +static BOOL showMenuAccelerators; + +typedef struct ThreadSpecificData { + int inPostMenu; /* We cannot be re-entrant like X Windows. */ + WORD lastCommandID; /* The last command ID we allocated. */ + HWND menuHWND; /* A window to service popup-menu messages + * in. */ + HWND embeddedMenuHWND; /* A window to service embedded menu + * messages */ + int oldServiceMode; /* Used while processing a menu; we need to + * set the event mode specially when we enter + * the menu processing modal loop and reset it + * when menus go away. */ + TkMenu *modalMenuPtr; /* The menu we are processing inside the modal + * loop. We need this to reset all of the + * active items when menus go away since + * Windows does not see fit to give this to us + * when it sends its WM_MENUSELECT. */ + Tcl_HashTable commandTable; /* A map of command ids to menu entries */ + Tcl_HashTable winMenuTable; /* Need this to map HMENUs back to menuPtrs */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * The following are default menu value strings. + */ + +static int defaultBorderWidth; /* The windows default border width. */ +static Tcl_DString menuFontDString; + /* A buffer to store the default menu font + * string. */ +/* + * Forward declarations for functions defined later in this file: + */ + +static void DrawMenuEntryAccelerator(TkMenu *menuPtr, + TkMenuEntry *mePtr, Drawable d, GC gc, + Tk_Font tkfont, const Tk_FontMetrics *fmPtr, + Tk_3DBorder activeBorder, int x, int y, + int width, int height); +static void DrawMenuEntryArrow(TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_3DBorder activeBorder, + int x,int y, int width, int height, int drawArrow); +static void DrawMenuEntryBackground(TkMenu *menuPtr, + TkMenuEntry *mePtr, Drawable d, + Tk_3DBorder activeBorder, Tk_3DBorder bgBorder, + int x, int y, int width, int heigth); +static void DrawMenuEntryIndicator(TkMenu *menuPtr, + TkMenuEntry *mePtr, Drawable d, GC gc, + GC indicatorGC, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int x, int y, + int width, int height); +static void DrawMenuEntryLabel(TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int x, int y, + int width, int height, int underline); +static void DrawMenuSeparator(TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, + int x, int y, int width, int height); +static void DrawTearoffEntry(TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int x, int y, + int width, int height); +static void DrawMenuUnderline(TkMenu *menuPtr, TkMenuEntry *mePtr, + Drawable d, GC gc, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int x, int y, + int width, int height); +static void DrawWindowsSystemBitmap(Display *display, + Drawable drawable, GC gc, const RECT *rectPtr, + int bitmapID, int alignFlags); +static void FreeID(WORD commandID); +static char * GetEntryText(TkMenu *menuPtr, TkMenuEntry *mePtr); +static void GetMenuAccelGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int *widthPtr, + int *heightPtr); +static void GetMenuLabelGeometry(TkMenuEntry *mePtr, + Tk_Font tkfont, const Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr); +static void GetMenuIndicatorGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr); +static void GetMenuSeparatorGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, + int *widthPtr, int *heightPtr); +static void GetTearoffEntryGeometry(TkMenu *menuPtr, + TkMenuEntry *mePtr, Tk_Font tkfont, + const Tk_FontMetrics *fmPtr, int *widthPtr, + int *heightPtr); +static int GetNewID(TkMenuEntry *mePtr, WORD *menuIDPtr); +static int TkWinMenuKeyObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static void MenuSelectEvent(TkMenu *menuPtr); +static void ReconfigureWindowsMenu(ClientData clientData); +static void RecursivelyClearActiveMenu(TkMenu *menuPtr); +static void SetDefaults(int firstTime); +static LRESULT CALLBACK TkWinMenuProc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam); +static LRESULT CALLBACK TkWinEmbeddedMenuProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); + +static inline void +ScheduleMenuReconfigure( + TkMenu *menuPtr) +{ + if (!(menuPtr->menuFlags & MENU_RECONFIGURE_PENDING)) { + menuPtr->menuFlags |= MENU_RECONFIGURE_PENDING; + Tcl_DoWhenIdle(ReconfigureWindowsMenu, menuPtr); + } +} + +static inline void +CallPendingReconfigureImmediately( + TkMenu *menuPtr) +{ + if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { + Tcl_CancelIdleCall(ReconfigureWindowsMenu, menuPtr); + ReconfigureWindowsMenu(menuPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * GetNewID -- + * + * Allocates a new menu id and marks it in use. + * + * Results: + * Returns TCL_OK if succesful; TCL_ERROR if there are no more ids of the + * appropriate type to allocate. menuIDPtr contains the new id if + * succesful. + * + * Side effects: + * An entry is created for the menu in the command hash table, and the + * hash entry is stored in the appropriate field in the menu data + * structure. + * + *---------------------------------------------------------------------- + */ + +static int +GetNewID( + TkMenuEntry *mePtr, /* The menu we are working with. */ + WORD *menuIDPtr) /* The resulting id. */ +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + WORD curID = tsdPtr->lastCommandID; + + while (1) { + Tcl_HashEntry *commandEntryPtr; + int new; + + /* + * Try the next ID number, taking care to wrap rather than stray + * into the system menu IDs. [Bug 3235256] + */ + if (++curID >= 0xF000) { + curID = 1; + } + + /* Return error when we've checked all IDs without success. */ + if (curID == tsdPtr->lastCommandID) { + return TCL_ERROR; + } + + commandEntryPtr = Tcl_CreateHashEntry(&tsdPtr->commandTable, + INT2PTR(curID), &new); + if (new) { + Tcl_SetHashValue(commandEntryPtr, mePtr); + *menuIDPtr = curID; + tsdPtr->lastCommandID = curID; + return TCL_OK; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * FreeID -- + * + * Marks the itemID as free. + * + * Results: + * None. + * + * Side effects: + * The hash table entry for the ID is cleared. + * + *---------------------------------------------------------------------- + */ + +static void +FreeID( + WORD commandID) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * If the menuHWND is NULL, this table has been finalized already. + */ + + if (tsdPtr->menuHWND != NULL) { + Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&tsdPtr->commandTable, + INT2PTR(commandID)); + + if (entryPtr != NULL) { + Tcl_DeleteHashEntry(entryPtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpNewMenu -- + * + * Gets a new blank menu. Only the platform specific options are filled + * in. + * + * Results: + * Standard TCL error. + * + * Side effects: + * Allocates a Windows menu handle and places it in the platformData + * field of the menuPtr. + * + *---------------------------------------------------------------------- + */ + +int +TkpNewMenu( + TkMenu *menuPtr) /* The common structure we are making the + * platform structure for. */ +{ + HMENU winMenuHdl; + Tcl_HashEntry *hashEntryPtr; + int newEntry; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + winMenuHdl = CreatePopupMenu(); + if (winMenuHdl == NULL) { + Tcl_SetObjResult(menuPtr->interp, Tcl_NewStringObj( + "No more menus can be allocated.", -1)); + Tcl_SetErrorCode(menuPtr->interp, "TK", "MENU", "SYSTEM_RESOURCES", NULL); + return TCL_ERROR; + } + + /* + * We hash all of the HMENU's so that we can get their menu ptrs back when + * dispatch messages. + */ + + hashEntryPtr = Tcl_CreateHashEntry(&tsdPtr->winMenuTable, + (char *) winMenuHdl, &newEntry); + Tcl_SetHashValue(hashEntryPtr, menuPtr); + + menuPtr->platformData = (TkMenuPlatformData) winMenuHdl; + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyMenu -- + * + * Destroys platform-specific menu structures. + * + * Results: + * None. + * + * Side effects: + * All platform-specific allocations are freed up. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyMenu( + TkMenu *menuPtr) /* The common menu structure */ +{ + HMENU winMenuHdl = (HMENU) menuPtr->platformData; + const char *searchName; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (menuPtr->menuFlags & MENU_RECONFIGURE_PENDING) { + Tcl_CancelIdleCall(ReconfigureWindowsMenu, menuPtr); + } + + if (winMenuHdl == NULL) { + return; + } + + if (menuPtr->menuFlags & MENU_SYSTEM_MENU) { + TkMenuEntry *searchEntryPtr; + Tcl_HashTable *tablePtr = TkGetMenuHashTable(menuPtr->interp); + char *menuName = Tcl_GetHashKey(tablePtr, + menuPtr->menuRefPtr->hashEntryPtr); + + /* + * Search for the menu in the menubar, if it is present, get the + * wrapper window associated with the toplevel and reset its + * system menu to the default menu. + */ + + for (searchEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; + searchEntryPtr != NULL; + searchEntryPtr = searchEntryPtr->nextCascadePtr) { + searchName = Tcl_GetString(searchEntryPtr->namePtr); + if (strcmp(searchName, menuName) == 0) { + Tk_Window parentTopLevelPtr = searchEntryPtr + ->menuPtr->parentTopLevelPtr; + + if (parentTopLevelPtr != NULL) { + GetSystemMenu( + TkWinGetWrapperWindow(parentTopLevelPtr), TRUE); + } + break; + } + } + } else { + /* + * Remove the menu from the menu hash table, then destroy the handle. + * If the menuHWND is NULL, this table has been finalized already. + */ + + if (tsdPtr->menuHWND != NULL) { + Tcl_HashEntry *hashEntryPtr = + Tcl_FindHashEntry(&tsdPtr->winMenuTable, (char *) winMenuHdl); + + if (hashEntryPtr != NULL) { + Tcl_DeleteHashEntry(hashEntryPtr); + } + } + DestroyMenu(winMenuHdl); + } + menuPtr->platformData = NULL; + + if (menuPtr == tsdPtr->modalMenuPtr) { + tsdPtr->modalMenuPtr = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyMenuEntry -- + * + * Cleans up platform-specific menu entry items. + * + * Results: + * None + * + * Side effects: + * All platform-specific allocations are freed up. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyMenuEntry( + TkMenuEntry *mePtr) /* The entry to destroy */ +{ + TkMenu *menuPtr = mePtr->menuPtr; + HMENU winMenuHdl = (HMENU) menuPtr->platformData; + + if (NULL != winMenuHdl) { + ScheduleMenuReconfigure(menuPtr); + } + FreeID((WORD) PTR2INT(mePtr->platformEntryData)); + mePtr->platformEntryData = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * GetEntryText -- + * + * Given a menu entry, gives back the text that should go in it. + * Separators should be done by the caller, as they have to be handled + * specially. Allocates the memory with alloc. The caller should free the + * memory. + * + * Results: + * itemText points to the new text for the item. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static char * +GetEntryText( + TkMenu *menuPtr, /* The menu considered. */ + TkMenuEntry *mePtr) /* A pointer to the menu entry. */ +{ + char *itemText; + + if (mePtr->type == TEAROFF_ENTRY) { + itemText = ckalloc(sizeof("(Tear-off)")); + strcpy(itemText, "(Tear-off)"); + } else if (mePtr->imagePtr != NULL) { + itemText = ckalloc(sizeof("(Image)")); + strcpy(itemText, "(Image)"); + } else if (mePtr->bitmapPtr != NULL) { + itemText = ckalloc(sizeof("(Pixmap)")); + strcpy(itemText, "(Pixmap)"); + } else if (mePtr->labelPtr == NULL || mePtr->labelLength == 0) { + itemText = ckalloc(sizeof("( )")); + strcpy(itemText, "( )"); + } else { + int i; + const char *label = (mePtr->labelPtr == NULL) ? "" + : Tcl_GetString(mePtr->labelPtr); + const char *accel = ((menuPtr->menuType == MENUBAR) || (mePtr->accelPtr == NULL)) ? "" + : Tcl_GetString(mePtr->accelPtr); + const char *p, *next; + Tcl_DString itemString; + + /* + * We have to construct the string with an ampersand preceeding the + * underline character, and a tab seperating the text and the accel + * text. We have to be careful with ampersands in the string. + */ + + Tcl_DStringInit(&itemString); + + for (p = label, i = 0; *p != '\0'; i++, p = next) { + if (i == mePtr->underline) { + Tcl_DStringAppend(&itemString, "&", 1); + } + if (*p == '&') { + Tcl_DStringAppend(&itemString, "&", 1); + } + next = Tcl_UtfNext(p); + Tcl_DStringAppend(&itemString, p, (int) (next - p)); + } + if (mePtr->accelLength > 0) { + Tcl_DStringAppend(&itemString, "\t", 1); + for (p = accel, i = 0; *p != '\0'; i++, p = next) { + if (*p == '&') { + Tcl_DStringAppend(&itemString, "&", 1); + } + next = Tcl_UtfNext(p); + Tcl_DStringAppend(&itemString, p, (int) (next - p)); + } + } + + itemText = ckalloc(Tcl_DStringLength(&itemString) + 1); + strcpy(itemText, Tcl_DStringValue(&itemString)); + Tcl_DStringFree(&itemString); + } + return itemText; +} + +/* + *---------------------------------------------------------------------- + * + * ReconfigureWindowsMenu -- + * + * Tears down and rebuilds the platform-specific part of this menu. + * + * Results: + * None. + * + * Side effects: + * Configuration information get set for mePtr; old resources get freed, + * if any need it. + * + *---------------------------------------------------------------------- + */ + +static void +ReconfigureWindowsMenu( + ClientData clientData) /* The menu we are rebuilding */ +{ + TkMenu *menuPtr = clientData; + TkMenuEntry *mePtr; + HMENU winMenuHdl = (HMENU) menuPtr->platformData; + char *itemText = NULL; + const TCHAR *lpNewItem; + UINT flags; + UINT itemID; + int i, count, systemMenu = 0, base; + Tcl_DString translatedText; + + if (NULL == winMenuHdl) { + return; + } + + /* + * Reconstruct the entire menu. Takes care of nasty system menu and index + * problem. + */ + + base = (menuPtr->menuFlags & MENU_SYSTEM_MENU) ? 7 : 0; + count = GetMenuItemCount(winMenuHdl); + for (i = base; i < count; i++) { + RemoveMenu(winMenuHdl, base, MF_BYPOSITION); + } + + count = menuPtr->numEntries; + for (i = 0; i < count; i++) { + mePtr = menuPtr->entries[i]; + lpNewItem = NULL; + flags = MF_BYPOSITION; + itemID = 0; + Tcl_DStringInit(&translatedText); + + if ((menuPtr->menuType == MENUBAR) && (mePtr->type == TEAROFF_ENTRY)) { + continue; + } + + itemText = GetEntryText(menuPtr, mePtr); + if ((menuPtr->menuType == MENUBAR) + || (menuPtr->menuFlags & MENU_SYSTEM_MENU)) { + Tcl_WinUtfToTChar(itemText, -1, &translatedText); + lpNewItem = (const TCHAR *) Tcl_DStringValue(&translatedText); + flags |= MF_STRING; + } else { + lpNewItem = (LPCTSTR) mePtr; + flags |= MF_OWNERDRAW; + } + + /* + * Set enabling and disabling correctly. + */ + + if (mePtr->state == ENTRY_DISABLED) { + flags |= MF_DISABLED | MF_GRAYED; + } + + /* + * Set the check mark for check entries and radio entries. + */ + + if (((mePtr->type == CHECK_BUTTON_ENTRY) + || (mePtr->type == RADIO_BUTTON_ENTRY)) + && (mePtr->entryFlags & ENTRY_SELECTED)) { + flags |= MF_CHECKED; + } + + /* + * Set the SEPARATOR bit for separator entries. This bit is not used + * by our internal drawing functions, but it is used by the system + * when drawing the system menu (we do not draw the system menu + * ourselves). If this bit is not set, separator entries on the system + * menu will not be drawn correctly. + */ + + if (mePtr->type == SEPARATOR_ENTRY) { + flags |= MF_SEPARATOR; + } + + if (mePtr->columnBreak) { + flags |= MF_MENUBREAK; + } + + itemID = PTR2INT(mePtr->platformEntryData); + if ((mePtr->type == CASCADE_ENTRY) + && (mePtr->childMenuRefPtr != NULL) + && (mePtr->childMenuRefPtr->menuPtr != NULL)) { + HMENU childMenuHdl = (HMENU) mePtr->childMenuRefPtr->menuPtr + ->platformData; + if (childMenuHdl != NULL) { + /* + * Win32 draws the popup arrow in the wrong color for a + * disabled cascade menu, so do it by hand. Given it is + * disabled, there's no need for it to be connected to its + * child. + */ + + if (mePtr->state != ENTRY_DISABLED) { + flags |= MF_POPUP; + /* + * If the MF_POPUP flag is set, then the id is interpreted + * as the handle of a submenu. + */ + itemID = PTR2INT(childMenuHdl); + } + } + if ((menuPtr->menuType == MENUBAR) + && !(mePtr->childMenuRefPtr->menuPtr->menuFlags + & MENU_SYSTEM_MENU)) { + Tcl_DString ds; + TkMenuReferences *menuRefPtr; + TkMenu *systemMenuPtr = mePtr->childMenuRefPtr->menuPtr; + + Tcl_DStringInit(&ds); + Tcl_DStringAppend(&ds, + Tk_PathName(menuPtr->masterMenuPtr->tkwin), -1); + Tcl_DStringAppend(&ds, ".system", 7); + + menuRefPtr = TkFindMenuReferences(menuPtr->interp, + Tcl_DStringValue(&ds)); + + Tcl_DStringFree(&ds); + + if ((menuRefPtr != NULL) + && (menuRefPtr->menuPtr != NULL) + && (menuPtr->parentTopLevelPtr != NULL) + && (systemMenuPtr->masterMenuPtr + == menuRefPtr->menuPtr)) { + HMENU systemMenuHdl = (HMENU) systemMenuPtr->platformData; + HWND wrapper = TkWinGetWrapperWindow(menuPtr + ->parentTopLevelPtr); + + if (wrapper != NULL) { + DestroyMenu(systemMenuHdl); + systemMenuHdl = GetSystemMenu(wrapper, FALSE); + systemMenuPtr->menuFlags |= MENU_SYSTEM_MENU; + systemMenuPtr->platformData = + (TkMenuPlatformData) systemMenuHdl; + ScheduleMenuReconfigure(systemMenuPtr); + } + } + } + if (mePtr->childMenuRefPtr->menuPtr->menuFlags + & MENU_SYSTEM_MENU) { + systemMenu++; + } + } + if (!systemMenu) { + InsertMenu(winMenuHdl, 0xFFFFFFFF, flags, itemID, lpNewItem); + } + Tcl_DStringFree(&translatedText); + if (itemText != NULL) { + ckfree(itemText); + itemText = NULL; + } + } + + + if ((menuPtr->menuType == MENUBAR) + && (menuPtr->parentTopLevelPtr != NULL)) { + HANDLE bar = TkWinGetWrapperWindow(menuPtr->parentTopLevelPtr); + + if (bar) { + DrawMenuBar(bar); + } + } + + menuPtr->menuFlags &= ~(MENU_RECONFIGURE_PENDING); +} + +/* + *---------------------------------------------------------------------- + * + * TkpPostMenu -- + * + * Posts a menu on the screen + * + * Results: + * None. + * + * Side effects: + * The menu is posted and handled. + * + *---------------------------------------------------------------------- + */ + +int +TkpPostMenu( + Tcl_Interp *interp, + TkMenu *menuPtr, + int x, int y) +{ + HMENU winMenuHdl = (HMENU) menuPtr->platformData; + int result, flags; + RECT noGoawayRect; + POINT point; + Tk_Window parentWindow = Tk_Parent(menuPtr->tkwin); + int oldServiceMode = Tcl_GetServiceMode(); + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + tsdPtr->inPostMenu++; + + CallPendingReconfigureImmediately(menuPtr); + + result = TkPreprocessMenu(menuPtr); + if (result != TCL_OK) { + tsdPtr->inPostMenu--; + return result; + } + + /* + * The post commands could have deleted the menu, which means + * we are dead and should go away. + */ + + if (menuPtr->tkwin == NULL) { + tsdPtr->inPostMenu--; + return TCL_OK; + } + + if (NULL == parentWindow) { + noGoawayRect.top = y - 50; + noGoawayRect.bottom = y + 50; + noGoawayRect.left = x - 50; + noGoawayRect.right = x + 50; + } else { + int left, top; + Tk_GetRootCoords(parentWindow, &left, &top); + noGoawayRect.left = left; + noGoawayRect.top = top; + noGoawayRect.bottom = noGoawayRect.top + Tk_Height(parentWindow); + noGoawayRect.right = noGoawayRect.left + Tk_Width(parentWindow); + } + + Tcl_SetServiceMode(TCL_SERVICE_NONE); + + /* + * Make an assumption here. If the right button is down, + * then we want to track it. Otherwise, track the left mouse button. + */ + + flags = TPM_LEFTALIGN; + if (GetSystemMetrics(SM_SWAPBUTTON)) { + if (GetAsyncKeyState(VK_LBUTTON) < 0) { + flags |= TPM_RIGHTBUTTON; + } else { + flags |= TPM_LEFTBUTTON; + } + } else { + if (GetAsyncKeyState(VK_RBUTTON) < 0) { + flags |= TPM_RIGHTBUTTON; + } else { + flags |= TPM_LEFTBUTTON; + } + } + + TrackPopupMenu(winMenuHdl, flags, x, y, 0, + tsdPtr->menuHWND, &noGoawayRect); + Tcl_SetServiceMode(oldServiceMode); + + GetCursorPos(&point); + Tk_PointerEvent(NULL, point.x, point.y); + + if (tsdPtr->inPostMenu) { + tsdPtr->inPostMenu = 0; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuNewEntry -- + * + * Adds a pointer to a new menu entry structure with the platform- + * specific fields filled in. + * + * Results: + * Standard TCL error. + * + * Side effects: + * A new command ID is allocated and stored in the platformEntryData + * field of mePtr. + * + *---------------------------------------------------------------------- + */ + +int +TkpMenuNewEntry( + TkMenuEntry *mePtr) +{ + WORD commandID; + TkMenu *menuPtr = mePtr->menuPtr; + + if (GetNewID(mePtr, &commandID) != TCL_OK) { + return TCL_ERROR; + } + ScheduleMenuReconfigure(menuPtr); + mePtr->platformEntryData = (TkMenuPlatformEntryData) INT2PTR(commandID); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinMenuProc -- + * + * The window proc for the dummy window we put popups in. This allows + * is to post a popup whether or not we know what the parent window + * is. + * + * Results: + * Returns whatever is appropriate for the message in question. + * + * Side effects: + * Normal side-effect for windows messages. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +TkWinMenuProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT lResult; + + if (!TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, &lResult)) { + lResult = DefWindowProc(hwnd, message, wParam, lParam); + } + return lResult; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateEmbeddedMenu -- + * + * This function is used as work-around for updating the pull-down window + * of an embedded menu which may show as a blank popup window. + * + * Results: + * Invalidate the client area of the embedded pull-down menu and + * redraw it. + * + * Side effects: + * Redraw the embedded menu window. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateEmbeddedMenu( + ClientData clientData) +{ + RECT rc; + HWND hMenuWnd = (HWND)clientData; + + GetClientRect(hMenuWnd, &rc); + InvalidateRect(hMenuWnd, &rc, FALSE); + UpdateWindow(hMenuWnd); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinEmbeddedMenuProc -- + * + * This window proc is for the embedded menu windows. It provides + * message services to an embedded menu in a different process. + * + * Results: + * Returns 1 if the message has been handled or 0 otherwise. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +TkWinEmbeddedMenuProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + static int nIdles = 0; + LRESULT lResult = 1; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + switch(message) { + case WM_ENTERIDLE: + if ((wParam == MSGF_MENU) && (nIdles < 1) + && (hwnd == tsdPtr->embeddedMenuHWND)) { + Tcl_CreateTimerHandler(200, UpdateEmbeddedMenu, + (ClientData) lParam); + nIdles++; + } + break; + + case WM_INITMENUPOPUP: + nIdles = 0; + break; + + case WM_SETTINGCHANGE: + if (wParam == SPI_SETNONCLIENTMETRICS + || wParam == SPI_SETKEYBOARDCUES) { + SetDefaults(0); + } + break; + + case WM_INITMENU: + case WM_SYSCOMMAND: + case WM_COMMAND: + case WM_MENUCHAR: + case WM_MEASUREITEM: + case WM_DRAWITEM: + case WM_MENUSELECT: + lResult = TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, + &lResult); + if (lResult || (GetCapture() != hwnd)) { + break; + } + + default: + lResult = DefWindowProc(hwnd, message, wParam, lParam); + break; + } + return lResult; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinHandleMenuEvent -- + * + * Filters out menu messages from messages passed to a top-level. Will + * respond appropriately to WM_COMMAND, WM_MENUSELECT, WM_MEASUREITEM, + * WM_DRAWITEM + * + * Result: + * Returns 1 if this handled the message; 0 if it did not. + * + * Side effects: + * All of the parameters may be modified so that the caller can think it + * is getting a different message. plResult points to the result that + * should be returned to windows from this message. + * + *---------------------------------------------------------------------- + */ + +int +TkWinHandleMenuEvent( + HWND *phwnd, + UINT *pMessage, + WPARAM *pwParam, + LPARAM *plParam, + LRESULT *plResult) +{ + Tcl_HashEntry *hashEntryPtr; + int returnResult = 0; + TkMenu *menuPtr; + TkMenuEntry *mePtr; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + switch (*pMessage) { + case WM_UNINITMENUPOPUP: + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable, + (char *) *pwParam); + if (hashEntryPtr != NULL) { + menuPtr = Tcl_GetHashValue(hashEntryPtr); + if ((menuPtr->menuRefPtr != NULL) + && (menuPtr->menuRefPtr->parentEntryPtr != NULL)) { + TkPostSubmenu(menuPtr->interp, + menuPtr->menuRefPtr->parentEntryPtr->menuPtr, NULL); + } + } + break; + + case WM_INITMENU: + TkMenuInit(); + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable, + (char *) *pwParam); + if (hashEntryPtr != NULL) { + tsdPtr->oldServiceMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + menuPtr = Tcl_GetHashValue(hashEntryPtr); + tsdPtr->modalMenuPtr = menuPtr; + CallPendingReconfigureImmediately(menuPtr); + RecursivelyClearActiveMenu(menuPtr); + if (!tsdPtr->inPostMenu) { + Tcl_Interp *interp = menuPtr->interp; + int code; + + Tcl_Preserve(interp); + code = TkPreprocessMenu(menuPtr); + if ((code != TCL_OK) && (code != TCL_CONTINUE) + && (code != TCL_BREAK)) { + Tcl_AddErrorInfo(interp, "\n (menu preprocess)"); + Tcl_BackgroundException(interp, code); + } + Tcl_Release(interp); + } + TkActivateMenuEntry(menuPtr, -1); + *plResult = 0; + returnResult = 1; + } else { + tsdPtr->modalMenuPtr = NULL; + } + break; + + case WM_SYSCOMMAND: + case WM_COMMAND: + TkMenuInit(); + if (HIWORD(*pwParam) != 0) { + break; + } + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->commandTable, + INT2PTR(LOWORD(*pwParam))); + if (hashEntryPtr == NULL) { + break; + } + mePtr = Tcl_GetHashValue(hashEntryPtr); + if (mePtr != NULL) { + TkMenuReferences *menuRefPtr; + TkMenuEntry *parentEntryPtr; + Tcl_Interp *interp; + int code; + + /* + * We have to set the parent of this menu to be active if this is + * a submenu so that tearoffs will get the correct title. + */ + + menuPtr = mePtr->menuPtr; + menuRefPtr = TkFindMenuReferences(menuPtr->interp, + Tk_PathName(menuPtr->tkwin)); + if ((menuRefPtr != NULL) && (menuRefPtr->parentEntryPtr != NULL)) { + for (parentEntryPtr = menuRefPtr->parentEntryPtr ; ; + parentEntryPtr = parentEntryPtr->nextCascadePtr) { + const char *name = Tcl_GetString(parentEntryPtr->namePtr); + + if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { + break; + } + } + if (parentEntryPtr->menuPtr->entries[parentEntryPtr->index] + ->state != ENTRY_DISABLED) { + TkActivateMenuEntry(parentEntryPtr->menuPtr, + parentEntryPtr->index); + } + } + + interp = menuPtr->interp; + Tcl_Preserve(interp); + code = TkInvokeMenu(interp, menuPtr, mePtr->index); + if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (menu invoke)"); + Tcl_BackgroundException(interp, code); + } + Tcl_Release(interp); + *plResult = 0; + returnResult = 1; + } + break; + + case WM_MENUCHAR: { + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable, + (char *) *plParam); + if (hashEntryPtr != NULL) { + int i, len, underline; + Tcl_Obj *labelPtr; + Tcl_UniChar *wlabel, menuChar; + + *plResult = 0; + menuPtr = Tcl_GetHashValue(hashEntryPtr); + /* + * Assume we have something directly convertable to Tcl_UniChar. + * True at least for wide systems. + */ + menuChar = Tcl_UniCharToUpper((Tcl_UniChar) LOWORD(*pwParam)); + + for (i = 0; i < menuPtr->numEntries; i++) { + underline = menuPtr->entries[i]->underline; + labelPtr = menuPtr->entries[i]->labelPtr; + if ((underline >= 0) && (labelPtr != NULL)) { + /* + * Ensure we don't exceed the label length, then check + */ + wlabel = Tcl_GetUnicodeFromObj(labelPtr, &len); + if ((underline < len) && (menuChar == + Tcl_UniCharToUpper(wlabel[underline]))) { + *plResult = (2 << 16) | i; + returnResult = 1; + break; + } + } + } + } + break; + } + + case WM_MEASUREITEM: { + LPMEASUREITEMSTRUCT itemPtr = (LPMEASUREITEMSTRUCT) *plParam; + + if (itemPtr != NULL && tsdPtr->modalMenuPtr != NULL) { + mePtr = (TkMenuEntry *) itemPtr->itemData; + menuPtr = mePtr->menuPtr; + + TkRecomputeMenu(menuPtr); + itemPtr->itemHeight = mePtr->height; + itemPtr->itemWidth = mePtr->width; + if (mePtr->hideMargin) { + itemPtr->itemWidth += 2 - indicatorDimensions[1]; + } else { + int activeBorderWidth; + + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->activeBorderWidthPtr, &activeBorderWidth); + itemPtr->itemWidth += 2 * activeBorderWidth; + } + *plResult = 1; + returnResult = 1; + } + break; + } + + case WM_DRAWITEM: { + TkWinDrawable *twdPtr; + LPDRAWITEMSTRUCT itemPtr = (LPDRAWITEMSTRUCT) *plParam; + Tk_FontMetrics fontMetrics; + int drawingParameters = 0; + + if (itemPtr != NULL && tsdPtr->modalMenuPtr != NULL) { + Tk_Font tkfont; + + if (itemPtr->itemState & ODS_NOACCEL && !showMenuAccelerators) { + drawingParameters |= DRAW_MENU_ENTRY_NOUNDERLINE; + } + mePtr = (TkMenuEntry *) itemPtr->itemData; + menuPtr = mePtr->menuPtr; + twdPtr = ckalloc(sizeof(TkWinDrawable)); + twdPtr->type = TWD_WINDC; + twdPtr->winDC.hdc = itemPtr->hDC; + + if (mePtr->state != ENTRY_DISABLED) { + if (itemPtr->itemState & ODS_SELECTED) { + TkActivateMenuEntry(menuPtr, mePtr->index); + } else { + TkActivateMenuEntry(menuPtr, -1); + } + } else { + /* + * On windows, menu entries should highlight even if they are + * disabled. (I know this seems dumb, but it is the way native + * windows menus works so we ought to mimic it.) The + * ENTRY_PLATFORM_FLAG1 flag will indicate that the entry + * should be highlighted even though it is disabled. + */ + + if (itemPtr->itemState & ODS_SELECTED) { + mePtr->entryFlags |= ENTRY_PLATFORM_FLAG1; + } else { + mePtr->entryFlags &= ~ENTRY_PLATFORM_FLAG1; + } + + /* + * Also, set the DRAW_MENU_ENTRY_ARROW flag for a disabled + * cascade menu since we need to draw the arrow ourselves. + */ + + if (mePtr->type == CASCADE_ENTRY) { + drawingParameters |= DRAW_MENU_ENTRY_ARROW; + } + } + + tkfont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); + Tk_GetFontMetrics(tkfont, &fontMetrics); + TkpDrawMenuEntry(mePtr, (Drawable) twdPtr, tkfont, &fontMetrics, + itemPtr->rcItem.left, itemPtr->rcItem.top, + itemPtr->rcItem.right - itemPtr->rcItem.left, + itemPtr->rcItem.bottom - itemPtr->rcItem.top, + 0, drawingParameters); + + ckfree(twdPtr); + } + *plResult = 1; + returnResult = 1; + break; + } + + case WM_MENUSELECT: { + UINT flags = HIWORD(*pwParam); + + TkMenuInit(); + + if ((flags == 0xFFFF) && (*plParam == 0)) { + if (tsdPtr->modalMenuPtr != NULL) { + Tcl_SetServiceMode(tsdPtr->oldServiceMode); + RecursivelyClearActiveMenu(tsdPtr->modalMenuPtr); + } + } else { + menuPtr = NULL; + if (*plParam != 0) { + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable, + (char *) *plParam); + if (hashEntryPtr != NULL) { + menuPtr = Tcl_GetHashValue(hashEntryPtr); + } + } + + if (menuPtr != NULL) { + long entryIndex = LOWORD(*pwParam); + + if ((menuPtr->menuType == MENUBAR) && menuPtr->tearoff) { + /* + * Windows passes the entry index starting at 0 for + * the first menu entry. However this entry #0 is the + * tearoff entry for Tk (the menu has -tearoff 1), + * which is ignored for MENUBAR menues on Windows. + */ + + entryIndex++; + } + mePtr = NULL; + if (flags != 0xFFFF) { + if ((flags&MF_POPUP) && (entryIndex<menuPtr->numEntries)) { + mePtr = menuPtr->entries[entryIndex]; + } else { + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->commandTable, + INT2PTR(entryIndex)); + if (hashEntryPtr != NULL) { + mePtr = Tcl_GetHashValue(hashEntryPtr); + } + } + } + + if ((mePtr == NULL) || (mePtr->state == ENTRY_DISABLED)) { + TkActivateMenuEntry(menuPtr, -1); + } else { + if (mePtr->index >= menuPtr->numEntries) { + Tcl_Panic("Trying to activate an entry which doesn't exist"); + } + TkActivateMenuEntry(menuPtr, mePtr->index); + } + MenuSelectEvent(menuPtr); + Tcl_ServiceAll(); + *plResult = 0; + returnResult = 1; + } + } + break; + } + } + return returnResult; +} + +/* + *---------------------------------------------------------------------- + * + * RecursivelyClearActiveMenu -- + * + * Recursively clears the active entry in the menu's cascade hierarchy. + * + * Results: + * None. + * + * Side effects: + * Generates <<MenuSelect>> virtual events. + * + *---------------------------------------------------------------------- + */ + +void +RecursivelyClearActiveMenu( + TkMenu *menuPtr) /* The menu to reset. */ +{ + int i; + TkMenuEntry *mePtr; + + TkActivateMenuEntry(menuPtr, -1); + MenuSelectEvent(menuPtr); + for (i = 0; i < menuPtr->numEntries; i++) { + mePtr = menuPtr->entries[i]; + if (mePtr->state == ENTRY_ACTIVE) { + mePtr->state = ENTRY_NORMAL; + } + mePtr->entryFlags &= ~ENTRY_PLATFORM_FLAG1; + if (mePtr->type == CASCADE_ENTRY) { + if ((mePtr->childMenuRefPtr != NULL) + && (mePtr->childMenuRefPtr->menuPtr != NULL)) { + RecursivelyClearActiveMenu(mePtr->childMenuRefPtr->menuPtr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetWindowMenuBar -- + * + * Associates a given menu with a window. + * + * Results: + * None. + * + * Side effects: + * On Windows and UNIX, associates the platform menu with the + * platform window. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetWindowMenuBar( + Tk_Window tkwin, /* The window we are putting the menubar + * into.*/ + TkMenu *menuPtr) /* The menu we are inserting */ +{ + HMENU winMenuHdl; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (menuPtr != NULL) { + Tcl_HashEntry *hashEntryPtr; + int newEntry; + + winMenuHdl = (HMENU) menuPtr->platformData; + hashEntryPtr = Tcl_FindHashEntry(&tsdPtr->winMenuTable, + (char *) winMenuHdl); + Tcl_DeleteHashEntry(hashEntryPtr); + DestroyMenu(winMenuHdl); + winMenuHdl = CreateMenu(); + hashEntryPtr = Tcl_CreateHashEntry(&tsdPtr->winMenuTable, + (char *) winMenuHdl, &newEntry); + Tcl_SetHashValue(hashEntryPtr, menuPtr); + menuPtr->platformData = (TkMenuPlatformData) winMenuHdl; + TkWinSetMenu(tkwin, winMenuHdl); + ScheduleMenuReconfigure(menuPtr); + } else { + TkWinSetMenu(tkwin, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetMainMenubar -- + * + * Puts the menu associated with a window into the menubar. Should only + * be called when the window is in front. + * + * Results: + * None. + * + * Side effects: + * The menubar is changed. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetMainMenubar( + Tcl_Interp *interp, /* The interpreter of the application */ + Tk_Window tkwin, /* The frame we are setting up */ + const char *menuName) /* The name of the menu to put in front. If + * NULL, use the default menu bar. */ +{ + /* + * Nothing to do. + */ +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuIndicatorGeometry -- + * + * Gets the width and height of the indicator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuIndicatorGeometry( + TkMenu *menuPtr, /* The menu we are measuring */ + TkMenuEntry *mePtr, /* The entry we are measuring */ + Tk_Font tkfont, /* Precalculated font */ + const Tk_FontMetrics *fmPtr,/* Precalculated font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *heightPtr = indicatorDimensions[0]; + if (mePtr->hideMargin) { + *widthPtr = 0; + } else { + int borderWidth; + + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->borderWidthPtr, &borderWidth); + *widthPtr = indicatorDimensions[1] - borderWidth; + } +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuAccelGeometry -- + * + * Gets the width and height of the indicator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuAccelGeometry( + TkMenu *menuPtr, /* The menu we are measuring */ + TkMenuEntry *mePtr, /* The entry we are measuring */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *heightPtr = fmPtr->linespace; + if (mePtr->type == CASCADE_ENTRY) { + *widthPtr = 0; + } else if ((menuPtr->menuType != MENUBAR) && (mePtr->accelPtr != NULL)) { + const char *accel = Tcl_GetString(mePtr->accelPtr); + + *widthPtr = Tk_TextWidth(tkfont, accel, mePtr->accelLength); + } else { + *widthPtr = 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * GetTearoffEntryGeometry -- + * + * Gets the width and height of the indicator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetTearoffEntryGeometry( + TkMenu *menuPtr, /* The menu we are measuring */ + TkMenuEntry *mePtr, /* The entry we are measuring */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + if (menuPtr->menuType != MASTER_MENU) { + *heightPtr = 0; + } else { + *heightPtr = fmPtr->linespace; + } + *widthPtr = 0; +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuSeparatorGeometry -- + * + * Gets the width and height of the indicator area of a menu. + * + * Results: + * widthPtr and heightPtr are set. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +GetMenuSeparatorGeometry( + TkMenu *menuPtr, /* The menu we are measuring */ + TkMenuEntry *mePtr, /* The entry we are measuring */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalcualted font metrics */ + int *widthPtr, /* The resulting width */ + int *heightPtr) /* The resulting height */ +{ + *widthPtr = 0; + *heightPtr = fmPtr->linespace - (2 * fmPtr->descent); +} + +/* + *---------------------------------------------------------------------- + * + * DrawWindowsSystemBitmap -- + * + * Draws the windows system bitmap given by bitmapID into the rect given + * by rectPtr in the drawable. The bitmap is centered in the rectangle. + * It is not clipped, so if the bitmap is bigger than the rect it will + * bleed. + * + * Results: + * None. + * + * Side effects: + * Drawing occurs. Some storage is allocated and released. + * + *---------------------------------------------------------------------- + */ + +static void +DrawWindowsSystemBitmap( + Display *display, /* The display we are drawing into */ + Drawable drawable, /* The drawable we are working with */ + GC gc, /* The GC to draw with */ + const RECT *rectPtr, /* The rectangle to draw into */ + int bitmapID, /* The windows id of the system bitmap to + * draw. */ + int alignFlags) /* How to align the bitmap inside the + * rectangle. */ +{ + TkWinDCState state; + HDC hdc = TkWinGetDrawableDC(display, drawable, &state); + HDC scratchDC; + HBITMAP bitmap; + BITMAP bm; + POINT ptSize; + POINT ptOrg; + int topOffset, leftOffset; + + SetBkColor(hdc, gc->background); + SetTextColor(hdc, gc->foreground); + + scratchDC = CreateCompatibleDC(hdc); + bitmap = LoadBitmap(NULL, MAKEINTRESOURCE(bitmapID)); + + SelectObject(scratchDC, bitmap); + SetMapMode(scratchDC, GetMapMode(hdc)); + GetObjectA(bitmap, sizeof(BITMAP), &bm); + ptSize.x = bm.bmWidth; + ptSize.y = bm.bmHeight; + DPtoLP(scratchDC, &ptSize, 1); + + ptOrg.y = ptOrg.x = 0; + DPtoLP(scratchDC, &ptOrg, 1); + + if (alignFlags & ALIGN_BITMAP_TOP) { + topOffset = 0; + } else if (alignFlags & ALIGN_BITMAP_BOTTOM) { + topOffset = (rectPtr->bottom - rectPtr->top) - ptSize.y; + } else { + topOffset = (rectPtr->bottom - rectPtr->top) / 2 - (ptSize.y / 2); + } + + if (alignFlags & ALIGN_BITMAP_LEFT) { + leftOffset = 0; + } else if (alignFlags & ALIGN_BITMAP_RIGHT) { + leftOffset = (rectPtr->right - rectPtr->left) - ptSize.x; + } else { + leftOffset = (rectPtr->right - rectPtr->left) / 2 - (ptSize.x / 2); + } + + BitBlt(hdc, rectPtr->left + leftOffset, rectPtr->top + topOffset, ptSize.x, + ptSize.y, scratchDC, ptOrg.x, ptOrg.y, SRCCOPY); + DeleteDC(scratchDC); + DeleteObject(bitmap); + + TkWinReleaseDrawableDC(drawable, hdc, &state); +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryIndicator -- + * + * This function draws the indicator part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryIndicator( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc we are drawing with */ + GC indicatorGC, /* The gc for indicator objects */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* Left edge */ + int y, /* Top edge */ + int width, + int height) +{ + if ((mePtr->type == CHECK_BUTTON_ENTRY) + || (mePtr->type == RADIO_BUTTON_ENTRY)) { + if (mePtr->indicatorOn && (mePtr->entryFlags & ENTRY_SELECTED)) { + RECT rect; + GC whichGC; + int borderWidth, activeBorderWidth; + + if (mePtr->state != ENTRY_NORMAL) { + whichGC = gc; + } else { + whichGC = indicatorGC; + } + + rect.top = y; + rect.bottom = y + mePtr->height; + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->borderWidthPtr, &borderWidth); + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->activeBorderWidthPtr, &activeBorderWidth); + rect.left = borderWidth + activeBorderWidth + x; + rect.right = mePtr->indicatorSpace + x; + + if ((mePtr->state == ENTRY_DISABLED) + && (menuPtr->disabledFgPtr != NULL)) { + RECT hilightRect; + COLORREF oldFgColor = whichGC->foreground; + + whichGC->foreground = GetSysColor(COLOR_3DHILIGHT); + hilightRect.top = rect.top + 1; + hilightRect.bottom = rect.bottom + 1; + hilightRect.left = rect.left + 1; + hilightRect.right = rect.right + 1; + DrawWindowsSystemBitmap(menuPtr->display, d, whichGC, + &hilightRect, OBM_CHECK, 0); + whichGC->foreground = oldFgColor; + } + + DrawWindowsSystemBitmap(menuPtr->display, d, whichGC, &rect, + OBM_CHECK, 0); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryAccelerator -- + * + * This function draws the accelerator part of a menu. For example, the + * string "CTRL-Z" could be drawn to to the right of the label text for + * an Undo menu entry. Need to decide what to draw here. Should we + * replace strings like "Control", "Command", etc? + * + * Results: + * None. + * + * Side effects: + * Commands are output to display the menu in its + * current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryAccelerator( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc we are drawing with */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + Tk_3DBorder activeBorder, /* The border when an item is active */ + int x, /* left edge */ + int y, /* top edge */ + int width, /* Width of menu entry */ + int height) /* Height of menu entry */ +{ + int baseline; + int leftEdge = x + mePtr->indicatorSpace + mePtr->labelWidth; + const char *accel; + + if (menuPtr->menuType == MENUBAR) { + return; + } + + if (mePtr->accelPtr != NULL) { + accel = Tcl_GetString(mePtr->accelPtr); + } else { + accel = NULL; + } + + baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; + + /* + * Draw disabled 3D text highlight only with the Win95/98 look. + */ + + if (TkWinGetPlatformTheme() == TK_THEME_WIN_CLASSIC) { + if ((mePtr->state == ENTRY_DISABLED) + && (menuPtr->disabledFgPtr != NULL) && (accel != NULL)) { + COLORREF oldFgColor = gc->foreground; + + gc->foreground = GetSysColor(COLOR_3DHILIGHT); + if (!(mePtr->entryFlags & ENTRY_PLATFORM_FLAG1)) { + Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, + mePtr->accelLength, leftEdge + 1, baseline + 1); + } + gc->foreground = oldFgColor; + } + } + + if (accel != NULL) { + Tk_DrawChars(menuPtr->display, d, gc, tkfont, accel, + mePtr->accelLength, leftEdge, baseline); + } +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryArrow -- + * + * This function draws the arrow bitmap on the right side of a menu + * entry. This function is only used when drawing the arrow for a + * disabled cascade menu. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuEntryArrow( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc we are drawing with */ + Tk_3DBorder activeBorder, /* The border when an item is active */ + int x, /* left edge */ + int y, /* top edge */ + int width, /* Width of menu entry */ + int height, /* Height of menu entry */ + int drawArrow) /* For cascade menus, whether of not to draw + * the arraw. I cannot figure out Windows' + * algorithm for where to draw this. */ +{ + COLORREF oldFgColor; + COLORREF oldBgColor; + RECT rect; + + if (!drawArrow || (mePtr->type != CASCADE_ENTRY)) { + return; + } + + oldFgColor = gc->foreground; + oldBgColor = gc->background; + + /* + * Set bitmap bg to highlight color if the menu is highlighted. + */ + + if (mePtr->entryFlags & ENTRY_PLATFORM_FLAG1) { + XColor *activeBgColor = Tk_3DBorderColor(Tk_Get3DBorderFromObj( + mePtr->menuPtr->tkwin, (mePtr->activeBorderPtr == NULL) + ? mePtr->menuPtr->activeBorderPtr + : mePtr->activeBorderPtr)); + + gc->background = activeBgColor->pixel; + } + + gc->foreground = GetSysColor((mePtr->state == ENTRY_DISABLED) + ? COLOR_GRAYTEXT + : ((mePtr->state == ENTRY_ACTIVE) + ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT)); + + rect.top = y + GetSystemMetrics(SM_CYBORDER); + rect.bottom = y + height - GetSystemMetrics(SM_CYBORDER); + rect.left = x + mePtr->indicatorSpace + mePtr->labelWidth; + rect.right = x + width; + + DrawWindowsSystemBitmap(menuPtr->display, d, gc, &rect, OBM_MNARROW, + ALIGN_BITMAP_RIGHT); + + gc->foreground = oldFgColor; + gc->background = oldBgColor; + return; +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuSeparator -- + * + * The menu separator is drawn. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawMenuSeparator( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc we are drawing with */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* left edge */ + int y, /* top edge */ + int width, /* width of item */ + int height) /* height of item */ +{ + XPoint points[2]; + Tk_3DBorder border; + + points[0].x = x; + points[0].y = y + height / 2; + points[1].x = x + width - 1; + points[1].y = points[0].y; + border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); + Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, + TK_RELIEF_RAISED); +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuUnderline -- + * + * On appropriate platforms, draw the underline character for the menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +static void +DrawMenuUnderline( + TkMenu *menuPtr, /* The menu to draw into */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc to draw into */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* Left Edge */ + int y, /* Top Edge */ + int width, /* Width of entry */ + int height) /* Height of entry */ +{ + if ((mePtr->underline >= 0) && (mePtr->labelPtr != NULL)) { + int len; + + /* do the unicode call just to prevent overruns */ + Tcl_GetUnicodeFromObj(mePtr->labelPtr, &len); + if (mePtr->underline < len) { + const char *label, *start, *end; + + label = Tcl_GetString(mePtr->labelPtr); + start = Tcl_UtfAtIndex(label, mePtr->underline); + end = Tcl_UtfNext(start); + Tk_UnderlineChars(menuPtr->display, d, + gc, tkfont, label, x + mePtr->indicatorSpace, + y + (height + fmPtr->ascent - fmPtr->descent) / 2, + (int) (start - label), (int) (end - label)); + } + } +} + +/* + *-------------------------------------------------------------- + * + * TkWinMenuKeyObjCmd -- + * + * This function is invoked when keys related to pulling down menus is + * pressed. The corresponding Windows events are generated and passed to + * DefWindowProc if appropriate. This cmd is registered as tk::WinMenuKey + * in the interp. + * + * Results: + * Always returns TCL_OK. + * + * Side effects: + * The menu system may take over and process user events for menu input. + * + *-------------------------------------------------------------- + */ + +static int +TkWinMenuKeyObjCmd( + ClientData clientData, /* Unused. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + UINT scanCode; + UINT virtualKey; + XEvent *eventPtr; + Tk_Window tkwin; + TkWindow *winPtr; + KeySym keySym; + int i; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "window keySym"); + return TCL_ERROR; + } + + tkwin = Tk_NameToWindow(interp, Tcl_GetString(objv[1]), + Tk_MainWindow(interp)); + + if (tkwin == NULL) { + /* + * If we don't find the key, just return, as the window may have + * been destroyed in the binding. [Bug 1236306] + */ + return TCL_OK; + } + + eventPtr = TkpGetBindingXEvent(interp); + + winPtr = (TkWindow *)tkwin; + + if (Tcl_GetIntFromObj(interp, objv[2], &i) != TCL_OK) { + return TCL_ERROR; + } + keySym = i; + + if (eventPtr->type == KeyPress) { + switch (keySym) { + case XK_Alt_L: + scanCode = MapVirtualKey(VK_LMENU, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYDOWN, VK_MENU, + (int) (scanCode << 16) | (1 << 29)); + break; + case XK_Alt_R: + scanCode = MapVirtualKey(VK_RMENU, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYDOWN, VK_MENU, + (int) (scanCode << 16) | (1 << 29) | (1 << 24)); + break; + case XK_F10: + scanCode = MapVirtualKey(VK_F10, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYDOWN, VK_F10, (int) (scanCode << 16)); + break; + default: + virtualKey = XKeysymToKeycode(winPtr->display, keySym); + scanCode = MapVirtualKey(virtualKey, 0); + if (0 != scanCode) { + XKeyEvent xkey = eventPtr->xkey; + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYDOWN, virtualKey, + (int) ((scanCode << 16) | (1 << 29))); + if (xkey.nbytes > 0) { + for (i = 0; i < xkey.nbytes; i++) { + CallWindowProc(DefWindowProc, + Tk_GetHWND(Tk_WindowId(tkwin)), WM_SYSCHAR, + xkey.trans_chars[i], + (int) ((scanCode << 16) | (1 << 29))); + } + } + } + } + } else if (eventPtr->type == KeyRelease) { + switch (keySym) { + case XK_Alt_L: + scanCode = MapVirtualKey(VK_LMENU, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYUP, VK_MENU, (int) (scanCode << 16) + | (1 << 29) | (1 << 30) | (1 << 31)); + break; + case XK_Alt_R: + scanCode = MapVirtualKey(VK_RMENU, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYUP, VK_MENU, (int) (scanCode << 16) | (1 << 24) + | (1 << 29) | (1 << 30) | (1 << 31)); + break; + case XK_F10: + scanCode = MapVirtualKey(VK_F10, 0); + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYUP, VK_F10, + (int) (scanCode << 16) | (1 << 30) | (1 << 31)); + break; + default: + virtualKey = XKeysymToKeycode(winPtr->display, keySym); + scanCode = MapVirtualKey(virtualKey, 0); + if (0 != scanCode) { + CallWindowProc(DefWindowProc, Tk_GetHWND(Tk_WindowId(tkwin)), + WM_SYSKEYUP, virtualKey, (int) ((scanCode << 16) + | (1 << 29) | (1 << 30) | (1 << 31))); + } + } + } + return TCL_OK; +} + +/* + *-------------------------------------------------------------- + * + * TkpInitializeMenuBindings -- + * + * For every interp, initializes the bindings for Windows menus. Does + * nothing on Mac or XWindows. + * + * Results: + * None. + * + * Side effects: + * bindings are setup for the interp which will handle Alt-key sequences + * for menus without beeping or interfering with user-defined Alt-key + * bindings. + * + *-------------------------------------------------------------- + */ + +void +TkpInitializeMenuBindings( + Tcl_Interp *interp, /* The interpreter to set. */ + Tk_BindingTable bindingTable) + /* The table to add to. */ +{ + Tk_Uid uid = Tk_GetUid("all"); + + /* + * We need to set up the bindings for menubars. These have to recreate + * windows events, so we need to invoke C code to generate the + * WM_SYSKEYDOWNS and WM_SYSKEYUPs appropriately. Trick is, we can't + * create a C level binding directly since we may want to modify the + * binding in Tcl code. + */ + + (void) Tcl_CreateObjCommand(interp, "tk::WinMenuKey", + TkWinMenuKeyObjCmd, + (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<Alt_L>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<KeyRelease-Alt_L>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<Alt_R>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<KeyRelease-Alt_R>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<Alt-KeyPress>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<Alt-KeyRelease>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<KeyPress-F10>", "tk::WinMenuKey %W %N", 0); + + (void) Tk_CreateBinding(interp, bindingTable, (ClientData) uid, + "<KeyRelease-F10>", "tk::WinMenuKey %W %N", 0); +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryLabel -- + * + * This function draws the label part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its + * current mode. + * + *---------------------------------------------------------------------- + */ + +static void +DrawMenuEntryLabel( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* What we are drawing into */ + GC gc, /* The gc we are drawing into */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated font metrics */ + int x, /* left edge */ + int y, /* right edge */ + int width, /* width of entry */ + int height, /* height of entry */ + int underline) /* accelerator cue should be drawn */ +{ + int indicatorSpace = mePtr->indicatorSpace; + int activeBorderWidth; + int leftEdge; + int imageHeight, imageWidth; + int textHeight = 0, textWidth = 0; + int haveImage = 0, haveText = 0; + int imageXOffset = 0, imageYOffset = 0; + int textXOffset = 0, textYOffset = 0; + + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->activeBorderWidthPtr, &activeBorderWidth); + leftEdge = x + indicatorSpace + activeBorderWidth; + + /* + * Work out what we will need to draw first. + */ + + if (mePtr->image != NULL) { + Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight); + haveImage = 1; + } else if (mePtr->bitmapPtr != NULL) { + Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); + + Tk_SizeOfBitmap(menuPtr->display, bitmap, &imageWidth, &imageHeight); + haveImage = 1; + } + if (!haveImage || (mePtr->compound != COMPOUND_NONE)) { + if (mePtr->labelLength > 0) { + const char *label = Tcl_GetString(mePtr->labelPtr); + + textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); + textHeight = fmPtr->linespace; + haveText = 1; + } + } + + /* + * Now work out what the relative positions are. + */ + + if (haveImage && haveText) { + int fullWidth = (imageWidth > textWidth ? imageWidth : textWidth); + switch ((enum compound) mePtr->compound) { + case COMPOUND_TOP: + textXOffset = (fullWidth - textWidth)/2; + textYOffset = imageHeight/2 + 2; + imageXOffset = (fullWidth - imageWidth)/2; + imageYOffset = -textHeight/2; + break; + case COMPOUND_BOTTOM: + textXOffset = (fullWidth - textWidth)/2; + textYOffset = -imageHeight/2; + imageXOffset = (fullWidth - imageWidth)/2; + imageYOffset = textHeight/2 + 2; + break; + case COMPOUND_LEFT: + /* + * The standard image position on Windows is in the indicator + * space to the left of the entries, unless this entry is a + * radio|check button because then the indicator space will be + * used. + */ + + textXOffset = imageWidth + 2; + textYOffset = 0; + imageXOffset = 0; + imageYOffset = 0; + if ((mePtr->type != CHECK_BUTTON_ENTRY) + && (mePtr->type != RADIO_BUTTON_ENTRY)) { + textXOffset -= indicatorSpace; + if (textXOffset < 0) { + textXOffset = 0; + } + imageXOffset = -indicatorSpace; + } + break; + case COMPOUND_RIGHT: + textXOffset = 0; + textYOffset = 0; + imageXOffset = textWidth + 2; + imageYOffset = 0; + break; + case COMPOUND_CENTER: + textXOffset = (fullWidth - textWidth)/2; + textYOffset = 0; + imageXOffset = (fullWidth - imageWidth)/2; + imageYOffset = 0; + break; + case COMPOUND_NONE: + break; + } + } else { + textXOffset = 0; + textYOffset = 0; + imageXOffset = 0; + imageYOffset = 0; + } + + /* + * Draw label and/or bitmap or image for entry. + */ + + if (mePtr->image != NULL) { + if ((mePtr->selectImage != NULL) + && (mePtr->entryFlags & ENTRY_SELECTED)) { + Tk_RedrawImage(mePtr->selectImage, 0, 0, + imageWidth, imageHeight, d, leftEdge + imageXOffset, + (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); + } else { + Tk_RedrawImage(mePtr->image, 0, 0, imageWidth, + imageHeight, d, leftEdge + imageXOffset, + (int) (y + (mePtr->height-imageHeight)/2 + imageYOffset)); + } + } else if (mePtr->bitmapPtr != NULL) { + Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); + XCopyPlane(menuPtr->display, bitmap, d, gc, 0, 0, + (unsigned) imageWidth, (unsigned) imageHeight, + leftEdge + imageXOffset, + (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), 1); + } + if ((mePtr->compound != COMPOUND_NONE) || !haveImage) { + if (mePtr->labelLength > 0) { + int baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2; + const char *label = Tcl_GetString(mePtr->labelPtr); + + if (TkWinGetPlatformTheme() == TK_THEME_WIN_CLASSIC) { + /* + * Win 95/98 systems draw disabled menu text with a 3D + * highlight, unless the menu item is highlighted, + */ + + if ((mePtr->state == ENTRY_DISABLED) && + !(mePtr->entryFlags & ENTRY_PLATFORM_FLAG1)) { + COLORREF oldFgColor = gc->foreground; + + gc->foreground = GetSysColor(COLOR_3DHILIGHT); + Tk_DrawChars(menuPtr->display, d, gc, tkfont, label, + mePtr->labelLength, leftEdge + textXOffset + 1, + baseline + textYOffset + 1); + gc->foreground = oldFgColor; + } + } + Tk_DrawChars(menuPtr->display, d, gc, tkfont, label, + mePtr->labelLength, leftEdge + textXOffset, + baseline + textYOffset); + if (underline) { + DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, + x + textXOffset, y + textYOffset, width, height); + } + } + } + + if (mePtr->state == ENTRY_DISABLED) { + if (menuPtr->disabledFgPtr == NULL) { + XFillRectangle(menuPtr->display, d, menuPtr->disabledGC, x, y, + (unsigned) width, (unsigned) height); + } else if ((mePtr->image != NULL) + && (menuPtr->disabledImageGC != None)) { + XFillRectangle(menuPtr->display, d, menuPtr->disabledImageGC, + leftEdge + imageXOffset, + (int) (y + (mePtr->height - imageHeight)/2 + imageYOffset), + (unsigned) imageWidth, (unsigned) imageHeight); + } + } +} + +/* + *-------------------------------------------------------------- + * + * TkpComputeMenubarGeometry -- + * + * This function is invoked to recompute the size and layout of a menu + * that is a menubar clone. + * + * Results: + * None. + * + * Side effects: + * Fields of menu entries are changed to reflect their current positions, + * and the size of the menu window itself may be changed. + * + *-------------------------------------------------------------- + */ + +void +TkpComputeMenubarGeometry( + TkMenu *menuPtr) /* Structure describing menu. */ +{ + TkpComputeStandardMenuGeometry(menuPtr); +} + +/* + *---------------------------------------------------------------------- + * + * DrawTearoffEntry -- + * + * This function draws the background part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +void +DrawTearoffEntry( + TkMenu *menuPtr, /* The menu we are drawing */ + TkMenuEntry *mePtr, /* The entry we are drawing */ + Drawable d, /* The drawable we are drawing into */ + GC gc, /* The gc we are drawing with */ + Tk_Font tkfont, /* The font we are drawing with */ + const Tk_FontMetrics *fmPtr,/* The metrics we are drawing with */ + int x, int y, + int width, int height) +{ + XPoint points[2]; + int segmentWidth, maxX; + Tk_3DBorder border; + + if (menuPtr->menuType != MASTER_MENU) { + return; + } + + points[0].x = x; + points[0].y = y + height/2; + points[1].y = points[0].y; + segmentWidth = 6; + maxX = width - 1; + border = Tk_Get3DBorderFromObj(menuPtr->tkwin, menuPtr->borderPtr); + + while (points[0].x < maxX) { + points[1].x = points[0].x + segmentWidth; + if (points[1].x > maxX) { + points[1].x = maxX; + } + Tk_Draw3DPolygon(menuPtr->tkwin, d, border, points, 2, 1, + TK_RELIEF_RAISED); + points[0].x += 2*segmentWidth; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpConfigureMenuEntry -- + * + * Processes configurations for menu entries. + * + * Results: + * Returns standard TCL result. If TCL_ERROR is returned, then the + * interp's result contains an error message. + * + * Side effects: + * Configuration information get set for mePtr; old resources get freed, + * if any need it. + * + *---------------------------------------------------------------------- + */ + +int +TkpConfigureMenuEntry( + register TkMenuEntry *mePtr)/* Information about menu entry; may or may + * not already have values for some fields. */ +{ + ScheduleMenuReconfigure(mePtr->menuPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpDrawMenuEntry -- + * + * Draws the given menu entry at the given coordinates with the given + * attributes. + * + * Results: + * None. + * + * Side effects: + * X Server commands are executed to display the menu entry. + * + *---------------------------------------------------------------------- + */ + +void +TkpDrawMenuEntry( + TkMenuEntry *mePtr, /* The entry to draw */ + Drawable menuDrawable, /* Menu to draw into */ + Tk_Font tkfont, /* Precalculated font for menu */ + const Tk_FontMetrics *menuMetricsPtr, + /* Precalculated metrics for menu */ + int x, /* X-coordinate of topleft of entry */ + int y, /* Y-coordinate of topleft of entry */ + int width, /* Width of the entry rectangle */ + int height, /* Height of the current rectangle */ + int strictMotif, /* Boolean flag */ + int drawingParameters) /* Whether or not to draw the cascade arrow + * for cascade items and accelerator + * cues. Only applies to Windows. */ +{ + GC gc, indicatorGC; + TkMenu *menuPtr = mePtr->menuPtr; + Tk_3DBorder bgBorder, activeBorder; + const Tk_FontMetrics *fmPtr; + Tk_FontMetrics entryMetrics; + int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0; + int adjustedX, adjustedY; + int adjustedHeight = height - 2 * padY; + TkWinDrawable memWinDraw; + TkWinDCState dcState; + HBITMAP oldBitmap = NULL; + Drawable d; + HDC memDc = NULL, menuDc = NULL; + + /* + * If the menu entry includes an image then draw the entry into a + * compatible bitmap first. This avoids problems with clipping on + * animated menus. [Bug 1329198] + */ + + if (mePtr->image != NULL) { + menuDc = TkWinGetDrawableDC(menuPtr->display, menuDrawable, &dcState); + + memDc = CreateCompatibleDC(menuDc); + oldBitmap = SelectObject(memDc, + CreateCompatibleBitmap(menuDc, width, height) ); + + memWinDraw.type = TWD_WINDC; + memWinDraw.winDC.hdc = memDc; + d = (Drawable)&memWinDraw; + adjustedX = 0; + adjustedY = padY; + + } else { + d = menuDrawable; + adjustedX = x; + adjustedY = y + padY; + } + + /* + * Choose the gc for drawing the foreground part of the entry. + */ + + if ((mePtr->state == ENTRY_ACTIVE) && !strictMotif) { + gc = mePtr->activeGC; + if (gc == NULL) { + gc = menuPtr->activeGC; + } + } else { + TkMenuEntry *cascadeEntryPtr; + int parentDisabled = 0; + const char *name; + + for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr; + cascadeEntryPtr != NULL; + cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) { + name = Tcl_GetString(cascadeEntryPtr->namePtr); + if (strcmp(name, Tk_PathName(menuPtr->tkwin)) == 0) { + if (mePtr->state == ENTRY_DISABLED) { + parentDisabled = 1; + } + break; + } + } + + if (((parentDisabled || (mePtr->state == ENTRY_DISABLED))) + && (menuPtr->disabledFgPtr != NULL)) { + gc = mePtr->disabledGC; + if (gc == NULL) { + gc = menuPtr->disabledGC; + } + } else { + gc = mePtr->textGC; + if (gc == NULL) { + gc = menuPtr->textGC; + } + } + } + indicatorGC = mePtr->indicatorGC; + if (indicatorGC == NULL) { + indicatorGC = menuPtr->indicatorGC; + } + + bgBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, + (mePtr->borderPtr == NULL) ? menuPtr->borderPtr + : mePtr->borderPtr); + if (strictMotif) { + activeBorder = bgBorder; + } else { + activeBorder = Tk_Get3DBorderFromObj(menuPtr->tkwin, + (mePtr->activeBorderPtr == NULL) ? menuPtr->activeBorderPtr + : mePtr->activeBorderPtr); + } + + if (mePtr->fontPtr == NULL) { + fmPtr = menuMetricsPtr; + } else { + tkfont = Tk_GetFontFromObj(menuPtr->tkwin, mePtr->fontPtr); + Tk_GetFontMetrics(tkfont, &entryMetrics); + fmPtr = &entryMetrics; + } + + /* + * Need to draw the entire background, including padding. On Unix, for + * menubars, we have to draw the rest of the entry taking into account the + * padding. + */ + + DrawMenuEntryBackground(menuPtr, mePtr, d, activeBorder, + bgBorder, adjustedX, adjustedY-padY, width, height); + + if (mePtr->type == SEPARATOR_ENTRY) { + DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, + fmPtr, adjustedX, adjustedY, width, adjustedHeight); + } else if (mePtr->type == TEAROFF_ENTRY) { + DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, + adjustedX, adjustedY, width, adjustedHeight); + } else { + DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, + adjustedX, adjustedY, width, adjustedHeight, + (drawingParameters & DRAW_MENU_ENTRY_NOUNDERLINE)?0:1); + DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, + activeBorder, adjustedX, adjustedY, width, adjustedHeight); + DrawMenuEntryArrow(menuPtr, mePtr, d, gc, + activeBorder, adjustedX, adjustedY, width, adjustedHeight, + (drawingParameters & DRAW_MENU_ENTRY_ARROW)?1:0); + if (!mePtr->hideMargin) { + DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, + fmPtr, adjustedX, adjustedY, width, adjustedHeight); + } + } + + /* + * Copy the entry contents from the temporary bitmap to the menu. + */ + + if (mePtr->image != NULL) { + BitBlt(menuDc, x, y, width, height, memDc, 0, 0, SRCCOPY); + DeleteObject(SelectObject(memDc, oldBitmap)); + DeleteDC(memDc); + + TkWinReleaseDrawableDC(menuDrawable, menuDc, &dcState); + } +} + +/* + *---------------------------------------------------------------------- + * + * GetMenuLabelGeometry -- + * + * Figures out the size of the label portion of a menu item. + * + * Results: + * widthPtr and heightPtr are filled in with the correct geometry + * information. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +GetMenuLabelGeometry( + TkMenuEntry *mePtr, /* The entry we are computing */ + Tk_Font tkfont, /* The precalculated font */ + const Tk_FontMetrics *fmPtr,/* The precalculated metrics */ + int *widthPtr, /* The resulting width of the label portion */ + int *heightPtr) /* The resulting height of the label + * portion */ +{ + TkMenu *menuPtr = mePtr->menuPtr; + int haveImage = 0; + + if (mePtr->image != NULL) { + Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr); + haveImage = 1; + } else if (mePtr->bitmapPtr != NULL) { + Pixmap bitmap = Tk_GetBitmapFromObj(menuPtr->tkwin, mePtr->bitmapPtr); + + Tk_SizeOfBitmap(menuPtr->display, bitmap, widthPtr, heightPtr); + haveImage = 1; + } else { + *heightPtr = 0; + *widthPtr = 0; + } + + if (haveImage && (mePtr->compound == COMPOUND_NONE)) { + /* + * We don't care about the text in this case. + */ + } else { + /* + * Either it is compound or we don't have an image, + */ + + if (mePtr->labelPtr != NULL) { + int textWidth; + const char *label = Tcl_GetString(mePtr->labelPtr); + + textWidth = Tk_TextWidth(tkfont, label, mePtr->labelLength); + + if ((mePtr->compound != COMPOUND_NONE) && haveImage) { + switch ((enum compound) mePtr->compound) { + case COMPOUND_TOP: + case COMPOUND_BOTTOM: + if (textWidth > *widthPtr) { + *widthPtr = textWidth; + } + + /* + * Add text and padding. + */ + + *heightPtr += fmPtr->linespace + 2; + break; + case COMPOUND_LEFT: + case COMPOUND_RIGHT: + if (fmPtr->linespace > *heightPtr) { + *heightPtr = fmPtr->linespace; + } + + /* + * Add text and padding. + */ + + *widthPtr += textWidth + 2; + break; + case COMPOUND_CENTER: + if (fmPtr->linespace > *heightPtr) { + *heightPtr = fmPtr->linespace; + } + if (textWidth > *widthPtr) { + *widthPtr = textWidth; + } + break; + case COMPOUND_NONE: + break; + } + } else { + /* + * We don't have an image or we're not compound. + */ + + *heightPtr = fmPtr->linespace; + *widthPtr = textWidth; + } + } else { + /* + * An empty entry still has this height. + */ + + *heightPtr = fmPtr->linespace; + } + } + *heightPtr += 1; +} + +/* + *---------------------------------------------------------------------- + * + * DrawMenuEntryBackground -- + * + * This function draws the background part of a menu. + * + * Results: + * None. + * + * Side effects: + * Commands are output to X to display the menu in its current mode. + * + *---------------------------------------------------------------------- + */ + +static void +DrawMenuEntryBackground( + TkMenu *menuPtr, /* The menu we are drawing. */ + TkMenuEntry *mePtr, /* The entry we are drawing. */ + Drawable d, /* What we are drawing into */ + Tk_3DBorder activeBorder, /* Border for active items */ + Tk_3DBorder bgBorder, /* Border for the background */ + int x, /* left edge */ + int y, /* top edge */ + int width, /* width of rectangle to draw */ + int height) /* height of rectangle to draw */ +{ + if (mePtr->state == ENTRY_ACTIVE + || (mePtr->entryFlags & ENTRY_PLATFORM_FLAG1)!=0 ) { + bgBorder = activeBorder; + } + Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height, 0, + TK_RELIEF_FLAT); +} + +/* + *-------------------------------------------------------------- + * + * TkpComputeStandardMenuGeometry -- + * + * This function is invoked to recompute the size and layout of a menu + * that is not a menubar clone. + * + * Results: + * None. + * + * Side effects: + * Fields of menu entries are changed to reflect their current positions, + * and the size of the menu window itself may be changed. + * + *-------------------------------------------------------------- + */ + +void +TkpComputeStandardMenuGeometry( + TkMenu *menuPtr) /* Structure describing menu. */ +{ + Tk_Font menuFont, tkfont; + Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr; + int x, y, height, width, indicatorSpace, labelWidth, accelWidth; + int windowWidth, windowHeight, accelSpace; + int i, j, lastColumnBreak = 0; + int activeBorderWidth, borderWidth; + + if (menuPtr->tkwin == NULL) { + return; + } + + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->borderWidthPtr, &borderWidth); + x = y = borderWidth; + indicatorSpace = labelWidth = accelWidth = 0; + windowHeight = 0; + + /* + * On the Mac especially, getting font metrics can be quite slow, so we + * want to do it intelligently. We are going to precalculate them and pass + * them down to all of the measuring and drawing routines. We will measure + * the font metrics of the menu once. If an entry does not have its own + * font set, then we give the geometry/drawing routines the menu's font + * and metrics. If an entry has its own font, we will measure that font + * and give all of the geometry/drawing the entry's font and metrics. + */ + + menuFont = Tk_GetFontFromObj(menuPtr->tkwin, menuPtr->fontPtr); + Tk_GetFontMetrics(menuFont, &menuMetrics); + accelSpace = Tk_TextWidth(menuFont, "M", 1); + Tk_GetPixelsFromObj(menuPtr->interp, menuPtr->tkwin, + menuPtr->activeBorderWidthPtr, &activeBorderWidth); + + for (i = 0; i < menuPtr->numEntries; i++) { + if (menuPtr->entries[i]->fontPtr == NULL) { + tkfont = menuFont; + fmPtr = &menuMetrics; + } else { + tkfont = Tk_GetFontFromObj(menuPtr->tkwin, + menuPtr->entries[i]->fontPtr); + Tk_GetFontMetrics(tkfont, &entryMetrics); + fmPtr = &entryMetrics; + } + if ((i > 0) && menuPtr->entries[i]->columnBreak) { + if (accelWidth != 0) { + labelWidth += accelSpace; + } + for (j = lastColumnBreak; j < i; j++) { + menuPtr->entries[j]->indicatorSpace = indicatorSpace; + menuPtr->entries[j]->labelWidth = labelWidth; + menuPtr->entries[j]->width = indicatorSpace + labelWidth + + accelWidth + 2 * activeBorderWidth; + menuPtr->entries[j]->x = x; + menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN; + } + x += indicatorSpace + labelWidth + accelWidth + + 2 * borderWidth; + indicatorSpace = labelWidth = accelWidth = 0; + lastColumnBreak = i; + y = borderWidth; + } + + if (menuPtr->entries[i]->type == SEPARATOR_ENTRY) { + GetMenuSeparatorGeometry(menuPtr, menuPtr->entries[i], tkfont, + fmPtr, &width, &height); + menuPtr->entries[i]->height = height; + } else if (menuPtr->entries[i]->type == TEAROFF_ENTRY) { + GetTearoffEntryGeometry(menuPtr, menuPtr->entries[i], tkfont, + fmPtr, &width, &height); + menuPtr->entries[i]->height = height; + + } else { + /* + * For each entry, compute the height required by that particular + * entry, plus three widths: the width of the label, the width to + * allow for an indicator to be displayed to the left of the label + * (if any), and the width of the accelerator to be displayed to + * the right of the label (if any). These sizes depend, of course, + * on the type of the entry. + */ + + GetMenuLabelGeometry(menuPtr->entries[i], tkfont, fmPtr, &width, + &height); + menuPtr->entries[i]->height = height; + if (width > labelWidth) { + labelWidth = width; + } + + GetMenuAccelGeometry(menuPtr, menuPtr->entries[i], tkfont, + fmPtr, &width, &height); + if (height > menuPtr->entries[i]->height) { + menuPtr->entries[i]->height = height; + } + if (width > accelWidth) { + accelWidth = width; + } + + GetMenuIndicatorGeometry(menuPtr, menuPtr->entries[i], tkfont, + fmPtr, &width, &height); + if (height > menuPtr->entries[i]->height) { + menuPtr->entries[i]->height = height; + } + if (width > indicatorSpace) { + indicatorSpace = width; + } + + menuPtr->entries[i]->height += 2 * activeBorderWidth + 1; + } + menuPtr->entries[i]->y = y; + y += menuPtr->entries[i]->height; + if (y > windowHeight) { + windowHeight = y; + } + } + + if (accelWidth != 0) { + labelWidth += accelSpace; + } + for (j = lastColumnBreak; j < menuPtr->numEntries; j++) { + menuPtr->entries[j]->indicatorSpace = indicatorSpace; + menuPtr->entries[j]->labelWidth = labelWidth; + menuPtr->entries[j]->width = indicatorSpace + labelWidth + + accelWidth + 2 * activeBorderWidth; + menuPtr->entries[j]->x = x; + menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN; + } + windowWidth = x + indicatorSpace + labelWidth + accelWidth + accelSpace + + 2 * activeBorderWidth + 2 * borderWidth; + + + windowHeight += borderWidth; + + /* + * The X server doesn't like zero dimensions, so round up to at least 1 (a + * zero-sized menu should never really occur, anyway). + */ + + if (windowWidth <= 0) { + windowWidth = 1; + } + if (windowHeight <= 0) { + windowHeight = 1; + } + menuPtr->totalWidth = windowWidth; + menuPtr->totalHeight = windowHeight; +} + +/* + *---------------------------------------------------------------------- + * + * MenuSelectEvent -- + * + * Generates a "MenuSelect" virtual event. This can be used to do + * context-sensitive menu help. + * + * Results: + * None. + * + * Side effects: + * Places a virtual event on the event queue. + * + *---------------------------------------------------------------------- + */ + +static void +MenuSelectEvent( + TkMenu *menuPtr) /* the menu we have selected. */ +{ + XVirtualEvent event; + union {DWORD msgpos; POINTS point;} root; + + event.type = VirtualEvent; + event.serial = menuPtr->display->request; + event.send_event = 0; + event.display = menuPtr->display; + Tk_MakeWindowExist(menuPtr->tkwin); + event.event = Tk_WindowId(menuPtr->tkwin); + event.root = XRootWindow(menuPtr->display, 0); + event.subwindow = None; + event.time = TkpGetMS(); + + root.msgpos = GetMessagePos(); + event.x_root = root.point.x; + event.y_root = root.point.y; + event.state = TkWinGetModifierState(); + event.same_screen = 1; + event.name = Tk_GetUid("MenuSelect"); + event.user_data = NULL; + Tk_QueueWindowEvent((XEvent *) &event, TCL_QUEUE_TAIL); +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuNotifyToplevelCreate -- + * + * This routine reconfigures the menu and the clones indicated by + * menuName becuase a toplevel has been created and any system menus need + * to be created. + * + * Results: + * None. + * + * Side effects: + * An idle handler is set up to do the reconfiguration. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuNotifyToplevelCreate( + Tcl_Interp *interp, /* The interp the menu lives in. */ + const char *menuName) /* The name of the menu to reconfigure. */ +{ + TkMenuReferences *menuRefPtr; + TkMenu *menuPtr; + + if ((menuName != NULL) && (menuName[0] != '\0')) { + menuRefPtr = TkFindMenuReferences(interp, menuName); + if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) { + for (menuPtr = menuRefPtr->menuPtr->masterMenuPtr; menuPtr != NULL; + menuPtr = menuPtr->nextInstancePtr) { + if (menuPtr->menuType == MENUBAR) { + ScheduleMenuReconfigure(menuPtr); + } + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetMenuHWND -- + * + * This function returns the HWND of a hidden menu Window that processes + * messages of a popup menu. This hidden menu window is used to handle + * either a dynamic popup menu in the same process or a pull-down menu of + * an embedded window in a different process. + * + * Results: + * Returns the HWND of the hidden menu Window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HWND +Tk_GetMenuHWND( + Tk_Window tkwin) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + TkMenuInit(); + return tsdPtr->embeddedMenuHWND; +} + +/* + *---------------------------------------------------------------------- + * + * MenuExitHandler -- + * + * Unregisters the class of utility windows. + * + * Results: + * None. + * + * Side effects: + * Menus have to be reinitialized next time. + * + *---------------------------------------------------------------------- + */ + +static void +MenuExitHandler( + ClientData clientData) /* Not used */ +{ + UnregisterClass(MENU_CLASS_NAME, Tk_GetHINSTANCE()); + UnregisterClass(EMBEDDED_MENU_CLASS_NAME, Tk_GetHINSTANCE()); +} + +/* + *---------------------------------------------------------------------- + * + * MenuExitHandler -- + * + * Throws away the utility window needed for menus and delete hash + * tables. + * + * Results: + * None. + * + * Side effects: + * Menus have to be reinitialized next time. + * + *---------------------------------------------------------------------- + */ + +static void +MenuThreadExitHandler( + ClientData clientData) /* Not used */ +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + DestroyWindow(tsdPtr->menuHWND); + DestroyWindow(tsdPtr->embeddedMenuHWND); + tsdPtr->menuHWND = NULL; + tsdPtr->embeddedMenuHWND = NULL; + + Tcl_DeleteHashTable(&tsdPtr->winMenuTable); + Tcl_DeleteHashTable(&tsdPtr->commandTable); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetMenuSystemDefault -- + * + * Gets the Windows specific default value for a given X resource + * database name. + * + * Results: + * Returns a Tcl_Obj* with the default value. If there is no + * Windows-specific default for this attribute, returns NULL. This object + * has a ref count of 0. + * + * Side effects: + * Storage is allocated. + * + *---------------------------------------------------------------------- + */ + +Tcl_Obj * +TkWinGetMenuSystemDefault( + Tk_Window tkwin, /* A window to use. */ + const char *dbName, /* The option database name. */ + const char *className) /* The name of the option class. */ +{ + Tcl_Obj *valuePtr = NULL; + + if ((strcmp(dbName, "activeBorderWidth") == 0) || + (strcmp(dbName, "borderWidth") == 0)) { + valuePtr = Tcl_NewIntObj(defaultBorderWidth); + } else if (strcmp(dbName, "font") == 0) { + valuePtr = Tcl_NewStringObj(Tcl_DStringValue(&menuFontDString), -1); + } + + return valuePtr; +} + +/* + *---------------------------------------------------------------------- + * + * SetDefaults -- + * + * Read system menu settings (font, sizes of items, use of accelerators) + * This is called if the UI theme or settings are changed. + * + * Results: + * None. + * + * Side effects: + * May result in menu items being redrawn with different appearance. + * + *---------------------------------------------------------------------- + */ + +static void +SetDefaults( + int firstTime) /* Is this the first time this has been + * called? */ +{ + char sizeString[TCL_INTEGER_SPACE]; + char faceName[LF_FACESIZE]; + HDC scratchDC; + int bold = 0; + int italic = 0; + TEXTMETRIC tm; + int pointSize; + HFONT menuFont; + /* See: [Bug #3239768] tk8.4.19 (and later) WIN32 menu font support */ + struct { + NONCLIENTMETRICS metrics; +#if (WINVER < 0x0600) + int padding; +#endif + } nc; + OSVERSIONINFOW os; + + /* + * Set all of the default options. The loop will terminate when we run out + * of options via a break statement. + */ + + defaultBorderWidth = GetSystemMetrics(SM_CXBORDER); + if (GetSystemMetrics(SM_CYBORDER) > defaultBorderWidth) { + defaultBorderWidth = GetSystemMetrics(SM_CYBORDER); + } + + scratchDC = CreateDCA("DISPLAY", NULL, NULL, NULL); + if (!firstTime) { + Tcl_DStringFree(&menuFontDString); + } + Tcl_DStringInit(&menuFontDString); + + nc.metrics.cbSize = sizeof(nc); + + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&os); + if (os.dwMajorVersion < 6) { + nc.metrics.cbSize -= sizeof(int); + } + + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, nc.metrics.cbSize, + &nc.metrics, 0); + menuFont = CreateFontIndirect(&nc.metrics.lfMenuFont); + SelectObject(scratchDC, menuFont); + GetTextMetrics(scratchDC, &tm); + GetTextFaceA(scratchDC, LF_FACESIZE, faceName); + pointSize = MulDiv(tm.tmHeight - tm.tmInternalLeading, + 72, GetDeviceCaps(scratchDC, LOGPIXELSY)); + if (tm.tmWeight >= 700) { + bold = 1; + } + if (tm.tmItalic) { + italic = 1; + } + + SelectObject(scratchDC, GetStockObject(SYSTEM_FONT)); + DeleteDC(scratchDC); + + DeleteObject(menuFont); + + Tcl_DStringAppendElement(&menuFontDString, faceName); + sprintf(sizeString, "%d", pointSize); + Tcl_DStringAppendElement(&menuFontDString, sizeString); + + if (bold || italic) { + Tcl_DString boldItalicDString; + + Tcl_DStringInit(&boldItalicDString); + if (bold) { + Tcl_DStringAppendElement(&boldItalicDString, "bold"); + } + if (italic) { + Tcl_DStringAppendElement(&boldItalicDString, "italic"); + } + Tcl_DStringAppendElement(&menuFontDString, + Tcl_DStringValue(&boldItalicDString)); + Tcl_DStringFree(&boldItalicDString); + } + + /* + * Now we go ahead and get the dimensions of the check mark and the + * appropriate margins. Since this is fairly hairy, we do it here to save + * time when traversing large sets of menu items. + * + * The code below was given to me by Microsoft over the phone. It is the + * only way to ensure menu items line up, and is not documented. + */ + + indicatorDimensions[0] = GetSystemMetrics(SM_CYMENUCHECK); + indicatorDimensions[1] = ((GetSystemMetrics(SM_CXFIXEDFRAME) + + GetSystemMetrics(SM_CXBORDER) + + GetSystemMetrics(SM_CXMENUCHECK) + 7) & 0xFFF8) + - GetSystemMetrics(SM_CXFIXEDFRAME); + + /* + * Accelerators used to be always underlines until Win2K when a system + * parameter was introduced to hide them unless Alt is pressed. + */ + + showMenuAccelerators = TRUE; + SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &showMenuAccelerators, 0); +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuInit -- + * + * Sets up the process-wide variables used by the menu package. + * + * Results: + * None. + * + * Side effects: + * lastMenuID gets initialized. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuInit(void) +{ + WNDCLASS wndClass; + + wndClass.style = CS_OWNDC; + wndClass.lpfnWndProc = TkWinMenuProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = Tk_GetHINSTANCE(); + wndClass.hIcon = NULL; + wndClass.hCursor = NULL; + wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = MENU_CLASS_NAME; + if (!RegisterClass(&wndClass)) { + Tcl_Panic("Failed to register menu window class"); + } + + wndClass.lpfnWndProc = TkWinEmbeddedMenuProc; + wndClass.lpszClassName = EMBEDDED_MENU_CLASS_NAME; + if (!RegisterClass(&wndClass)) { + Tcl_Panic("Failed to register embedded menu window class"); + } + + TkCreateExitHandler(MenuExitHandler, NULL); + SetDefaults(1); +} + +/* + *---------------------------------------------------------------------- + * + * TkpMenuThreadInit -- + * + * Sets up the thread-local hash tables used by the menu module. Assumes + * that TkpMenuInit has been called. + * + * Results: + * None. + * + * Side effects: + * Hash tables winMenuTable and commandTable are initialized. + * + *---------------------------------------------------------------------- + */ + +void +TkpMenuThreadInit(void) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + tsdPtr->menuHWND = CreateWindow(MENU_CLASS_NAME, TEXT("MenuWindow"), WS_POPUP, + 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL); + + if (!tsdPtr->menuHWND) { + Tcl_Panic("Failed to create the menu window"); + } + + tsdPtr->embeddedMenuHWND = + CreateWindow(EMBEDDED_MENU_CLASS_NAME, TEXT("EmbeddedMenuWindow"), + WS_POPUP, 0, 0, 10, 10, NULL, NULL, Tk_GetHINSTANCE(), NULL); + + if (!tsdPtr->embeddedMenuHWND) { + Tcl_Panic("Failed to create the embedded menu window"); + } + + Tcl_InitHashTable(&tsdPtr->winMenuTable, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&tsdPtr->commandTable, TCL_ONE_WORD_KEYS); + + TkCreateThreadExitHandler(MenuThreadExitHandler, NULL); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinPixmap.c b/tk8.6/win/tkWinPixmap.c new file mode 100644 index 0000000..1cf0634 --- /dev/null +++ b/tk8.6/win/tkWinPixmap.c @@ -0,0 +1,251 @@ +/* + * tkWinPixmap.c -- + * + * This file contains the Xlib emulation functions pertaining to creating + * and destroying pixmaps. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + *---------------------------------------------------------------------- + * + * Tk_GetPixmap -- + * + * Creates an in memory drawing surface. + * + * Results: + * Returns a handle to a new pixmap. + * + * Side effects: + * Allocates a new Win32 bitmap. + * + *---------------------------------------------------------------------- + */ + +Pixmap +Tk_GetPixmap( + Display *display, + Drawable d, + int width, + int height, + int depth) +{ + TkWinDrawable *newTwdPtr, *twdPtr; + int planes; + Screen *screen; + + display->request++; + + newTwdPtr = ckalloc(sizeof(TkWinDrawable)); + newTwdPtr->type = TWD_BITMAP; + newTwdPtr->bitmap.depth = depth; + twdPtr = (TkWinDrawable *) d; + if (twdPtr->type != TWD_BITMAP) { + if (twdPtr->window.winPtr == NULL) { + newTwdPtr->bitmap.colormap = DefaultColormap(display, + DefaultScreen(display)); + } else { + newTwdPtr->bitmap.colormap = twdPtr->window.winPtr->atts.colormap; + } + } else { + newTwdPtr->bitmap.colormap = twdPtr->bitmap.colormap; + } + screen = &display->screens[0]; + planes = 1; + if (depth == screen->root_depth) { + planes = PTR2INT(screen->ext_data); + depth /= planes; + } + newTwdPtr->bitmap.handle = + CreateBitmap(width, height, (DWORD) planes, (DWORD) depth, NULL); + + /* + * CreateBitmap tries to use memory on the graphics card. If it fails, + * call CreateDIBSection which uses real memory; slower, but at least + * still works. [Bug 2080533] + */ + + if (newTwdPtr->bitmap.handle == NULL) { + static int repeatError = 0; + void *bits = NULL; + BITMAPINFO bitmapInfo; + HDC dc; + + memset(&bitmapInfo, 0, sizeof(bitmapInfo)); + bitmapInfo.bmiHeader.biSize = sizeof(bitmapInfo.bmiHeader); + bitmapInfo.bmiHeader.biWidth = width; + bitmapInfo.bmiHeader.biHeight = height; + bitmapInfo.bmiHeader.biPlanes = planes; + bitmapInfo.bmiHeader.biBitCount = depth; + bitmapInfo.bmiHeader.biCompression = BI_RGB; + bitmapInfo.bmiHeader.biSizeImage = 0; + dc = GetDC(NULL); + newTwdPtr->bitmap.handle = CreateDIBSection(dc, &bitmapInfo, + DIB_RGB_COLORS, &bits, 0, 0); + ReleaseDC(NULL, dc); + + /* + * Oh no! Things are still going wrong. Pop up a warning message here + * (because things will probably crash soon) which will encourage + * people to report this as a bug... + */ + + if (newTwdPtr->bitmap.handle == NULL && !repeatError) { + LPVOID lpMsgBuf; + + repeatError = 1; + if (FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPSTR) &lpMsgBuf, 0, NULL)) { + MessageBoxA(NULL, (LPCSTR) lpMsgBuf, + "Tk_GetPixmap: Error from CreateDIBSection", + MB_OK | MB_ICONINFORMATION); + LocalFree(lpMsgBuf); + } + } + } + + if (newTwdPtr->bitmap.handle == NULL) { + ckfree(newTwdPtr); + return None; + } + + return (Pixmap) newTwdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_FreePixmap -- + * + * Release the resources associated with a pixmap. + * + * Results: + * None. + * + * Side effects: + * Deletes the bitmap created by Tk_GetPixmap. + * + *---------------------------------------------------------------------- + */ + +void +Tk_FreePixmap( + Display *display, + Pixmap pixmap) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *) pixmap; + + display->request++; + if (twdPtr != NULL) { + DeleteObject(twdPtr->bitmap.handle); + ckfree(twdPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkSetPixmapColormap -- + * + * The following function is a hack used by the photo widget to + * explicitly set the colormap slot of a Pixmap. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkSetPixmapColormap( + Pixmap pixmap, + Colormap colormap) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)pixmap; + twdPtr->bitmap.colormap = colormap; +} + +/* + *---------------------------------------------------------------------- + * + * XGetGeometry -- + * + * Retrieve the geometry of the given drawable. Note that this is a + * degenerate implementation that only returns the size of a pixmap or + * window. + * + * Results: + * Returns 0. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +XGetGeometry( + Display *display, + Drawable d, + Window *root_return, + int *x_return, + int *y_return, + unsigned int *width_return, + unsigned int *height_return, + unsigned int *border_width_return, + unsigned int *depth_return) +{ + TkWinDrawable *twdPtr = (TkWinDrawable *)d; + + if (twdPtr->type == TWD_BITMAP) { + HDC dc; + BITMAPINFO info; + + if (twdPtr->bitmap.handle == NULL) { + Tcl_Panic("XGetGeometry: invalid pixmap"); + } + dc = GetDC(NULL); + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biBitCount = 0; + if (!GetDIBits(dc, twdPtr->bitmap.handle, 0, 0, NULL, &info, + DIB_RGB_COLORS)) { + Tcl_Panic("XGetGeometry: unable to get bitmap size"); + } + ReleaseDC(NULL, dc); + + *width_return = info.bmiHeader.biWidth; + *height_return = info.bmiHeader.biHeight; + } else if (twdPtr->type == TWD_WINDOW) { + RECT rect; + + if (twdPtr->window.handle == NULL) { + Tcl_Panic("XGetGeometry: invalid window"); + } + GetClientRect(twdPtr->window.handle, &rect); + *width_return = rect.right - rect.left; + *height_return = rect.bottom - rect.top; + } else { + Tcl_Panic("XGetGeometry: invalid window"); + } + return 1; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinPointer.c b/tk8.6/win/tkWinPointer.c new file mode 100644 index 0000000..6f1f840 --- /dev/null +++ b/tk8.6/win/tkWinPointer.c @@ -0,0 +1,546 @@ +/* + * tkWinPointer.c -- + * + * Windows specific mouse tracking code. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-1999 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * Check for enter/leave events every MOUSE_TIMER_INTERVAL milliseconds. + */ + +#define MOUSE_TIMER_INTERVAL 250 + +/* + * Declarations of static variables used in this file. + */ + +static int captured = 0; /* 1 if mouse is currently captured. */ +static TkWindow *keyboardWinPtr = NULL; /* Current keyboard grab window. */ +static Tcl_TimerToken mouseTimer; /* Handle to the latest mouse timer. */ +static int mouseTimerSet = 0; /* 1 if the mouse timer is active. */ + +/* + * Forward declarations of procedures used in this file. + */ + +static void MouseTimerProc(ClientData clientData); + +/* + *---------------------------------------------------------------------- + * + * TkWinGetModifierState -- + * + * Return the modifier state as of the last message. + * + * Results: + * Returns the X modifier mask. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkWinGetModifierState(void) +{ + int state = 0; + + if (GetKeyState(VK_SHIFT) & 0x8000) { + state |= ShiftMask; + } + if (GetKeyState(VK_CONTROL) & 0x8000) { + state |= ControlMask; + } + if (GetKeyState(VK_MENU) & 0x8000) { + state |= ALT_MASK; + } + if (GetKeyState(VK_CAPITAL) & 0x0001) { + state |= LockMask; + } + if (GetKeyState(VK_NUMLOCK) & 0x0001) { + state |= Mod1Mask; + } + if (GetKeyState(VK_SCROLL) & 0x0001) { + state |= Mod3Mask; + } + if (GetKeyState(VK_LBUTTON) & 0x8000) { + state |= Button1Mask; + } + if (GetKeyState(VK_MBUTTON) & 0x8000) { + state |= Button2Mask; + } + if (GetKeyState(VK_RBUTTON) & 0x8000) { + state |= Button3Mask; + } + return state; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_PointerEvent -- + * + * This procedure is called for each pointer-related event. It converts + * the position to root coords and updates the global pointer state + * machine. It also ensures that the mouse timer is scheduled. + * + * Results: + * None. + * + * Side effects: + * May queue events and change the grab state. + * + *---------------------------------------------------------------------- + */ + +void +Tk_PointerEvent( + HWND hwnd, /* Window for coords, or NULL for the root + * window. */ + int x, int y) /* Coords relative to hwnd, or screen if hwnd + * is NULL. */ +{ + POINT pos; + int state; + Tk_Window tkwin; + + pos.x = x; + pos.y = y; + + /* + * Convert client coords to root coords if we were given a window. + */ + + if (hwnd) { + ClientToScreen(hwnd, &pos); + } + + /* + * If the mouse is captured, Windows will report all pointer events to the + * capture window. So, we need to determine which window the mouse is + * really over and change the event. Note that the computed hwnd may point + * to a window not owned by Tk, or a toplevel decorative frame, so tkwin + * can be NULL. + */ + + if (captured || hwnd == NULL) { + hwnd = WindowFromPoint(pos); + } + tkwin = Tk_HWNDToWindow(hwnd); + + state = TkWinGetModifierState(); + + Tk_UpdatePointer(tkwin, pos.x, pos.y, state); + + if ((captured || tkwin) && !mouseTimerSet) { + mouseTimerSet = 1; + mouseTimer = Tcl_CreateTimerHandler(MOUSE_TIMER_INTERVAL, + MouseTimerProc, NULL); + } +} + +/* + *---------------------------------------------------------------------- + * + * XGrabKeyboard -- + * + * Simulates a keyboard grab by setting the focus. + * + * Results: + * Always returns GrabSuccess. + * + * Side effects: + * Sets the keyboard focus to the specified window. + * + *---------------------------------------------------------------------- + */ + +int +XGrabKeyboard( + Display *display, + Window grab_window, + Bool owner_events, + int pointer_mode, + int keyboard_mode, + Time time) +{ + keyboardWinPtr = TkWinGetWinPtr(grab_window); + return GrabSuccess; +} + +/* + *---------------------------------------------------------------------- + * + * XUngrabKeyboard -- + * + * Releases the simulated keyboard grab. + * + * Results: + * None. + * + * Side effects: + * Sets the keyboard focus back to the value before the grab. + * + *---------------------------------------------------------------------- + */ + +int +XUngrabKeyboard( + Display *display, + Time time) +{ + keyboardWinPtr = NULL; + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * MouseTimerProc -- + * + * Check the current mouse position and look for enter/leave events. + * + * Results: + * None. + * + * Side effects: + * May schedule a new timer and/or generate enter/leave events. + * + *---------------------------------------------------------------------- + */ + +void +MouseTimerProc( + ClientData clientData) +{ + POINT pos; + + mouseTimerSet = 0; + + /* + * Get the current mouse position and window. Don't do anything if the + * mouse hasn't moved since the last time we looked. + */ + + GetCursorPos(&pos); + Tk_PointerEvent(NULL, pos.x, pos.y); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinCancelMouseTimer -- + * + * If the mouse timer is set, cancel it. + * + * Results: + * None. + * + * Side effects: + * May cancel the mouse timer. + * + *---------------------------------------------------------------------- + */ + +void +TkWinCancelMouseTimer(void) +{ + if (mouseTimerSet) { + Tcl_DeleteTimerHandler(mouseTimer); + mouseTimerSet = 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * TkGetPointerCoords -- + * + * Fetch the position of the mouse pointer. + * + * Results: + * *xPtr and *yPtr are filled in with the root coordinates of the mouse + * pointer for the display. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkGetPointerCoords( + Tk_Window tkwin, /* Window that identifies screen on which + * lookup is to be done. */ + int *xPtr, int *yPtr) /* Store pointer coordinates here. */ +{ + POINT point; + + GetCursorPos(&point); + *xPtr = point.x; + *yPtr = point.y; +} + +/* + *---------------------------------------------------------------------- + * + * XQueryPointer -- + * + * Check the current state of the mouse. This is not a complete + * implementation of this function. It only computes the root coordinates + * and the current mask. + * + * Results: + * Sets root_x_return, root_y_return, and mask_return. Returns true on + * success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +XQueryPointer( + Display *display, + Window w, + Window *root_return, + Window *child_return, + int *root_x_return, + int *root_y_return, + int *win_x_return, + int *win_y_return, + unsigned int *mask_return) +{ + display->request++; + TkGetPointerCoords(NULL, root_x_return, root_y_return); + *mask_return = TkWinGetModifierState(); + return True; +} + +/* + *---------------------------------------------------------------------- + * + * XWarpPointer -- + * + * Move pointer to new location. This is not a complete implementation of + * this function. + * + * Results: + * None. + * + * Side effects: + * Mouse pointer changes position on screen. + * + *---------------------------------------------------------------------- + */ + +int +XWarpPointer( + Display *display, + Window src_w, + Window dest_w, + int src_x, + int src_y, + unsigned int src_width, + unsigned int src_height, + int dest_x, + int dest_y) +{ + RECT r; + + GetWindowRect(Tk_GetHWND(dest_w), &r); + SetCursorPos(r.left+dest_x, r.top+dest_y); + return Success; +} + +void +TkpWarpPointer( + TkDisplay *dispPtr) +{ + if (dispPtr->warpWindow) { + RECT r; + + GetWindowRect(Tk_GetHWND(Tk_WindowId(dispPtr->warpWindow)), &r); + SetCursorPos(r.left + dispPtr->warpX, r.top + dispPtr->warpY); + } else { + SetCursorPos(dispPtr->warpX, dispPtr->warpY); + } +} + +/* + *---------------------------------------------------------------------- + * + * XGetInputFocus -- + * + * Retrieves the current keyboard focus window. + * + * Results: + * Returns the current focus window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +XGetInputFocus( + Display *display, + Window *focus_return, + int *revert_to_return) +{ + Tk_Window tkwin = Tk_HWNDToWindow(GetFocus()); + + *focus_return = tkwin ? Tk_WindowId(tkwin) : None; + *revert_to_return = RevertToParent; + display->request++; + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XSetInputFocus -- + * + * Set the current focus window. + * + * Results: + * None. + * + * Side effects: + * Changes the keyboard focus and causes the selected window to + * be activated. + * + *---------------------------------------------------------------------- + */ + +int +XSetInputFocus( + Display *display, + Window focus, + int revert_to, + Time time) +{ + display->request++; + if (focus != None) { + SetFocus(Tk_GetHWND(focus)); + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkpChangeFocus -- + * + * This procedure is invoked to move the system focus from one window to + * another. + * + * Results: + * The return value is the serial number of the command that changed the + * focus. It may be needed by the caller to filter out focus change + * events that were queued before the command. If the procedure doesn't + * actually change the focus then it returns 0. + * + * Side effects: + * The official Windows focus window changes; the application's focus + * window isn't changed by this procedure. + * + *---------------------------------------------------------------------- + */ + +int +TkpChangeFocus( + TkWindow *winPtr, /* Window that is to receive the X focus. */ + int force) /* Non-zero means claim the focus even if it + * didn't originally belong to topLevelPtr's + * application. */ +{ + TkDisplay *dispPtr = winPtr->dispPtr; + Window focusWindow; + int dummy, serial; + TkWindow *winPtr2; + + if (!force) { + XGetInputFocus(dispPtr->display, &focusWindow, &dummy); + winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow); + if ((winPtr2 == NULL) || (winPtr2->mainPtr != winPtr->mainPtr)) { + return 0; + } + } + + if (winPtr->window == None) { + Tcl_Panic("ChangeXFocus got null X window"); + } + + /* + * Change the foreground window so the focus window is raised to the top + * of the system stacking order and gets the keyboard focus. + */ + + if (force) { + TkWinSetForegroundWindow(winPtr); + } + XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent, + CurrentTime); + + /* + * Remember the current serial number for the X server and issue a dummy + * server request. This marks the position at which we changed the focus, + * so we can distinguish FocusIn and FocusOut events on either side of the + * mark. + */ + + serial = NextRequest(winPtr->display); + XNoOp(winPtr->display); + return serial; +} + +/* + *---------------------------------------------------------------------- + * + * TkpSetCapture -- + * + * This function captures the mouse so that all future events will be + * reported to this window, even if the mouse is outside the window. If + * the specified window is NULL, then the mouse is released. + * + * Results: + * None. + * + * Side effects: + * Sets the capture flag and captures the mouse. + * + *---------------------------------------------------------------------- + */ + +void +TkpSetCapture( + TkWindow *winPtr) /* Capture window, or NULL. */ +{ + if (winPtr) { + SetCapture(Tk_GetHWND(Tk_WindowId(winPtr))); + captured = 1; + } else { + captured = 0; + ReleaseCapture(); + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinPort.h b/tk8.6/win/tkWinPort.h new file mode 100644 index 0000000..965dbc5 --- /dev/null +++ b/tk8.6/win/tkWinPort.h @@ -0,0 +1,123 @@ +/* + * tkWinPort.h -- + * + * This header file handles porting issues that occur because of + * differences between Windows and Unix. It should be the only + * file that contains #ifdefs to handle different flavors of OS. + * + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef _WINPORT +#define _WINPORT + +/* + *--------------------------------------------------------------------------- + * The following sets of #includes and #ifdefs are required to get Tcl to + * compile under the windows compilers. + *--------------------------------------------------------------------------- + */ + +#include <wchar.h> +#include <io.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <malloc.h> +#include <ctype.h> +#include <math.h> +#include <string.h> +#include <limits.h> + +/* + * Need to block out this include for building extensions with MetroWerks + * compiler for Win32. + */ + +#ifndef __MWERKS__ +#include <sys/stat.h> +#endif + +#include <time.h> + +#ifdef _MSC_VER +# ifndef hypot +# define hypot _hypot +# endif +#endif /* _MSC_VER */ + +/* + * Pull in the typedef of TCHAR for windows. + */ +#include <tchar.h> +#ifndef _TCHAR_DEFINED + /* Borland seems to forget to set this. */ + typedef _TCHAR TCHAR; +# define _TCHAR_DEFINED +#endif +#if defined(_MSC_VER) && defined(__STDC__) + /* VS2005 SP1 misses this. See [Bug #3110161] */ + typedef _TCHAR TCHAR; +#endif + +#include <X11/Xlib.h> +#include <X11/cursorfont.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> + +#ifndef __GNUC__ +# define strncasecmp _strnicmp +# define strcasecmp _stricmp +#endif + +#define NBBY 8 + +#ifndef OPEN_MAX +#define OPEN_MAX 32 +#endif + +/* + * The following define causes Tk to use its internal keysym hash table + */ + +#define REDO_KEYSYM_LOOKUP + +/* + * See ticket [916c1095438eae56]: GetVersionExW triggers warnings + */ +#if defined(_MSC_VER) +# pragma warning(disable:4996) +#endif + +/* + * The following macro checks to see whether there is buffered + * input data available for a stdio FILE. + */ + +#ifdef _MSC_VER +# define TK_READ_DATA_PENDING(f) ((f)->_cnt > 0) +#else /* _MSC_VER */ +# define TK_READ_DATA_PENDING(f) ((f)->level > 0) +#endif /* _MSC_VER */ + +/* + * The following Tk functions are implemented as macros under Windows. + */ + +#define TkpGetPixel(p) (((((p)->red >> 8) & 0xff) \ + | ((p)->green & 0xff00) | (((p)->blue << 8) & 0xff0000)) | 0x20000000) + +/* + * These calls implement native bitmaps which are not currently + * supported under Windows. The macros eliminate the calls. + */ + +#define TkpDefineNativeBitmaps() +#define TkpCreateNativeBitmap(display, source) None +#define TkpGetNativeAppBitmap(display, name, w, h) None + +#endif /* _WINPORT */ diff --git a/tk8.6/win/tkWinRegion.c b/tk8.6/win/tkWinRegion.c new file mode 100644 index 0000000..d097047 --- /dev/null +++ b/tk8.6/win/tkWinRegion.c @@ -0,0 +1,288 @@ +/* + * tkWinRegion.c -- + * + * Tk Region emulation code. + * + * Copyright (c) 1995 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +#undef TkCreateRegion +#undef TkDestroyRegion +#undef TkClipBox +#undef TkIntersectRegion +#undef TkUnionRectWithRegion +#undef TkRectInRegion +#undef TkSubtractRegion + +/* + *---------------------------------------------------------------------- + * + * TkCreateRegion -- + * + * Construct an empty region. + * + * Results: + * Returns a new region handle. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkRegion +TkCreateRegion(void) +{ + RECT rect; + memset(&rect, 0, sizeof(RECT)); + return (TkRegion) CreateRectRgnIndirect(&rect); +} + +/* + *---------------------------------------------------------------------- + * + * TkDestroyRegion -- + * + * Destroy the specified region. + * + * Results: + * None. + * + * Side effects: + * Frees the storage associated with the specified region. + * + *---------------------------------------------------------------------- + */ + +void +TkDestroyRegion( + TkRegion r) +{ + DeleteObject((HRGN) r); +} + +/* + *---------------------------------------------------------------------- + * + * TkClipBox -- + * + * Computes the bounding box of a region. + * + * Results: + * Sets rect_return to the bounding box of the region. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkClipBox( + TkRegion r, + XRectangle* rect_return) +{ + RECT rect; + + GetRgnBox((HRGN)r, &rect); + rect_return->x = (short) rect.left; + rect_return->y = (short) rect.top; + rect_return->width = (short) (rect.right - rect.left); + rect_return->height = (short) (rect.bottom - rect.top); +} + +/* + *---------------------------------------------------------------------- + * + * TkIntersectRegion -- + * + * Compute the intersection of two regions. + * + * Results: + * Returns the result in the dr_return region. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkIntersectRegion( + TkRegion sra, + TkRegion srb, + TkRegion dr_return) +{ + CombineRgn((HRGN) dr_return, (HRGN) sra, (HRGN) srb, RGN_AND); +} + +/* + *---------------------------------------------------------------------- + * + * TkUnionRectWithRegion -- + * + * Create the union of a source region and a rectangle. + * + * Results: + * Returns the result in the dr_return region. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkUnionRectWithRegion( + XRectangle *rectangle, + TkRegion src_region, + TkRegion dest_region_return) +{ + HRGN rectRgn = CreateRectRgn(rectangle->x, rectangle->y, + rectangle->x + rectangle->width, rectangle->y + rectangle->height); + + CombineRgn((HRGN) dest_region_return, (HRGN) src_region, + (HRGN) rectRgn, RGN_OR); + DeleteObject(rectRgn); +} + +/* + *---------------------------------------------------------------------- + * + * TkpBuildRegionFromAlphaData -- + * + * Set up a rectangle of the given region based on the supplied alpha + * data. + * + * Results: + * None + * + * Side effects: + * The region is updated, with extra pixels added to it. + * + *---------------------------------------------------------------------- + */ + +void +TkpBuildRegionFromAlphaData( + TkRegion region, + unsigned int x, unsigned int y, + /* Where in region to update. */ + unsigned int width, unsigned int height, + /* Size of rectangle to update. */ + unsigned char *dataPtr, /* Data to read from. */ + unsigned int pixelStride, /* Num bytes from one piece of alpha data to + * the next in the line. */ + unsigned int lineStride) /* Num bytes from one line of alpha data to + * the next line. */ +{ + unsigned char *lineDataPtr; + unsigned int x1, y1, end; + HRGN rectRgn = CreateRectRgn(0,0,1,1); /* Workspace region. */ + + for (y1 = 0; y1 < height; y1++) { + lineDataPtr = dataPtr; + for (x1 = 0; x1 < width; x1 = end) { + /* + * Search for first non-transparent pixel. + */ + + while ((x1 < width) && !*lineDataPtr) { + x1++; + lineDataPtr += pixelStride; + } + end = x1; + + /* + * Search for first transparent pixel. + */ + + while ((end < width) && *lineDataPtr) { + end++; + lineDataPtr += pixelStride; + } + if (end > x1) { + /* + * Manipulate Win32 regions directly; it's more efficient. + */ + + SetRectRgn(rectRgn, (int) (x+x1), (int) (y+y1), + (int) (x+end), (int) (y+y1+1)); + CombineRgn((HRGN) region, (HRGN) region, rectRgn, RGN_OR); + } + } + dataPtr += lineStride; + } + + DeleteObject(rectRgn); +} + +/* + *---------------------------------------------------------------------- + * + * TkRectInRegion -- + * + * Test whether a given rectangle overlaps with a region. + * + * Results: + * Returns RectanglePart or RectangleOut. Note that this is not a + * complete implementation since it doesn't test for RectangleIn. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkRectInRegion( + TkRegion r, /* Region to inspect */ + int x, int y, /* Top-left of rectangle */ + unsigned int width, /* Width of rectangle */ + unsigned int height) /* Height of rectangle */ +{ + RECT rect; + rect.top = y; + rect.left = x; + rect.bottom = y+height; + rect.right = x+width; + return RectInRegion((HRGN)r, &rect) ? RectanglePart : RectangleOut; +} + +/* + *---------------------------------------------------------------------- + * + * TkSubtractRegion -- + * + * Compute the set-difference of two regions. + * + * Results: + * Returns the result in the dr_return region. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkSubtractRegion( + TkRegion sra, + TkRegion srb, + TkRegion dr_return) +{ + CombineRgn((HRGN) dr_return, (HRGN) sra, (HRGN) srb, RGN_DIFF); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinScrlbr.c b/tk8.6/win/tkWinScrlbr.c new file mode 100644 index 0000000..1b3717e --- /dev/null +++ b/tk8.6/win/tkWinScrlbr.c @@ -0,0 +1,701 @@ +/* + * tkWinScrollbar.c -- + * + * This file implements the Windows specific portion of the scrollbar + * widget. + * + * Copyright (c) 1996 by Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkScrollbar.h" + +/* + * The following constant is used to specify the maximum scroll position. This + * value is limited by the Win32 API to either 16-bits or 32-bits, depending + * on the context. For now we'll just use a value small enough to fit in + * 16-bits, but which gives us 4-digits of precision. + */ + +#define MAX_SCROLL 10000 + +/* + * Declaration of Windows specific scrollbar structure. + */ + +typedef struct WinScrollbar { + TkScrollbar info; /* Generic scrollbar info. */ + WNDPROC oldProc; /* Old window procedure. */ + int lastVertical; /* 1 if was vertical at last refresh. */ + HWND hwnd; /* Current window handle. */ + int winFlags; /* Various flags; see below. */ +} WinScrollbar; + +/* + * Flag bits for native scrollbars: + * + * IN_MODAL_LOOP: Non-zero means this scrollbar is in the middle + * of a modal loop. + * ALREADY_DEAD: Non-zero means this scrollbar has been + * destroyed, but has not been cleaned up. + */ + +#define IN_MODAL_LOOP 1 +#define ALREADY_DEAD 2 + +/* + * Cached system metrics used to determine scrollbar geometry. + */ + +static int initialized = 0; +static int hArrowWidth, hThumb; /* Horizontal control metrics. */ +static int vArrowHeight, vThumb; /* Vertical control metrics. */ + +TCL_DECLARE_MUTEX(winScrlbrMutex) + +/* + * Declarations for functions defined in this file. + */ + +static Window CreateProc(Tk_Window tkwin, Window parent, + ClientData instanceData); +static void ModalLoop(WinScrollbar *, XEvent *eventPtr); +static LRESULT CALLBACK ScrollbarProc(HWND hwnd, UINT message, WPARAM wParam, + LPARAM lParam); +static void UpdateScrollbar(WinScrollbar *scrollPtr); +static void UpdateScrollbarMetrics(void); + +/* + * The class procedure table for the scrollbar widget. + */ + +const Tk_ClassProcs tkpScrollbarProcs = { + sizeof(Tk_ClassProcs), /* size */ + NULL, /* worldChangedProc */ + CreateProc, /* createProc */ + NULL /* modalProc */ +}; + +static void +WinScrollbarEventProc(ClientData clientData, XEvent *eventPtr) +{ + WinScrollbar *scrollPtr = clientData; + + if (eventPtr->type == ButtonPress) { + ModalLoop(scrollPtr, eventPtr); + } else { + TkScrollbarEventProc(clientData, eventPtr); + } +} + + +/* + *---------------------------------------------------------------------- + * + * TkpCreateScrollbar -- + * + * Allocate a new TkScrollbar structure. + * + * Results: + * Returns a newly allocated TkScrollbar structure. + * + * Side effects: + * Registers an event handler for the widget. + * + *---------------------------------------------------------------------- + */ + +TkScrollbar * +TkpCreateScrollbar( + Tk_Window tkwin) +{ + WinScrollbar *scrollPtr; + + if (!initialized) { + Tcl_MutexLock(&winScrlbrMutex); + UpdateScrollbarMetrics(); + initialized = 1; + Tcl_MutexUnlock(&winScrlbrMutex); + } + + scrollPtr = ckalloc(sizeof(WinScrollbar)); + scrollPtr->winFlags = 0; + scrollPtr->hwnd = NULL; + + Tk_CreateEventHandler(tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask|ButtonPressMask, + WinScrollbarEventProc, scrollPtr); + + return (TkScrollbar *) scrollPtr; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateScrollbar -- + * + * This function updates the position and size of the scrollbar thumb + * based on the current settings. + * + * Results: + * None. + * + * Side effects: + * Moves the thumb. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateScrollbar( + WinScrollbar *scrollPtr) +{ + SCROLLINFO scrollInfo; + double thumbSize; + + /* + * Update the current scrollbar position and shape. + */ + + scrollInfo.fMask = SIF_PAGE | SIF_POS | SIF_RANGE; + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.nMin = 0; + scrollInfo.nMax = MAX_SCROLL; + thumbSize = (scrollPtr->info.lastFraction - scrollPtr->info.firstFraction); + scrollInfo.nPage = ((UINT) (thumbSize * (double) MAX_SCROLL)) + 1; + if (thumbSize < 1.0) { + scrollInfo.nPos = (int) + ((scrollPtr->info.firstFraction / (1.0-thumbSize)) + * (MAX_SCROLL - (scrollInfo.nPage - 1))); + } else { + scrollInfo.nPos = 0; + + /* + * Disable the scrollbar when there is nothing to scroll. This is + * standard Windows style (see eg Notepad). Also prevents possible + * crash on XP+ systems [Bug #624116]. + */ + + scrollInfo.fMask |= SIF_DISABLENOSCROLL; + } + SetScrollInfo(scrollPtr->hwnd, SB_CTL, &scrollInfo, TRUE); +} + +/* + *---------------------------------------------------------------------- + * + * CreateProc -- + * + * This function creates a new Scrollbar control, subclasses the + * instance, and generates a new Window object. + * + * Results: + * Returns the newly allocated Window object, or None on failure. + * + * Side effects: + * Causes a new Scrollbar control to come into existence. + * + *---------------------------------------------------------------------- + */ + +static Window +CreateProc( + Tk_Window tkwin, /* Token for window. */ + Window parentWin, /* Parent of new window. */ + ClientData instanceData) /* Scrollbar instance data. */ +{ + DWORD style; + Window window; + HWND parent; + TkWindow *winPtr; + WinScrollbar *scrollPtr = (WinScrollbar *)instanceData; + + parent = Tk_GetHWND(parentWin); + + if (scrollPtr->info.vertical) { + style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + | SBS_VERT; + } else { + style = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS + | SBS_HORZ; + } + + scrollPtr->hwnd = CreateWindow(TEXT("SCROLLBAR"), NULL, style, + Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), + parent, NULL, Tk_GetHINSTANCE(), NULL); + + /* + * Ensure new window is inserted into the stacking order at the correct + * place. + */ + + SetWindowPos(scrollPtr->hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + + for (winPtr = ((TkWindow*)tkwin)->nextPtr; winPtr != NULL; + winPtr = winPtr->nextPtr) { + if ((winPtr->window != None) && !(winPtr->flags & TK_TOP_HIERARCHY)) { + TkWinSetWindowPos(scrollPtr->hwnd, Tk_GetHWND(winPtr->window), + Below); + break; + } + } + + scrollPtr->lastVertical = scrollPtr->info.vertical; + scrollPtr->oldProc = (WNDPROC)SetWindowLongPtr(scrollPtr->hwnd, + GWLP_WNDPROC, (LONG_PTR) ScrollbarProc); + window = Tk_AttachHWND(tkwin, scrollPtr->hwnd); + + UpdateScrollbar(scrollPtr); + return window; +} + +/* + *-------------------------------------------------------------- + * + * TkpDisplayScrollbar -- + * + * This procedure redraws the contents of a scrollbar window. It is + * invoked as a do-when-idle handler, so it only runs when there's + * nothing else for the application to do. + * + * Results: + * None. + * + * Side effects: + * Information appears on the screen. + * + *-------------------------------------------------------------- + */ + +void +TkpDisplayScrollbar( + ClientData clientData) /* Information about window. */ +{ + WinScrollbar *scrollPtr = (WinScrollbar *) clientData; + Tk_Window tkwin = scrollPtr->info.tkwin; + + scrollPtr->info.flags &= ~REDRAW_PENDING; + if ((tkwin == NULL) || !Tk_IsMapped(tkwin)) { + return; + } + + /* + * Destroy and recreate the scrollbar control if the orientation has + * changed. + */ + + if (scrollPtr->lastVertical != scrollPtr->info.vertical) { + HWND hwnd = Tk_GetHWND(Tk_WindowId(tkwin)); + + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) scrollPtr->oldProc); + DestroyWindow(hwnd); + + CreateProc(tkwin, Tk_WindowId(Tk_Parent(tkwin)), + (ClientData) scrollPtr); + } else { + UpdateScrollbar(scrollPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpDestroyScrollbar -- + * + * Free data structures associated with the scrollbar control. + * + * Results: + * None. + * + * Side effects: + * Restores the default control state. + * + *---------------------------------------------------------------------- + */ + +void +TkpDestroyScrollbar( + TkScrollbar *scrollPtr) +{ + WinScrollbar *winScrollPtr = (WinScrollbar *)scrollPtr; + HWND hwnd = winScrollPtr->hwnd; + + if (hwnd) { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (INT_PTR) winScrollPtr->oldProc); + if (winScrollPtr->winFlags & IN_MODAL_LOOP) { + ((TkWindow *)scrollPtr->tkwin)->flags |= TK_DONT_DESTROY_WINDOW; + SetParent(hwnd, NULL); + } + } + winScrollPtr->winFlags |= ALREADY_DEAD; +} + +/* + *---------------------------------------------------------------------- + * + * UpdateScrollbarMetrics -- + * + * This function retrieves the current system metrics for a scrollbar. + * + * Results: + * None. + * + * Side effects: + * Updates the geometry cache info for all scrollbars. + * + *---------------------------------------------------------------------- + */ + +void +UpdateScrollbarMetrics(void) +{ + int arrowWidth = GetSystemMetrics(SM_CXVSCROLL); + + hArrowWidth = GetSystemMetrics(SM_CXHSCROLL); + hThumb = GetSystemMetrics(SM_CXHTHUMB); + vArrowHeight = GetSystemMetrics(SM_CYVSCROLL); + vThumb = GetSystemMetrics(SM_CYVTHUMB); + + sprintf(tkDefScrollbarWidth, "%d", arrowWidth); +} + +/* + *---------------------------------------------------------------------- + * + * TkpComputeScrollbarGeometry -- + * + * After changes in a scrollbar's size or configuration, this procedure + * recomputes various geometry information used in displaying the + * scrollbar. + * + * Results: + * None. + * + * Side effects: + * The scrollbar will be displayed differently. + * + *---------------------------------------------------------------------- + */ + +void +TkpComputeScrollbarGeometry( + register TkScrollbar *scrollPtr) + /* Scrollbar whose geometry may have + * changed. */ +{ + int fieldLength, minThumbSize; + + /* + * Windows doesn't use focus rings on scrollbars, but we still perform + * basic sanity checks to appease backwards compatibility. + */ + + if (scrollPtr->highlightWidth < 0) { + scrollPtr->highlightWidth = 0; + } + + if (scrollPtr->vertical) { + scrollPtr->arrowLength = vArrowHeight; + fieldLength = Tk_Height(scrollPtr->tkwin); + minThumbSize = vThumb; + } else { + scrollPtr->arrowLength = hArrowWidth; + fieldLength = Tk_Width(scrollPtr->tkwin); + minThumbSize = hThumb; + } + fieldLength -= 2*scrollPtr->arrowLength; + if (fieldLength < 0) { + fieldLength = 0; + } + scrollPtr->sliderFirst = (int) ((double)fieldLength + * scrollPtr->firstFraction); + scrollPtr->sliderLast = (int) ((double)fieldLength + * scrollPtr->lastFraction); + + /* + * Adjust the slider so that some piece of it is always displayed in the + * scrollbar and so that it has at least a minimal width (so it can be + * grabbed with the mouse). + */ + + if (scrollPtr->sliderFirst > fieldLength) { + scrollPtr->sliderFirst = fieldLength; + } + if (scrollPtr->sliderFirst < 0) { + scrollPtr->sliderFirst = 0; + } + if (scrollPtr->sliderLast < (scrollPtr->sliderFirst + + minThumbSize)) { + scrollPtr->sliderLast = scrollPtr->sliderFirst + minThumbSize; + } + if (scrollPtr->sliderLast > fieldLength) { + scrollPtr->sliderLast = fieldLength; + } + scrollPtr->sliderFirst += scrollPtr->arrowLength; + scrollPtr->sliderLast += scrollPtr->arrowLength; + + /* + * Register the desired geometry for the window (leave enough space for + * the two arrows plus a minimum-size slider, plus border around the whole + * window, if any). Then arrange for the window to be redisplayed. + */ + + if (scrollPtr->vertical) { + Tk_GeometryRequest(scrollPtr->tkwin, + scrollPtr->width, 2*scrollPtr->arrowLength + minThumbSize); + } else { + Tk_GeometryRequest(scrollPtr->tkwin, + 2*scrollPtr->arrowLength + minThumbSize, scrollPtr->width); + } + Tk_SetInternalBorder(scrollPtr->tkwin, 0); +} + +/* + *---------------------------------------------------------------------- + * + * ScrollbarProc -- + * + * This function is call by Windows whenever an event occurs on a + * scrollbar control created by Tk. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * May generate events. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +ScrollbarProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + POINT point; + WinScrollbar *scrollPtr; + Tk_Window tkwin = Tk_HWNDToWindow(hwnd); + + if (tkwin == NULL) { + Tcl_Panic("ScrollbarProc called on an invalid HWND"); + } + scrollPtr = (WinScrollbar *)((TkWindow*)tkwin)->instanceData; + + switch(message) { + case WM_HSCROLL: + case WM_VSCROLL: { + Tcl_Interp *interp; + Tcl_DString cmdString; + int command = LOWORD(wParam); + int code; + + GetCursorPos(&point); + Tk_TranslateWinEvent(NULL, WM_MOUSEMOVE, 0, + MAKELPARAM(point.x, point.y), &result); + + if (command == SB_ENDSCROLL) { + return 0; + } + + /* + * Bail out immediately if there isn't a command to invoke. + */ + + if (scrollPtr->info.commandSize == 0) { + Tcl_ServiceAll(); + return 0; + } + + Tcl_DStringInit(&cmdString); + Tcl_DStringAppend(&cmdString, scrollPtr->info.command, + scrollPtr->info.commandSize); + + if (command == SB_LINELEFT || command == SB_LINERIGHT) { + Tcl_DStringAppendElement(&cmdString, "scroll"); + Tcl_DStringAppendElement(&cmdString, + (command == SB_LINELEFT ) ? "-1" : "1"); + Tcl_DStringAppendElement(&cmdString, "units"); + } else if (command == SB_PAGELEFT || command == SB_PAGERIGHT) { + Tcl_DStringAppendElement(&cmdString, "scroll"); + Tcl_DStringAppendElement(&cmdString, + (command == SB_PAGELEFT ) ? "-1" : "1"); + Tcl_DStringAppendElement(&cmdString, "pages"); + } else { + char valueString[TCL_DOUBLE_SPACE]; + double pos = 0.0; + + switch (command) { + case SB_THUMBPOSITION: + pos = ((double)HIWORD(wParam)) / MAX_SCROLL; + break; + case SB_THUMBTRACK: + pos = ((double)HIWORD(wParam)) / MAX_SCROLL; + break; + case SB_TOP: + pos = 0.0; + break; + case SB_BOTTOM: + pos = 1.0; + break; + } + + Tcl_PrintDouble(NULL, pos, valueString); + Tcl_DStringAppendElement(&cmdString, "moveto"); + Tcl_DStringAppendElement(&cmdString, valueString); + } + + interp = scrollPtr->info.interp; + code = Tcl_EvalEx(interp, cmdString.string, -1, TCL_EVAL_GLOBAL); + if (code != TCL_OK && code != TCL_CONTINUE && code != TCL_BREAK) { + Tcl_AddErrorInfo(interp, "\n (scrollbar command)"); + Tcl_BackgroundException(interp, code); + } + Tcl_DStringFree(&cmdString); + + Tcl_ServiceAll(); + return 0; + } + + default: + if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { + return result; + } + } + return CallWindowProc(scrollPtr->oldProc, hwnd, message, wParam, lParam); +} + +/* + *---------------------------------------------------------------------- + * + * TkpConfigureScrollbar -- + * + * This procedure is called after the generic code has finished + * processing configuration options, in order to configure platform + * specific options. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpConfigureScrollbar( + register TkScrollbar *scrollPtr) + /* Information about widget; may or may not + * already have values for some fields. */ +{ +} + +/* + *---------------------------------------------------------------------- + * + * ModalLoop -- + * + * This function is invoked in response to a ButtonPress event. + * It resends the event to the Scrollbar window procedure, + * which in turn enters a modal loop. + * + *---------------------------------------------------------------------- + */ + +static void +ModalLoop( + WinScrollbar *scrollPtr, + XEvent *eventPtr) +{ + int oldMode; + + if (scrollPtr->hwnd) { + Tcl_Preserve((ClientData)scrollPtr); + scrollPtr->winFlags |= IN_MODAL_LOOP; + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + TkWinResendEvent(scrollPtr->oldProc, scrollPtr->hwnd, eventPtr); + (void) Tcl_SetServiceMode(oldMode); + scrollPtr->winFlags &= ~IN_MODAL_LOOP; + if (scrollPtr->hwnd && scrollPtr->winFlags & ALREADY_DEAD) { + DestroyWindow(scrollPtr->hwnd); + } + Tcl_Release((ClientData)scrollPtr); + } +} + +/* + *-------------------------------------------------------------- + * + * TkpScrollbarPosition -- + * + * Determine the scrollbar element corresponding to a given position. + * + * Results: + * One of TOP_ARROW, TOP_GAP, etc., indicating which element of the + * scrollbar covers the position given by (x, y). If (x,y) is outside the + * scrollbar entirely, then OUTSIDE is returned. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +int +TkpScrollbarPosition( + register TkScrollbar *scrollPtr, + /* Scrollbar widget record. */ + int x, int y) /* Coordinates within scrollPtr's window. */ +{ + int length, width, tmp; + + if (scrollPtr->vertical) { + length = Tk_Height(scrollPtr->tkwin); + width = Tk_Width(scrollPtr->tkwin); + } else { + tmp = x; + x = y; + y = tmp; + length = Tk_Width(scrollPtr->tkwin); + width = Tk_Height(scrollPtr->tkwin); + } + + if ((x < scrollPtr->inset) || (x >= (width - scrollPtr->inset)) + || (y < scrollPtr->inset) || (y >= (length - scrollPtr->inset))) { + return OUTSIDE; + } + + /* + * All of the calculations in this procedure mirror those in + * TkpDisplayScrollbar. Be sure to keep the two consistent. + */ + + if (y < (scrollPtr->inset + scrollPtr->arrowLength)) { + return TOP_ARROW; + } + if (y < scrollPtr->sliderFirst) { + return TOP_GAP; + } + if (y < scrollPtr->sliderLast) { + return SLIDER; + } + if (y >= (length - (scrollPtr->arrowLength + scrollPtr->inset))) { + return BOTTOM_ARROW; + } + return BOTTOM_GAP; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinSend.c b/tk8.6/win/tkWinSend.c new file mode 100644 index 0000000..fca8561 --- /dev/null +++ b/tk8.6/win/tkWinSend.c @@ -0,0 +1,1017 @@ +/* + * tkWinSend.c -- + * + * This file provides functions that implement the "send" command, + * allowing commands to be passed from interpreter to interpreter. + * + * Copyright (c) 1997 by Sun Microsystems, Inc. + * Copyright (c) 2003 Pat Thoyts <patthoyts@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkInt.h" +#include "tkWinSendCom.h" + +/* + * Should be defined in WTypes.h but mingw 1.0 is missing them. + */ + +#ifndef _ROTFLAGS_DEFINED +#define _ROTFLAGS_DEFINED +#define ROTFLAGS_REGISTRATIONKEEPSALIVE 0x01 +#define ROTFLAGS_ALLOWANYCLIENT 0x02 +#endif /* ! _ROTFLAGS_DEFINED */ + +#define TKWINSEND_CLASS_NAME "TclEval" +#define TKWINSEND_REGISTRATION_BASE L"TclEval" + +#define MK_E_MONIKERALREADYREGISTERED \ + MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF, 0x02A1) + +/* + * Package information structure. This is used to keep interpreter specific + * details for use when releasing the package resources upon interpreter + * deletion or package removal. + */ + +typedef struct { + char *name; /* The registered application name */ + DWORD cookie; /* ROT cookie returned on registration */ + LPUNKNOWN obj; /* Interface for the registration object */ + Tcl_Interp *interp; + Tcl_Command token; /* Winsend command token */ +} RegisteredInterp; + +typedef struct SendEvent { + Tcl_Event header; + Tcl_Interp *interp; + Tcl_Obj *cmdPtr; +} SendEvent; + +#ifdef TK_SEND_ENABLED_ON_WINDOWS +typedef struct { + int initialized; +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ + +/* + * Functions internal to this file. + */ + +#ifdef TK_SEND_ENABLED_ON_WINDOWS +static void CmdDeleteProc(ClientData clientData); +static void InterpDeleteProc(ClientData clientData, + Tcl_Interp *interp); +static void RevokeObjectRegistration(RegisteredInterp *riPtr); +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ +static HRESULT BuildMoniker(const char *name, LPMONIKER *pmk); +#ifdef TK_SEND_ENABLED_ON_WINDOWS +static HRESULT RegisterInterp(const char *name, + RegisteredInterp *riPtr); +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ +static int FindInterpreterObject(Tcl_Interp *interp, + const char *name, LPDISPATCH *ppdisp); +static int Send(LPDISPATCH pdispInterp, Tcl_Interp *interp, + int async, ClientData clientData, int objc, + Tcl_Obj *const objv[]); +static void SendTrace(const char *format, ...); +static Tcl_EventProc SendEventProc; + +#if defined(DEBUG) || defined(_DEBUG) +#define TRACE SendTrace +#else +#define TRACE 1 ? ((void)0) : SendTrace +#endif /* DEBUG || _DEBUG */ + +/* + *-------------------------------------------------------------- + * + * Tk_SetAppName -- + * + * This function is called to associate an ASCII name with a Tk + * application. If the application has already been named, the name + * replaces the old one. + * + * Results: + * The return value is the name actually given to the application. This + * will normally be the same as name, but if name was already in use for + * an application then a name of the form "name #2" will be chosen, with + * a high enough number to make the name unique. + * + * Side effects: + * Registration info is saved, thereby allowing the "send" command to be + * used later to invoke commands in the application. In addition, the + * "send" command is created in the application's interpreter. The + * registration will be removed automatically if the interpreter is + * deleted or the "send" command is removed. + * + *-------------------------------------------------------------- + */ + +const char * +Tk_SetAppName( + Tk_Window tkwin, /* Token for any window in the application to + * be named: it is just used to identify the + * application and the display. */ + const char *name) /* The name that will be used to refer to the + * interpreter in later "send" commands. Must + * be globally unique. */ +{ +#ifndef TK_SEND_ENABLED_ON_WINDOWS + /* + * Temporarily disabled for bug #858822 + */ + + return name; +#else /* TK_SEND_ENABLED_ON_WINDOWS */ + + ThreadSpecificData *tsdPtr = NULL; + TkWindow *winPtr = (TkWindow *) tkwin; + RegisteredInterp *riPtr = NULL; + Tcl_Interp *interp; + HRESULT hr = S_OK; + + interp = winPtr->mainPtr->interp; + tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * Initialise the COM library for this interpreter just once. + */ + + if (tsdPtr->initialized == 0) { + hr = CoInitialize(0); + if (FAILED(hr)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "failed to initialize the COM library", -1)); + Tcl_SetErrorCode(interp, "TK", "SEND", "COM", NULL); + return ""; + } + tsdPtr->initialized = 1; + TRACE("Initialized COM library for interp 0x%08X\n", (long)interp); + } + + /* + * If the interp hasn't been registered before then we need to create the + * registration structure and the COM object. If it has been registered + * already then we can reuse all and just register the new name. + */ + + riPtr = Tcl_GetAssocData(interp, "tkWinSend::ri", NULL); + if (riPtr == NULL) { + LPUNKNOWN *objPtr; + + riPtr = ckalloc(sizeof(RegisteredInterp)); + memset(riPtr, 0, sizeof(RegisteredInterp)); + riPtr->interp = interp; + + objPtr = &riPtr->obj; + hr = TkWinSendCom_CreateInstance(interp, &IID_IUnknown, + (void **) objPtr); + + Tcl_CreateObjCommand(interp, "send", Tk_SendObjCmd, riPtr, + CmdDeleteProc); + if (Tcl_IsSafe(interp)) { + Tcl_HideCommand(interp, "send", "send"); + } + Tcl_SetAssocData(interp, "tkWinSend::ri", NULL, riPtr); + } else { + RevokeObjectRegistration(riPtr); + } + + RegisterInterp(name, riPtr); + return (const char *) riPtr->name; +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ +} + +/* + *---------------------------------------------------------------------- + * + * TkGetInterpNames -- + * + * This function is invoked to fetch a list of all the interpreter names + * currently registered for the display of a particular window. + * + * Results: + * A standard Tcl return value. Interp->result will be set to hold a list + * of all the interpreter names defined for tkwin's display. If an error + * occurs, then TCL_ERROR is returned and interp->result will hold an + * error message. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkGetInterpNames( + Tcl_Interp *interp, /* Interpreter for returning a result. */ + Tk_Window tkwin) /* Window whose display is to be used for the + * lookup. */ +{ +#ifndef TK_SEND_ENABLED_ON_WINDOWS + /* + * Temporarily disabled for bug #858822 + */ + + return TCL_OK; +#else /* TK_SEND_ENABLED_ON_WINDOWS */ + + LPRUNNINGOBJECTTABLE pROT = NULL; + LPCOLESTR oleszStub = TKWINSEND_REGISTRATION_BASE; + HRESULT hr = S_OK; + Tcl_Obj *objList = NULL; + int result = TCL_OK; + + hr = GetRunningObjectTable(0, &pROT); + if (SUCCEEDED(hr)) { + IBindCtx* pBindCtx = NULL; + objList = Tcl_NewListObj(0, NULL); + hr = CreateBindCtx(0, &pBindCtx); + + if (SUCCEEDED(hr)) { + IEnumMoniker* pEnum; + + hr = pROT->lpVtbl->EnumRunning(pROT, &pEnum); + if (SUCCEEDED(hr)) { + IMoniker* pmk = NULL; + + while (pEnum->lpVtbl->Next(pEnum, 1, &pmk, NULL) == S_OK) { + LPOLESTR olestr; + + hr = pmk->lpVtbl->GetDisplayName(pmk, pBindCtx, NULL, + &olestr); + if (SUCCEEDED(hr)) { + IMalloc *pMalloc = NULL; + + if (wcsncmp(olestr, oleszStub, + wcslen(oleszStub)) == 0) { + LPOLESTR p = olestr + wcslen(oleszStub); + + if (*p) { + Tcl_DString ds; + + Tcl_WinTCharToUtf(p + 1, -1, &ds); + result = Tcl_ListObjAppendElement(interp, + objList, + Tcl_NewStringObj(Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds))); + Tcl_DStringFree(&ds); + } + } + + hr = CoGetMalloc(1, &pMalloc); + if (SUCCEEDED(hr)) { + pMalloc->lpVtbl->Free(pMalloc, (void*)olestr); + pMalloc->lpVtbl->Release(pMalloc); + } + } + pmk->lpVtbl->Release(pmk); + } + pEnum->lpVtbl->Release(pEnum); + } + pBindCtx->lpVtbl->Release(pBindCtx); + } + pROT->lpVtbl->Release(pROT); + } + + if (FAILED(hr)) { + /* + * Expire the list if set. + */ + + if (objList != NULL) { + Tcl_DecrRefCount(objList); + } + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); + result = TCL_ERROR; + } + + if (result == TCL_OK) { + Tcl_SetObjResult(interp, objList); + } + + return result; +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ +} + +/* + *-------------------------------------------------------------- + * + * Tk_SendCmd -- + * + * This function is invoked to process the "send" Tcl command. See the + * user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *-------------------------------------------------------------- + */ + +int +Tk_SendObjCmd( + ClientData clientData, /* Information about sender (only dispPtr + * field is used). */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument strings. */ +{ + enum { + SEND_ASYNC, SEND_DISPLAYOF, SEND_LAST + }; + static const char *const sendOptions[] = { + "-async", "-displayof", "--", NULL + }; + int result = TCL_OK; + int i, optind, async = 0; + Tcl_Obj *displayPtr = NULL; + + /* + * Process the command options. + */ + + for (i = 1; i < objc; i++) { + if (Tcl_GetIndexFromObjStruct(interp, objv[i], sendOptions, + sizeof(char *), "option", 0, &optind) != TCL_OK) { + break; + } + if (optind == SEND_ASYNC) { + ++async; + } else if (optind == SEND_DISPLAYOF) { + displayPtr = objv[++i]; + } else if (optind == SEND_LAST) { + i++; + break; + } + } + + /* + * Ensure we still have a valid command. + */ + + if ((objc - i) < 2) { + Tcl_WrongNumArgs(interp, 1, objv, + "?-async? ?-displayof? ?--? interpName arg ?arg ...?"); + result = TCL_ERROR; + } + + /* + * We don't support displayPtr. See TIP #150. + */ + + if (displayPtr) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "option not implemented: \"displayof\" is not available" + " for this platform.", -1)); + Tcl_SetErrorCode(interp, "TK", "SEND", "DISPLAYOF_WIN", NULL); + result = TCL_ERROR; + } + + /* + * Send the arguments to the foreign interp. + */ + /* FIX ME: we need to check for local interp */ + if (result == TCL_OK) { + LPDISPATCH pdisp; + + result = FindInterpreterObject(interp, Tcl_GetString(objv[i]), &pdisp); + if (result == TCL_OK) { + i++; + result = Send(pdisp, interp, async, clientData, objc-i, objv+i); + pdisp->lpVtbl->Release(pdisp); + } + } + + return result; +} + +/* + *-------------------------------------------------------------- + * + * FindInterpreterObject -- + * + * Search the set of objects currently registered with the Running Object + * Table for one which matches the registered name. Tk objects are named + * using BuildMoniker by always prefixing with TclEval. + * + * Results: + * If a matching object registration is found, then the registered + * IDispatch interface pointer is returned. If not, then an error message + * is placed in the interpreter and TCL_ERROR is returned. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +static int +FindInterpreterObject( + Tcl_Interp *interp, + const char *name, + LPDISPATCH *ppdisp) +{ + LPRUNNINGOBJECTTABLE pROT = NULL; + int result = TCL_OK; + HRESULT hr = GetRunningObjectTable(0, &pROT); + + if (SUCCEEDED(hr)) { + IBindCtx* pBindCtx = NULL; + + hr = CreateBindCtx(0, &pBindCtx); + if (SUCCEEDED(hr)) { + LPMONIKER pmk = NULL; + + hr = BuildMoniker(name, &pmk); + if (SUCCEEDED(hr)) { + IUnknown *pUnkInterp = NULL, **ppUnkInterp = &pUnkInterp; + + hr = pROT->lpVtbl->IsRunning(pROT, pmk); + hr = pmk->lpVtbl->BindToObject(pmk, pBindCtx, NULL, + &IID_IUnknown, (void **) ppUnkInterp); + if (SUCCEEDED(hr)) { + hr = pUnkInterp->lpVtbl->QueryInterface(pUnkInterp, + &IID_IDispatch, (void **) ppdisp); + pUnkInterp->lpVtbl->Release(pUnkInterp); + + } else { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "no application named \"%s\"", name)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "APPLICATION", + NULL); + result = TCL_ERROR; + } + + pmk->lpVtbl->Release(pmk); + } + pBindCtx->lpVtbl->Release(pBindCtx); + } + pROT->lpVtbl->Release(pROT); + } + if (FAILED(hr) && result == TCL_OK) { + Tcl_SetObjResult(interp, TkWin32ErrorObj(hr)); + result = TCL_ERROR; + } + return result; +} + +/* + *-------------------------------------------------------------- + * + * CmdDeleteProc -- + * + * This function is invoked by Tcl when the "send" command is deleted in + * an interpreter. It unregisters the interpreter. + * + * Results: + * None. + * + * Side effects: + * The interpreter given by riPtr is unregistered, the registration + * structure is free'd and the COM object unregistered and released. + * + *-------------------------------------------------------------- + */ + +#ifdef TK_SEND_ENABLED_ON_WINDOWS +static void +CmdDeleteProc( + ClientData clientData) +{ + RegisteredInterp *riPtr = (RegisteredInterp *)clientData; + + /* + * Lock the package structure in memory. + */ + + Tcl_Preserve(clientData); + + /* + * Revoke the ROT registration. + */ + + RevokeObjectRegistration(riPtr); + + /* + * Release the registration object. + */ + + riPtr->obj->lpVtbl->Release(riPtr->obj); + riPtr->obj = NULL; + + Tcl_DeleteAssocData(riPtr->interp, "tkWinSend::ri"); + + /* + * Unlock the package data structure. + */ + + Tcl_Release(clientData); + + ckfree(clientData); +} + +/* + *-------------------------------------------------------------- + * + * RevokeObjectRegistration -- + * + * Releases the interpreters registration object from the Running Object + * Table. + * + * Results: + * None. + * + * Side effects: + * The stored cookie value is zeroed and the name is free'd and the + * pointer set to NULL. + * + *-------------------------------------------------------------- + */ + +static void +RevokeObjectRegistration( + RegisteredInterp *riPtr) +{ + LPRUNNINGOBJECTTABLE pROT = NULL; + HRESULT hr = S_OK; + + if (riPtr->cookie != 0) { + hr = GetRunningObjectTable(0, &pROT); + if (SUCCEEDED(hr)) { + hr = pROT->lpVtbl->Revoke(pROT, riPtr->cookie); + pROT->lpVtbl->Release(pROT); + riPtr->cookie = 0; + } + } + + /* + * Release the name storage. + */ + + if (riPtr->name != NULL) { + free(riPtr->name); + riPtr->name = NULL; + } +} +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ + +/* + * ---------------------------------------------------------------------- + * + * InterpDeleteProc -- + * + * This is called when the interpreter is deleted and used to unregister + * the COM libraries. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------- + */ + +#ifdef TK_SEND_ENABLED_ON_WINDOWS +static void +InterpDeleteProc( + ClientData clientData, + Tcl_Interp *interp) +{ + CoUninitialize(); +} +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ + +/* + * ---------------------------------------------------------------------- + * + * BuildMoniker -- + * + * Construct a moniker from the given name. This ensures that all our + * monikers have the same prefix. + * + * Results: + * S_OK. If the name cannot be turned into a moniker then a COM error + * code is returned. + * + * Side effects: + * The moniker created is stored at the address given by ppmk. + * + * ---------------------------------------------------------------------- + */ + +static HRESULT +BuildMoniker( + const char *name, + LPMONIKER *ppmk) +{ + LPMONIKER pmkClass = NULL; + HRESULT hr = CreateFileMoniker(TKWINSEND_REGISTRATION_BASE, &pmkClass); + + if (SUCCEEDED(hr)) { + LPMONIKER pmkItem = NULL; + Tcl_DString dString; + + Tcl_WinUtfToTChar(name, -1, &dString); + hr = CreateFileMoniker((LPOLESTR)Tcl_DStringValue(&dString), &pmkItem); + Tcl_DStringFree(&dString); + if (SUCCEEDED(hr)) { + hr = pmkClass->lpVtbl->ComposeWith(pmkClass, pmkItem, FALSE, ppmk); + pmkItem->lpVtbl->Release(pmkItem); + } + pmkClass->lpVtbl->Release(pmkClass); + } + return hr; +} + +/* + * ---------------------------------------------------------------------- + * + * RegisterInterp -- + * + * Attempts to register the provided name for this interpreter. If the + * given name is already in use, then a numeric suffix is appended as + * " #n" until we identify a unique name. + * + * Results: + * Returns S_OK if successful, else a COM error code. + * + * Side effects: + * Registration returns a cookie value which is stored. We also store a + * copy of the name. + * + * ---------------------------------------------------------------------- + */ + +#ifdef TK_SEND_ENABLED_ON_WINDOWS +static HRESULT +RegisterInterp( + const char *name, + RegisteredInterp *riPtr) +{ + HRESULT hr = S_OK; + LPRUNNINGOBJECTTABLE pROT = NULL; + LPMONIKER pmk = NULL; + int i, offset; + const char *actualName = name; + Tcl_DString dString; + Tcl_DStringInit(&dString); + + hr = GetRunningObjectTable(0, &pROT); + if (SUCCEEDED(hr)) { + offset = 0; + for (i = 1; SUCCEEDED(hr); i++) { + if (i > 1) { + if (i == 2) { + Tcl_DStringInit(&dString); + Tcl_DStringAppend(&dString, name, -1); + Tcl_DStringAppend(&dString, " #", 2); + offset = Tcl_DStringLength(&dString); + Tcl_DStringSetLength(&dString, offset+TCL_INTEGER_SPACE); + actualName = Tcl_DStringValue(&dString); + } + sprintf(Tcl_DStringValue(&dString) + offset, "%d", i); + } + + hr = BuildMoniker(actualName, &pmk); + if (SUCCEEDED(hr)) { + + hr = pROT->lpVtbl->Register(pROT, + ROTFLAGS_REGISTRATIONKEEPSALIVE, + riPtr->obj, pmk, &riPtr->cookie); + + pmk->lpVtbl->Release(pmk); + } + + if (hr == MK_S_MONIKERALREADYREGISTERED) { + pROT->lpVtbl->Revoke(pROT, riPtr->cookie); + } else if (hr == S_OK) { + break; + } + } + + pROT->lpVtbl->Release(pROT); + } + + if (SUCCEEDED(hr)) { + riPtr->name = strdup(actualName); + } + + Tcl_DStringFree(&dString); + return hr; +} +#endif /* TK_SEND_ENABLED_ON_WINDOWS */ + +/* + * ---------------------------------------------------------------------- + * + * Send -- + * + * Perform an interface call to the server object. We convert the Tcl + * arguments into a BSTR using 'concat'. The result should be a BSTR that + * we can set as the interp's result string. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------- + */ + +static int +Send( + LPDISPATCH pdispInterp, /* Pointer to the remote interp's COM + * object. */ + Tcl_Interp *interp, /* The local interpreter. */ + int async, /* Flag for the calling style. */ + ClientData clientData, /* The RegisteredInterp structure for this + * interp. */ + int objc, /* Number of arguments to be sent. */ + Tcl_Obj *const objv[]) /* The arguments to be sent. */ +{ + VARIANT vCmd, vResult; + DISPPARAMS dp; + EXCEPINFO ei; + UINT uiErr = 0; + HRESULT hr = S_OK, ehr = S_OK; + Tcl_Obj *cmd = NULL; + DISPID dispid; + Tcl_DString ds; + const char *src; + + cmd = Tcl_ConcatObj(objc, objv); + + /* + * Setup the arguments for the COM method call. + */ + + VariantInit(&vCmd); + VariantInit(&vResult); + memset(&dp, 0, sizeof(dp)); + memset(&ei, 0, sizeof(ei)); + + vCmd.vt = VT_BSTR; + src = Tcl_GetString(cmd); + Tcl_WinUtfToTChar(src, cmd->length, &ds); + vCmd.bstrVal = SysAllocString((WCHAR *) Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + + dp.cArgs = 1; + dp.rgvarg = &vCmd; + + /* + * Select the method to use based upon the async flag and call the method. + */ + + dispid = async ? TKWINSENDCOM_DISPID_ASYNC : TKWINSENDCOM_DISPID_SEND; + + hr = pdispInterp->lpVtbl->Invoke(pdispInterp, dispid, + &IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, + &dp, &vResult, &ei, &uiErr); + + /* + * Convert the result into a string and place in the interps result. + */ + + ehr = VariantChangeType(&vResult, &vResult, 0, VT_BSTR); + if (SUCCEEDED(ehr)) { + Tcl_WinTCharToUtf(vResult.bstrVal, (int) SysStringLen(vResult.bstrVal) * + sizeof (WCHAR), &ds); + Tcl_DStringResult(interp, &ds); + } + + /* + * Errors are returned as dispatch exceptions. If an error code was + * returned then we decode the exception and setup the Tcl error + * variables. + */ + + if (hr == DISP_E_EXCEPTION && ei.bstrSource != NULL) { + Tcl_Obj *opError, *opErrorCode, *opErrorInfo; + Tcl_WinTCharToUtf(ei.bstrSource, (int) SysStringLen(ei.bstrSource) * + sizeof (WCHAR), &ds); + opError = Tcl_NewStringObj(Tcl_DStringValue(&ds), + Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + Tcl_ListObjIndex(interp, opError, 0, &opErrorCode); + Tcl_SetObjErrorCode(interp, opErrorCode); + Tcl_ListObjIndex(interp, opError, 1, &opErrorInfo); + Tcl_AppendObjToErrorInfo(interp, opErrorInfo); + } + + /* + * Clean up any COM allocated resources. + */ + + SysFreeString(ei.bstrDescription); + SysFreeString(ei.bstrSource); + SysFreeString(ei.bstrHelpFile); + VariantClear(&vCmd); + + return (SUCCEEDED(hr) ? TCL_OK : TCL_ERROR); +} + +/* + * ---------------------------------------------------------------------- + * + * TkWinSend_SetExcepInfo -- + * + * Convert the error information from a Tcl interpreter into a COM + * exception structure. This information is then registered with the COM + * thread exception object so that it can be used for rich error + * reporting by COM clients. + * + * Results: + * None. + * + * Side effects: + * The current COM thread has its error object modified. + * + * ---------------------------------------------------------------------- + */ + +void +TkWinSend_SetExcepInfo( + Tcl_Interp *interp, + EXCEPINFO *pExcepInfo) +{ + Tcl_Obj *opError, *opErrorInfo, *opErrorCode; + ICreateErrorInfo *pCEI; + IErrorInfo *pEI, **ppEI = &pEI; + HRESULT hr; + Tcl_DString ds; + const char *src; + + if (!pExcepInfo) { + return; + } + + opError = Tcl_GetObjResult(interp); + opErrorInfo = Tcl_GetVar2Ex(interp, "errorInfo", NULL, TCL_GLOBAL_ONLY); + opErrorCode = Tcl_GetVar2Ex(interp, "errorCode", NULL, TCL_GLOBAL_ONLY); + + /* + * Pack the trace onto the end of the Tcl exception descriptor. + */ + + opErrorCode = Tcl_DuplicateObj(opErrorCode); + Tcl_IncrRefCount(opErrorCode); + Tcl_ListObjAppendElement(interp, opErrorCode, opErrorInfo); + /* TODO: Handle failure to append */ + + src = Tcl_GetString(opError); + Tcl_WinUtfToTChar(src, opError->length, &ds); + pExcepInfo->bstrDescription = + SysAllocString((WCHAR *) Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + src = Tcl_GetString(opErrorCode); + Tcl_WinUtfToTChar(src, opErrorCode->length, &ds); + pExcepInfo->bstrSource = + SysAllocString((WCHAR *) Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + Tcl_DecrRefCount(opErrorCode); + pExcepInfo->scode = E_FAIL; + + hr = CreateErrorInfo(&pCEI); + if (!SUCCEEDED(hr)) { + return; + } + + hr = pCEI->lpVtbl->SetGUID(pCEI, &IID_IDispatch); + hr = pCEI->lpVtbl->SetDescription(pCEI, pExcepInfo->bstrDescription); + hr = pCEI->lpVtbl->SetSource(pCEI, pExcepInfo->bstrSource); + hr = pCEI->lpVtbl->QueryInterface(pCEI, &IID_IErrorInfo, (void **) ppEI); + if (SUCCEEDED(hr)) { + SetErrorInfo(0, pEI); + pEI->lpVtbl->Release(pEI); + } + pCEI->lpVtbl->Release(pCEI); +} + +/* + * ---------------------------------------------------------------------- + * + * TkWinSend_QueueCommand -- + * + * Queue a script for asynchronous evaluation. This is called from the + * COM objects Async method. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------- + */ + +int +TkWinSend_QueueCommand( + Tcl_Interp *interp, + Tcl_Obj *cmdPtr) +{ + SendEvent *evPtr; + + TRACE("SendQueueCommand()\n"); + + evPtr = ckalloc(sizeof(SendEvent)); + evPtr->header.proc = SendEventProc; + evPtr->header.nextPtr = NULL; + evPtr->interp = interp; + Tcl_Preserve(evPtr->interp); + + if (Tcl_IsShared(cmdPtr)) { + evPtr->cmdPtr = Tcl_DuplicateObj(cmdPtr); + } else { + evPtr->cmdPtr = cmdPtr; + Tcl_IncrRefCount(evPtr->cmdPtr); + } + + Tcl_QueueEvent((Tcl_Event *)evPtr, TCL_QUEUE_TAIL); + + return 0; +} + +/* + * ---------------------------------------------------------------------- + * + * SendEventProc -- + * + * Handle a request for an asynchronous send. Nothing is returned to the + * caller so the result is discarded. + * + * Results: + * Returns 1 if the event was handled or 0 to indicate it has been + * deferred. + * + * Side effects: + * The target interpreter's result will be modified. + * + * ---------------------------------------------------------------------- + */ + +static int +SendEventProc( + Tcl_Event *eventPtr, + int flags) +{ + SendEvent *evPtr = (SendEvent *)eventPtr; + + TRACE("SendEventProc\n"); + + Tcl_EvalObjEx(evPtr->interp, evPtr->cmdPtr, + TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); + + Tcl_DecrRefCount(evPtr->cmdPtr); + Tcl_Release(evPtr->interp); + + return 1; /* 1 to indicate the event has been handled */ +} + +/* + * ---------------------------------------------------------------------- + * + * SendTrace -- + * + * Provide trace information to the Windows debug stream. To use this - + * use the TRACE macro, which compiles to nothing when DEBUG is not + * defined. + * + * Results: + * None. + * + * Side effects: + * None. + * + * ---------------------------------------------------------------------- + */ + +static void +SendTrace( + const char *format, ...) +{ + va_list args; + static char buffer[1024]; + + va_start(args, format); + _vsnprintf(buffer, 1023, format, args); + OutputDebugStringA(buffer); + va_end(args); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinSendCom.c b/tk8.6/win/tkWinSendCom.c new file mode 100644 index 0000000..9e5b7a0 --- /dev/null +++ b/tk8.6/win/tkWinSendCom.c @@ -0,0 +1,479 @@ +/* + * tkWinSendCom.c -- + * + * This file provides support functions that implement the Windows "send" + * command using COM interfaces, allowing commands to be passed from + * interpreter to interpreter. See also tkWinSend.c, where most of the + * interesting functions are. + * + * We implement a COM class for use in registering Tcl interpreters with the + * system's Running Object Table. This class implements an IDispatch interface + * with the following method: + * Send(String cmd) As String + * In other words the Send methods takes a string and evaluates this in the + * Tcl interpreter. The result is returned as another string. + * + * Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkInt.h" +#include "tkWinSendCom.h" + +/* + * ---------------------------------------------------------------------- + * Non-public prototypes. + * + * These are the interface methods for IUnknown, IDispatch and + * ISupportErrorInfo. + * + * ---------------------------------------------------------------------- + */ + +static void TkWinSendCom_Destroy(LPDISPATCH pdisp); + +static STDMETHODIMP WinSendCom_QueryInterface(IDispatch *This, + REFIID riid, void **ppvObject); +static STDMETHODIMP_(ULONG) WinSendCom_AddRef(IDispatch *This); +static STDMETHODIMP_(ULONG) WinSendCom_Release(IDispatch *This); +static STDMETHODIMP WinSendCom_GetTypeInfoCount(IDispatch *This, + UINT *pctinfo); +static STDMETHODIMP WinSendCom_GetTypeInfo(IDispatch *This, UINT iTInfo, + LCID lcid, ITypeInfo **ppTI); +static STDMETHODIMP WinSendCom_GetIDsOfNames(IDispatch *This, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, + DISPID *rgDispId); +static STDMETHODIMP WinSendCom_Invoke(IDispatch *This, DISPID dispidMember, + REFIID riid, LCID lcid, WORD wFlags, + DISPPARAMS *pDispParams, VARIANT *pvarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr); +static STDMETHODIMP ISupportErrorInfo_QueryInterface( + ISupportErrorInfo *This, REFIID riid, + void **ppvObject); +static STDMETHODIMP_(ULONG) ISupportErrorInfo_AddRef( + ISupportErrorInfo *This); +static STDMETHODIMP_(ULONG) ISupportErrorInfo_Release( + ISupportErrorInfo *This); +static STDMETHODIMP ISupportErrorInfo_InterfaceSupportsErrorInfo( + ISupportErrorInfo *This, REFIID riid); +static HRESULT Send(TkWinSendCom *obj, VARIANT vCmd, + VARIANT *pvResult, EXCEPINFO *pExcepInfo, + UINT *puArgErr); +static HRESULT Async(TkWinSendCom *obj, VARIANT Cmd, + EXCEPINFO *pExcepInfo, UINT *puArgErr); + +/* + * ---------------------------------------------------------------------- + * + * CreateInstance -- + * + * Create and initialises a new instance of the WinSend COM class and + * returns an interface pointer for you to use. + * + * ---------------------------------------------------------------------- + */ + +HRESULT +TkWinSendCom_CreateInstance( + Tcl_Interp *interp, + REFIID riid, + void **ppv) +{ + /* + * Construct v-tables for each interface. + */ + + static IDispatchVtbl vtbl = { + WinSendCom_QueryInterface, + WinSendCom_AddRef, + WinSendCom_Release, + WinSendCom_GetTypeInfoCount, + WinSendCom_GetTypeInfo, + WinSendCom_GetIDsOfNames, + WinSendCom_Invoke, + }; + static ISupportErrorInfoVtbl vtbl2 = { + ISupportErrorInfo_QueryInterface, + ISupportErrorInfo_AddRef, + ISupportErrorInfo_Release, + ISupportErrorInfo_InterfaceSupportsErrorInfo, + }; + TkWinSendCom *obj = NULL; + + /* + * This had probably better always be globally visible memory so we shall + * use the COM Task allocator. + */ + + obj = (TkWinSendCom *) CoTaskMemAlloc(sizeof(TkWinSendCom)); + if (obj == NULL) { + *ppv = NULL; + return E_OUTOFMEMORY; + } + + obj->lpVtbl = &vtbl; + obj->lpVtbl2 = &vtbl2; + obj->refcount = 0; + obj->interp = interp; + + /* + * lock the interp? Tcl_AddRef/Retain? + */ + + return obj->lpVtbl->QueryInterface((IDispatch *) obj, riid, ppv); +} + +/* + * ---------------------------------------------------------------------- + * + * TkWinSendCom_Destroy -- + * + * This helper function is the destructor for our COM class. + * + * Results: + * None. + * + * Side effects: + * Releases the storage allocated for this object. + * + * ---------------------------------------------------------------------- + */ +static void +TkWinSendCom_Destroy( + LPDISPATCH pdisp) +{ + CoTaskMemFree((void *) pdisp); +} + +/* + * ---------------------------------------------------------------------- + * + * IDispatch -- + * + * The IDispatch interface implements the 'late-binding' COM methods + * typically used by scripting COM clients. The Invoke method is the most + * important one. + * + * ---------------------------------------------------------------------- + */ + +static STDMETHODIMP +WinSendCom_QueryInterface( + IDispatch *This, + REFIID riid, + void **ppvObject) +{ + HRESULT hr = E_NOINTERFACE; + TkWinSendCom *this = (TkWinSendCom *) This; + *ppvObject = NULL; + + if (memcmp(riid, &IID_IUnknown, sizeof(IID)) == 0 + || memcmp(riid, &IID_IDispatch, sizeof(IID)) == 0) { + *ppvObject = (void **) this; + this->lpVtbl->AddRef(This); + hr = S_OK; + } else if (memcmp(riid, &IID_ISupportErrorInfo, sizeof(IID)) == 0) { + *ppvObject = (void **) (this + 1); + this->lpVtbl2->AddRef((ISupportErrorInfo *) (this + 1)); + hr = S_OK; + } + return hr; +} + +static STDMETHODIMP_(ULONG) +WinSendCom_AddRef( + IDispatch *This) +{ + TkWinSendCom *this = (TkWinSendCom*)This; + + return InterlockedIncrement(&this->refcount); +} + +static STDMETHODIMP_(ULONG) +WinSendCom_Release( + IDispatch *This) +{ + long r = 0; + TkWinSendCom *this = (TkWinSendCom*)This; + + if ((r = InterlockedDecrement(&this->refcount)) == 0) { + TkWinSendCom_Destroy(This); + } + return r; +} + +static STDMETHODIMP +WinSendCom_GetTypeInfoCount( + IDispatch *This, + UINT *pctinfo) +{ + HRESULT hr = E_POINTER; + + if (pctinfo != NULL) { + *pctinfo = 0; + hr = S_OK; + } + return hr; +} + +static STDMETHODIMP +WinSendCom_GetTypeInfo( + IDispatch *This, + UINT iTInfo, + LCID lcid, + ITypeInfo **ppTI) +{ + HRESULT hr = E_POINTER; + + if (ppTI) { + *ppTI = NULL; + hr = E_NOTIMPL; + } + return hr; +} + +static STDMETHODIMP +WinSendCom_GetIDsOfNames( + IDispatch *This, + REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId) +{ + HRESULT hr = E_POINTER; + + if (rgDispId) { + hr = DISP_E_UNKNOWNNAME; + if (_wcsicmp(*rgszNames, L"Send") == 0) { + *rgDispId = TKWINSENDCOM_DISPID_SEND, hr = S_OK; + } else if (_wcsicmp(*rgszNames, L"Async") == 0) { + *rgDispId = TKWINSENDCOM_DISPID_ASYNC, hr = S_OK; + } + } + return hr; +} + +static STDMETHODIMP +WinSendCom_Invoke( + IDispatch *This, + DISPID dispidMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pvarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + HRESULT hr = DISP_E_MEMBERNOTFOUND; + TkWinSendCom *this = (TkWinSendCom*)This; + + switch (dispidMember) { + case TKWINSENDCOM_DISPID_SEND: + if (wFlags | DISPATCH_METHOD) { + if (pDispParams->cArgs != 1) { + hr = DISP_E_BADPARAMCOUNT; + } else { + hr = Send(this, pDispParams->rgvarg[0], pvarResult, + pExcepInfo, puArgErr); + } + } + break; + + case TKWINSENDCOM_DISPID_ASYNC: + if (wFlags | DISPATCH_METHOD) { + if (pDispParams->cArgs != 1) { + hr = DISP_E_BADPARAMCOUNT; + } else { + hr = Async(this, pDispParams->rgvarg[0], pExcepInfo, puArgErr); + } + } + break; + } + return hr; +} + +/* + * ---------------------------------------------------------------------- + * + * ISupportErrorInfo -- + * + * This interface provides rich error information to COM clients. Used by + * VB and scripting COM clients. + * + * ---------------------------------------------------------------------- + */ + +static STDMETHODIMP +ISupportErrorInfo_QueryInterface( + ISupportErrorInfo *This, + REFIID riid, + void **ppvObject) +{ + TkWinSendCom *this = (TkWinSendCom *)(This - 1); + + return this->lpVtbl->QueryInterface((IDispatch *) this, riid, ppvObject); +} + +static STDMETHODIMP_(ULONG) +ISupportErrorInfo_AddRef( + ISupportErrorInfo *This) +{ + TkWinSendCom *this = (TkWinSendCom *)(This - 1); + + return InterlockedIncrement(&this->refcount); +} + +static STDMETHODIMP_(ULONG) +ISupportErrorInfo_Release( + ISupportErrorInfo *This) +{ + TkWinSendCom *this = (TkWinSendCom *)(This - 1); + + return this->lpVtbl->Release((IDispatch *) this); +} + +static STDMETHODIMP +ISupportErrorInfo_InterfaceSupportsErrorInfo( + ISupportErrorInfo *This, + REFIID riid) +{ + /*TkWinSendCom *this = (TkWinSendCom*)(This - 1);*/ + return S_OK; /* or S_FALSE */ +} + +/* + * ---------------------------------------------------------------------- + * + * Async -- + * + * Queues the command for evaluation in the assigned interpreter. + * + * Results: + * A standard COM HRESULT is returned. The Tcl result is discarded. + * + * Side effects: + * The interpreters state and result will be modified. + * + * ---------------------------------------------------------------------- + */ + +static HRESULT +Async( + TkWinSendCom *obj, + VARIANT Cmd, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + HRESULT hr = S_OK; + VARIANT vCmd; + Tcl_DString ds; + + VariantInit(&vCmd); + + hr = VariantChangeType(&vCmd, &Cmd, 0, VT_BSTR); + if (FAILED(hr)) { + Tcl_SetObjResult(obj->interp, Tcl_NewStringObj( + "invalid args: Async(command)", -1)); + TkWinSend_SetExcepInfo(obj->interp, pExcepInfo); + hr = DISP_E_EXCEPTION; + } + + if (SUCCEEDED(hr) && obj->interp) { + Tcl_Obj *scriptPtr; + + Tcl_WinTCharToUtf(vCmd.bstrVal, (int) SysStringLen(vCmd.bstrVal) * + sizeof (WCHAR), &ds); + scriptPtr = + Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + TkWinSend_QueueCommand(obj->interp, scriptPtr); + } + + VariantClear(&vCmd); + return hr; +} + +/* + * ---------------------------------------------------------------------- + * + * Send -- + * + * Evaluates the string in the assigned interpreter. If the result is a + * valid address then set it to the result returned by the evaluation. + * Tcl exceptions are converted into COM exceptions. + * + * Results: + * A standard COM HRESULT is returned. The Tcl result is set as the + * method calls result. + * + * Side effects: + * The interpreters state and result will be modified. + * + * ---------------------------------------------------------------------- + */ + +static HRESULT +Send( + TkWinSendCom *obj, + VARIANT vCmd, + VARIANT *pvResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr) +{ + HRESULT hr = S_OK; + int result = TCL_OK; + VARIANT v; + register Tcl_Interp *interp = obj->interp; + Tcl_Obj *scriptPtr; + Tcl_DString ds; + + if (interp == NULL) { + return S_OK; + } + VariantInit(&v); + hr = VariantChangeType(&v, &vCmd, 0, VT_BSTR); + if (!SUCCEEDED(hr)) { + return hr; + } + + Tcl_WinTCharToUtf(v.bstrVal, (int) SysStringLen(v.bstrVal) * + sizeof (WCHAR), &ds); + scriptPtr = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + Tcl_Preserve(interp); + Tcl_IncrRefCount(scriptPtr); + result = Tcl_EvalObjEx(interp, scriptPtr, + TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL); + Tcl_DecrRefCount(scriptPtr); + if (pvResult != NULL) { + Tcl_Obj *obj; + const char *src; + + VariantInit(pvResult); + pvResult->vt = VT_BSTR; + obj = Tcl_GetObjResult(interp); + src = Tcl_GetString(obj); + Tcl_WinUtfToTChar(src, obj->length, &ds); + pvResult->bstrVal = SysAllocString((WCHAR *) Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + } + if (result == TCL_ERROR) { + hr = DISP_E_EXCEPTION; + TkWinSend_SetExcepInfo(interp, pExcepInfo); + } + Tcl_Release(interp); + VariantClear(&v); + return hr; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinSendCom.h b/tk8.6/win/tkWinSendCom.h new file mode 100644 index 0000000..cd6ec18 --- /dev/null +++ b/tk8.6/win/tkWinSendCom.h @@ -0,0 +1,61 @@ +/* + * tkWinSendCom.h -- + * + * This file provides procedures that implement the Windows "send" + * command, allowing commands to be passed from interpreter to + * interpreter. + * + * Copyright (C) 2002 Pat Thoyts <patthoyts@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#ifndef _tkWinSendCom_h_INCLUDE +#define _tkWinSendCom_h_INCLUDE + +#include "tkWinInt.h" +#include <ole2.h> + +#ifdef _MSC_VER +# pragma comment (lib, "ole32.lib") +# pragma comment (lib, "oleaut32.lib") +# pragma comment (lib, "uuid.lib") +#endif + +/* + * TkWinSendCom CoClass structure + */ + +typedef struct { + IDispatchVtbl *lpVtbl; + ISupportErrorInfoVtbl *lpVtbl2; + long refcount; + Tcl_Interp *interp; +} TkWinSendCom; + +/* + * TkWinSendCom Dispatch IDs + */ + +#define TKWINSENDCOM_DISPID_SEND 1 +#define TKWINSENDCOM_DISPID_ASYNC 2 + +/* + * TkWinSendCom public functions + */ + +MODULE_SCOPE HRESULT TkWinSendCom_CreateInstance(Tcl_Interp *interp, + REFIID riid, void **ppv); +MODULE_SCOPE int TkWinSend_QueueCommand(Tcl_Interp *interp, + Tcl_Obj *cmdPtr); +MODULE_SCOPE void TkWinSend_SetExcepInfo(Tcl_Interp *interp, + EXCEPINFO *pExcepInfo); + +#endif /* _tkWinSendCom_h_INCLUDE */ + +/* + * Local Variables: + * mode: c + * End: + */ diff --git a/tk8.6/win/tkWinTest.c b/tk8.6/win/tkWinTest.c new file mode 100644 index 0000000..6e79df3 --- /dev/null +++ b/tk8.6/win/tkWinTest.c @@ -0,0 +1,583 @@ +/* + * tkWinTest.c -- + * + * Contains commands for platform specific tests for the Windows + * platform. + * + * Copyright (c) 1997 Sun Microsystems, Inc. + * Copyright (c) 2000 by Scriptics Corporation. + * Copyright (c) 2001 by ActiveState Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#undef USE_TCL_STUBS +#define USE_TCL_STUBS +#undef USE_TK_STUBS +#define USE_TK_STUBS +#include "tkWinInt.h" + +HWND tkWinCurrentDialog; + +/* + * Forward declarations of functions defined later in this file: + */ + +static int TestclipboardObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int TestwineventObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int TestfindwindowObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int TestgetwindowinfoObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int TestwinlocaleObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static Tk_GetSelProc SetSelectionResult; + +/* + *---------------------------------------------------------------------- + * + * TkplatformtestInit -- + * + * Defines commands that test platform specific functionality for Windows + * platforms. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * Defines new commands. + * + *---------------------------------------------------------------------- + */ + +int +TkplatformtestInit( + Tcl_Interp *interp) /* Interpreter to add commands to. */ +{ + /* + * Add commands for platform specific tests on MacOS here. + */ + + Tcl_CreateObjCommand(interp, "testclipboard", TestclipboardObjCmd, + (ClientData) Tk_MainWindow(interp), NULL); + Tcl_CreateObjCommand(interp, "testwinevent", TestwineventObjCmd, + (ClientData) Tk_MainWindow(interp), NULL); + Tcl_CreateObjCommand(interp, "testfindwindow", TestfindwindowObjCmd, + (ClientData) Tk_MainWindow(interp), NULL); + Tcl_CreateObjCommand(interp, "testgetwindowinfo", TestgetwindowinfoObjCmd, + (ClientData) Tk_MainWindow(interp), NULL); + Tcl_CreateObjCommand(interp, "testwinlocale", TestwinlocaleObjCmd, + (ClientData) Tk_MainWindow(interp), NULL); + return TCL_OK; +} + +struct TestFindControlState { + int id; + HWND control; +}; + +/* Callback for window enumeration - used for TestFindControl */ +BOOL CALLBACK TestFindControlCallback( + HWND hwnd, + LPARAM lParam +) +{ + struct TestFindControlState *fcsPtr = (struct TestFindControlState *)lParam; + fcsPtr->control = GetDlgItem(hwnd, fcsPtr->id); + /* If we have found the control, return FALSE to stop the enumeration */ + return fcsPtr->control == NULL ? TRUE : FALSE; +} + +/* + * Finds the descendent control window with the specified ID and returns + * its HWND. + */ +HWND TestFindControl(HWND root, int id) +{ + struct TestFindControlState fcs; + + fcs.control = GetDlgItem(root, id); + if (fcs.control == NULL) { + /* Control is not a direct child. Look in descendents */ + fcs.id = id; + fcs.control = NULL; + EnumChildWindows(root, TestFindControlCallback, (LPARAM) &fcs); + } + return fcs.control; +} + + +/* + *---------------------------------------------------------------------- + * + * AppendSystemError -- + * + * This routine formats a Windows system error message and places it into + * the interpreter result. Originally from tclWinReg.c. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +AppendSystemError( + Tcl_Interp *interp, /* Current interpreter. */ + DWORD error) /* Result code from error. */ +{ + int length; + WCHAR *wMsgPtr, **wMsgPtrPtr = &wMsgPtr; + const char *msg; + char id[TCL_INTEGER_SPACE], msgBuf[24 + TCL_INTEGER_SPACE]; + Tcl_DString ds; + Tcl_Obj *resultPtr = Tcl_GetObjResult(interp); + + if (Tcl_IsShared(resultPtr)) { + resultPtr = Tcl_DuplicateObj(resultPtr); + } + length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS + | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (WCHAR *) wMsgPtrPtr, + 0, NULL); + if (length == 0) { + char *msgPtr; + + length = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_IGNORE_INSERTS + | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char *) &msgPtr, + 0, NULL); + if (length > 0) { + wMsgPtr = (WCHAR *) LocalAlloc(LPTR, (length + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, msgPtr, length + 1, wMsgPtr, + length + 1); + LocalFree(msgPtr); + } + } + if (length == 0) { + if (error == ERROR_CALL_NOT_IMPLEMENTED) { + strcpy(msgBuf, "function not supported under Win32s"); + } else { + sprintf(msgBuf, "unknown error: %ld", error); + } + msg = msgBuf; + } else { + Tcl_Encoding encoding; + char *msgPtr; + + encoding = Tcl_GetEncoding(NULL, "unicode"); + Tcl_ExternalToUtfDString(encoding, (char *) wMsgPtr, -1, &ds); + Tcl_FreeEncoding(encoding); + LocalFree(wMsgPtr); + + msgPtr = Tcl_DStringValue(&ds); + length = Tcl_DStringLength(&ds); + + /* + * Trim the trailing CR/LF from the system message. + */ + + if (msgPtr[length-1] == '\n') { + --length; + } + if (msgPtr[length-1] == '\r') { + --length; + } + msgPtr[length] = 0; + msg = msgPtr; + } + + sprintf(id, "%ld", error); + Tcl_SetErrorCode(interp, "WINDOWS", id, msg, NULL); + Tcl_AppendToObj(resultPtr, msg, length); + Tcl_SetObjResult(interp, resultPtr); + + if (length != 0) { + Tcl_DStringFree(&ds); + } +} + +/* + *---------------------------------------------------------------------- + * + * TestclipboardObjCmd -- + * + * This function implements the testclipboard command. It provides a way + * to determine the actual contents of the Windows clipboard. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +SetSelectionResult( + ClientData dummy, + Tcl_Interp *interp, + const char *selection) +{ + Tcl_AppendResult(interp, selection, NULL); + return TCL_OK; +} + +static int +TestclipboardObjCmd( + ClientData clientData, /* Main window for application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument values. */ +{ + Tk_Window tkwin = (Tk_Window) clientData; + + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, NULL); + return TCL_ERROR; + } + return TkSelGetSelection(interp, tkwin, Tk_InternAtom(tkwin, "CLIPBOARD"), + XA_STRING, SetSelectionResult, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * TestwineventObjCmd -- + * + * This function implements the testwinevent command. It provides a way + * to send messages to windows dialogs. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +TestwineventObjCmd( + ClientData clientData, /* Main window for application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument strings. */ +{ + HWND hwnd = 0; + HWND child = 0; + HWND control; + int id; + char *rest; + UINT message; + WPARAM wParam; + LPARAM lParam; + LRESULT result; + static const TkStateMap messageMap[] = { + {WM_LBUTTONDOWN, "WM_LBUTTONDOWN"}, + {WM_LBUTTONUP, "WM_LBUTTONUP"}, + {WM_CHAR, "WM_CHAR"}, + {WM_GETTEXT, "WM_GETTEXT"}, + {WM_SETTEXT, "WM_SETTEXT"}, + {WM_COMMAND, "WM_COMMAND"}, + {-1, NULL} + }; + + if ((objc == 3) && (strcmp(Tcl_GetString(objv[1]), "debug") == 0)) { + int b; + + if (Tcl_GetBoolean(interp, Tcl_GetString(objv[2]), &b) != TCL_OK) { + return TCL_ERROR; + } + TkWinDialogDebug(b); + return TCL_OK; + } + + if (objc < 4) { + return TCL_ERROR; + } + + hwnd = INT2PTR(strtol(Tcl_GetString(objv[1]), &rest, 0)); + if (rest == Tcl_GetString(objv[1])) { + hwnd = FindWindowA(NULL, Tcl_GetString(objv[1])); + if (hwnd == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("no such window", -1)); + return TCL_ERROR; + } + } + UpdateWindow(hwnd); + + id = strtol(Tcl_GetString(objv[2]), &rest, 0); + if (rest == Tcl_GetString(objv[2])) { + char buf[256]; + + child = GetWindow(hwnd, GW_CHILD); + while (child != NULL) { + SendMessageA(child, WM_GETTEXT, (WPARAM) sizeof(buf), (LPARAM) buf); + if (strcasecmp(buf, Tcl_GetString(objv[2])) == 0) { + id = GetDlgCtrlID(child); + break; + } + child = GetWindow(child, GW_HWNDNEXT); + } + if (child == NULL) { + Tcl_AppendResult(interp, "could not find a control matching \"", + Tcl_GetString(objv[2]), "\"", NULL); + return TCL_ERROR; + } + } + + message = TkFindStateNum(NULL, NULL, messageMap, Tcl_GetString(objv[3])); + wParam = 0; + lParam = 0; + + if (objc > 4) { + wParam = strtol(Tcl_GetString(objv[4]), NULL, 0); + } + if (objc > 5) { + lParam = strtol(Tcl_GetString(objv[5]), NULL, 0); + } + + switch (message) { + case WM_GETTEXT: { + Tcl_DString ds; + char buf[256]; + +#if 0 + GetDlgItemTextA(hwnd, id, buf, 256); +#else + control = TestFindControl(hwnd, id); + if (control == NULL) { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Could not find control with id %d", id)); + return TCL_ERROR; + } + buf[0] = 0; + SendMessageA(control, WM_GETTEXT, (WPARAM)sizeof(buf), + (LPARAM) buf); +#endif + Tcl_ExternalToUtfDString(NULL, buf, -1, &ds); + Tcl_AppendResult(interp, Tcl_DStringValue(&ds), NULL); + Tcl_DStringFree(&ds); + break; + } + case WM_SETTEXT: { + Tcl_DString ds; + + control = TestFindControl(hwnd, id); + if (control == NULL) { + Tcl_SetObjResult(interp, + Tcl_ObjPrintf("Could not find control with id %d", id)); + return TCL_ERROR; + } + Tcl_UtfToExternalDString(NULL, Tcl_GetString(objv[4]), -1, &ds); + result = SendMessageA(control, WM_SETTEXT, 0, + (LPARAM) Tcl_DStringValue(&ds)); + Tcl_DStringFree(&ds); + if (result == 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to send text to dialog: ", -1)); + AppendSystemError(interp, GetLastError()); + return TCL_ERROR; + } + break; + } + case WM_COMMAND: { + char buf[TCL_INTEGER_SPACE]; + if (objc < 5) { + wParam = MAKEWPARAM(id, 0); + lParam = (LPARAM)child; + } + sprintf(buf, "%d", (int) SendMessageA(hwnd, message, wParam, lParam)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); + break; + } + default: { + char buf[TCL_INTEGER_SPACE]; + + sprintf(buf, "%d", + (int) SendDlgItemMessageA(hwnd, id, message, wParam, lParam)); + Tcl_SetObjResult(interp, Tcl_NewStringObj(buf, -1)); + break; + } + } + return TCL_OK; +} + +/* + * testfindwindow title ?class? + * Find a Windows window using the FindWindow API call. This takes the window + * title and optionally the window class and if found returns the HWND and + * raises an error if the window is not found. + * eg: testfindwindow Console TkTopLevel + * Can find the console window if it is visible. + * eg: testfindwindow "TkTest #10201" "#32770" + * Can find a messagebox window with this title. + */ + +static int +TestfindwindowObjCmd( + ClientData clientData, /* Main window for application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument values. */ +{ + const TCHAR *title = NULL, *class = NULL; + Tcl_DString titleString, classString; + HWND hwnd = NULL; + int r = TCL_OK; + DWORD myPid; + + Tcl_DStringInit(&classString); + Tcl_DStringInit(&titleString); + + if (objc < 2 || objc > 3) { + Tcl_WrongNumArgs(interp, 1, objv, "title ?class?"); + return TCL_ERROR; + } + + title = Tcl_WinUtfToTChar(Tcl_GetString(objv[1]), -1, &titleString); + if (objc == 3) { + class = Tcl_WinUtfToTChar(Tcl_GetString(objv[2]), -1, &classString); + } + if (title[0] == 0) + title = NULL; +#if 0 + hwnd = FindWindow(class, title); +#else + /* We want find a window the belongs to us and not some other process */ + hwnd = NULL; + myPid = GetCurrentProcessId(); + while (1) { + DWORD pid, tid; + hwnd = FindWindowEx(NULL, hwnd, class, title); + if (hwnd == NULL) + break; + tid = GetWindowThreadProcessId(hwnd, &pid); + if (tid == 0) { + /* Window has gone */ + hwnd = NULL; + break; + } + if (pid == myPid) + break; /* Found it */ + } + +#endif + + if (hwnd == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to find window: ", -1)); + AppendSystemError(interp, GetLastError()); + r = TCL_ERROR; + } else { + Tcl_SetObjResult(interp, Tcl_NewLongObj(PTR2INT(hwnd))); + } + + Tcl_DStringFree(&titleString); + Tcl_DStringFree(&classString); + return r; + +} + +static BOOL CALLBACK +EnumChildrenProc( + HWND hwnd, + LPARAM lParam) +{ + Tcl_Obj *listObj = (Tcl_Obj *) lParam; + + Tcl_ListObjAppendElement(NULL, listObj, Tcl_NewLongObj(PTR2INT(hwnd))); + return TRUE; +} + +static int +TestgetwindowinfoObjCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + long hwnd; + Tcl_Obj *dictObj = NULL, *classObj = NULL, *textObj = NULL; + Tcl_Obj *childrenObj = NULL; + TCHAR buf[512]; + int cch, cchBuf = 256; + Tcl_DString ds; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "hwnd"); + return TCL_ERROR; + } + + if (Tcl_GetLongFromObj(interp, objv[1], &hwnd) != TCL_OK) + return TCL_ERROR; + + cch = GetClassName(INT2PTR(hwnd), buf, cchBuf); + if (cch == 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("failed to get class name: ", -1)); + AppendSystemError(interp, GetLastError()); + return TCL_ERROR; + } else { + Tcl_DString ds; + Tcl_WinTCharToUtf(buf, -1, &ds); + classObj = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + } + + dictObj = Tcl_NewDictObj(); + Tcl_DictObjPut(interp, dictObj, Tcl_NewStringObj("class", 5), classObj); + Tcl_DictObjPut(interp, dictObj, Tcl_NewStringObj("id", 2), + Tcl_NewLongObj(GetWindowLongA(INT2PTR(hwnd), GWL_ID))); + + cch = GetWindowText(INT2PTR(hwnd), (LPTSTR)buf, cchBuf); + Tcl_WinTCharToUtf(buf, cch * sizeof (WCHAR), &ds); + textObj = Tcl_NewStringObj(Tcl_DStringValue(&ds), Tcl_DStringLength(&ds)); + Tcl_DStringFree(&ds); + + Tcl_DictObjPut(interp, dictObj, Tcl_NewStringObj("text", 4), textObj); + Tcl_DictObjPut(interp, dictObj, Tcl_NewStringObj("parent", 6), + Tcl_NewLongObj(PTR2INT(GetParent((INT2PTR(hwnd)))))); + + childrenObj = Tcl_NewListObj(0, NULL); + EnumChildWindows(INT2PTR(hwnd), EnumChildrenProc, (LPARAM)childrenObj); + Tcl_DictObjPut(interp, dictObj, Tcl_NewStringObj("children", -1), childrenObj); + + Tcl_SetObjResult(interp, dictObj); + return TCL_OK; +} + +static int +TestwinlocaleObjCmd( + ClientData clientData, /* Main window for application. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument values. */ +{ + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, NULL); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewIntObj((int)GetThreadLocale())); + return TCL_OK; +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinWindow.c b/tk8.6/win/tkWinWindow.c new file mode 100644 index 0000000..385e72b --- /dev/null +++ b/tk8.6/win/tkWinWindow.c @@ -0,0 +1,1017 @@ +/* + * tkWinWindow.c -- + * + * Xlib emulation routines for Windows related to creating, displaying + * and destroying windows. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include "tkBusy.h" + +typedef struct ThreadSpecificData { + int initialized; /* 0 means table below needs initializing. */ + Tcl_HashTable windowTable; /* The windowTable maps from HWND to Tk_Window + * handles. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Forward declarations for functions defined in this file: + */ + +static void NotifyVisibility(XEvent *eventPtr, TkWindow *winPtr); + +/* + *---------------------------------------------------------------------- + * + * Tk_AttachHWND -- + * + * This function binds an HWND and a reflection function to the specified + * Tk_Window. + * + * Results: + * Returns an X Window that encapsulates the HWND. + * + * Side effects: + * May allocate a new X Window. Also enters the HWND into the global + * window table. + * + *---------------------------------------------------------------------- + */ + +Window +Tk_AttachHWND( + Tk_Window tkwin, + HWND hwnd) +{ + int new; + Tcl_HashEntry *entryPtr; + TkWinDrawable *twdPtr = (TkWinDrawable *) Tk_WindowId(tkwin); + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS); + tsdPtr->initialized = 1; + } + + /* + * Allocate a new drawable if necessary. Otherwise, remove the previous + * HWND from from the window table. + */ + + if (twdPtr == NULL) { + twdPtr = ckalloc(sizeof(TkWinDrawable)); + twdPtr->type = TWD_WINDOW; + twdPtr->window.winPtr = (TkWindow *) tkwin; + } else if (twdPtr->window.handle != NULL) { + entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, + (char *)twdPtr->window.handle); + Tcl_DeleteHashEntry(entryPtr); + } + + /* + * Insert the new HWND into the window table. + */ + + twdPtr->window.handle = hwnd; + entryPtr = Tcl_CreateHashEntry(&tsdPtr->windowTable, (char *)hwnd, &new); + Tcl_SetHashValue(entryPtr, tkwin); + + return (Window)twdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_HWNDToWindow -- + * + * This function retrieves a Tk_Window from the window table given an + * HWND. + * + * Results: + * Returns the matching Tk_Window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tk_Window +Tk_HWNDToWindow( + HWND hwnd) +{ + Tcl_HashEntry *entryPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + Tcl_InitHashTable(&tsdPtr->windowTable, TCL_ONE_WORD_KEYS); + tsdPtr->initialized = 1; + } + entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char *) hwnd); + if (entryPtr != NULL) { + return (Tk_Window) Tcl_GetHashValue(entryPtr); + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetHWND -- + * + * This function extracts the HWND from an X Window. + * + * Results: + * Returns the HWND associated with the Window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HWND +Tk_GetHWND( + Window window) +{ + return ((TkWinDrawable *) window)->window.handle; +} + +/* + *---------------------------------------------------------------------- + * + * TkpPrintWindowId -- + * + * This routine stores the string representation of the platform + * dependent window handle for an X Window in the given buffer. + * + * Results: + * Returns the result in the specified buffer. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpPrintWindowId( + char *buf, /* Pointer to string large enough to hold the + * hex representation of a pointer. */ + Window window) /* Window to be printed into buffer. */ +{ + HWND hwnd = (window) ? Tk_GetHWND(window) : 0; + + /* + * Use pointer representation, because Win64 is P64 (*not* LP64). Windows + * doesn't print the 0x for %p, so we do it. + * Bug 2026405: cygwin does output 0x for %p so test and recover. + */ + + sprintf(buf, "0x%p", hwnd); + if (buf[2] == '0' && buf[3] == 'x') { + sprintf(buf, "%p", hwnd); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpScanWindowId -- + * + * Given a string which represents the platform dependent window handle, + * produce the X Window id for the window. + * + * Results: + * The return value is normally TCL_OK; in this case *idPtr will be set + * to the X Window id equivalent to string. If string is improperly + * formed then TCL_ERROR is returned and an error message will be left in + * the interp's result. If the number does not correspond to a Tk Window, + * then *idPtr will be set to None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkpScanWindowId( + Tcl_Interp *interp, /* Interpreter to use for error reporting. */ + const char *string, /* String containing a (possibly signed) + * integer in a form acceptable to strtol. */ + Window *idPtr) /* Place to store converted result. */ +{ + Tk_Window tkwin; + union { + HWND hwnd; + int number; + } win; + + /* + * We want sscanf for the 64-bit check, but if that doesn't work, then + * Tcl_GetInt manages the error correctly. + */ + + if ( +#ifdef _WIN64 + (sscanf(string, "0x%p", &win.hwnd) != 1) && +#endif + Tcl_GetInt(interp, string, &win.number) != TCL_OK) { + return TCL_ERROR; + } + + tkwin = Tk_HWNDToWindow(win.hwnd); + if (tkwin) { + *idPtr = Tk_WindowId(tkwin); + } else { + *idPtr = None; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkpMakeWindow -- + * + * Creates a Windows window object based on the current attributes of the + * specified TkWindow. + * + * Results: + * Returns a pointer to a new TkWinDrawable cast to a Window. + * + * Side effects: + * Creates a new window. + * + *---------------------------------------------------------------------- + */ + +Window +TkpMakeWindow( + TkWindow *winPtr, + Window parent) +{ + HWND parentWin; + int style; + HWND hwnd; + + if (parent != None) { + parentWin = Tk_GetHWND(parent); + style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + } else { + parentWin = NULL; + style = WS_POPUP | WS_CLIPCHILDREN; + } + + /* + * Create the window, then ensure that it is at the top of the stacking + * order. + */ + + hwnd = CreateWindowEx(WS_EX_NOPARENTNOTIFY, TK_WIN_CHILD_CLASS_NAME, NULL, + (DWORD) style, Tk_X(winPtr), Tk_Y(winPtr), Tk_Width(winPtr), + Tk_Height(winPtr), parentWin, NULL, Tk_GetHINSTANCE(), NULL); + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + return Tk_AttachHWND((Tk_Window)winPtr, hwnd); +} + +/* + *---------------------------------------------------------------------- + * + * XDestroyWindow -- + * + * Destroys the given window. + * + * Results: + * None. + * + * Side effects: + * Sends the WM_DESTROY message to the window and then destroys it the + * Win32 resources associated with the window. + * + *---------------------------------------------------------------------- + */ + +int +XDestroyWindow( + Display *display, + Window w) +{ + Tcl_HashEntry *entryPtr; + TkWinDrawable *twdPtr = (TkWinDrawable *)w; + TkWindow *winPtr = TkWinGetWinPtr(w); + HWND hwnd = Tk_GetHWND(w); + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + display->request++; + + /* + * Remove references to the window in the pointer module then release the + * drawable. + */ + + TkPointerDeadWindow(winPtr); + + entryPtr = Tcl_FindHashEntry(&tsdPtr->windowTable, (char*)hwnd); + if (entryPtr != NULL) { + Tcl_DeleteHashEntry(entryPtr); + } + + ckfree(twdPtr); + + /* + * Don't bother destroying the window if we are going to destroy the + * parent later. + */ + + if (hwnd != NULL && !(winPtr->flags & TK_DONT_DESTROY_WINDOW)) { + DestroyWindow(hwnd); + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XMapWindow -- + * + * Cause the given window to become visible. + * + * Results: + * None + * + * Side effects: + * Causes the window state to change, and generates a MapNotify event. + * + *---------------------------------------------------------------------- + */ + +int +XMapWindow( + Display *display, + Window w) +{ + XEvent event; + TkWindow *parentPtr; + TkWindow *winPtr = TkWinGetWinPtr(w); + + display->request++; + + ShowWindow(Tk_GetHWND(w), SW_SHOWNORMAL); + winPtr->flags |= TK_MAPPED; + + /* + * Check to see if this window is visible now. If all of the parent + * windows up to the first toplevel are mapped, then this window and its + * mapped children have just become visible. + */ + + if (!(winPtr->flags & TK_TOP_HIERARCHY)) { + for (parentPtr = winPtr->parentPtr; ; + parentPtr = parentPtr->parentPtr) { + if ((parentPtr == NULL) || !(parentPtr->flags & TK_MAPPED)) { + return Success; + } + if (parentPtr->flags & TK_TOP_HIERARCHY) { + break; + } + } + } else { + event.type = MapNotify; + event.xmap.serial = display->request; + event.xmap.send_event = False; + event.xmap.display = display; + event.xmap.event = winPtr->window; + event.xmap.window = winPtr->window; + event.xmap.override_redirect = winPtr->atts.override_redirect; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + } + + /* + * Generate VisibilityNotify events for this window and its mapped + * children. + */ + + event.type = VisibilityNotify; + event.xvisibility.serial = display->request; + event.xvisibility.send_event = False; + event.xvisibility.display = display; + event.xvisibility.window = winPtr->window; + event.xvisibility.state = VisibilityUnobscured; + NotifyVisibility(&event, winPtr); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * NotifyVisibility -- + * + * This function recursively notifies the mapped children of the + * specified window of a change in visibility. Note that we don't + * properly report the visibility state, since Windows does not provide + * that info. The eventPtr argument must point to an event that has been + * completely initialized except for the window slot. + * + * Results: + * None. + * + * Side effects: + * Generates lots of events. + * + *---------------------------------------------------------------------- + */ + +static void +NotifyVisibility( + XEvent *eventPtr, /* Initialized VisibilityNotify event. */ + TkWindow *winPtr) /* Window to notify. */ +{ + if (winPtr->atts.event_mask & VisibilityChangeMask) { + eventPtr->xvisibility.window = winPtr->window; + Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL); + } + for (winPtr = winPtr->childList; winPtr != NULL; + winPtr = winPtr->nextPtr) { + if (winPtr->flags & TK_MAPPED) { + NotifyVisibility(eventPtr, winPtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * XUnmapWindow -- + * + * Cause the given window to become invisible. + * + * Results: + * None + * + * Side effects: + * Causes the window state to change, and generates an UnmapNotify event. + * + *---------------------------------------------------------------------- + */ + +int +XUnmapWindow( + Display *display, + Window w) +{ + XEvent event; + TkWindow *winPtr = TkWinGetWinPtr(w); + + display->request++; + + /* + * Bug fix: Don't short circuit this routine based on TK_MAPPED because it + * will be cleared before XUnmapWindow is called. + */ + + ShowWindow(Tk_GetHWND(w), SW_HIDE); + winPtr->flags &= ~TK_MAPPED; + + if (winPtr->flags & TK_WIN_MANAGED) { + event.type = UnmapNotify; + event.xunmap.serial = display->request; + event.xunmap.send_event = False; + event.xunmap.display = display; + event.xunmap.event = winPtr->window; + event.xunmap.window = winPtr->window; + event.xunmap.from_configure = False; + Tk_HandleEvent(&event); + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XMoveResizeWindow -- + * + * Move and resize a window relative to its parent. + * + * Results: + * None. + * + * Side effects: + * Repositions and resizes the specified window. + * + *---------------------------------------------------------------------- + */ + +int +XMoveResizeWindow( + Display *display, + Window w, + int x, int y, /* Position relative to parent. */ + unsigned int width, unsigned int height) +{ + display->request++; + MoveWindow(Tk_GetHWND(w), x, y, (int) width, (int) height, TRUE); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XMoveWindow -- + * + * Move a window relative to its parent. + * + * Results: + * None. + * + * Side effects: + * Repositions the specified window. + * + *---------------------------------------------------------------------- + */ + +int +XMoveWindow( + Display *display, + Window w, + int x, int y) /* Position relative to parent */ +{ + TkWindow *winPtr = TkWinGetWinPtr(w); + + display->request++; + + MoveWindow(Tk_GetHWND(w), x, y, winPtr->changes.width, + winPtr->changes.height, TRUE); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XResizeWindow -- + * + * Resize a window. + * + * Results: + * None. + * + * Side effects: + * Resizes the specified window. + * + *---------------------------------------------------------------------- + */ + +int +XResizeWindow( + Display *display, + Window w, + unsigned int width, unsigned int height) +{ + TkWindow *winPtr = TkWinGetWinPtr(w); + + display->request++; + + MoveWindow(Tk_GetHWND(w), winPtr->changes.x, winPtr->changes.y, (int)width, + (int)height, TRUE); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XRaiseWindow, XLowerWindow -- + * + * Change the stacking order of a window. + * + * Results: + * None. + * + * Side effects: + * Changes the stacking order of the specified window. + * + *---------------------------------------------------------------------- + */ + +int +XRaiseWindow( + Display *display, + Window w) +{ + HWND window = Tk_GetHWND(w); + + display->request++; + SetWindowPos(window, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + return Success; +} + +int +XLowerWindow( + Display *display, + Window w) +{ + HWND window = Tk_GetHWND(w); + + display->request++; + SetWindowPos(window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XConfigureWindow -- + * + * Change the size, position, stacking, or border of the specified + * window. + * + * Results: + * None. + * + * Side effects: + * Changes the attributes of the specified window. Note that we ignore + * the passed in values and use the values stored in the TkWindow data + * structure. + * + *---------------------------------------------------------------------- + */ + +int +XConfigureWindow( + Display *display, + Window w, + unsigned int valueMask, + XWindowChanges *values) +{ + TkWindow *winPtr = TkWinGetWinPtr(w); + HWND hwnd = Tk_GetHWND(w); + + display->request++; + + /* + * Change the shape and/or position of the window. + */ + + if (valueMask & (CWX|CWY|CWWidth|CWHeight)) { + MoveWindow(hwnd, winPtr->changes.x, winPtr->changes.y, + winPtr->changes.width, winPtr->changes.height, TRUE); + } + + /* + * Change the stacking order of the window. + */ + + if (valueMask & CWStackMode) { + HWND sibling; + + if ((valueMask & CWSibling) && (values->sibling != None)) { + sibling = Tk_GetHWND(values->sibling); + } else { + sibling = NULL; + } + TkWinSetWindowPos(hwnd, sibling, values->stack_mode); + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XClearWindow -- + * + * Clears the entire window to the current background color. + * + * Results: + * None. + * + * Side effects: + * Erases the current contents of the window. + * + *---------------------------------------------------------------------- + */ + +int +XClearWindow( + Display *display, + Window w) +{ + RECT rc; + HBRUSH brush; + HPALETTE oldPalette, palette; + TkWindow *winPtr; + HWND hwnd = Tk_GetHWND(w); + HDC dc = GetDC(hwnd); + + palette = TkWinGetPalette(display->screens[0].cmap); + oldPalette = SelectPalette(dc, palette, FALSE); + + display->request++; + + winPtr = TkWinGetWinPtr(w); + brush = CreateSolidBrush(winPtr->atts.background_pixel); + GetWindowRect(hwnd, &rc); + rc.right = rc.right - rc.left; + rc.bottom = rc.bottom - rc.top; + rc.left = rc.top = 0; + FillRect(dc, &rc, brush); + + DeleteObject(brush); + SelectPalette(dc, oldPalette, TRUE); + ReleaseDC(hwnd, dc); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XChangeWindowAttributes -- + * + * This function is called when the attributes on a window are updated. + * Since Tk maintains all of the window state, the only relevant value is + * the cursor. + * + * Results: + * None. + * + * Side effects: + * May cause the mouse position to be updated. + * + *---------------------------------------------------------------------- + */ + +int +XChangeWindowAttributes( + Display *display, + Window w, + unsigned long valueMask, + XSetWindowAttributes* attributes) +{ + if (valueMask & CWCursor) { + XDefineCursor(display, w, attributes->cursor); + } + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * XReparentWindow -- + * + * TODO: currently placeholder to satisfy Xlib stubs. + * + * Results: + * None. + * + * Side effects: + * TODO. + * + *---------------------------------------------------------------------- + */ + +int +XReparentWindow( + Display *display, + Window w, + Window parent, + int x, + int y) +{ + return BadWindow; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinSetWindowPos -- + * + * Adjust the stacking order of a window relative to a second window (or + * NULL). + * + * Results: + * None. + * + * Side effects: + * Moves the specified window in the stacking order. + * + *---------------------------------------------------------------------- + */ + +void +TkWinSetWindowPos( + HWND hwnd, /* Window to restack. */ + HWND siblingHwnd, /* Sibling window. */ + int pos) /* One of Above or Below. */ +{ + HWND temp; + + /* + * Since Windows does not support Above mode, we place the specified + * window below the sibling and then swap them. + */ + + if (siblingHwnd) { + if (pos == Above) { + SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); + temp = hwnd; + hwnd = siblingHwnd; + siblingHwnd = temp; + } + } else { + siblingHwnd = (pos == Above) ? HWND_TOP : HWND_BOTTOM; + } + + SetWindowPos(hwnd, siblingHwnd, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); +} + +/* + *---------------------------------------------------------------------- + * + * TkpShowBusyWindow -- + * + * Makes a busy window "appear". + * + * Results: + * None. + * + * Side effects: + * Arranges for the busy window to start intercepting events and the + * cursor to change to the configured "hey, I'm busy!" setting. + * + *---------------------------------------------------------------------- + */ + +void +TkpShowBusyWindow( + TkBusy busy) +{ + Busy *busyPtr = (Busy *) busy; + HWND hWnd; + POINT point; + Display *display; + Window window; + + if (busyPtr->tkBusy != NULL) { + Tk_MapWindow(busyPtr->tkBusy); + window = Tk_WindowId(busyPtr->tkBusy); + display = Tk_Display(busyPtr->tkBusy); + hWnd = Tk_GetHWND(window); + display->request++; + SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + + /* + * Under Win32, cursors aren't associated with windows. Tk fakes this by + * watching Motion events on its windows. So Tk will automatically change + * the cursor when the pointer enters the Busy window. But Windows does + * not immediately change the cursor; it waits for the cursor position to + * change or a system call. We need to change the cursor before the + * application starts processing, so set the cursor position redundantly + * back to the current position. + */ + + GetCursorPos(&point); + SetCursorPos(point.x, point.y); +} + +/* + *---------------------------------------------------------------------- + * + * TkpHideBusyWindow -- + * + * Makes a busy window "disappear". + * + * Results: + * None. + * + * Side effects: + * Arranges for the busy window to stop intercepting events, and the + * cursor to change back to its normal setting. + * + *---------------------------------------------------------------------- + */ + +void +TkpHideBusyWindow( + TkBusy busy) +{ + Busy *busyPtr = (Busy *) busy; + POINT point; + + if (busyPtr->tkBusy != NULL) { + Tk_UnmapWindow(busyPtr->tkBusy); + } + + /* + * Under Win32, cursors aren't associated with windows. Tk fakes this by + * watching Motion events on its windows. So Tk will automatically change + * the cursor when the pointer enters the Busy window. But Windows does + * not immediately change the cursor: it waits for the cursor position to + * change or a system call. We need to change the cursor before the + * application starts processing, so set the cursor position redundantly + * back to the current position. + */ + + GetCursorPos(&point); + SetCursorPos(point.x, point.y); +} + +/* + *---------------------------------------------------------------------- + * + * TkpMakeTransparentWindowExist -- + * + * Construct the platform-specific resources for a transparent window. + * + * Results: + * None. + * + * Side effects: + * Moves the specified window in the stacking order. + * + *---------------------------------------------------------------------- + */ + +void +TkpMakeTransparentWindowExist( + Tk_Window tkwin, /* Token for window. */ + Window parent) /* Parent window. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + HWND hParent = (HWND) parent, hWnd; + int style = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + DWORD exStyle = WS_EX_TRANSPARENT | WS_EX_TOPMOST; + + hWnd = CreateWindowEx(exStyle, TK_WIN_CHILD_CLASS_NAME, NULL, style, + Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin), + hParent, NULL, Tk_GetHINSTANCE(), NULL); + winPtr->window = Tk_AttachHWND(tkwin, hWnd); +} + +/* + *---------------------------------------------------------------------- + * + * TkpCreateBusy -- + * + * Construct the platform-specific parts of a busy window. Note that this + * postpones the actual creation of the window resource until later. + * + * Results: + * None. + * + * Side effects: + * Sets up part of the busy window structure. + * + *---------------------------------------------------------------------- + */ + +void +TkpCreateBusy( + Tk_FakeWin *winPtr, + Tk_Window tkRef, + Window *parentPtr, + Tk_Window tkParent, + TkBusy busy) +{ + Busy *busyPtr = (Busy *) busy; + + if (winPtr->flags & TK_REPARENTED) { + /* + * This works around a bug in the implementation of menubars for + * non-Macintosh window systems (Win32 and X11). Tk doesn't reset the + * pointers to the parent window when the menu is reparented + * (winPtr->parentPtr points to the wrong window). We get around this + * by determining the parent via the native API calls. + */ + + HWND hWnd = GetParent(Tk_GetHWND(Tk_WindowId(tkRef))); + RECT rect; + + if (GetWindowRect(hWnd, &rect)) { + busyPtr->width = rect.right - rect.left; + busyPtr->height = rect.bottom - rect.top; + } + } else { + *parentPtr = Tk_WindowId(tkParent); + *parentPtr = (Window) Tk_GetHWND(*parentPtr); + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinWm.c b/tk8.6/win/tkWinWm.c new file mode 100644 index 0000000..4e7618d --- /dev/null +++ b/tk8.6/win/tkWinWm.c @@ -0,0 +1,8688 @@ +/* + * tkWinWm.c -- + * + * This module takes care of the interactions between a Tk-based + * application and the window manager. Among other things, it implements + * the "wm" command and passes geometry information to the window + * manager. + * + * Copyright (c) 1995-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-2000 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" +#include <shellapi.h> + +/* + * These next two defines are only valid on Win2K/XP+. + */ + +#ifndef WS_EX_LAYERED +#define WS_EX_LAYERED 0x00080000 +#endif +#ifndef LWA_COLORKEY +#define LWA_COLORKEY 0x00000001 +#endif +#ifndef LWA_ALPHA +#define LWA_ALPHA 0x00000002 +#endif + +/* + * Event structure for synthetic activation events. These events are placed on + * the event queue whenever a toplevel gets a WM_MOUSEACTIVATE message or + * a WM_ACTIVATE. If the window is being moved (*flagPtr will be true) + * then the handling of this event must be delayed until the operation + * has completed to avoid a premature WM_EXITSIZEMOVE event. + */ + +typedef struct ActivateEvent { + Tcl_Event ev; + TkWindow *winPtr; + const int *flagPtr; + HWND hwnd; +} ActivateEvent; + +/* + * A data structure of the following type holds information for each window + * manager protocol (such as WM_DELETE_WINDOW) for which a handler (i.e. a Tcl + * command) has been defined for a particular top-level window. + */ + +typedef struct ProtocolHandler { + Atom protocol; /* Identifies the protocol. */ + struct ProtocolHandler *nextPtr; + /* Next in list of protocol handlers for the + * same top-level window, or NULL for end of + * list. */ + Tcl_Interp *interp; /* Interpreter in which to invoke command. */ + char command[1]; /* Tcl command to invoke when a client message + * for this protocol arrives. The actual size + * of the structure varies to accommodate the + * needs of the actual command. THIS MUST BE + * THE LAST FIELD OF THE STRUCTURE. */ +} ProtocolHandler; + +#define HANDLER_SIZE(cmdLength) \ + ((unsigned) ((Tk_Offset(ProtocolHandler, command) + 1) + cmdLength)) + +/* + * Helper type passed via lParam to TkWmStackorderToplevelEnumProc + */ + +typedef struct TkWmStackorderToplevelPair { + Tcl_HashTable *table; + TkWindow **windowPtr; +} TkWmStackorderToplevelPair; + +/* + * This structure represents the contents of a icon, in terms of its image. + * The HICON is an internal Windows format. Most of these icon-specific + * structures originated with the Winico extension. We stripped out unused + * parts of that code, and integrated the code more naturally with Tcl. + */ + +typedef struct { + UINT Width, Height, Colors; /* Width, Height and bpp */ + LPBYTE lpBits; /* Ptr to DIB bits */ + DWORD dwNumBytes; /* How many bytes? */ + LPBITMAPINFO lpbi; /* Ptr to header */ + LPBYTE lpXOR; /* Ptr to XOR image bits */ + LPBYTE lpAND; /* Ptr to AND image bits */ + HICON hIcon; /* DAS ICON */ +} ICONIMAGE, *LPICONIMAGE; + +/* + * This structure is how we represent a block of the above items. We will + * reallocate these structures according to how many images they need to + * contain. + */ + +typedef struct { + int nNumImages; /* How many images? */ + ICONIMAGE IconImages[1]; /* Image entries */ +} BlockOfIconImages, *BlockOfIconImagesPtr; + +/* + * These two structures are used to read in icons from an 'icon directory' + * (i.e. the contents of a .icr file, say). We only use these structures + * temporarily, since we copy the information we want into a + * BlockOfIconImages. + */ + +typedef struct { + BYTE bWidth; /* Width of the image */ + BYTE bHeight; /* Height of the image (times 2) */ + BYTE bColorCount; /* Number of colors in image (0 if >=8bpp) */ + BYTE bReserved; /* Reserved */ + WORD wPlanes; /* Color Planes */ + WORD wBitCount; /* Bits per pixel */ + DWORD dwBytesInRes; /* How many bytes in this resource? */ + DWORD dwImageOffset; /* Where in the file is this image */ +} ICONDIRENTRY, *LPICONDIRENTRY; + +typedef struct { + WORD idReserved; /* Reserved */ + WORD idType; /* Resource type (1 for icons) */ + WORD idCount; /* How many images? */ + ICONDIRENTRY idEntries[1]; /* The entries for each image */ +} ICONDIR, *LPICONDIR; + +/* + * A pointer to one of these strucutures is associated with each toplevel. + * This allows us to free up all memory associated with icon resources when a + * window is deleted or if the window's icon is changed. They are simply + * reference counted according to: + * + * (1) How many WmInfo structures point to this object + * (2) Whether the ThreadSpecificData defined in this file contains a pointer + * to this object. + * + * The former count is for windows whose icons are individually set, and the + * latter is for the global default icon choice. + * + * Icons loaded from .icr/.icr use the iconBlock field, icons loaded from + * .exe/.dll use the hIcon field. + */ + +typedef struct WinIconInstance { + size_t refCount; /* Number of instances that share this data + * structure. */ + BlockOfIconImagesPtr iconBlock; + /* Pointer to icon resource data for image */ +} WinIconInstance; + +typedef struct WinIconInstance *WinIconPtr; + +/* + * A data structure of the following type holds window-manager-related + * information for each top-level window in an application. + */ + +typedef struct TkWmInfo { + TkWindow *winPtr; /* Pointer to main Tk information for this + * window. */ + HWND wrapper; /* This is the decorative frame window created + * by the window manager to wrap a toplevel + * window. This window is a direct child of + * the root window. */ + char *title; /* Title to display in window caption. If + * NULL, use name of widget. Malloced. */ + char *iconName; /* Name to display in icon. Malloced. */ + XWMHints hints; /* Various pieces of information for window + * manager. */ + char *leaderName; /* Path name of leader of window group + * (corresponds to hints.window_group). + * Malloc-ed. Note: this field doesn't get + * updated if leader is destroyed. */ + TkWindow *masterPtr; /* Master window for TRANSIENT_FOR property, + * or NULL. */ + Tk_Window icon; /* Window to use as icon for this window, or + * NULL. */ + Tk_Window iconFor; /* Window for which this window is icon, or + * NULL if this isn't an icon for anyone. */ + + /* + * Information used to construct an XSizeHints structure for the window + * manager: + */ + + int defMinWidth, defMinHeight, defMaxWidth, defMaxHeight; + /* Default resize limits given by system. */ + int sizeHintsFlags; /* Flags word for XSizeHints structure. If the + * PBaseSize flag is set then the window is + * gridded; otherwise it isn't gridded. */ + int minWidth, minHeight; /* Minimum dimensions of window, in pixels or + * grid units. */ + int maxWidth, maxHeight; /* Maximum dimensions of window, in pixels or + * grid units. 0 to default. */ + Tk_Window gridWin; /* Identifies the window that controls + * gridding for this top-level, or NULL if the + * top-level isn't currently gridded. */ + int widthInc, heightInc; /* Increments for size changes (# pixels per + * step). */ + struct { + int x; /* numerator */ + int y; /* denominator */ + } minAspect, maxAspect; /* Min/max aspect ratios for window. */ + int reqGridWidth, reqGridHeight; + /* The dimensions of the window (in grid + * units) requested through the geometry + * manager. */ + int gravity; /* Desired window gravity. */ + + /* + * Information used to manage the size and location of a window. + */ + + int width, height; /* Desired dimensions of window, specified in + * pixels or grid units. These values are set + * by the "wm geometry" command and by + * ConfigureNotify events (for when wm resizes + * window). -1 means user hasn't requested + * dimensions. */ + int x, y; /* Desired X and Y coordinates for window. + * These values are set by "wm geometry", plus + * by ConfigureNotify events (when wm moves + * window). These numbers are different than + * the numbers stored in winPtr->changes + * because (a) they could be measured from the + * right or bottom edge of the screen (see + * WM_NEGATIVE_X and WM_NEGATIVE_Y flags) and + * (b) if the window has been reparented then + * they refer to the parent rather than the + * window itself. */ + int borderWidth, borderHeight; + /* Width and height of window dressing, in + * pixels for the current style/exStyle. This + * includes the border on both sides of the + * window. */ + int configX, configY; /* x,y position of toplevel when window is + * switched into fullscreen state, */ + int configWidth, configHeight; + /* Dimensions passed to last request that we + * issued to change geometry of window. Used + * to eliminate redundant resize operations */ + HMENU hMenu; /* the hMenu associated with this menu */ + DWORD style, exStyle; /* Style flags for the wrapper window. */ + LONG styleConfig; /* Extra user requested style bits */ + LONG exStyleConfig; /* Extra user requested extended style bits */ + Tcl_Obj *crefObj; /* COLORREF object for transparent handling */ + COLORREF colorref; /* COLORREF for transparent handling */ + double alpha; /* Alpha transparency level 0.0 (fully + * transparent) .. 1.0 (opaque) */ + + /* + * List of children of the toplevel which have private colormaps. + */ + + TkWindow **cmapList; /* Array of window with private colormaps. */ + int cmapCount; /* Number of windows in array. */ + + /* + * Miscellaneous information. + */ + + ProtocolHandler *protPtr; /* First in list of protocol handlers for this + * window (NULL means none). */ + int cmdArgc; /* Number of elements in cmdArgv below. */ + const char **cmdArgv; /* Array of strings to store in the WM_COMMAND + * property. NULL means nothing available. */ + char *clientMachine; /* String to store in WM_CLIENT_MACHINE + * property, or NULL. */ + int flags; /* Miscellaneous flags, defined below. */ + int numTransients; /* Number of transients on this window */ + WinIconPtr iconPtr; /* Pointer to titlebar icon structure for this + * window, or NULL. */ + struct TkWmInfo *nextPtr; /* Next in list of all top-level windows. */ +} WmInfo; + +/* + * Flag values for WmInfo structures: + * + * WM_NEVER_MAPPED - Non-zero means window has never been mapped; + * need to update all info when window is first + * mapped. + * WM_UPDATE_PENDING - Non-zero means a call to UpdateGeometryInfo + * has already been scheduled for this window; + * no need to schedule another one. + * WM_NEGATIVE_X - Non-zero means x-coordinate is measured in + * pixels from right edge of screen, rather than + * from left edge. + * WM_NEGATIVE_Y - Non-zero means y-coordinate is measured in + * pixels up from bottom of screen, rather than + * down from top. + * WM_UPDATE_SIZE_HINTS - Non-zero means that new size hints need to be + * propagated to window manager. Not used on Win. + * WM_SYNC_PENDING - Set to non-zero while waiting for the window + * manager to respond to some state change. + * WM_MOVE_PENDING - Non-zero means the application has requested a + * new position for the window, but it hasn't + * been reflected through the window manager yet. + * WM_COLORMAPS_EXPLICIT - Non-zero means the colormap windows were set + * explicitly via "wm colormapwindows". + * WM_ADDED_TOPLEVEL_COLORMAP - Non-zero means that when "wm colormapwindows" + * was called the top-level itself wasn't + * specified, so we added it implicitly at the + * end of the list. + * WM_WIDTH_NOT_RESIZABLE - Non-zero means that we're not supposed to + * allow the user to change the width of the + * window (controlled by "wm resizable" command). + * WM_HEIGHT_NOT_RESIZABLE - Non-zero means that we're not supposed to + * allow the user to change the height of the + * window (controlled by "wm resizable" command). + * WM_WITHDRAWN - Non-zero means that this window has explicitly + * been withdrawn. If it's a transient, it should + * not mirror state changes in the master. + * WM_FULLSCREEN - Non-zero means that this window has been placed + * in the full screen mode. It should be mapped at + * 0,0 and be the width and height of the screen. + */ + +#define WM_NEVER_MAPPED (1<<0) +#define WM_UPDATE_PENDING (1<<1) +#define WM_NEGATIVE_X (1<<2) +#define WM_NEGATIVE_Y (1<<3) +#define WM_UPDATE_SIZE_HINTS (1<<4) +#define WM_SYNC_PENDING (1<<5) +#define WM_CREATE_PENDING (1<<6) +#define WM_MOVE_PENDING (1<<7) +#define WM_COLORMAPS_EXPLICIT (1<<8) +#define WM_ADDED_TOPLEVEL_COLORMAP (1<<9) +#define WM_WIDTH_NOT_RESIZABLE (1<<10) +#define WM_HEIGHT_NOT_RESIZABLE (1<<11) +#define WM_WITHDRAWN (1<<12) +#define WM_FULLSCREEN (1<<13) + +/* + * Window styles for various types of toplevel windows. + */ + +#define WM_OVERRIDE_STYLE (WS_CLIPCHILDREN|WS_CLIPSIBLINGS|CS_DBLCLKS) +#define EX_OVERRIDE_STYLE (WS_EX_TOOLWINDOW) + +#define WM_FULLSCREEN_STYLE (WS_POPUP|WM_OVERRIDE_STYLE) +#define EX_FULLSCREEN_STYLE (WS_EX_APPWINDOW) + +#define WM_TOPLEVEL_STYLE (WS_OVERLAPPEDWINDOW|WS_CLIPCHILDREN|CS_DBLCLKS) +#define EX_TOPLEVEL_STYLE (0) + +#define WM_TRANSIENT_STYLE \ + (WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPSIBLINGS|CS_DBLCLKS) +#define EX_TRANSIENT_STYLE (WS_EX_DLGMODALFRAME) + +/* + * The following structure is the official type record for geometry management + * of top-level windows. + */ + +static void TopLevelReqProc(ClientData dummy, Tk_Window tkwin); +static void RemapWindows(TkWindow *winPtr, HWND parentHWND); + +static const Tk_GeomMgr wmMgrType = { + "wm", /* name */ + TopLevelReqProc, /* requestProc */ + NULL, /* lostSlaveProc */ +}; + +typedef struct ThreadSpecificData { + HPALETTE systemPalette; /* System palette; refers to the currently + * installed foreground logical palette. */ + TkWindow *createWindow; /* Window that is being constructed. This + * value is set immediately before a call to + * CreateWindowEx, and is used by SetLimits. + * This is a gross hack needed to work around + * Windows brain damage where it sends the + * WM_GETMINMAXINFO message before the + * WM_CREATE window. */ + int initialized; /* Flag indicating whether thread-specific + * elements of module have been + * initialized. */ + int firstWindow; /* Flag, cleared when the first window is + * mapped in a non-iconic state. */ + WinIconPtr iconPtr; /* IconPtr being used as default for all + * toplevels, or NULL. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * The following variables cannot be placed in thread local storage because + * they must be shared across threads. + */ + +static int initialized; /* Flag indicating whether module has been + * initialized. */ + +TCL_DECLARE_MUTEX(winWmMutex) + +/* + * Forward declarations for functions defined in this file: + */ + +static int ActivateWindow(Tcl_Event *evPtr, int flags); +static void ConfigureTopLevel(WINDOWPOS *pos); +static void GenerateConfigureNotify(TkWindow *winPtr); +static void GenerateActivateEvent(TkWindow *winPtr, const int *flagPtr); +static void GetMaxSize(WmInfo *wmPtr, + int *maxWidthPtr, int *maxHeightPtr); +static void GetMinSize(WmInfo *wmPtr, + int *minWidthPtr, int *minHeightPtr); +static TkWindow * GetTopLevel(HWND hwnd); +static void InitWm(void); +static int InstallColormaps(HWND hwnd, int message, + int isForemost); +static void InvalidateSubTree(TkWindow *winPtr, Colormap colormap); +static void InvalidateSubTreeDepth(TkWindow *winPtr); +static int ParseGeometry(Tcl_Interp *interp, const char *string, + TkWindow *winPtr); +static void RefreshColormap(Colormap colormap, TkDisplay *dispPtr); +static void SetLimits(HWND hwnd, MINMAXINFO *info); +static void TkWmStackorderToplevelWrapperMap(TkWindow *winPtr, + Display *display, Tcl_HashTable *table); +static LRESULT CALLBACK TopLevelProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); +static void TopLevelEventProc(ClientData clientData, + XEvent *eventPtr); +static void TopLevelReqProc(ClientData dummy, Tk_Window tkwin); +static void UpdateGeometryInfo(ClientData clientData); +static void UpdateWrapper(TkWindow *winPtr); +static LRESULT CALLBACK WmProc(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); +static void WmWaitVisibilityOrMapProc(ClientData clientData, + XEvent *eventPtr); +static BlockOfIconImagesPtr ReadIconOrCursorFromFile(Tcl_Interp *interp, + Tcl_Obj* fileName, BOOL isIcon); +static WinIconPtr ReadIconFromFile(Tcl_Interp *interp, + Tcl_Obj *fileName); +static WinIconPtr GetIconFromPixmap(Display *dsPtr, Pixmap pixmap); +static int ReadICOHeader(Tcl_Channel channel); +static BOOL AdjustIconImagePointers(LPICONIMAGE lpImage); +static HICON MakeIconOrCursorFromResource(LPICONIMAGE lpIcon, + BOOL isIcon); +static HICON GetIcon(WinIconPtr titlebaricon, int icon_size); +static int WinSetIcon(Tcl_Interp *interp, + WinIconPtr titlebaricon, Tk_Window tkw); +static void FreeIconBlock(BlockOfIconImagesPtr lpIR); +static void DecrIconRefCount(WinIconPtr titlebaricon); + +static int WmAspectCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmAttributesCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmClientCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmColormapwindowsCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmCommandCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmDeiconifyCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmFocusmodelCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmForgetCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmFrameCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmGeometryCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmGridCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmGroupCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconbitmapCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconifyCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconmaskCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconnameCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconphotoCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconpositionCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmIconwindowCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmManageCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmMaxsizeCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmMinsizeCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmOverrideredirectCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmPositionfromCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmProtocolCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmResizableCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmSizefromCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmStackorderCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmStateCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmTitleCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmTransientCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static int WmWithdrawCmd(Tk_Window tkwin, + TkWindow *winPtr, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +static void WmUpdateGeom(WmInfo *wmPtr, TkWindow *winPtr); + +/* + * Used in BytesPerLine + */ + +#define WIDTHBYTES(bits) ((((bits) + 31)>>5)<<2) + +/* + *---------------------------------------------------------------------- + * + * DIBNumColors -- + * + * Calculates the number of entries in the color table, given by LPSTR + * lpbi - pointer to the CF_DIB memory block. Used by titlebar icon code. + * + * Results: + * WORD - Number of entries in the color table. + * + *---------------------------------------------------------------------- + */ + +static WORD +DIBNumColors( + LPSTR lpbi) +{ + WORD wBitCount; + DWORD dwClrUsed; + + dwClrUsed = ((LPBITMAPINFOHEADER) lpbi)->biClrUsed; + + if (dwClrUsed) { + return (WORD) dwClrUsed; + } + + wBitCount = ((LPBITMAPINFOHEADER) lpbi)->biBitCount; + + switch (wBitCount) { + case 1: + return 2; + case 4: + return 16; + case 8: + return 256; + default: + return 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * PaletteSize -- + * + * Calculates the number of bytes in the color table, as given by LPSTR + * lpbi - pointer to the CF_DIB memory block. Used by titlebar icon code. + * + * Results: + * Number of bytes in the color table + * + *---------------------------------------------------------------------- + */ +static WORD +PaletteSize( + LPSTR lpbi) +{ + return (WORD) (DIBNumColors(lpbi) * sizeof(RGBQUAD)); +} + +/* + *---------------------------------------------------------------------- + * + * FindDIBits -- + * + * Locate the image bits in a CF_DIB format DIB, as given by LPSTR lpbi - + * pointer to the CF_DIB memory block. Used by titlebar icon code. + * + * Results: + * pointer to the image bits + * + * Side effects: None + * + * + *---------------------------------------------------------------------- + */ + +static LPSTR +FindDIBBits( + LPSTR lpbi) +{ + return lpbi + *((LPDWORD) lpbi) + PaletteSize(lpbi); +} + +/* + *---------------------------------------------------------------------- + * + * BytesPerLine -- + * + * Calculates the number of bytes in one scan line, as given by + * LPBITMAPINFOHEADER lpBMIH - pointer to the BITMAPINFOHEADER that + * begins the CF_DIB block. Used by titlebar icon code. + * + * Results: + * number of bytes in one scan line (DWORD aligned) + * + *---------------------------------------------------------------------- + */ + +static DWORD +BytesPerLine( + LPBITMAPINFOHEADER lpBMIH) +{ + return WIDTHBYTES(lpBMIH->biWidth * lpBMIH->biPlanes * lpBMIH->biBitCount); +} + +/* + *---------------------------------------------------------------------- + * + * AdjustIconImagePointers -- + * + * Adjusts internal pointers in icon resource struct, as given by + * LPICONIMAGE lpImage - the resource to handle. Used by titlebar icon + * code. + * + * Results: + * BOOL - TRUE for success, FALSE for failure + * + *---------------------------------------------------------------------- + */ + +static BOOL +AdjustIconImagePointers( + LPICONIMAGE lpImage) +{ + /* + * Sanity check. + */ + + if (lpImage == NULL) { + return FALSE; + } + + /* + * BITMAPINFO is at beginning of bits. + */ + + lpImage->lpbi = (LPBITMAPINFO) lpImage->lpBits; + + /* + * Width - simple enough. + */ + + lpImage->Width = lpImage->lpbi->bmiHeader.biWidth; + + /* + * Icons are stored in funky format where height is doubled so account for + * that. + */ + + lpImage->Height = (lpImage->lpbi->bmiHeader.biHeight)/2; + + /* + * How many colors? + */ + + lpImage->Colors = lpImage->lpbi->bmiHeader.biPlanes + * lpImage->lpbi->bmiHeader.biBitCount; + + /* + * XOR bits follow the header and color table. + */ + + lpImage->lpXOR = (LPBYTE) FindDIBBits((LPSTR) lpImage->lpbi); + + /* + * AND bits follow the XOR bits. + */ + + lpImage->lpAND = lpImage->lpXOR + + lpImage->Height*BytesPerLine((LPBITMAPINFOHEADER) lpImage->lpbi); + return TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * MakeIconOrCursorFromResource -- + * + * Construct an actual HICON structure from the information in a + * resource. + * + * Results: + * Icon + * + *---------------------------------------------------------------------- + */ + +static HICON +MakeIconOrCursorFromResource( + LPICONIMAGE lpIcon, + BOOL isIcon) +{ + HICON hIcon; + + /* + * Sanity Check + */ + + if (lpIcon == NULL || lpIcon->lpBits == NULL) { + return NULL; + } + + /* + * Let the OS do the real work :) + */ + + hIcon = (HICON) CreateIconFromResourceEx(lpIcon->lpBits, + lpIcon->dwNumBytes, isIcon, 0x00030000, + (*(LPBITMAPINFOHEADER) lpIcon->lpBits).biWidth, + (*(LPBITMAPINFOHEADER) lpIcon->lpBits).biHeight/2, 0); + + /* + * It failed, odds are good we're on NT so try the non-Ex way. + */ + + if (hIcon == NULL) { + /* + * We would break on NT if we try with a 16bpp image. + */ + + if (lpIcon->lpbi->bmiHeader.biBitCount != 16) { + hIcon = CreateIconFromResource(lpIcon->lpBits, lpIcon->dwNumBytes, + isIcon, 0x00030000); + } + } + return hIcon; +} + +/* + *---------------------------------------------------------------------- + * + * ReadICOHeader -- + * + * Reads the header from an ICO file, as specfied by channel. + * + * Results: + * UINT - Number of images in file, -1 for failure. If this succeeds, + * there is a decent chance this is a valid icon file. + * + *---------------------------------------------------------------------- + */ + +static int +ReadICOHeader( + Tcl_Channel channel) +{ + union { + WORD word; + char bytes[sizeof(WORD)]; + } input; + + /* + * Read the 'reserved' WORD, which should be a zero word. + */ + + if (Tcl_Read(channel, input.bytes, sizeof(WORD)) != sizeof(WORD)) { + return -1; + } + if (input.word != 0) { + return -1; + } + + /* + * Read the type WORD, which should be of type 1. + */ + + if (Tcl_Read(channel, input.bytes, sizeof(WORD)) != sizeof(WORD)) { + return -1; + } + if (input.word != 1) { + return -1; + } + + /* + * Get and return the count of images. + */ + + if (Tcl_Read(channel, input.bytes, sizeof(WORD)) != sizeof(WORD)) { + return -1; + } + return (int) input.word; +} + +/* + *---------------------------------------------------------------------- + * + * InitWindowClass -- + * + * This routine creates the Wm toplevel decorative frame class. + * + * Results: + * None. + * + * Side effects: + * Registers a new window class. + * + *---------------------------------------------------------------------- + */ + +static int +InitWindowClass( + WinIconPtr titlebaricon) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + tsdPtr->initialized = 1; + tsdPtr->firstWindow = 1; + tsdPtr->iconPtr = NULL; + } + if (!initialized) { + Tcl_MutexLock(&winWmMutex); + if (!initialized) { + WNDCLASS class; + + initialized = 1; + + /* + * The only difference between WNDCLASSW and WNDCLASSA are in + * pointers, so we can use the generic structure WNDCLASS. + */ + + ZeroMemory(&class, sizeof(WNDCLASS)); + + class.style = CS_HREDRAW | CS_VREDRAW; + class.hInstance = Tk_GetHINSTANCE(); + class.lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME; + class.lpfnWndProc = WmProc; + if (titlebaricon == NULL) { + class.hIcon = LoadIcon(Tk_GetHINSTANCE(), TEXT("tk")); + } else { + class.hIcon = GetIcon(titlebaricon, ICON_BIG); + if (class.hIcon == NULL) { + return TCL_ERROR; + } + + /* + * Store pointer to default icon so we know when we need to + * free that information + */ + + tsdPtr->iconPtr = titlebaricon; + } + class.hCursor = LoadCursor(NULL, IDC_ARROW); + + if (!RegisterClass(&class)) { + Tcl_Panic("Unable to register TkTopLevel class"); + } + } + Tcl_MutexUnlock(&winWmMutex); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * InitWm -- + * + * This initialises the window manager + * + * Results: + * None. + * + * Side effects: + * Registers a new window class. + * + *---------------------------------------------------------------------- + */ + +static void +InitWm(void) +{ + /* Ignore return result */ + (void) InitWindowClass(NULL); +} + +/* + *---------------------------------------------------------------------- + * + * WinSetIcon -- + * + * Sets either the default toplevel titlebar icon, or the icon for a + * specific toplevel (if tkw is given, then only that window is used). + * + * The ref-count of the titlebaricon is NOT changed. If this function + * returns successfully, the caller should assume the icon was used (and + * therefore the ref-count should be adjusted to reflect that fact). If + * the function returned an error, the caller should assume the icon was + * not used (and may wish to free the memory associated with it). + * + * Results: + * A standard Tcl return code. + * + * Side effects: + * One or all windows may have their icon changed. The Tcl result may be + * modified. The window-manager will be initialised if it wasn't already. + * The given window will be forced into existence. + * + *---------------------------------------------------------------------- + */ + +static int +WinSetIcon( + Tcl_Interp *interp, + WinIconPtr titlebaricon, + Tk_Window tkw) +{ + WmInfo *wmPtr; + HWND hwnd; + int application = 0; + + if (tkw == NULL) { + tkw = Tk_MainWindow(interp); + application = 1; + } + + if (!(Tk_IsTopLevel(tkw))) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" isn't a top-level window", Tk_PathName(tkw))); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "TOPLEVEL", Tk_PathName(tkw), + NULL); + return TCL_ERROR; + } + if (Tk_WindowId(tkw) == None) { + Tk_MakeWindowExist(tkw); + } + + /* + * We must get the window's wrapper, not the window itself. + */ + + wmPtr = ((TkWindow *) tkw)->wmInfoPtr; + hwnd = wmPtr->wrapper; + + if (application) { + if (hwnd == NULL) { + /* + * I don't actually think this is ever the correct thing, unless + * perhaps the window doesn't have a wrapper. But I believe all + * windows have wrappers. + */ + + hwnd = Tk_GetHWND(Tk_WindowId(tkw)); + } + + /* + * If we aren't initialised, then just initialise with the user's + * icon. Otherwise our icon choice will be ignored moments later when + * Tk finishes initialising. + */ + + if (!initialized) { + if (InitWindowClass(titlebaricon) != TCL_OK) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "Unable to set icon", -1)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICON", "FAILED", NULL); + return TCL_ERROR; + } + } else { + ThreadSpecificData *tsdPtr; + + /* + * Don't check return result of SetClassLong() or + * SetClassLongPtr() since they return the previously set value + * which is zero on the initial call or in an error case. The MSDN + * documentation does not indicate that the result needs to be + * checked. + */ + + SetClassLongPtr(hwnd, GCLP_HICONSM, + (LPARAM) GetIcon(titlebaricon, ICON_SMALL)); + SetClassLongPtr(hwnd, GCLP_HICON, + (LPARAM) GetIcon(titlebaricon, ICON_BIG)); + tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + if (tsdPtr->iconPtr != NULL) { + DecrIconRefCount(tsdPtr->iconPtr); + } + tsdPtr->iconPtr = titlebaricon; + } + } else { + if (!initialized) { + /* + * Need to initialise the wm otherwise we will fail on code which + * tries to set a toplevel's icon before that happens. Ignore + * return result. + */ + + (void) InitWindowClass(NULL); + } + + /* + * The following code is exercised if you do + * + * toplevel .t ; wm titlebaricon .t foo.icr + * + * i.e. the wm hasn't had time to properly create the '.t' window + * before you set the icon. + */ + + if (hwnd == NULL) { + /* + * This little snippet is copied from the 'Map' function, and + * should probably be placed in one proper location. + */ + + UpdateWrapper(wmPtr->winPtr); + wmPtr = ((TkWindow *) tkw)->wmInfoPtr; + hwnd = wmPtr->wrapper; + if (hwnd == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "Can't set icon; window has no wrapper.", -1)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICON", "WRAPPER", NULL); + return TCL_ERROR; + } + } + SendMessage(hwnd, WM_SETICON, ICON_SMALL, + (LPARAM) GetIcon(titlebaricon, ICON_SMALL)); + SendMessage(hwnd, WM_SETICON, ICON_BIG, + (LPARAM) GetIcon(titlebaricon, ICON_BIG)); + + /* + * Update the iconPtr we keep for each WmInfo structure. + */ + + if (wmPtr->iconPtr != NULL) { + /* + * Free any old icon ptr which is associated with this window. + */ + + DecrIconRefCount(wmPtr->iconPtr); + } + + /* + * We do not need to increment the ref count for the titlebaricon, + * because it was already incremented when we retrieved it. + */ + + wmPtr->iconPtr = titlebaricon; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetIcon -- + * + * Gets either the default toplevel titlebar icon, or the icon for a + * specific toplevel (ICON_SMALL or ICON_BIG). + * + * Results: + * A Windows HICON. + * + * Side effects: + * The given window will be forced into existence. + * + *---------------------------------------------------------------------- + */ + +HICON +TkWinGetIcon( + Tk_Window tkwin, + DWORD iconsize) +{ + WmInfo *wmPtr; + HICON icon; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->iconPtr != NULL) { + /* + * return default toplevel icon + */ + + return GetIcon(tsdPtr->iconPtr, (int) iconsize); + } + + /* + * Ensure we operate on the toplevel, that has the icon refs. + */ + + while (!Tk_IsTopLevel(tkwin)) { + tkwin = Tk_Parent(tkwin); + if (tkwin == NULL) { + return NULL; + } + } + + if (Tk_WindowId(tkwin) == None) { + Tk_MakeWindowExist(tkwin); + } + + wmPtr = ((TkWindow *) tkwin)->wmInfoPtr; + if (wmPtr->iconPtr != NULL) { + /* + * return window toplevel icon + */ + + return GetIcon(wmPtr->iconPtr, (int) iconsize); + } + + /* + * Find the icon otherwise associated with the toplevel, or finally with + * the window class. + */ + + icon = (HICON) SendMessage(wmPtr->wrapper, WM_GETICON, iconsize, + (LPARAM) NULL); + if (icon == (HICON) NULL) { + icon = (HICON) GetClassLongPtr(wmPtr->wrapper, + (iconsize == ICON_BIG) ? GCLP_HICON : GCLP_HICONSM); + } + return icon; +} + +/* + *---------------------------------------------------------------------- + * + * ReadIconFromFile -- + * + * Read the contents of a file (usually .ico, .icr) and extract an icon + * resource, if possible, otherwise check if the shell has an icon + * assigned to the given file and use that. If both of those fail, then + * NULL is returned, and an error message will already be in the + * interpreter. + * + * Results: + * A WinIconPtr structure containing the icons in the file, with its ref + * count already incremented. The calling function should either place + * this structure inside a WmInfo structure, or it should pass it on to + * DecrIconRefCount() to ensure no memory leaks occur. + * + * If the given fileName did not contain a valid icon structure, + * return NULL. + * + * Side effects: + * Memory is allocated for the returned structure and the icons it + * contains. If the structure is not wanted, it should be passed to + * DecrIconRefCount, and in any case a valid ref count should be ensured + * to avoid memory leaks. + * + * Currently icon resources are not shared, so the ref count of one of + * these structures will always be 0 or 1. However all we need do is + * implement some sort of lookup function between filenames and + * WinIconPtr structures and no other code will need to be changed. The + * pseudo-code for this is implemented below in the 'if (0)' branch. It + * did not seem necessary to implement this optimisation here, since + * moving to icon<->image conversions will probably make it obsolete. + * + *---------------------------------------------------------------------- + */ + +static WinIconPtr +ReadIconFromFile( + Tcl_Interp *interp, + Tcl_Obj *fileName) +{ + WinIconPtr titlebaricon = NULL; + BlockOfIconImagesPtr lpIR; + +#if 0 /* TODO: Dead code? */ + if (0 /* If we already have an icon for this filename */) { + titlebaricon = NULL; /* Get the real value from a lookup */ + titlebaricon->refCount++; + return titlebaricon; + } +#endif + + /* + * First check if it is a .ico file. + */ + + lpIR = ReadIconOrCursorFromFile(interp, fileName, TRUE); + + /* + * Then see if we can ask the shell for the icon for this file. We + * want both the regular and small icons so that the Alt-Tab (task- + * switching) display uses the right icon. + */ + + if (lpIR == NULL) { + SHFILEINFO sfiSM; + Tcl_DString ds, ds2; + DWORD *res; + const char *file; + + file = Tcl_TranslateFileName(interp, Tcl_GetString(fileName), &ds); + if (file == NULL) { + return NULL; + } + Tcl_WinUtfToTChar(file, -1, &ds2); + Tcl_DStringFree(&ds); + res = (DWORD *)SHGetFileInfo((TCHAR *)Tcl_DStringValue(&ds2), 0, &sfiSM, + sizeof(SHFILEINFO), SHGFI_SMALLICON|SHGFI_ICON); + + if (res != 0) { + SHFILEINFO sfi; + unsigned size; + + Tcl_ResetResult(interp); + res = (DWORD *)SHGetFileInfo((TCHAR *)Tcl_DStringValue(&ds2), 0, &sfi, + sizeof(SHFILEINFO), SHGFI_ICON); + + /* + * Account for extra icon, if necessary. + */ + + size = sizeof(BlockOfIconImages) + + ((res != 0) ? sizeof(ICONIMAGE) : 0); + lpIR = ckalloc(size); + if (lpIR == NULL) { + if (res != 0) { + DestroyIcon(sfi.hIcon); + } + DestroyIcon(sfiSM.hIcon); + Tcl_DStringFree(&ds2); + return NULL; + } + ZeroMemory(lpIR, size); + + lpIR->nNumImages = ((res != 0) ? 2 : 1); + lpIR->IconImages[0].Width = 16; + lpIR->IconImages[0].Height = 16; + lpIR->IconImages[0].Colors = 4; + lpIR->IconImages[0].hIcon = sfiSM.hIcon; + + /* + * All other IconImages fields are ignored. + */ + + if (res != 0) { + lpIR->IconImages[1].Width = 32; + lpIR->IconImages[1].Height = 32; + lpIR->IconImages[1].Colors = 4; + lpIR->IconImages[1].hIcon = sfi.hIcon; + } + } + Tcl_DStringFree(&ds2); + } + if (lpIR != NULL) { + titlebaricon = ckalloc(sizeof(WinIconInstance)); + titlebaricon->iconBlock = lpIR; + titlebaricon->refCount = 1; + } + return titlebaricon; +} + +/* + *---------------------------------------------------------------------- + * + * GetIconFromPixmap -- + * + * Turn a Tk Pixmap (i.e. a bitmap) into an icon resource, if possible, + * otherwise NULL is returned. + * + * Results: + * A WinIconPtr structure containing a conversion of the given bitmap + * into an icon, with its ref count already incremented. The calling + * function should either place this structure inside a WmInfo structure, + * or it should pass it on to DecrIconRefCount() to ensure no memory + * leaks occur. + * + * If the given pixmap did not contain a valid icon structure, return + * NULL. + * + * Side effects: + * Memory is allocated for the returned structure and the icons it + * contains. If the structure is not wanted, it should be passed to + * DecrIconRefCount, and in any case a valid ref count should be ensured + * to avoid memory leaks. + * + * Currently icon resources are not shared, so the ref count of one of + * these structures will always be 0 or 1. However all we need do is + * implement some sort of lookup function between pixmaps and WinIconPtr + * structures and no other code will need to be changed. + * + *---------------------------------------------------------------------- + */ + +static WinIconPtr +GetIconFromPixmap( + Display *dsPtr, + Pixmap pixmap) +{ + WinIconPtr titlebaricon = NULL; + TkWinDrawable *twdPtr = (TkWinDrawable *) pixmap; + BlockOfIconImagesPtr lpIR; + ICONINFO icon; + HICON hIcon; + int width, height; + + if (twdPtr == NULL) { + return NULL; + } + +#if 0 /* TODO: Dead code?*/ + if (0 /* If we already have an icon for this pixmap */) { + titlebaricon = NULL; /* Get the real value from a lookup */ + titlebaricon->refCount++; + return titlebaricon; + } +#endif + + Tk_SizeOfBitmap(dsPtr, pixmap, &width, &height); + + icon.fIcon = TRUE; + icon.xHotspot = 0; + icon.yHotspot = 0; + icon.hbmMask = twdPtr->bitmap.handle; + icon.hbmColor = twdPtr->bitmap.handle; + + hIcon = CreateIconIndirect(&icon); + if (hIcon == NULL) { + return NULL; + } + + lpIR = ckalloc(sizeof(BlockOfIconImages)); + if (lpIR == NULL) { + DestroyIcon(hIcon); + return NULL; + } + + lpIR->nNumImages = 1; + lpIR->IconImages[0].Width = width; + lpIR->IconImages[0].Height = height; + lpIR->IconImages[0].Colors = 1 << twdPtr->bitmap.depth; + lpIR->IconImages[0].hIcon = hIcon; + + /* + * These fields are ignored. + */ + + lpIR->IconImages[0].lpBits = 0; + lpIR->IconImages[0].dwNumBytes = 0; + lpIR->IconImages[0].lpXOR = 0; + lpIR->IconImages[0].lpAND = 0; + + titlebaricon = ckalloc(sizeof(WinIconInstance)); + titlebaricon->iconBlock = lpIR; + titlebaricon->refCount = 1; + return titlebaricon; +} + +/* + *---------------------------------------------------------------------- + * + * DecrIconRefCount -- + * + * Reduces the reference count. + * + * Results: + * None. + * + * Side effects: + * If the ref count falls to zero, free the memory associated with the + * icon resource structures. In this case the pointer passed into this + * function is no longer valid. + * + *---------------------------------------------------------------------- + */ + +static void +DecrIconRefCount( + WinIconPtr titlebaricon) +{ + if (titlebaricon->refCount-- <= 1) { + if (titlebaricon->iconBlock != NULL) { + FreeIconBlock(titlebaricon->iconBlock); + } + titlebaricon->iconBlock = NULL; + + ckfree(titlebaricon); + } +} + +/* + *---------------------------------------------------------------------- + * + * FreeIconBlock -- + * + * Frees all memory associated with a previously loaded titlebaricon. + * The icon block pointer is no longer valid once this function returns. + * + * Results: + * None. + * + * Side effects: + * + * + *---------------------------------------------------------------------- + */ + +static void +FreeIconBlock( + BlockOfIconImagesPtr lpIR) +{ + int i; + + /* + * Free all the bits. + */ + + for (i=0 ; i<lpIR->nNumImages ; i++) { + if (lpIR->IconImages[i].lpBits != NULL) { + ckfree(lpIR->IconImages[i].lpBits); + } + if (lpIR->IconImages[i].hIcon != NULL) { + DestroyIcon(lpIR->IconImages[i].hIcon); + } + } + ckfree(lpIR); +} + +/* + *---------------------------------------------------------------------- + * + * GetIcon -- + * + * Extracts an icon of a given size from an icon resource + * + * Results: + * Returns the icon, if found, else NULL. + * + *---------------------------------------------------------------------- + */ + +static HICON +GetIcon( + WinIconPtr titlebaricon, + int icon_size) +{ + BlockOfIconImagesPtr lpIR; + unsigned int size = (icon_size == 0 ? 16 : 32); + int i; + + if (titlebaricon == NULL) { + return NULL; + } + + lpIR = titlebaricon->iconBlock; + if (lpIR == NULL) { + return NULL; + } + + for (i=0 ; i<lpIR->nNumImages ; i++) { + /* + * Take the first or a 32x32 16 color icon + */ + + if ((lpIR->IconImages[i].Height == size) + && (lpIR->IconImages[i].Width == size) + && (lpIR->IconImages[i].Colors >= 4)) { + return lpIR->IconImages[i].hIcon; + } + } + + /* + * If we get here, then just return the first one, it will have to do! + */ + + if (lpIR->nNumImages >= 1) { + return lpIR->IconImages[0].hIcon; + } + return NULL; +} + +#if 0 /* UNUSED */ +static HCURSOR +TclWinReadCursorFromFile( + Tcl_Interp* interp, + Tcl_Obj* fileName) +{ + BlockOfIconImagesPtr lpIR; + HICON res = NULL; + + lpIR = ReadIconOrCursorFromFile(interp, fileName, FALSE); + if (lpIR == NULL) { + return NULL; + } + if (lpIR->nNumImages >= 1) { + res = CopyImage(lpIR->IconImages[0].hIcon, IMAGE_CURSOR, 0, 0, 0); + } + FreeIconBlock(lpIR); + return res; +} +#endif + +/* + *---------------------------------------------------------------------- + * + * ReadIconOrCursorFromFile -- + * + * Reads an Icon Resource from an ICO file, as given by char* fileName - + * Name of the ICO file. This name should be in Utf format. + * + * Results: + * Returns an icon resource, if found, else NULL. + * + * Side effects: + * May leave error messages in the Tcl interpreter. + * + *---------------------------------------------------------------------- + */ + +static BlockOfIconImagesPtr +ReadIconOrCursorFromFile( + Tcl_Interp *interp, + Tcl_Obj *fileName, + BOOL isIcon) +{ + BlockOfIconImagesPtr lpIR; + Tcl_Channel channel; + int i; + DWORD dwBytesRead; + LPICONDIRENTRY lpIDE; + + /* + * Open the file. + */ + + channel = Tcl_FSOpenFileChannel(interp, fileName, "r", 0); + if (channel == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error opening file \"%s\" for reading: %s", + Tcl_GetString(fileName), Tcl_PosixError(interp))); + return NULL; + } + if (Tcl_SetChannelOption(interp, channel, "-translation", "binary") + != TCL_OK) { + Tcl_Close(NULL, channel); + return NULL; + } + if (Tcl_SetChannelOption(interp, channel, "-encoding", "binary") + != TCL_OK) { + Tcl_Close(NULL, channel); + return NULL; + } + + /* + * Allocate memory for the resource structure + */ + + lpIR = ckalloc(sizeof(BlockOfIconImages)); + + /* + * Read in the header + */ + + lpIR->nNumImages = ReadICOHeader(channel); + if (lpIR->nNumImages == -1) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("Invalid file header", -1)); + Tcl_Close(NULL, channel); + ckfree(lpIR); + return NULL; + } + + /* + * Adjust the size of the struct to account for the images. + */ + + lpIR = ckrealloc(lpIR, sizeof(BlockOfIconImages) + + (lpIR->nNumImages - 1) * sizeof(ICONIMAGE)); + + /* + * Allocate enough memory for the icon directory entries. + */ + + lpIDE = ckalloc(lpIR->nNumImages * sizeof(ICONDIRENTRY)); + + /* + * Read in the icon directory entries. + */ + + dwBytesRead = Tcl_Read(channel, (char *) lpIDE, + (int) (lpIR->nNumImages * sizeof(ICONDIRENTRY))); + if (dwBytesRead != lpIR->nNumImages * sizeof(ICONDIRENTRY)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error reading file: %s", Tcl_PosixError(interp))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICON", "READ", NULL); + Tcl_Close(NULL, channel); + ckfree(lpIDE); + ckfree(lpIR); + return NULL; + } + + /* + * NULL-out everything to make memory management easier. + */ + + for (i = 0; i < lpIR->nNumImages; i++) { + lpIR->IconImages[i].lpBits = NULL; + } + + /* + * Loop through and read in each image. + */ + + for (i=0 ; i<lpIR->nNumImages ; i++) { + /* + * Allocate memory for the resource. + */ + + lpIR->IconImages[i].lpBits = ckalloc(lpIDE[i].dwBytesInRes); + lpIR->IconImages[i].dwNumBytes = lpIDE[i].dwBytesInRes; + + /* + * Seek to beginning of this image. + */ + + if (Tcl_Seek(channel, lpIDE[i].dwImageOffset, FILE_BEGIN) == -1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error seeking in file: %s", Tcl_PosixError(interp))); + goto readError; + } + + /* + * Read it in. + */ + + dwBytesRead = Tcl_Read(channel, (char *)lpIR->IconImages[i].lpBits, + (int) lpIDE[i].dwBytesInRes); + if (dwBytesRead != lpIDE[i].dwBytesInRes) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "error reading file: %s", Tcl_PosixError(interp))); + goto readError; + } + + /* + * Set the internal pointers appropriately. + */ + + if (!AdjustIconImagePointers(&lpIR->IconImages[i])) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "Error converting to internal format", -1)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICON", "FORMAT", NULL); + goto readError; + } + lpIR->IconImages[i].hIcon = + MakeIconOrCursorFromResource(&lpIR->IconImages[i], isIcon); + } + + /* + * Clean up + */ + + ckfree(lpIDE); + Tcl_Close(NULL, channel); + return lpIR; + + readError: + Tcl_Close(NULL, channel); + for (i = 0; i < lpIR->nNumImages; i++) { + if (lpIR->IconImages[i].lpBits != NULL) { + ckfree(lpIR->IconImages[i].lpBits); + } + } + ckfree(lpIDE); + ckfree(lpIR); + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * GetTopLevel -- + * + * This function retrieves the TkWindow associated with the given HWND. + * + * Results: + * Returns the matching TkWindow. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static TkWindow * +GetTopLevel( + HWND hwnd) +{ + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + /* + * If this function is called before the CreateWindowEx call has + * completed, then the user data slot will not have been set yet, so we + * use the global createWindow variable. + */ + + if (tsdPtr->createWindow) { + return tsdPtr->createWindow; + } + return (TkWindow *) GetWindowLongPtr(hwnd, GWLP_USERDATA); +} + +/* + *---------------------------------------------------------------------- + * + * SetLimits -- + * + * Updates the minimum and maximum window size constraints. + * + * Results: + * None. + * + * Side effects: + * Changes the values of the info pointer to reflect the current minimum + * and maximum size values. + * + *---------------------------------------------------------------------- + */ + +static void +SetLimits( + HWND hwnd, + MINMAXINFO *info) +{ + register WmInfo *wmPtr; + int maxWidth, maxHeight; + int minWidth, minHeight; + int base; + TkWindow *winPtr = GetTopLevel(hwnd); + + if (winPtr == NULL) { + return; + } + + wmPtr = winPtr->wmInfoPtr; + + /* + * Copy latest constraint info. + */ + + wmPtr->defMinWidth = info->ptMinTrackSize.x; + wmPtr->defMinHeight = info->ptMinTrackSize.y; + wmPtr->defMaxWidth = info->ptMaxTrackSize.x; + wmPtr->defMaxHeight = info->ptMaxTrackSize.y; + + GetMaxSize(wmPtr, &maxWidth, &maxHeight); + GetMinSize(wmPtr, &minWidth, &minHeight); + + if (wmPtr->gridWin != NULL) { + base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc); + if (base < 0) { + base = 0; + } + base += wmPtr->borderWidth; + info->ptMinTrackSize.x = base + (minWidth * wmPtr->widthInc); + info->ptMaxTrackSize.x = base + (maxWidth * wmPtr->widthInc); + + base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc); + if (base < 0) { + base = 0; + } + base += wmPtr->borderHeight; + info->ptMinTrackSize.y = base + (minHeight * wmPtr->heightInc); + info->ptMaxTrackSize.y = base + (maxHeight * wmPtr->heightInc); + } else { + info->ptMaxTrackSize.x = maxWidth + wmPtr->borderWidth; + info->ptMaxTrackSize.y = maxHeight + wmPtr->borderHeight; + info->ptMinTrackSize.x = minWidth + wmPtr->borderWidth; + info->ptMinTrackSize.y = minHeight + wmPtr->borderHeight; + } + + /* + * If the window isn't supposed to be resizable, then set the minimum and + * maximum dimensions to be the same as the current size. + */ + + if (!(wmPtr->flags & WM_SYNC_PENDING)) { + if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) { + info->ptMinTrackSize.x = winPtr->changes.width + + wmPtr->borderWidth; + info->ptMaxTrackSize.x = info->ptMinTrackSize.x; + } + if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) { + info->ptMinTrackSize.y = winPtr->changes.height + + wmPtr->borderHeight; + info->ptMaxTrackSize.y = info->ptMinTrackSize.y; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWinWmCleanup -- + * + * Unregisters classes registered by the window manager. This is called + * from the DLL main entry point when the DLL is unloaded. + * + * Results: + * None. + * + * Side effects: + * The window classes are discarded. + * + *---------------------------------------------------------------------- + */ + +void +TkWinWmCleanup( + HINSTANCE hInstance) +{ + ThreadSpecificData *tsdPtr; + + /* + * If we're using stubs to access the Tcl library, and they haven't been + * initialized, we can't call Tcl_GetThreadData. + */ + +#ifdef USE_TCL_STUBS + if (tclStubsPtr == NULL) { + return; + } +#endif + + if (!initialized) { + return; + } + initialized = 0; + + tsdPtr = Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + return; + } + tsdPtr->initialized = 0; + + UnregisterClass(TK_WIN_TOPLEVEL_CLASS_NAME, hInstance); +} + +/* + *-------------------------------------------------------------- + * + * TkWmNewWindow -- + * + * This function is invoked whenever a new top-level window is created. + * Its job is to initialize the WmInfo structure for the window. + * + * Results: + * None. + * + * Side effects: + * A WmInfo structure gets allocated and initialized. + * + *-------------------------------------------------------------- + */ + +void +TkWmNewWindow( + TkWindow *winPtr) /* Newly-created top-level window. */ +{ + register WmInfo *wmPtr = ckalloc(sizeof(WmInfo)); + + /* + * Initialize full structure, then set what isn't NULL + */ + + ZeroMemory(wmPtr, sizeof(WmInfo)); + winPtr->wmInfoPtr = wmPtr; + wmPtr->winPtr = winPtr; + wmPtr->hints.flags = InputHint | StateHint; + wmPtr->hints.input = True; + wmPtr->hints.initial_state = NormalState; + wmPtr->hints.icon_pixmap = None; + wmPtr->hints.icon_window = None; + wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0; + wmPtr->hints.icon_mask = None; + wmPtr->hints.window_group = None; + + /* + * Default the maximum dimensions to the size of the display. + */ + + wmPtr->defMinWidth = wmPtr->defMinHeight = 0; + wmPtr->defMaxWidth = DisplayWidth(winPtr->display, winPtr->screenNum); + wmPtr->defMaxHeight = DisplayHeight(winPtr->display, winPtr->screenNum); + wmPtr->minWidth = wmPtr->minHeight = 1; + wmPtr->maxWidth = wmPtr->maxHeight = 0; + wmPtr->widthInc = wmPtr->heightInc = 1; + wmPtr->minAspect.x = wmPtr->minAspect.y = 1; + wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1; + wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1; + wmPtr->gravity = NorthWestGravity; + wmPtr->width = -1; + wmPtr->height = -1; + wmPtr->x = winPtr->changes.x; + wmPtr->y = winPtr->changes.y; + wmPtr->crefObj = NULL; + wmPtr->colorref = (COLORREF) 0; + wmPtr->alpha = 1.0; + + wmPtr->configWidth = -1; + wmPtr->configHeight = -1; + wmPtr->flags = WM_NEVER_MAPPED; + wmPtr->nextPtr = winPtr->dispPtr->firstWmPtr; + winPtr->dispPtr->firstWmPtr = wmPtr; + + /* + * Tk must monitor structure events for top-level windows, in order to + * detect size and position changes caused by window managers. + */ + + Tk_CreateEventHandler((Tk_Window) winPtr, StructureNotifyMask, + TopLevelEventProc, winPtr); + + /* + * Arrange for geometry requests to be reflected from the window to the + * window manager. + */ + + Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * UpdateWrapper -- + * + * This function creates the wrapper window that contains the window + * decorations and menus for a toplevel. This function may be called + * after a window is mapped to change the window style. + * + * Results: + * None. + * + * Side effects: + * Destroys any old wrapper window and replaces it with a newly created + * wrapper. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateWrapper( + TkWindow *winPtr) /* Top-level window to redecorate. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + HWND parentHWND, oldWrapper = wmPtr->wrapper; + HWND child, nextHWND, focusHWND; + int x, y, width, height, state; + WINDOWPLACEMENT place; + HICON hSmallIcon = NULL; + HICON hBigIcon = NULL; + Tcl_DString titleString; + int *childStateInfo = NULL; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (winPtr->window == None) { + /* + * Ensure existence of the window to update the wrapper for. + */ + + Tk_MakeWindowExist((Tk_Window) winPtr); + } + + child = TkWinGetHWND(winPtr->window); + parentHWND = NULL; + + /* + * nextHWND will help us maintain Z order. focusHWND will help us maintain + * focus, if we had it. + */ + + nextHWND = NULL; + focusHWND = GetFocus(); + if ((oldWrapper == NULL) || (oldWrapper != GetForegroundWindow())) { + focusHWND = NULL; + } + + if (winPtr->flags & TK_EMBEDDED) { + wmPtr->wrapper = (HWND) winPtr->privatePtr; + if (wmPtr->wrapper == NULL) { + Tcl_Panic("UpdateWrapper: Cannot find container window"); + } + if (!IsWindow(wmPtr->wrapper)) { + Tcl_Panic("UpdateWrapper: Container was destroyed"); + } + } else { + /* + * Pick the decorative frame style. Override redirect windows get + * created as undecorated popups if they have no transient parent, + * otherwise they are children. This allows splash screens to operate + * as an independent window, while having dropdows (like for a + * combobox) not grab focus away from their parent. Transient windows + * get a modal dialog frame. Neither override, nor transient windows + * appear in the Windows taskbar. Note that a transient window does + * not resize by default, so we need to explicitly add the + * WS_THICKFRAME style if we want it to be resizeable. + */ + + if (winPtr->atts.override_redirect) { + wmPtr->style = WM_OVERRIDE_STYLE; + wmPtr->exStyle = EX_OVERRIDE_STYLE; + + /* + * Parent must be desktop even if we have a transient parent. + */ + + parentHWND = GetDesktopWindow(); + if (wmPtr->masterPtr) { + wmPtr->style |= WS_CHILD; + } else { + wmPtr->style |= WS_POPUP; + } + } else if (wmPtr->flags & WM_FULLSCREEN) { + wmPtr->style = WM_FULLSCREEN_STYLE; + wmPtr->exStyle = EX_FULLSCREEN_STYLE; + } else if (wmPtr->masterPtr) { + wmPtr->style = WM_TRANSIENT_STYLE; + wmPtr->exStyle = EX_TRANSIENT_STYLE; + parentHWND = Tk_GetHWND(Tk_WindowId(wmPtr->masterPtr)); + if (! ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) + && (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE))) { + wmPtr->style |= WS_THICKFRAME; + } + } else { + wmPtr->style = WM_TOPLEVEL_STYLE; + wmPtr->exStyle = EX_TOPLEVEL_STYLE; + } + + wmPtr->style |= wmPtr->styleConfig; + wmPtr->exStyle |= wmPtr->exStyleConfig; + + if ((wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) + && (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE)) { + wmPtr->style &= ~ (WS_MAXIMIZEBOX | WS_SIZEBOX); + } + + /* + * Compute the geometry of the parent and child windows. + */ + + wmPtr->flags |= WM_CREATE_PENDING|WM_MOVE_PENDING; + UpdateGeometryInfo(winPtr); + wmPtr->flags &= ~(WM_CREATE_PENDING|WM_MOVE_PENDING); + + width = wmPtr->borderWidth + winPtr->changes.width; + height = wmPtr->borderHeight + winPtr->changes.height; + + /* + * Set the initial position from the user or program specified + * location. If nothing has been specified, then let the system pick a + * location. In full screen mode the x,y origin is 0,0 and the window + * width and height match that of the screen. + */ + + if (wmPtr->flags & WM_FULLSCREEN) { + x = 0; + y = 0; + width = WidthOfScreen(Tk_Screen(winPtr)); + height = HeightOfScreen(Tk_Screen(winPtr)); + } else if (!(wmPtr->sizeHintsFlags & (USPosition | PPosition)) + && (wmPtr->flags & WM_NEVER_MAPPED)) { + x = CW_USEDEFAULT; + y = CW_USEDEFAULT; + } else { + x = winPtr->changes.x; + y = winPtr->changes.y; + } + + /* + * Create the containing window, and set the user data to point to the + * TkWindow. + */ + + tsdPtr->createWindow = winPtr; + Tcl_WinUtfToTChar(((wmPtr->title != NULL) ? + wmPtr->title : winPtr->nameUid), -1, &titleString); + + wmPtr->wrapper = CreateWindowEx(wmPtr->exStyle, + TK_WIN_TOPLEVEL_CLASS_NAME, + (LPCTSTR) Tcl_DStringValue(&titleString), + wmPtr->style, x, y, width, height, + parentHWND, NULL, Tk_GetHINSTANCE(), NULL); + Tcl_DStringFree(&titleString); + SetWindowLongPtr(wmPtr->wrapper, GWLP_USERDATA, (LONG_PTR) winPtr); + tsdPtr->createWindow = NULL; + + if (wmPtr->exStyleConfig & WS_EX_LAYERED) { + /* + * The user supplies a double from [0..1], but Windows wants an + * int (transparent) 0..255 (opaque), so do the translation. Add + * the 0.5 to round the value. + */ + + SetLayeredWindowAttributes((HWND) wmPtr->wrapper, + wmPtr->colorref, (BYTE) (wmPtr->alpha * 255 + 0.5), + (unsigned)(LWA_ALPHA | (wmPtr->crefObj?LWA_COLORKEY:0))); + } else { + /* + * Layering not used or supported. + */ + + wmPtr->alpha = 1.0; + if (wmPtr->crefObj) { + Tcl_DecrRefCount(wmPtr->crefObj); + wmPtr->crefObj = NULL; + } + } + + place.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(wmPtr->wrapper, &place); + wmPtr->x = place.rcNormalPosition.left; + wmPtr->y = place.rcNormalPosition.top; + + if (!(winPtr->flags & TK_ALREADY_DEAD)) { + TkInstallFrameMenu((Tk_Window) winPtr); + } + + if (oldWrapper && (oldWrapper != wmPtr->wrapper) + && !(wmPtr->exStyle & WS_EX_TOPMOST)) { + /* + * We will adjust wrapper to have the same Z order as oldWrapper + * if it isn't a TOPMOST window. + */ + + nextHWND = GetNextWindow(oldWrapper, GW_HWNDPREV); + } + } + + /* + * Now we need to reparent the contained window and set its style + * appropriately. Be sure to update the style first so that Windows + * doesn't try to set the focus to the child window. + */ + + SetWindowLongPtr(child, GWL_STYLE, + WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS); + + if (winPtr->flags & TK_EMBEDDED) { + SetWindowLongPtr(child, GWLP_WNDPROC, (LONG_PTR) TopLevelProc); + } + + SetParent(child, wmPtr->wrapper); + if (oldWrapper) { + hSmallIcon = (HICON) + SendMessage(oldWrapper, WM_GETICON, ICON_SMALL, (LPARAM)NULL); + hBigIcon = (HICON) + SendMessage(oldWrapper, WM_GETICON, ICON_BIG, (LPARAM) NULL); + } + + if (oldWrapper && (oldWrapper != wmPtr->wrapper) + && (oldWrapper != GetDesktopWindow())) { + SetWindowLongPtr(oldWrapper, GWLP_USERDATA, (LONG_PTR) 0); + + if (wmPtr->numTransients > 0) { + /* + * Unset the current wrapper as the parent for all transient + * children for whom this is the master + */ + + WmInfo *wmPtr2; + + childStateInfo = ckalloc(wmPtr->numTransients * sizeof(int)); + state = 0; + for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL; + wmPtr2 = wmPtr2->nextPtr) { + if (wmPtr2->masterPtr == winPtr + && !(wmPtr2->flags & WM_NEVER_MAPPED)) { + childStateInfo[state++] = wmPtr2->hints.initial_state; + SetParent(TkWinGetHWND(wmPtr2->winPtr->window), NULL); + } + } + } + + /* + * Remove the menubar before destroying the window so the menubar + * isn't destroyed. + */ + + SetMenu(oldWrapper, NULL); + DestroyWindow(oldWrapper); + } + + wmPtr->flags &= ~WM_NEVER_MAPPED; + if (winPtr->flags & TK_EMBEDDED && + SendMessage(wmPtr->wrapper, TK_ATTACHWINDOW, (WPARAM) child, 0)) { + SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, + Tk_ReqWidth((Tk_Window) winPtr), + Tk_ReqHeight((Tk_Window) winPtr)); + SendMessage(wmPtr->wrapper, TK_SETMENU, (WPARAM) wmPtr->hMenu, + (LPARAM) Tk_GetMenuHWND((Tk_Window) winPtr)); + } + + /* + * Force an initial transition from withdrawn to the real initial state. + * Set the Z order based on previous wrapper before we set the state. + */ + + state = wmPtr->hints.initial_state; + wmPtr->hints.initial_state = WithdrawnState; + if (nextHWND) { + SetWindowPos(wmPtr->wrapper, nextHWND, 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOSENDCHANGING + |SWP_NOOWNERZORDER); + } + TkpWmSetState(winPtr, state); + wmPtr->hints.initial_state = state; + + if (hSmallIcon != NULL) { + SendMessage(wmPtr->wrapper, WM_SETICON, ICON_SMALL, + (LPARAM) hSmallIcon); + } + if (hBigIcon != NULL) { + SendMessage(wmPtr->wrapper, WM_SETICON, ICON_BIG, (LPARAM) hBigIcon); + } + + /* + * If we are embedded then force a mapping of the window now, because we + * do not necessarily own the wrapper and may not get another opportunity + * to map ourselves. We should not be in either iconified or zoomed states + * when we get here, so it is safe to just check for TK_EMBEDDED without + * checking what state we are supposed to be in (default to NormalState). + */ + + if (winPtr->flags & TK_EMBEDDED) { + if (state+1 != SendMessage(wmPtr->wrapper, TK_STATE, state, 0)) { + TkpWmSetState(winPtr, NormalState); + wmPtr->hints.initial_state = NormalState; + } + XMapWindow(winPtr->display, winPtr->window); + } + + /* + * Set up menus on the wrapper if required. + */ + + if (wmPtr->hMenu != NULL) { + wmPtr->flags |= WM_SYNC_PENDING; + SetMenu(wmPtr->wrapper, wmPtr->hMenu); + wmPtr->flags &= ~WM_SYNC_PENDING; + } + + if (childStateInfo) { + if (wmPtr->numTransients > 0) { + /* + * Reset all transient children for whom this is the master. + */ + + WmInfo *wmPtr2; + + state = 0; + for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL; + wmPtr2 = wmPtr2->nextPtr) { + if (wmPtr2->masterPtr == winPtr + && !(wmPtr2->flags & WM_NEVER_MAPPED)) { + UpdateWrapper(wmPtr2->winPtr); + TkpWmSetState(wmPtr2->winPtr, childStateInfo[state++]); + } + } + } + + ckfree(childStateInfo); + } + + /* + * If this is the first window created by the application, then we should + * activate the initial window. Otherwise, if this had the focus, we need + * to restore that. + * XXX: Rewrapping generates a <FocusOut> and <FocusIn> that would best be + * XXX: avoided, if we could safely mask them. + */ + + if (tsdPtr->firstWindow) { + tsdPtr->firstWindow = 0; + SetActiveWindow(wmPtr->wrapper); + } else if (focusHWND) { + SetFocus(focusHWND); + } +} + +/* + *-------------------------------------------------------------- + * + * TkWmMapWindow -- + * + * This function is invoked to map a top-level window. This module gets a + * chance to update all window-manager-related information in properties + * before the window manager sees the map event and checks the + * properties. It also gets to decide whether or not to even map the + * window after all. + * + * Results: + * None. + * + * Side effects: + * Properties of winPtr may get updated to provide up-to-date information + * to the window manager. The window may also get mapped, but it may not + * be if this function decides that isn't appropriate (e.g. because the + * window is withdrawn). + * + *-------------------------------------------------------------- + */ + +void +TkWmMapWindow( + TkWindow *winPtr) /* Top-level window that's about to be + * mapped. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (!tsdPtr->initialized) { + InitWm(); + } + + if (wmPtr->flags & WM_NEVER_MAPPED) { + /* + * Don't map a transient if the master is not mapped. + */ + + if (wmPtr->masterPtr != NULL && !Tk_IsMapped(wmPtr->masterPtr)) { + wmPtr->hints.initial_state = WithdrawnState; + return; + } + } else { + if (wmPtr->hints.initial_state == WithdrawnState) { + return; + } + + /* + * Map the window in either the iconified or normal state. Note that + * we only send a map event if the window is in the normal state. + */ + + TkpWmSetState(winPtr, wmPtr->hints.initial_state); + } + + /* + * This is the first time this window has ever been mapped. Store all the + * window-manager-related information for the window. + */ + + UpdateWrapper(winPtr); +} + +/* + *-------------------------------------------------------------- + * + * TkWmUnmapWindow -- + * + * This function is invoked to unmap a top-level window. The only thing + * it does special is unmap the decorative frame before unmapping the + * toplevel window. + * + * Results: + * None. + * + * Side effects: + * Unmaps the decorative frame and the window. + * + *-------------------------------------------------------------- + */ + +void +TkWmUnmapWindow( + TkWindow *winPtr) /* Top-level window that's about to be + * unmapped. */ +{ + TkpWmSetState(winPtr, WithdrawnState); +} + +/* + *---------------------------------------------------------------------- + * + * TkpWmSetState -- + * + * Sets the window manager state for the wrapper window of a given + * toplevel window. + * + * Results: + * None. + * + * Side effects: + * May maximize, minimize, restore, or withdraw a window. + * + *---------------------------------------------------------------------- + */ + +int +TkpWmSetState( + TkWindow *winPtr, /* Toplevel window to operate on. */ + int state) /* One of IconicState, ZoomState, NormalState, + * or WithdrawnState. */ +{ + WmInfo *wmPtr = winPtr->wmInfoPtr; + int cmd; + + if (wmPtr->flags & WM_NEVER_MAPPED) { + wmPtr->hints.initial_state = state; + goto setStateEnd; + } + + wmPtr->flags |= WM_SYNC_PENDING; + if (state == WithdrawnState) { + cmd = SW_HIDE; + } else if (state == IconicState) { + cmd = SW_SHOWMINNOACTIVE; + } else if (state == NormalState) { + cmd = SW_SHOWNOACTIVATE; + } else if (state == ZoomState) { + cmd = SW_SHOWMAXIMIZED; + } else { + goto setStateEnd; + } + + ShowWindow(wmPtr->wrapper, cmd); + wmPtr->flags &= ~WM_SYNC_PENDING; +setStateEnd: + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * TkpWmSetFullScreen -- + * + * Sets the fullscreen state for a toplevel window. + * + * Results: + * The WM_FULLSCREEN flag is updated. + * + * Side effects: + * May create a new wrapper window and raise it. + * + *---------------------------------------------------------------------- + */ + +static void +TkpWmSetFullScreen( + TkWindow *winPtr, /* Toplevel window to operate on. */ + int full_screen_state) /* True if window should be full screen */ +{ + int changed = 0; + int full_screen = False; + WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (full_screen_state) { + if (! (wmPtr->flags & WM_FULLSCREEN)) { + full_screen = True; + changed = 1; + } + } else { + if (wmPtr->flags & WM_FULLSCREEN) { + full_screen = False; + changed = 1; + } + } + + if (changed) { + if (full_screen) { + wmPtr->flags |= WM_FULLSCREEN; + wmPtr->configX = wmPtr->x; + wmPtr->configY = wmPtr->y; + } else { + wmPtr->flags &= ~WM_FULLSCREEN; + wmPtr->x = wmPtr->configX; + wmPtr->y = wmPtr->configY; + } + + /* + * If the window has been mapped, then we need to update the native + * wrapper window, and reset the focus to the widget that had it + * before. + */ + + if (!(wmPtr->flags & (WM_NEVER_MAPPED) + && !(winPtr->flags & TK_EMBEDDED))) { + TkWindow *focusWinPtr; + + UpdateWrapper(winPtr); + + focusWinPtr = TkGetFocusWin(winPtr); + if (focusWinPtr) { + TkSetFocusWin(focusWinPtr, 1); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinGetState -- + * + * This function returns state value of a toplevel window. + * + * Results: + * none + * + * Side effects: + * May deiconify the toplevel window. + * + *---------------------------------------------------------------------- + */ + +int +TkpWmGetState( + TkWindow *winPtr) +{ + return winPtr->wmInfoPtr->hints.initial_state; +} + +/* + *-------------------------------------------------------------- + * + * TkWmDeadWindow -- + * + * This function is invoked when a top-level window is about to be + * deleted. It cleans up the wm-related data structures for the window. + * + * Results: + * None. + * + * Side effects: + * The WmInfo structure for winPtr gets freed up. + * + *-------------------------------------------------------------- + */ + +void +TkWmDeadWindow( + TkWindow *winPtr) /* Top-level window that's being deleted. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + WmInfo *wmPtr2; + + if (wmPtr == NULL) { + return; + } + + /* + * Clean up event related window info. + */ + + if (winPtr->dispPtr->firstWmPtr == wmPtr) { + winPtr->dispPtr->firstWmPtr = wmPtr->nextPtr; + } else { + register WmInfo *prevPtr; + + for (prevPtr = winPtr->dispPtr->firstWmPtr; ; + prevPtr = prevPtr->nextPtr) { + if (prevPtr == NULL) { + Tcl_Panic("couldn't unlink window in TkWmDeadWindow"); + } + if (prevPtr->nextPtr == wmPtr) { + prevPtr->nextPtr = wmPtr->nextPtr; + break; + } + } + } + + /* + * Reset all transient windows whose master is the dead window. + */ + + for (wmPtr2 = winPtr->dispPtr->firstWmPtr; wmPtr2 != NULL; + wmPtr2 = wmPtr2->nextPtr) { + if (wmPtr2->masterPtr == winPtr) { + wmPtr->numTransients--; + Tk_DeleteEventHandler((Tk_Window) wmPtr2->masterPtr, + VisibilityChangeMask|StructureNotifyMask, + WmWaitVisibilityOrMapProc, wmPtr2->winPtr); + wmPtr2->masterPtr = NULL; + if ((wmPtr2->wrapper != None) + && !(wmPtr2->flags & (WM_NEVER_MAPPED))) { + UpdateWrapper(wmPtr2->winPtr); + } + } + } + if (wmPtr->numTransients != 0) + Tcl_Panic("numTransients should be 0"); + + if (wmPtr->title != NULL) { + ckfree(wmPtr->title); + } + if (wmPtr->iconName != NULL) { + ckfree(wmPtr->iconName); + } + if (wmPtr->hints.flags & IconPixmapHint) { + Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap); + } + if (wmPtr->hints.flags & IconMaskHint) { + Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask); + } + if (wmPtr->leaderName != NULL) { + ckfree(wmPtr->leaderName); + } + if (wmPtr->icon != NULL) { + wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr; + wmPtr2->iconFor = NULL; + } + if (wmPtr->iconFor != NULL) { + wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr; + wmPtr2->icon = NULL; + wmPtr2->hints.flags &= ~IconWindowHint; + } + while (wmPtr->protPtr != NULL) { + ProtocolHandler *protPtr; + + protPtr = wmPtr->protPtr; + wmPtr->protPtr = protPtr->nextPtr; + Tcl_EventuallyFree(protPtr, TCL_DYNAMIC); + } + if (wmPtr->cmdArgv != NULL) { + ckfree(wmPtr->cmdArgv); + } + if (wmPtr->clientMachine != NULL) { + ckfree(wmPtr->clientMachine); + } + if (wmPtr->flags & WM_UPDATE_PENDING) { + Tcl_CancelIdleCall(UpdateGeometryInfo, winPtr); + } + if (wmPtr->masterPtr != NULL) { + wmPtr2 = wmPtr->masterPtr->wmInfoPtr; + + /* + * If we had a master, tell them that we aren't tied to them anymore. + */ + + if (wmPtr2 != NULL) { + wmPtr2->numTransients--; + } + Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr, + VisibilityChangeMask|StructureNotifyMask, + WmWaitVisibilityOrMapProc, winPtr); + wmPtr->masterPtr = NULL; + } + if (wmPtr->crefObj != NULL) { + Tcl_DecrRefCount(wmPtr->crefObj); + wmPtr->crefObj = NULL; + } + + /* + * Destroy the decorative frame window. + */ + + if (!(winPtr->flags & TK_EMBEDDED)) { + if (wmPtr->wrapper != NULL) { + DestroyWindow(wmPtr->wrapper); + } else if (winPtr->window) { + DestroyWindow(Tk_GetHWND(winPtr->window)); + } + } else { + if (wmPtr->wrapper != NULL) { + SendMessage(wmPtr->wrapper, TK_DETACHWINDOW, 0, 0); + } + } + if (wmPtr->iconPtr != NULL) { + /* + * This may delete the icon resource data. I believe we should do this + * after destroying the decorative frame, because the decorative frame + * is using this icon. + */ + + DecrIconRefCount(wmPtr->iconPtr); + } + + ckfree(wmPtr); + winPtr->wmInfoPtr = NULL; +} + +/* + *-------------------------------------------------------------- + * + * TkWmSetClass -- + * + * This function is invoked whenever a top-level window's class is + * changed. If the window has been mapped then this function updates the + * window manager property for the class. If the window hasn't been + * mapped, the update is deferred until just before the first mapping. + * + * Results: + * None. + * + * Side effects: + * A window property may get updated. + * + *-------------------------------------------------------------- + */ + +void +TkWmSetClass( + TkWindow *winPtr) /* Newly-created top-level window. */ +{ + /* Do nothing */ + return; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_WmObjCmd -- + * + * This function is invoked to process the "wm" Tcl command. See the user + * documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + + /* ARGSUSED */ +int +Tk_WmObjCmd( + ClientData clientData, /* Main window associated with interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tk_Window tkwin = clientData; + static const char *const optionStrings[] = { + "aspect", "attributes", "client", "colormapwindows", + "command", "deiconify", "focusmodel", "forget", "frame", + "geometry", "grid", "group", "iconbitmap", + "iconify", "iconmask", "iconname", + "iconphoto", "iconposition", + "iconwindow", "manage", "maxsize", "minsize", "overrideredirect", + "positionfrom", "protocol", "resizable", "sizefrom", + "stackorder", "state", "title", "transient", + "withdraw", NULL + }; + enum options { + WMOPT_ASPECT, WMOPT_ATTRIBUTES, WMOPT_CLIENT, WMOPT_COLORMAPWINDOWS, + WMOPT_COMMAND, WMOPT_DEICONIFY, WMOPT_FOCUSMODEL, WMOPT_FORGET, + WMOPT_FRAME, + WMOPT_GEOMETRY, WMOPT_GRID, WMOPT_GROUP, WMOPT_ICONBITMAP, + WMOPT_ICONIFY, WMOPT_ICONMASK, WMOPT_ICONNAME, + WMOPT_ICONPHOTO, WMOPT_ICONPOSITION, + WMOPT_ICONWINDOW, WMOPT_MANAGE, WMOPT_MAXSIZE, WMOPT_MINSIZE, + WMOPT_OVERRIDEREDIRECT, + WMOPT_POSITIONFROM, WMOPT_PROTOCOL, WMOPT_RESIZABLE, WMOPT_SIZEFROM, + WMOPT_STACKORDER, WMOPT_STATE, WMOPT_TITLE, WMOPT_TRANSIENT, + WMOPT_WITHDRAW + }; + int index; + size_t length; + const char *argv1; + TkWindow *winPtr, **winPtrPtr = &winPtr; + TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; + + if (objc < 2) { + wrongNumArgs: + Tcl_WrongNumArgs(interp, 1, objv, "option window ?arg ...?"); + return TCL_ERROR; + } + + argv1 = Tcl_GetString(objv[1]); + length = objv[1]->length; + if ((argv1[0] == 't') && !strncmp(argv1, "tracing", length) + && (length >= 3)) { + int wmTracing; + + if ((objc != 2) && (objc != 3)) { + Tcl_WrongNumArgs(interp, 2, objv, "?boolean?"); + return TCL_ERROR; + } + if (objc == 2) { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + dispPtr->flags & TK_DISPLAY_WM_TRACING)); + return TCL_OK; + } + if (Tcl_GetBooleanFromObj(interp, objv[2], &wmTracing) != TCL_OK) { + return TCL_ERROR; + } + if (wmTracing) { + dispPtr->flags |= TK_DISPLAY_WM_TRACING; + } else { + dispPtr->flags &= ~TK_DISPLAY_WM_TRACING; + } + return TCL_OK; + } + + if (Tcl_GetIndexFromObjStruct(interp, objv[1], optionStrings, + sizeof(char *), "option", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + + if (objc < 3) { + goto wrongNumArgs; + } + + if (TkGetWindowFromObj(interp, tkwin, objv[2], (Tk_Window *) winPtrPtr) + != TCL_OK) { + return TCL_ERROR; + } + if (!Tk_IsTopLevel(winPtr) && (index != WMOPT_MANAGE) + && (index != WMOPT_FORGET)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" isn't a top-level window", winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "LOOKUP", "TOPLEVEL", winPtr->pathName, + NULL); + return TCL_ERROR; + } + + switch ((enum options) index) { + case WMOPT_ASPECT: + return WmAspectCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ATTRIBUTES: + return WmAttributesCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_CLIENT: + return WmClientCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_COLORMAPWINDOWS: + return WmColormapwindowsCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_COMMAND: + return WmCommandCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_DEICONIFY: + return WmDeiconifyCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_FOCUSMODEL: + return WmFocusmodelCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_FORGET: + return WmForgetCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_FRAME: + return WmFrameCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_GEOMETRY: + return WmGeometryCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_GRID: + return WmGridCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_GROUP: + return WmGroupCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONBITMAP: + return WmIconbitmapCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONIFY: + return WmIconifyCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONMASK: + return WmIconmaskCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONNAME: + return WmIconnameCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONPHOTO: + return WmIconphotoCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONPOSITION: + return WmIconpositionCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_ICONWINDOW: + return WmIconwindowCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_MANAGE: + return WmManageCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_MAXSIZE: + return WmMaxsizeCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_MINSIZE: + return WmMinsizeCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_OVERRIDEREDIRECT: + return WmOverrideredirectCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_POSITIONFROM: + return WmPositionfromCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_PROTOCOL: + return WmProtocolCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_RESIZABLE: + return WmResizableCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_SIZEFROM: + return WmSizefromCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_STACKORDER: + return WmStackorderCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_STATE: + return WmStateCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_TITLE: + return WmTitleCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_TRANSIENT: + return WmTransientCmd(tkwin, winPtr, interp, objc, objv); + case WMOPT_WITHDRAW: + return WmWithdrawCmd(tkwin, winPtr, interp, objc, objv); + } + + /* This should not happen */ + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * WmAspectCmd -- + * + * This function is invoked to process the "wm aspect" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmAspectCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int numer1, denom1, numer2, denom2; + + if ((objc != 3) && (objc != 7)) { + Tcl_WrongNumArgs(interp, 2, objv, + "window ?minNumer minDenom maxNumer maxDenom?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->sizeHintsFlags & PAspect) { + Tcl_Obj *results[4]; + + results[0] = Tcl_NewIntObj(wmPtr->minAspect.x); + results[1] = Tcl_NewIntObj(wmPtr->minAspect.y); + results[2] = Tcl_NewIntObj(wmPtr->maxAspect.x); + results[3] = Tcl_NewIntObj(wmPtr->maxAspect.y); + Tcl_SetObjResult(interp, Tcl_NewListObj(4, results)); + } + return TCL_OK; + } + if (*Tcl_GetString(objv[3]) == '\0') { + wmPtr->sizeHintsFlags &= ~PAspect; + } else { + if ((Tcl_GetIntFromObj(interp, objv[3], &numer1) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[4], &denom1) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[5], &numer2) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[6], &denom2) != TCL_OK)) { + return TCL_ERROR; + } + if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) || (denom2 <= 0)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "aspect number can't be <= 0", -1)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "ASPECT", NULL); + return TCL_ERROR; + } + wmPtr->minAspect.x = numer1; + wmPtr->minAspect.y = denom1; + wmPtr->maxAspect.x = numer2; + wmPtr->maxAspect.y = denom2; + wmPtr->sizeHintsFlags |= PAspect; + } + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmAttributesCmd -- + * + * This function is invoked to process the "wm attributes" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmAttributesCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + LONG style, exStyle, styleBit, *stylePtr = NULL; + const char *string; + int i, boolean; + size_t length; + int config_fullscreen = 0, updatewrapper = 0; + int fullscreen_attr_changed = 0, fullscreen_attr = 0; + + if ((objc < 3) || ((objc > 5) && ((objc%2) == 0))) { + configArgs: + Tcl_WrongNumArgs(interp, 2, objv, + "window" + " ?-alpha ?double??" + " ?-transparentcolor ?color??" + " ?-disabled ?bool??" + " ?-fullscreen ?bool??" + " ?-toolwindow ?bool??" + " ?-topmost ?bool??"); + return TCL_ERROR; + } + exStyle = wmPtr->exStyleConfig; + style = wmPtr->styleConfig; + if (objc == 3) { + Tcl_Obj *objPtr = Tcl_NewObj(); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-alpha", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, Tcl_NewDoubleObj(wmPtr->alpha)); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-transparentcolor", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, + wmPtr->crefObj ? wmPtr->crefObj : Tcl_NewObj()); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-disabled", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewBooleanObj((style & WS_DISABLED))); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-fullscreen", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewBooleanObj((wmPtr->flags & WM_FULLSCREEN))); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-toolwindow", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewBooleanObj((exStyle & WS_EX_TOOLWINDOW))); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewStringObj("-topmost", -1)); + Tcl_ListObjAppendElement(NULL, objPtr, + Tcl_NewBooleanObj((exStyle & WS_EX_TOPMOST))); + Tcl_SetObjResult(interp, objPtr); + return TCL_OK; + } + for (i = 3; i < objc; i += 2) { + string = Tcl_GetString(objv[i]); + length = objv[i]->length; + if ((length < 2) || (string[0] != '-')) { + goto configArgs; + } + if (strncmp(string, "-disabled", length) == 0) { + stylePtr = &style; + styleBit = WS_DISABLED; + } else if ((strncmp(string, "-alpha", length) == 0) + || ((length > 2) && (strncmp(string, "-transparentcolor", + length) == 0))) { + stylePtr = &exStyle; + styleBit = WS_EX_LAYERED; + } else if (strncmp(string, "-fullscreen", length) == 0) { + config_fullscreen = 1; + styleBit = 0; + } else if ((length > 3) + && (strncmp(string, "-toolwindow", length) == 0)) { + stylePtr = &exStyle; + styleBit = WS_EX_TOOLWINDOW; + if (objc != 4) { + /* + * Changes to toolwindow style require an update + */ + updatewrapper = 1; + } + } else if ((length > 3) + && (strncmp(string, "-topmost", length) == 0)) { + stylePtr = &exStyle; + styleBit = WS_EX_TOPMOST; + if ((i < objc-1) && (winPtr->flags & TK_EMBEDDED)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't set topmost flag on %s: it is an embedded window", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ATTR", "TOPMOST", NULL); + return TCL_ERROR; + } + } else { + goto configArgs; + } + if (styleBit == WS_EX_LAYERED) { + if (objc == 4) { + if (string[1] == 'a') { /* -alpha */ + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(wmPtr->alpha)); + } else { /* -transparentcolor */ + Tcl_SetObjResult(interp, + wmPtr->crefObj ? wmPtr->crefObj : Tcl_NewObj()); + } + } else { + if (string[1] == 'a') { /* -alpha */ + double dval; + + if (Tcl_GetDoubleFromObj(interp, objv[i+1], &dval) + != TCL_OK) { + return TCL_ERROR; + } + + /* + * The user should give (transparent) 0 .. 1.0 (opaque), + * but we ignore the setting of this (it will always be 1) + * in the case that the API is not available. + */ + if (dval < 0.0) { + dval = 0; + } else if (dval > 1.0) { + dval = 1; + } + wmPtr->alpha = dval; + } else { /* -transparentcolor */ + const char *crefstr = Tcl_GetString(objv[i+1]); + + length = objv[i+1]->length; + if (length == 0) { + /* reset to no transparent color */ + if (wmPtr->crefObj) { + Tcl_DecrRefCount(wmPtr->crefObj); + wmPtr->crefObj = NULL; + } + } else { + XColor *cPtr = + Tk_GetColor(interp, tkwin, Tk_GetUid(crefstr)); + if (cPtr == NULL) { + return TCL_ERROR; + } + + if (wmPtr->crefObj) { + Tcl_DecrRefCount(wmPtr->crefObj); + } + wmPtr->crefObj = objv[i+1]; + Tcl_IncrRefCount(wmPtr->crefObj); + wmPtr->colorref = RGB((BYTE) (cPtr->red >> 8), + (BYTE) (cPtr->green >> 8), + (BYTE) (cPtr->blue >> 8)); + Tk_FreeColor(cPtr); + } + } + + /* + * Only ever add the WS_EX_LAYERED bit, as it can cause + * flashing to change this window style. This allows things + * like fading tooltips to avoid flash ugliness without + * forcing all window to be layered. + */ + + if ((wmPtr->alpha < 1.0) || (wmPtr->crefObj != NULL)) { + *stylePtr |= styleBit; + } + if (wmPtr->wrapper != NULL) { + /* + * Set the window directly regardless of UpdateWrapper. + * The user supplies a double from [0..1], but Windows + * wants an int (transparent) 0..255 (opaque), so do the + * translation. Add the 0.5 to round the value. + */ + + if (!(wmPtr->exStyleConfig & WS_EX_LAYERED)) { + SetWindowLongPtr(wmPtr->wrapper, GWL_EXSTYLE, + *stylePtr); + } + SetLayeredWindowAttributes((HWND) wmPtr->wrapper, + wmPtr->colorref, (BYTE) (wmPtr->alpha * 255 + 0.5), + (unsigned) (LWA_ALPHA | + (wmPtr->crefObj ? LWA_COLORKEY : 0))); + } + } + } else { + if ((i < objc-1) + && Tcl_GetBooleanFromObj(interp, objv[i+1], &boolean) + != TCL_OK) { + return TCL_ERROR; + } + if (config_fullscreen) { + if (objc == 4) { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj( + wmPtr->flags & WM_FULLSCREEN)); + } else { + fullscreen_attr_changed = 1; + fullscreen_attr = boolean; + } + config_fullscreen = 0; + } else if (objc == 4) { + Tcl_SetObjResult(interp, + Tcl_NewBooleanObj(*stylePtr & styleBit)); + } else if (boolean) { + *stylePtr |= styleBit; + } else { + *stylePtr &= ~styleBit; + } + } + if ((styleBit == WS_EX_TOPMOST) && (wmPtr->wrapper != NULL)) { + /* + * Force the topmost position aspect to ensure that switching + * between (no)topmost reflects properly when rewrapped. + */ + + SetWindowPos(wmPtr->wrapper, + ((exStyle & WS_EX_TOPMOST) ? + HWND_TOPMOST : HWND_NOTOPMOST), 0, 0, 0, 0, + SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE|SWP_NOSENDCHANGING + |SWP_NOOWNERZORDER); + } + } + if (wmPtr->styleConfig != style) { + /* + * Currently this means only WS_DISABLED changed, which we can effect + * with EnableWindow. + */ + + wmPtr->styleConfig = style; + if ((wmPtr->exStyleConfig == exStyle) + && !(wmPtr->flags & WM_NEVER_MAPPED)) { + EnableWindow(wmPtr->wrapper, (style & WS_DISABLED) ? 0 : 1); + } + } + if (wmPtr->exStyleConfig != exStyle) { + wmPtr->exStyleConfig = exStyle; + if (updatewrapper) { + /* + * UpdateWrapper ensure that all effects are properly handled, + * such as TOOLWINDOW disappearing from the taskbar. + */ + + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + UpdateWrapper(winPtr); + } + } + } + if (fullscreen_attr_changed) { + if (fullscreen_attr) { + if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't set fullscreen attribute for \"%s\":" + " override-redirect flag is set", winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ATTR", + "OVERRIDE_REDIRECT", NULL); + return TCL_ERROR; + } + + /* + * Check max width and height if set by the user, don't worry + * about the default values since they will likely be smaller than + * screen width/height. + */ + + if (((wmPtr->maxWidth > 0) && + (WidthOfScreen(Tk_Screen(winPtr)) > wmPtr->maxWidth)) || + ((wmPtr->maxHeight > 0) && + (HeightOfScreen(Tk_Screen(winPtr)) > wmPtr->maxHeight))) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't set fullscreen attribute for \"%s\":" + " max width/height is too small", winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ATTR", "SMALL_MAX", NULL); + return TCL_ERROR; + } + } + + TkpWmSetFullScreen(winPtr, fullscreen_attr); + } + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmClientCmd -- + * + * This function is invoked to process the "wm client" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmClientCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + const char *argv3; + size_t length; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?name?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->clientMachine != NULL) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj(wmPtr->clientMachine, -1)); + } + return TCL_OK; + } + argv3 = Tcl_GetString(objv[3]); + length = objv[3]->length; + if (argv3[0] == 0) { + if (wmPtr->clientMachine != NULL) { + ckfree(wmPtr->clientMachine); + wmPtr->clientMachine = NULL; + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + XDeleteProperty(winPtr->display, winPtr->window, + Tk_InternAtom((Tk_Window) winPtr,"WM_CLIENT_MACHINE")); + } + } + return TCL_OK; + } + if (wmPtr->clientMachine != NULL) { + ckfree(wmPtr->clientMachine); + } + wmPtr->clientMachine = ckalloc(length + 1); + memcpy(wmPtr->clientMachine, argv3, length + 1); + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + XTextProperty textProp; + + if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp) + != 0) { + XSetWMClientMachine(winPtr->display, winPtr->window, + &textProp); + XFree((char *) textProp.value); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmColormapwindowsCmd -- + * + * This function is invoked to process the "wm colormapwindows" Tcl + * command. See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmColormapwindowsCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + TkWindow **cmapList, *winPtr2, **winPtr2Ptr = &winPtr2; + int i, windowObjc, gotToplevel; + Tcl_Obj **windowObjv, *resultObj; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?windowList?"); + return TCL_ERROR; + } + if (objc == 3) { + Tk_MakeWindowExist((Tk_Window) winPtr); + resultObj = Tcl_NewObj(); + for (i = 0; i < wmPtr->cmapCount; i++) { + if ((i == (wmPtr->cmapCount-1)) + && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) { + break; + } + Tcl_ListObjAppendElement(NULL, resultObj, + TkNewWindowObj((Tk_Window) wmPtr->cmapList[i])); + } + Tcl_SetObjResult(interp, resultObj); + return TCL_OK; + } + if (Tcl_ListObjGetElements(interp, objv[3], &windowObjc, &windowObjv) + != TCL_OK) { + return TCL_ERROR; + } + cmapList = ckalloc((windowObjc + 1) * sizeof(TkWindow*)); + gotToplevel = 0; + for (i = 0; i < windowObjc; i++) { + if (TkGetWindowFromObj(interp, tkwin, windowObjv[i], + (Tk_Window *) winPtr2Ptr) != TCL_OK) { + ckfree(cmapList); + return TCL_ERROR; + } + if (winPtr2 == winPtr) { + gotToplevel = 1; + } + if (winPtr2->window == None) { + Tk_MakeWindowExist((Tk_Window) winPtr2); + } + cmapList[i] = winPtr2; + } + if (!gotToplevel) { + wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP; + cmapList[windowObjc] = winPtr; + windowObjc++; + } else { + wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP; + } + wmPtr->flags |= WM_COLORMAPS_EXPLICIT; + if (wmPtr->cmapList != NULL) { + ckfree(wmPtr->cmapList); + } + wmPtr->cmapList = cmapList; + wmPtr->cmapCount = windowObjc; + + /* + * Now we need to force the updated colormaps to be installed. + */ + + if (wmPtr == winPtr->dispPtr->foregroundWmPtr) { + InstallColormaps(wmPtr->wrapper, WM_QUERYNEWPALETTE, 1); + } else { + InstallColormaps(wmPtr->wrapper, WM_PALETTECHANGED, 0); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmCommandCmd -- + * + * This function is invoked to process the "wm command" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmCommandCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + const char *argv3; + int cmdArgc; + const char **cmdArgv; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?value?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->cmdArgv != NULL) { + char *merged = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv); + + Tcl_SetObjResult(interp, Tcl_NewStringObj(merged, -1)); + ckfree(merged); + } + return TCL_OK; + } + argv3 = Tcl_GetString(objv[3]); + if (argv3[0] == 0) { + if (wmPtr->cmdArgv != NULL) { + ckfree(wmPtr->cmdArgv); + wmPtr->cmdArgv = NULL; + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + XDeleteProperty(winPtr->display, winPtr->window, + Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND")); + } + } + return TCL_OK; + } + if (Tcl_SplitList(interp, argv3, &cmdArgc, &cmdArgv) != TCL_OK) { + return TCL_ERROR; + } + if (wmPtr->cmdArgv != NULL) { + ckfree(wmPtr->cmdArgv); + } + wmPtr->cmdArgc = cmdArgc; + wmPtr->cmdArgv = cmdArgv; + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + XSetCommand(winPtr->display, winPtr->window, (char **) cmdArgv, cmdArgc); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmDeiconifyCmd -- + * + * This function is invoked to process the "wm deiconify" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmDeiconifyCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window"); + return TCL_ERROR; + } + if (wmPtr->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't deiconify %s: it is an icon for %s", + Tcl_GetString(objv[2]), Tk_PathName(wmPtr->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "DEICONIFY", "ICON", NULL); + return TCL_ERROR; + } + if (winPtr->flags & TK_EMBEDDED) { + if (!SendMessage(wmPtr->wrapper, TK_DEICONIFY, 0, 0)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't deiconify %s: the container does not support the request", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "COMMUNICATION", NULL); + return TCL_ERROR; + } + return TCL_OK; + } + TkpWinToplevelDeiconify(winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmFocusmodelCmd -- + * + * This function is invoked to process the "wm focusmodel" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmFocusmodelCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + static const char *const optionStrings[] = { + "active", "passive", NULL + }; + enum options { + OPT_ACTIVE, OPT_PASSIVE + }; + int index; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?active|passive?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + wmPtr->hints.input ? "passive" : "active", -1)); + return TCL_OK; + } + + if (Tcl_GetIndexFromObjStruct(interp, objv[3], optionStrings, + sizeof(char *), "argument", 0,&index) != TCL_OK) { + return TCL_ERROR; + } + if (index == OPT_ACTIVE) { + wmPtr->hints.input = False; + } else { /* OPT_PASSIVE */ + wmPtr->hints.input = True; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmForgetCmd -- + * + * This procedure is invoked to process the "wm forget" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmForgetCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel or Frame to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register Tk_Window frameWin = (Tk_Window) winPtr; + + if (Tk_IsTopLevel(frameWin)) { + Tk_UnmapWindow(frameWin); + winPtr->flags &= ~(TK_TOP_HIERARCHY|TK_TOP_LEVEL|TK_HAS_WRAPPER|TK_WIN_MANAGED); + Tk_MakeWindowExist((Tk_Window)winPtr->parentPtr); + RemapWindows(winPtr, Tk_GetHWND(winPtr->parentPtr->window)); + + /* + * Make sure wm no longer manages this window + */ + Tk_ManageGeometry(frameWin, NULL, NULL); + + TkWmDeadWindow(winPtr); + /* flags (above) must be cleared before calling */ + /* TkMapTopFrame (below) */ + TkMapTopFrame(frameWin); + } else { + /* Already not managed by wm - ignore it */ + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmFrameCmd -- + * + * This function is invoked to process the "wm frame" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmFrameCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + HWND hwnd; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window"); + return TCL_ERROR; + } + if (Tk_WindowId((Tk_Window) winPtr) == None) { + Tk_MakeWindowExist((Tk_Window) winPtr); + } + hwnd = wmPtr->wrapper; + if (hwnd == NULL) { + hwnd = Tk_GetHWND(Tk_WindowId((Tk_Window) winPtr)); + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf("0x%x", PTR2INT(hwnd))); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmGeometryCmd -- + * + * This function is invoked to process the "wm geometry" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmGeometryCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + char xSign, ySign; + int width, height; + const char *argv3; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?newGeometry?"); + return TCL_ERROR; + } + + if (objc == 3) { + xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+'; + ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+'; + if (wmPtr->gridWin != NULL) { + width = wmPtr->reqGridWidth + (winPtr->changes.width + - winPtr->reqWidth)/wmPtr->widthInc; + height = wmPtr->reqGridHeight + (winPtr->changes.height + - winPtr->reqHeight)/wmPtr->heightInc; + } else { + width = winPtr->changes.width; + height = winPtr->changes.height; + } + if (winPtr->flags & TK_EMBEDDED) { + int result = SendMessage(wmPtr->wrapper, TK_MOVEWINDOW, -1, -1); + + wmPtr->x = result >> 16; + wmPtr->y = result & 0x0000ffff; + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf("%dx%d%c%d%c%d", + width, height, xSign, wmPtr->x, ySign, wmPtr->y)); + return TCL_OK; + } + + argv3 = Tcl_GetString(objv[3]); + if (*argv3 == '\0') { + wmPtr->width = -1; + wmPtr->height = -1; + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; + } + return ParseGeometry(interp, argv3, winPtr); +} + +/* + *---------------------------------------------------------------------- + * + * WmGridCmd -- + * + * This function is invoked to process the "wm grid" Tcl command. See the + * user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmGridCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int reqWidth, reqHeight, widthInc, heightInc; + + if ((objc != 3) && (objc != 7)) { + Tcl_WrongNumArgs(interp, 2, objv, + "window ?baseWidth baseHeight widthInc heightInc?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->sizeHintsFlags & PBaseSize) { + Tcl_Obj *results[4]; + + results[0] = Tcl_NewIntObj(wmPtr->reqGridWidth); + results[1] = Tcl_NewIntObj(wmPtr->reqGridHeight); + results[2] = Tcl_NewIntObj(wmPtr->widthInc); + results[3] = Tcl_NewIntObj(wmPtr->heightInc); + Tcl_SetObjResult(interp, Tcl_NewListObj(4, results)); + } + return TCL_OK; + } + if (*Tcl_GetString(objv[3]) == '\0') { + /* + * Turn off gridding and reset the width and height to make sense as + * ungridded numbers. + */ + + wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc); + if (wmPtr->width != -1) { + wmPtr->width = winPtr->reqWidth + (wmPtr->width + - wmPtr->reqGridWidth)*wmPtr->widthInc; + wmPtr->height = winPtr->reqHeight + (wmPtr->height + - wmPtr->reqGridHeight)*wmPtr->heightInc; + } + wmPtr->widthInc = 1; + wmPtr->heightInc = 1; + } else { + if ((Tcl_GetIntFromObj(interp, objv[3], &reqWidth) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[4], &reqHeight) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[5], &widthInc) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[6], &heightInc) != TCL_OK)) { + return TCL_ERROR; + } + if (reqWidth < 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "baseWidth can't be < 0", -1)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "GRID", NULL); + return TCL_ERROR; + } + if (reqHeight < 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "baseHeight can't be < 0", -1)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "GRID", NULL); + return TCL_ERROR; + } + if (widthInc <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "widthInc can't be <= 0", -1)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "GRID", NULL); + return TCL_ERROR; + } + if (heightInc <= 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "heightInc can't be <= 0", -1)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "GRID", NULL); + return TCL_ERROR; + } + Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc, + heightInc); + } + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmGroupCmd -- + * + * This function is invoked to process the "wm group" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmGroupCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + Tk_Window tkwin2; + const char *argv3; + size_t length; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->hints.flags & WindowGroupHint) { + Tcl_SetObjResult(interp, Tcl_NewStringObj(wmPtr->leaderName, -1)); + } + return TCL_OK; + } + argv3 = Tcl_GetString(objv[3]); + length = objv[3]->length; + if (*argv3 == '\0') { + wmPtr->hints.flags &= ~WindowGroupHint; + if (wmPtr->leaderName != NULL) { + ckfree(wmPtr->leaderName); + } + wmPtr->leaderName = NULL; + } else { + if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) { + return TCL_ERROR; + } + Tk_MakeWindowExist(tkwin2); + if (wmPtr->leaderName != NULL) { + ckfree(wmPtr->leaderName); + } + wmPtr->hints.window_group = Tk_WindowId(tkwin2); + wmPtr->hints.flags |= WindowGroupHint; + wmPtr->leaderName = ckalloc(length + 1); + memcpy(wmPtr->leaderName, argv3, length + 1); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconbitmapCmd -- + * + * This function is invoked to process the "wm iconbitmap" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconbitmapCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + TkWindow *useWinPtr = winPtr; /* window to apply to (NULL if -default) */ + const char *string; + + if ((objc < 3) || (objc > 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?-default? ?image?"); + return TCL_ERROR; + } else if (objc == 5) { + /* + * If we have 5 arguments, we must have a '-default' flag. + */ + + const char *argv3 = Tcl_GetString(objv[3]); + + if (strcmp(argv3, "-default")) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "illegal option \"%s\" must be \"-default\"", argv3)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONBITMAP", "OPTION",NULL); + return TCL_ERROR; + } + useWinPtr = NULL; + } else if (objc == 3) { + /* + * No arguments were given. + */ + + if (wmPtr->hints.flags & IconPixmapHint) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_pixmap), + -1)); + } + return TCL_OK; + } + + string = Tcl_GetString(objv[objc-1]); + if (*string == '\0') { + if (wmPtr->hints.icon_pixmap != None) { + Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap); + wmPtr->hints.icon_pixmap = None; + } + wmPtr->hints.flags &= ~IconPixmapHint; + if (WinSetIcon(interp, NULL, (Tk_Window) useWinPtr) != TCL_OK) { + return TCL_ERROR; + } + } else { + /* + * In the future this block of code will use Tk's 'image' + * functionality to allow all supported image formats. However, this + * will require a change to the way icons are handled. We will need to + * add icon<->image conversions routines. + * + * Until that happens we simply try to find an icon in the given + * argument, and if that fails, we use the older bitmap code. We do + * things this way round (icon then bitmap), because the bitmap code + * actually seems to have no visible effect, so we want to give the + * icon code the first try at doing something. + */ + + /* + * Either return NULL, or return a valid titlebaricon with its ref + * count already incremented. + */ + + WinIconPtr titlebaricon = ReadIconFromFile(interp, objv[objc-1]); + if (titlebaricon != NULL) { + /* + * Try to set the icon for the window. If it is a '-default' icon, + * we must pass in NULL + */ + + if (WinSetIcon(interp, titlebaricon, (Tk_Window) useWinPtr) + != TCL_OK) { + /* + * We didn't use the titlebaricon after all. + */ + + DecrIconRefCount(titlebaricon); + titlebaricon = NULL; + } + } + if (titlebaricon == NULL) { + /* + * We didn't manage to handle the argument as a valid icon. Try as + * a bitmap. First we must clear the error message which was + * placed in the interpreter. + */ + + Pixmap pixmap; + + Tcl_ResetResult(interp); + pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr, string); + if (pixmap == None) { + return TCL_ERROR; + } + wmPtr->hints.icon_pixmap = pixmap; + wmPtr->hints.flags |= IconPixmapHint; + titlebaricon = GetIconFromPixmap(Tk_Display(winPtr), pixmap); + if (titlebaricon != NULL && WinSetIcon(interp, titlebaricon, + (Tk_Window) useWinPtr) != TCL_OK) { + /* + * We didn't use the titlebaricon after all. + */ + + DecrIconRefCount(titlebaricon); + titlebaricon = NULL; + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconifyCmd -- + * + * This function is invoked to process the "wm iconify" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconifyCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window"); + return TCL_ERROR; + } + if (winPtr->flags & TK_EMBEDDED) { + if (!SendMessage(wmPtr->wrapper, TK_ICONIFY, 0, 0)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify %s: the container does not support the request", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONIFY", "EMBEDDED", NULL); + return TCL_ERROR; + } + } + if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify \"%s\": override-redirect flag is set", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONIFY", "OVERRIDE_REDIRECT", + NULL); + return TCL_ERROR; + } + if (wmPtr->masterPtr != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify \"%s\": it is a transient", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONIFY", "TRANSIENT", NULL); + return TCL_ERROR; + } + if (wmPtr->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify %s: it is an icon for %s", + winPtr->pathName, Tk_PathName(wmPtr->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONIFY", "ICON", NULL); + return TCL_ERROR; + } + TkpWmSetState(winPtr, IconicState); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconmaskCmd -- + * + * This function is invoked to process the "wm iconmask" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconmaskCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + Pixmap pixmap; + const char *argv3; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?bitmap?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->hints.flags & IconMaskHint) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tk_NameOfBitmap(winPtr->display, wmPtr->hints.icon_mask), + -1)); + } + return TCL_OK; + } + argv3 = Tcl_GetString(objv[3]); + if (*argv3 == '\0') { + if (wmPtr->hints.icon_mask != None) { + Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask); + } + wmPtr->hints.flags &= ~IconMaskHint; + } else { + pixmap = Tk_GetBitmap(interp, tkwin, argv3); + if (pixmap == None) { + return TCL_ERROR; + } + wmPtr->hints.icon_mask = pixmap; + wmPtr->hints.flags |= IconMaskHint; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconnameCmd -- + * + * This function is invoked to process the "wm iconname" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconnameCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + const char *argv3; + size_t length; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?newName?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + (wmPtr->iconName ? wmPtr->iconName : ""), -1)); + return TCL_OK; + } else { + if (wmPtr->iconName != NULL) { + ckfree(wmPtr->iconName); + } + argv3 = Tcl_GetString(objv[3]); + length = objv[3]->length; + wmPtr->iconName = ckalloc(length + 1); + memcpy(wmPtr->iconName, argv3, length + 1); + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + XSetIconName(winPtr->display, winPtr->window, wmPtr->iconName); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconphotoCmd -- + * + * This function is invoked to process the "wm iconphoto" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconphotoCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + TkWindow *useWinPtr = winPtr; /* window to apply to (NULL if -default) */ + Tk_PhotoHandle photo; + Tk_PhotoImageBlock block; + int i, width, height, idx, bufferSize, startObj = 3; + union {unsigned char *ptr; void *voidPtr;} bgraPixel; + union {unsigned char *ptr; void *voidPtr;} bgraMask; + BlockOfIconImagesPtr lpIR; + WinIconPtr titlebaricon = NULL; + HICON hIcon; + unsigned size; + BITMAPINFO bmInfo; + ICONINFO iconInfo; + + if (objc < 4) { + Tcl_WrongNumArgs(interp, 2, objv, + "window ?-default? image1 ?image2 ...?"); + return TCL_ERROR; + } + + /* + * Iterate over all images to validate their existence. + */ + + if (strcmp(Tcl_GetString(objv[3]), "-default") == 0) { + useWinPtr = NULL; + startObj = 4; + if (objc == 4) { + Tcl_WrongNumArgs(interp, 2, objv, + "window ?-default? image1 ?image2 ...?"); + return TCL_ERROR; + } + } + for (i = startObj; i < objc; i++) { + photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i])); + if (photo == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't use \"%s\" as iconphoto: not a photo image", + Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONPHOTO", "PHOTO", NULL); + return TCL_ERROR; + } + } + + /* + * We have calculated the size of the data. Try to allocate the needed + * memory space. + */ + + size = sizeof(BlockOfIconImages) + (sizeof(ICONIMAGE) * (objc-startObj-1)); + lpIR = attemptckalloc(size); + if (lpIR == NULL) { + return TCL_ERROR; + } + ZeroMemory(lpIR, size); + + lpIR->nNumImages = objc - startObj; + + for (i = startObj; i < objc; i++) { + photo = Tk_FindPhoto(interp, Tcl_GetString(objv[i])); + Tk_PhotoGetSize(photo, &width, &height); + Tk_PhotoGetImage(photo, &block); + + /* + * Don't use CreateIcon to create the icon, as it requires color + * bitmap data in device-dependent format. Instead we use + * CreateIconIndirect which takes device-independent bitmaps and + * converts them as required. Initialise icon info structure. + */ + + ZeroMemory(&iconInfo, sizeof(iconInfo)); + iconInfo.fIcon = TRUE; + + /* + * Create device-independant color bitmap. + */ + + ZeroMemory(&bmInfo, sizeof bmInfo); + bmInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bmInfo.bmiHeader.biWidth = width; + bmInfo.bmiHeader.biHeight = -height; + bmInfo.bmiHeader.biPlanes = 1; + bmInfo.bmiHeader.biBitCount = 32; + bmInfo.bmiHeader.biCompression = BI_RGB; + + iconInfo.hbmColor = CreateDIBSection(NULL, &bmInfo, DIB_RGB_COLORS, + &bgraPixel.voidPtr, NULL, 0); + if (!iconInfo.hbmColor) { + ckfree(lpIR); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "failed to create color bitmap for \"%s\"", + Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONPHOTO", "BITMAP", NULL); + return TCL_ERROR; + } + + /* + * Convert the photo image data into BGRA format (RGBQUAD). + */ + + bufferSize = height * width * 4; + for (idx = 0 ; idx < bufferSize ; idx += 4) { + bgraPixel.ptr[idx] = block.pixelPtr[idx+2]; + bgraPixel.ptr[idx+1] = block.pixelPtr[idx+1]; + bgraPixel.ptr[idx+2] = block.pixelPtr[idx+0]; + bgraPixel.ptr[idx+3] = block.pixelPtr[idx+3]; + } + + /* + * Create a dummy mask bitmap. The contents of this don't appear to + * matter, as CreateIconIndirect will setup the icon mask based on the + * alpha channel in our color bitmap. + */ + + bmInfo.bmiHeader.biBitCount = 1; + + iconInfo.hbmMask = CreateDIBSection(NULL, &bmInfo, DIB_RGB_COLORS, + &bgraMask.voidPtr, NULL, 0); + if (!iconInfo.hbmMask) { + DeleteObject(iconInfo.hbmColor); + ckfree(lpIR); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "failed to create mask bitmap for \"%s\"", + Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONPHOTO", "MASK", NULL); + return TCL_ERROR; + } + + ZeroMemory(bgraMask.ptr, width*height/8); + + /* + * Create an icon from the bitmaps. + */ + + hIcon = CreateIconIndirect(&iconInfo); + DeleteObject(iconInfo.hbmColor); + DeleteObject(iconInfo.hbmMask); + if (hIcon == NULL) { + /* + * XXX should free up created icons. + */ + + ckfree(lpIR); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "failed to create icon for \"%s\"", + Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONPHOTO", "ICON", NULL); + return TCL_ERROR; + } + lpIR->IconImages[i-startObj].Width = width; + lpIR->IconImages[i-startObj].Height = height; + lpIR->IconImages[i-startObj].Colors = 4; + lpIR->IconImages[i-startObj].hIcon = hIcon; + } + + titlebaricon = ckalloc(sizeof(WinIconInstance)); + titlebaricon->iconBlock = lpIR; + titlebaricon->refCount = 1; + if (WinSetIcon(interp, titlebaricon, (Tk_Window) useWinPtr) != TCL_OK) { + /* + * We didn't use the titlebaricon after all. + */ + + DecrIconRefCount(titlebaricon); + return TCL_ERROR; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconpositionCmd -- + * + * This function is invoked to process the "wm iconposition" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconpositionCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int x, y; + + if ((objc != 3) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?x y?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->hints.flags & IconPositionHint) { + Tcl_Obj *results[2]; + + results[0] = Tcl_NewIntObj(wmPtr->hints.icon_x); + results[1] = Tcl_NewIntObj(wmPtr->hints.icon_y); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, results)); + } + return TCL_OK; + } + if (*Tcl_GetString(objv[3]) == '\0') { + wmPtr->hints.flags &= ~IconPositionHint; + } else { + if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)) { + return TCL_ERROR; + } + wmPtr->hints.icon_x = x; + wmPtr->hints.icon_y = y; + wmPtr->hints.flags |= IconPositionHint; + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmIconwindowCmd -- + * + * This function is invoked to process the "wm iconwindow" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmIconwindowCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + Tk_Window tkwin2; + WmInfo *wmPtr2; + XSetWindowAttributes atts; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?pathName?"); + return TCL_ERROR; + } + if (objc == 3) { + if (wmPtr->icon != NULL) { + Tcl_SetObjResult(interp, TkNewWindowObj(wmPtr->icon)); + } + return TCL_OK; + } + if (*Tcl_GetString(objv[3]) == '\0') { + wmPtr->hints.flags &= ~IconWindowHint; + if (wmPtr->icon != NULL) { + /* + * Let the window use button events again, then remove it as icon + * window. + */ + + atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask + | ButtonPressMask; + Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts); + wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr; + wmPtr2->iconFor = NULL; + wmPtr2->hints.initial_state = WithdrawnState; + } + wmPtr->icon = NULL; + } else { + if (TkGetWindowFromObj(interp, tkwin, objv[3], &tkwin2) != TCL_OK) { + return TCL_ERROR; + } + if (!Tk_IsTopLevel(tkwin2)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't use %s as icon window: not at top level", + Tcl_GetString(objv[3]))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONWINDOW", "INNER", NULL); + return TCL_ERROR; + } + wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr; + if (wmPtr2->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s is already an icon for %s", + Tcl_GetString(objv[3]), Tk_PathName(wmPtr2->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "ICONWINDOW", "ICON", NULL); + return TCL_ERROR; + } + if (wmPtr->icon != NULL) { + WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr; + wmPtr3->iconFor = NULL; + + /* + * Let the window use button events again. + */ + + atts.event_mask = Tk_Attributes(wmPtr->icon)->event_mask + | ButtonPressMask; + Tk_ChangeWindowAttributes(wmPtr->icon, CWEventMask, &atts); + } + + /* + * Disable button events in the icon window: some window managers + * (like olvwm) want to get the events themselves, but X only allows + * one application at a time to receive button events for a window. + */ + + atts.event_mask = Tk_Attributes(tkwin2)->event_mask + & ~ButtonPressMask; + Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts); + Tk_MakeWindowExist(tkwin2); + wmPtr->hints.icon_window = Tk_WindowId(tkwin2); + wmPtr->hints.flags |= IconWindowHint; + wmPtr->icon = tkwin2; + wmPtr2->iconFor = (Tk_Window) winPtr; + if (!(wmPtr2->flags & WM_NEVER_MAPPED)) { + wmPtr2->flags |= WM_WITHDRAWN; + TkpWmSetState(((TkWindow *) tkwin2), WithdrawnState); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmManageCmd -- + * + * This procedure is invoked to process the "wm manage" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmManageCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel or Frame to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register Tk_Window frameWin = (Tk_Window) winPtr; + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (!Tk_IsTopLevel(frameWin)) { + if (!Tk_IsManageable(frameWin)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" is not manageable: must be a frame," + " labelframe or toplevel", Tk_PathName(frameWin))); + Tcl_SetErrorCode(interp, "TK", "WM", "MANAGE", NULL); + return TCL_ERROR; + } + TkFocusSplit(winPtr); + Tk_UnmapWindow(frameWin); + winPtr->flags |= TK_TOP_HIERARCHY|TK_TOP_LEVEL|TK_HAS_WRAPPER|TK_WIN_MANAGED; + RemapWindows(winPtr, NULL); + if (wmPtr == NULL) { + TkWmNewWindow(winPtr); + } + wmPtr = winPtr->wmInfoPtr; + winPtr->flags &= ~TK_MAPPED; + /* flags (above) must be set before calling */ + /* TkMapTopFrame (below) */ + TkMapTopFrame (frameWin); + } else if (Tk_IsTopLevel(frameWin)) { + /* Already managed by wm - ignore it */ + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmMaxsizeCmd -- + * + * This function is invoked to process the "wm maxsize" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmMaxsizeCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int width, height; + + if ((objc != 3) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_Obj *results[2]; + + GetMaxSize(wmPtr, &width, &height); + results[0] = Tcl_NewIntObj(width); + results[1] = Tcl_NewIntObj(height); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, results)); + return TCL_OK; + } + if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) { + return TCL_ERROR; + } + wmPtr->maxWidth = width; + wmPtr->maxHeight = height; + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmMinsizeCmd -- + * + * This function is invoked to process the "wm minsize" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmMinsizeCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int width, height; + + if ((objc != 3) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_Obj *results[2]; + + GetMinSize(wmPtr, &width, &height); + results[0] = Tcl_NewIntObj(width); + results[1] = Tcl_NewIntObj(height); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, results)); + return TCL_OK; + } + if ((Tcl_GetIntFromObj(interp, objv[3], &width) != TCL_OK) + || (Tcl_GetIntFromObj(interp, objv[4], &height) != TCL_OK)) { + return TCL_ERROR; + } + wmPtr->minWidth = width; + wmPtr->minHeight = height; + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmOverrideredirectCmd -- + * + * This function is invoked to process the "wm overrideredirect" Tcl + * command. See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmOverrideredirectCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int boolean, curValue; + XSetWindowAttributes atts; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?boolean?"); + return TCL_ERROR; + } + if (winPtr->flags & TK_EMBEDDED) { + curValue = SendMessage(wmPtr->wrapper, TK_OVERRIDEREDIRECT, -1, -1)-1; + if (curValue < 0) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "Container does not support overrideredirect", -1)); + Tcl_SetErrorCode(interp, "TK", "WM", "COMMUNICATION", NULL); + return TCL_ERROR; + } + } else { + curValue = Tk_Attributes((Tk_Window) winPtr)->override_redirect; + } + if (objc == 3) { + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(curValue)); + return TCL_OK; + } + if (Tcl_GetBooleanFromObj(interp, objv[3], &boolean) != TCL_OK) { + return TCL_ERROR; + } + if (curValue != boolean) { + if (winPtr->flags & TK_EMBEDDED) { + SendMessage(wmPtr->wrapper, TK_OVERRIDEREDIRECT, boolean, 0); + } else { + /* + * Only do this if we are really changing value, because it causes + * some funky stuff to occur. + */ + + atts.override_redirect = (boolean) ? True : False; + Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect, + &atts); + if (!(wmPtr->flags & (WM_NEVER_MAPPED)) + && !(winPtr->flags & TK_EMBEDDED)) { + UpdateWrapper(winPtr); + } + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmPositionfromCmd -- + * + * This function is invoked to process the "wm positionfrom" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmPositionfromCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + static const char *const optionStrings[] = { + "program", "user", NULL + }; + enum options { + OPT_PROGRAM, OPT_USER + }; + int index; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?user/program?"); + return TCL_ERROR; + } + if (objc == 3) { + const char *sourceStr = ""; + + if (wmPtr->sizeHintsFlags & USPosition) { + sourceStr = "user"; + } else if (wmPtr->sizeHintsFlags & PPosition) { + sourceStr = "program"; + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(sourceStr, -1)); + return TCL_OK; + } + if (*Tcl_GetString(objv[3]) == '\0') { + wmPtr->sizeHintsFlags &= ~(USPosition|PPosition); + } else { + if (Tcl_GetIndexFromObjStruct(interp, objv[3], optionStrings, + sizeof(char *), "argument", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + if (index == OPT_USER) { + wmPtr->sizeHintsFlags &= ~PPosition; + wmPtr->sizeHintsFlags |= USPosition; + } else { + wmPtr->sizeHintsFlags &= ~USPosition; + wmPtr->sizeHintsFlags |= PPosition; + } + } + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmProtocolCmd -- + * + * This function is invoked to process the "wm protocol" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmProtocolCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + register ProtocolHandler *protPtr, *prevPtr; + Atom protocol; + const char *cmd; + size_t cmdLength; + Tcl_Obj *resultObj; + + if ((objc < 3) || (objc > 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?name? ?command?"); + return TCL_ERROR; + } + if (objc == 3) { + /* + * Return a list of all defined protocols for the window. + */ + + resultObj = Tcl_NewObj(); + for (protPtr = wmPtr->protPtr; protPtr != NULL; + protPtr = protPtr->nextPtr) { + Tcl_ListObjAppendElement(NULL, resultObj, Tcl_NewStringObj( + Tk_GetAtomName((Tk_Window)winPtr, protPtr->protocol), -1)); + } + Tcl_SetObjResult(interp, resultObj); + return TCL_OK; + } + protocol = Tk_InternAtom((Tk_Window) winPtr, Tcl_GetString(objv[3])); + if (objc == 4) { + /* + * Return the command to handle a given protocol. + */ + + for (protPtr = wmPtr->protPtr; protPtr != NULL; + protPtr = protPtr->nextPtr) { + if (protPtr->protocol == protocol) { + Tcl_SetObjResult(interp, + Tcl_NewStringObj(protPtr->command, -1)); + return TCL_OK; + } + } + return TCL_OK; + } + + /* + * Delete any current protocol handler, then create a new one with the + * specified command, unless the command is empty. + */ + + for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL; + prevPtr = protPtr, protPtr = protPtr->nextPtr) { + if (protPtr->protocol == protocol) { + if (prevPtr == NULL) { + wmPtr->protPtr = protPtr->nextPtr; + } else { + prevPtr->nextPtr = protPtr->nextPtr; + } + Tcl_EventuallyFree(protPtr, TCL_DYNAMIC); + break; + } + } + cmd = Tcl_GetString(objv[4]); + cmdLength = objv[4]->length; + if (cmdLength > 0) { + protPtr = ckalloc(HANDLER_SIZE(cmdLength)); + protPtr->protocol = protocol; + protPtr->nextPtr = wmPtr->protPtr; + wmPtr->protPtr = protPtr; + protPtr->interp = interp; + memcpy(protPtr->command, cmd, cmdLength + 1); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmResizableCmd -- + * + * This function is invoked to process the "wm resizable" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmResizableCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int width, height; + + if ((objc != 3) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?width height?"); + return TCL_ERROR; + } + if (objc == 3) { + Tcl_Obj *results[2]; + + results[0] = Tcl_NewBooleanObj(!(wmPtr->flags&WM_WIDTH_NOT_RESIZABLE)); + results[1] = Tcl_NewBooleanObj(!(wmPtr->flags&WM_HEIGHT_NOT_RESIZABLE)); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, results)); + return TCL_OK; + } + if ((Tcl_GetBooleanFromObj(interp, objv[3], &width) != TCL_OK) + || (Tcl_GetBooleanFromObj(interp, objv[4], &height) != TCL_OK)) { + return TCL_ERROR; + } + if (width) { + wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE; + } else { + wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE; + } + if (height) { + wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE; + } else { + wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE; + } + if (!((wmPtr->flags & WM_NEVER_MAPPED) + && !(winPtr->flags & TK_EMBEDDED))) { + UpdateWrapper(winPtr); + } + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmSizefromCmd -- + * + * This function is invoked to process the "wm sizefrom" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmSizefromCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + static const char *const optionStrings[] = { + "program", "user", NULL + }; + enum options { + OPT_PROGRAM, OPT_USER + }; + int index; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?user|program?"); + return TCL_ERROR; + } + if (objc == 3) { + const char *sourceStr = ""; + + if (wmPtr->sizeHintsFlags & USSize) { + sourceStr = "user"; + } else if (wmPtr->sizeHintsFlags & PSize) { + sourceStr = "program"; + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(sourceStr, -1)); + return TCL_OK; + } + + if (*Tcl_GetString(objv[3]) == '\0') { + wmPtr->sizeHintsFlags &= ~(USSize|PSize); + } else { + if (Tcl_GetIndexFromObjStruct(interp, objv[3], optionStrings, + sizeof(char *), "argument", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + if (index == OPT_USER) { + wmPtr->sizeHintsFlags &= ~PSize; + wmPtr->sizeHintsFlags |= USSize; + } else { /* OPT_PROGRAM */ + wmPtr->sizeHintsFlags &= ~USSize; + wmPtr->sizeHintsFlags |= PSize; + } + } + WmUpdateGeom(wmPtr, winPtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmStackorderCmd -- + * + * This function is invoked to process the "wm stackorder" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmStackorderCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + TkWindow **windows, **windowPtr; + static const char *const optionStrings[] = { + "isabove", "isbelow", NULL + }; + enum options { + OPT_ISABOVE, OPT_ISBELOW + }; + Tcl_Obj *resultObj; + int index; + + if ((objc != 3) && (objc != 5)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?isabove|isbelow window?"); + return TCL_ERROR; + } + + if (objc == 3) { + windows = TkWmStackorderToplevel(winPtr); + if (windows == NULL) { + Tcl_Panic("TkWmStackorderToplevel failed"); + } + + resultObj = Tcl_NewObj(); + for (windowPtr = windows; *windowPtr ; windowPtr++) { + Tcl_ListObjAppendElement(NULL, resultObj, + TkNewWindowObj((Tk_Window) *windowPtr)); + } + Tcl_SetObjResult(interp, resultObj); + ckfree(windows); + return TCL_OK; + } else { + TkWindow *winPtr2, **winPtr2Ptr = &winPtr2; + int index1 = -1, index2 = -1, result; + + if (TkGetWindowFromObj(interp, tkwin, objv[4], + (Tk_Window *) winPtr2Ptr) != TCL_OK) { + return TCL_ERROR; + } + + if (!Tk_IsTopLevel(winPtr2)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" isn't a top-level window", + winPtr2->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "STACK", "TOPLEVEL", NULL); + return TCL_ERROR; + } + + if (!Tk_IsMapped(winPtr)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" isn't mapped", winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "STACK", "MAPPED", NULL); + return TCL_ERROR; + } + + if (!Tk_IsMapped(winPtr2)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "window \"%s\" isn't mapped", winPtr2->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "STACK", "MAPPED", NULL); + return TCL_ERROR; + } + + /* + * Lookup stacking order of all toplevels that are children of "." and + * find the position of winPtr and winPtr2 in the stacking order. + */ + + windows = TkWmStackorderToplevel(winPtr->mainPtr->winPtr); + if (windows == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "TkWmStackorderToplevel failed", -1)); + Tcl_SetErrorCode(interp, "TK", "WM", "COMMUNICATION", NULL); + return TCL_ERROR; + } + + for (windowPtr = windows; *windowPtr ; windowPtr++) { + if (*windowPtr == winPtr) { + index1 = (windowPtr - windows); + } + if (*windowPtr == winPtr2) { + index2 = (windowPtr - windows); + } + } + if (index1 == -1) { + Tcl_Panic("winPtr window not found"); + } else if (index2 == -1) { + Tcl_Panic("winPtr2 window not found"); + } + + ckfree(windows); + + if (Tcl_GetIndexFromObjStruct(interp, objv[3], optionStrings, + sizeof(char *), "argument", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + if (index == OPT_ISABOVE) { + result = index1 > index2; + } else { /* OPT_ISBELOW */ + result = index1 < index2; + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); + return TCL_OK; + } +} + +/* + *---------------------------------------------------------------------- + * + * WmStateCmd -- + * + * This function is invoked to process the "wm state" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmStateCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + static const char *const optionStrings[] = { + "normal", "iconic", "withdrawn", "zoomed", NULL + }; + enum options { + OPT_NORMAL, OPT_ICONIC, OPT_WITHDRAWN, OPT_ZOOMED + }; + int index; + + if ((objc < 3) || (objc > 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?state?"); + return TCL_ERROR; + } + if (objc == 4) { + if (wmPtr->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't change state of %s: it is an icon for %s", + Tcl_GetString(objv[2]), Tk_PathName(wmPtr->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "STATE", "ICON", NULL); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObjStruct(interp, objv[3], optionStrings, + sizeof(char *), "argument", 0, &index) != TCL_OK) { + return TCL_ERROR; + } + + if (winPtr->flags & TK_EMBEDDED) { + int state = 0; + + switch (index) { + case OPT_NORMAL: + state = NormalState; + break; + case OPT_ICONIC: + state = IconicState; + break; + case OPT_WITHDRAWN: + state = WithdrawnState; + break; + case OPT_ZOOMED: + state = ZoomState; + break; + default: + Tcl_Panic("unexpected index"); + } + + if (state+1 != SendMessage(wmPtr->wrapper, TK_STATE, state, 0)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't change state of %s: the container does not support the request", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "COMMUNICATION", NULL); + return TCL_ERROR; + } + return TCL_OK; + } + + if (index == OPT_NORMAL) { + wmPtr->flags &= ~WM_WITHDRAWN; + TkpWmSetState(winPtr, NormalState); + + /* + * This varies from 'wm deiconify' because it does not force the + * window to be raised and receive focus. + */ + } else if (index == OPT_ICONIC) { + if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify \"%s\": override-redirect flag is set", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "STATE", + "OVERRIDE_REDIRECT", NULL); + return TCL_ERROR; + } + if (wmPtr->masterPtr != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't iconify \"%s\": it is a transient", + winPtr->pathName)); + Tcl_SetErrorCode(interp, "TK", "WM", "STATE", "TRANSIENT", + NULL); + return TCL_ERROR; + } + TkpWmSetState(winPtr, IconicState); + } else if (index == OPT_WITHDRAWN) { + wmPtr->flags |= WM_WITHDRAWN; + TkpWmSetState(winPtr, WithdrawnState); + } else if (index == OPT_ZOOMED) { + TkpWmSetState(winPtr, ZoomState); + } else { + Tcl_Panic("wm state not matched"); + } + } else { + const char *stateStr = ""; + + if (wmPtr->iconFor != NULL) { + stateStr = "icon"; + } else { + int state; + + if (winPtr->flags & TK_EMBEDDED) { + state = SendMessage(wmPtr->wrapper, TK_STATE, -1, -1) - 1; + } else { + state = wmPtr->hints.initial_state; + } + switch (state) { + case NormalState: stateStr = "normal"; break; + case IconicState: stateStr = "iconic"; break; + case WithdrawnState: stateStr = "withdrawn"; break; + case ZoomState: stateStr = "zoomed"; break; + } + } + Tcl_SetObjResult(interp, Tcl_NewStringObj(stateStr, -1)); + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmTitleCmd -- + * + * This function is invoked to process the "wm title" Tcl command. See + * the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmTitleCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + const char *argv3; + size_t length; + HWND wrapper; + + if (objc > 4) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?newTitle?"); + return TCL_ERROR; + } + + if (winPtr->flags & TK_EMBEDDED) { + wrapper = (HWND) SendMessage(wmPtr->wrapper, TK_GETFRAMEWID, 0, 0); + } else { + wrapper = wmPtr->wrapper; + } + if (objc == 3) { + if (wrapper) { + TCHAR buf[256]; + Tcl_DString titleString; + int size = 256; + + GetWindowText(wrapper, buf, size); + Tcl_WinTCharToUtf(buf, -1, &titleString); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tcl_DStringValue(&titleString), + Tcl_DStringLength(&titleString))); + Tcl_DStringFree(&titleString); + } else { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + (wmPtr->title ? wmPtr->title : winPtr->nameUid), -1)); + } + } else { + if (wmPtr->title != NULL) { + ckfree(wmPtr->title); + } + argv3 = Tcl_GetString(objv[3]); + length = objv[3]->length; + wmPtr->title = ckalloc(length + 1); + memcpy(wmPtr->title, argv3, length + 1); + + if (!(wmPtr->flags & WM_NEVER_MAPPED) && wmPtr->wrapper != NULL) { + Tcl_DString titleString; + + Tcl_WinUtfToTChar(wmPtr->title, -1, &titleString); + SetWindowText(wrapper, (LPCTSTR) Tcl_DStringValue(&titleString)); + Tcl_DStringFree(&titleString); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmTransientCmd -- + * + * This function is invoked to process the "wm transient" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmTransientCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + TkWindow *masterPtr = wmPtr->masterPtr, **masterPtrPtr = &masterPtr; + WmInfo *wmPtr2; + + if ((objc != 3) && (objc != 4)) { + Tcl_WrongNumArgs(interp, 2, objv, "window ?master?"); + return TCL_ERROR; + } + if (objc == 3) { + if (masterPtr != NULL) { + Tcl_SetObjResult(interp, TkNewWindowObj((Tk_Window) masterPtr)); + } + return TCL_OK; + } + if (Tcl_GetString(objv[3])[0] == '\0') { + if (masterPtr != NULL) { + /* + * If we had a master, tell them that we aren't tied to them + * anymore. + */ + + masterPtr->wmInfoPtr->numTransients--; + Tk_DeleteEventHandler((Tk_Window) masterPtr, + VisibilityChangeMask|StructureNotifyMask, + WmWaitVisibilityOrMapProc, winPtr); + } + + wmPtr->masterPtr = NULL; + } else { + if (TkGetWindowFromObj(interp, tkwin, objv[3], + (Tk_Window *) masterPtrPtr) != TCL_OK) { + return TCL_ERROR; + } + while (!Tk_TopWinHierarchy(masterPtr)) { + /* + * Ensure that the master window is actually a Tk toplevel. + */ + + masterPtr = masterPtr->parentPtr; + } + Tk_MakeWindowExist((Tk_Window) masterPtr); + + if (wmPtr->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't make \"%s\" a transient: it is an icon for %s", + Tcl_GetString(objv[2]), Tk_PathName(wmPtr->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "TRANSIENT", "ICON", NULL); + return TCL_ERROR; + } + + wmPtr2 = masterPtr->wmInfoPtr; + + if (wmPtr2->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't make \"%s\" a master: it is an icon for %s", + Tcl_GetString(objv[3]), Tk_PathName(wmPtr2->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "TRANSIENT", "ICON", NULL); + return TCL_ERROR; + } + + if (masterPtr == winPtr) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't make \"%s\" its own master", Tk_PathName(winPtr))); + Tcl_SetErrorCode(interp, "TK", "WM", "TRANSIENT", "SELF", NULL); + return TCL_ERROR; + } else if (masterPtr != wmPtr->masterPtr) { + /* + * Remove old master map/unmap binding before setting the new + * master. The event handler will ensure that transient states + * reflect the state of the master. + */ + + if (wmPtr->masterPtr != NULL) { + wmPtr->masterPtr->wmInfoPtr->numTransients--; + Tk_DeleteEventHandler((Tk_Window) wmPtr->masterPtr, + VisibilityChangeMask|StructureNotifyMask, + WmWaitVisibilityOrMapProc, winPtr); + } + + masterPtr->wmInfoPtr->numTransients++; + Tk_CreateEventHandler((Tk_Window) masterPtr, + VisibilityChangeMask|StructureNotifyMask, + WmWaitVisibilityOrMapProc, winPtr); + + wmPtr->masterPtr = masterPtr; + } + } + if (!((wmPtr->flags & WM_NEVER_MAPPED) + && !(winPtr->flags & TK_EMBEDDED))) { + if (wmPtr->masterPtr != NULL + && !Tk_IsMapped(wmPtr->masterPtr)) { + TkpWmSetState(winPtr, WithdrawnState); + } else { + UpdateWrapper(winPtr); + } + } + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * WmWithdrawCmd -- + * + * This function is invoked to process the "wm withdraw" Tcl command. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ + +static int +WmWithdrawCmd( + Tk_Window tkwin, /* Main window of the application. */ + TkWindow *winPtr, /* Toplevel to work with */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (objc != 3) { + Tcl_WrongNumArgs(interp, 2, objv, "window"); + return TCL_ERROR; + } + if (wmPtr->iconFor != NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't withdraw %s: it is an icon for %s", + Tcl_GetString(objv[2]), Tk_PathName(wmPtr->iconFor))); + Tcl_SetErrorCode(interp, "TK", "WM", "WITHDRAW", "ICON", NULL); + return TCL_ERROR; + } + + if (winPtr->flags & TK_EMBEDDED) { + if (SendMessage(wmPtr->wrapper, TK_WITHDRAW, 0, 0) < 0) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "can't withdraw %s: the container does not support the request", + Tcl_GetString(objv[2]))); + Tcl_SetErrorCode(interp, "TK", "WM", "COMMUNICATION", NULL); + return TCL_ERROR; + } + } else { + wmPtr->flags |= WM_WITHDRAWN; + TkpWmSetState(winPtr, WithdrawnState); + } + return TCL_OK; +} + +/* + * Invoked by those wm subcommands that affect geometry. Schedules a geometry + * update. + */ + +static void +WmUpdateGeom( + WmInfo *wmPtr, + TkWindow *winPtr) +{ + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } +} + + /*ARGSUSED*/ +static void +WmWaitVisibilityOrMapProc( + ClientData clientData, /* Pointer to window. */ + XEvent *eventPtr) /* Information about event. */ +{ + TkWindow *winPtr = clientData; + TkWindow *masterPtr = winPtr->wmInfoPtr->masterPtr; + + if (masterPtr == NULL) + return; + + if (eventPtr->type == MapNotify) { + if (!(winPtr->wmInfoPtr->flags & WM_WITHDRAWN)) { + TkpWmSetState(winPtr, NormalState); + } + } else if (eventPtr->type == UnmapNotify) { + TkpWmSetState(winPtr, WithdrawnState); + } + + if (eventPtr->type == VisibilityNotify) { + int state = masterPtr->wmInfoPtr->hints.initial_state; + + if ((state == NormalState) || (state == ZoomState)) { + state = winPtr->wmInfoPtr->hints.initial_state; + if ((state == NormalState) || (state == ZoomState)) { + UpdateWrapper(winPtr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_SetGrid -- + * + * This function is invoked by a widget when it wishes to set a grid + * coordinate system that controls the size of a top-level window. It + * provides a C interface equivalent to the "wm grid" command and is + * usually associated with the -setgrid option. + * + * Results: + * None. + * + * Side effects: + * Grid-related information will be passed to the window manager, so that + * the top-level window associated with tkwin will resize on even grid + * units. If some other window already controls gridding for the + * top-level window then this function call has no effect. + * + *---------------------------------------------------------------------- + */ + +void +Tk_SetGrid( + Tk_Window tkwin, /* Token for window. New window mgr info will + * be posted for the top-level window + * associated with this window. */ + int reqWidth, /* Width (in grid units) corresponding to the + * requested geometry for tkwin. */ + int reqHeight, /* Height (in grid units) corresponding to the + * requested geometry for tkwin. */ + int widthInc, int heightInc)/* Pixel increments corresponding to a change + * of one grid unit. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + register WmInfo *wmPtr; + + /* + * Ensure widthInc and heightInc are greater than 0 + */ + + if (widthInc <= 0) { + widthInc = 1; + } + if (heightInc <= 0) { + heightInc = 1; + } + + /* + * Find the top-level window for tkwin, plus the window manager + * information. + */ + + while (!(winPtr->flags & TK_TOP_HIERARCHY)) { + winPtr = winPtr->parentPtr; + } + wmPtr = winPtr->wmInfoPtr; + if (wmPtr == NULL) { + return; + } + + if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) { + return; + } + + if ((wmPtr->reqGridWidth == reqWidth) + && (wmPtr->reqGridHeight == reqHeight) + && (wmPtr->widthInc == widthInc) + && (wmPtr->heightInc == heightInc) + && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc)) + == (PBaseSize|PResizeInc))) { + return; + } + + /* + * If gridding was previously off, then forget about any window size + * requests made by the user or via "wm geometry": these are in pixel + * units and there's no easy way to translate them to grid units since the + * new requested size of the top-level window in pixels may not yet have + * been registered yet (it may filter up the hierarchy in DoWhenIdle + * handlers). However, if the window has never been mapped yet then just + * leave the window size alone: assume that it is intended to be in grid + * units but just happened to have been specified before this function was + * called. + */ + + if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) { + wmPtr->width = -1; + wmPtr->height = -1; + } + + /* + * Set the new gridding information, and start the process of passing all + * of this information to the window manager. + */ + + wmPtr->gridWin = tkwin; + wmPtr->reqGridWidth = reqWidth; + wmPtr->reqGridHeight = reqHeight; + wmPtr->widthInc = widthInc; + wmPtr->heightInc = heightInc; + wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc; + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_UnsetGrid -- + * + * This function cancels the effect of a previous call to Tk_SetGrid. + * + * Results: + * None. + * + * Side effects: + * If tkwin currently controls gridding for its top-level window, + * gridding is cancelled for that top-level window; if some other window + * controls gridding then this function has no effect. + * + *---------------------------------------------------------------------- + */ + +void +Tk_UnsetGrid( + Tk_Window tkwin) /* Token for window that is currently + * controlling gridding. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + register WmInfo *wmPtr; + + /* + * Find the top-level window for tkwin, plus the window manager + * information. + */ + + while (!(winPtr->flags & TK_TOP_HIERARCHY)) { + winPtr = winPtr->parentPtr; + } + wmPtr = winPtr->wmInfoPtr; + if (wmPtr == NULL) { + return; + } + + if (tkwin != wmPtr->gridWin) { + return; + } + + wmPtr->gridWin = NULL; + wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc); + if (wmPtr->width != -1) { + wmPtr->width = winPtr->reqWidth + (wmPtr->width + - wmPtr->reqGridWidth)*wmPtr->widthInc; + wmPtr->height = winPtr->reqHeight + (wmPtr->height + - wmPtr->reqGridHeight)*wmPtr->heightInc; + } + wmPtr->widthInc = 1; + wmPtr->heightInc = 1; + + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } +} + +/* + *---------------------------------------------------------------------- + * + * TopLevelEventProc -- + * + * This function is invoked when a top-level (or other externally-managed + * window) is restructured in any way. + * + * Results: + * None. + * + * Side effects: + * Tk's internal data structures for the window get modified to reflect + * the structural change. + * + *---------------------------------------------------------------------- + */ + +static void +TopLevelEventProc( + ClientData clientData, /* Window for which event occurred. */ + XEvent *eventPtr) /* Event that just happened. */ +{ + register TkWindow *winPtr = clientData; + + if (eventPtr->type == DestroyNotify) { + Tk_ErrorHandler handler; + + if (!(winPtr->flags & TK_ALREADY_DEAD)) { + /* + * A top-level window was deleted externally (e.g., by the window + * manager). This is probably not a good thing, but cleanup as + * best we can. The error handler is needed because + * Tk_DestroyWindow will try to destroy the window, but of course + * it's already gone. + */ + + handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1, + NULL, NULL); + Tk_DestroyWindow((Tk_Window) winPtr); + Tk_DeleteErrorHandler(handler); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TopLevelReqProc -- + * + * This function is invoked by the geometry manager whenever the + * requested size for a top-level window is changed. + * + * Results: + * None. + * + * Side effects: + * Arrange for the window to be resized to satisfy the request (this + * happens as a when-idle action). + * + *---------------------------------------------------------------------- + */ + + /* ARGSUSED */ +static void +TopLevelReqProc( + ClientData dummy, /* Not used. */ + Tk_Window tkwin) /* Information about window. */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + WmInfo *wmPtr; + + wmPtr = winPtr->wmInfoPtr; + if (wmPtr) { + if ((winPtr->flags & TK_EMBEDDED) && (wmPtr->wrapper != NULL)) { + SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, Tk_ReqWidth(tkwin), + Tk_ReqHeight(tkwin)); + } + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * UpdateGeometryInfo -- + * + * This function is invoked when a top-level window is first mapped, and + * also as a when-idle function, to bring the geometry and/or position of + * a top-level window back into line with what has been requested by the + * user and/or widgets. This function doesn't return until the system has + * responded to the geometry change. + * + * Results: + * None. + * + * Side effects: + * The window's size and location may change, unless the WM prevents that + * from happening. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateGeometryInfo( + ClientData clientData) /* Pointer to the window's record. */ +{ + int x, y; /* Position of border on desktop. */ + int width, height; /* Size of client area. */ + int min, max; + RECT rect; + register TkWindow *winPtr = clientData; + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + wmPtr->flags &= ~WM_UPDATE_PENDING; + + /* + * If the window is minimized or maximized, we should not update our + * geometry since it will end up with the wrong values. ConfigureToplevel + * will reschedule UpdateGeometryInfo when the state of the window + * changes. + */ + + if (wmPtr->wrapper && (IsIconic(wmPtr->wrapper) || + IsZoomed(wmPtr->wrapper) || (wmPtr->flags & WM_FULLSCREEN))) { + return; + } + + /* + * Compute the border size for the current window style. This size will + * include the resize handles, the title bar and the menubar. Note that + * this size will not be correct if the menubar spans multiple lines. The + * height will be off by a multiple of the menubar height. It really only + * measures the minimum size of the border. + */ + + rect.left = rect.right = rect.top = rect.bottom = 0; + AdjustWindowRectEx(&rect, wmPtr->style, wmPtr->hMenu != NULL, + wmPtr->exStyle); + wmPtr->borderWidth = rect.right - rect.left; + wmPtr->borderHeight = rect.bottom - rect.top; + + /* + * Compute the new size for the top-level window. See the user + * documentation for details on this, but the size requested depends on + * (a) the size requested internally by the window's widgets, (b) the size + * requested by the user in a "wm geometry" command or via wm-based + * interactive resizing (if any), (c) whether or not the window is + * gridded, and (d) the current min or max size for the toplevel. Don't + * permit sizes <= 0 because this upsets the X server. + */ + + if (wmPtr->width == -1) { + width = winPtr->reqWidth; + } else if (wmPtr->gridWin != NULL) { + width = winPtr->reqWidth + + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc; + } else { + width = wmPtr->width; + } + if (width <= 0) { + width = 1; + } + + /* + * Account for window max/min width + */ + + if (wmPtr->gridWin != NULL) { + min = winPtr->reqWidth + + (wmPtr->minWidth - wmPtr->reqGridWidth)*wmPtr->widthInc; + if (wmPtr->maxWidth > 0) { + max = winPtr->reqWidth + + (wmPtr->maxWidth - wmPtr->reqGridWidth)*wmPtr->widthInc; + } else { + max = 0; + } + } else { + min = wmPtr->minWidth; + max = wmPtr->maxWidth; + } + if (width < min) { + width = min; + } else if ((max > 0) && (width > max)) { + width = max; + } + + if (wmPtr->height == -1) { + height = winPtr->reqHeight; + } else if (wmPtr->gridWin != NULL) { + height = winPtr->reqHeight + + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc; + } else { + height = wmPtr->height; + } + if (height <= 0) { + height = 1; + } + + /* + * Account for window max/min height + */ + + if (wmPtr->gridWin != NULL) { + min = winPtr->reqHeight + + (wmPtr->minHeight - wmPtr->reqGridHeight)*wmPtr->heightInc; + if (wmPtr->maxHeight > 0) { + max = winPtr->reqHeight + + (wmPtr->maxHeight-wmPtr->reqGridHeight)*wmPtr->heightInc; + } else { + max = 0; + } + } else { + min = wmPtr->minHeight; + max = wmPtr->maxHeight; + } + if (height < min) { + height = min; + } else if ((max > 0) && (height > max)) { + height = max; + } + + /* + * Compute the new position for the upper-left pixel of the window's + * decorative frame. This is tricky, because we need to include the border + * widths supplied by a reparented parent in this calculation, but can't + * use the parent's current overall size since that may change as a result + * of this code. + */ + + if (wmPtr->flags & WM_NEGATIVE_X) { + x = DisplayWidth(winPtr->display, winPtr->screenNum) - wmPtr->x + - (width + wmPtr->borderWidth); + } else { + x = wmPtr->x; + } + if (wmPtr->flags & WM_NEGATIVE_Y) { + y = DisplayHeight(winPtr->display, winPtr->screenNum) - wmPtr->y + - (height + wmPtr->borderHeight); + } else { + y = wmPtr->y; + } + + /* + * Reconfigure the window if it isn't already configured correctly. Base + * the size check on what we *asked for* last time, not what we got. + * Return immediately if there have been no changes in the requested + * geometry of the toplevel. + */ + + /* TODO: need to add flag for possible menu size change */ + + if (!(wmPtr->flags & WM_MOVE_PENDING) + && (width == wmPtr->configWidth) + && (height == wmPtr->configHeight)) { + return; + } + wmPtr->flags &= ~WM_MOVE_PENDING; + + wmPtr->configWidth = width; + wmPtr->configHeight = height; + + /* + * Don't bother moving the window if we are in the process of creating it. + * Just update the geometry info based on what we asked for. + */ + + if (wmPtr->flags & WM_CREATE_PENDING) { + winPtr->changes.x = x; + winPtr->changes.y = y; + winPtr->changes.width = width; + winPtr->changes.height = height; + return; + } + + wmPtr->flags |= WM_SYNC_PENDING; + if (winPtr->flags & TK_EMBEDDED) { + /* + * The wrapper window is in a different process, so we need to send it + * a geometry request. This protocol assumes that the other process + * understands this Tk message, otherwise our requested geometry will + * be ignored. + */ + + SendMessage(wmPtr->wrapper, TK_MOVEWINDOW, x, y); + SendMessage(wmPtr->wrapper, TK_GEOMETRYREQ, width, height); + } else { + int reqHeight, reqWidth; + RECT windowRect; + int menuInc = GetSystemMetrics(SM_CYMENU); + int newHeight; + + /* + * We have to keep resizing the window until we get the requested + * height in the client area. If the client area has zero height, then + * the window rect is too small by definition. Try increasing the + * border height and try again. Once we have a positive size, then we + * can adjust the height exactly. If the window rect comes back + * smaller than we requested, we have hit the maximum constraints that + * Windows imposes. Once we find a positive client size, the next size + * is the one we try no matter what. + */ + + reqHeight = height + wmPtr->borderHeight; + reqWidth = width + wmPtr->borderWidth; + + while (1) { + MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE); + GetWindowRect(wmPtr->wrapper, &windowRect); + newHeight = windowRect.bottom - windowRect.top; + + /* + * If the request wasn't satisfied, we have hit an external + * constraint and must stop. + */ + + if (newHeight < reqHeight) { + break; + } + + /* + * Now check the size of the client area against our ideal. + */ + + GetClientRect(wmPtr->wrapper, &windowRect); + newHeight = windowRect.bottom - windowRect.top; + + if (newHeight == height) { + /* + * We're done. + */ + + break; + } else if (newHeight > height) { + /* + * One last resize to get rid of the extra space. + */ + + menuInc = newHeight - height; + reqHeight -= menuInc; + if (wmPtr->flags & WM_NEGATIVE_Y) { + y += menuInc; + } + MoveWindow(wmPtr->wrapper, x, y, reqWidth, reqHeight, TRUE); + break; + } + + /* + * We didn't get enough space to satisfy our requested height, so + * the menu must have wrapped. Increase the size of the window by + * one menu height and move the window if it is positioned + * relative to the lower right corner of the screen. + */ + + reqHeight += menuInc; + if (wmPtr->flags & WM_NEGATIVE_Y) { + y -= menuInc; + } + } + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + DrawMenuBar(wmPtr->wrapper); + } + } + wmPtr->flags &= ~WM_SYNC_PENDING; +} + +/* + *-------------------------------------------------------------- + * + * ParseGeometry -- + * + * This function parses a geometry string and updates information used to + * control the geometry of a top-level window. + * + * Results: + * A standard Tcl return value, plus an error message in the interp's + * result if an error occurs. + * + * Side effects: + * The size and/or location of winPtr may change. + * + *-------------------------------------------------------------- + */ + +static int +ParseGeometry( + Tcl_Interp *interp, /* Used for error reporting. */ + const char *string, /* String containing new geometry. Has the + * standard form "=wxh+x+y". */ + TkWindow *winPtr) /* Pointer to top-level window whose geometry + * is to be changed. */ +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + int x, y, width, height, flags; + char *end; + register const char *p = string; + + /* + * The leading "=" is optional. + */ + + if (*p == '=') { + p++; + } + + /* + * Parse the width and height, if they are present. Don't actually update + * any of the fields of wmPtr until we've successfully parsed the entire + * geometry string. + */ + + width = wmPtr->width; + height = wmPtr->height; + x = wmPtr->x; + y = wmPtr->y; + flags = wmPtr->flags; + if (isdigit(UCHAR(*p))) { + width = strtoul(p, &end, 10); + p = end; + if (*p != 'x') { + goto error; + } + p++; + if (!isdigit(UCHAR(*p))) { + goto error; + } + height = strtoul(p, &end, 10); + p = end; + } + + /* + * Parse the X and Y coordinates, if they are present. + */ + + if (*p != '\0') { + flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y); + if (*p == '-') { + flags |= WM_NEGATIVE_X; + } else if (*p != '+') { + goto error; + } + p++; + if (!isdigit(UCHAR(*p)) && (*p != '-')) { + goto error; + } + x = strtol(p, &end, 10); + p = end; + if (*p == '-') { + flags |= WM_NEGATIVE_Y; + } else if (*p != '+') { + goto error; + } + p++; + if (!isdigit(UCHAR(*p)) && (*p != '-')) { + goto error; + } + y = strtol(p, &end, 10); + if (*end != '\0') { + goto error; + } + + /* + * Assume that the geometry information came from the user, unless an + * explicit source has been specified. Otherwise most window managers + * assume that the size hints were program-specified and they ignore + * them. + */ + + if (!(wmPtr->sizeHintsFlags & (USPosition|PPosition))) { + wmPtr->sizeHintsFlags |= USPosition; + } + } + + /* + * Everything was parsed OK. Update the fields of *wmPtr and arrange for + * the appropriate information to be percolated out to the window manager + * at the next idle moment. + */ + + wmPtr->width = width; + wmPtr->height = height; + wmPtr->x = x; + wmPtr->y = y; + flags |= WM_MOVE_PENDING; + wmPtr->flags = flags; + + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } + return TCL_OK; + + error: + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "bad geometry specifier \"%s\"", string)); + Tcl_SetErrorCode(interp, "TK", "VALUE", "GEOMETRY", NULL); + return TCL_ERROR; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetRootCoords -- + * + * Given a token for a window, this function traces through the window's + * lineage to find the (virtual) root-window coordinates corresponding to + * point (0,0) in the window. + * + * Results: + * The locations pointed to by xPtr and yPtr are filled in with the root + * coordinates of the (0,0) point in tkwin. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tk_GetRootCoords( + Tk_Window tkwin, /* Token for window. */ + int *xPtr, /* Where to store x-displacement of (0,0). */ + int *yPtr) /* Where to store y-displacement of (0,0). */ +{ + register TkWindow *winPtr = (TkWindow *) tkwin; + + /* + * If the window is mapped, let Windows figure out the translation. + */ + + if (winPtr->window != None) { + HWND hwnd = Tk_GetHWND(winPtr->window); + POINT point; + + point.x = 0; + point.y = 0; + + ClientToScreen(hwnd, &point); + + *xPtr = point.x; + *yPtr = point.y; + } else { + *xPtr = 0; + *yPtr = 0; + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_CoordsToWindow -- + * + * Given the (virtual) root coordinates of a point, this function returns + * the token for the top-most window covering that point, if there exists + * such a window in this application. + * + * Results: + * The return result is either a token for the window corresponding to + * rootX and rootY, or else NULL to indicate that there is no such + * window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tk_Window +Tk_CoordsToWindow( + int rootX, int rootY, /* Coordinates of point in root window. If a + * virtual-root window manager is in use, + * these coordinates refer to the virtual + * root, not the real root. */ + Tk_Window tkwin) /* Token for any window in application; used + * to identify the display. */ +{ + POINT pos; + HWND hwnd; + TkWindow *winPtr; + + pos.x = rootX; + pos.y = rootY; + hwnd = WindowFromPoint(pos); + + winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + if (winPtr && (winPtr->mainPtr == ((TkWindow *) tkwin)->mainPtr)) { + return (Tk_Window) winPtr; + } + return NULL; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetVRootGeometry -- + * + * This function returns information about the virtual root window + * corresponding to a particular Tk window. + * + * Results: + * The values at xPtr, yPtr, widthPtr, and heightPtr are set with the + * offset and dimensions of the root window corresponding to tkwin. If + * tkwin is being managed by a virtual root window manager these values + * correspond to the virtual root window being used for tkwin; otherwise + * the offsets will be 0 and the dimensions will be those of the screen. + * + * Side effects: + * Vroot window information is refreshed if it is out of date. + * + *---------------------------------------------------------------------- + */ + +void +Tk_GetVRootGeometry( + Tk_Window tkwin, /* Window whose virtual root is to be + * queried. */ + int *xPtr, int *yPtr, /* Store x and y offsets of virtual root + * here. */ + int *widthPtr, int *heightPtr) + /* Store dimensions of virtual root here. */ +{ + *xPtr = GetSystemMetrics(SM_XVIRTUALSCREEN); + *yPtr = GetSystemMetrics(SM_YVIRTUALSCREEN); + *widthPtr = GetSystemMetrics(SM_CXVIRTUALSCREEN); + *heightPtr = GetSystemMetrics(SM_CYVIRTUALSCREEN); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_MoveToplevelWindow -- + * + * This function is called instead of Tk_MoveWindow to adjust the x-y + * location of a top-level window. It delays the actual move to a later + * time and keeps window-manager information up-to-date with the move + * + * Results: + * None. + * + * Side effects: + * The window is eventually moved so that its upper-left corner + * (actually, the upper-left corner of the window's decorative frame, if + * there is one) is at (x,y). + * + *---------------------------------------------------------------------- + */ + +void +Tk_MoveToplevelWindow( + Tk_Window tkwin, /* Window to move. */ + int x, int y) /* New location for window (within parent). */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (!(winPtr->flags & TK_TOP_LEVEL)) { + Tcl_Panic("Tk_MoveToplevelWindow called with non-toplevel window"); + } + wmPtr->x = x; + wmPtr->y = y; + wmPtr->flags |= WM_MOVE_PENDING; + wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y); + if (!(wmPtr->sizeHintsFlags & (USPosition|PPosition))) { + wmPtr->sizeHintsFlags |= USPosition; + } + + /* + * If the window has already been mapped, must bring its geometry + * up-to-date immediately, otherwise an event might arrive from the server + * that would overwrite wmPtr->x and wmPtr->y and lose the new position. + */ + + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + if (wmPtr->flags & WM_UPDATE_PENDING) { + Tcl_CancelIdleCall(UpdateGeometryInfo, winPtr); + } + UpdateGeometryInfo(winPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWmProtocolEventProc -- + * + * This function is called by the Tk_HandleEvent whenever a ClientMessage + * event arrives whose type is "WM_PROTOCOLS". This function handles the + * message from the window manager in an appropriate fashion. + * + * Results: + * None. + * + * Side effects: + * Depends on what sort of handler, if any, was set up for the protocol. + * + *---------------------------------------------------------------------- + */ + +void +TkWmProtocolEventProc( + TkWindow *winPtr, /* Window to which the event was sent. */ + XEvent *eventPtr) /* X event. */ +{ + WmInfo *wmPtr; + register ProtocolHandler *protPtr; + Atom protocol; + int result; + Tcl_Interp *interp; + + wmPtr = winPtr->wmInfoPtr; + if (wmPtr == NULL) { + return; + } + protocol = (Atom) eventPtr->xclient.data.l[0]; + for (protPtr = wmPtr->protPtr; protPtr != NULL; + protPtr = protPtr->nextPtr) { + if (protocol == protPtr->protocol) { + /* + * Cache atom name, as we might destroy the window as a result of + * the eval. + */ + + const char *name = Tk_GetAtomName((Tk_Window) winPtr, protocol); + + Tcl_Preserve(protPtr); + interp = protPtr->interp; + Tcl_Preserve(interp); + result = Tcl_EvalEx(interp, protPtr->command, -1, TCL_EVAL_GLOBAL); + if (result != TCL_OK) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (command for \"%s\" window manager protocol)", + name)); + Tcl_BackgroundException(interp, result); + } + Tcl_Release(interp); + Tcl_Release(protPtr); + return; + } + } + + /* + * No handler was present for this protocol. If this is a WM_DELETE_WINDOW + * message then just destroy the window. + */ + + if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) { + Tk_DestroyWindow((Tk_Window) winPtr); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWmStackorderToplevelEnumProc -- + * + * This function is invoked once for each HWND Window on the display as a + * result of calling EnumWindows from TkWmStackorderToplevel. + * + * Results: + * TRUE to request further iteration. + * + * Side effects: + * Adds entries to the passed array of TkWindows. + * + *---------------------------------------------------------------------- + */ + +BOOL CALLBACK +TkWmStackorderToplevelEnumProc( + HWND hwnd, /* Handle to parent window */ + LPARAM lParam) /* Application-defined value */ +{ + Tcl_HashEntry *hPtr; + TkWindow *childWinPtr; + + TkWmStackorderToplevelPair *pair = + (TkWmStackorderToplevelPair *) lParam; + + /*fprintf(stderr, "Looking up HWND %d\n", hwnd);*/ + + hPtr = Tcl_FindHashEntry(pair->table, (char *) hwnd); + if (hPtr != NULL) { + childWinPtr = Tcl_GetHashValue(hPtr); + + /* + * Double check that same HWND does not get passed twice. + */ + + if (childWinPtr == NULL) { + Tcl_Panic("duplicate HWND in TkWmStackorderToplevelEnumProc"); + } else { + Tcl_SetHashValue(hPtr, NULL); + } + /* + fprintf(stderr, "Found mapped HWND %d -> %x (%s)\n", hwnd, + childWinPtr, childWinPtr->pathName); + */ + *(pair->windowPtr)-- = childWinPtr; + } + return TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * TkWmStackorderToplevelWrapperMap -- + * + * This function will create a table that maps the wrapper HWND id for a + * toplevel to the TkWindow structure that is wraps. + * + * Results: + * None. + * + * Side effects: + * Adds entries to the passed hashtable. + * + *---------------------------------------------------------------------- + */ + +static void +TkWmStackorderToplevelWrapperMap( + TkWindow *winPtr, /* TkWindow to recurse on */ + Display *display, /* X display of parent window */ + Tcl_HashTable *table) /* Table to maps HWND to TkWindow */ +{ + TkWindow *childPtr; + Tcl_HashEntry *hPtr; + HWND wrapper; + int newEntry; + + if (Tk_IsMapped(winPtr) && Tk_IsTopLevel(winPtr) + && !Tk_IsEmbedded(winPtr) && (winPtr->display == display)) { + wrapper = TkWinGetWrapperWindow((Tk_Window) winPtr); + + /* + fprintf(stderr, "Mapped HWND %d to %x (%s)\n", wrapper, + winPtr, winPtr->pathName); + */ + + hPtr = Tcl_CreateHashEntry(table, (char *) wrapper, &newEntry); + Tcl_SetHashValue(hPtr, winPtr); + } + + for (childPtr = winPtr->childList; childPtr != NULL; + childPtr = childPtr->nextPtr) { + TkWmStackorderToplevelWrapperMap(childPtr, display, table); + } +} +/* + *---------------------------------------------------------------------- + * + * TkWmStackorderToplevel -- + * + * This function returns the stack order of toplevel windows. + * + * Results: + * An array of pointers to tk window objects in stacking order or else + * NULL if there was an error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkWindow ** +TkWmStackorderToplevel( + TkWindow *parentPtr) /* Parent toplevel window. */ +{ + TkWmStackorderToplevelPair pair; + TkWindow **windows; + Tcl_HashTable table; + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + /* + * Map HWND ids to a TkWindow of the wrapped toplevel. + */ + + Tcl_InitHashTable(&table, TCL_ONE_WORD_KEYS); + TkWmStackorderToplevelWrapperMap(parentPtr, parentPtr->display, &table); + + windows = ckalloc((table.numEntries+1) * sizeof(TkWindow *)); + + /* + * Special cases: If zero or one toplevels were mapped there is no need to + * call EnumWindows. + */ + + switch (table.numEntries) { + case 0: + windows[0] = NULL; + goto done; + case 1: + hPtr = Tcl_FirstHashEntry(&table, &search); + windows[0] = Tcl_GetHashValue(hPtr); + windows[1] = NULL; + goto done; + } + + /* + * We will be inserting into the array starting at the end and working our + * way to the beginning since EnumWindows returns windows in highest to + * lowest order. + */ + + pair.table = &table; + pair.windowPtr = windows + table.numEntries; + *pair.windowPtr-- = NULL; + + if (EnumWindows((WNDENUMPROC) TkWmStackorderToplevelEnumProc, + (LPARAM) &pair) == 0) { + ckfree(windows); + windows = NULL; + } else if (pair.windowPtr != (windows-1)) { + Tcl_Panic("num matched toplevel windows does not equal num children"); + } + + done: + Tcl_DeleteHashTable(&table); + return windows; +} + +/* + *---------------------------------------------------------------------- + * + * TkWmRestackToplevel -- + * + * This function restacks a top-level window. + * + * Results: + * None. + * + * Side effects: + * WinPtr gets restacked as specified by aboveBelow and otherPtr. This + * function doesn't return until the restack has taken effect and the + * ConfigureNotify event for it has been received. + * + *---------------------------------------------------------------------- + */ + +void +TkWmRestackToplevel( + TkWindow *winPtr, /* Window to restack. */ + int aboveBelow, /* Gives relative position for restacking; + * must be Above or Below. */ + TkWindow *otherPtr) /* Window relative to which to restack; if + * NULL, then winPtr gets restacked above or + * below *all* siblings. */ +{ + HWND hwnd, insertAfter; + + /* + * Can't set stacking order properly until the window is on the screen + * (mapping it may give it a reparent window). + */ + + if (winPtr->window == None) { + Tk_MakeWindowExist((Tk_Window) winPtr); + } + if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) { + TkWmMapWindow(winPtr); + } + hwnd = (winPtr->wmInfoPtr->wrapper != NULL) + ? winPtr->wmInfoPtr->wrapper : Tk_GetHWND(winPtr->window); + + if (otherPtr != NULL) { + if (otherPtr->window == None) { + Tk_MakeWindowExist((Tk_Window) otherPtr); + } + if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) { + TkWmMapWindow(otherPtr); + } + insertAfter = (otherPtr->wmInfoPtr->wrapper != NULL) + ? otherPtr->wmInfoPtr->wrapper : Tk_GetHWND(otherPtr->window); + } else { + insertAfter = NULL; + } + + if (winPtr->flags & TK_EMBEDDED) { + SendMessage(winPtr->wmInfoPtr->wrapper, TK_RAISEWINDOW, + (WPARAM) insertAfter, aboveBelow); + } else { + TkWinSetWindowPos(hwnd, insertAfter, aboveBelow); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWmAddToColormapWindows -- + * + * This function is called to add a given window to the + * WM_COLORMAP_WINDOWS property for its top-level, if it isn't already + * there. It is invoked by the Tk code that creates a new colormap, in + * order to make sure that colormap information is propagated to the + * window manager by default. + * + * Results: + * None. + * + * Side effects: + * WinPtr's window gets added to the WM_COLORMAP_WINDOWS property of its + * nearest top-level ancestor, unless the colormaps have been set + * explicitly with the "wm colormapwindows" command. + * + *---------------------------------------------------------------------- + */ + +void +TkWmAddToColormapWindows( + TkWindow *winPtr) /* Window with a non-default colormap. Should + * not be a top-level window. */ +{ + TkWindow *topPtr; + TkWindow **oldPtr, **newPtr; + int count, i; + + if (winPtr->window == None) { + return; + } + + for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) { + if (topPtr == NULL) { + /* + * Window is being deleted. Skip the whole operation. + */ + + return; + } + if (topPtr->flags & TK_TOP_HIERARCHY) { + break; + } + } + if (topPtr->wmInfoPtr == NULL) { + return; + } + + if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) { + return; + } + + /* + * Make sure that the window isn't already in the list. + */ + + count = topPtr->wmInfoPtr->cmapCount; + oldPtr = topPtr->wmInfoPtr->cmapList; + + for (i = 0; i < count; i++) { + if (oldPtr[i] == winPtr) { + return; + } + } + + /* + * Make a new bigger array and use it to reset the property. + * Automatically add the toplevel itself as the last element of the list. + */ + + newPtr = ckalloc((count+2) * sizeof(TkWindow *)); + if (count > 0) { + memcpy(newPtr, oldPtr, count * sizeof(TkWindow*)); + } + if (count == 0) { + count++; + } + newPtr[count-1] = winPtr; + newPtr[count] = topPtr; + if (oldPtr != NULL) { + ckfree(oldPtr); + } + + topPtr->wmInfoPtr->cmapList = newPtr; + topPtr->wmInfoPtr->cmapCount = count+1; + + /* + * Now we need to force the updated colormaps to be installed. + */ + + if (topPtr->wmInfoPtr == winPtr->dispPtr->foregroundWmPtr) { + InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_QUERYNEWPALETTE, 1); + } else { + InstallColormaps(topPtr->wmInfoPtr->wrapper, WM_PALETTECHANGED, 0); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWmRemoveFromColormapWindows -- + * + * This function is called to remove a given window from the + * WM_COLORMAP_WINDOWS property for its top-level. It is invoked when + * windows are deleted. + * + * Results: + * None. + * + * Side effects: + * WinPtr's window gets removed from the WM_COLORMAP_WINDOWS property of + * its nearest top-level ancestor, unless the top-level itself is being + * deleted too. + * + *---------------------------------------------------------------------- + */ + +void +TkWmRemoveFromColormapWindows( + TkWindow *winPtr) /* Window that may be present in + * WM_COLORMAP_WINDOWS property for its + * top-level. Should not be a top-level + * window. */ +{ + TkWindow *topPtr; + TkWindow **oldPtr; + int count, i, j; + + for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) { + if (topPtr == NULL) { + /* + * Ancestors have been deleted, so skip the whole operation. + * Seems like this can't ever happen? + */ + + return; + } + if (topPtr->flags & TK_TOP_LEVEL) { + break; + } + } + if (topPtr->flags & TK_ALREADY_DEAD) { + /* + * Top-level is being deleted, so there's no need to cleanup the + * WM_COLORMAP_WINDOWS property. + */ + + return; + } + + if (topPtr->wmInfoPtr == NULL) { + return; + } + + /* + * Find the window and slide the following ones down to cover it up. + */ + + count = topPtr->wmInfoPtr->cmapCount; + oldPtr = topPtr->wmInfoPtr->cmapList; + for (i = 0; i < count; i++) { + if (oldPtr[i] == winPtr) { + for (j = i ; j < count-1; j++) { + oldPtr[j] = oldPtr[j+1]; + } + topPtr->wmInfoPtr->cmapCount = count-1; + break; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWinSetMenu-- + * + * Associcates a given HMENU to a window. + * + * Results: + * None. + * + * Side effects: + * The menu will end up being drawn in the window, and the geometry of + * the window will have to be changed. + * + *---------------------------------------------------------------------- + */ + +void +TkWinSetMenu( + Tk_Window tkwin, /* the window to put the menu in */ + HMENU hMenu) /* the menu to set */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + WmInfo *wmPtr = winPtr->wmInfoPtr; + + /* Could be a Frame (i.e. not a Toplevel) */ + if (wmPtr == NULL) + return; + + wmPtr->hMenu = hMenu; + if (!(wmPtr->flags & WM_NEVER_MAPPED)) { + int syncPending = wmPtr->flags & WM_SYNC_PENDING; + + wmPtr->flags |= WM_SYNC_PENDING; + SetMenu(wmPtr->wrapper, hMenu); + if (!syncPending) { + wmPtr->flags &= ~WM_SYNC_PENDING; + } + } + if (!(winPtr->flags & TK_EMBEDDED)) { + if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING|WM_MOVE_PENDING; + } + } else { + SendMessage(wmPtr->wrapper, TK_SETMENU, (WPARAM) hMenu, + (LPARAM) Tk_GetMenuHWND(tkwin)); + } +} + +/* + *---------------------------------------------------------------------- + * + * ConfigureTopLevel -- + * + * Generate a ConfigureNotify event based on the current position + * information. This function is called by TopLevelProc. + * + * Results: + * None. + * + * Side effects: + * Queues a new event. + * + *---------------------------------------------------------------------- + */ + +static void +ConfigureTopLevel( + WINDOWPOS *pos) +{ + TkWindow *winPtr = GetTopLevel(pos->hwnd); + WmInfo *wmPtr; + int state; /* Current window state. */ + RECT rect; + WINDOWPLACEMENT windowPos; + + if (winPtr == NULL) { + return; + } + + wmPtr = winPtr->wmInfoPtr; + + /* + * Determine the current window state. + */ + + if (!IsWindowVisible(wmPtr->wrapper)) { + state = WithdrawnState; + } else { + windowPos.length = sizeof(WINDOWPLACEMENT); + GetWindowPlacement(wmPtr->wrapper, &windowPos); + switch (windowPos.showCmd) { + case SW_SHOWMAXIMIZED: + state = ZoomState; + break; + case SW_SHOWMINIMIZED: + state = IconicState; + break; + case SW_SHOWNORMAL: + default: + state = NormalState; + break; + } + } + + /* + * If the state of the window just changed, be sure to update the + * child window information. + */ + + if (wmPtr->hints.initial_state != state) { + wmPtr->hints.initial_state = state; + switch (state) { + case WithdrawnState: + case IconicState: + XUnmapWindow(winPtr->display, winPtr->window); + break; + + case NormalState: + /* + * Schedule a geometry update. Since we ignore geometry requests + * while in any other state, the geometry info may be stale. + */ + + if (!(wmPtr->flags & WM_UPDATE_PENDING)) { + Tcl_DoWhenIdle(UpdateGeometryInfo, winPtr); + wmPtr->flags |= WM_UPDATE_PENDING; + } + /* fall through */ + case ZoomState: + XMapWindow(winPtr->display, winPtr->window); + pos->flags |= SWP_NOMOVE | SWP_NOSIZE; + break; + } + } + + /* + * Don't report geometry changes in the Iconic or Withdrawn states. + */ + + if (state == WithdrawnState || state == IconicState) { + return; + } + + + /* + * Compute the current geometry of the client area, reshape the Tk window + * and generate a ConfigureNotify event. + */ + + GetClientRect(wmPtr->wrapper, &rect); + winPtr->changes.x = pos->x; + winPtr->changes.y = pos->y; + winPtr->changes.width = rect.right - rect.left; + winPtr->changes.height = rect.bottom - rect.top; + wmPtr->borderHeight = pos->cy - winPtr->changes.height; + MoveWindow(Tk_GetHWND(winPtr->window), 0, 0, + winPtr->changes.width, winPtr->changes.height, TRUE); + GenerateConfigureNotify(winPtr); + + /* + * Update window manager geometry info if needed. + */ + + if (state == NormalState) { + + /* + * Update size information from the event. There are a couple of + * tricky points here: + * + * 1. If the user changed the size externally then set wmPtr->width + * and wmPtr->height just as if a "wm geometry" command had been + * invoked with the same information. + * 2. However, if the size is changing in response to a request coming + * from us (sync is set), then don't set wmPtr->width or + * wmPtr->height (otherwise the window will stop tracking geometry + * manager requests). + */ + + if (!(wmPtr->flags & WM_SYNC_PENDING)) { + if (!(pos->flags & SWP_NOSIZE)) { + if ((wmPtr->width == -1) + && (winPtr->changes.width == winPtr->reqWidth)) { + /* + * Don't set external width, since the user didn't change + * it from what the widgets asked for. + */ + } else { + if (wmPtr->gridWin != NULL) { + wmPtr->width = wmPtr->reqGridWidth + + (winPtr->changes.width - winPtr->reqWidth) + / wmPtr->widthInc; + if (wmPtr->width < 0) { + wmPtr->width = 0; + } + } else { + wmPtr->width = winPtr->changes.width; + } + } + if ((wmPtr->height == -1) + && (winPtr->changes.height == winPtr->reqHeight)) { + /* + * Don't set external height, since the user didn't change + * it from what the widgets asked for. + */ + } else { + if (wmPtr->gridWin != NULL) { + wmPtr->height = wmPtr->reqGridHeight + + (winPtr->changes.height - winPtr->reqHeight) + / wmPtr->heightInc; + if (wmPtr->height < 0) { + wmPtr->height = 0; + } + } else { + wmPtr->height = winPtr->changes.height; + } + } + wmPtr->configWidth = winPtr->changes.width; + wmPtr->configHeight = winPtr->changes.height; + } + + /* + * If the user moved the window, we should switch back to normal + * coordinates. + */ + + if (!(pos->flags & SWP_NOMOVE)) { + wmPtr->flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y); + } + } + + /* + * Update the wrapper window location information. + */ + + if (wmPtr->flags & WM_NEGATIVE_X) { + wmPtr->x = DisplayWidth(winPtr->display, winPtr->screenNum) + - winPtr->changes.x - (winPtr->changes.width + + wmPtr->borderWidth); + } else { + wmPtr->x = winPtr->changes.x; + } + if (wmPtr->flags & WM_NEGATIVE_Y) { + wmPtr->y = DisplayHeight(winPtr->display, winPtr->screenNum) + - winPtr->changes.y - (winPtr->changes.height + + wmPtr->borderHeight); + } else { + wmPtr->y = winPtr->changes.y; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * GenerateConfigureNotify -- + * + * Generate a ConfigureNotify event from the current geometry information + * for the specified toplevel window. + * + * Results: + * None. + * + * Side effects: + * Sends an X event. + * + *---------------------------------------------------------------------- + */ + +static void +GenerateConfigureNotify( + TkWindow *winPtr) +{ + XEvent event; + + /* + * Generate a ConfigureNotify event. + */ + + event.type = ConfigureNotify; + event.xconfigure.serial = winPtr->display->request; + event.xconfigure.send_event = False; + event.xconfigure.display = winPtr->display; + event.xconfigure.event = winPtr->window; + event.xconfigure.window = winPtr->window; + event.xconfigure.border_width = winPtr->changes.border_width; + event.xconfigure.override_redirect = winPtr->atts.override_redirect; + event.xconfigure.x = winPtr->changes.x; + event.xconfigure.y = winPtr->changes.y; + event.xconfigure.width = winPtr->changes.width; + event.xconfigure.height = winPtr->changes.height; + event.xconfigure.above = None; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); +} + +/* + *---------------------------------------------------------------------- + * + * InstallColormaps -- + * + * Installs the colormaps associated with the toplevel which is currently + * active. + * + * Results: + * None. + * + * Side effects: + * May change the system palette and generate damage. + * + *---------------------------------------------------------------------- + */ + +static int +InstallColormaps( + HWND hwnd, /* Toplevel wrapper window whose colormaps + * should be installed. */ + int message, /* Either WM_PALETTECHANGED or + * WM_QUERYNEWPALETTE */ + int isForemost) /* 1 if window is foremost, else 0 */ +{ + int i; + HDC dc; + HPALETTE oldPalette; + TkWindow *winPtr = GetTopLevel(hwnd); + WmInfo *wmPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (winPtr == NULL || (winPtr->flags & TK_ALREADY_DEAD)) { + return 0; + } + + wmPtr = winPtr->wmInfoPtr; + + if (message == WM_QUERYNEWPALETTE) { + /* + * Case 1: This window is about to become the foreground window, so we + * need to install the primary palette. If the system palette was + * updated, then Windows will generate a WM_PALETTECHANGED message. + * Otherwise, we have to synthesize one in order to ensure that the + * secondary palettes are installed properly. + */ + + winPtr->dispPtr->foregroundWmPtr = wmPtr; + + if (wmPtr->cmapCount > 0) { + winPtr = wmPtr->cmapList[0]; + } + + tsdPtr->systemPalette = TkWinGetPalette(winPtr->atts.colormap); + dc = GetDC(hwnd); + oldPalette = SelectPalette(dc, tsdPtr->systemPalette, FALSE); + if (RealizePalette(dc)) { + RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr); + } else if (wmPtr->cmapCount > 1) { + SelectPalette(dc, oldPalette, TRUE); + RealizePalette(dc); + ReleaseDC(hwnd, dc); + SendMessage(hwnd, WM_PALETTECHANGED, (WPARAM) hwnd, (LPARAM) NULL); + return TRUE; + } + } else { + /* + * Window is being notified of a change in the system palette. If this + * window is the foreground window, then we should only install the + * secondary palettes, since the primary was installed in response to + * the WM_QUERYPALETTE message. Otherwise, install all of the + * palettes. + */ + + + if (!isForemost) { + if (wmPtr->cmapCount > 0) { + winPtr = wmPtr->cmapList[0]; + } + i = 1; + } else { + if (wmPtr->cmapCount <= 1) { + return TRUE; + } + winPtr = wmPtr->cmapList[1]; + i = 2; + } + dc = GetDC(hwnd); + oldPalette = SelectPalette(dc, + TkWinGetPalette(winPtr->atts.colormap), TRUE); + if (RealizePalette(dc)) { + RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr); + } + for (; i < wmPtr->cmapCount; i++) { + winPtr = wmPtr->cmapList[i]; + SelectPalette(dc, TkWinGetPalette(winPtr->atts.colormap), TRUE); + if (RealizePalette(dc)) { + RefreshColormap(winPtr->atts.colormap, winPtr->dispPtr); + } + } + } + + SelectPalette(dc, oldPalette, TRUE); + RealizePalette(dc); + ReleaseDC(hwnd, dc); + return TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * RefreshColormap -- + * + * This function is called to force all of the windows that use a given + * colormap to redraw themselves. The quickest way to do this is to + * iterate over the toplevels, looking in the cmapList for matches. This + * will quickly eliminate subtrees that don't use a given colormap. + * + * Results: + * None. + * + * Side effects: + * Causes damage events to be generated. + * + *---------------------------------------------------------------------- + */ + +static void +RefreshColormap( + Colormap colormap, + TkDisplay *dispPtr) +{ + WmInfo *wmPtr; + int i; + + for (wmPtr = dispPtr->firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) { + if (wmPtr->cmapCount > 0) { + for (i = 0; i < wmPtr->cmapCount; i++) { + if ((wmPtr->cmapList[i]->atts.colormap == colormap) + && Tk_IsMapped(wmPtr->cmapList[i])) { + InvalidateSubTree(wmPtr->cmapList[i], colormap); + } + } + } else if ((wmPtr->winPtr->atts.colormap == colormap) + && Tk_IsMapped(wmPtr->winPtr)) { + InvalidateSubTree(wmPtr->winPtr, colormap); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * InvalidateSubTree -- + * + * This function recursively generates damage for a window and all of its + * mapped children that belong to the same toplevel and are using the + * specified colormap. + * + * Results: + * None. + * + * Side effects: + * Generates damage for the specified subtree. + * + *---------------------------------------------------------------------- + */ + +static void +InvalidateSubTree( + TkWindow *winPtr, + Colormap colormap) +{ + TkWindow *childPtr; + + /* + * Generate damage for the current window if it is using the specified + * colormap. + */ + + if (winPtr->atts.colormap == colormap) { + InvalidateRect(Tk_GetHWND(winPtr->window), NULL, FALSE); + } + + for (childPtr = winPtr->childList; childPtr != NULL; + childPtr = childPtr->nextPtr) { + /* + * We can stop the descent when we hit an unmapped or toplevel window. + */ + + if (!Tk_TopWinHierarchy(childPtr) && Tk_IsMapped(childPtr)) { + InvalidateSubTree(childPtr, colormap); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * InvalidateSubTreeDepth -- + * + * This function recursively updates depth info for a window and all of + * its children that belong to the same toplevel. + * + * Results: + * None. + * + * Side effects: + * Sets the depth of each window to that of the display. + * + *---------------------------------------------------------------------- + */ + +static void +InvalidateSubTreeDepth( + TkWindow *winPtr) +{ + Display *display = Tk_Display(winPtr); + int screenNum = Tk_ScreenNumber(winPtr); + TkWindow *childPtr; + + winPtr->depth = DefaultDepth(display, screenNum); + +#if 0 + /* + * XXX: What other elements may require changes? Changing just the depth + * works for standard windows and 16/24/32-bpp changes. I suspect 8-bit + * (palettized) displays may require colormap and/or visual changes as + * well. + */ + + if (winPtr->window) { + InvalidateRect(Tk_GetHWND(winPtr->window), NULL, FALSE); + } + winPtr->visual = DefaultVisual(display, screenNum); + winPtr->atts.colormap = DefaultColormap(display, screenNum); + winPtr->dirtyAtts |= CWColormap; +#endif + + for (childPtr = winPtr->childList; childPtr != NULL; + childPtr = childPtr->nextPtr) { + /* + * We can stop the descent when we hit a non-embedded toplevel window, + * as it should get its own message. + */ + + if (childPtr->flags & TK_EMBEDDED || !Tk_TopWinHierarchy(childPtr)) { + InvalidateSubTreeDepth(childPtr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetSystemPalette -- + * + * Retrieves the currently installed foreground palette. + * + * Results: + * Returns the global foreground palette, if there is one. Otherwise, + * returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HPALETTE +TkWinGetSystemPalette(void) +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + return tsdPtr->systemPalette; +} + +/* + *---------------------------------------------------------------------- + * + * GetMinSize -- + * + * This function computes the current minWidth and minHeight values for a + * window, taking into account the possibility that they may be + * defaulted. + * + * Results: + * The values at *minWidthPtr and *minHeightPtr are filled in with the + * minimum allowable dimensions of wmPtr's window, in grid units. If the + * requested minimum is smaller than the system required minimum, then + * this function computes the smallest size that will satisfy both the + * system and the grid constraints. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +GetMinSize( + WmInfo *wmPtr, /* Window manager information for the + * window. */ + int *minWidthPtr, /* Where to store the current minimum width of + * the window. */ + int *minHeightPtr) /* Where to store the current minimum height + * of the window. */ +{ + int tmp, base; + TkWindow *winPtr = wmPtr->winPtr; + + /* + * Compute the minimum width by taking the default client size and + * rounding it up to the nearest grid unit. Return the greater of the + * default minimum and the specified minimum. + */ + + tmp = wmPtr->defMinWidth - wmPtr->borderWidth; + if (tmp < 0) { + tmp = 0; + } + if (wmPtr->gridWin != NULL) { + base = winPtr->reqWidth - (wmPtr->reqGridWidth * wmPtr->widthInc); + if (base < 0) { + base = 0; + } + tmp = ((tmp - base) + wmPtr->widthInc - 1)/wmPtr->widthInc; + } + if (tmp < wmPtr->minWidth) { + tmp = wmPtr->minWidth; + } + *minWidthPtr = tmp; + + /* + * Compute the minimum height in a similar fashion. + */ + + tmp = wmPtr->defMinHeight - wmPtr->borderHeight; + if (tmp < 0) { + tmp = 0; + } + if (wmPtr->gridWin != NULL) { + base = winPtr->reqHeight - (wmPtr->reqGridHeight * wmPtr->heightInc); + if (base < 0) { + base = 0; + } + tmp = ((tmp - base) + wmPtr->heightInc - 1)/wmPtr->heightInc; + } + if (tmp < wmPtr->minHeight) { + tmp = wmPtr->minHeight; + } + *minHeightPtr = tmp; +} + +/* + *---------------------------------------------------------------------- + * + * GetMaxSize -- + * + * This function computes the current maxWidth and maxHeight values for a + * window, taking into account the possibility that they may be + * defaulted. + * + * Results: + * The values at *maxWidthPtr and *maxHeightPtr are filled in with the + * maximum allowable dimensions of wmPtr's window, in grid units. If no + * maximum has been specified for the window, then this function computes + * the largest sizes that will fit on the screen. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +GetMaxSize( + WmInfo *wmPtr, /* Window manager information for the + * window. */ + int *maxWidthPtr, /* Where to store the current maximum width of + * the window. */ + int *maxHeightPtr) /* Where to store the current maximum height + * of the window. */ +{ + int tmp; + + if (wmPtr->maxWidth > 0) { + *maxWidthPtr = wmPtr->maxWidth; + } else { + /* + * Must compute a default width. Fill up the display, leaving a bit of + * extra space for the window manager's borders. + */ + + tmp = wmPtr->defMaxWidth - wmPtr->borderWidth; + if (wmPtr->gridWin != NULL) { + /* + * Gridding is turned on; convert from pixels to grid units. + */ + + tmp = wmPtr->reqGridWidth + + (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc; + } + *maxWidthPtr = tmp; + } + if (wmPtr->maxHeight > 0) { + *maxHeightPtr = wmPtr->maxHeight; + } else { + tmp = wmPtr->defMaxHeight - wmPtr->borderHeight; + if (wmPtr->gridWin != NULL) { + tmp = wmPtr->reqGridHeight + + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc; + } + *maxHeightPtr = tmp; + } +} + +/* + *---------------------------------------------------------------------- + * + * TopLevelProc -- + * + * Callback from Windows whenever an event occurs on a top level window. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * Default window behavior. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +TopLevelProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + if (message == WM_WINDOWPOSCHANGED || message == WM_WINDOWPOSCHANGING) { + WINDOWPOS *pos = (WINDOWPOS *) lParam; + TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(pos->hwnd); + + if (winPtr == NULL) { + return 0; + } + + /* + * Update the shape of the contained window. + */ + + if (!(pos->flags & SWP_NOSIZE)) { + winPtr->changes.width = pos->cx; + winPtr->changes.height = pos->cy; + } + if (!(pos->flags & SWP_NOMOVE)) { + long result = SendMessage(winPtr->wmInfoPtr->wrapper, + TK_MOVEWINDOW, -1, -1); + winPtr->wmInfoPtr->x = winPtr->changes.x = result >> 16; + winPtr->wmInfoPtr->y = winPtr->changes.y = result & 0xffff; + } + + GenerateConfigureNotify(winPtr); + + Tcl_ServiceAll(); + return 0; + } + return TkWinChildProc(hwnd, message, wParam, lParam); +} + +/* + *---------------------------------------------------------------------- + * + * WmProc -- + * + * Callback from Windows whenever an event occurs on the decorative + * frame. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * Default window behavior. + * + *---------------------------------------------------------------------- + */ + +static LRESULT CALLBACK +WmProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + static int inMoveSize = 0; + static int oldMode; /* This static is set upon entering move/size + * mode and is used to reset the service mode + * after leaving move/size mode. Note that + * this mechanism assumes move/size is only + * one level deep. */ + LRESULT result = 0; + TkWindow *winPtr = NULL; + + switch (message) { + case WM_KILLFOCUS: + case WM_ERASEBKGND: + result = 0; + goto done; + + case WM_ENTERSIZEMOVE: + inMoveSize = 1; + + /* + * Cancel any current mouse timer. If the mouse timer fires during the + * size/move mouse capture, it will release the capture, which is + * wrong. + */ + + TkWinCancelMouseTimer(); + + oldMode = Tcl_SetServiceMode(TCL_SERVICE_ALL); + break; + + case WM_ACTIVATE: + if (WA_ACTIVE == LOWORD(wParam)) { + winPtr = GetTopLevel(hwnd); + if (winPtr && (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)) { + /* + * There is a grab in progress so queue an Activate event + */ + + GenerateActivateEvent(winPtr, &inMoveSize); + result = 0; + goto done; + } + } + /* fall through */ + + case WM_EXITSIZEMOVE: + if (inMoveSize) { + inMoveSize = 0; + Tcl_SetServiceMode(oldMode); + } + break; + + case WM_GETMINMAXINFO: + SetLimits(hwnd, (MINMAXINFO *) lParam); + result = 0; + goto done; + + case WM_DISPLAYCHANGE: + /* + * Display and/or color resolution changed. + */ + + winPtr = GetTopLevel(hwnd); + if (winPtr) { + Screen *screen = Tk_Screen(winPtr); + if (screen->root_depth != (int) wParam) { + /* + * Color resolution changed, so do extensive rebuild of + * display parameters. This will affect the display for all Tk + * windows. We will receive this event for each toplevel, but + * this check makes us update only once, for the first + * toplevel that receives the message. + */ + + TkWinDisplayChanged(Tk_Display(winPtr)); + } else { + HDC dc = GetDC(NULL); + + screen->width = LOWORD(lParam); /* horizontal res */ + screen->height = HIWORD(lParam); /* vertical res */ + screen->mwidth = MulDiv(screen->width, 254, + GetDeviceCaps(dc, LOGPIXELSX) * 10); + screen->mheight = MulDiv(screen->height, 254, + GetDeviceCaps(dc, LOGPIXELSY) * 10); + ReleaseDC(NULL, dc); + } + if (Tk_Depth(winPtr) != (int) wParam) { + /* + * Defer the window depth check to here so that each toplevel + * will properly update depth info. + */ + + InvalidateSubTreeDepth(winPtr); + } + } + result = 0; + goto done; + + case WM_SYSCOLORCHANGE: + /* + * XXX: Called when system color changes. We need to update any + * widgets that use a system color. + */ + + break; + + case WM_PALETTECHANGED: + result = InstallColormaps(hwnd, WM_PALETTECHANGED, + hwnd == (HWND) wParam); + goto done; + + case WM_QUERYNEWPALETTE: + result = InstallColormaps(hwnd, WM_QUERYNEWPALETTE, TRUE); + goto done; + + case WM_SETTINGCHANGE: + if (wParam == SPI_SETNONCLIENTMETRICS) { + winPtr = GetTopLevel(hwnd); + TkWinSetupSystemFonts(winPtr->mainPtr); + result = 0; + goto done; + } + break; + + case WM_WINDOWPOSCHANGED: + ConfigureTopLevel((WINDOWPOS *) lParam); + result = 0; + goto done; + + case WM_NCHITTEST: { + winPtr = GetTopLevel(hwnd); + if (winPtr && (TkGrabState(winPtr) == TK_GRAB_EXCLUDED)) { + /* + * This window is outside the grab heirarchy, so don't let any of + * the normal non-client processing occur. Note that this + * implementation is not strictly correct because the grab might + * change between now and when the event would have been processed + * by Tk, but it's close enough. + */ + + result = HTCLIENT; + goto done; + } + break; + } + + case WM_MOUSEACTIVATE: { + winPtr = GetTopLevel((HWND) wParam); + if (winPtr && (TkGrabState(winPtr) != TK_GRAB_EXCLUDED)) { + /* + * This allows us to pass the message onto the native menus [Bug: + * 2272] + */ + + result = DefWindowProc(hwnd, message, wParam, lParam); + goto done; + } + + /* + * Don't activate the window yet since there is a grab that takes + * precedence. Instead we need to queue an event so we can check the + * grab state right before we handle the mouse event. + */ + + if (winPtr) { + GenerateActivateEvent(winPtr, &inMoveSize); + } + result = MA_NOACTIVATE; + goto done; + } + + case WM_QUERYENDSESSION: { + XEvent event; + + /* + * Synthesize WM_SAVE_YOURSELF wm protocol message on Windows logout + * or restart. + */ + winPtr = GetTopLevel(hwnd); + event.xclient.message_type = + Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"); + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WM_SAVE_YOURSELF"); + TkWmProtocolEventProc(winPtr, &event); + break; + } + + default: + break; + } + + winPtr = GetTopLevel(hwnd); + switch(message) { + case WM_SYSCOMMAND: + /* + * If there is a grab in effect then ignore the minimize command + * unless the grab is on the main window (.). This is to permit + * applications that leave a grab on . to work normally. + * All other toplevels are deemed non-minimizable when a grab is + * present. + * If there is a grab in effect and this window is outside the + * grab tree then ignore all system commands. [Bug 1847002] + */ + + if (winPtr) { + int cmd = wParam & 0xfff0; + int grab = TkGrabState(winPtr); + if ((SC_MINIMIZE == cmd) + && (grab == TK_GRAB_IN_TREE || grab == TK_GRAB_ANCESTOR) + && (winPtr != winPtr->mainPtr->winPtr)) { + goto done; + } + if (grab == TK_GRAB_EXCLUDED + && !(SC_MOVE == cmd || SC_SIZE == cmd)) { + goto done; + } + } + /* fall through */ + + case WM_INITMENU: + case WM_COMMAND: + case WM_MENUCHAR: + case WM_MEASUREITEM: + case WM_DRAWITEM: + case WM_MENUSELECT: + case WM_ENTERIDLE: + case WM_INITMENUPOPUP: + if (winPtr) { + HWND hMenuHWnd = Tk_GetEmbeddedMenuHWND((Tk_Window) winPtr); + + if (hMenuHWnd) { + if (SendMessage(hMenuHWnd, message, wParam, lParam)) { + goto done; + } + } else if (TkWinHandleMenuEvent(&hwnd, &message, &wParam, &lParam, + &result)) { + goto done; + } + } + break; + } + + if (winPtr && winPtr->window) { + HWND child = Tk_GetHWND(winPtr->window); + + if (message == WM_SETFOCUS) { + SetFocus(child); + result = 0; + } else if (!Tk_TranslateWinEvent(child, message, wParam, lParam, + &result)) { + result = DefWindowProc(hwnd, message, wParam, lParam); + } + } else { + result = DefWindowProc(hwnd, message, wParam, lParam); + } + + done: + Tcl_ServiceAll(); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * TkpMakeMenuWindow -- + * + * Configure the window to be either a pull-down (or pop-up) menu, or as + * a toplevel (torn-off) menu or palette. + * + * Results: + * None. + * + * Side effects: + * Changes the style bit used to create a new toplevel. + * + *---------------------------------------------------------------------- + */ + +void +TkpMakeMenuWindow( + Tk_Window tkwin, /* New window. */ + int transient) /* 1 means menu is only posted briefly as a + * popup or pulldown or cascade. 0 means menu + * is always visible, e.g. as a torn-off menu. + * Determines whether save_under and + * override_redirect should be set. */ +{ + XSetWindowAttributes atts; + + if (transient) { + atts.override_redirect = True; + atts.save_under = True; + } else { + atts.override_redirect = False; + atts.save_under = False; + } + + if ((atts.override_redirect != Tk_Attributes(tkwin)->override_redirect) + || (atts.save_under != Tk_Attributes(tkwin)->save_under)) { + Tk_ChangeWindowAttributes(tkwin, CWOverrideRedirect|CWSaveUnder, + &atts); + } + +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetWrapperWindow -- + * + * Gets the Windows HWND for a given window. + * + * Results: + * Returns the wrapper window for a Tk window. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HWND +TkWinGetWrapperWindow( + Tk_Window tkwin) /* The window we need the wrapper from */ +{ + TkWindow *winPtr = (TkWindow *) tkwin; + + return winPtr->wmInfoPtr->wrapper; +} + +/* + *---------------------------------------------------------------------- + * + * TkWmFocusToplevel -- + * + * This is a utility function invoked by focus-management code. It exists + * because of the extra wrapper windows that exist under Unix; its job is + * to map from wrapper windows to the corresponding toplevel windows. On + * PCs and Macs there are no wrapper windows so no mapping is necessary; + * this function just determines whether a window is a toplevel or not. + * + * Results: + * If winPtr is a toplevel window, returns the pointer to the window; + * otherwise returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkWindow * +TkWmFocusToplevel( + TkWindow *winPtr) /* Window that received a focus-related + * event. */ +{ + if (!(winPtr->flags & TK_TOP_HIERARCHY)) { + return NULL; + } + return winPtr; +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetWrapperWindow -- + * + * This is a utility function invoked by focus-management code. It maps + * to the wrapper for a top-level, which is just the same as the + * top-level on Macs and PCs. + * + * Results: + * If winPtr is a toplevel window, returns the pointer to the window; + * otherwise returns NULL. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +TkWindow * +TkpGetWrapperWindow( + TkWindow *winPtr) /* Window that received a focus-related + * event. */ +{ + if (!(winPtr->flags & TK_TOP_HIERARCHY)) { + return NULL; + } + return winPtr; +} + +/* + *---------------------------------------------------------------------- + * + * GenerateActivateEvent -- + * + * This function is called to activate a Tk window. + */ + +static void +GenerateActivateEvent(TkWindow * winPtr, const int *flagPtr) +{ + ActivateEvent *eventPtr = ckalloc(sizeof(ActivateEvent)); + + eventPtr->ev.proc = ActivateWindow; + eventPtr->winPtr = winPtr; + eventPtr->flagPtr = flagPtr; + eventPtr->hwnd = Tk_GetHWND(winPtr->window); + Tcl_QueueEvent((Tcl_Event *)eventPtr, TCL_QUEUE_TAIL); +} + +/* + *---------------------------------------------------------------------- + * + * ActivateWindow -- + * + * This function is called when an ActivateEvent is processed. + * + * Results: + * Returns 1 to indicate that the event was handled, else 0. + * + * Side effects: + * May activate the toplevel window associated with the event. + * + *---------------------------------------------------------------------- + */ + +static int +ActivateWindow( + Tcl_Event *evPtr, /* Pointer to ActivateEvent. */ + int flags) /* Notifier event mask. */ +{ + ActivateEvent *eventPtr = (ActivateEvent *)evPtr; + TkWindow *winPtr = eventPtr->winPtr; + + if (! (flags & TCL_WINDOW_EVENTS)) { + return 0; + } + + /* + * Ensure the window has not been destroyed while we delayed + * processing the WM_ACTIVATE message [Bug 2899949]. + */ + + if (!IsWindow(eventPtr->hwnd)) { + return 1; + } + + /* + * If the toplevel is in the middle of a move or size operation then + * we must delay handling of this event to avoid stealing the focus + * while the window manage is in control. + */ + + if (eventPtr->flagPtr && *eventPtr->flagPtr) { + return 0; + } + + /* + * If the window is excluded by a grab, call SetFocus on the grabbed + * window instead. [Bug 220908] + */ + + if (winPtr) { + Window window; + if (TkGrabState(winPtr) != TK_GRAB_EXCLUDED) { + window = winPtr->window; + } else { + window = winPtr->dispPtr->grabWinPtr->window; + } + + /* + * Ensure the window was not destroyed while we were postponing + * the activation [Bug 2799589] + */ + + if (window) { + SetFocus(Tk_GetHWND(window)); + } + } + + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinSetForegroundWindow -- + * + * This function is a wrapper for SetForegroundWindow, calling it on the + * wrapper window because it has no affect on child windows. + * + * Results: + * none + * + * Side effects: + * May activate the toplevel window. + * + *---------------------------------------------------------------------- + */ + +void +TkWinSetForegroundWindow( + TkWindow *winPtr) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (wmPtr->wrapper != NULL) { + SetForegroundWindow(wmPtr->wrapper); + } else { + SetForegroundWindow(Tk_GetHWND(winPtr->window)); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelWithdraw -- + * + * This function is to be used by a window manage to withdraw a toplevel + * window. + * + * Results: + * none + * + * Side effects: + * May withdraw the toplevel window. + * + *---------------------------------------------------------------------- + */ + +void +TkpWinToplevelWithDraw( + TkWindow *winPtr) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + wmPtr->flags |= WM_WITHDRAWN; + TkpWmSetState(winPtr, WithdrawnState); +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelIconify -- + * + * This function is to be used by a window manage to iconify a toplevel + * window. + * + * Results: + * none + * + * Side effects: + * May iconify the toplevel window. + * + *---------------------------------------------------------------------- + */ + +void +TkpWinToplevelIconify( + TkWindow *winPtr) +{ + TkpWmSetState(winPtr, IconicState); +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelDeiconify -- + * + * This function is to be used by a window manage to deiconify a toplevel + * window. + * + * Results: + * none + * + * Side effects: + * May deiconify the toplevel window. + * + *---------------------------------------------------------------------- + */ + +void +TkpWinToplevelDeiconify( + TkWindow *winPtr) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + wmPtr->flags &= ~WM_WITHDRAWN; + + /* + * If WM_UPDATE_PENDING is true, a pending UpdateGeometryInfo may need to + * be called first to update a withdrawn toplevel's geometry before it is + * deiconified by TkpWmSetState. Don't bother if we've never been mapped. + */ + + if ((wmPtr->flags & WM_UPDATE_PENDING) + && !(wmPtr->flags & WM_NEVER_MAPPED)) { + Tcl_CancelIdleCall(UpdateGeometryInfo, winPtr); + UpdateGeometryInfo(winPtr); + } + + /* + * If we were in the ZoomState (maximized), 'wm deiconify' should not + * cause the window to shrink + */ + + if (wmPtr->hints.initial_state == ZoomState) { + TkpWmSetState(winPtr, ZoomState); + } else { + TkpWmSetState(winPtr, NormalState); + } + + /* + * An unmapped window will be mapped at idle time by a call to MapFrame. + * That calls CreateWrapper which sets the focus and raises the window. + */ + + if (wmPtr->flags & WM_NEVER_MAPPED) { + return; + } + + /* + * Follow Windows-like style here, raising the window to the top. + */ + + TkWmRestackToplevel(winPtr, Above, NULL); + if (!(Tk_Attributes((Tk_Window) winPtr)->override_redirect)) { + TkSetFocusWin(winPtr, 1); + } +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinGeometryIsControlledByWm -- + * + * This function is to be used by a window manage to see if wm has + * canceled geometry control. + * + * Results: + * 0 - if the window manager has canceled its control + * 1 - if the window manager controls the geometry + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +long +TkpWinToplevelIsControlledByWm( + TkWindow *winPtr) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (!wmPtr) { + return 0; + } + return ((wmPtr->width != -1) && (wmPtr->height != -1)) ? 1 : 0; +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelMove -- + * + * This function is to be used by a container to move an embedded window. + * + * Results: + * position of the upper left frame in a 32-bit long: + * 16-MSBits - x; 16-LSBits - y + * + * Side effects: + * May move the embedded window. + * + *---------------------------------------------------------------------- + */ + +long +TkpWinToplevelMove( + TkWindow *winPtr, + int x, int y) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (wmPtr && x >= 0 && y >= 0 && !TkpWinToplevelIsControlledByWm(winPtr)) { + Tk_MoveToplevelWindow((Tk_Window) winPtr, x, y); + } + return ((winPtr->changes.x << 16) & 0xffff0000) + | (winPtr->changes.y & 0xffff); +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelOverrideRedirect -- + * + * This function is to be used by a container to overrideredirect the + * contaner's frame window. + * + * Results: + * The current overrideredirect value + * + * Side effects: + * May change the overrideredirect value of the container window + * + *---------------------------------------------------------------------- + */ + +long +TkpWinToplevelOverrideRedirect( + TkWindow *winPtr, + int reqValue) +{ + int curValue; + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + curValue = Tk_Attributes((Tk_Window) winPtr)->override_redirect; + if (reqValue < 0) { + return curValue; + } + + if (curValue != reqValue) { + XSetWindowAttributes atts; + + /* + * Only do this if we are really changing value, because it causes + * some funky stuff to occur + */ + + atts.override_redirect = reqValue ? True : False; + Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect, + &atts); + if (!(wmPtr->flags & (WM_NEVER_MAPPED)) + && !(winPtr->flags & TK_EMBEDDED)) { + UpdateWrapper(winPtr); + } + } + return reqValue; +} + +/* + *---------------------------------------------------------------------- + * + * TkpWinToplevelDetachWindow -- + * + * This function is to be usd for changing a toplevel's wrapper or + * container. + * + * Results: + * The window's wrapper/container is removed. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkpWinToplevelDetachWindow( + TkWindow *winPtr) +{ + register WmInfo *wmPtr = winPtr->wmInfoPtr; + + if (winPtr->flags & TK_EMBEDDED) { + int state = SendMessage(wmPtr->wrapper, TK_STATE, -1, -1) - 1; + + SendMessage(wmPtr->wrapper, TK_SETMENU, 0, 0); + SendMessage(wmPtr->wrapper, TK_DETACHWINDOW, 0, 0); + winPtr->flags &= ~TK_EMBEDDED; + winPtr->privatePtr = NULL; + wmPtr->wrapper = None; + if (state >= 0 && state <= 3) { + wmPtr->hints.initial_state = state; + } + } + if (winPtr->flags & TK_TOP_LEVEL) { + TkpWinToplevelOverrideRedirect(winPtr, 1); + } +} + +/* + *---------------------------------------------------------------------- + * + * RemapWindows + * + * Adjust parent/child relation ships of the given window hierarchy. + * + * Results: + * none + * + * Side effects: + * keeps windowing system happy + * + *---------------------------------------------------------------------- + */ + +static void +RemapWindows( + TkWindow *winPtr, + HWND parentHWND) +{ + TkWindow *childPtr; + const char *className = Tk_Class(winPtr); + + /* + * Skip menus as they are handled differently. + */ + + if (className != NULL && strcmp(className, "Menu") == 0) { + return; + } + if (winPtr->window) { + SetParent(Tk_GetHWND(winPtr->window), parentHWND); + } + + /* + * Repeat for all the children. + */ + + for (childPtr = winPtr->childList; childPtr != NULL; + childPtr = childPtr->nextPtr) { + RemapWindows(childPtr, + winPtr->window ? Tk_GetHWND(winPtr->window) : NULL); + } +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/tkWinX.c b/tk8.6/win/tkWinX.c new file mode 100644 index 0000000..fca72c3 --- /dev/null +++ b/tk8.6/win/tkWinX.c @@ -0,0 +1,1955 @@ +/* + * tkWinX.c -- + * + * This file contains Windows emulation procedures for X routines. + * + * Copyright (c) 1995-1996 Sun Microsystems, Inc. + * Copyright (c) 1994 Software Research Associates, Inc. + * Copyright (c) 1998-2000 by Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkWinInt.h" + +/* + * The w32api 1.1 package (included in Mingw 1.1) does not define _WIN32_IE by + * default. Define it here to gain access to the InitCommonControlsEx API in + * commctrl.h. + */ + +#ifndef _WIN32_IE +#define _WIN32_IE 0x0550 /* IE 5.5 */ +#endif + +#include <commctrl.h> +#ifdef _MSC_VER +# pragma comment (lib, "comctl32.lib") +# pragma comment (lib, "advapi32.lib") +#endif + +/* + * The zmouse.h file includes the definition for WM_MOUSEWHEEL. + */ + +#include <zmouse.h> + +/* + * imm.h is needed by HandleIMEComposition + */ + +#include <imm.h> +#ifdef _MSC_VER +# pragma comment (lib, "imm32.lib") +#endif + +/* + * WM_UNICHAR is a message for Unicode input on all windows systems. + * Perhaps this definition should be moved in another file. + */ +#ifndef WM_UNICHAR +#define WM_UNICHAR 0x0109 +#define UNICODE_NOCHAR 0xFFFF +#endif + +/* + * Declarations of static variables used in this file. + */ + +static const char winScreenName[] = ":0"; /* Default name of windows display. */ +static HINSTANCE tkInstance = NULL; /* Application instance handle. */ +static int childClassInitialized; /* Registered child class? */ +static WNDCLASS childClass; /* Window class for child windows. */ +static int tkPlatformId = 0; /* version of Windows platform */ +static int tkWinTheme = 0; /* See TkWinGetPlatformTheme */ +static Tcl_Encoding keyInputEncoding = NULL; + /* The current character encoding for + * keyboard input */ +static int keyInputCharset = -1; /* The Win32 CHARSET for the keyboard + * encoding */ +static Tcl_Encoding unicodeEncoding = NULL; + /* The UNICODE encoding */ + +/* + * Thread local storage. Notice that now each thread must have its own + * TkDisplay structure, since this structure contains most of the thread- + * specific date for threads. + */ + +typedef struct ThreadSpecificData { + TkDisplay *winDisplay; /* TkDisplay structure that represents Windows + * screen. */ + int updatingClipboard; /* If 1, we are updating the clipboard. */ + int surrogateBuffer; /* Buffer for first of surrogate pair. */ +} ThreadSpecificData; +static Tcl_ThreadDataKey dataKey; + +/* + * Forward declarations of functions used in this file. + */ + +static void GenerateXEvent(HWND hwnd, UINT message, + WPARAM wParam, LPARAM lParam); +static unsigned int GetState(UINT message, WPARAM wParam, LPARAM lParam); +static void GetTranslatedKey(XKeyEvent *xkey, UINT type); +static void UpdateInputLanguage(int charset); +static int HandleIMEComposition(HWND hwnd, LPARAM lParam); + +/* + *---------------------------------------------------------------------- + * + * TkGetServerInfo -- + * + * Given a window, this function returns information about the window + * server for that window. This function provides the guts of the "winfo + * server" command. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkGetServerInfo( + Tcl_Interp *interp, /* The server information is returned in this + * interpreter's result. */ + Tk_Window tkwin) /* Token for window; this selects a particular + * display and server. */ +{ + static char buffer[32]; /* Empty string means not initialized yet. */ + OSVERSIONINFOW os; + + if (!buffer[0]) { + HANDLE handle = GetModuleHandle(TEXT("NTDLL")); + int(__stdcall *getversion)(void *) = + (int(__stdcall *)(void *))GetProcAddress(handle, "RtlGetVersion"); + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + if (!getversion || getversion(&os)) { + GetVersionExW(&os); + } + /* Write the first character last, preventing multi-thread issues. */ + sprintf(buffer+1, "indows %d.%d %d %s", (int)os.dwMajorVersion, + (int)os.dwMinorVersion, (int)os.dwBuildNumber, +#ifdef _WIN64 + "Win64" +#else + "Win32" +#endif + ); + buffer[0] = 'W'; + } + Tcl_AppendResult(interp, buffer, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetHINSTANCE -- + * + * Retrieves the global instance handle used by the Tk library. + * + * Results: + * Returns the global instance handle. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +HINSTANCE +Tk_GetHINSTANCE(void) +{ + if (tkInstance == NULL) { + tkInstance = GetModuleHandle(NULL); + } + return tkInstance; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinSetHINSTANCE -- + * + * Sets the global instance handle used by the Tk library. This should be + * called by DllMain. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkWinSetHINSTANCE( + HINSTANCE hInstance) +{ + tkInstance = hInstance; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinXInit -- + * + * Initialize Xlib emulation layer. + * + * Results: + * None. + * + * Side effects: + * Sets up various data structures. + * + *---------------------------------------------------------------------- + */ + +void +TkWinXInit( + HINSTANCE hInstance) +{ + INITCOMMONCONTROLSEX comctl; + CHARSETINFO lpCs; + DWORD lpCP; + + if (childClassInitialized != 0) { + return; + } + childClassInitialized = 1; + + comctl.dwSize = sizeof(INITCOMMONCONTROLSEX); + comctl.dwICC = ICC_WIN95_CLASSES; + if (!InitCommonControlsEx(&comctl)) { + Tcl_Panic("Unable to load common controls?!"); + } + + childClass.style = CS_HREDRAW | CS_VREDRAW; + childClass.cbClsExtra = 0; + childClass.cbWndExtra = 0; + childClass.hInstance = hInstance; + childClass.hbrBackground = NULL; + childClass.lpszMenuName = NULL; + + /* + * Register the Child window class. + */ + + childClass.lpszClassName = TK_WIN_CHILD_CLASS_NAME; + childClass.lpfnWndProc = TkWinChildProc; + childClass.hIcon = NULL; + childClass.hCursor = NULL; + + if (!RegisterClass(&childClass)) { + Tcl_Panic("Unable to register TkChild class"); + } + + /* + * Initialize input language info + */ + + if (GetLocaleInfo(LANGIDFROMLCID(PTR2INT(GetKeyboardLayout(0))), + LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER, + (LPTSTR) &lpCP, sizeof(lpCP)/sizeof(TCHAR)) + && TranslateCharsetInfo(INT2PTR(lpCP), &lpCs, TCI_SRCCODEPAGE)) { + UpdateInputLanguage((int) lpCs.ciCharset); + } + + /* + * Make sure we cleanup on finalize. + */ + + TkCreateExitHandler(TkWinXCleanup, (ClientData) hInstance); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinXCleanup -- + * + * Removes the registered classes for Tk. + * + * Results: + * None. + * + * Side effects: + * Removes window classes from the system. + * + *---------------------------------------------------------------------- + */ + +void +TkWinXCleanup( + ClientData clientData) +{ + HINSTANCE hInstance = (HINSTANCE) clientData; + + /* + * Clean up our own class. + */ + + if (childClassInitialized) { + childClassInitialized = 0; + UnregisterClass(TK_WIN_CHILD_CLASS_NAME, hInstance); + } + + if (unicodeEncoding != NULL) { + Tcl_FreeEncoding(unicodeEncoding); + unicodeEncoding = NULL; + } + + /* + * And let the window manager clean up its own class(es). + */ + + TkWinWmCleanup(hInstance); + TkWinCleanupContainerList(); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetPlatformId -- + * + * Determines whether running under NT, 95, or Win32s, to allow runtime + * conditional code. Win32s is no longer supported. + * + * Results: + * The return value is one of: + * VER_PLATFORM_WIN32s Win32s on Windows 3.1 (not supported) + * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95, 98, ME (not supported) + * VER_PLATFORM_WIN32_NT Win32 on Windows XP, Vista, Windows 7, Windows 8 + * VER_PLATFORM_WIN32_CE Win32 on Windows CE + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +int +TkWinGetPlatformId(void) +{ + if (tkPlatformId == 0) { + OSVERSIONINFOW os; + + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&os); + tkPlatformId = os.dwPlatformId; + + /* + * Set tkWinTheme to be TK_THEME_WIN_XP or TK_THEME_WIN_CLASSIC. The + * TK_THEME_WIN_CLASSIC could be set even when running under XP if the + * windows classic theme was selected. + */ + + if ((os.dwPlatformId == VER_PLATFORM_WIN32_NT) && + (os.dwMajorVersion == 5 && os.dwMinorVersion == 1)) { + HKEY hKey; + LPCTSTR szSubKey = TEXT("Control Panel\\Appearance"); + LPCTSTR szCurrent = TEXT("Current"); + DWORD dwSize = 200; + char pBuffer[200]; + + memset(pBuffer, 0, dwSize); + if (RegOpenKeyEx(HKEY_CURRENT_USER, szSubKey, 0L, + KEY_READ, &hKey) != ERROR_SUCCESS) { + tkWinTheme = TK_THEME_WIN_XP; + } else { + RegQueryValueEx(hKey, szCurrent, NULL, NULL, (LPBYTE) pBuffer, &dwSize); + RegCloseKey(hKey); + if (strcmp(pBuffer, "Windows Standard") == 0) { + tkWinTheme = TK_THEME_WIN_CLASSIC; + } else { + tkWinTheme = TK_THEME_WIN_XP; + } + } + } else { + tkWinTheme = TK_THEME_WIN_CLASSIC; + } + } + return tkPlatformId; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetPlatformTheme -- + * + * Return the Windows drawing style we should be using. + * + * Results: + * The return value is one of: + * TK_THEME_WIN_CLASSIC 95/98/NT or XP in classic mode + * TK_THEME_WIN_XP XP not in classic mode + * + * Side effects: + * Could invoke TkWinGetPlatformId. + * + *---------------------------------------------------------------------- + */ + +int +TkWinGetPlatformTheme(void) +{ + if (tkPlatformId == 0) { + TkWinGetPlatformId(); + } + return tkWinTheme; +} + +/* + *---------------------------------------------------------------------- + * + * TkGetDefaultScreenName -- + * + * Returns the name of the screen that Tk should use during + * initialization. + * + * Results: + * Returns a statically allocated string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +const char * +TkGetDefaultScreenName( + Tcl_Interp *interp, /* Not used. */ + const char *screenName) /* If NULL, use default string. */ +{ + if ((screenName == NULL) || (screenName[0] == '\0')) { + screenName = winScreenName; + } + return screenName; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinDisplayChanged -- + * + * Called to set up initial screen info or when an event indicated + * display (screen) change. + * + * Results: + * None. + * + * Side effects: + * May change info regarding the screen. + * + *---------------------------------------------------------------------- + */ + +void +TkWinDisplayChanged( + Display *display) +{ + HDC dc; + Screen *screen; + + if (display == NULL || display->screens == NULL) { + return; + } + screen = display->screens; + + dc = GetDC(NULL); + screen->width = GetDeviceCaps(dc, HORZRES); + screen->height = GetDeviceCaps(dc, VERTRES); + screen->mwidth = MulDiv(screen->width, 254, + GetDeviceCaps(dc, LOGPIXELSX) * 10); + screen->mheight = MulDiv(screen->height, 254, + GetDeviceCaps(dc, LOGPIXELSY) * 10); + + /* + * On windows, when creating a color bitmap, need two pieces of + * information: the number of color planes and the number of pixels per + * plane. Need to remember both quantities so that when constructing an + * HBITMAP for offscreen rendering, we can specify the correct value for + * the number of planes. Otherwise the HBITMAP won't be compatible with + * the HWND and we'll just get blank spots copied onto the screen. + */ + + screen->ext_data = INT2PTR(GetDeviceCaps(dc, PLANES)); + screen->root_depth = GetDeviceCaps(dc, BITSPIXEL) * PTR2INT(screen->ext_data); + + if (screen->root_visual != NULL) { + ckfree(screen->root_visual); + } + screen->root_visual = ckalloc(sizeof(Visual)); + screen->root_visual->visualid = 0; + if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { + screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE); + screen->root_visual->class = PseudoColor; + screen->root_visual->red_mask = 0x0; + screen->root_visual->green_mask = 0x0; + screen->root_visual->blue_mask = 0x0; + } else if (screen->root_depth == 4) { + screen->root_visual->class = StaticColor; + screen->root_visual->map_entries = 16; + } else if (screen->root_depth == 8) { + screen->root_visual->class = StaticColor; + screen->root_visual->map_entries = 256; + } else if (screen->root_depth == 12) { + screen->root_visual->class = TrueColor; + screen->root_visual->map_entries = 32; + screen->root_visual->red_mask = 0xf0; + screen->root_visual->green_mask = 0xf000; + screen->root_visual->blue_mask = 0xf00000; + } else if (screen->root_depth == 16) { + screen->root_visual->class = TrueColor; + screen->root_visual->map_entries = 64; + screen->root_visual->red_mask = 0xf8; + screen->root_visual->green_mask = 0xfc00; + screen->root_visual->blue_mask = 0xf80000; + } else if (screen->root_depth >= 24) { + screen->root_visual->class = TrueColor; + screen->root_visual->map_entries = 256; + screen->root_visual->red_mask = 0xff; + screen->root_visual->green_mask = 0xff00; + screen->root_visual->blue_mask = 0xff0000; + } + screen->root_visual->bits_per_rgb = screen->root_depth; + ReleaseDC(NULL, dc); + + if (screen->cmap != None) { + XFreeColormap(display, screen->cmap); + } + screen->cmap = XCreateColormap(display, None, screen->root_visual, + AllocNone); +} + +/* + *---------------------------------------------------------------------- + * + * TkpOpenDisplay -- + * + * Create the Display structure and fill it with device specific + * information. + * + * Results: + * Returns a TkDisplay structure on success or NULL on failure. + * + * Side effects: + * Allocates a new TkDisplay structure. + * + *---------------------------------------------------------------------- + */ + +TkDisplay * +TkpOpenDisplay( + const char *display_name) +{ + Screen *screen; + TkWinDrawable *twdPtr; + Display *display; + ThreadSpecificData *tsdPtr = + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (tsdPtr->winDisplay != NULL) { + if (!strcmp(tsdPtr->winDisplay->display->display_name, display_name)) { + return tsdPtr->winDisplay; + } else { + return NULL; + } + } + + display = ckalloc(sizeof(Display)); + ZeroMemory(display, sizeof(Display)); + + display->display_name = ckalloc(strlen(display_name) + 1); + strcpy(display->display_name, display_name); + + display->cursor_font = 1; + display->nscreens = 1; + display->request = 1; + display->qlen = 0; + + screen = ckalloc(sizeof(Screen)); + ZeroMemory(screen, sizeof(Screen)); + screen->display = display; + + /* + * Set up the root window. + */ + + twdPtr = ckalloc(sizeof(TkWinDrawable)); + if (twdPtr == NULL) { + return None; + } + twdPtr->type = TWD_WINDOW; + twdPtr->window.winPtr = NULL; + twdPtr->window.handle = NULL; + screen->root = (Window)twdPtr; + + /* + * Note that these pixel values are not palette relative. + */ + + screen->white_pixel = RGB(255, 255, 255); + screen->black_pixel = RGB(0, 0, 0); + screen->cmap = None; + + display->screens = screen; + display->nscreens = 1; + display->default_screen = 0; + + TkWinDisplayChanged(display); + + tsdPtr->winDisplay = ckalloc(sizeof(TkDisplay)); + ZeroMemory(tsdPtr->winDisplay, sizeof(TkDisplay)); + tsdPtr->winDisplay->display = display; + tsdPtr->updatingClipboard = FALSE; + + return tsdPtr->winDisplay; +} + +/* + *---------------------------------------------------------------------- + * + * TkpCloseDisplay -- + * + * Closes and deallocates a Display structure created with the + * TkpOpenDisplay function. + * + * Results: + * None. + * + * Side effects: + * Frees up memory. + * + *---------------------------------------------------------------------- + */ + +void +TkpCloseDisplay( + TkDisplay *dispPtr) +{ + Display *display = dispPtr->display; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (dispPtr != tsdPtr->winDisplay) { + Tcl_Panic("TkpCloseDisplay: tried to call TkpCloseDisplay on another display"); + return; /* not reached */ + } + + tsdPtr->winDisplay = NULL; + + if (display->display_name != NULL) { + ckfree(display->display_name); + } + if (display->screens != NULL) { + if (display->screens->root_visual != NULL) { + ckfree(display->screens->root_visual); + } + if (display->screens->root != None) { + ckfree(display->screens->root); + } + if (display->screens->cmap != None) { + XFreeColormap(display, display->screens->cmap); + } + ckfree(display->screens); + } + ckfree(display); +} + +/* + *---------------------------------------------------------------------- + * + * TkClipCleanup -- + * + * This function is called to cleanup resources associated with claiming + * clipboard ownership and for receiving selection get results. This + * function is called in tkWindow.c. This has to be called by the display + * cleanup function because we still need the access display elements. + * + * Results: + * None. + * + * Side effects: + * Resources are freed - the clipboard may no longer be used. + * + *---------------------------------------------------------------------- + */ + +void +TkClipCleanup( + TkDisplay *dispPtr) /* Display associated with clipboard. */ +{ + if (dispPtr->clipWindow != NULL) { + Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, + dispPtr->applicationAtom); + Tk_DeleteSelHandler(dispPtr->clipWindow, dispPtr->clipboardAtom, + dispPtr->windowAtom); + + Tk_DestroyWindow(dispPtr->clipWindow); + Tcl_Release((ClientData) dispPtr->clipWindow); + dispPtr->clipWindow = NULL; + } +} + +/* + *---------------------------------------------------------------------- + * + * XBell -- + * + * Generate a beep. + * + * Results: + * None. + * + * Side effects: + * Plays a sounds out the system speakers. + * + *---------------------------------------------------------------------- + */ + +int +XBell( + Display *display, + int percent) +{ + MessageBeep(MB_OK); + return Success; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinChildProc -- + * + * Callback from Windows whenever an event occurs on a child window. + * + * Results: + * Standard Windows return value. + * + * Side effects: + * May process events off the Tk event queue. + * + *---------------------------------------------------------------------- + */ + +LRESULT CALLBACK +TkWinChildProc( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + LRESULT result; + + switch (message) { + case WM_INPUTLANGCHANGE: + UpdateInputLanguage((int) wParam); + result = 1; + break; + + case WM_IME_COMPOSITION: + result = 0; + if (HandleIMEComposition(hwnd, lParam) == 0) { + result = DefWindowProc(hwnd, message, wParam, lParam); + } + break; + + case WM_SETCURSOR: + /* + * Short circuit the WM_SETCURSOR message since we set the cursor + * elsewhere. + */ + + result = TRUE; + break; + + case WM_CREATE: + case WM_ERASEBKGND: + result = 0; + break; + + case WM_PAINT: + GenerateXEvent(hwnd, message, wParam, lParam); + result = DefWindowProc(hwnd, message, wParam, lParam); + break; + + case TK_CLAIMFOCUS: + case TK_GEOMETRYREQ: + case TK_ATTACHWINDOW: + case TK_DETACHWINDOW: + case TK_ICONIFY: + case TK_DEICONIFY: + case TK_MOVEWINDOW: + case TK_WITHDRAW: + case TK_RAISEWINDOW: + case TK_GETFRAMEWID: + case TK_OVERRIDEREDIRECT: + case TK_SETMENU: + case TK_STATE: + case TK_INFO: + result = TkWinEmbeddedEventProc(hwnd, message, wParam, lParam); + break; + + case WM_UNICHAR: + if (wParam == UNICODE_NOCHAR) { + /* If wParam is UNICODE_NOCHAR and the application processes + * this message, then return TRUE. */ + result = 1; + } else { + /* If the event was translated, we must return 0 */ + if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { + result = 0; + } else { + result = 1; + } + } + break; + + default: + if (!Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) { + result = DefWindowProc(hwnd, message, wParam, lParam); + } + break; + } + + /* + * Handle any newly queued events before returning control to Windows. + */ + + Tcl_ServiceAll(); + return result; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_TranslateWinEvent -- + * + * This function is called by widget window functions to handle the + * translation from Win32 events to Tk events. + * + * Results: + * Returns 1 if the event was handled, else 0. + * + * Side effects: + * Depends on the event. + * + *---------------------------------------------------------------------- + */ + +int +Tk_TranslateWinEvent( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam, + LRESULT *resultPtr) +{ + *resultPtr = 0; + switch (message) { + case WM_RENDERFORMAT: { + TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + + if (winPtr) { + TkWinClipboardRender(winPtr->dispPtr, wParam); + } + return 1; + } + + case WM_RENDERALLFORMATS: { + TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + + if (winPtr && OpenClipboard(hwnd)) { + /* + * Make sure that nobody had taken ownership of the clipboard + * before we opened it. + */ + + if (GetClipboardOwner() == hwnd) { + TkWinClipboardRender(winPtr->dispPtr, CF_TEXT); + } + CloseClipboard(); + } + return 1; + } + + case WM_COMMAND: + case WM_NOTIFY: + case WM_VSCROLL: + case WM_HSCROLL: { + /* + * Reflect these messages back to the sender so that they can be + * handled by the window proc for the control. Note that we need to be + * careful not to reflect a message that is targeted to this window, + * or we will loop. + */ + + HWND target = (message == WM_NOTIFY) + ? ((NMHDR*)lParam)->hwndFrom : (HWND) lParam; + + if (target && target != hwnd) { + *resultPtr = SendMessage(target, message, wParam, lParam); + return 1; + } + break; + } + + case WM_LBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDOWN: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDOWN: + case WM_RBUTTONDBLCLK: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_MOUSEMOVE: + Tk_PointerEvent(hwnd, (short) LOWORD(lParam), (short) HIWORD(lParam)); + return 1; + + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + if (wParam == VK_PACKET) { + /* + * This will trigger WM_CHAR event(s) with unicode data. + */ + *resultPtr = + PostMessageW(hwnd, message, HIWORD(lParam), LOWORD(lParam)); + return 1; + } + /* else fall through */ + case WM_CLOSE: + case WM_SETFOCUS: + case WM_KILLFOCUS: + case WM_DESTROYCLIPBOARD: + case WM_UNICHAR: + case WM_CHAR: + case WM_SYSKEYUP: + case WM_KEYUP: + case WM_MOUSEWHEEL: + GenerateXEvent(hwnd, message, wParam, lParam); + return 1; + case WM_MENUCHAR: + GenerateXEvent(hwnd, message, wParam, lParam); + + /* + * MNC_CLOSE is the only one that looks right. This is a hack. + */ + + *resultPtr = MAKELONG (0, MNC_CLOSE); + return 1; + } + return 0; +} + +/* + *---------------------------------------------------------------------- + * + * GenerateXEvent -- + * + * This routine generates an X event from the corresponding Windows + * event. + * + * Results: + * None. + * + * Side effects: + * Queues one or more X events. + * + *---------------------------------------------------------------------- + */ + +static void +GenerateXEvent( + HWND hwnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + XEvent event; + TkWindow *winPtr; + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + if (message == WM_MOUSEWHEEL) { + union {LPARAM lParam; POINTS point;} root; + POINT pos; + root.lParam = lParam; + + /* + * Redirect mousewheel events to the window containing the cursor. + * That feels much less strange to users, and is how all the other + * platforms work. + */ + + pos.x = root.point.x; + pos.y = root.point.y; + hwnd = WindowFromPoint(pos); + } + + winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + if (!winPtr || winPtr->window == None) { + return; + } + + memset(&event, 0, sizeof(XEvent)); + event.xany.serial = winPtr->display->request++; + event.xany.send_event = False; + event.xany.display = winPtr->display; + event.xany.window = winPtr->window; + + switch (message) { + case WM_PAINT: { + PAINTSTRUCT ps; + + event.type = Expose; + BeginPaint(hwnd, &ps); + event.xexpose.x = ps.rcPaint.left; + event.xexpose.y = ps.rcPaint.top; + event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left; + event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top; + EndPaint(hwnd, &ps); + event.xexpose.count = 0; + break; + } + + case WM_CLOSE: + event.type = ClientMessage; + event.xclient.message_type = + Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"); + event.xclient.format = 32; + event.xclient.data.l[0] = + Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW"); + break; + + case WM_SETFOCUS: + case WM_KILLFOCUS: { + TkWindow *otherWinPtr = (TkWindow *) Tk_HWNDToWindow((HWND) wParam); + + /* + * Compare toplevel windows to avoid reporting focus changes within + * the same toplevel. + */ + + while (!(winPtr->flags & TK_TOP_LEVEL)) { + winPtr = winPtr->parentPtr; + if (winPtr == NULL) { + return; + } + } + while (otherWinPtr && !(otherWinPtr->flags & TK_TOP_LEVEL)) { + otherWinPtr = otherWinPtr->parentPtr; + } + + /* + * Do a catch-all Tk_SetCaretPos here to make sure that the window + * receiving focus sets the caret at least once. + */ + + if (message == WM_SETFOCUS) { + Tk_SetCaretPos((Tk_Window) winPtr, 0, 0, 0); + } + + if (otherWinPtr == winPtr) { + return; + } + + event.xany.window = winPtr->window; + event.type = (message == WM_SETFOCUS) ? FocusIn : FocusOut; + event.xfocus.mode = NotifyNormal; + event.xfocus.detail = NotifyNonlinear; + + /* + * Destroy the caret if we own it. If we are moving to another Tk + * window, it will reclaim and reposition it with Tk_SetCaretPos. + */ + + if (message == WM_KILLFOCUS) { + DestroyCaret(); + } + break; + } + + case WM_DESTROYCLIPBOARD: + if (tsdPtr->updatingClipboard == TRUE) { + /* + * We want to avoid this event if we are the ones that caused this + * event. + */ + + return; + } + event.type = SelectionClear; + event.xselectionclear.selection = + Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD"); + event.xselectionclear.time = TkpGetMS(); + break; + + case WM_MOUSEWHEEL: + case WM_CHAR: + case WM_UNICHAR: + case WM_SYSKEYDOWN: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_KEYUP: { + unsigned int state = GetState(message, wParam, lParam); + Time time = TkpGetMS(); + POINT clientPoint; + union {DWORD msgpos; POINTS point;} root; /* Note: POINT and POINTS are different */ + + /* + * Compute the screen and window coordinates of the event. + */ + + root.msgpos = GetMessagePos(); + clientPoint.x = root.point.x; + clientPoint.y = root.point.y; + ScreenToClient(hwnd, &clientPoint); + + /* + * Set up the common event fields. + */ + + event.xbutton.root = RootWindow(winPtr->display, winPtr->screenNum); + event.xbutton.subwindow = None; + event.xbutton.x = clientPoint.x; + event.xbutton.y = clientPoint.y; + event.xbutton.x_root = root.point.x; + event.xbutton.y_root = root.point.y; + event.xbutton.state = state; + event.xbutton.time = time; + event.xbutton.same_screen = True; + + /* + * Now set up event specific fields. + */ + + switch (message) { + case WM_MOUSEWHEEL: + /* + * We have invented a new X event type to handle this event. It + * still uses the KeyPress struct. However, the keycode field has + * been overloaded to hold the zDelta of the wheel. Set nbytes to + * 0 to prevent conversion of the keycode to a keysym in + * TkpGetString. [Bug 1118340]. + */ + + event.type = MouseWheelEvent; + event.xany.send_event = -1; + event.xkey.nbytes = 0; + event.xkey.keycode = (short) HIWORD(wParam); + break; + case WM_SYSKEYDOWN: + case WM_KEYDOWN: + /* + * Check for translated characters in the event queue. Setting + * xany.send_event to -1 indicates to the Windows implementation + * of TkpGetString() that this event was generated by windows and + * that the Windows extension xkey.trans_chars is filled with the + * MBCS characters that came from the TranslateMessage call. + */ + + event.type = KeyPress; + event.xany.send_event = -1; + event.xkey.keycode = wParam; + GetTranslatedKey(&event.xkey, (message == WM_KEYDOWN) ? WM_CHAR : + WM_SYSCHAR); + break; + + case WM_SYSKEYUP: + case WM_KEYUP: + /* + * We don't check for translated characters on keyup because Tk + * won't know what to do with them. Instead, we wait for the + * WM_CHAR messages which will follow. + */ + + event.type = KeyRelease; + event.xkey.keycode = wParam; + event.xkey.nbytes = 0; + break; + + case WM_CHAR: + /* + * Synthesize both a KeyPress and a KeyRelease. Strings generated + * by Input Method Editor are handled in the following manner: + * 1. A series of WM_KEYDOWN & WM_KEYUP messages that cause + * GetTranslatedKey() to be called and return immediately + * because the WM_KEYDOWNs have no associated WM_CHAR messages + * -- the IME window is accumulating the characters and + * translating them itself. In the "bind" command, you get an + * event with a mystery keysym and %A == "" for each WM_KEYDOWN + * that actually was meant for the IME. + * 2. A WM_KEYDOWN corresponding to the "confirm typing" + * character. This causes GetTranslatedKey() to be called. + * 3. A WM_IME_NOTIFY message saying that the IME is done. A side + * effect of this message is that GetTranslatedKey() thinks + * this means that there are no WM_CHAR messages and returns + * immediately. In the "bind" command, you get an another event + * with a mystery keysym and %A == "". + * 4. A sequence of WM_CHAR messages that correspond to the + * characters in the IME window. A bunch of simulated + * KeyPress/KeyRelease events will be generated, one for each + * character. Adjacent WM_CHAR messages may actually specify + * the high and low bytes of a multi-byte character -- in that + * case the two WM_CHAR messages will be combined into one + * event. It is the event-consumer's responsibility to convert + * the string returned from XLookupString from system encoding + * to UTF-8. + * 5. And finally we get the WM_KEYUP for the "confirm typing" + * character. + */ + + event.type = KeyPress; + event.xany.send_event = -1; + event.xkey.keycode = 0; + if ((int)wParam & 0xff00) { + int ch1 = wParam & 0xffff; + + if ((ch1 & 0xfc00) == 0xd800) { + tsdPtr->surrogateBuffer = ch1; + return; + } + if ((ch1 & 0xfc00) == 0xdc00) { + ch1 = ((tsdPtr->surrogateBuffer & 0x3ff) << 10) | + (ch1 & 0x3ff) | 0x10000; + tsdPtr->surrogateBuffer = 0; + } + event.xany.send_event = -3; + event.xkey.nbytes = 0; + event.xkey.keycode = ch1; + } else { + event.xkey.nbytes = 1; + event.xkey.trans_chars[0] = (char) wParam; + + if (IsDBCSLeadByte((BYTE) wParam)) { + MSG msg; + + if ((PeekMessage(&msg, NULL, WM_CHAR, WM_CHAR, + PM_NOREMOVE) != 0) + && (msg.message == WM_CHAR)) { + GetMessage(&msg, NULL, WM_CHAR, WM_CHAR); + event.xkey.nbytes = 2; + event.xkey.trans_chars[1] = (char) msg.wParam; + } + } + } + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + event.type = KeyRelease; + break; + + case WM_UNICHAR: { + event.type = KeyPress; + event.xany.send_event = -3; + event.xkey.keycode = wParam; + event.xkey.nbytes = 0; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + event.type = KeyRelease; + break; + } + + } + break; + } + + default: + /* + * Don't know how to translate this event, so ignore it. (It probably + * should not have got here, but ignoring it should be harmless.) + */ + + return; + } + + /* + * Post the translated event to the main Tk event queue. + */ + + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); +} + +/* + *---------------------------------------------------------------------- + * + * GetState -- + * + * This function constructs a state mask for the mouse buttons and + * modifier keys as they were before the event occured. + * + * Results: + * Returns a composite value of all the modifier and button state flags + * that were set at the time the event occurred. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static unsigned int +GetState( + UINT message, /* Win32 message type */ + WPARAM wParam, /* wParam of message, used if key message */ + LPARAM lParam) /* lParam of message, used if key message */ +{ + int mask; + int prevState; /* 1 if key was previously down */ + unsigned int state = TkWinGetModifierState(); + + /* + * If the event is a key press or release, we check for modifier keys so + * we can report the state of the world before the event. + */ + + if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN + || message == WM_SYSKEYUP || message == WM_KEYUP) { + mask = 0; + prevState = HIWORD(lParam) & KF_REPEAT; + switch(wParam) { + case VK_SHIFT: + mask = ShiftMask; + break; + case VK_CONTROL: + mask = ControlMask; + break; + case VK_MENU: + mask = ALT_MASK; + break; + case VK_CAPITAL: + if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { + mask = LockMask; + prevState = ((state & mask) ^ prevState) ? 0 : 1; + } + break; + case VK_NUMLOCK: + if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { + mask = Mod1Mask; + prevState = ((state & mask) ^ prevState) ? 0 : 1; + } + break; + case VK_SCROLL: + if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { + mask = Mod3Mask; + prevState = ((state & mask) ^ prevState) ? 0 : 1; + } + break; + } + if (prevState) { + state |= mask; + } else { + state &= ~mask; + } + if (HIWORD(lParam) & KF_EXTENDED) { + state |= EXTENDED_MASK; + } + } + return state; +} + +/* + *---------------------------------------------------------------------- + * + * GetTranslatedKey -- + * + * Retrieves WM_CHAR messages that are placed on the system queue by the + * TranslateMessage system call and places them in the given KeyPress + * event. + * + * Results: + * Sets the trans_chars and nbytes member of the key event. + * + * Side effects: + * Removes any WM_CHAR messages waiting on the top of the system event + * queue. + * + *---------------------------------------------------------------------- + */ + +static void +GetTranslatedKey( + XKeyEvent *xkey, + UINT type) +{ + MSG msg; + + xkey->nbytes = 0; + + while ((xkey->nbytes < XMaxTransChars) + && (PeekMessageA(&msg, NULL, type, type, PM_NOREMOVE) != 0)) { + if (msg.message != type) { + break; + } + + GetMessageA(&msg, NULL, type, type); + + /* + * If this is a normal character message, we may need to strip off the + * Alt modifier (e.g. Alt-digits). Note that we don't want to do this + * for system messages, because those were presumably generated as an + * Alt-char sequence (e.g. accelerator keys). + */ + + if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) { + xkey->state = 0; + } + xkey->trans_chars[xkey->nbytes] = (char) msg.wParam; + xkey->nbytes++; + + if (((unsigned short) msg.wParam) > ((unsigned short) 0xff)) { + /* + * Some "addon" input devices, such as the popular PenPower + * Chinese writing pad, generate 16 bit values in WM_CHAR messages + * (instead of passing them in two separate WM_CHAR messages + * containing two 8-bit values. + */ + + xkey->trans_chars[xkey->nbytes] = (char) (msg.wParam >> 8); + xkey->nbytes ++; + } + } +} + +/* + *---------------------------------------------------------------------- + * + * UpdateInputLanguage -- + * + * Gets called when a WM_INPUTLANGCHANGE message is received by the Tk + * child window function. This message is sent by the Input Method Editor + * system when the user chooses a different input method. All subsequent + * WM_CHAR messages will contain characters in the new encoding. We + * record the new encoding so that TkpGetString() knows how to correctly + * translate the WM_CHAR into unicode. + * + * Results: + * Records the new encoding in keyInputEncoding. + * + * Side effects: + * Old value of keyInputEncoding is freed. + * + *---------------------------------------------------------------------- + */ + +static void +UpdateInputLanguage( + int charset) +{ + CHARSETINFO charsetInfo; + Tcl_Encoding encoding; + char codepage[4 + TCL_INTEGER_SPACE]; + + if (keyInputCharset == charset) { + return; + } + if (TranslateCharsetInfo(INT2PTR(charset), &charsetInfo, + TCI_SRCCHARSET) == 0) { + /* + * Some mysterious failure. + */ + + return; + } + + wsprintfA(codepage, "cp%d", charsetInfo.ciACP); + + if ((encoding = Tcl_GetEncoding(NULL, codepage)) == NULL) { + /* + * The encoding is not supported by Tcl. + */ + + return; + } + + if (keyInputEncoding != NULL) { + Tcl_FreeEncoding(keyInputEncoding); + } + + keyInputEncoding = encoding; + keyInputCharset = charset; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetKeyInputEncoding -- + * + * Returns the current keyboard input encoding selected by the user (with + * WM_INPUTLANGCHANGE events). + * + * Results: + * The current keyboard input encoding. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Encoding +TkWinGetKeyInputEncoding(void) +{ + return keyInputEncoding; +} + +/* + *---------------------------------------------------------------------- + * + * TkWinGetUnicodeEncoding -- + * + * Returns the cached unicode encoding. + * + * Results: + * The unicode encoding. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Tcl_Encoding +TkWinGetUnicodeEncoding(void) +{ + if (unicodeEncoding == NULL) { + unicodeEncoding = Tcl_GetEncoding(NULL, "unicode"); + } + return unicodeEncoding; +} + +/* + *---------------------------------------------------------------------- + * + * HandleIMEComposition -- + * + * This function works around a deficiency in some versions of Windows + * 2000 to make it possible to entry multi-lingual characters under all + * versions of Windows 2000. + * + * When an Input Method Editor (IME) is ready to send input characters to + * an application, it sends a WM_IME_COMPOSITION message with the + * GCS_RESULTSTR. However, The DefWindowProc() on English Windows 2000 + * arbitrarily converts all non-Latin-1 characters in the composition to + * "?". + * + * This function correctly processes the composition data and sends the + * UNICODE values of the composed characters to TK's event queue. + * + * Results: + * If this function has processed the composition data, returns 1. + * Otherwise returns 0. + * + * Side effects: + * Key events are put into the TK event queue. + * + *---------------------------------------------------------------------- + */ + +static int +HandleIMEComposition( + HWND hwnd, /* Window receiving the message. */ + LPARAM lParam) /* Flags for the WM_IME_COMPOSITION message */ +{ + HIMC hIMC; + int n; + int high = 0; + + if ((lParam & GCS_RESULTSTR) == 0) { + /* + * Composition is not finished yet. + */ + + return 0; + } + + hIMC = ImmGetContext(hwnd); + if (!hIMC) { + return 0; + } + + n = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0); + + if (n > 0) { + WCHAR *buff = (WCHAR *) ckalloc(n); + TkWindow *winPtr; + XEvent event; + int i; + + n = ImmGetCompositionString(hIMC, GCS_RESULTSTR, buff, (unsigned) n) / 2; + + /* + * Set up the fields pertinent to key event. + * + * We set send_event to the special value of -3, so that TkpGetString + * in tkWinKey.c knows that keycode already contains a UNICODE + * char and there's no need to do encoding conversion. + * + * Note that the event *must* be zeroed out first; Tk plays cunning + * games with the overalls structure. [Bug 2992129] + */ + + winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); + + memset(&event, 0, sizeof(XEvent)); + event.xkey.serial = winPtr->display->request++; + event.xkey.send_event = -3; + event.xkey.display = winPtr->display; + event.xkey.window = winPtr->window; + event.xkey.root = RootWindow(winPtr->display, winPtr->screenNum); + event.xkey.subwindow = None; + event.xkey.state = TkWinGetModifierState(); + event.xkey.time = TkpGetMS(); + event.xkey.same_screen = True; + + for (i=0; i<n; ) { + /* + * Simulate a pair of KeyPress and KeyRelease events for each + * UNICODE character in the composition. + */ + + event.xkey.keycode = buff[i++]; + + if ((event.xkey.keycode & 0xfc00) == 0xd800) { + high = ((event.xkey.keycode & 0x3ff) << 10) + 0x10000; + break; + } else if (high && (event.xkey.keycode & 0xfc00) == 0xdc00) { + event.xkey.keycode &= 0x3ff; + event.xkey.keycode += high; + high = 0; + } + event.type = KeyPress; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + + event.type = KeyRelease; + Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); + } + + ckfree(buff); + } + ImmReleaseContext(hwnd, hIMC); + return 1; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_FreeXId -- + * + * This interface is not needed under Windows. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +Tk_FreeXId( + Display *display, + XID xid) +{ + /* Do nothing */ +} + +/* + *---------------------------------------------------------------------- + * + * TkWinResendEvent -- + * + * This function converts an X event into a Windows event and invokes the + * specified windo function. + * + * Results: + * A standard Windows result. + * + * Side effects: + * Invokes the window function + * + *---------------------------------------------------------------------- + */ + +LRESULT +TkWinResendEvent( + WNDPROC wndproc, + HWND hwnd, + XEvent *eventPtr) +{ + UINT msg; + WPARAM wparam; + LPARAM lparam; + + if (eventPtr->type != ButtonPress) { + return 0; + } + + switch (eventPtr->xbutton.button) { + case Button1: + msg = WM_LBUTTONDOWN; + wparam = MK_LBUTTON; + break; + case Button2: + msg = WM_MBUTTONDOWN; + wparam = MK_MBUTTON; + break; + case Button3: + msg = WM_RBUTTONDOWN; + wparam = MK_RBUTTON; + break; + default: + return 0; + } + + if (eventPtr->xbutton.state & Button1Mask) { + wparam |= MK_LBUTTON; + } + if (eventPtr->xbutton.state & Button2Mask) { + wparam |= MK_MBUTTON; + } + if (eventPtr->xbutton.state & Button3Mask) { + wparam |= MK_RBUTTON; + } + if (eventPtr->xbutton.state & ShiftMask) { + wparam |= MK_SHIFT; + } + if (eventPtr->xbutton.state & ControlMask) { + wparam |= MK_CONTROL; + } + lparam = MAKELPARAM((short) eventPtr->xbutton.x, + (short) eventPtr->xbutton.y); + return CallWindowProc(wndproc, hwnd, msg, wparam, lparam); +} + +/* + *---------------------------------------------------------------------- + * + * TkpGetMS -- + * + * Return a relative time in milliseconds. It doesn't matter when the + * epoch was. + * + * Results: + * Number of milliseconds. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +unsigned long +TkpGetMS(void) +{ + return GetTickCount(); +} + +/* + *---------------------------------------------------------------------- + * + * TkWinUpdatingClipboard -- + * + * + * Results: + * Number of milliseconds. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TkWinUpdatingClipboard( + int mode) +{ + ThreadSpecificData *tsdPtr = (ThreadSpecificData *) + Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); + + tsdPtr->updatingClipboard = mode; +} + +/* + *---------------------------------------------------------------------- + * + * Tk_SetCaretPos -- + * + * This enables correct movement of focus in the MS Magnifier, as well as + * allowing us to correctly position the IME Window. The following Win32 + * APIs are used to work with MS caret: + * + * CreateCaret DestroyCaret SetCaretPos GetCaretPos + * + * Only one instance of caret can be active at any time (e.g. + * DestroyCaret API does not take any argument such as handle). Since + * do-it-right approach requires to track the create/destroy caret status + * all the time in a global scope among windows (or widgets), we just + * implement this minimal setup to get the job done. + * + * Results: + * None + * + * Side effects: + * Sets the global Windows caret position. + * + *---------------------------------------------------------------------- + */ + +void +Tk_SetCaretPos( + Tk_Window tkwin, + int x, int y, + int height) +{ + static HWND caretHWND = NULL; + TkCaret *caretPtr = &(((TkWindow *) tkwin)->dispPtr->caret); + Window win; + + /* + * Prevent processing anything if the values haven't changed. Windows only + * has one display, so we can do this with statics. + */ + + if ((caretPtr->winPtr == ((TkWindow *) tkwin)) + && (caretPtr->x == x) && (caretPtr->y == y)) { + return; + } + + caretPtr->winPtr = ((TkWindow *) tkwin); + caretPtr->x = x; + caretPtr->y = y; + caretPtr->height = height; + + /* + * We adjust to the toplevel to get the coords right, as setting the IME + * composition window is based on the toplevel hwnd, so ignore height. + */ + + while (!Tk_IsTopLevel(tkwin)) { + x += Tk_X(tkwin); + y += Tk_Y(tkwin); + tkwin = Tk_Parent(tkwin); + if (tkwin == NULL) { + return; + } + } + + win = Tk_WindowId(tkwin); + if (win) { + HIMC hIMC; + HWND hwnd = Tk_GetHWND(win); + + if (hwnd != caretHWND) { + DestroyCaret(); + if (CreateCaret(hwnd, NULL, 0, 0)) { + caretHWND = hwnd; + } + } + + if (!SetCaretPos(x, y) && CreateCaret(hwnd, NULL, 0, 0)) { + caretHWND = hwnd; + SetCaretPos(x, y); + } + + /* + * The IME composition window should be updated whenever the caret + * position is changed because a clause of the composition string may + * be converted to the final characters and the other clauses still + * stay on the composition window. -- yamamoto + */ + + hIMC = ImmGetContext(hwnd); + if (hIMC) { + COMPOSITIONFORM cform; + + cform.dwStyle = CFS_POINT; + cform.ptCurrentPos.x = x; + cform.ptCurrentPos.y = y; + ImmSetCompositionWindow(hIMC, &cform); + ImmReleaseContext(hwnd, hIMC); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * Tk_GetUserInactiveTime -- + * + * Return the number of milliseconds the user was inactive. + * + * Results: + * Milliseconds of user inactive time or -1 if the user32.dll doesn't + * have the symbol GetLastInputInfo or GetLastInputInfo returns an error. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +long +Tk_GetUserInactiveTime( + Display *dpy) /* Ignored on Windows */ +{ + LASTINPUTINFO li; + + li.cbSize = sizeof(li); + if (!(BOOL)GetLastInputInfo(&li)) { + return -1; + } + + /* + * Last input info is in milliseconds, since restart time. + */ + + return (GetTickCount()-li.dwTime); +} + +/* + *---------------------------------------------------------------------- + * + * Tk_ResetUserInactiveTime -- + * + * Reset the user inactivity timer + * + * Results: + * none + * + * Side effects: + * The user inactivity timer of the underlaying windowing system is reset + * to zero. + * + *---------------------------------------------------------------------- + */ + +void +Tk_ResetUserInactiveTime( + Display *dpy) +{ + INPUT inp; + + inp.type = INPUT_MOUSE; + inp.mi.dx = 0; + inp.mi.dy = 0; + inp.mi.mouseData = 0; + inp.mi.dwFlags = MOUSEEVENTF_MOVE; + inp.mi.time = 0; + inp.mi.dwExtraInfo = (DWORD) 0; + + SendInput(1, &inp, sizeof(inp)); +} + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/ttkWinMonitor.c b/tk8.6/win/ttkWinMonitor.c new file mode 100644 index 0000000..6e46374 --- /dev/null +++ b/tk8.6/win/ttkWinMonitor.c @@ -0,0 +1,161 @@ +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#endif + +#include <tkWinInt.h> +#include "ttk/ttkTheme.h" + +#if !defined(WM_THEMECHANGED) +#define WM_THEMECHANGED 0x031A +#endif + +static LRESULT WINAPI WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp); + +/* + * RegisterSystemColors -- + * Register all known Windows system colors (as per GetSysColor) as Tk + * named colors. + */ + +typedef struct { + const char *name; + int index; +} SystemColorEntry; + +static SystemColorEntry sysColors[] = { + { "System3dDarkShadow", COLOR_3DDKSHADOW }, + { "System3dLight", COLOR_3DLIGHT }, + { "SystemActiveBorder", COLOR_ACTIVEBORDER }, + { "SystemActiveCaption", COLOR_ACTIVECAPTION }, + { "SystemAppWorkspace", COLOR_APPWORKSPACE }, + { "SystemBackground", COLOR_BACKGROUND }, + { "SystemButtonFace", COLOR_BTNFACE }, + { "SystemButtonHighlight", COLOR_BTNHIGHLIGHT }, + { "SystemButtonShadow", COLOR_BTNSHADOW }, + { "SystemButtonText", COLOR_BTNTEXT }, + { "SystemCaptionText", COLOR_CAPTIONTEXT }, + { "SystemDisabledText", COLOR_GRAYTEXT }, + { "SystemGrayText", COLOR_GRAYTEXT }, + { "SystemHighlight", COLOR_HIGHLIGHT }, + { "SystemHighlightText", COLOR_HIGHLIGHTTEXT }, + { "SystemInactiveBorder", COLOR_INACTIVEBORDER }, + { "SystemInactiveCaption", COLOR_INACTIVECAPTION }, + { "SystemInactiveCaptionText", COLOR_INACTIVECAPTIONTEXT }, + { "SystemInfoBackground", COLOR_INFOBK }, + { "SystemInfoText", COLOR_INFOTEXT }, + { "SystemMenu", COLOR_MENU }, + { "SystemMenuText", COLOR_MENUTEXT }, + { "SystemScrollbar", COLOR_SCROLLBAR }, + { "SystemWindow", COLOR_WINDOW }, + { "SystemWindowFrame", COLOR_WINDOWFRAME }, + { "SystemWindowText", COLOR_WINDOWTEXT }, + { NULL, 0 } +}; + +static void RegisterSystemColors(Tcl_Interp *interp) +{ + Ttk_ResourceCache cache = Ttk_GetResourceCache(interp); + SystemColorEntry *sysColor; + + for (sysColor = sysColors; sysColor->name; ++sysColor) { + DWORD pixel = GetSysColor(sysColor->index); + XColor colorSpec; + colorSpec.red = GetRValue(pixel) * 257; + colorSpec.green = GetGValue(pixel) * 257; + colorSpec.blue = GetBValue(pixel) * 257; + Ttk_RegisterNamedColor(cache, sysColor->name, &colorSpec); + } +} + +static HWND +CreateThemeMonitorWindow(HINSTANCE hinst, Tcl_Interp *interp) +{ + WNDCLASSEX wc; + HWND hwnd = NULL; + TCHAR title[32] = TEXT("TtkMonitorWindow"); + TCHAR name[32] = TEXT("TtkMonitorClass"); + + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC)WndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + wc.hInstance = hinst; + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.lpszMenuName = name; + wc.lpszClassName = name; + + if (RegisterClassEx(&wc)) { + hwnd = CreateWindow( name, title, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hinst, NULL ); + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) interp); + ShowWindow(hwnd, SW_HIDE); + UpdateWindow(hwnd); + } + return hwnd; +} + +static void +DestroyThemeMonitorWindow(void *clientData) +{ + HWND hwnd = (HWND)clientData; + DestroyWindow(hwnd); +} + +static LRESULT WINAPI +WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) +{ + Tcl_Interp *interp = (Tcl_Interp *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + Ttk_Theme theme; + + switch (msg) { + case WM_DESTROY: + break; + + case WM_SYSCOLORCHANGE: + RegisterSystemColors(interp); + break; + + case WM_THEMECHANGED: + /* + * Reset the application theme. + * On windows, it is possible to sign in as a second user, change + * the theme to 'winnative' (by setting the ui to 'best performance'), + * which is a machine-wide change, and then sign back on to the original user. + * Ttk_UseTheme needs to be executed again in order to process the fallback + * from vista/xpnative to winnative. + */ + + theme = Ttk_GetCurrentTheme(interp); + if (theme) { + Ttk_UseTheme(interp, theme); + /* @@@ What to do about errors here? */ + } + break; + } + return DefWindowProc(hwnd, msg, wp, lp); +} + +/* + * Windows-specific platform initialization: + */ + +MODULE_SCOPE int TtkWinTheme_Init(Tcl_Interp *, HWND hwnd); +MODULE_SCOPE int TtkXPTheme_Init(Tcl_Interp *, HWND hwnd); + +MODULE_SCOPE int Ttk_WinPlatformInit(Tcl_Interp *interp) +{ + HWND hwnd; + + hwnd = CreateThemeMonitorWindow(Tk_GetHINSTANCE(), interp); + Ttk_RegisterCleanup(interp, (ClientData)hwnd, DestroyThemeMonitorWindow); + + TtkWinTheme_Init(interp, hwnd); + TtkXPTheme_Init(interp, hwnd); + + return TCL_OK; +} diff --git a/tk8.6/win/ttkWinTheme.c b/tk8.6/win/ttkWinTheme.c new file mode 100644 index 0000000..63e9704 --- /dev/null +++ b/tk8.6/win/ttkWinTheme.c @@ -0,0 +1,733 @@ +/* winTheme.c - Copyright (C) 2004 Pat Thoyts <patthoyts@users.sf.net> + */ + +#ifdef _MSC_VER +#define WIN32_LEAN_AND_MEAN +#endif + +#include <tkWinInt.h> + +#ifndef DFCS_HOT /* Windows 98/Me, Windows 200/XP only */ +#define DFCS_HOT 0 +#endif + +#include "ttk/ttkTheme.h" + +/* + * BoxToRect -- + * Helper routine. Converts a Ttk_Box to a Win32 RECT. + */ +static RECT BoxToRect(Ttk_Box b) +{ + RECT rc; + rc.top = b.y; + rc.left = b.x; + rc.bottom = b.y + b.height; + rc.right = b.x + b.width; + return rc; +} + +/* + * ReliefToEdge -- + * Convert a Tk "relief" value into an Windows "edge" value. + * NB: Caller must check for RELIEF_FLAT and RELIEF_SOLID, + * which must be handled specially. + * + * Passing the BF_FLAT flag to DrawEdge() yields something similar + * to TK_RELIEF_SOLID. TK_RELIEF_FLAT can be implemented by not + * drawing anything. + */ +static unsigned int ReliefToEdge(int relief) +{ + switch (relief) { + case TK_RELIEF_RAISED: return EDGE_RAISED; + case TK_RELIEF_SUNKEN: return EDGE_SUNKEN; + case TK_RELIEF_RIDGE: return EDGE_BUMP; + case TK_RELIEF_GROOVE: return EDGE_ETCHED; + case TK_RELIEF_SOLID: return BDR_RAISEDOUTER; + default: + case TK_RELIEF_FLAT: return BDR_RAISEDOUTER; + } +} + +/*------------------------------------------------------------------------ + * +++ State tables for FrameControlElements. + */ + +static Ttk_StateTable checkbutton_statemap[] = { /* see also SF#1865898 */ + { DFCS_BUTTON3STATE|DFCS_CHECKED|DFCS_INACTIVE, + TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0 }, + { DFCS_BUTTON3STATE|DFCS_CHECKED|DFCS_PUSHED, + TTK_STATE_ALTERNATE|TTK_STATE_PRESSED, 0 }, + { DFCS_BUTTON3STATE|DFCS_CHECKED|DFCS_HOT, + TTK_STATE_ALTERNATE|TTK_STATE_ACTIVE, 0 }, + { DFCS_BUTTON3STATE|DFCS_CHECKED, + TTK_STATE_ALTERNATE, 0 }, + + { DFCS_CHECKED|DFCS_INACTIVE, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0 }, + { DFCS_CHECKED|DFCS_PUSHED, TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0 }, + { DFCS_CHECKED|DFCS_HOT, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0 }, + { DFCS_CHECKED, TTK_STATE_SELECTED, 0 }, + + { DFCS_INACTIVE, TTK_STATE_DISABLED, 0 }, + { DFCS_PUSHED, TTK_STATE_PRESSED, 0 }, + { DFCS_HOT, TTK_STATE_ACTIVE, 0 }, + { 0, 0, 0 }, +}; + +static Ttk_StateTable pushbutton_statemap[] = { + { DFCS_INACTIVE, TTK_STATE_DISABLED, 0 }, + { DFCS_PUSHED, TTK_STATE_PRESSED, 0 }, + { DFCS_HOT, TTK_STATE_ACTIVE, 0 }, + { 0, 0, 0 } +}; + +static Ttk_StateTable arrow_statemap[] = { + { DFCS_INACTIVE, TTK_STATE_DISABLED, 0 }, + { DFCS_PUSHED | DFCS_FLAT, TTK_STATE_PRESSED, 0 }, + { 0, 0, 0 } +}; + +/*------------------------------------------------------------------------ + * +++ FrameControlElement -- + * General-purpose element for things drawn with DrawFrameControl + */ +typedef struct { + const char *name; /* element name */ + int classId; /* class id for DrawFrameControl */ + int partId; /* part id for DrawFrameControl */ + int cxId; /* system metric ids for width/height... */ + int cyId; /* ... or size if FIXEDSIZE bit set */ + Ttk_StateTable *stateMap; /* map Tk states to Win32 flags */ + Ttk_Padding margins; /* additional placement padding */ +} FrameControlElementData; + +#define _FIXEDSIZE 0x80000000L +#define _HALFMETRIC 0x40000000L +#define FIXEDSIZE(id) (id|_FIXEDSIZE) +#define HALFMETRIC(id) (id|_HALFMETRIC) +#define GETMETRIC(m) \ + ((m) & _FIXEDSIZE ? (int)((m) & ~_FIXEDSIZE) : GetSystemMetrics((m)&0x0fffffff)) + +static FrameControlElementData FrameControlElements[] = { + { "Checkbutton.indicator", + DFC_BUTTON, DFCS_BUTTONCHECK, FIXEDSIZE(13), FIXEDSIZE(13), + checkbutton_statemap, {0,0,4,0} }, + { "Radiobutton.indicator", + DFC_BUTTON, DFCS_BUTTONRADIO, FIXEDSIZE(13), FIXEDSIZE(13), + checkbutton_statemap, {0,0,4,0} }, + { "uparrow", + DFC_SCROLL, DFCS_SCROLLUP, SM_CXVSCROLL, SM_CYVSCROLL, + arrow_statemap, {0,0,0,0} }, + { "downarrow", + DFC_SCROLL, DFCS_SCROLLDOWN, SM_CXVSCROLL, SM_CYVSCROLL, + arrow_statemap, {0,0,0,0} }, + { "leftarrow", + DFC_SCROLL, DFCS_SCROLLLEFT, SM_CXHSCROLL, SM_CYHSCROLL, + arrow_statemap, {0,0,0,0} }, + { "rightarrow", + DFC_SCROLL, DFCS_SCROLLRIGHT, SM_CXHSCROLL, SM_CYHSCROLL, + arrow_statemap, {0,0,0,0} }, + { "sizegrip", + DFC_SCROLL, DFCS_SCROLLSIZEGRIP, SM_CXVSCROLL, SM_CYHSCROLL, + arrow_statemap, {0,0,0,0} }, + { "Spinbox.uparrow", + DFC_SCROLL, DFCS_SCROLLUP, SM_CXVSCROLL, HALFMETRIC(SM_CYVSCROLL), + arrow_statemap, {0,0,0,0} }, + { "Spinbox.downarrow", + DFC_SCROLL, DFCS_SCROLLDOWN, SM_CXVSCROLL, HALFMETRIC(SM_CYVSCROLL), + arrow_statemap, {0,0,0,0} }, + + { 0,0,0,0,0,0, {0,0,0,0} } +}; + +/* ---------------------------------------------------------------------- */ + +static void FrameControlElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + FrameControlElementData *p = clientData; + int cx = GETMETRIC(p->cxId); + int cy = GETMETRIC(p->cyId); + if (p->cxId & _HALFMETRIC) cx /= 2; + if (p->cyId & _HALFMETRIC) cy /= 2; + *widthPtr = cx + Ttk_PaddingWidth(p->margins); + *heightPtr = cy + Ttk_PaddingHeight(p->margins); +} + +static void FrameControlElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + FrameControlElementData *elementData = clientData; + RECT rc = BoxToRect(Ttk_PadBox(b, elementData->margins)); + TkWinDCState dcState; + HDC hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + + DrawFrameControl(hdc, &rc, + elementData->classId, + elementData->partId|Ttk_StateTableLookup(elementData->stateMap, state)); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec FrameControlElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + FrameControlElementSize, + FrameControlElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Border element implementation. + */ + +typedef struct { + Tcl_Obj *reliefObj; +} BorderElement; + +static Ttk_ElementOptionSpec BorderElementOptions[] = { + { "-relief",TK_OPTION_RELIEF,Tk_Offset(BorderElement,reliefObj), "flat" }, + {NULL, 0, 0, NULL} +}; + +static void BorderElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + paddingPtr->left = paddingPtr->right = GetSystemMetrics(SM_CXEDGE); + paddingPtr->top = paddingPtr->bottom = GetSystemMetrics(SM_CYEDGE); +} + +static void BorderElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + BorderElement *border = elementRecord; + RECT rc = BoxToRect(b); + int relief = TK_RELIEF_FLAT; + TkWinDCState dcState; + HDC hdc; + + Tk_GetReliefFromObj(NULL, border->reliefObj, &relief); + + if (relief != TK_RELIEF_FLAT) { + UINT xFlags = (relief == TK_RELIEF_SOLID) ? BF_FLAT : 0; + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawEdge(hdc, &rc, ReliefToEdge(relief), BF_RECT | xFlags); + TkWinReleaseDrawableDC(d, hdc, &dcState); + } +} + +static Ttk_ElementSpec BorderElementSpec = { + TK_STYLE_VERSION_2, + sizeof(BorderElement), + BorderElementOptions, + BorderElementSize, + BorderElementDraw +}; + +/* + * Entry field borders: + * Sunken border; also fill with window color. + */ + +typedef struct { + Tcl_Obj *backgroundObj; +} FieldElement; + +static Ttk_ElementOptionSpec FieldElementOptions[] = { + { "-fieldbackground", TK_OPTION_BORDER, + Tk_Offset(FieldElement,backgroundObj), "white" }, + { NULL, 0, 0, NULL } +}; + +static void FieldElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + paddingPtr->left = paddingPtr->right = GetSystemMetrics(SM_CXEDGE); + paddingPtr->top = paddingPtr->bottom = GetSystemMetrics(SM_CYEDGE); +} + +static void FieldElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + FieldElement *field = elementRecord; + Tk_3DBorder bg = Tk_Get3DBorderFromObj(tkwin, field->backgroundObj); + RECT rc = BoxToRect(b); + TkWinDCState dcState; + HDC hdc; + + Tk_Fill3DRectangle( + tkwin, d, bg, b.x, b.y, b.width, b.height, 0, TK_RELIEF_FLAT); + + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec FieldElementSpec = { + TK_STYLE_VERSION_2, + sizeof(FieldElement), + FieldElementOptions, + FieldElementSize, + FieldElementDraw +}; + +/*------------------------------------------------------------------------ + * +++ Button borders. + * Drawn with DrawFrameControl instead of DrawEdge; + * Also draw default indicator and focus ring. + */ +typedef struct { + Tcl_Obj *reliefObj; + Tcl_Obj *highlightColorObj; + Tcl_Obj *defaultStateObj; +} ButtonBorderElement; + +static Ttk_ElementOptionSpec ButtonBorderElementOptions[] = { + { "-relief",TK_OPTION_RELIEF, + Tk_Offset(ButtonBorderElement,reliefObj), "flat" }, + { "-highlightcolor",TK_OPTION_COLOR, + Tk_Offset(ButtonBorderElement,highlightColorObj), "black" }, + { "-default", TK_OPTION_ANY, + Tk_Offset(ButtonBorderElement,defaultStateObj), "disabled" }, + {NULL, 0, 0, NULL} +}; + +static void ButtonBorderElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ButtonBorderElement *bd = elementRecord; + int relief = TK_RELIEF_RAISED; + int defaultState = TTK_BUTTON_DEFAULT_DISABLED; + short int cx, cy; + + Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief); + Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState); + cx = GetSystemMetrics(SM_CXEDGE); + cy = GetSystemMetrics(SM_CYEDGE); + + /* Space for default indicator: + */ + if (defaultState != TTK_BUTTON_DEFAULT_DISABLED) { + ++cx; ++cy; + } + + /* Space for focus ring: + */ + cx += 2; + cy += 2; + + *paddingPtr = Ttk_MakePadding(cx,cy,cx,cy); +} + +static void ButtonBorderElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + ButtonBorderElement *bd = elementRecord; + int relief = TK_RELIEF_FLAT; + int defaultState = TTK_BUTTON_DEFAULT_DISABLED; + TkWinDCState dcState; + HDC hdc; + RECT rc; + + Tk_GetReliefFromObj(NULL, bd->reliefObj, &relief); + Ttk_GetButtonDefaultStateFromObj(NULL, bd->defaultStateObj, &defaultState); + + if (defaultState == TTK_BUTTON_DEFAULT_ACTIVE) { + XColor *highlightColor = + Tk_GetColorFromObj(tkwin, bd->highlightColorObj); + GC gc = Tk_GCForColor(highlightColor, d); + XDrawRectangle(Tk_Display(tkwin), d, gc, b.x,b.y,b.width-1,b.height-1); + } + if (defaultState != TTK_BUTTON_DEFAULT_DISABLED) { + ++b.x; ++b.y; b.width -= 2; b.height -= 2; + } + + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + + rc = BoxToRect(b); + DrawFrameControl(hdc, &rc, + DFC_BUTTON, /* classId */ + DFCS_BUTTONPUSH | Ttk_StateTableLookup(pushbutton_statemap, state)); + + /* Draw focus ring: + */ + if (state & TTK_STATE_FOCUS) { + short int borderWidth = 3; /* @@@ Use GetSystemMetrics?*/ + rc = BoxToRect(Ttk_PadBox(b, Ttk_UniformPadding(borderWidth))); + DrawFocusRect(hdc, &rc); + } + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec ButtonBorderElementSpec = { + TK_STYLE_VERSION_2, + sizeof(ButtonBorderElement), + ButtonBorderElementOptions, + ButtonBorderElementSize, + ButtonBorderElementDraw +}; + +/*------------------------------------------------------------------------ + * +++ Focus element. + * Draw dashed focus rectangle. + */ + +static void FocusElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + *paddingPtr = Ttk_UniformPadding(1); +} + +static void FocusElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + if (state & TTK_STATE_FOCUS) { + RECT rc = BoxToRect(b); + TkWinDCState dcState; + HDC hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawFocusRect(hdc, &rc); + TkWinReleaseDrawableDC(d, hdc, &dcState); + } +} + +static Ttk_ElementSpec FocusElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + FocusElementSize, + FocusElementDraw +}; + +/* FillFocusElement -- + * Draws a focus ring filled with the selection color + */ + +typedef struct { + Tcl_Obj *fillColorObj; +} FillFocusElement; + +static Ttk_ElementOptionSpec FillFocusElementOptions[] = { + { "-focusfill", TK_OPTION_COLOR, + Tk_Offset(FillFocusElement,fillColorObj), "white" }, + {NULL, 0, 0, NULL} +}; + + /* @@@ FIX THIS */ +static void FillFocusElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + FillFocusElement *focus = elementRecord; + if (state & TTK_STATE_FOCUS) { + RECT rc = BoxToRect(b); + TkWinDCState dcState; + XColor *fillColor = Tk_GetColorFromObj(tkwin, focus->fillColorObj); + GC gc = Tk_GCForColor(fillColor, d); + HDC hdc; + + XFillRectangle(Tk_Display(tkwin),d,gc, b.x,b.y,b.width,b.height); + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawFocusRect(hdc, &rc); + TkWinReleaseDrawableDC(d, hdc, &dcState); + } +} + +/* + * ComboboxFocusElement -- + * Read-only comboboxes have a filled focus ring, editable ones do not. + */ +static void ComboboxFocusElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + if (state & TTK_STATE_READONLY) { + FillFocusElementDraw(clientData, elementRecord, tkwin, d, b, state); + } +} + +static Ttk_ElementSpec ComboboxFocusElementSpec = { + TK_STYLE_VERSION_2, + sizeof(FillFocusElement), + FillFocusElementOptions, + FocusElementSize, + ComboboxFocusElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Scrollbar trough element. + * + * The native windows scrollbar is drawn using a pattern brush giving a + * stippled appearance when the trough might otherwise be invisible. + * We can deal with this here. + */ + +typedef struct { /* clientData for Trough element */ + HBRUSH PatternBrush; + HBITMAP PatternBitmap; +} TroughClientData; + +static const WORD Pattern[] = { + 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa +}; + +static void TroughClientDataDeleteProc(void *clientData) +{ + TroughClientData *cd = clientData; + DeleteObject(cd->PatternBrush); + DeleteObject(cd->PatternBitmap); + ckfree(clientData); +} + +static TroughClientData *TroughClientDataInit(Tcl_Interp *interp) +{ + TroughClientData *cd = ckalloc(sizeof(*cd)); + cd->PatternBitmap = CreateBitmap(8, 8, 1, 1, Pattern); + cd->PatternBrush = CreatePatternBrush(cd->PatternBitmap); + Ttk_RegisterCleanup(interp, cd, TroughClientDataDeleteProc); + return cd; +} + +static void TroughElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + TroughClientData *cd = clientData; + TkWinDCState dcState; + HDC hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + HBRUSH hbr; + COLORREF bk, oldbk, oldtxt; + + hbr = SelectObject(hdc, GetSysColorBrush(COLOR_SCROLLBAR)); + bk = GetSysColor(COLOR_3DHIGHLIGHT); + oldtxt = SetTextColor(hdc, GetSysColor(COLOR_3DFACE)); + oldbk = SetBkColor(hdc, bk); + + /* WAS: if (bk (COLOR_3DHIGHLIGHT) == GetSysColor(COLOR_WINDOW)) ... */ + if (GetSysColor(COLOR_SCROLLBAR) == GetSysColor(COLOR_BTNFACE)) { + /* Draw using the pattern brush */ + SelectObject(hdc, cd->PatternBrush); + } + + PatBlt(hdc, b.x, b.y, b.width, b.height, PATCOPY); + SetBkColor(hdc, oldbk); + SetTextColor(hdc, oldtxt); + SelectObject(hdc, hbr); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec TroughElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + TtkNullElementSize, + TroughElementDraw +}; + +/*------------------------------------------------------------------------ + * +++ Thumb element. + */ + +typedef struct { + Tcl_Obj *orientObj; +} ThumbElement; + +static Ttk_ElementOptionSpec ThumbElementOptions[] = { + { "-orient", TK_OPTION_ANY,Tk_Offset(ThumbElement,orientObj),"horizontal"}, + { NULL, 0, 0, NULL } +}; + +static void ThumbElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ThumbElement *thumbPtr = elementRecord; + int orient; + + Ttk_GetOrientFromObj(NULL, thumbPtr->orientObj, &orient); + if (orient == TTK_ORIENT_HORIZONTAL) { + *widthPtr = GetSystemMetrics(SM_CXHTHUMB); + *heightPtr = GetSystemMetrics(SM_CYHSCROLL); + } else { + *widthPtr = GetSystemMetrics(SM_CXVSCROLL); + *heightPtr = GetSystemMetrics(SM_CYVTHUMB); + } +} + +static void ThumbElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + RECT rc = BoxToRect(b); + TkWinDCState dcState; + HDC hdc; + + /* Windows doesn't show a thumb when the scrollbar is disabled */ + if (state & TTK_STATE_DISABLED) + return; + + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawEdge(hdc, &rc, EDGE_RAISED, BF_RECT | BF_MIDDLE); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec ThumbElementSpec = { + TK_STYLE_VERSION_2, + sizeof(ThumbElement), + ThumbElementOptions, + ThumbElementSize, + ThumbElementDraw +}; + +/* ---------------------------------------------------------------------- + * The slider element is the shaped thumb used in the slider widget. + * Windows likes to call this a trackbar. + */ + +typedef struct { + Tcl_Obj *orientObj; /* orientation of the slider widget */ +} SliderElement; + +static Ttk_ElementOptionSpec SliderElementOptions[] = { + { "-orient", TK_OPTION_ANY, Tk_Offset(SliderElement,orientObj), + "horizontal" }, + { NULL, 0, 0, NULL } +}; + +static void SliderElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + SliderElement *slider = elementRecord; + int orient; + + Ttk_GetOrientFromObj(NULL, slider->orientObj, &orient); + if (orient == TTK_ORIENT_HORIZONTAL) { + *widthPtr = (GetSystemMetrics(SM_CXHTHUMB) / 2) | 1; + *heightPtr = GetSystemMetrics(SM_CYHSCROLL); + } else { + *widthPtr = GetSystemMetrics(SM_CXVSCROLL); + *heightPtr = (GetSystemMetrics(SM_CYVTHUMB) / 2) | 1; + } +} + +static void SliderElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + RECT rc = BoxToRect(b); + TkWinDCState dcState; + HDC hdc; + + hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawEdge(hdc, &rc, EDGE_RAISED, BF_RECT | BF_MIDDLE); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec SliderElementSpec = { + TK_STYLE_VERSION_2, + sizeof(SliderElement), + SliderElementOptions, + SliderElementSize, + SliderElementDraw +}; + +/*------------------------------------------------------------------------ + * +++ Notebook elements. + */ + +static void ClientElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + paddingPtr->left = paddingPtr->right = GetSystemMetrics(SM_CXEDGE); + paddingPtr->top = paddingPtr->bottom = GetSystemMetrics(SM_CYEDGE); +} + +static void ClientElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + RECT rc = BoxToRect(b); + TkWinDCState dcState; + HDC hdc = TkWinGetDrawableDC(Tk_Display(tkwin), d, &dcState); + DrawEdge(hdc, &rc, EDGE_RAISED, BF_RECT | BF_SOFT); + TkWinReleaseDrawableDC(d, hdc, &dcState); +} + +static Ttk_ElementSpec ClientElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + ClientElementSize, + ClientElementDraw +}; + +/*------------------------------------------------------------------------ + * +++ Layouts. + */ + +TTK_BEGIN_LAYOUT_TABLE(LayoutTable) + +TTK_LAYOUT("TButton", + TTK_GROUP("Button.border", TTK_FILL_BOTH, + TTK_GROUP("Button.padding", TTK_FILL_BOTH, + TTK_NODE("Button.label", TTK_FILL_BOTH)))) + +TTK_LAYOUT("TCombobox", + TTK_GROUP("Combobox.field", TTK_FILL_BOTH, + TTK_NODE("Combobox.downarrow", TTK_PACK_RIGHT|TTK_FILL_Y) + TTK_GROUP("Combobox.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH, + TTK_GROUP("Combobox.focus", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_BOTH, + TTK_NODE("Combobox.textarea", TTK_FILL_BOTH))))) + +TTK_END_LAYOUT_TABLE + +/* ---------------------------------------------------------------------- */ + +MODULE_SCOPE +int TtkWinTheme_Init(Tcl_Interp *interp, HWND hwnd) +{ + Ttk_Theme themePtr, parentPtr; + FrameControlElementData *fce = FrameControlElements; + + parentPtr = Ttk_GetTheme(interp, "alt"); + themePtr = Ttk_CreateTheme(interp, "winnative", parentPtr); + if (!themePtr) { + return TCL_ERROR; + } + + Ttk_RegisterElementSpec(themePtr, "border", &BorderElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "Button.border", + &ButtonBorderElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "field", &FieldElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "focus", &FocusElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "Combobox.focus", + &ComboboxFocusElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "thumb", &ThumbElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "slider", &SliderElementSpec, NULL); + Ttk_RegisterElementSpec(themePtr, "Scrollbar.trough", &TroughElementSpec, + TroughClientDataInit(interp)); + + Ttk_RegisterElementSpec(themePtr, "client", &ClientElementSpec, NULL); + + for (fce = FrameControlElements; fce->name != 0; ++fce) { + Ttk_RegisterElementSpec(themePtr, fce->name, + &FrameControlElementSpec, fce); + } + + Ttk_RegisterLayouts(themePtr, LayoutTable); + + Tcl_PkgProvide(interp, "ttk::theme::winnative", TTK_VERSION); + return TCL_OK; +} + diff --git a/tk8.6/win/ttkWinXPTheme.c b/tk8.6/win/ttkWinXPTheme.c new file mode 100644 index 0000000..3de1504 --- /dev/null +++ b/tk8.6/win/ttkWinXPTheme.c @@ -0,0 +1,1329 @@ +/* + * Tk theme engine which uses the Windows XP "Visual Styles" API + * Adapted from Georgios Petasis' XP theme patch. + * + * Copyright (c) 2003 by Georgios Petasis, petasis@iit.demokritos.gr. + * Copyright (c) 2003 by Joe English + * Copyright (c) 2003 by Pat Thoyts + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * + * See also: + * + * <URL: http://msdn.microsoft.com/library/en-us/ + * shellcc/platform/commctls/userex/refentry.asp > + */ + +#ifndef HAVE_UXTHEME_H +/* Stub for platforms that lack the XP theme API headers: */ +#include <tkWinInt.h> +int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd) { return TCL_OK; } +#else + +#define WINVER 0x0501 /* Requires Windows XP APIs */ + +#include <windows.h> +#include <uxtheme.h> +#if defined(HAVE_VSSYM32_H) || _MSC_VER > 1500 +# include <vssym32.h> +#else +# include <tmschema.h> +#endif + +#include <tkWinInt.h> + +#include "ttk/ttkTheme.h" + +typedef HTHEME (STDAPICALLTYPE OpenThemeDataProc)(HWND hwnd, + LPCWSTR pszClassList); +typedef HRESULT (STDAPICALLTYPE CloseThemeDataProc)(HTHEME hTheme); +typedef HRESULT (STDAPICALLTYPE DrawThemeBackgroundProc)(HTHEME hTheme, + HDC hdc, int iPartId, int iStateId, const RECT *pRect, + OPTIONAL const RECT *pClipRect); +typedef HRESULT (STDAPICALLTYPE GetThemePartSizeProc)(HTHEME,HDC, + int iPartId, int iStateId, + RECT *prc, enum THEMESIZE eSize, SIZE *psz); +typedef int (STDAPICALLTYPE GetThemeSysSizeProc)(HTHEME,int); +/* GetThemeTextExtent and DrawThemeText only used with BROKEN_TEXT_ELEMENT */ +typedef HRESULT (STDAPICALLTYPE GetThemeTextExtentProc)(HTHEME hTheme, HDC hdc, + int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, + DWORD dwTextFlags, const RECT *pBoundingRect, RECT *pExtent); +typedef HRESULT (STDAPICALLTYPE DrawThemeTextProc)(HTHEME hTheme, HDC hdc, + int iPartId, int iStateId, LPCWSTR pszText, int iCharCount, + DWORD dwTextFlags, DWORD dwTextFlags2, const RECT *pRect); +typedef BOOL (STDAPICALLTYPE IsThemeActiveProc)(void); +typedef BOOL (STDAPICALLTYPE IsAppThemedProc)(void); + +typedef struct +{ + OpenThemeDataProc *OpenThemeData; + CloseThemeDataProc *CloseThemeData; + GetThemePartSizeProc *GetThemePartSize; + GetThemeSysSizeProc *GetThemeSysSize; + DrawThemeBackgroundProc *DrawThemeBackground; + DrawThemeTextProc *DrawThemeText; + GetThemeTextExtentProc *GetThemeTextExtent; + IsThemeActiveProc *IsThemeActive; + IsAppThemedProc *IsAppThemed; + + HWND stubWindow; +} XPThemeProcs; + +typedef struct +{ + HINSTANCE hlibrary; + XPThemeProcs *procs; +} XPThemeData; + +/* + *---------------------------------------------------------------------- + * + * LoadXPThemeProcs -- + * Initialize XP theming support. + * + * XP theme support is included in UXTHEME.DLL + * We dynamically load this DLL at runtime instead of linking + * to it at build-time. + * + * Returns: + * A pointer to an XPThemeProcs table if successful, NULL otherwise. + */ + +static XPThemeProcs * +LoadXPThemeProcs(HINSTANCE *phlib) +{ + /* + * Load the library "uxtheme.dll", where the native widget + * drawing routines are implemented. This will only succeed + * if we are running at least on Windows XP. + */ + HINSTANCE handle; + *phlib = handle = LoadLibrary(TEXT("uxtheme.dll")); + if (handle != 0) + { + /* + * We have successfully loaded the library. Proceed in storing the + * addresses of the functions we want to use. + */ + XPThemeProcs *procs = ckalloc(sizeof(XPThemeProcs)); +#define LOADPROC(name) \ + (0 != (procs->name = (name ## Proc *)GetProcAddress(handle, #name) )) + + if ( LOADPROC(OpenThemeData) + && LOADPROC(CloseThemeData) + && LOADPROC(GetThemePartSize) + && LOADPROC(GetThemeSysSize) + && LOADPROC(DrawThemeBackground) + && LOADPROC(GetThemeTextExtent) + && LOADPROC(DrawThemeText) + && LOADPROC(IsThemeActive) + && LOADPROC(IsAppThemed) + ) + { + return procs; + } +#undef LOADPROC + ckfree(procs); + } + return 0; +} + +/* + * XPThemeDeleteProc -- + * + * Release any theme allocated resources. + */ + +static void +XPThemeDeleteProc(void *clientData) +{ + XPThemeData *themeData = clientData; + FreeLibrary(themeData->hlibrary); + ckfree(clientData); +} + +static int +XPThemeEnabled(Ttk_Theme theme, void *clientData) +{ + XPThemeData *themeData = clientData; + int active = themeData->procs->IsThemeActive(); + int themed = themeData->procs->IsAppThemed(); + return (active && themed); +} + +/* + * BoxToRect -- + * Helper routine. Returns a RECT data structure. + */ +static RECT +BoxToRect(Ttk_Box b) +{ + RECT rc; + rc.top = b.y; + rc.left = b.x; + rc.bottom = b.y + b.height; + rc.right = b.x + b.width; + return rc; +} + +/* + * Map Tk state bitmaps to XP style enumerated values. + */ +static Ttk_StateTable null_statemap[] = { {0,0,0} }; + +/* + * Pushbuttons (Tk: "Button") + */ +static Ttk_StateTable pushbutton_statemap[] = +{ + { PBS_DISABLED, TTK_STATE_DISABLED, 0 }, + { PBS_PRESSED, TTK_STATE_PRESSED, 0 }, + { PBS_HOT, TTK_STATE_ACTIVE, 0 }, + { PBS_DEFAULTED, TTK_STATE_ALTERNATE, 0 }, + { PBS_NORMAL, 0, 0 } +}; + +/* + * Checkboxes (Tk: "Checkbutton") + */ +static Ttk_StateTable checkbox_statemap[] = +{ +{CBS_MIXEDDISABLED, TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0}, +{CBS_MIXEDPRESSED, TTK_STATE_ALTERNATE|TTK_STATE_PRESSED, 0}, +{CBS_MIXEDHOT, TTK_STATE_ALTERNATE|TTK_STATE_ACTIVE, 0}, +{CBS_MIXEDNORMAL, TTK_STATE_ALTERNATE, 0}, +{CBS_CHECKEDDISABLED, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0}, +{CBS_CHECKEDPRESSED, TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0}, +{CBS_CHECKEDHOT, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0}, +{CBS_CHECKEDNORMAL, TTK_STATE_SELECTED, 0}, +{CBS_UNCHECKEDDISABLED, TTK_STATE_DISABLED, 0}, +{CBS_UNCHECKEDPRESSED, TTK_STATE_PRESSED, 0}, +{CBS_UNCHECKEDHOT, TTK_STATE_ACTIVE, 0}, +{CBS_UNCHECKEDNORMAL, 0,0 } +}; + +/* + * Radiobuttons: + */ +static Ttk_StateTable radiobutton_statemap[] = +{ +{RBS_UNCHECKEDDISABLED, TTK_STATE_ALTERNATE|TTK_STATE_DISABLED, 0}, +{RBS_UNCHECKEDNORMAL, TTK_STATE_ALTERNATE, 0}, +{RBS_CHECKEDDISABLED, TTK_STATE_SELECTED|TTK_STATE_DISABLED, 0}, +{RBS_CHECKEDPRESSED, TTK_STATE_SELECTED|TTK_STATE_PRESSED, 0}, +{RBS_CHECKEDHOT, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0}, +{RBS_CHECKEDNORMAL, TTK_STATE_SELECTED, 0}, +{RBS_UNCHECKEDDISABLED, TTK_STATE_DISABLED, 0}, +{RBS_UNCHECKEDPRESSED, TTK_STATE_PRESSED, 0}, +{RBS_UNCHECKEDHOT, TTK_STATE_ACTIVE, 0}, +{RBS_UNCHECKEDNORMAL, 0,0 } +}; + +/* + * Groupboxes (tk: "frame") + */ +static Ttk_StateTable groupbox_statemap[] = +{ +{GBS_DISABLED, TTK_STATE_DISABLED, 0}, +{GBS_NORMAL, 0,0 } +}; + +/* + * Edit fields (tk: "entry") + */ +static Ttk_StateTable edittext_statemap[] = +{ + { ETS_DISABLED, TTK_STATE_DISABLED, 0 }, + { ETS_READONLY, TTK_STATE_READONLY, 0 }, + { ETS_FOCUSED, TTK_STATE_FOCUS, 0 }, + { ETS_HOT, TTK_STATE_ACTIVE, 0 }, + { ETS_NORMAL, 0, 0 } +/* NOT USED: ETS_ASSIST, ETS_SELECTED */ +}; + +/* + * Combobox text field statemap: + * Same as edittext_statemap, but doesn't use ETS_READONLY + * (fixes: #1032409) + */ +static Ttk_StateTable combotext_statemap[] = +{ + { ETS_DISABLED, TTK_STATE_DISABLED, 0 }, + { ETS_FOCUSED, TTK_STATE_FOCUS, 0 }, + { ETS_HOT, TTK_STATE_ACTIVE, 0 }, + { ETS_NORMAL, 0, 0 } +}; + +/* + * Combobox button: (CBP_DROPDOWNBUTTON) + */ +static Ttk_StateTable combobox_statemap[] = { + { CBXS_DISABLED, TTK_STATE_DISABLED, 0 }, + { CBXS_PRESSED, TTK_STATE_PRESSED, 0 }, + { CBXS_HOT, TTK_STATE_ACTIVE, 0 }, + { CBXS_HOT, TTK_STATE_HOVER, 0 }, + { CBXS_NORMAL, 0, 0 } +}; + +/* + * Toolbar buttons (TP_BUTTON): + */ +static Ttk_StateTable toolbutton_statemap[] = { + { TS_DISABLED, TTK_STATE_DISABLED, 0 }, + { TS_PRESSED, TTK_STATE_PRESSED, 0 }, + { TS_HOTCHECKED, TTK_STATE_SELECTED|TTK_STATE_ACTIVE, 0 }, + { TS_CHECKED, TTK_STATE_SELECTED, 0 }, + { TS_HOT, TTK_STATE_ACTIVE, 0 }, + { TS_NORMAL, 0,0 } +}; + +/* + * Scrollbars (Tk: "Scrollbar.thumb") + */ +static Ttk_StateTable scrollbar_statemap[] = +{ + { SCRBS_DISABLED, TTK_STATE_DISABLED, 0 }, + { SCRBS_PRESSED, TTK_STATE_PRESSED, 0 }, + { SCRBS_HOT, TTK_STATE_ACTIVE, 0 }, + { SCRBS_NORMAL, 0, 0 } +}; + +static Ttk_StateTable uparrow_statemap[] = +{ + { ABS_UPDISABLED, TTK_STATE_DISABLED, 0 }, + { ABS_UPPRESSED, TTK_STATE_PRESSED, 0 }, + { ABS_UPHOT, TTK_STATE_ACTIVE, 0 }, + { ABS_UPNORMAL, 0, 0 } +}; + +static Ttk_StateTable downarrow_statemap[] = +{ + { ABS_DOWNDISABLED, TTK_STATE_DISABLED, 0 }, + { ABS_DOWNPRESSED, TTK_STATE_PRESSED, 0 }, + { ABS_DOWNHOT, TTK_STATE_ACTIVE, 0 }, + { ABS_DOWNNORMAL, 0, 0 } +}; + +static Ttk_StateTable leftarrow_statemap[] = +{ + { ABS_LEFTDISABLED, TTK_STATE_DISABLED, 0 }, + { ABS_LEFTPRESSED, TTK_STATE_PRESSED, 0 }, + { ABS_LEFTHOT, TTK_STATE_ACTIVE, 0 }, + { ABS_LEFTNORMAL, 0, 0 } +}; + +static Ttk_StateTable rightarrow_statemap[] = +{ + { ABS_RIGHTDISABLED,TTK_STATE_DISABLED, 0 }, + { ABS_RIGHTPRESSED, TTK_STATE_PRESSED, 0 }, + { ABS_RIGHTHOT, TTK_STATE_ACTIVE, 0 }, + { ABS_RIGHTNORMAL, 0, 0 } +}; + +static Ttk_StateTable spinbutton_statemap[] = +{ + { DNS_DISABLED, TTK_STATE_DISABLED, 0 }, + { DNS_PRESSED, TTK_STATE_PRESSED, 0 }, + { DNS_HOT, TTK_STATE_ACTIVE, 0 }, + { DNS_NORMAL, 0, 0 }, +}; + +/* + * Trackbar thumb: (Tk: "scale slider") + */ +static Ttk_StateTable scale_statemap[] = +{ + { TUS_DISABLED, TTK_STATE_DISABLED, 0 }, + { TUS_PRESSED, TTK_STATE_PRESSED, 0 }, + { TUS_FOCUSED, TTK_STATE_FOCUS, 0 }, + { TUS_HOT, TTK_STATE_ACTIVE, 0 }, + { TUS_NORMAL, 0, 0 } +}; + +static Ttk_StateTable tabitem_statemap[] = +{ + { TIS_DISABLED, TTK_STATE_DISABLED, 0 }, + { TIS_SELECTED, TTK_STATE_SELECTED, 0 }, + { TIS_HOT, TTK_STATE_ACTIVE, 0 }, + { TIS_FOCUSED, TTK_STATE_FOCUS, 0 }, + { TIS_NORMAL, 0, 0 }, +}; + + +/* + *---------------------------------------------------------------------- + * +++ Element data: + * + * The following structure is passed as the 'clientData' pointer + * to most elements in this theme. It contains data relevant + * to a single XP Theme "part". + * + * <<NOTE-GetThemeMargins>>: + * In theory, we should be call GetThemeMargins(...TMT_CONTENTRECT...) + * to calculate the internal padding. In practice, this routine + * only seems to work properly for BP_PUSHBUTTON. So we hardcode + * the required padding at element registration time instead. + * + * The PAD_MARGINS flag bit determines whether the padding + * should be added on the inside (0) or outside (1) of the element. + * + * <<NOTE-GetThemePartSize>>: + * This gives bogus metrics for some parts (in particular, + * BP_PUSHBUTTONS). Set the IGNORE_THEMESIZE flag to skip this call. + */ + +typedef struct /* XP element specifications */ +{ + const char *elementName; /* Tk theme engine element name */ + Ttk_ElementSpec *elementSpec; + /* Element spec (usually GenericElementSpec) */ + LPCWSTR className; /* Windows window class name */ + int partId; /* BP_PUSHBUTTON, BP_CHECKBUTTON, etc. */ + Ttk_StateTable *statemap; /* Map Tk states to XP states */ + Ttk_Padding padding; /* See NOTE-GetThemeMargins */ + int flags; +# define IGNORE_THEMESIZE 0x80000000 /* See NOTE-GetThemePartSize */ +# define PAD_MARGINS 0x40000000 /* See NOTE-GetThemeMargins */ +# define HEAP_ELEMENT 0x20000000 /* ElementInfo is on heap */ +# define HALF_HEIGHT 0x10000000 /* Used by GenericSizedElements */ +# define HALF_WIDTH 0x08000000 /* Used by GenericSizedElements */ +} ElementInfo; + +typedef struct +{ + /* + * Static data, initialized when element is registered: + */ + ElementInfo *info; + XPThemeProcs *procs; /* Pointer to theme procedure table */ + + /* + * Dynamic data, allocated by InitElementData: + */ + HTHEME hTheme; + HDC hDC; + HWND hwnd; + + /* For TkWinDrawableReleaseDC: */ + Drawable drawable; + TkWinDCState dcState; +} ElementData; + +static ElementData * +NewElementData(XPThemeProcs *procs, ElementInfo *info) +{ + ElementData *elementData = ckalloc(sizeof(ElementData)); + + elementData->procs = procs; + elementData->info = info; + elementData->hTheme = elementData->hDC = 0; + + return elementData; +} + +/* + * Destroy elements. If the element was created by the element factory + * then the info member is dynamically allocated. Otherwise it was + * static data from the C object and only the ElementData needs freeing. + */ +static void DestroyElementData(void *clientData) +{ + ElementData *elementData = clientData; + if (elementData->info->flags & HEAP_ELEMENT) { + ckfree(elementData->info->statemap); + ckfree(elementData->info->className); + ckfree(elementData->info->elementName); + ckfree(elementData->info); + } + ckfree(clientData); +} + +/* + * InitElementData -- + * Looks up theme handle. If Drawable argument is non-NULL, + * also initializes DC. + * + * Returns: + * 1 on success, 0 on error. + * Caller must later call FreeElementData() so this element + * can be reused. + */ + +static int +InitElementData(ElementData *elementData, Tk_Window tkwin, Drawable d) +{ + Window win = Tk_WindowId(tkwin); + + if (win != None) { + elementData->hwnd = Tk_GetHWND(win); + } else { + elementData->hwnd = elementData->procs->stubWindow; + } + + elementData->hTheme = elementData->procs->OpenThemeData( + elementData->hwnd, elementData->info->className); + + if (!elementData->hTheme) + return 0; + + elementData->drawable = d; + if (d != 0) { + elementData->hDC = TkWinGetDrawableDC(Tk_Display(tkwin), d, + &elementData->dcState); + } + + return 1; +} + +static void +FreeElementData(ElementData *elementData) +{ + elementData->procs->CloseThemeData(elementData->hTheme); + if (elementData->drawable != 0) { + TkWinReleaseDrawableDC( + elementData->drawable, elementData->hDC, &elementData->dcState); + } +} + +/*---------------------------------------------------------------------- + * +++ Generic element implementation. + * + * Used for elements which are handled entirely by the XP Theme API, + * such as radiobutton and checkbutton indicators, scrollbar arrows, etc. + */ + +static void GenericElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ElementData *elementData = clientData; + HRESULT result; + SIZE size; + + if (!InitElementData(elementData, tkwin, 0)) + return; + + if (!(elementData->info->flags & IGNORE_THEMESIZE)) { + result = elementData->procs->GetThemePartSize( + elementData->hTheme, + elementData->hDC, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, 0), + NULL /*RECT *prc*/, + TS_TRUE, + &size); + + if (SUCCEEDED(result)) { + *widthPtr = size.cx; + *heightPtr = size.cy; + } + } + + /* See NOTE-GetThemeMargins + */ + *paddingPtr = elementData->info->padding; + if (elementData->info->flags & PAD_MARGINS) { + *widthPtr += Ttk_PaddingWidth(elementData->info->padding); + *heightPtr += Ttk_PaddingHeight(elementData->info->padding); + } +} + +static void GenericElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + ElementData *elementData = clientData; + RECT rc; + + if (!InitElementData(elementData, tkwin, d)) { + return; + } + + if (elementData->info->flags & PAD_MARGINS) { + b = Ttk_PadBox(b, elementData->info->padding); + } + rc = BoxToRect(b); + + elementData->procs->DrawThemeBackground( + elementData->hTheme, + elementData->hDC, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, state), + &rc, + NULL/*pContentRect*/); + + FreeElementData(elementData); +} + +static Ttk_ElementSpec GenericElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + GenericElementSize, + GenericElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Sized element implementation. + * + * Used for elements which are handled entirely by the XP Theme API, + * but that require a fixed size adjustment. + * Note that GetThemeSysSize calls through to GetSystemMetrics + */ + +static void +GenericSizedElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ElementData *elementData = clientData; + + if (!InitElementData(elementData, tkwin, 0)) + return; + + GenericElementSize(clientData, elementRecord, tkwin, + widthPtr, heightPtr, paddingPtr); + + *widthPtr = elementData->procs->GetThemeSysSize(NULL, + (elementData->info->flags >> 8) & 0xff); + *heightPtr = elementData->procs->GetThemeSysSize(NULL, + elementData->info->flags & 0xff); + if (elementData->info->flags & HALF_HEIGHT) + *heightPtr /= 2; + if (elementData->info->flags & HALF_WIDTH) + *widthPtr /= 2; +} + +static Ttk_ElementSpec GenericSizedElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + GenericSizedElementSize, + GenericElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Spinbox arrow element. + * These are half-height scrollbar buttons. + */ + +static void +SpinboxArrowElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ElementData *elementData = clientData; + + if (!InitElementData(elementData, tkwin, 0)) + return; + + GenericSizedElementSize(clientData, elementRecord, tkwin, + widthPtr, heightPtr, paddingPtr); + + /* force the arrow button height to half size */ + *heightPtr /= 2; +} + +static Ttk_ElementSpec SpinboxArrowElementSpec = { + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + SpinboxArrowElementSize, + GenericElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Scrollbar thumb element. + * Same as a GenericElement, but don't draw in the disabled state. + */ + +static void ThumbElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + ElementData *elementData = clientData; + unsigned stateId = Ttk_StateTableLookup(elementData->info->statemap, state); + RECT rc = BoxToRect(b); + + /* + * Don't draw the thumb if we are disabled. + */ + if (state & TTK_STATE_DISABLED) + return; + + if (!InitElementData(elementData, tkwin, d)) + return; + + elementData->procs->DrawThemeBackground(elementData->hTheme, + elementData->hDC, elementData->info->partId, stateId, + &rc, NULL); + + FreeElementData(elementData); +} + +static Ttk_ElementSpec ThumbElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + GenericElementSize, + ThumbElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Progress bar element. + * Increases the requested length of PP_CHUNK and PP_CHUNKVERT parts + * so that indeterminate progress bars show 3 bars instead of 1. + */ + +static void PbarElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + ElementData *elementData = clientData; + int nBars = 3; + + GenericElementSize(clientData, elementRecord, tkwin, + widthPtr, heightPtr, paddingPtr); + + if (elementData->info->partId == PP_CHUNK) { + *widthPtr *= nBars; + } else if (elementData->info->partId == PP_CHUNKVERT) { + *heightPtr *= nBars; + } +} + +static Ttk_ElementSpec PbarElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + PbarElementSize, + GenericElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Notebook tab element. + * Same as generic element, with additional logic to select + * proper iPartID for the leftmost tab. + * + * Notes: TABP_TABITEMRIGHTEDGE (or TABP_TOPTABITEMRIGHTEDGE, + * which appears to be identical) should be used if the + * tab is exactly at the right edge of the notebook, but + * not if it's simply the rightmost tab. This information + * is not available. + * + * The TIS_* and TILES_* definitions are identical, so + * we can use the same statemap no matter what the partId. + */ +static void TabElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + ElementData *elementData = clientData; + int partId = elementData->info->partId; + RECT rc = BoxToRect(b); + + if (!InitElementData(elementData, tkwin, d)) + return; + if (state & TTK_STATE_USER1) + partId = TABP_TABITEMLEFTEDGE; + elementData->procs->DrawThemeBackground( + elementData->hTheme, elementData->hDC, partId, + Ttk_StateTableLookup(elementData->info->statemap, state), &rc, NULL); + FreeElementData(elementData); +} + +static Ttk_ElementSpec TabElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + GenericElementSize, + TabElementDraw +}; + +/*---------------------------------------------------------------------- + * +++ Tree indicator element. + * + * Generic element, but don't display at all if TTK_STATE_LEAF (=USER2) set + */ + +#define TTK_STATE_OPEN TTK_STATE_USER1 +#define TTK_STATE_LEAF TTK_STATE_USER2 + +static Ttk_StateTable header_statemap[] = +{ + { HIS_PRESSED, TTK_STATE_PRESSED, 0 }, + { HIS_HOT, TTK_STATE_ACTIVE, 0 }, + { HIS_NORMAL, 0,0 }, +}; + +static Ttk_StateTable treeview_statemap[] = +{ + { TREIS_DISABLED, TTK_STATE_DISABLED, 0 }, + { TREIS_SELECTED, TTK_STATE_SELECTED, 0}, + { TREIS_HOT, TTK_STATE_ACTIVE, 0 }, + { TREIS_NORMAL, 0,0 }, +}; + +static Ttk_StateTable tvpglyph_statemap[] = +{ + { GLPS_OPENED, TTK_STATE_OPEN, 0 }, + { GLPS_CLOSED, 0,0 }, +}; + +static void TreeIndicatorElementDraw( + void *clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + if (!(state & TTK_STATE_LEAF)) { + GenericElementDraw(clientData,elementRecord,tkwin,d,b,state); + } +} + +static Ttk_ElementSpec TreeIndicatorElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(NullElement), + TtkNullElementOptions, + GenericElementSize, + TreeIndicatorElementDraw +}; + +#if BROKEN_TEXT_ELEMENT + +/* + *---------------------------------------------------------------------- + * Text element (does not work yet). + * + * According to "Using Windows XP Visual Styles", we need to select + * a font into the DC before calling DrawThemeText(). + * There's just no easy way to get an HFONT out of a Tk_Font. + * Maybe GetThemeFont() would work? + * + */ + +typedef struct +{ + Tcl_Obj *textObj; + Tcl_Obj *fontObj; +} TextElement; + +static Ttk_ElementOptionSpec TextElementOptions[] = +{ + { "-text", TK_OPTION_STRING, + Tk_Offset(TextElement,textObj), "" }, + { "-font", TK_OPTION_FONT, + Tk_Offset(TextElement,fontObj), DEFAULT_FONT }, + { NULL } +}; + +static void TextElementSize( + void *clientData, void *elementRecord, Tk_Window tkwin, + int *widthPtr, int *heightPtr, Ttk_Padding *paddingPtr) +{ + TextElement *element = elementRecord; + ElementData *elementData = clientData; + RECT rc = {0, 0}; + HRESULT hr = S_OK; + + if (!InitElementData(elementData, tkwin, 0)) + return; + + hr = elementData->procs->GetThemeTextExtent( + elementData->hTheme, + elementData->hDC, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, 0), + Tcl_GetUnicode(element->textObj), + -1, + DT_LEFT,// | DT_BOTTOM | DT_NOPREFIX, + NULL, + &rc); + + if (SUCCEEDED(hr)) { + *widthPtr = rc.right - rc.left; + *heightPtr = rc.bottom - rc.top; + } + if (*widthPtr < 80) *widthPtr = 80; + if (*heightPtr < 20) *heightPtr = 20; + + FreeElementData(elementData); +} + +static void TextElementDraw( + ClientData clientData, void *elementRecord, Tk_Window tkwin, + Drawable d, Ttk_Box b, unsigned int state) +{ + TextElement *element = elementRecord; + ElementData *elementData = clientData; + RECT rc = BoxToRect(b); + HRESULT hr = S_OK; + + if (!InitElementData(elementData, tkwin, d)) + return; + + hr = elementData->procs->DrawThemeText( + elementData->hTheme, + elementData->hDC, + elementData->info->partId, + Ttk_StateTableLookup(elementData->info->statemap, state), + Tcl_GetUnicode(element->textObj), + -1, + DT_LEFT,// | DT_BOTTOM | DT_NOPREFIX, + (state & TTK_STATE_DISABLED) ? DTT_GRAYED : 0, + &rc); + FreeElementData(elementData); +} + +static Ttk_ElementSpec TextElementSpec = +{ + TK_STYLE_VERSION_2, + sizeof(TextElement), + TextElementOptions, + TextElementSize, + TextElementDraw +}; + +#endif /* BROKEN_TEXT_ELEMENT */ + +/*---------------------------------------------------------------------- + * +++ Widget layouts: + */ + +TTK_BEGIN_LAYOUT_TABLE(LayoutTable) + +TTK_LAYOUT("TButton", + TTK_GROUP("Button.button", TTK_FILL_BOTH, + TTK_GROUP("Button.focus", TTK_FILL_BOTH, + TTK_GROUP("Button.padding", TTK_FILL_BOTH, + TTK_NODE("Button.label", TTK_FILL_BOTH))))) + +TTK_LAYOUT("TMenubutton", + TTK_NODE("Menubutton.dropdown", TTK_PACK_RIGHT|TTK_FILL_Y) + TTK_GROUP("Menubutton.button", TTK_PACK_RIGHT|TTK_EXPAND|TTK_FILL_BOTH, + TTK_GROUP("Menubutton.padding", TTK_PACK_LEFT|TTK_EXPAND|TTK_FILL_X, + TTK_NODE("Menubutton.label", 0)))) + +TTK_LAYOUT("Horizontal.TScrollbar", + TTK_GROUP("Horizontal.Scrollbar.trough", TTK_FILL_X, + TTK_NODE("Horizontal.Scrollbar.leftarrow", TTK_PACK_LEFT) + TTK_NODE("Horizontal.Scrollbar.rightarrow", TTK_PACK_RIGHT) + TTK_GROUP("Horizontal.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT, + TTK_NODE("Horizontal.Scrollbar.grip", 0)))) + +TTK_LAYOUT("Vertical.TScrollbar", + TTK_GROUP("Vertical.Scrollbar.trough", TTK_FILL_Y, + TTK_NODE("Vertical.Scrollbar.uparrow", TTK_PACK_TOP) + TTK_NODE("Vertical.Scrollbar.downarrow", TTK_PACK_BOTTOM) + TTK_GROUP("Vertical.Scrollbar.thumb", TTK_FILL_BOTH|TTK_UNIT, + TTK_NODE("Vertical.Scrollbar.grip", 0)))) + +TTK_LAYOUT("Horizontal.TScale", + TTK_GROUP("Scale.focus", TTK_EXPAND|TTK_FILL_BOTH, + TTK_GROUP("Horizontal.Scale.trough", TTK_EXPAND|TTK_FILL_BOTH, + TTK_NODE("Horizontal.Scale.track", TTK_FILL_X) + TTK_NODE("Horizontal.Scale.slider", TTK_PACK_LEFT) ))) + +TTK_LAYOUT("Vertical.TScale", + TTK_GROUP("Scale.focus", TTK_EXPAND|TTK_FILL_BOTH, + TTK_GROUP("Vertical.Scale.trough", TTK_EXPAND|TTK_FILL_BOTH, + TTK_NODE("Vertical.Scale.track", TTK_FILL_Y) + TTK_NODE("Vertical.Scale.slider", TTK_PACK_TOP) ))) + +TTK_END_LAYOUT_TABLE + +/*---------------------------------------------------------------------- + * +++ XP element info table: + */ + +#define PAD(l,t,r,b) {l,t,r,b} +#define NOPAD {0,0,0,0} + +/* name spec className partId statemap padding flags */ + +static ElementInfo ElementInfoTable[] = { + { "Checkbutton.indicator", &GenericElementSpec, L"BUTTON", + BP_CHECKBOX, checkbox_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, + { "Radiobutton.indicator", &GenericElementSpec, L"BUTTON", + BP_RADIOBUTTON, radiobutton_statemap, PAD(0, 0, 4, 0), PAD_MARGINS }, + { "Button.button", &GenericElementSpec, L"BUTTON", + BP_PUSHBUTTON, pushbutton_statemap, PAD(3, 3, 3, 3), IGNORE_THEMESIZE }, + { "Labelframe.border", &GenericElementSpec, L"BUTTON", + BP_GROUPBOX, groupbox_statemap, PAD(2, 2, 2, 2), 0 }, + { "Entry.field", &GenericElementSpec, L"EDIT", EP_EDITTEXT, + edittext_statemap, PAD(1, 1, 1, 1), 0 }, + { "Combobox.field", &GenericElementSpec, L"EDIT", + EP_EDITTEXT, combotext_statemap, PAD(1, 1, 1, 1), 0 }, + { "Combobox.downarrow", &GenericSizedElementSpec, L"COMBOBOX", + CP_DROPDOWNBUTTON, combobox_statemap, NOPAD, + (SM_CXVSCROLL << 8) | SM_CYVSCROLL }, + { "Vertical.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR", + SBP_UPPERTRACKVERT, scrollbar_statemap, NOPAD, 0 }, + { "Vertical.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR", + SBP_THUMBBTNVERT, scrollbar_statemap, NOPAD, 0 }, + { "Vertical.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR", + SBP_GRIPPERVERT, scrollbar_statemap, NOPAD, 0 }, + { "Horizontal.Scrollbar.trough", &GenericElementSpec, L"SCROLLBAR", + SBP_UPPERTRACKHORZ, scrollbar_statemap, NOPAD, 0 }, + { "Horizontal.Scrollbar.thumb", &ThumbElementSpec, L"SCROLLBAR", + SBP_THUMBBTNHORZ, scrollbar_statemap, NOPAD, 0 }, + { "Horizontal.Scrollbar.grip", &GenericElementSpec, L"SCROLLBAR", + SBP_GRIPPERHORZ, scrollbar_statemap, NOPAD, 0 }, + { "Scrollbar.uparrow", &GenericSizedElementSpec, L"SCROLLBAR", + SBP_ARROWBTN, uparrow_statemap, NOPAD, + (SM_CXVSCROLL << 8) | SM_CYVSCROLL }, + { "Scrollbar.downarrow", &GenericSizedElementSpec, L"SCROLLBAR", + SBP_ARROWBTN, downarrow_statemap, NOPAD, + (SM_CXVSCROLL << 8) | SM_CYVSCROLL }, + { "Scrollbar.leftarrow", &GenericSizedElementSpec, L"SCROLLBAR", + SBP_ARROWBTN, leftarrow_statemap, NOPAD, + (SM_CXHSCROLL << 8) | SM_CYHSCROLL }, + { "Scrollbar.rightarrow", &GenericSizedElementSpec, L"SCROLLBAR", + SBP_ARROWBTN, rightarrow_statemap, NOPAD, + (SM_CXHSCROLL << 8) | SM_CYHSCROLL }, + { "Horizontal.Scale.slider", &GenericElementSpec, L"TRACKBAR", + TKP_THUMB, scale_statemap, NOPAD, 0 }, + { "Vertical.Scale.slider", &GenericElementSpec, L"TRACKBAR", + TKP_THUMBVERT, scale_statemap, NOPAD, 0 }, + { "Horizontal.Scale.track", &GenericElementSpec, L"TRACKBAR", + TKP_TRACK, scale_statemap, NOPAD, 0 }, + { "Vertical.Scale.track", &GenericElementSpec, L"TRACKBAR", + TKP_TRACKVERT, scale_statemap, NOPAD, 0 }, + /* ttk::progressbar elements */ + { "Horizontal.Progressbar.pbar", &PbarElementSpec, L"PROGRESS", + PP_CHUNK, null_statemap, NOPAD, 0 }, + { "Vertical.Progressbar.pbar", &PbarElementSpec, L"PROGRESS", + PP_CHUNKVERT, null_statemap, NOPAD, 0 }, + { "Horizontal.Progressbar.trough", &GenericElementSpec, L"PROGRESS", + PP_BAR, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE }, + { "Vertical.Progressbar.trough", &GenericElementSpec, L"PROGRESS", + PP_BARVERT, null_statemap, PAD(3,3,3,3), IGNORE_THEMESIZE }, + /* ttk::notebook */ + { "tab", &TabElementSpec, L"TAB", + TABP_TABITEM, tabitem_statemap, PAD(3,3,3,0), 0 }, + { "client", &GenericElementSpec, L"TAB", + TABP_PANE, null_statemap, PAD(1,1,3,3), 0 }, + { "NotebookPane.background", &GenericElementSpec, L"TAB", + TABP_BODY, null_statemap, NOPAD, 0 }, + { "Toolbutton.border", &GenericElementSpec, L"TOOLBAR", + TP_BUTTON, toolbutton_statemap, NOPAD,0 }, + { "Menubutton.button", &GenericElementSpec, L"TOOLBAR", + TP_SPLITBUTTON,toolbutton_statemap, NOPAD,0 }, + { "Menubutton.dropdown", &GenericElementSpec, L"TOOLBAR", + TP_SPLITBUTTONDROPDOWN,toolbutton_statemap, NOPAD,0 }, + { "Treeview.field", &GenericElementSpec, L"TREEVIEW", + TVP_TREEITEM, treeview_statemap, PAD(1, 1, 1, 1), 0 }, + { "Treeitem.indicator", &TreeIndicatorElementSpec, L"TREEVIEW", + TVP_GLYPH, tvpglyph_statemap, PAD(1,1,6,0), PAD_MARGINS }, + { "Treeheading.border", &GenericElementSpec, L"HEADER", + HP_HEADERITEM, header_statemap, PAD(4,0,4,0),0 }, + { "sizegrip", &GenericElementSpec, L"STATUS", + SP_GRIPPER, null_statemap, NOPAD,0 }, + { "Spinbox.field", &GenericElementSpec, L"EDIT", + EP_EDITTEXT, edittext_statemap, PAD(1, 1, 1, 1), 0 }, + { "Spinbox.uparrow", &SpinboxArrowElementSpec, L"SPIN", + SPNP_UP, spinbutton_statemap, NOPAD, + PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) }, + { "Spinbox.downarrow", &SpinboxArrowElementSpec, L"SPIN", + SPNP_DOWN, spinbutton_statemap, NOPAD, + PAD_MARGINS | ((SM_CXVSCROLL << 8) | SM_CYVSCROLL) }, + +#if BROKEN_TEXT_ELEMENT + { "Labelframe.text", &TextElementSpec, L"BUTTON", + BP_GROUPBOX, groupbox_statemap, NOPAD,0 }, +#endif + + { 0,0,0,0,0,NOPAD,0 } +}; +#undef PAD + + +static int +GetSysFlagFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int *resultPtr) +{ + static const char *names[] = { + "SM_CXBORDER", "SM_CYBORDER", "SM_CXVSCROLL", "SM_CYVSCROLL", + "SM_CXHSCROLL", "SM_CYHSCROLL", "SM_CXMENUCHECK", "SM_CYMENUCHECK", + "SM_CXMENUSIZE", "SM_CYMENUSIZE", "SM_CXSIZE", "SM_CYSIZE", "SM_CXSMSIZE", + "SM_CYSMSIZE" + }; + int flags[] = { + SM_CXBORDER, SM_CYBORDER, SM_CXVSCROLL, SM_CYVSCROLL, + SM_CXHSCROLL, SM_CYHSCROLL, SM_CXMENUCHECK, SM_CYMENUCHECK, + SM_CXMENUSIZE, SM_CYMENUSIZE, SM_CXSIZE, SM_CYSIZE, SM_CXSMSIZE, + SM_CYSMSIZE + }; + + Tcl_Obj **objv; + int i, objc; + + if (Tcl_ListObjGetElements(interp, objPtr, &objc, &objv) != TCL_OK) + return TCL_ERROR; + if (objc != 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj("wrong # args", -1)); + Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL); + return TCL_ERROR; + } + for (i = 0; i < objc; ++i) { + int option; + if (Tcl_GetIndexFromObjStruct(interp, objv[i], names, + sizeof(char *), "system constant", 0, &option) != TCL_OK) + return TCL_ERROR; + *resultPtr |= (flags[option] << (8 * (1 - i))); + } + return TCL_OK; +} + +/*---------------------------------------------------------------------- + * Windows Visual Styles API Element Factory + * + * The Vista release has shown that the Windows Visual Styles can be + * extended with additional elements. This element factory can permit + * the programmer to create elements for use with script-defined layouts + * + * eg: to create the small close button: + * style element create smallclose vsapi \ + * WINDOW 19 {disabled 4 pressed 3 active 2 {} 1} + */ + +static int +Ttk_CreateVsapiElement( + Tcl_Interp *interp, + void *clientData, + Ttk_Theme theme, + const char *elementName, + int objc, + Tcl_Obj *const objv[]) +{ + XPThemeData *themeData = clientData; + ElementInfo *elementPtr = NULL; + ClientData elementData; + Tcl_UniChar *className; + int partId = 0; + Ttk_StateTable *stateTable; + Ttk_Padding pad = {0, 0, 0, 0}; + int flags = 0; + int length = 0; + char *name; + LPWSTR wname; + Ttk_ElementSpec *elementSpec = &GenericElementSpec; + + static const char *optionStrings[] = + { "-padding","-width","-height","-margins", "-syssize", + "-halfheight", "-halfwidth", NULL }; + enum { O_PADDING, O_WIDTH, O_HEIGHT, O_MARGINS, O_SYSSIZE, + O_HALFHEIGHT, O_HALFWIDTH }; + + if (objc < 2) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "missing required arguments 'class' and/or 'partId'", -1)); + Tcl_SetErrorCode(interp, "TTK", "VSAPI", "REQUIRED", NULL); + return TCL_ERROR; + } + + if (Tcl_GetIntFromObj(interp, objv[1], &partId) != TCL_OK) { + return TCL_ERROR; + } + className = Tcl_GetUnicodeFromObj(objv[0], &length); + + /* flags or padding */ + if (objc > 3) { + int i = 3, option = 0; + for (i = 3; i < objc; i += 2) { + int tmp = 0; + if (i == objc -1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "Missing value for \"%s\".", + Tcl_GetString(objv[i]))); + Tcl_SetErrorCode(interp, "TTK", "VSAPI", "MISSING", NULL); + return TCL_ERROR; + } + if (Tcl_GetIndexFromObjStruct(interp, objv[i], optionStrings, + sizeof(char *), "option", 0, &option) != TCL_OK) + return TCL_ERROR; + switch (option) { + case O_PADDING: + if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) { + return TCL_ERROR; + } + break; + case O_MARGINS: + if (Ttk_GetBorderFromObj(interp, objv[i+1], &pad) != TCL_OK) { + return TCL_ERROR; + } + flags |= PAD_MARGINS; + break; + case O_WIDTH: + if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { + return TCL_ERROR; + } + pad.left = pad.right = tmp; + flags |= IGNORE_THEMESIZE; + break; + case O_HEIGHT: + if (Tcl_GetIntFromObj(interp, objv[i+1], &tmp) != TCL_OK) { + return TCL_ERROR; + } + pad.top = pad.bottom = tmp; + flags |= IGNORE_THEMESIZE; + break; + case O_SYSSIZE: + if (GetSysFlagFromObj(interp, objv[i+1], &tmp) != TCL_OK) { + return TCL_ERROR; + } + elementSpec = &GenericSizedElementSpec; + flags |= (tmp & 0xFFFF); + break; + case O_HALFHEIGHT: + if (Tcl_GetBooleanFromObj(interp, objv[i+1], &tmp) != TCL_OK) { + return TCL_ERROR; + } + if (tmp) + flags |= HALF_HEIGHT; + break; + case O_HALFWIDTH: + if (Tcl_GetBooleanFromObj(interp, objv[i+1], &tmp) != TCL_OK) { + return TCL_ERROR; + } + if (tmp) + flags |= HALF_WIDTH; + break; + } + } + } + + /* convert a statemap into a state table */ + if (objc > 2) { + Tcl_Obj **specs; + int n,j,count, status = TCL_OK; + if (Tcl_ListObjGetElements(interp, objv[2], &count, &specs) != TCL_OK) + return TCL_ERROR; + /* we over-allocate to ensure there is a terminating entry */ + stateTable = ckalloc(sizeof(Ttk_StateTable) * (count + 1)); + memset(stateTable, 0, sizeof(Ttk_StateTable) * (count + 1)); + for (n = 0, j = 0; status == TCL_OK && n < count; n += 2, ++j) { + Ttk_StateSpec spec = {0,0}; + status = Ttk_GetStateSpecFromObj(interp, specs[n], &spec); + if (status == TCL_OK) { + stateTable[j].onBits = spec.onbits; + stateTable[j].offBits = spec.offbits; + status = Tcl_GetIntFromObj(interp, specs[n+1], + &stateTable[j].index); + } + } + if (status != TCL_OK) { + ckfree(stateTable); + return status; + } + } else { + stateTable = ckalloc(sizeof(Ttk_StateTable)); + memset(stateTable, 0, sizeof(Ttk_StateTable)); + } + + elementPtr = ckalloc(sizeof(ElementInfo)); + elementPtr->elementSpec = elementSpec; + elementPtr->partId = partId; + elementPtr->statemap = stateTable; + elementPtr->padding = pad; + elementPtr->flags = HEAP_ELEMENT | flags; + + /* set the element name to an allocated copy */ + name = ckalloc(strlen(elementName) + 1); + strcpy(name, elementName); + elementPtr->elementName = name; + + /* set the class name to an allocated copy */ + wname = ckalloc(sizeof(WCHAR) * (length + 1)); + wcscpy(wname, className); + elementPtr->className = wname; + + elementData = NewElementData(themeData->procs, elementPtr); + Ttk_RegisterElementSpec( + theme, elementName, elementPtr->elementSpec, elementData); + + Ttk_RegisterCleanup(interp, elementData, DestroyElementData); + Tcl_SetObjResult(interp, Tcl_NewStringObj(elementName, -1)); + return TCL_OK; +} + +/*---------------------------------------------------------------------- + * +++ Initialization routine: + */ + +MODULE_SCOPE int TtkXPTheme_Init(Tcl_Interp *interp, HWND hwnd) +{ + XPThemeData *themeData; + XPThemeProcs *procs; + HINSTANCE hlibrary; + Ttk_Theme themePtr, parentPtr, vistaPtr; + ElementInfo *infoPtr; + OSVERSIONINFOW os; + + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + GetVersionExW(&os); + + procs = LoadXPThemeProcs(&hlibrary); + if (!procs) + return TCL_ERROR; + procs->stubWindow = hwnd; + + /* + * Create the new style engine. + */ + parentPtr = Ttk_GetTheme(interp, "winnative"); + themePtr = Ttk_CreateTheme(interp, "xpnative", parentPtr); + + if (!themePtr) + return TCL_ERROR; + + /* + * Set theme data and cleanup proc + */ + + themeData = ckalloc(sizeof(XPThemeData)); + themeData->procs = procs; + themeData->hlibrary = hlibrary; + + Ttk_SetThemeEnabledProc(themePtr, XPThemeEnabled, themeData); + Ttk_RegisterCleanup(interp, themeData, XPThemeDeleteProc); + Ttk_RegisterElementFactory(interp, "vsapi", Ttk_CreateVsapiElement, themeData); + + /* + * Create the vista theme on suitable platform versions and set the theme + * enable function. The theme itself is defined in script. + */ + + if (os.dwPlatformId == VER_PLATFORM_WIN32_NT && os.dwMajorVersion > 5) { + vistaPtr = Ttk_CreateTheme(interp, "vista", themePtr); + if (vistaPtr) { + Ttk_SetThemeEnabledProc(vistaPtr, XPThemeEnabled, themeData); + } + } + + /* + * New elements: + */ + for (infoPtr = ElementInfoTable; infoPtr->elementName != 0; ++infoPtr) { + ClientData clientData = NewElementData(procs, infoPtr); + Ttk_RegisterElementSpec( + themePtr, infoPtr->elementName, infoPtr->elementSpec, clientData); + Ttk_RegisterCleanup(interp, clientData, DestroyElementData); + } + + Ttk_RegisterElementSpec(themePtr, "Scale.trough", &ttkNullElementSpec, 0); + + /* + * Layouts: + */ + Ttk_RegisterLayouts(themePtr, LayoutTable); + + Tcl_PkgProvide(interp, "ttk::theme::xpnative", TTK_VERSION); + + return TCL_OK; +} + +#endif /* HAVE_UXTHEME_H */ diff --git a/tk8.6/win/winMain.c b/tk8.6/win/winMain.c new file mode 100644 index 0000000..62bcbd8 --- /dev/null +++ b/tk8.6/win/winMain.c @@ -0,0 +1,436 @@ +/* + * winMain.c -- + * + * Provides a default version of the main program and Tcl_AppInit + * procedure for wish and other Tk-based applications. + * + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-1999 Scriptics Corporation. + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tk.h" +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#undef WIN32_LEAN_AND_MEAN +#include <locale.h> +#include <stdlib.h> +#include <tchar.h> + +#if defined(__GNUC__) +int _CRT_glob = 0; +#endif /* __GNUC__ */ + +#ifdef TK_TEST +extern Tcl_PackageInitProc Tktest_Init; +#endif /* TK_TEST */ + +#if defined(STATIC_BUILD) && TCL_USE_STATIC_PACKAGES +extern Tcl_PackageInitProc Registry_Init; +extern Tcl_PackageInitProc Dde_Init; +extern Tcl_PackageInitProc Dde_SafeInit; +#endif + +#ifdef TCL_BROKEN_MAINARGS +static void setargv(int *argcPtr, TCHAR ***argvPtr); +#endif + +/* + * Forward declarations for procedures defined later in this file: + */ + +static BOOL consoleRequired = TRUE; + +/* + * The following #if block allows you to change the AppInit function by using + * a #define of TCL_LOCAL_APPINIT instead of rewriting this entire file. The + * #if checks for that #define and uses Tcl_AppInit if it doesn't exist. + */ + +#ifndef TK_LOCAL_APPINIT +#define TK_LOCAL_APPINIT Tcl_AppInit +#endif +#ifndef MODULE_SCOPE +# define MODULE_SCOPE extern +#endif +MODULE_SCOPE int TK_LOCAL_APPINIT(Tcl_Interp *interp); + +/* + * The following #if block allows you to change how Tcl finds the startup + * script, prime the library or encoding paths, fiddle with the argv, etc., + * without needing to rewrite Tk_Main() + */ + +#ifdef TK_LOCAL_MAIN_HOOK +MODULE_SCOPE int TK_LOCAL_MAIN_HOOK(int *argc, TCHAR ***argv); +#endif + +/* Make sure the stubbed variants of those are never used. */ +#undef Tcl_ObjSetVar2 +#undef Tcl_NewStringObj + +/* + *---------------------------------------------------------------------- + * + * _tWinMain -- + * + * Main entry point from Windows. + * + * Results: + * Returns false if initialization fails, otherwise it never returns. + * + * Side effects: + * Just about anything, since from here we call arbitrary Tcl code. + * + *---------------------------------------------------------------------- + */ + +int APIENTRY +#ifdef TCL_BROKEN_MAINARGS +WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpszCmdLine, + int nCmdShow) +#else +_tWinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPTSTR lpszCmdLine, + int nCmdShow) +#endif +{ + TCHAR **argv; + int argc; + TCHAR *p; + + /* + * Create the console channels and install them as the standard channels. + * All I/O will be discarded until Tk_CreateConsoleWindow is called to + * attach the console to a text widget. + */ + + consoleRequired = TRUE; + + /* + * Set up the default locale to be standard "C" locale so parsing is + * performed correctly. + */ + + setlocale(LC_ALL, "C"); + + /* + * Get our args from the c-runtime. Ignore lpszCmdLine. + */ + +#if defined(TCL_BROKEN_MAINARGS) + setargv(&argc, &argv); +#else + argc = __argc; + argv = __targv; +#endif + + /* + * Forward slashes substituted for backslashes. + */ + + for (p = argv[0]; *p != '\0'; p++) { + if (*p == '\\') { + *p = '/'; + } + } + +#ifdef TK_LOCAL_MAIN_HOOK + TK_LOCAL_MAIN_HOOK(&argc, &argv); +#endif + + Tk_Main(argc, argv, TK_LOCAL_APPINIT); + return 0; /* Needed only to prevent compiler warning. */ +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. Most + * applications, especially those that incorporate additional packages, + * will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error message in + * the interp's result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_AppInit( + Tcl_Interp *interp) /* Interpreter for application. */ +{ + if ((Tcl_Init)(interp) == TCL_ERROR) { + return TCL_ERROR; + } + if (Tk_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "Tk", Tk_Init, Tk_SafeInit); + + /* + * Initialize the console only if we are running as an interactive + * application. + */ + + if (consoleRequired) { + if (Tk_CreateConsoleWindow(interp) == TCL_ERROR) { + return TCL_ERROR; + } + } +#if defined(STATIC_BUILD) && TCL_USE_STATIC_PACKAGES + if (Registry_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "registry", Registry_Init, 0); + + if (Dde_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "dde", Dde_Init, Dde_SafeInit); +#endif + +#ifdef TK_TEST + if (Tktest_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + Tcl_StaticPackage(interp, "Tktest", Tktest_Init, 0); +#endif /* TK_TEST */ + + /* + * Call the init procedures for included packages. Each call should look + * like this: + * + * if (Mod_Init(interp) == TCL_ERROR) { + * return TCL_ERROR; + * } + * + * where "Mod" is the name of the module. (Dynamically-loadable packages + * should have the same entry-point name.) + */ + + /* + * Call Tcl_CreateObjCommand for application-specific commands, if they + * weren't already created by the init procedures called above. + */ + + /* + * Specify a user-specific startup file to invoke if the application is + * run interactively. Typically the startup file is "~/.apprc" where "app" + * is the name of the application. If this line is deleted then no user- + * specific startup file will be run under any conditions. + */ + + Tcl_ObjSetVar2(interp, Tcl_NewStringObj("tcl_rcFileName", -1), NULL, + Tcl_NewStringObj("~/wishrc.tcl", -1), TCL_GLOBAL_ONLY); + return TCL_OK; +} + +#if defined(TK_TEST) +/* + *---------------------------------------------------------------------- + * + * _tmain -- + * + * Main entry point from the console. + * + * Results: + * None: Tk_Main never returns here, so this procedure never returns + * either. + * + * Side effects: + * Whatever the applications does. + * + *---------------------------------------------------------------------- + */ + +#ifdef TCL_BROKEN_MAINARGS +int +main( + int argc, + char **dummy) +{ + TCHAR **argv; +#else +int +_tmain( + int argc, + TCHAR **argv) +{ +#endif + /* + * Set up the default locale to be standard "C" locale so parsing is + * performed correctly. + */ + + setlocale(LC_ALL, "C"); + +#ifdef TCL_BROKEN_MAINARGS + /* + * Get our args from the c-runtime. Ignore argc/argv. + */ + + setargv(&argc, &argv); +#endif + /* + * Console emulation widget not required as this entry is from the + * console subsystem, thus stdin,out,err already have end-points. + */ + + consoleRequired = FALSE; + +#ifdef TK_LOCAL_MAIN_HOOK + TK_LOCAL_MAIN_HOOK(&argc, &argv); +#endif + + Tk_Main(argc, argv, Tcl_AppInit); + return 0; +} +#endif /* !__GNUC__ || TK_TEST */ + + +/* + *------------------------------------------------------------------------- + * + * setargv -- + * + * Parse the Windows command line string into argc/argv. Done here + * because we don't trust the builtin argument parser in crt0. Windows + * applications are responsible for breaking their command line into + * arguments. + * + * 2N backslashes + quote -> N backslashes + begin quoted string + * 2N + 1 backslashes + quote -> literal + * N backslashes + non-quote -> literal + * quote + quote in a quoted string -> single quote + * quote + quote not in quoted string -> empty string + * quote -> begin quoted string + * + * Results: + * Fills argcPtr with the number of arguments and argvPtr with the array + * of arguments. + * + * Side effects: + * Memory allocated. + * + *-------------------------------------------------------------------------- + */ + +#ifdef TCL_BROKEN_MAINARGS +static void +setargv( + int *argcPtr, /* Filled with number of argument strings. */ + TCHAR ***argvPtr) /* Filled with argument strings (malloc'd). */ +{ + TCHAR *cmdLine, *p, *arg, *argSpace; + TCHAR **argv; + int argc, size, inquote, copy, slashes; + + cmdLine = GetCommandLine(); + + /* + * Precompute an overly pessimistic guess at the number of arguments in + * the command line by counting non-space spans. + */ + + size = 2; + for (p = cmdLine; *p != '\0'; p++) { + if ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ + size++; + while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ + p++; + } + if (*p == '\0') { + break; + } + } + } + + /* Make sure we don't call ckalloc through the (not yet initialized) stub table */ + #undef Tcl_Alloc + #undef Tcl_DbCkalloc + + argSpace = ckalloc(size * sizeof(char *) + + (_tcslen(cmdLine) * sizeof(TCHAR)) + sizeof(TCHAR)); + argv = (TCHAR **) argSpace; + argSpace += size * (sizeof(char *)/sizeof(TCHAR)); + size--; + + p = cmdLine; + for (argc = 0; argc < size; argc++) { + argv[argc] = arg = argSpace; + while ((*p == ' ') || (*p == '\t')) { /* INTL: ISO space. */ + p++; + } + if (*p == '\0') { + break; + } + + inquote = 0; + slashes = 0; + while (1) { + copy = 1; + while (*p == '\\') { + slashes++; + p++; + } + if (*p == '"') { + if ((slashes & 1) == 0) { + copy = 0; + if ((inquote) && (p[1] == '"')) { + p++; + copy = 1; + } else { + inquote = !inquote; + } + } + slashes >>= 1; + } + + while (slashes) { + *arg = '\\'; + arg++; + slashes--; + } + + if ((*p == '\0') || (!inquote && + ((*p == ' ') || (*p == '\t')))) { /* INTL: ISO space. */ + break; + } + if (copy != 0) { + *arg = *p; + arg++; + } + p++; + } + *arg = '\0'; + argSpace = arg + 1; + } + argv[argc] = NULL; + + *argcPtr = argc; + *argvPtr = argv; +} +#endif /* TCL_BROKEN_MAINARGS */ + +/* + * Local Variables: + * mode: c + * c-basic-offset: 4 + * fill-column: 78 + * End: + */ diff --git a/tk8.6/win/wish.exe.manifest.in b/tk8.6/win/wish.exe.manifest.in new file mode 100644 index 0000000..4829471 --- /dev/null +++ b/tk8.6/win/wish.exe.manifest.in @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" + xmlns:asmv3="urn:schemas-microsoft-com:asm.v3"> + <assemblyIdentity + version="@TK_WIN_VERSION@" + processorArchitecture="@MACHINE@" + name="Tcl.Tk.wish" + type="win32" + /> + <description>Tcl/Tk windowing shell (wish)</description> + <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> + <security> + <requestedPrivileges> + <requestedExecutionLevel + level="asInvoker" + uiAccess="false" + /> + </requestedPrivileges> + </security> + </trustInfo> + <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> + <application> + <!-- Windows 10 --> + <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/> + <!-- Windows 8.1 --> + <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/> + <!-- Windows 8 --> + <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/> + <!-- Windows 7 --> + <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/> + <!-- Windows Vista --> + <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> + </application> + </compatibility> + <asmv3:application> + <asmv3:windowsSettings + xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"> + <dpiAware>true</dpiAware> + </asmv3:windowsSettings> + </asmv3:application> + <dependency> + <dependentAssembly> + <assemblyIdentity + type="win32" + name="Microsoft.Windows.Common-Controls" + version="6.0.0.0" + processorArchitecture="@MACHINE@" + publicKeyToken="6595b64144ccf1df" + language="*" + /> + </dependentAssembly> + </dependency> +</assembly> |