summaryrefslogtreecommitdiffstats
path: root/tk8.6/win
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2018-12-25 17:48:54 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2018-12-25 17:48:54 (GMT)
commit8eb0f61e2e27ef6594eee8bcf68d574fb087fe66 (patch)
treefc0f3692516c8c3e8090df20223d342a1b64df93 /tk8.6/win
parent5f5fd2864a3193a8d5da12fcb92ba7379084c286 (diff)
downloadblt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.zip
blt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.tar.gz
blt-8eb0f61e2e27ef6594eee8bcf68d574fb087fe66.tar.bz2
update tcl/tk
Diffstat (limited to 'tk8.6/win')
-rw-r--r--tk8.6/win/Makefile.in748
-rw-r--r--tk8.6/win/README21
-rw-r--r--tk8.6/win/aclocal.m41
-rwxr-xr-xtk8.6/win/buildall.vc.bat107
-rwxr-xr-xtk8.6/win/configure6176
-rw-r--r--tk8.6/win/configure.in314
-rw-r--r--tk8.6/win/license.terms40
-rw-r--r--tk8.6/win/makefile.vc697
-rw-r--r--tk8.6/win/mkd.bat12
-rw-r--r--tk8.6/win/nmakehlp.c814
-rw-r--r--tk8.6/win/rc/buttons.bmpbin0 -> 846 bytes
-rw-r--r--tk8.6/win/rc/cursor00.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor02.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor04.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor06.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor08.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor0a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor0c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor0e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor10.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor12.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor14.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor16.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor18.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor1a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor1c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor1e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor20.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor22.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor24.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor26.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor28.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor2a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor2c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor2e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor30.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor32.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor34.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor36.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor38.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor3a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor3c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor3e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor40.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor42.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor44.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor46.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor48.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor4a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor4c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor4e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor50.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor52.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor54.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor56.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor58.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor5a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor5c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor5e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor60.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor62.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor64.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor66.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor68.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor6a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor6c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor6e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor70.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor72.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor74.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor76.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor78.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor7a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor7c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor7e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor80.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor82.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor84.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor86.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor88.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor8a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor8c.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor8e.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor90.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor92.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor94.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor96.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor98.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/cursor9a.curbin0 -> 326 bytes
-rw-r--r--tk8.6/win/rc/lamp.bmpbin0 -> 2102 bytes
-rw-r--r--tk8.6/win/rc/tk.icobin0 -> 57022 bytes
-rw-r--r--tk8.6/win/rc/tk.rc74
-rw-r--r--tk8.6/win/rc/tk_base.rc140
-rw-r--r--tk8.6/win/rc/wish.icobin0 -> 43646 bytes
-rw-r--r--tk8.6/win/rc/wish.rc87
-rw-r--r--tk8.6/win/rmd.bat20
-rw-r--r--tk8.6/win/rules-ext.vc118
-rw-r--r--tk8.6/win/rules.vc1752
-rw-r--r--tk8.6/win/stubs.c474
-rw-r--r--tk8.6/win/targets.vc98
-rw-r--r--tk8.6/win/tcl.m41299
-rw-r--r--tk8.6/win/tkConfig.sh.in87
-rw-r--r--tk8.6/win/tkWin.h81
-rw-r--r--tk8.6/win/tkWin32Dll.c296
-rw-r--r--tk8.6/win/tkWin3d.c573
-rw-r--r--tk8.6/win/tkWinButton.c1307
-rw-r--r--tk8.6/win/tkWinClipboard.c478
-rw-r--r--tk8.6/win/tkWinColor.c589
-rw-r--r--tk8.6/win/tkWinConfig.c64
-rw-r--r--tk8.6/win/tkWinCursor.c272
-rw-r--r--tk8.6/win/tkWinDefault.h536
-rw-r--r--tk8.6/win/tkWinDialog.c3623
-rw-r--r--tk8.6/win/tkWinDraw.c1523
-rw-r--r--tk8.6/win/tkWinEmbed.c1118
-rw-r--r--tk8.6/win/tkWinFont.c2908
-rw-r--r--tk8.6/win/tkWinImage.c696
-rw-r--r--tk8.6/win/tkWinInit.c223
-rw-r--r--tk8.6/win/tkWinInt.h256
-rw-r--r--tk8.6/win/tkWinKey.c750
-rw-r--r--tk8.6/win/tkWinMenu.c3406
-rw-r--r--tk8.6/win/tkWinPixmap.c251
-rw-r--r--tk8.6/win/tkWinPointer.c546
-rw-r--r--tk8.6/win/tkWinPort.h123
-rw-r--r--tk8.6/win/tkWinRegion.c288
-rw-r--r--tk8.6/win/tkWinScrlbr.c701
-rw-r--r--tk8.6/win/tkWinSend.c1017
-rw-r--r--tk8.6/win/tkWinSendCom.c479
-rw-r--r--tk8.6/win/tkWinSendCom.h61
-rw-r--r--tk8.6/win/tkWinTest.c583
-rw-r--r--tk8.6/win/tkWinWindow.c1017
-rw-r--r--tk8.6/win/tkWinWm.c8688
-rw-r--r--tk8.6/win/tkWinX.c1977
-rw-r--r--tk8.6/win/ttkWinMonitor.c161
-rw-r--r--tk8.6/win/ttkWinTheme.c733
-rw-r--r--tk8.6/win/ttkWinXPTheme.c1329
-rw-r--r--tk8.6/win/winMain.c436
-rw-r--r--tk8.6/win/wish.exe.manifest.in53
137 files changed, 50221 insertions, 0 deletions
diff --git a/tk8.6/win/Makefile.in b/tk8.6/win/Makefile.in
new file mode 100644
index 0000000..e878484
--- /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 =
+#NO_DEPRECATED_FLAGS = -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..203d702
--- /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=".9"
+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..167fd3d
--- /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=".9"
+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..4050663
--- /dev/null
+++ b/tk8.6/win/makefile.vc
@@ -0,0 +1,697 @@
+#------------------------------------------------------------- -*- 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.
+!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..4e3d792
--- /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
new file mode 100644
index 0000000..f37a4c9
--- /dev/null
+++ b/tk8.6/win/rc/buttons.bmp
Binary files differ
diff --git a/tk8.6/win/rc/cursor00.cur b/tk8.6/win/rc/cursor00.cur
new file mode 100644
index 0000000..0c58bfe
--- /dev/null
+++ b/tk8.6/win/rc/cursor00.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor02.cur b/tk8.6/win/rc/cursor02.cur
new file mode 100644
index 0000000..46f0f11
--- /dev/null
+++ b/tk8.6/win/rc/cursor02.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor04.cur b/tk8.6/win/rc/cursor04.cur
new file mode 100644
index 0000000..1b3028d
--- /dev/null
+++ b/tk8.6/win/rc/cursor04.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor06.cur b/tk8.6/win/rc/cursor06.cur
new file mode 100644
index 0000000..bb06edf
--- /dev/null
+++ b/tk8.6/win/rc/cursor06.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor08.cur b/tk8.6/win/rc/cursor08.cur
new file mode 100644
index 0000000..d9f15f7
--- /dev/null
+++ b/tk8.6/win/rc/cursor08.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor0a.cur b/tk8.6/win/rc/cursor0a.cur
new file mode 100644
index 0000000..6bffda5
--- /dev/null
+++ b/tk8.6/win/rc/cursor0a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor0c.cur b/tk8.6/win/rc/cursor0c.cur
new file mode 100644
index 0000000..a991da6
--- /dev/null
+++ b/tk8.6/win/rc/cursor0c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor0e.cur b/tk8.6/win/rc/cursor0e.cur
new file mode 100644
index 0000000..6e0434d
--- /dev/null
+++ b/tk8.6/win/rc/cursor0e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor10.cur b/tk8.6/win/rc/cursor10.cur
new file mode 100644
index 0000000..c4f7809
--- /dev/null
+++ b/tk8.6/win/rc/cursor10.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor12.cur b/tk8.6/win/rc/cursor12.cur
new file mode 100644
index 0000000..0d0d667
--- /dev/null
+++ b/tk8.6/win/rc/cursor12.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor14.cur b/tk8.6/win/rc/cursor14.cur
new file mode 100644
index 0000000..c7de122
--- /dev/null
+++ b/tk8.6/win/rc/cursor14.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor16.cur b/tk8.6/win/rc/cursor16.cur
new file mode 100644
index 0000000..cfc08f2
--- /dev/null
+++ b/tk8.6/win/rc/cursor16.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor18.cur b/tk8.6/win/rc/cursor18.cur
new file mode 100644
index 0000000..95ed2ee
--- /dev/null
+++ b/tk8.6/win/rc/cursor18.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor1a.cur b/tk8.6/win/rc/cursor1a.cur
new file mode 100644
index 0000000..ea51361
--- /dev/null
+++ b/tk8.6/win/rc/cursor1a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor1c.cur b/tk8.6/win/rc/cursor1c.cur
new file mode 100644
index 0000000..6f10bfb
--- /dev/null
+++ b/tk8.6/win/rc/cursor1c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor1e.cur b/tk8.6/win/rc/cursor1e.cur
new file mode 100644
index 0000000..3272db1
--- /dev/null
+++ b/tk8.6/win/rc/cursor1e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor20.cur b/tk8.6/win/rc/cursor20.cur
new file mode 100644
index 0000000..48080be
--- /dev/null
+++ b/tk8.6/win/rc/cursor20.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor22.cur b/tk8.6/win/rc/cursor22.cur
new file mode 100644
index 0000000..2f8e912
--- /dev/null
+++ b/tk8.6/win/rc/cursor22.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor24.cur b/tk8.6/win/rc/cursor24.cur
new file mode 100644
index 0000000..87ba5b4
--- /dev/null
+++ b/tk8.6/win/rc/cursor24.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor26.cur b/tk8.6/win/rc/cursor26.cur
new file mode 100644
index 0000000..0b2dbd2
--- /dev/null
+++ b/tk8.6/win/rc/cursor26.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor28.cur b/tk8.6/win/rc/cursor28.cur
new file mode 100644
index 0000000..30550f9
--- /dev/null
+++ b/tk8.6/win/rc/cursor28.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor2a.cur b/tk8.6/win/rc/cursor2a.cur
new file mode 100644
index 0000000..5e75f16
--- /dev/null
+++ b/tk8.6/win/rc/cursor2a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor2c.cur b/tk8.6/win/rc/cursor2c.cur
new file mode 100644
index 0000000..7be3494
--- /dev/null
+++ b/tk8.6/win/rc/cursor2c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor2e.cur b/tk8.6/win/rc/cursor2e.cur
new file mode 100644
index 0000000..7a0bc69
--- /dev/null
+++ b/tk8.6/win/rc/cursor2e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor30.cur b/tk8.6/win/rc/cursor30.cur
new file mode 100644
index 0000000..70ef4fd
--- /dev/null
+++ b/tk8.6/win/rc/cursor30.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor32.cur b/tk8.6/win/rc/cursor32.cur
new file mode 100644
index 0000000..93b5c47
--- /dev/null
+++ b/tk8.6/win/rc/cursor32.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor34.cur b/tk8.6/win/rc/cursor34.cur
new file mode 100644
index 0000000..0fad3f1
--- /dev/null
+++ b/tk8.6/win/rc/cursor34.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor36.cur b/tk8.6/win/rc/cursor36.cur
new file mode 100644
index 0000000..fc8d4f6
--- /dev/null
+++ b/tk8.6/win/rc/cursor36.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor38.cur b/tk8.6/win/rc/cursor38.cur
new file mode 100644
index 0000000..4447d7d
--- /dev/null
+++ b/tk8.6/win/rc/cursor38.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor3a.cur b/tk8.6/win/rc/cursor3a.cur
new file mode 100644
index 0000000..e3eda81
--- /dev/null
+++ b/tk8.6/win/rc/cursor3a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor3c.cur b/tk8.6/win/rc/cursor3c.cur
new file mode 100644
index 0000000..6a3111d
--- /dev/null
+++ b/tk8.6/win/rc/cursor3c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor3e.cur b/tk8.6/win/rc/cursor3e.cur
new file mode 100644
index 0000000..fa6fe5b
--- /dev/null
+++ b/tk8.6/win/rc/cursor3e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor40.cur b/tk8.6/win/rc/cursor40.cur
new file mode 100644
index 0000000..f07bf4f
--- /dev/null
+++ b/tk8.6/win/rc/cursor40.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor42.cur b/tk8.6/win/rc/cursor42.cur
new file mode 100644
index 0000000..387d5f0
--- /dev/null
+++ b/tk8.6/win/rc/cursor42.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor44.cur b/tk8.6/win/rc/cursor44.cur
new file mode 100644
index 0000000..190320c
--- /dev/null
+++ b/tk8.6/win/rc/cursor44.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor46.cur b/tk8.6/win/rc/cursor46.cur
new file mode 100644
index 0000000..4f969d1
--- /dev/null
+++ b/tk8.6/win/rc/cursor46.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor48.cur b/tk8.6/win/rc/cursor48.cur
new file mode 100644
index 0000000..97e499a
--- /dev/null
+++ b/tk8.6/win/rc/cursor48.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor4a.cur b/tk8.6/win/rc/cursor4a.cur
new file mode 100644
index 0000000..30febfa
--- /dev/null
+++ b/tk8.6/win/rc/cursor4a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor4c.cur b/tk8.6/win/rc/cursor4c.cur
new file mode 100644
index 0000000..3512a51
--- /dev/null
+++ b/tk8.6/win/rc/cursor4c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor4e.cur b/tk8.6/win/rc/cursor4e.cur
new file mode 100644
index 0000000..28992c4
--- /dev/null
+++ b/tk8.6/win/rc/cursor4e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor50.cur b/tk8.6/win/rc/cursor50.cur
new file mode 100644
index 0000000..7352420
--- /dev/null
+++ b/tk8.6/win/rc/cursor50.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor52.cur b/tk8.6/win/rc/cursor52.cur
new file mode 100644
index 0000000..435f99f
--- /dev/null
+++ b/tk8.6/win/rc/cursor52.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor54.cur b/tk8.6/win/rc/cursor54.cur
new file mode 100644
index 0000000..54eb4f2
--- /dev/null
+++ b/tk8.6/win/rc/cursor54.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor56.cur b/tk8.6/win/rc/cursor56.cur
new file mode 100644
index 0000000..c808bd4
--- /dev/null
+++ b/tk8.6/win/rc/cursor56.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor58.cur b/tk8.6/win/rc/cursor58.cur
new file mode 100644
index 0000000..98b6a2f
--- /dev/null
+++ b/tk8.6/win/rc/cursor58.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor5a.cur b/tk8.6/win/rc/cursor5a.cur
new file mode 100644
index 0000000..b00070e
--- /dev/null
+++ b/tk8.6/win/rc/cursor5a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor5c.cur b/tk8.6/win/rc/cursor5c.cur
new file mode 100644
index 0000000..a407b55
--- /dev/null
+++ b/tk8.6/win/rc/cursor5c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor5e.cur b/tk8.6/win/rc/cursor5e.cur
new file mode 100644
index 0000000..ab3449f
--- /dev/null
+++ b/tk8.6/win/rc/cursor5e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor60.cur b/tk8.6/win/rc/cursor60.cur
new file mode 100644
index 0000000..dc61469
--- /dev/null
+++ b/tk8.6/win/rc/cursor60.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor62.cur b/tk8.6/win/rc/cursor62.cur
new file mode 100644
index 0000000..ff7c009
--- /dev/null
+++ b/tk8.6/win/rc/cursor62.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor64.cur b/tk8.6/win/rc/cursor64.cur
new file mode 100644
index 0000000..a6bdd0e
--- /dev/null
+++ b/tk8.6/win/rc/cursor64.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor66.cur b/tk8.6/win/rc/cursor66.cur
new file mode 100644
index 0000000..269f772
--- /dev/null
+++ b/tk8.6/win/rc/cursor66.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor68.cur b/tk8.6/win/rc/cursor68.cur
new file mode 100644
index 0000000..27cfaf0
--- /dev/null
+++ b/tk8.6/win/rc/cursor68.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor6a.cur b/tk8.6/win/rc/cursor6a.cur
new file mode 100644
index 0000000..20f138e
--- /dev/null
+++ b/tk8.6/win/rc/cursor6a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor6c.cur b/tk8.6/win/rc/cursor6c.cur
new file mode 100644
index 0000000..d8fb0f1
--- /dev/null
+++ b/tk8.6/win/rc/cursor6c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor6e.cur b/tk8.6/win/rc/cursor6e.cur
new file mode 100644
index 0000000..3a9b6b0
--- /dev/null
+++ b/tk8.6/win/rc/cursor6e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor70.cur b/tk8.6/win/rc/cursor70.cur
new file mode 100644
index 0000000..e2d7673
--- /dev/null
+++ b/tk8.6/win/rc/cursor70.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor72.cur b/tk8.6/win/rc/cursor72.cur
new file mode 100644
index 0000000..471bcf0
--- /dev/null
+++ b/tk8.6/win/rc/cursor72.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor74.cur b/tk8.6/win/rc/cursor74.cur
new file mode 100644
index 0000000..176d2b0
--- /dev/null
+++ b/tk8.6/win/rc/cursor74.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor76.cur b/tk8.6/win/rc/cursor76.cur
new file mode 100644
index 0000000..34f402a
--- /dev/null
+++ b/tk8.6/win/rc/cursor76.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor78.cur b/tk8.6/win/rc/cursor78.cur
new file mode 100644
index 0000000..70e25dd
--- /dev/null
+++ b/tk8.6/win/rc/cursor78.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor7a.cur b/tk8.6/win/rc/cursor7a.cur
new file mode 100644
index 0000000..5ea95c4
--- /dev/null
+++ b/tk8.6/win/rc/cursor7a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor7c.cur b/tk8.6/win/rc/cursor7c.cur
new file mode 100644
index 0000000..38036ab
--- /dev/null
+++ b/tk8.6/win/rc/cursor7c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor7e.cur b/tk8.6/win/rc/cursor7e.cur
new file mode 100644
index 0000000..4b24e50
--- /dev/null
+++ b/tk8.6/win/rc/cursor7e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor80.cur b/tk8.6/win/rc/cursor80.cur
new file mode 100644
index 0000000..a3955a5
--- /dev/null
+++ b/tk8.6/win/rc/cursor80.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor82.cur b/tk8.6/win/rc/cursor82.cur
new file mode 100644
index 0000000..984cfba
--- /dev/null
+++ b/tk8.6/win/rc/cursor82.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor84.cur b/tk8.6/win/rc/cursor84.cur
new file mode 100644
index 0000000..cd6807e
--- /dev/null
+++ b/tk8.6/win/rc/cursor84.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor86.cur b/tk8.6/win/rc/cursor86.cur
new file mode 100644
index 0000000..52cb5c3
--- /dev/null
+++ b/tk8.6/win/rc/cursor86.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor88.cur b/tk8.6/win/rc/cursor88.cur
new file mode 100644
index 0000000..fdba0d6
--- /dev/null
+++ b/tk8.6/win/rc/cursor88.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor8a.cur b/tk8.6/win/rc/cursor8a.cur
new file mode 100644
index 0000000..369e71f
--- /dev/null
+++ b/tk8.6/win/rc/cursor8a.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor8c.cur b/tk8.6/win/rc/cursor8c.cur
new file mode 100644
index 0000000..811a0c3
--- /dev/null
+++ b/tk8.6/win/rc/cursor8c.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor8e.cur b/tk8.6/win/rc/cursor8e.cur
new file mode 100644
index 0000000..a500a38
--- /dev/null
+++ b/tk8.6/win/rc/cursor8e.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor90.cur b/tk8.6/win/rc/cursor90.cur
new file mode 100644
index 0000000..3c655d5
--- /dev/null
+++ b/tk8.6/win/rc/cursor90.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor92.cur b/tk8.6/win/rc/cursor92.cur
new file mode 100644
index 0000000..4364b5d
--- /dev/null
+++ b/tk8.6/win/rc/cursor92.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor94.cur b/tk8.6/win/rc/cursor94.cur
new file mode 100644
index 0000000..16e6b61
--- /dev/null
+++ b/tk8.6/win/rc/cursor94.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor96.cur b/tk8.6/win/rc/cursor96.cur
new file mode 100644
index 0000000..cecaea3
--- /dev/null
+++ b/tk8.6/win/rc/cursor96.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor98.cur b/tk8.6/win/rc/cursor98.cur
new file mode 100644
index 0000000..5cab68e
--- /dev/null
+++ b/tk8.6/win/rc/cursor98.cur
Binary files differ
diff --git a/tk8.6/win/rc/cursor9a.cur b/tk8.6/win/rc/cursor9a.cur
new file mode 100644
index 0000000..048f06b
--- /dev/null
+++ b/tk8.6/win/rc/cursor9a.cur
Binary files differ
diff --git a/tk8.6/win/rc/lamp.bmp b/tk8.6/win/rc/lamp.bmp
new file mode 100644
index 0000000..1e2f9d4
--- /dev/null
+++ b/tk8.6/win/rc/lamp.bmp
Binary files differ
diff --git a/tk8.6/win/rc/tk.ico b/tk8.6/win/rc/tk.ico
new file mode 100644
index 0000000..e254318
--- /dev/null
+++ b/tk8.6/win/rc/tk.ico
Binary files differ
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
new file mode 100644
index 0000000..5801fb8
--- /dev/null
+++ b/tk8.6/win/rc/wish.ico
Binary files differ
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..58c70fa
--- /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..543e959
--- /dev/null
+++ b/tk8.6/win/rules.vc
@@ -0,0 +1,1752 @@
+#------------------------------------------------------------- -*- 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 = 1
+
+# 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.
+!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]
+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
+
+# When building extensions, we need to locate tclsh. Depending on version
+# of Tcl we are building against, this may or may not have a "t" suffix.
+# Try various possibilities in turn.
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX).exe
+!if !exist("$(TCLSH)") && $(TCL_THREADS)
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)t$(SUFX).exe
+!endif
+!if !exist("$(TCLSH)")
+TCLSH = $(_TCLDIR)\bin\tclsh$(TCL_VERSION)$(SUFX:t=).exe
+!endif
+
+TCLSTUBLIB = $(_TCLDIR)\lib\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\lib\tcl$(TCL_VERSION)$(SUFX:t=).lib
+!endif
+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
+!if !exist($(TCLSH))
+TCLSH = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclsh$(TCL_VERSION)$(SUFX:t=).exe
+!endif
+TCLSTUBLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tclstub$(TCL_VERSION).lib
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX).lib
+# When building extensions, may be linking against Tcl that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TCLIMPLIB)")
+TCLIMPLIB = $(_TCLDIR)\win\$(BUILDDIRTOP)\tcl$(TCL_VERSION)$(SUFX:t=).lib
+!endif
+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)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\lib\$(TKIMPLIBNAME)
+!endif
+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)
+# When building extensions, may be linking against Tk that does not add
+# "t" suffix (e.g. 8.5 or 8.7). If lib not found check for that possibility.
+!if !exist("$(TKIMPLIB)")
+TKIMPLIBNAME = tk$(TK_VERSION)$(SUFX:t=).lib
+TKIMPLIB = $(_TKDIR)\win\$(BUILDDIRTOP)\$(TKIMPLIBNAME)
+!endif
+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), host $(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..7f1d388
--- /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..d6d2c7d
--- /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);
+ }
+ 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);
+ }
+ 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..43c5e25
--- /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 defined/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.
+ */
+
+ TCHAR msg[256];
+
+ wsprintf(msg, TEXT("Unable to get information of window \"%.40hs\". 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 == MessageBox(hwnd, msg, TEXT("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..a409764
--- /dev/null
+++ b/tk8.6/win/tkWinMenu.c
@@ -0,0 +1,3406 @@
+/*
+ * 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;
+
+ /*
+ * Quite dubious about the above (why would borderWidth play a role?)
+ * and about how indicatorDimensions[1] is obtained in SetDefaults().
+ * At least don't let the result be negative!
+ */
+ if (*widthPtr < 0) {
+ *widthPtr = 0;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 arrow. 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 = x + 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. */
+{
+ 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 * activeBorderWidth;
+ 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
+ + 2 * activeBorderWidth + 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.
+ * How strange the calculation of indicatorDimensions[1] is...!
+ */
+
+ 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..aa1ebde
--- /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 (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR)&lpMsgBuf, 0, NULL)) {
+ MessageBox(NULL, (LPTSTR) lpMsgBuf,
+ TEXT("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..a217108
--- /dev/null
+++ b/tk8.6/win/tkWinX.c
@@ -0,0 +1,1977 @@
+/*
+ * 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. */
+ DWORD wheelTickPrev; /* For high resolution wheels. */
+ short wheelAcc; /* For high resolution wheels. */
+} 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;
+ tsdPtr->wheelTickPrev = GetTickCount();
+ tsdPtr->wheelAcc = 0;
+
+ 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: {
+ /*
+ * Support for high resolution wheels.
+ */
+
+ DWORD wheelTick = GetTickCount();
+
+ if (wheelTick - tsdPtr->wheelTickPrev < 1500) {
+ tsdPtr->wheelAcc += (short) HIWORD(wParam);
+ } else {
+ tsdPtr->wheelAcc = (short) HIWORD(wParam);
+ }
+ tsdPtr->wheelTickPrev = wheelTick;
+ if (abs(tsdPtr->wheelAcc) < WHEEL_DELTA) {
+ return;
+ }
+
+ /*
+ * 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 = tsdPtr->wheelAcc / WHEEL_DELTA * WHEEL_DELTA;
+ tsdPtr->wheelAcc = tsdPtr->wheelAcc % WHEEL_DELTA;
+ 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>