summaryrefslogtreecommitdiffstats
path: root/unix
diff options
context:
space:
mode:
authorrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
committerrjohnson <rjohnson>1998-04-01 09:51:44 (GMT)
commit066ea7fd88d49cb456f74da71dbe875e4fc0aabb (patch)
tree8fb30cb152c4dc191be47fa043d2e6f5ea38c7ba /unix
parent13242623d2ff3ea02ab6a62bfb48a7dbb5c27e22 (diff)
downloadtk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.zip
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.gz
tk-066ea7fd88d49cb456f74da71dbe875e4fc0aabb.tar.bz2
Initial revision
Diffstat (limited to 'unix')
-rw-r--r--unix/Makefile.in1003
-rw-r--r--unix/README125
-rw-r--r--unix/configure.in407
-rw-r--r--unix/install-sh119
-rw-r--r--unix/mkLinks878
-rw-r--r--unix/porting.notes86
-rw-r--r--unix/porting.old324
-rw-r--r--unix/tkAppInit.c120
-rw-r--r--unix/tkConfig.sh.in68
-rw-r--r--unix/tkUnix.c79
-rw-r--r--unix/tkUnix3d.c448
-rw-r--r--unix/tkUnixButton.c478
-rw-r--r--unix/tkUnixColor.c424
-rw-r--r--unix/tkUnixCursor.c407
-rw-r--r--unix/tkUnixDefault.h450
-rw-r--r--unix/tkUnixDialog.c207
-rw-r--r--unix/tkUnixDraw.c171
-rw-r--r--unix/tkUnixEmbed.c1001
-rw-r--r--unix/tkUnixEvent.c498
-rw-r--r--unix/tkUnixFocus.c149
-rw-r--r--unix/tkUnixFont.c979
-rw-r--r--unix/tkUnixInit.c130
-rw-r--r--unix/tkUnixInt.h32
-rw-r--r--unix/tkUnixMenu.c1603
-rw-r--r--unix/tkUnixMenubu.c307
-rw-r--r--unix/tkUnixPort.h235
-rw-r--r--unix/tkUnixScale.c828
-rw-r--r--unix/tkUnixScrlbr.c476
-rw-r--r--unix/tkUnixSelect.c1189
-rw-r--r--unix/tkUnixSend.c1851
-rw-r--r--unix/tkUnixWm.c4813
-rw-r--r--unix/tkUnixXId.c537
32 files changed, 20422 insertions, 0 deletions
diff --git a/unix/Makefile.in b/unix/Makefile.in
new file mode 100644
index 0000000..51b9723
--- /dev/null
+++ b/unix/Makefile.in
@@ -0,0 +1,1003 @@
+#
+# 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.
+#
+# SCCS: @(#) Makefile.in 1.146 97/11/05 11:10:45
+
+# Current Tk version; used in various names.
+
+TCLVERSION = @TCL_VERSION@
+VERSION = @TK_VERSION@
+
+#----------------------------------------------------------------
+# 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@
+
+# 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 Tcl
+# scripts (note: you can set the TK_LIBRARY environment variable at
+# run-time to override the compiled-in location):
+TK_LIBRARY = $(prefix)/lib/tk$(VERSION)
+
+# Path name to use when installing library scripts:
+SCRIPT_INSTALL_DIR = $(INSTALL_ROOT)$(TK_LIBRARY)
+
+# Directory in which to install the .a or .so binary for the Tk library:
+LIB_INSTALL_DIR = $(INSTALL_ROOT)$(exec_prefix)/lib
+
+# Path to use at runtime to refer to LIB_INSTALL_DIR:
+LIB_RUNTIME_DIR = $(exec_prefix)/lib
+
+# Directory in which to install the program wish:
+BIN_INSTALL_DIR = $(INSTALL_ROOT)$(exec_prefix)/bin
+
+# Directory from which the program wish should be referenced by scripts:
+BIN_DIR = $(exec_prefix)/bin
+
+# Directory in which to install the include file tk.h:
+INCLUDE_INSTALL_DIR = $(INSTALL_ROOT)$(prefix)/include
+
+# Top-level directory for manual entries:
+MAN_INSTALL_DIR = $(INSTALL_ROOT)$(prefix)/man
+
+# 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
+# Tcl commands implemented by Tk:
+MANN_INSTALL_DIR = $(MAN_INSTALL_DIR)/mann
+
+# 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 Tcl library archive file appropriate
+# for this version of Tk:
+TCL_BIN_DIR = @TCL_BIN_DIR@
+
+# A "-I" switch that can be used when compiling to make all of the
+# X11 include files accessible (the configure script will try to
+# set this value, and will cause it to be an empty string if the
+# include files are accessible via /usr/include).
+X11_INCLUDES = @XINCLUDES@
+
+# Linker switch(es) to use to link with the X11 library archive (the
+# configure script will try to set this value automatically, but you
+# can override it).
+X11_LIB_SWITCHES = @XLIBSW@
+
+# Libraries to use when linking. This definition is determined by the
+# configure script.
+LIBS = @TCL_BUILD_LIB_SPEC@ @LIBS@ $(X11_LIB_SWITCHES) @DL_LIBS@ @MATH_LIBS@ -lc
+
+# To change the compiler switches, for example to change from -O
+# to -g, change the following line:
+CFLAGS = -O
+
+# To turn off the security checks that disallow incoming sends when
+# the X server appears to be insecure, reverse the comments on the
+# following lines:
+SECURITY_FLAGS =
+#SECURITY_FLAGS = -DTK_NO_SECURITY
+
+# To disable ANSI-C procedure prototypes reverse the comment characters
+# on the following lines:
+PROTO_FLAGS =
+#PROTO_FLAGS = -DNO_PROTOTYPE
+
+# To enable memory debugging reverse the comment characters on the following
+# lines. Warning: if you enable memory debugging, you must do it
+# *everywhere*, including all the code that calls Tcl, and you must use
+# ckalloc and ckfree everywhere instead of malloc and free.
+MEM_DEBUG_FLAGS =
+#MEM_DEBUG_FLAGS = -DTCL_MEM_DEBUG
+
+# If your X server is X11R4 or earlier, then you may wish to reverse
+# the comment characters on the following two lines. This will enable
+# extra code to speed up XStringToKeysym. In X11R5 and later releases
+# XStringToKeysym is plenty fast, so you needn't define REDO_KEYSYM_LOOKUP.
+KEYSYM_FLAGS =
+#KEYSYM_FLAGS = -DREDO_KEYSYM_LOOKUP
+
+# Some versions of make, like SGI's, use the following variable to
+# determine which shell to use for executing commands:
+SHELL = /bin/sh
+
+# 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 = @srcdir@/install-sh -c
+INSTALL_PROGRAM = ${INSTALL}
+INSTALL_DATA = ${INSTALL} -m 644
+
+
+# The symbols below provide support for dynamic loading and shared
+# libraries. The values of the symbols are normally set by the
+# configure script. You shouldn't normally need to modify any of
+# these definitions by hand.
+
+TK_SHLIB_CFLAGS = @TK_SHLIB_CFLAGS@
+
+TK_LIB_FILE = @TK_LIB_FILE@
+#TK_LIB_FILE = libtk.a
+
+# The symbol below provides support for dynamic loading and shared
+# libraries. See configure.in for a description of what it means.
+# The values of the symbolis normally set by the configure script.
+
+SHLIB_LD = @SHLIB_LD@
+
+# Additional search flags needed to find the various shared libraries
+# at run-time. The first symbol is for use when creating a binary
+# with cc, and the second is for use when running ld directly.
+TK_CC_SEARCH_FLAGS = @TK_CC_SEARCH_FLAGS@
+TK_LD_SEARCH_FLAGS = @TK_LD_SEARCH_FLAGS@
+
+#----------------------------------------------------------------
+# The information below is modified by the configure script when
+# Makefile is generated from Makefile.in. You shouldn't normally
+# modify any of this stuff by hand.
+#----------------------------------------------------------------
+
+AC_FLAGS = @DEFS@
+RANLIB = @RANLIB@
+SRC_DIR = @srcdir@/..
+TOP_DIR = @srcdir@/..
+GENERIC_DIR = $(TOP_DIR)/generic
+UNIX_DIR = @srcdir@
+BMAP_DIR = $(TOP_DIR)/bitmaps
+TOOL_DIR = @TCL_SRC_DIR@/tools
+
+#----------------------------------------------------------------
+# The information below should be usable as is. The configure
+# script won't modify it and you shouldn't need to modify it
+# either.
+#----------------------------------------------------------------
+
+
+CC = @CC@
+CC_SWITCHES = ${CFLAGS} ${TK_SHLIB_CFLAGS} -I${UNIX_DIR} -I${GENERIC_DIR} \
+-I${BMAP_DIR} -I${TCL_GENERIC_DIR} ${X11_INCLUDES} ${AC_FLAGS} ${PROTO_FLAGS} \
+${SECURITY_FLAGS} ${MEM_DEBUG_FLAGS} ${KEYSYM_FLAGS}
+
+DEPEND_SWITCHES = ${CFLAGS} -I${UNIX_DIR} -I${GENERIC_DIR} \
+-I${BMAP_DIR} \
+-I${TCL_GENERIC_DIR} ${X11_INCLUDES} \
+${AC_FLAGS} ${PROTO_FLAGS} ${SECURITY_FLAGS} ${MEM_DEBUG_FLAGS} \
+${KEYSYM_FLAGS}
+
+WISH_OBJS = tkAppInit.o
+
+TKTEST_OBJS = tkTestInit.o tkTest.o tkSquare.o
+
+WIDGOBJS = tkButton.o tkEntry.o tkFrame.o tkListbox.o \
+ tkMenu.o tkMenubutton.o tkMenuDraw.o tkMessage.o tkScale.o \
+ tkScrollbar.o
+
+CANVOBJS = tkCanvas.o tkCanvArc.o tkCanvBmap.o tkCanvImg.o \
+ tkCanvLine.o tkCanvPoly.o tkCanvPs.o tkCanvText.o \
+ tkCanvUtil.o tkCanvWind.o tkRectOval.o tkTrig.o
+
+IMAGEOBJS = tkImage.o tkImgBmap.o tkImgGIF.o tkImgPPM.o tkImgPhoto.o
+
+TEXTOBJS = tkText.o tkTextBTree.o tkTextDisp.o tkTextImage.o tkTextIndex.o \
+ tkTextMark.o tkTextTag.o tkTextWind.o
+
+UNIXOBJS = tkUnix.o tkUnix3d.o tkUnixButton.o tkUnixColor.o tkUnixCursor.o \
+ tkUnixDialog.o tkUnixDraw.o \
+ tkUnixEmbed.o tkUnixEvent.o tkUnixFocus.o tkUnixFont.o tkUnixInit.o \
+ tkUnixMenu.o tkUnixMenubu.o tkUnixScale.o tkUnixScrlbr.o \
+ tkUnixSelect.o tkUnixSend.o tkUnixWm.o tkUnixXId.o
+
+OBJS = tk3d.o tkArgv.o tkAtom.o tkBind.o tkBitmap.o tkClipboard.o tkCmds.o \
+ tkColor.o tkConfig.o tkCursor.o tkError.o tkEvent.o \
+ tkFocus.o tkFont.o tkGet.o tkGC.o tkGeometry.o tkGrab.o tkGrid.o \
+ tkMain.o tkOption.o tkPack.o tkPlace.o \
+ tkSelect.o tkUtil.o tkVisual.o tkWindow.o \
+ $(UNIXOBJS) $(WIDGOBJS) $(CANVOBJS) $(IMAGEOBJS) $(TEXTOBJS)
+
+SRCS = \
+ $(GENERIC_DIR)/tk3d.c $(GENERIC_DIR)/tkArgv.c \
+ $(GENERIC_DIR)/tkAtom.c $(GENERIC_DIR)/tkBind.c \
+ $(GENERIC_DIR)/tkBitmap.c $(GENERIC_DIR)/tkClipboard.c \
+ $(GENERIC_DIR)/tkCmds.c $(GENERIC_DIR)/tkColor.c \
+ $(GENERIC_DIR)/tkConfig.c $(GENERIC_DIR)/tkCursor.c \
+ $(GENERIC_DIR)/tkError.c $(GENERIC_DIR)/tkEvent.c \
+ $(GENERIC_DIR)/tkFocus.c $(GENERIC_DIR)/tkFont.c \
+ $(GENERIC_DIR)/tkGet.c $(GENERIC_DIR)/tkGC.c \
+ $(GENERIC_DIR)/tkGeometry.c $(GENERIC_DIR)/tkGrab.c \
+ $(GENERIC_DIR)/tkGrid.c \
+ $(GENERIC_DIR)/tkMain.c $(GENERIC_DIR)/tkOption.c \
+ $(GENERIC_DIR)/tkPack.c $(GENERIC_DIR)/tkPlace.c \
+ $(GENERIC_DIR)/tkSelect.c $(GENERIC_DIR)/tkUtil.c \
+ $(GENERIC_DIR)/tkVisual.c $(GENERIC_DIR)/tkWindow.c \
+ $(GENERIC_DIR)/tkButton.c \
+ $(GENERIC_DIR)/tkEntry.c $(GENERIC_DIR)/tkFrame.c \
+ $(GENERIC_DIR)/tkListbox.c $(GENERIC_DIR)/tkMenu.c \
+ $(GENERIC_DIR)/tkMenubutton.c $(GENERIC_DIR)/tkMenuDraw.c \
+ $(GENERIC_DIR)/tkMessage.c \
+ $(GENERIC_DIR)/tkScale.c $(GENERIC_DIR)/tkScrollbar.c \
+ $(GENERIC_DIR)/tkCanvas.c $(GENERIC_DIR)/tkCanvArc.c \
+ $(GENERIC_DIR)/tkCanvBmap.c $(GENERIC_DIR)/tkCanvImg.c \
+ $(GENERIC_DIR)/tkCanvLine.c $(GENERIC_DIR)/tkCanvPoly.c \
+ $(GENERIC_DIR)/tkCanvPs.c $(GENERIC_DIR)/tkCanvText.c \
+ $(GENERIC_DIR)/tkCanvUtil.c \
+ $(GENERIC_DIR)/tkCanvWind.c $(GENERIC_DIR)/tkRectOval.c \
+ $(GENERIC_DIR)/tkTrig.c $(GENERIC_DIR)/tkImage.c \
+ $(GENERIC_DIR)/tkImgBmap.c $(GENERIC_DIR)/tkImgGIF.c \
+ $(GENERIC_DIR)/tkImgPPM.c \
+ $(GENERIC_DIR)/tkImgPhoto.c $(GENERIC_DIR)/tkText.c \
+ $(GENERIC_DIR)/tkTextBTree.c $(GENERIC_DIR)/tkTextDisp.c \
+ $(GENERIC_DIR)/tkTextImage.c \
+ $(GENERIC_DIR)/tkTextIndex.c $(GENERIC_DIR)/tkTextMark.c \
+ $(GENERIC_DIR)/tkTextTag.c $(GENERIC_DIR)/tkTextWind.c \
+ $(GENERIC_DIR)/tkSquare.c $(GENERIC_DIR)/tkTest.c \
+ $(UNIX_DIR)/tkAppInit.c $(UNIX_DIR)/tkUnix.c \
+ $(UNIX_DIR)/tkUnix3d.c \
+ $(UNIX_DIR)/tkUnixButton.c $(UNIX_DIR)/tkUnixColor.c \
+ $(UNIX_DIR)/tkUnixCursor.c \
+ $(UNIX_DIR)/tkUnixDialog.c $(UNIX_DIR)/tkUnixDraw.c \
+ $(UNIX_DIR)/tkUnixEmbed.c $(UNIX_DIR)/tkUnixEvent.c \
+ $(UNIX_DIR)/tkUnixFocus.c \
+ $(UNIX_DIR)/tkUnixFont.c $(UNIX_DIR)/tkUnixInit.c \
+ $(UNIX_DIR)/tkUnixMenu.c $(UNIX_DIR)/tkUnixMenubu.c \
+ $(UNIX_DIR)/tkUnixScale.c $(UNIX_DIR)/tkUnixScrlbr.c \
+ $(UNIX_DIR)/tkUnixSelect.c \
+ $(UNIX_DIR)/tkUnixSend.c $(UNIX_DIR)/tkUnixWm.c \
+ $(UNIX_DIR)/tkUnixXId.c
+
+
+HDRS = bltList.h \
+ default.h ks_names.h tkPatch.h tk.h tkButton.h tkCanvas.h tkInt.h \
+ tkPort.h tkScrollbar.h tkText.h
+
+DEMOPROGS = browse hello ixset rmt rolodex square tcolor timer widget
+
+all: wish
+
+# The following target is configured by autoconf to generate either
+# a shared library or non-shared library for Tk.
+@TK_LIB_FILE@: ${OBJS}
+ rm -f @TK_LIB_FILE@
+ @MAKE_LIB@
+ $(RANLIB) @TK_LIB_FILE@
+
+# Make target which outputs the list of the .o contained in the Tk lib
+# usefull to build a single big shared library containing Tcl/Tk and other
+# extensions. used for the Tcl Plugin. -- dl
+tkLibObjs:
+ @echo ${OBJS}
+# This targets actually build the objects needed for the lib in the above
+# case
+objs: ${OBJS}
+
+
+wish: $(WISH_OBJS) $(TK_LIB_FILE)
+ $(CC) @LD_FLAGS@ $(WISH_OBJS) @TK_BUILD_LIB_SPEC@ $(LIBS) \
+ $(TK_CC_SEARCH_FLAGS) -o wish
+
+tktest: $(TKTEST_OBJS) $(TK_LIB_FILE)
+ ${CC} @LD_FLAGS@ $(TKTEST_OBJS) @TK_BUILD_LIB_SPEC@ $(LIBS) \
+ $(TK_CC_SEARCH_FLAGS) -o tktest
+
+xttest: test.o tkTest.o tkSquare.o $(TK_LIB_FILE)
+ ${CC} @LD_FLAGS@ test.o tkTest.o tkSquare.o \
+ @TK_BUILD_LIB_SPEC@ $(LIBS) \
+ @TK_LD_SEARCH_FLAGS@ -lXt -o xttest
+
+# Note, in the target below TCL_LIBRARY needs to be set or else
+# "make test" won't work in the case where the compilation directory
+# isn't the same as the source directory.
+
+test: tktest
+ LD_LIBRARY_PATH=`pwd`:${TCL_BIN_DIR}:${LD_LIBRARY_PATH}; \
+ export LD_LIBRARY_PATH; \
+ TCL_LIBRARY=@TCL_SRC_DIR@/library; export TCL_LIBRARY; \
+ TK_LIBRARY=$(TOP_DIR)/library; export TK_LIBRARY; \
+ ( echo cd $(TOP_DIR)/tests\; source all\; exit ) \
+ | ./tktest -geometry +0+0
+
+
+# Useful target to launch a built tktest with the proper path,...
+runtest:
+ LD_LIBRARY_PATH=`pwd`:${TCL_BIN_DIR}:${LD_LIBRARY_PATH}; \
+ export LD_LIBRARY_PATH; \
+ TCL_LIBRARY=@TCL_SRC_DIR@/library; export TCL_LIBRARY; \
+ TK_LIBRARY=$(TOP_DIR)/library; export TK_LIBRARY; \
+ ./tktest
+
+install: install-binaries install-libraries install-demos install-man
+
+# Note: before running ranlib below, must cd to target directory because
+# some ranlibs write to current directory, and this might not always be
+# possible (e.g. if installing as root).
+
+install-binaries: $(TK_LIB_FILE) wish
+ @for i in $(LIB_INSTALL_DIR) $(BIN_INSTALL_DIR) ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @echo "Installing $(TK_LIB_FILE)"
+ @$(INSTALL_DATA) $(TK_LIB_FILE) $(LIB_INSTALL_DIR)/$(TK_LIB_FILE)
+ @(cd $(LIB_INSTALL_DIR); $(RANLIB) $(TK_LIB_FILE))
+ @chmod 555 $(LIB_INSTALL_DIR)/$(TK_LIB_FILE)
+ @echo "Installing wish"
+ @$(INSTALL_PROGRAM) wish $(BIN_INSTALL_DIR)/wish$(VERSION)
+ @echo "Installing tkConfig.sh"
+ @$(INSTALL_DATA) tkConfig.sh $(LIB_INSTALL_DIR)/tkConfig.sh
+
+install-libraries:
+ @for i in $(INSTALL_ROOT)$(prefix)/lib $(INCLUDE_INSTALL_DIR) \
+ $(SCRIPT_INSTALL_DIR) $(SCRIPT_INSTALL_DIR)/images; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @echo "Installing tk.h"
+ @$(INSTALL_DATA) $(GENERIC_DIR)/tk.h $(INCLUDE_INSTALL_DIR)/tk.h
+ for i in $(SRC_DIR)/library/*.tcl $(SRC_DIR)/library/tclIndex $(SRC_DIR)/library/prolog.ps $(UNIX_DIR)/tkAppInit.c; \
+ do \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR); \
+ done;
+ for i in $(SRC_DIR)/library/images/*; \
+ do \
+ if [ -f $$i ] ; then \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/images; \
+ 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;
+ @for i in $(SRC_DIR)/library/demos/*; \
+ do \
+ if [ -f $$i ] ; then \
+ echo "Installing $$i"; \
+ sed -e '3 s|exec wish|exec wish$(VERSION)|' \
+ $$i > $(SCRIPT_INSTALL_DIR)/demos/`basename $$i`; \
+ fi; \
+ done;
+ @for i in $(DEMOPROGS); \
+ do \
+ chmod 755 $(SCRIPT_INSTALL_DIR)/demos/$$i; \
+ done;
+ @for i in $(SRC_DIR)/library/demos/images/*; \
+ do \
+ if [ -f $$i ] ; then \
+ echo "Installing $$i"; \
+ $(INSTALL_DATA) $$i $(SCRIPT_INSTALL_DIR)/demos/images; \
+ fi; \
+ done;
+
+install-man:
+ @for i in $(MAN_INSTALL_DIR) $(MAN1_INSTALL_DIR) $(MAN3_INSTALL_DIR) $(MANN_INSTALL_DIR) ; \
+ do \
+ if [ ! -d $$i ] ; then \
+ echo "Making directory $$i"; \
+ mkdir $$i; \
+ chmod 755 $$i; \
+ else true; \
+ fi; \
+ done;
+ @cd $(SRC_DIR)/doc; for i in *.1; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MAN1_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MAN1_INSTALL_DIR)/$$i; \
+ chmod 444 $(MAN1_INSTALL_DIR)/$$i; \
+ done;
+ $(UNIX_DIR)/mkLinks $(MAN1_INSTALL_DIR)
+ @cd $(SRC_DIR)/doc; for i in *.3; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MAN3_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MAN3_INSTALL_DIR)/$$i; \
+ chmod 444 $(MAN3_INSTALL_DIR)/$$i; \
+ done;
+ $(UNIX_DIR)/mkLinks $(MAN3_INSTALL_DIR)
+ @cd $(SRC_DIR)/doc; for i in *.n; \
+ do \
+ echo "Installing doc/$$i"; \
+ rm -f $(MANN_INSTALL_DIR)/$$i; \
+ sed -e '/man\.macros/r man.macros' -e '/man\.macros/d' \
+ $$i > $(MANN_INSTALL_DIR)/$$i; \
+ chmod 444 $(MANN_INSTALL_DIR)/$$i; \
+ done;
+ $(UNIX_DIR)/mkLinks $(MANN_INSTALL_DIR)
+
+Makefile: $(UNIX_DIR)/Makefile.in
+ $(SHELL) config.status
+
+clean:
+ rm -f *.a *.o libtk* core errs *~ \#* TAGS *.E a.out errors \
+ tktest wish config.info lib.exp
+
+distclean: clean
+ rm -f Makefile config.status config.cache config.log tkConfig.sh \
+ SUNWtk.* prototype
+
+depend:
+ makedepend -- $(DEPEND_SWITCHES) -- $(SRCS)
+
+# Test binaries. The rule for tkTestInit.o is complicated because
+# it is is compiled from tkAppInit.c. Can't use the "-o" option
+# because this doesn't work on some strange compilers (e.g. UnixWare).
+
+tkTestInit.o: $(UNIX_DIR)/tkAppInit.c
+ @if test -f tkAppInit.o ; then \
+ rm -f tkAppInit.sav; \
+ mv tkAppInit.o tkAppInit.sav; \
+ fi;
+ $(CC) -c $(CC_SWITCHES) -DTK_TEST $(UNIX_DIR)/tkAppInit.c
+ rm -f tkTestInit.o
+ mv tkAppInit.o tkTestInit.o
+ @if test -f tkAppInit.sav ; then \
+ mv tkAppInit.sav tkAppInit.o; \
+ fi;
+
+tk3d.o: $(GENERIC_DIR)/tk3d.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tk3d.c
+
+tkAppInit.o: $(UNIX_DIR)/tkAppInit.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkAppInit.c
+
+tkArgv.o: $(GENERIC_DIR)/tkArgv.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkArgv.c
+
+tkAtom.o: $(GENERIC_DIR)/tkAtom.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkAtom.c
+
+tkBind.o: $(GENERIC_DIR)/tkBind.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkBind.c
+
+tkBitmap.o: $(GENERIC_DIR)/tkBitmap.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkBitmap.c
+
+tkClipboard.o: $(GENERIC_DIR)/tkClipboard.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkClipboard.c
+
+tkCmds.o: $(GENERIC_DIR)/tkCmds.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCmds.c
+
+tkColor.o: $(GENERIC_DIR)/tkColor.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkColor.c
+
+tkConfig.o: $(GENERIC_DIR)/tkConfig.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkConfig.c
+
+tkCursor.o: $(GENERIC_DIR)/tkCursor.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCursor.c
+
+tkError.o: $(GENERIC_DIR)/tkError.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkError.c
+
+tkEvent.o: $(GENERIC_DIR)/tkEvent.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkEvent.c
+
+tkFocus.o: $(GENERIC_DIR)/tkFocus.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkFocus.c
+
+tkFont.o: $(GENERIC_DIR)/tkFont.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkFont.c
+
+tkGet.o: $(GENERIC_DIR)/tkGet.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkGet.c
+
+tkGC.o: $(GENERIC_DIR)/tkGC.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkGC.c
+
+tkGeometry.o: $(GENERIC_DIR)/tkGeometry.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkGeometry.c
+
+tkGrab.o: $(GENERIC_DIR)/tkGrab.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkGrab.c
+
+tkGrid.o: $(GENERIC_DIR)/tkGrid.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkGrid.c
+
+tkMain.o: $(GENERIC_DIR)/tkMain.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkMain.c
+
+tkOption.o: $(GENERIC_DIR)/tkOption.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkOption.c
+
+tkPack.o: $(GENERIC_DIR)/tkPack.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkPack.c
+
+tkPlace.o: $(GENERIC_DIR)/tkPlace.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkPlace.c
+
+tkSelect.o: $(GENERIC_DIR)/tkSelect.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkSelect.c
+
+tkUtil.o: $(GENERIC_DIR)/tkUtil.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkUtil.c
+
+tkVisual.o: $(GENERIC_DIR)/tkVisual.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkVisual.c
+
+tkWindow.o: $(GENERIC_DIR)/tkWindow.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkWindow.c
+
+tkButton.o: $(GENERIC_DIR)/tkButton.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkButton.c
+
+tkEntry.o: $(GENERIC_DIR)/tkEntry.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkEntry.c
+
+tkFrame.o: $(GENERIC_DIR)/tkFrame.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkFrame.c
+
+tkListbox.o: $(GENERIC_DIR)/tkListbox.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkListbox.c
+
+tkMenu.o: $(GENERIC_DIR)/tkMenu.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkMenu.c
+
+tkMenubutton.o: $(GENERIC_DIR)/tkMenubutton.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkMenubutton.c
+
+tkMenuDraw.o: $(GENERIC_DIR)/tkMenuDraw.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkMenuDraw.c
+
+tkMessage.o: $(GENERIC_DIR)/tkMessage.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkMessage.c
+
+tkScale.o: $(GENERIC_DIR)/tkScale.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkScale.c
+
+tkScrollbar.o: $(GENERIC_DIR)/tkScrollbar.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkScrollbar.c
+
+tkSquare.o: $(GENERIC_DIR)/tkSquare.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkSquare.c
+
+tkCanvas.o: $(GENERIC_DIR)/tkCanvas.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvas.c
+
+tkCanvArc.o: $(GENERIC_DIR)/tkCanvArc.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvArc.c
+
+tkCanvBmap.o: $(GENERIC_DIR)/tkCanvBmap.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvBmap.c
+
+tkCanvImg.o: $(GENERIC_DIR)/tkCanvImg.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvImg.c
+
+tkCanvLine.o: $(GENERIC_DIR)/tkCanvLine.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvLine.c
+
+tkCanvPoly.o: $(GENERIC_DIR)/tkCanvPoly.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvPoly.c
+
+tkCanvPs.o: $(GENERIC_DIR)/tkCanvPs.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvPs.c
+
+tkCanvText.o: $(GENERIC_DIR)/tkCanvText.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvText.c
+
+tkCanvUtil.o: $(GENERIC_DIR)/tkCanvUtil.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvUtil.c
+
+tkCanvWind.o: $(GENERIC_DIR)/tkCanvWind.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkCanvWind.c
+
+tkRectOval.o: $(GENERIC_DIR)/tkRectOval.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkRectOval.c
+
+tkTrig.o: $(GENERIC_DIR)/tkTrig.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTrig.c
+
+tkImage.o: $(GENERIC_DIR)/tkImage.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkImage.c
+
+tkImgBmap.o: $(GENERIC_DIR)/tkImgBmap.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkImgBmap.c
+
+tkImgGIF.o: $(GENERIC_DIR)/tkImgGIF.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkImgGIF.c
+
+tkImgPPM.o: $(GENERIC_DIR)/tkImgPPM.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkImgPPM.c
+
+tkImgPhoto.o: $(GENERIC_DIR)/tkImgPhoto.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkImgPhoto.c
+
+tkTest.o: $(GENERIC_DIR)/tkTest.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTest.c
+
+tkText.o: $(GENERIC_DIR)/tkText.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkText.c
+
+tkTextBTree.o: $(GENERIC_DIR)/tkTextBTree.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextBTree.c
+
+tkTextDisp.o: $(GENERIC_DIR)/tkTextDisp.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextDisp.c
+
+tkTextImage.o: $(GENERIC_DIR)/tkTextImage.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextImage.c
+
+tkTextIndex.o: $(GENERIC_DIR)/tkTextIndex.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextIndex.c
+
+tkTextMark.o: $(GENERIC_DIR)/tkTextMark.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextMark.c
+
+tkTextTag.o: $(GENERIC_DIR)/tkTextTag.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextTag.c
+
+tkTextWind.o: $(GENERIC_DIR)/tkTextWind.c
+ $(CC) -c $(CC_SWITCHES) $(GENERIC_DIR)/tkTextWind.c
+
+tkUnix.o: $(UNIX_DIR)/tkUnix.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnix.c
+
+tkUnix3d.o: $(UNIX_DIR)/tkUnix3d.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnix3d.c
+
+tkUnixButton.o: $(UNIX_DIR)/tkUnixButton.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixButton.c
+
+tkUnixColor.o: $(UNIX_DIR)/tkUnixColor.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixColor.c
+
+tkUnixCursor.o: $(UNIX_DIR)/tkUnixCursor.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixCursor.c
+
+tkUnixDialog.o: $(UNIX_DIR)/tkUnixDialog.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixDialog.c
+
+tkUnixDraw.o: $(UNIX_DIR)/tkUnixDraw.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixDraw.c
+
+tkUnixEmbed.o: $(UNIX_DIR)/tkUnixEmbed.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixEmbed.c
+
+tkUnixEvent.o: $(UNIX_DIR)/tkUnixEvent.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixEvent.c
+
+tkUnixFocus.o: $(UNIX_DIR)/tkUnixFocus.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixFocus.c
+
+tkUnixFont.o: $(UNIX_DIR)/tkUnixFont.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixFont.c
+
+tkUnixInit.o: $(UNIX_DIR)/tkUnixInit.c $(GENERIC_DIR)/tkInitScript.h tkConfig.sh
+ $(CC) -c $(CC_SWITCHES) -DTK_LIBRARY=\"${TK_LIBRARY}\" \
+ $(UNIX_DIR)/tkUnixInit.c
+
+tkUnixMenu.o: $(UNIX_DIR)/tkUnixMenu.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixMenu.c
+
+tkUnixMenubu.o: $(UNIX_DIR)/tkUnixMenubu.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixMenubu.c
+
+tkUnixScale.o: $(UNIX_DIR)/tkUnixScale.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixScale.c
+
+tkUnixScrlbr.o: $(UNIX_DIR)/tkUnixScrlbr.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixScrlbr.c
+
+tkUnixSelect.o: $(UNIX_DIR)/tkUnixSelect.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixSelect.c
+
+tkUnixSend.o: $(UNIX_DIR)/tkUnixSend.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixSend.c
+
+tkUnixWm.o: $(UNIX_DIR)/tkUnixWm.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixWm.c
+
+tkUnixXId.o: $(UNIX_DIR)/tkUnixXId.c
+ $(CC) -c $(CC_SWITCHES) $(UNIX_DIR)/tkUnixXId.c
+
+.c.o:
+ $(CC) -c $(CC_SWITCHES) $<
+
+#
+# Target to check for proper usage of UCHAR macro.
+#
+
+checkuchar:
+ -egrep isalnum\|isalpha\|iscntrl\|isdigit\|islower\|isprint\|ispunct\|isspace\|isupper\|isxdigit\|toupper\|tolower $(SRCS) | grep -v UCHAR
+
+#
+# Target to make sure that only symbols with "Tk" prefixes are
+# exported.
+#
+
+checkexports: $(TK_LIB_FILE)
+ -nm -p $(TK_LIB_FILE) | awk '$$2 ~ /[TDB]/ { print $$3 }' | sort -n | grep -v '^[Tt]k'
+
+#
+# Target to create a proper Tk distribution from information in the
+# master source directory. DISTDIR must be defined to indicate where
+# to put the distribution. DISTDIR must be an absolute path name.
+#
+
+DISTNAME = tk@TK_VERSION@@TK_PATCH_LEVEL@
+ZIPNAME = tk@TK_MAJOR_VERSION@@TK_MINOR_VERSION@@TK_PATCH_LEVEL@.zip
+DISTDIR = /proj/tcl/dist/$(DISTNAME)
+TCLDIR = @TCL_SRC_DIR@
+$(UNIX_DIR)/configure: $(UNIX_DIR)/configure.in
+ autoconf $(UNIX_DIR)/configure.in > $(UNIX_DIR)/configure
+
+dist: $(UNIX_DIR)/configure
+ rm -rf $(DISTDIR)
+ mkdir $(DISTDIR)
+ mkdir $(DISTDIR)/unix
+ cp -p $(UNIX_DIR)/*.c $(UNIX_DIR)/*.h $(DISTDIR)/unix
+ cp $(TOP_DIR)/license.terms $(UNIX_DIR)/Makefile.in $(DISTDIR)/unix
+ chmod 664 $(DISTDIR)/unix/Makefile.in
+ cp $(UNIX_DIR)/configure $(UNIX_DIR)/configure.in \
+ $(UNIX_DIR)/tkConfig.sh.in $(TCLDIR)/unix/install-sh \
+ $(UNIX_DIR)/porting.notes $(UNIX_DIR)/porting.old \
+ $(UNIX_DIR)/README $(DISTDIR)/unix
+ chmod 775 $(DISTDIR)/unix/configure $(DISTDIR)/unix/configure.in
+ chmod +x $(DISTDIR)/unix/install-sh
+ tclsh $(TCLDIR)/unix/mkLinks.tcl $(TOP_DIR)/doc/*.[13n] \
+ > $(DISTDIR)/unix/mkLinks
+ chmod +x $(DISTDIR)/unix/mkLinks
+ mkdir $(DISTDIR)/bitmaps
+ @(cd $(TOP_DIR); for i in bitmaps/* ; do \
+ if [ -f $$i ] ; then \
+ sed -e 's/static char/static unsigned char/' \
+ $$i > $(DISTDIR)/$$i; \
+ fi; \
+ done;)
+ mkdir $(DISTDIR)/generic
+ cp -p $(GENERIC_DIR)/*.c $(GENERIC_DIR)/*.h $(DISTDIR)/generic
+ cp -p $(GENERIC_DIR)/README $(DISTDIR)/generic
+ cp -p $(TOP_DIR)/changes $(TOP_DIR)/README $(TOP_DIR)/license.terms \
+ $(DISTDIR)
+ rm -f $(DISTDIR)/generic/blt*.[ch]
+ mkdir $(DISTDIR)/win
+ cp -p $(TOP_DIR)/win/*.c $(TOP_DIR)/win/*.h $(DISTDIR)/win
+ cp -p $(TOP_DIR)/win/makefile.* $(DISTDIR)/win
+ cp -p $(TOP_DIR)/win/README $(DISTDIR)/win
+ cp -p $(TOP_DIR)/license.terms $(DISTDIR)/win
+ mkdir $(DISTDIR)/win/rc
+ cp -p $(TOP_DIR)/win/rc/*.rc $(TOP_DIR)/win/rc/*.cur \
+ $(TOP_DIR)/win/rc/*.ico $(TOP_DIR)/win/rc/*.bmp \
+ $(DISTDIR)/win/rc
+ mkdir $(DISTDIR)/mac
+ sccs edit -s $(TOP_DIR)/mac/tkMacProjects.sit.hqx
+ cp -p tkMacProjects.sit.hqx $(DISTDIR)/mac
+ sccs unedit $(TOP_DIR)/mac/tkMacProjects.sit.hqx
+ rm -f tkMacProjects.sit.hqx
+ cp -p $(TOP_DIR)/mac/*.c $(TOP_DIR)/mac/*.h $(TOP_DIR)/mac/*.r \
+ $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/mac/README $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/license.terms $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/mac/*.pch $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/mac/*.doc $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/mac/*.exp $(DISTDIR)/mac
+ cp -p $(TOP_DIR)/mac/*.tcl $(DISTDIR)/mac
+ mkdir $(DISTDIR)/compat
+ cp -p $(TOP_DIR)/license.terms $(TCLDIR)/compat/unistd.h \
+ $(TCLDIR)/compat/stdlib.h $(TCLDIR)/compat/limits.h \
+ $(DISTDIR)/compat
+ mkdir $(DISTDIR)/xlib
+ cp -p $(TOP_DIR)/xlib/*.h $(TOP_DIR)/xlib/*.c $(DISTDIR)/xlib
+ cp -p $(TOP_DIR)/license.terms $(DISTDIR)/xlib
+ mkdir $(DISTDIR)/xlib/X11
+ cp -p $(TOP_DIR)/xlib/X11/*.h $(DISTDIR)/xlib/X11
+ cp -p $(TOP_DIR)/license.terms $(DISTDIR)/xlib/X11
+ mkdir $(DISTDIR)/library
+ cp -p $(TOP_DIR)/license.terms $(TOP_DIR)/library/*.tcl \
+ $(TOP_DIR)/library/tclIndex $(TOP_DIR)/library/prolog.ps \
+ $(DISTDIR)/library
+ mkdir $(DISTDIR)/library/images
+ @(cd $(TOP_DIR); for i in library/images/* ; do \
+ if [ -f $$i ] ; then \
+ cp $$i $(DISTDIR)/$$i; \
+ fi; \
+ done;)
+ mkdir $(DISTDIR)/library/demos
+ cp -pr $(TOP_DIR)/library/demos/*.tcl \
+ $(TOP_DIR)/library/demos/tclIndex \
+ $(TOP_DIR)/library/demos/browse \
+ $(TOP_DIR)/library/demos/hello $(TOP_DIR)/library/demos/ixset \
+ $(TOP_DIR)/library/demos/rmt $(TOP_DIR)/library/demos/rolodex \
+ $(TOP_DIR)/library/demos/square \
+ $(TOP_DIR)/library/demos/tcolor \
+ $(TOP_DIR)/library/demos/timer \
+ $(TOP_DIR)/library/demos/widget \
+ $(TOP_DIR)/library/demos/README \
+ $(TOP_DIR)/license.terms $(DISTDIR)/library/demos
+ mkdir $(DISTDIR)/library/demos/images
+ @(cd $(TOP_DIR); for i in library/demos/images/* ; do \
+ if [ -f $$i ] ; then \
+ cp $$i $(DISTDIR)/$$i; \
+ fi; \
+ done;)
+ mkdir $(DISTDIR)/doc
+ cp -p $(TOP_DIR)/license.terms $(TOP_DIR)/doc/*.[13n] \
+ $(TCLDIR)/doc/man.macros $(DISTDIR)/doc
+ cp /home/ouster/papers/tk4.0/tk4.0.ps $(DISTDIR)/doc
+ mkdir $(DISTDIR)/tests
+ cp -p $(TOP_DIR)/license.terms $(TOP_DIR)/tests/*.test \
+ $(TOP_DIR)/tests/visual $(TOP_DIR)/tests/*.tcl \
+ $(TOP_DIR)/tests/README $(TOP_DIR)/tests/all \
+ $(TOP_DIR)/tests/defs $(TOP_DIR)/tests/option.file* \
+ $(DISTDIR)/tests
+
+#
+# The following target can only be used for non-patch releases. Use
+# the "allpatch" target below for patch releases.
+#
+
+alldist: dist
+ rm -f /proj/tcl/dist/$(DISTNAME).tar.Z \
+ /proj/tcl/dist/$(DISTNAME).tar.gz \
+ /proj/tcl/dist/$(ZIPNAME)
+ cd /proj/tcl/dist; tar cf $(DISTNAME).tar $(DISTNAME); \
+ gzip -9 -c $(DISTNAME).tar > $(DISTNAME).tar.gz; \
+ compress $(DISTNAME).tar; zip -r8 $(ZIPNAME) $(DISTNAME)
+
+#
+# The target below is similar to "alldist" except it works for patch
+# releases. It is needed because patch releases are peculiar: the
+# patch designation appears in the name of the compressed file
+# (e.g. tcl8.0p1.tar.gz) but the extracted source directory doesn't
+# include the patch designation (e.g. tcl8.0).
+#
+
+allpatch: dist
+ rm -f /proj/tcl/dist/$(DISTNAME).tar.Z \
+ /proj/tcl/dist/$(DISTNAME).tar.gz \
+ /proj/tcl/dist/$(ZIPNAME)
+ mv /proj/tcl/dist/tk${VERSION} /proj/tcl/dist/old
+ mv /proj/tcl/dist/$(DISTNAME) /proj/tcl/dist/tk${VERSION}
+ cd /proj/tcl/dist; tar cf $(DISTNAME).tar tk${VERSION}; \
+ gzip -9 -c $(DISTNAME).tar > $(DISTNAME).tar.gz; \
+ compress $(DISTNAME).tar; zip -r8 $(ZIPNAME) tk${VERSION}
+ mv /proj/tcl/dist/tk${VERSION} /proj/tcl/dist/$(DISTNAME)
+ mv /proj/tcl/dist/old /proj/tcl/dist/tk${VERSION}
+
+#
+# Target to create a Macintosh version of the distribution. This will
+# do a normal distribution and then massage the output to prepare it
+# for moving to the Mac platform. This requires a few scripts and
+# programs found only in the Tcl greoup's tool workspace.
+#
+
+macdist: dist
+ rm -f $(DISTDIR)/mac/tkMacProjects.sit.hqx
+ tclsh $(TOOL_DIR)/man2html.tcl $(DISTDIR)/tmp ../.. tk$(VERSION)
+ mv $(DISTDIR)/tmp/tk$(VERSION) $(DISTDIR)/html
+ rm -rf $(DISTDIR)/doc
+ rm -rf $(DISTDIR)/tmp
+ tclsh $(TOOL_DIR)/cvtEOL.tcl $(DISTDIR)
+
+#
+# Targets to build Solaris package of the distribution for the current
+# architecture. To build stream packages for both sun4 and i86pc
+# architectures:
+#
+# On the sun4 machine, execute the following:
+# make distclean; ./configure
+# make DISTDIR=<distdir> package
+#
+# Once the build is complete, execute the following on the i86pc
+# machine:
+# make DISTDIR=<distdir> package-quick
+#
+# <distdir> is the absolute path to a directory where the build should
+# take place. These steps will generate the SUNWtk.sun4 and
+# SUNWtk.i86pc stream packages. It is important that the packages be
+# built in this fashion in order to ensure that the architecture
+# independent files are exactly the same, including timestamps, in
+# both packages.
+#
+
+package: dist package-config package-common package-binaries package-generate
+package-quick: package-config package-binaries package-generate
+
+#
+# Configure for the current architecture in the dist directory.
+#
+package-config:
+ mkdir -p $(DISTDIR)/unix/`arch`
+ cd $(DISTDIR)/unix/`arch`; \
+ ../configure --prefix=/opt/SUNWtcl/$(TCLVERSION) \
+ --exec_prefix=/opt/SUNWtcl/$(TCLVERSION)/`arch` \
+ --with-tcl=$(DISTDIR)/../tcl$(TCLVERSION)/unix/`arch` \
+ --enable-shared
+ mkdir -p $(DISTDIR)/SUNWtcl/$(TCLVERSION)
+ mkdir -p $(DISTDIR)/SUNWtcl/$(TCLVERSION)/`arch`
+
+#
+# Build and install the architecture independent files in the dist directory.
+#
+
+package-common:
+ cd $(DISTDIR)/unix/`arch`;\
+ $(MAKE); \
+ $(MAKE) install-libraries install-man \
+ prefix=$(DISTDIR)/SUNWtcl/$(TCLVERSION) \
+ exec_prefix=$(DISTDIR)/SUNWtcl/$(TCLVERSION)/`arch`
+ mkdir -p $(DISTDIR)/SUNWtcl/$(TCLVERSION)/bin
+ sed -e "s/TCLVERSION/$(TCLVERSION)/g" \
+ -e "s/TKVERSION/$(VERSION)/g" < $(UNIX_DIR)/wish.sh \
+ > $(DISTDIR)/SUNWtcl/$(TCLVERSION)/bin/wish$(VERSION)
+ chmod 755 $(DISTDIR)/SUNWtcl/$(TCLVERSION)/bin/wish$(VERSION)
+
+#
+# Build and install the architecture specific files in the dist directory.
+#
+
+package-binaries:
+ cd $(DISTDIR)/unix/`arch`; \
+ $(MAKE); \
+ $(MAKE) install-binaries prefix=$(DISTDIR)/SUNWtcl/$(TCLVERSION) \
+ exec_prefix=$(DISTDIR)/SUNWtcl/$(TCLVERSION)/`arch`
+
+#
+# Generate a package from the installed files in the dist directory for the
+# current architecture.
+#
+
+package-generate:
+ pkgproto $(DISTDIR)/SUNWtcl/$(TCLVERSION)/bin=bin \
+ $(DISTDIR)/SUNWtcl/$(TCLVERSION)/include=include \
+ $(DISTDIR)/SUNWtcl/$(TCLVERSION)/lib=lib \
+ $(DISTDIR)/SUNWtcl/$(TCLVERSION)/man=man \
+ $(DISTDIR)/SUNWtcl/$(TCLVERSION)/`arch`=`arch` \
+ | tclsh $(TCLDIR)/unix/mkProto.tcl $(TCLVERSION) \
+ $(UNIX_DIR) > prototype
+ pkgmk -o -d . -f prototype -a `arch`
+ pkgtrans -s . SUNWtk.`arch` SUNWtk
+ rm -rf SUNWtk
+
+# DO NOT DELETE THIS LINE -- make depend depends on it.
diff --git a/unix/README b/unix/README
new file mode 100644
index 0000000..bb06d21
--- /dev/null
+++ b/unix/README
@@ -0,0 +1,125 @@
+This is the directory where you configure, compile, test, and install
+UNIX versions of Tk. This directory also contains source files for Tk
+that are specific to UNIX.
+
+The rest of this file contains instructions on how to do this. The
+release should compile and run either "out of the box" or with trivial
+changes on any UNIX-like system that approximates POSIX, BSD, or System
+V. We know that it runs on workstations from Sun, H-P, DEC, IBM, and
+SGI, as well as PCs running Linux, BSDI, and SCO UNIX. To compile for
+a PC running Windows, see the README file in the directory ../win. To
+compile for a Macintosh, see the README file in the directory ../mac.
+
+SCCS: @(#) README 1.24 97/08/13 17:31:19
+
+How To Compile And Install Tk:
+------------------------------
+
+(a) Make sure that the Tcl 8.0 release is present in the directory
+ ../../tcl8.0 (or else use the "--with-tcl" switch described below).
+ This release of Tk will only work with Tcl 8.0. Also, be sure that
+ you have configured Tcl before you configure Tk.
+
+(b) Check for patches as described in ../README.
+
+(c) If you have already compiled Tk once in this directory and are now
+ preparing to compile again in the same directory but for a different
+ platform, or if you have applied patches, type "make distclean" to
+ discard all the configuration information computed previously.
+
+(d) Type "./configure". This runs a configuration script created by GNU
+ autoconf, which configures Tcl for your system and creates a
+ Makefile. The configure script allows you to customize the Tcl
+ configuration for your site; for details on how you can do this,
+ type "./configure -help" or refer to the autoconf documentation (not
+ included here). Tk's "configure" script supports the following
+ special switches in addition to the standard ones:
+ --enable-gcc If this switch is set, Tk will configure
+ itself to use gcc if it is available on your
+ system. Note: it is not safe to modify the
+ Makefile to use gcc after configure is run.
+ --with-tcl=DIR Specifies the directory containing the Tcl
+ binaries and Tcl's platform-dependent
+ configuration information. By default
+ the Tcl directory is assumed to be in the
+ location given by (a) above.
+ --enable-shared If this switch is specified, Tk will compile
+ itself as a shared library if it can figure
+ out how to do that on this platform.
+ Note: be sure to use only absolute path names (those starting with "/")
+ in the --prefix and --exec_prefix options.
+
+(e) Type "make". This will create a library archive called "libtk.a"
+ or "libtk.so" and an interpreter application called "wish" that
+ allows you to type Tcl commands interactively or execute script files.
+
+(f) If the make fails then you'll have to personalize the Makefile
+ for your site or possibly modify the distribution in other ways.
+ First check the file "porting.notes" to see if there are hints
+ for compiling on your system. Then look at the porting Web page
+ described later in this file. If you need to modify Makefile,
+ there are comments at the beginning of it that describe the things
+ you might want to change and how to change them.
+
+(g) Type "make install" to install Tk's binaries and script files in
+ standard places. You'll need write permission on the installation
+ directoryies to do this. The installation directories are
+ determined by the "configure" script and may be specified with
+ the --prefix and --exec_prefix options to "configure". See the
+ Makefile for information on what directories were chosen; you
+ can override these choices by modifying the "prefix" and
+ "exec_prefix" variables in the Makefile.
+
+(h) At this point you can play with Tk by invoking the "wish"
+ program and typing Tcl commands. However, if you haven't installed
+ Tk then you'll first need to set your TK_LIBRARY environment
+ variable to hold the full path name of the "library" subdirectory.
+ If you haven't installed Tcl either then you'll need to set your
+ TCL_LIBRARY environment variable as well (see the Tcl README file
+ for information on this). Note that installed versions of wish,
+ libtk.a, libtk.so, and the Tk library have a version number in their
+ names, such as "wish8.0" or "libtk8.0.so"; to use the installed
+ versions, either specify the version number or create a symbolic
+ link (e.g. from "wish" to "wish8.0").
+
+If you have trouble compiling Tk, read through the file "porting.notes".
+It contains information that people have provided about changes they had
+to make to compile Tcl in various environments. Or, check out the
+following Web URL:
+ http://www.sunlabs.com/cgi-bin/tcl/info.8.0
+This is an on-line database of porting information. We make no guarantees
+that this information is accurate, complete, or up-to-date, but you may
+find it useful. If you get Tk running on a new configuration and had to
+make non-trivial changes to do it, we'd be happy to receive new information
+to add to "porting.notes". You can also make a new entry into the
+on-line Web database. We're also interested in hearing how to change the
+configuration setup so that Tcl compiles on additional platforms "out of
+the box".
+
+Test suite
+----------
+
+Tk has a substantial self-test suite, consisting of a set of scripts in
+the subdirectory "tests". To run the test suite just type "make test"
+in this directory. You should then see a printout of the test files
+processed. If any errors occur, you'll see a much more substantial
+printout for each error. In order to avoid false error reports, be sure
+to run the tests with an empty resource database (e.g., remove your
+.Xdefaults file or delete any entries starting with *). Also, don't
+try to do anything else with your display or keyboard whlie the tests
+are running, or you may get false violations. See the README file in
+the "tests" directory for more information on the test suite.
+
+If the test suite generates errors, most likely they are due to non-
+portable tests that are interacting badly with your system configuration.
+We are gradually eliminating the non-portable tests, but this release
+includes many new tests so there will probably be some portability
+problems. As long as the test suite doesn't core dump, it's probably
+safe to conclude that any errors represent portability problems in the
+test suite and not fundamental flaws with Tk.
+
+There are also a number of visual tests for things such as screen layout,
+Postscript generation, etc. These tests all have to be run manually and
+the results have to be verified visually. To run the tests, cd to the
+"tests" directory and run the script "visual". It will present a main
+window with a bunch of menus, which you can use to select various tests.
diff --git a/unix/configure.in b/unix/configure.in
new file mode 100644
index 0000000..7f3c15f
--- /dev/null
+++ b/unix/configure.in
@@ -0,0 +1,407 @@
+dnl This file is an input file used by the GNU "autoconf" program to
+dnl generate the file "configure", which is run during Tk installation
+dnl to configure the system for the local environment.
+AC_INIT(../generic/tk.h)
+# SCCS: @(#) configure.in 1.90 97/11/20 12:45:45
+
+TK_VERSION=8.0
+TK_MAJOR_VERSION=8
+TK_MINOR_VERSION=0
+TK_PATCH_LEVEL="p2"
+VERSION=${TK_VERSION}
+
+if test "${prefix}" = "NONE"; then
+ prefix=/usr/local
+fi
+if test "${exec_prefix}" = "NONE"; then
+ exec_prefix=$prefix
+fi
+TK_SRC_DIR=`cd $srcdir/..; pwd`
+
+AC_PROG_RANLIB
+AC_ARG_ENABLE(gcc, [ --enable-gcc allow use of gcc if available],
+ [tk_ok=$enableval], [tkl_ok=no])
+if test "$tk_ok" = "yes"; then
+ AC_PROG_CC
+else
+ CC=${CC-cc}
+AC_SUBST(CC)
+fi
+AC_C_CROSS
+AC_HAVE_HEADERS(unistd.h limits.h)
+
+#--------------------------------------------------------------------
+# See if there was a command-line option for where Tcl is; if
+# not, assume that its top-level directory is a sibling of ours.
+#--------------------------------------------------------------------
+
+AC_ARG_WITH(tcl, [ --with-tcl=DIR use Tcl 8.0 binaries from DIR],
+ TCL_BIN_DIR=$withval, TCL_BIN_DIR=`cd ../../tcl8.0/unix; pwd`)
+if test ! -d $TCL_BIN_DIR; then
+ AC_MSG_ERROR(Tcl directory $TCL_BIN_DIR doesn't exist)
+fi
+if test ! -f $TCL_BIN_DIR/Makefile; then
+ AC_MSG_ERROR(There's no Makefile in $TCL_BIN_DIR; perhaps you didn't specify the Tcl *build* directory (not the toplevel Tcl directory) or you forgot to configure Tcl?)
+fi
+
+#--------------------------------------------------------------------
+# Read in configuration information generated by Tcl for shared
+# libraries, and arrange for it to be substituted into our
+# Makefile.
+#--------------------------------------------------------------------
+
+file=$TCL_BIN_DIR/tclConfig.sh
+. $file
+SHLIB_CFLAGS=$TCL_SHLIB_CFLAGS
+SHLIB_LD=$TCL_SHLIB_LD
+SHLIB_LD_LIBS=$TCL_SHLIB_LD_LIBS
+SHLIB_SUFFIX=$TCL_SHLIB_SUFFIX
+SHLIB_VERSION=$TCL_SHLIB_VERSION
+DL_LIBS=$TCL_DL_LIBS
+LD_FLAGS=$TCL_LD_FLAGS
+
+LIB_RUNTIME_DIR='${LIB_RUNTIME_DIR}'
+
+# If Tcl and Tk are installed in different places, adjust the library
+# search path to reflect this.
+
+if test "$TCL_EXEC_PREFIX" != "$exec_prefix"; then
+ LIB_RUNTIME_DIR="${LIB_RUNTIME_DIR}:${TCL_EXEC_PREFIX}"
+fi
+
+#--------------------------------------------------------------------
+# Supply a substitute for stdlib.h if it doesn't define strtol,
+# strtoul, or strtod (which it doesn't in some versions of SunOS).
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING(stdlib.h)
+AC_HEADER_EGREP(strtol, stdlib.h, tk_ok=yes, tk_ok=no)
+AC_HEADER_EGREP(strtoul, stdlib.h, , tk_ok=no)
+AC_HEADER_EGREP(strtod, stdlib.h, , tk_ok=no)
+if test $tk_ok = no; then
+ AC_DEFINE(NO_STDLIB_H)
+fi
+AC_MSG_RESULT($tk_ok)
+
+#--------------------------------------------------------------------
+# Include sys/select.h if it exists and if it supplies things
+# that appear to be useful and aren't already in sys/types.h.
+# This appears to be true only on the RS/6000 under AIX. Some
+# systems like OSF/1 have a sys/select.h that's of no use, and
+# other systems like SCO UNIX have a sys/select.h that's
+# pernicious. If "fd_set" isn't defined anywhere then set a
+# special flag.
+#--------------------------------------------------------------------
+
+AC_MSG_CHECKING([fd_set and sys/select])
+AC_TRY_COMPILE([#include <sys/types.h>],
+ [fd_set readMask, writeMask;], tk_ok=yes, tk_ok=no)
+if test $tk_ok = no; then
+ AC_HEADER_EGREP(fd_mask, sys/select.h, tk_ok=yes)
+ if test $tk_ok = yes; then
+ AC_DEFINE(HAVE_SYS_SELECT_H)
+ fi
+fi
+AC_MSG_RESULT($tk_ok)
+if test $tk_ok = no; then
+ AC_DEFINE(NO_FD_SET)
+fi
+
+#--------------------------------------------------------------------
+# Check for various typedefs and provide substitutes if
+# they don't exist.
+#--------------------------------------------------------------------
+
+AC_MODE_T
+AC_PID_T
+AC_SIZE_T
+AC_UID_T
+
+#------------------------------------------------------------------------------
+# Find out about time handling differences.
+#------------------------------------------------------------------------------
+
+AC_CHECK_HEADERS(sys/time.h)
+AC_HEADER_TIME
+
+#--------------------------------------------------------------------
+# Locate the X11 header files and the X11 library archive. Try
+# the ac_path_x macro first, but if it doesn't find the X stuff
+# (e.g. because there's no xmkmf program) then check through
+# a list of possible directories. Under some conditions the
+# autoconf macro will return an include directory that contains
+# no include files, so double-check its result just to be safe.
+#--------------------------------------------------------------------
+
+AC_PATH_X
+not_really_there=""
+if test "$no_x" = ""; then
+ if test "$x_includes" = ""; then
+ AC_TRY_CPP([#include <X11/XIntrinsic.h>], , not_really_there="yes")
+ else
+ if test ! -r $x_includes/X11/Intrinsic.h; then
+ not_really_there="yes"
+ fi
+ fi
+fi
+if test "$no_x" = "yes" -o "$not_really_there" = "yes"; then
+ AC_MSG_CHECKING(for X11 header files)
+ XINCLUDES="# no special path needed"
+ AC_TRY_CPP([#include <X11/Intrinsic.h>], , XINCLUDES="nope")
+ if test "$XINCLUDES" = nope; then
+ dirs="/usr/unsupported/include /usr/local/include /usr/X386/include /usr/X11R6/include /usr/X11R5/include /usr/include/X11R5 /usr/include/X11R4 /usr/openwin/include /usr/X11/include /usr/sww/include"
+ for i in $dirs ; do
+ if test -r $i/X11/Intrinsic.h; then
+ AC_MSG_RESULT($i)
+ XINCLUDES=" -I$i"
+ break
+ fi
+ done
+ fi
+else
+ if test "$x_includes" != ""; then
+ XINCLUDES=-I$x_includes
+ else
+ XINCLUDES="# no special path needed"
+ fi
+fi
+if test "$XINCLUDES" = nope; then
+ AC_MSG_RESULT(couldn't find any!)
+ XINCLUDES="# no include files found"
+fi
+
+if test "$no_x" = yes; then
+ AC_MSG_CHECKING(for X11 libraries)
+ XLIBSW=nope
+ dirs="/usr/unsupported/lib /usr/local/lib /usr/X386/lib /usr/X11R6/lib /usr/X11R5/lib /usr/lib/X11R5 /usr/lib/X11R4 /usr/openwin/lib /usr/X11/lib /usr/sww/X11/lib"
+ for i in $dirs ; do
+ if test -r $i/libX11.a -o -r $i/libX11.so -o -r $i/libX11.sl; then
+ AC_MSG_RESULT($i)
+ XLIBSW="-L$i -lX11"
+ x_libraries="$i"
+ break
+ fi
+ done
+else
+ if test "$x_libraries" = ""; then
+ XLIBSW=-lX11
+ else
+ XLIBSW="-L$x_libraries -lX11"
+ fi
+fi
+if test "$XLIBSW" = nope ; then
+ AC_CHECK_LIB(Xwindow, XCreateWindow, XLIBSW=-lXwindow)
+fi
+if test "$XLIBSW" = nope ; then
+ AC_MSG_RESULT(couldn't find any! Using -lX11.)
+ XLIBSW=-lX11
+fi
+
+#--------------------------------------------------------------------
+# Various manipulations on the search path used at runtime to
+# find shared libraries:
+# 1. If the X library binaries are in a non-standard directory,
+# add the X library location into that search path.
+# 2. On systems such as AIX and Ultrix that use "-L" as the
+# search path option, colons cannot be used to separate
+# directories from each other. Change colons to " -L".
+# 3. Create two sets of search flags, one for use in cc lines
+# and the other for when the linker is invoked directly. In
+# the second case, '-Wl,' must be stripped off and commas must
+# be replaced by spaces.
+#--------------------------------------------------------------------
+
+if test "x${x_libraries}" != "x"; then
+ LIB_RUNTIME_DIR="${LIB_RUNTIME_DIR}:${x_libraries}"
+fi
+if test "${TCL_LD_SEARCH_FLAGS}" = '-L${LIB_RUNTIME_DIR}'; then
+ LIB_RUNTIME_DIR=`echo ${LIB_RUNTIME_DIR} |sed -e 's/:/ -L/g'`
+fi
+
+# The statement below is very tricky! It actually *evaluates* the
+# string in TCL_LD_SEARCH_FLAGS, which causes a substitution of the
+# variable LIB_RUNTIME_DIR.
+
+eval "TK_CC_SEARCH_FLAGS=\"$TCL_LD_SEARCH_FLAGS\""
+TK_LD_SEARCH_FLAGS=`echo ${TK_CC_SEARCH_FLAGS} |sed -e "s|-Wl,||g" -e "s|,| |g"`
+
+#--------------------------------------------------------------------
+# Check for the existence of various libraries. The order here
+# is important, so that then end up in the right order in the
+# command line generated by make. The -lsocket and -lnsl libraries
+# require a couple of special tricks:
+# 1. Use "connect" and "accept" to check for -lsocket, and
+# "gethostbyname" to check for -lnsl.
+# 2. Use each function name only once: can't redo a check because
+# autoconf caches the results of the last check and won't redo it.
+# 3. Use -lnsl and -lsocket only if they supply procedures that
+# aren't already present in the normal libraries. This is because
+# IRIX 5.2 has libraries, but they aren't needed and they're
+# bogus: they goof up name resolution if used.
+# 4. On some SVR4 systems, can't use -lsocket without -lnsl too.
+# To get around this problem, check for both libraries together
+# if -lsocket doesn't work by itself.
+#--------------------------------------------------------------------
+
+AC_CHECK_LIB(Xbsd, main, [LIBS="$LIBS -lXbsd"])
+
+tk_checkBoth=0
+AC_CHECK_FUNC(connect, tk_checkSocket=0, tk_checkSocket=1)
+if test "$tk_checkSocket" = 1; then
+ AC_CHECK_LIB(socket, main, LIBS="$LIBS -lsocket", tk_checkBoth=1)
+fi
+if test "$tk_checkBoth" = 1; then
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lsocket -lnsl"
+ AC_CHECK_FUNC(accept, tk_checkNsl=0, [LIBS=$tk_oldLibs])
+fi
+AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, main, [LIBS="$LIBS -lnsl"]))
+
+#--------------------------------------------------------------------
+# One more check related to the X libraries. The standard releases
+# of Ultrix don't support the "xauth" mechanism, so send won't work
+# unless TK_NO_SECURITY is defined. However, there are usually copies
+# of the MIT X server available as well, which do support xauth.
+# Check for the MIT stuff and use it if it exists.
+#
+# Note: can't use ac_check_lib macro (at least, not in Autoconf 2.1)
+# because it can't deal with the "-" in the library name.
+#--------------------------------------------------------------------
+
+if test -d /usr/include/mit ; then
+ AC_MSG_CHECKING([MIT X libraries])
+ tk_oldCFlags=$CFLAGS
+ CFLAGS="$CFLAGS -I/usr/include/mit"
+ tk_oldLibs=$LIBS
+ LIBS="$LIBS -lX11-mit"
+ AC_TRY_LINK([
+ #include <X11/Xlib.h>
+ ], [
+ XOpenDisplay(0);
+ ], [
+ AC_MSG_RESULT(yes)
+ XLIBSW="-lX11-mit"
+ XINCLUDES="-I/usr/include/mit"
+ ], AC_MSG_RESULT(no))
+ CFLAGS=$tk_oldCFlags
+ LIBS=$tk_oldLibs
+fi
+
+#--------------------------------------------------------------------
+# On a few very rare systems, all of the libm.a stuff is
+# already in libc.a. Set compiler flags accordingly.
+# Also, Linux requires the "ieee" library for math to
+# work right (and it must appear before "-lm").
+#--------------------------------------------------------------------
+
+MATH_LIBS=""
+AC_CHECK_FUNC(sin, , MATH_LIBS="-lm")
+AC_CHECK_LIB(ieee, main, [MATH_LIBS="-lieee $MATH_LIBS"])
+
+#--------------------------------------------------------------------
+# If this system doesn't have a memmove procedure, use memcpy
+# instead.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(memmove, , [AC_DEFINE(memmove, memcpy)])
+
+#--------------------------------------------------------------------
+# Figure out whether "char" is unsigned. If so, set a
+# #define for __CHAR_UNSIGNED__.
+#--------------------------------------------------------------------
+
+AC_C_CHAR_UNSIGNED
+
+#--------------------------------------------------------------------
+# Under Solaris 2.4, strtod returns the wrong value for the
+# terminating character under some conditions. Check for this
+# and if the problem exists use a substitute procedure
+# "fixstrtod" (provided by Tcl) that corrects the error.
+#--------------------------------------------------------------------
+
+AC_CHECK_FUNC(strtod, tk_strtod=1, tk_strtod=0)
+if test "$tk_strtod" = 1; then
+ AC_MSG_CHECKING([for Solaris 2.4 strtod bug])
+ AC_TRY_RUN([
+ extern double strtod();
+ int main()
+ {
+ char *string = "NaN";
+ char *term;
+ strtod(string, &term);
+ if ((term != string) && (term[-1] == 0)) {
+ exit(1);
+ }
+ exit(0);
+ }], tk_ok=1, tk_ok=0, tk_ok=0)
+ if test "$tk_ok" = 1; then
+ AC_MSG_RESULT(ok)
+ else
+ AC_MSG_RESULT(buggy)
+ AC_DEFINE(strtod, fixstrtod)
+ fi
+fi
+
+#--------------------------------------------------------------------
+# The statements below define a collection of symbols related to
+# building libtk as a shared library instead of a static library.
+#--------------------------------------------------------------------
+
+AC_ARG_ENABLE(shared,
+ [ --enable-shared build libtk as a shared library],
+ [ok=$enableval], [ok=no])
+if test "$ok" = "yes" -a "${SHLIB_SUFFIX}" != ""; then
+ TK_SHARED_BUILD=1
+ TK_SHLIB_CFLAGS="${SHLIB_CFLAGS}"
+ eval "TK_LIB_FILE=libtk${TCL_SHARED_LIB_SUFFIX}"
+ MAKE_LIB="\${SHLIB_LD} -o ${TK_LIB_FILE} \${OBJS} \$(TK_LD_SEARCH_FLAGS) ${SHLIB_LD_LIBS}"
+ RANLIB=":"
+else
+ TK_SHARED_BUILD=0
+ TK_SHLIB_CFLAGS=""
+ eval "TK_LIB_FILE=libtk${TCL_UNSHARED_LIB_SUFFIX}"
+ MAKE_LIB="ar cr ${TK_LIB_FILE} \${OBJS}"
+fi
+
+# Note: in the following variable, it's important to use the absolute
+# path name of the Tcl directory rather than "..": this is because
+# AIX remembers this path and will attempt to use it at run-time to look
+# up the Tcl library.
+
+if test "${TCL_LIB_VERSIONS_OK}" = "ok"; then
+ TK_BUILD_LIB_SPEC="-L`pwd` -ltk${VERSION}"
+ TK_LIB_SPEC="-L${exec_prefix}/lib -ltk${VERSION}"
+else
+ TK_BUILD_LIB_SPEC="-L`pwd` -ltk`echo ${VERSION} | tr -d .`"
+ TK_LIB_SPEC="-L${exec_prefix}/lib -ltk`echo ${VERSION} | tr -d .`"
+fi
+
+AC_SUBST(DL_LIBS)
+AC_SUBST(LD_FLAGS)
+AC_SUBST(MATH_LIBS)
+AC_SUBST(MAKE_LIB)
+AC_SUBST(SHLIB_CFLAGS)
+AC_SUBST(SHLIB_LD)
+AC_SUBST(SHLIB_LD_LIBS)
+AC_SUBST(SHLIB_SUFFIX)
+AC_SUBST(SHLIB_VERSION)
+AC_SUBST(TCL_BIN_DIR)
+AC_SUBST(TCL_BUILD_LIB_SPEC)
+AC_SUBST(TCL_SRC_DIR)
+AC_SUBST(TCL_VERSION)
+AC_SUBST(TK_BUILD_LIB_SPEC)
+AC_SUBST(TK_CC_SEARCH_FLAGS)
+AC_SUBST(TK_LD_SEARCH_FLAGS)
+AC_SUBST(TK_LIB_FILE)
+AC_SUBST(TK_LIB_SPEC)
+AC_SUBST(TK_MAJOR_VERSION)
+AC_SUBST(TK_MINOR_VERSION)
+AC_SUBST(TK_PATCH_LEVEL)
+AC_SUBST(TK_SHLIB_CFLAGS)
+AC_SUBST(TK_SRC_DIR)
+AC_SUBST(TK_VERSION)
+AC_SUBST(XINCLUDES)
+AC_SUBST(XLIBSW)
+AC_SUBST(TK_SHARED_BUILD)
+
+AC_OUTPUT(Makefile tkConfig.sh)
diff --git a/unix/install-sh b/unix/install-sh
new file mode 100644
index 0000000..0ff4b6a
--- /dev/null
+++ b/unix/install-sh
@@ -0,0 +1,119 @@
+#!/bin/sh
+
+#
+# install - install a program, script, or datafile
+# This comes from X11R5; it is not part of GNU.
+#
+# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+#
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+
+instcmd="$mvprog"
+chmodcmd=""
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+fi
+
+if [ x"$dst" = x ]
+then
+ echo "install: no destination specified"
+ exit 1
+fi
+
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+if [ -d $dst ]
+then
+ dst="$dst"/`basename $src`
+fi
+
+# Make a temp file name in the proper directory.
+
+dstdir=`dirname $dst`
+dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+$doit $instcmd $src $dsttmp
+
+# and set any options; do chmod last to preserve setuid bits
+
+if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi
+if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi
+if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi
+if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi
+
+# Now rename the file to the real destination.
+
+$doit $rmcmd $dst
+$doit $mvcmd $dsttmp $dst
+
+
+exit 0
diff --git a/unix/mkLinks b/unix/mkLinks
new file mode 100644
index 0000000..d817703
--- /dev/null
+++ b/unix/mkLinks
@@ -0,0 +1,878 @@
+#!/bin/sh
+# This script is invoked when installing manual entries. It generates
+# additional links to manual entries, corresponding to the procedure
+# and command names described by the manual entry. For example, the
+# Tcl manual entry Hash.3 describes procedures Tcl_InitHashTable,
+# Tcl_CreateHashEntry, and many more. This script will make hard
+# links so that Tcl_InitHashTable.3, Tcl_CreateHashEntry.3, and so
+# on all refer to Hash.3 in the installed directory.
+#
+# Because of the length of command and procedure names, this mechanism
+# only works on machines that support file names longer than 14 characters.
+# This script checks to see if long file names are supported, and it
+# doesn't make any links if they are not.
+#
+# The script takes one argument, which is the name of the directory
+# where the manual entries have been installed.
+
+if test $# != 1; then
+ echo "Usage: mkLinks dir"
+ exit 1
+fi
+
+cd $1
+echo foo > xyzzyTestingAVeryLongFileName.foo
+x=`echo xyzzyTe*`
+rm xyzzyTe*
+if test "$x" != "xyzzyTestingAVeryLongFileName.foo"; then
+ exit
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_3DBorderColor.3
+ ln 3DBorder.3 Tk_3DBorderColor.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_3DBorderGC.3
+ ln 3DBorder.3 Tk_3DBorderGC.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_3DHorizontalBevel.3
+ ln 3DBorder.3 Tk_3DHorizontalBevel.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_3DVerticalBevel.3
+ ln 3DBorder.3 Tk_3DVerticalBevel.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Attributes.3
+ ln WindowId.3 Tk_Attributes.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_BindEvent.3
+ ln BindTable.3 Tk_BindEvent.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasDrawableCoords.3
+ ln CanvTkwin.3 Tk_CanvasDrawableCoords.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasEventuallyRedraw.3
+ ln CanvTkwin.3 Tk_CanvasEventuallyRedraw.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasGetCoord.3
+ ln CanvTkwin.3 Tk_CanvasGetCoord.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsBitmap.3
+ ln CanvPsY.3 Tk_CanvasPsBitmap.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsColor.3
+ ln CanvPsY.3 Tk_CanvasPsColor.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsFont.3
+ ln CanvPsY.3 Tk_CanvasPsFont.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsPath.3
+ ln CanvPsY.3 Tk_CanvasPsPath.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsStipple.3
+ ln CanvPsY.3 Tk_CanvasPsStipple.3
+fi
+if test -r CanvPsY.3; then
+ rm -f Tk_CanvasPsY.3
+ ln CanvPsY.3 Tk_CanvasPsY.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasSetStippleOrigin.3
+ ln CanvTkwin.3 Tk_CanvasSetStippleOrigin.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasTagsOption.3
+ ln CanvTkwin.3 Tk_CanvasTagsOption.3
+fi
+if test -r CanvTxtInfo.3; then
+ rm -f Tk_CanvasTextInfo.3
+ ln CanvTxtInfo.3 Tk_CanvasTextInfo.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasTkwin.3
+ ln CanvTkwin.3 Tk_CanvasTkwin.3
+fi
+if test -r CanvTkwin.3; then
+ rm -f Tk_CanvasWindowCoords.3
+ ln CanvTkwin.3 Tk_CanvasWindowCoords.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_ChangeWindowAttributes.3
+ ln ConfigWind.3 Tk_ChangeWindowAttributes.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Changes.3
+ ln WindowId.3 Tk_Changes.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_CharBbox.3
+ ln TextLayout.3 Tk_CharBbox.3
+fi
+if test -r SetClass.3; then
+ rm -f Tk_Class.3
+ ln SetClass.3 Tk_Class.3
+fi
+if test -r ClrSelect.3; then
+ rm -f Tk_ClearSelection.3
+ ln ClrSelect.3 Tk_ClearSelection.3
+fi
+if test -r Clipboard.3; then
+ rm -f Tk_ClipboardAppend.3
+ ln Clipboard.3 Tk_ClipboardAppend.3
+fi
+if test -r Clipboard.3; then
+ rm -f Tk_ClipboardClear.3
+ ln Clipboard.3 Tk_ClipboardClear.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Colormap.3
+ ln WindowId.3 Tk_Colormap.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_ComputeTextLayout.3
+ ln TextLayout.3 Tk_ComputeTextLayout.3
+fi
+if test -r ConfigWidg.3; then
+ rm -f Tk_ConfigureInfo.3
+ ln ConfigWidg.3 Tk_ConfigureInfo.3
+fi
+if test -r ConfigWidg.3; then
+ rm -f Tk_ConfigureValue.3
+ ln ConfigWidg.3 Tk_ConfigureValue.3
+fi
+if test -r ConfigWidg.3; then
+ rm -f Tk_ConfigureWidget.3
+ ln ConfigWidg.3 Tk_ConfigureWidget.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_ConfigureWindow.3
+ ln ConfigWind.3 Tk_ConfigureWindow.3
+fi
+if test -r CoordToWin.3; then
+ rm -f Tk_CoordsToWindow.3
+ ln CoordToWin.3 Tk_CoordsToWindow.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_CreateBinding.3
+ ln BindTable.3 Tk_CreateBinding.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_CreateBindingTable.3
+ ln BindTable.3 Tk_CreateBindingTable.3
+fi
+if test -r CrtErrHdlr.3; then
+ rm -f Tk_CreateErrorHandler.3
+ ln CrtErrHdlr.3 Tk_CreateErrorHandler.3
+fi
+if test -r EventHndlr.3; then
+ rm -f Tk_CreateEventHandler.3
+ ln EventHndlr.3 Tk_CreateEventHandler.3
+fi
+if test -r CrtGenHdlr.3; then
+ rm -f Tk_CreateGenericHandler.3
+ ln CrtGenHdlr.3 Tk_CreateGenericHandler.3
+fi
+if test -r CrtImgType.3; then
+ rm -f Tk_CreateImageType.3
+ ln CrtImgType.3 Tk_CreateImageType.3
+fi
+if test -r CrtItemType.3; then
+ rm -f Tk_CreateItemType.3
+ ln CrtItemType.3 Tk_CreateItemType.3
+fi
+if test -r CrtPhImgFmt.3; then
+ rm -f Tk_CreatePhotoImageFormat.3
+ ln CrtPhImgFmt.3 Tk_CreatePhotoImageFormat.3
+fi
+if test -r CrtSelHdlr.3; then
+ rm -f Tk_CreateSelHandler.3
+ ln CrtSelHdlr.3 Tk_CreateSelHandler.3
+fi
+if test -r CrtWindow.3; then
+ rm -f Tk_CreateWindow.3
+ ln CrtWindow.3 Tk_CreateWindow.3
+fi
+if test -r CrtWindow.3; then
+ rm -f Tk_CreateWindowFromPath.3
+ ln CrtWindow.3 Tk_CreateWindowFromPath.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_DefineBitmap.3
+ ln GetBitmap.3 Tk_DefineBitmap.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_DefineCursor.3
+ ln ConfigWind.3 Tk_DefineCursor.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_DeleteAllBindings.3
+ ln BindTable.3 Tk_DeleteAllBindings.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_DeleteBinding.3
+ ln BindTable.3 Tk_DeleteBinding.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_DeleteBindingTable.3
+ ln BindTable.3 Tk_DeleteBindingTable.3
+fi
+if test -r CrtErrHdlr.3; then
+ rm -f Tk_DeleteErrorHandler.3
+ ln CrtErrHdlr.3 Tk_DeleteErrorHandler.3
+fi
+if test -r EventHndlr.3; then
+ rm -f Tk_DeleteEventHandler.3
+ ln EventHndlr.3 Tk_DeleteEventHandler.3
+fi
+if test -r CrtGenHdlr.3; then
+ rm -f Tk_DeleteGenericHandler.3
+ ln CrtGenHdlr.3 Tk_DeleteGenericHandler.3
+fi
+if test -r DeleteImg.3; then
+ rm -f Tk_DeleteImage.3
+ ln DeleteImg.3 Tk_DeleteImage.3
+fi
+if test -r CrtSelHdlr.3; then
+ rm -f Tk_DeleteSelHandler.3
+ ln CrtSelHdlr.3 Tk_DeleteSelHandler.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Depth.3
+ ln WindowId.3 Tk_Depth.3
+fi
+if test -r CrtWindow.3; then
+ rm -f Tk_DestroyWindow.3
+ ln CrtWindow.3 Tk_DestroyWindow.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Display.3
+ ln WindowId.3 Tk_Display.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_DisplayName.3
+ ln WindowId.3 Tk_DisplayName.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_DistanceToTextLayout.3
+ ln TextLayout.3 Tk_DistanceToTextLayout.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Draw3DPolygon.3
+ ln 3DBorder.3 Tk_Draw3DPolygon.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Draw3DRectangle.3
+ ln 3DBorder.3 Tk_Draw3DRectangle.3
+fi
+if test -r MeasureChar.3; then
+ rm -f Tk_DrawChars.3
+ ln MeasureChar.3 Tk_DrawChars.3
+fi
+if test -r DrawFocHlt.3; then
+ rm -f Tk_DrawFocusHighlight.3
+ ln DrawFocHlt.3 Tk_DrawFocusHighlight.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_DrawTextLayout.3
+ ln TextLayout.3 Tk_DrawTextLayout.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Fill3DPolygon.3
+ ln 3DBorder.3 Tk_Fill3DPolygon.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Fill3DRectangle.3
+ ln 3DBorder.3 Tk_Fill3DRectangle.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_FindPhoto.3
+ ln FindPhoto.3 Tk_FindPhoto.3
+fi
+if test -r FontId.3; then
+ rm -f Tk_FontId.3
+ ln FontId.3 Tk_FontId.3
+fi
+if test -r FontId.3; then
+ rm -f Tk_FontMetrics.3
+ ln FontId.3 Tk_FontMetrics.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Free3DBorder.3
+ ln 3DBorder.3 Tk_Free3DBorder.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_FreeBitmap.3
+ ln GetBitmap.3 Tk_FreeBitmap.3
+fi
+if test -r GetColor.3; then
+ rm -f Tk_FreeColor.3
+ ln GetColor.3 Tk_FreeColor.3
+fi
+if test -r GetClrmap.3; then
+ rm -f Tk_FreeColormap.3
+ ln GetClrmap.3 Tk_FreeColormap.3
+fi
+if test -r GetCursor.3; then
+ rm -f Tk_FreeCursor.3
+ ln GetCursor.3 Tk_FreeCursor.3
+fi
+if test -r GetFont.3; then
+ rm -f Tk_FreeFont.3
+ ln GetFont.3 Tk_FreeFont.3
+fi
+if test -r GetGC.3; then
+ rm -f Tk_FreeGC.3
+ ln GetGC.3 Tk_FreeGC.3
+fi
+if test -r GetImage.3; then
+ rm -f Tk_FreeImage.3
+ ln GetImage.3 Tk_FreeImage.3
+fi
+if test -r ConfigWidg.3; then
+ rm -f Tk_FreeOptions.3
+ ln ConfigWidg.3 Tk_FreeOptions.3
+fi
+if test -r GetPixmap.3; then
+ rm -f Tk_FreePixmap.3
+ ln GetPixmap.3 Tk_FreePixmap.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_FreeTextLayout.3
+ ln TextLayout.3 Tk_FreeTextLayout.3
+fi
+if test -r FreeXId.3; then
+ rm -f Tk_FreeXId.3
+ ln FreeXId.3 Tk_FreeXId.3
+fi
+if test -r GeomReq.3; then
+ rm -f Tk_GeometryRequest.3
+ ln GeomReq.3 Tk_GeometryRequest.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_Get3DBorder.3
+ ln 3DBorder.3 Tk_Get3DBorder.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_GetAllBindings.3
+ ln BindTable.3 Tk_GetAllBindings.3
+fi
+if test -r GetAnchor.3; then
+ rm -f Tk_GetAnchor.3
+ ln GetAnchor.3 Tk_GetAnchor.3
+fi
+if test -r InternAtom.3; then
+ rm -f Tk_GetAtomName.3
+ ln InternAtom.3 Tk_GetAtomName.3
+fi
+if test -r BindTable.3; then
+ rm -f Tk_GetBinding.3
+ ln BindTable.3 Tk_GetBinding.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_GetBitmap.3
+ ln GetBitmap.3 Tk_GetBitmap.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_GetBitmapFromData.3
+ ln GetBitmap.3 Tk_GetBitmapFromData.3
+fi
+if test -r GetCapStyl.3; then
+ rm -f Tk_GetCapStyle.3
+ ln GetCapStyl.3 Tk_GetCapStyle.3
+fi
+if test -r GetColor.3; then
+ rm -f Tk_GetColor.3
+ ln GetColor.3 Tk_GetColor.3
+fi
+if test -r GetColor.3; then
+ rm -f Tk_GetColorByValue.3
+ ln GetColor.3 Tk_GetColorByValue.3
+fi
+if test -r GetClrmap.3; then
+ rm -f Tk_GetColormap.3
+ ln GetClrmap.3 Tk_GetColormap.3
+fi
+if test -r GetCursor.3; then
+ rm -f Tk_GetCursor.3
+ ln GetCursor.3 Tk_GetCursor.3
+fi
+if test -r GetCursor.3; then
+ rm -f Tk_GetCursorFromData.3
+ ln GetCursor.3 Tk_GetCursorFromData.3
+fi
+if test -r GetFont.3; then
+ rm -f Tk_GetFont.3
+ ln GetFont.3 Tk_GetFont.3
+fi
+if test -r GetGC.3; then
+ rm -f Tk_GetGC.3
+ ln GetGC.3 Tk_GetGC.3
+fi
+if test -r GetImage.3; then
+ rm -f Tk_GetImage.3
+ ln GetImage.3 Tk_GetImage.3
+fi
+if test -r CrtImgType.3; then
+ rm -f Tk_GetImageMasterData.3
+ ln CrtImgType.3 Tk_GetImageMasterData.3
+fi
+if test -r CrtItemType.3; then
+ rm -f Tk_GetItemTypes.3
+ ln CrtItemType.3 Tk_GetItemTypes.3
+fi
+if test -r GetJoinStl.3; then
+ rm -f Tk_GetJoinStyle.3
+ ln GetJoinStl.3 Tk_GetJoinStyle.3
+fi
+if test -r GetJustify.3; then
+ rm -f Tk_GetJustify.3
+ ln GetJustify.3 Tk_GetJustify.3
+fi
+if test -r GetOption.3; then
+ rm -f Tk_GetOption.3
+ ln GetOption.3 Tk_GetOption.3
+fi
+if test -r GetPixels.3; then
+ rm -f Tk_GetPixels.3
+ ln GetPixels.3 Tk_GetPixels.3
+fi
+if test -r GetPixmap.3; then
+ rm -f Tk_GetPixmap.3
+ ln GetPixmap.3 Tk_GetPixmap.3
+fi
+if test -r GetRelief.3; then
+ rm -f Tk_GetRelief.3
+ ln GetRelief.3 Tk_GetRelief.3
+fi
+if test -r GetRootCrd.3; then
+ rm -f Tk_GetRootCoords.3
+ ln GetRootCrd.3 Tk_GetRootCoords.3
+fi
+if test -r GetPixels.3; then
+ rm -f Tk_GetScreenMM.3
+ ln GetPixels.3 Tk_GetScreenMM.3
+fi
+if test -r GetScroll.3; then
+ rm -f Tk_GetScrollInfo.3
+ ln GetScroll.3 Tk_GetScrollInfo.3
+fi
+if test -r GetSelect.3; then
+ rm -f Tk_GetSelection.3
+ ln GetSelect.3 Tk_GetSelection.3
+fi
+if test -r GetUid.3; then
+ rm -f Tk_GetUid.3
+ ln GetUid.3 Tk_GetUid.3
+fi
+if test -r GetVRoot.3; then
+ rm -f Tk_GetVRootGeometry.3
+ ln GetVRoot.3 Tk_GetVRootGeometry.3
+fi
+if test -r GetVisual.3; then
+ rm -f Tk_GetVisual.3
+ ln GetVisual.3 Tk_GetVisual.3
+fi
+if test -r HandleEvent.3; then
+ rm -f Tk_HandleEvent.3
+ ln HandleEvent.3 Tk_HandleEvent.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Height.3
+ ln WindowId.3 Tk_Height.3
+fi
+if test -r IdToWindow.3; then
+ rm -f Tk_IdToWindow.3
+ ln IdToWindow.3 Tk_IdToWindow.3
+fi
+if test -r ImgChanged.3; then
+ rm -f Tk_ImageChanged.3
+ ln ImgChanged.3 Tk_ImageChanged.3
+fi
+if test -r InternAtom.3; then
+ rm -f Tk_InternAtom.3
+ ln InternAtom.3 Tk_InternAtom.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_InternalBorderWidth.3
+ ln WindowId.3 Tk_InternalBorderWidth.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_IntersectTextLayout.3
+ ln TextLayout.3 Tk_IntersectTextLayout.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_IsMapped.3
+ ln WindowId.3 Tk_IsMapped.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_IsTopLevel.3
+ ln WindowId.3 Tk_IsTopLevel.3
+fi
+if test -r MainLoop.3; then
+ rm -f Tk_MainLoop.3
+ ln MainLoop.3 Tk_MainLoop.3
+fi
+if test -r MainWin.3; then
+ rm -f Tk_MainWindow.3
+ ln MainWin.3 Tk_MainWindow.3
+fi
+if test -r MaintGeom.3; then
+ rm -f Tk_MaintainGeometry.3
+ ln MaintGeom.3 Tk_MaintainGeometry.3
+fi
+if test -r CrtWindow.3; then
+ rm -f Tk_MakeWindowExist.3
+ ln CrtWindow.3 Tk_MakeWindowExist.3
+fi
+if test -r ManageGeom.3; then
+ rm -f Tk_ManageGeometry.3
+ ln ManageGeom.3 Tk_ManageGeometry.3
+fi
+if test -r MapWindow.3; then
+ rm -f Tk_MapWindow.3
+ ln MapWindow.3 Tk_MapWindow.3
+fi
+if test -r MeasureChar.3; then
+ rm -f Tk_MeasureChars.3
+ ln MeasureChar.3 Tk_MeasureChars.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_MoveResizeWindow.3
+ ln ConfigWind.3 Tk_MoveResizeWindow.3
+fi
+if test -r MoveToplev.3; then
+ rm -f Tk_MoveToplevelWindow.3
+ ln MoveToplev.3 Tk_MoveToplevelWindow.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_MoveWindow.3
+ ln ConfigWind.3 Tk_MoveWindow.3
+fi
+if test -r Name.3; then
+ rm -f Tk_Name.3
+ ln Name.3 Tk_Name.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_NameOf3DBorder.3
+ ln 3DBorder.3 Tk_NameOf3DBorder.3
+fi
+if test -r GetAnchor.3; then
+ rm -f Tk_NameOfAnchor.3
+ ln GetAnchor.3 Tk_NameOfAnchor.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_NameOfBitmap.3
+ ln GetBitmap.3 Tk_NameOfBitmap.3
+fi
+if test -r GetCapStyl.3; then
+ rm -f Tk_NameOfCapStyle.3
+ ln GetCapStyl.3 Tk_NameOfCapStyle.3
+fi
+if test -r GetColor.3; then
+ rm -f Tk_NameOfColor.3
+ ln GetColor.3 Tk_NameOfColor.3
+fi
+if test -r GetCursor.3; then
+ rm -f Tk_NameOfCursor.3
+ ln GetCursor.3 Tk_NameOfCursor.3
+fi
+if test -r GetFont.3; then
+ rm -f Tk_NameOfFont.3
+ ln GetFont.3 Tk_NameOfFont.3
+fi
+if test -r NameOfImg.3; then
+ rm -f Tk_NameOfImage.3
+ ln NameOfImg.3 Tk_NameOfImage.3
+fi
+if test -r GetJoinStl.3; then
+ rm -f Tk_NameOfJoinStyle.3
+ ln GetJoinStl.3 Tk_NameOfJoinStyle.3
+fi
+if test -r GetJustify.3; then
+ rm -f Tk_NameOfJustify.3
+ ln GetJustify.3 Tk_NameOfJustify.3
+fi
+if test -r GetRelief.3; then
+ rm -f Tk_NameOfRelief.3
+ ln GetRelief.3 Tk_NameOfRelief.3
+fi
+if test -r Name.3; then
+ rm -f Tk_NameToWindow.3
+ ln Name.3 Tk_NameToWindow.3
+fi
+if test -r ConfigWidg.3; then
+ rm -f Tk_Offset.3
+ ln ConfigWidg.3 Tk_Offset.3
+fi
+if test -r OwnSelect.3; then
+ rm -f Tk_OwnSelection.3
+ ln OwnSelect.3 Tk_OwnSelection.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Parent.3
+ ln WindowId.3 Tk_Parent.3
+fi
+if test -r ParseArgv.3; then
+ rm -f Tk_ParseArgv.3
+ ln ParseArgv.3 Tk_ParseArgv.3
+fi
+if test -r Name.3; then
+ rm -f Tk_PathName.3
+ ln Name.3 Tk_PathName.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoBlank.3
+ ln FindPhoto.3 Tk_PhotoBlank.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoExpand.3
+ ln FindPhoto.3 Tk_PhotoExpand.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoGetImage.3
+ ln FindPhoto.3 Tk_PhotoGetImage.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoGetSize.3
+ ln FindPhoto.3 Tk_PhotoGetSize.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoPutBlock.3
+ ln FindPhoto.3 Tk_PhotoPutBlock.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoPutZoomedBlock.3
+ ln FindPhoto.3 Tk_PhotoPutZoomedBlock.3
+fi
+if test -r FindPhoto.3; then
+ rm -f Tk_PhotoSetSize.3
+ ln FindPhoto.3 Tk_PhotoSetSize.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_PointToChar.3
+ ln TextLayout.3 Tk_PointToChar.3
+fi
+if test -r FontId.3; then
+ rm -f Tk_PostscriptFontName.3
+ ln FontId.3 Tk_PostscriptFontName.3
+fi
+if test -r QWinEvent.3; then
+ rm -f Tk_QueueWindowEvent.3
+ ln QWinEvent.3 Tk_QueueWindowEvent.3
+fi
+if test -r GetImage.3; then
+ rm -f Tk_RedrawImage.3
+ ln GetImage.3 Tk_RedrawImage.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_ReqHeight.3
+ ln WindowId.3 Tk_ReqHeight.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_ReqWidth.3
+ ln WindowId.3 Tk_ReqWidth.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_ResizeWindow.3
+ ln ConfigWind.3 Tk_ResizeWindow.3
+fi
+if test -r Restack.3; then
+ rm -f Tk_RestackWindow.3
+ ln Restack.3 Tk_RestackWindow.3
+fi
+if test -r RestrictEv.3; then
+ rm -f Tk_RestrictEvents.3
+ ln RestrictEv.3 Tk_RestrictEvents.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Screen.3
+ ln WindowId.3 Tk_Screen.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_ScreenNumber.3
+ ln WindowId.3 Tk_ScreenNumber.3
+fi
+if test -r SetAppName.3; then
+ rm -f Tk_SetAppName.3
+ ln SetAppName.3 Tk_SetAppName.3
+fi
+if test -r 3DBorder.3; then
+ rm -f Tk_SetBackgroundFromBorder.3
+ ln 3DBorder.3 Tk_SetBackgroundFromBorder.3
+fi
+if test -r SetClass.3; then
+ rm -f Tk_SetClass.3
+ ln SetClass.3 Tk_SetClass.3
+fi
+if test -r SetGrid.3; then
+ rm -f Tk_SetGrid.3
+ ln SetGrid.3 Tk_SetGrid.3
+fi
+if test -r GeomReq.3; then
+ rm -f Tk_SetInternalBorder.3
+ ln GeomReq.3 Tk_SetInternalBorder.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowBackground.3
+ ln ConfigWind.3 Tk_SetWindowBackground.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowBackgroundPixmap.3
+ ln ConfigWind.3 Tk_SetWindowBackgroundPixmap.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowBorder.3
+ ln ConfigWind.3 Tk_SetWindowBorder.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowBorderPixmap.3
+ ln ConfigWind.3 Tk_SetWindowBorderPixmap.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowBorderWidth.3
+ ln ConfigWind.3 Tk_SetWindowBorderWidth.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_SetWindowColormap.3
+ ln ConfigWind.3 Tk_SetWindowColormap.3
+fi
+if test -r SetVisual.3; then
+ rm -f Tk_SetWindowVisual.3
+ ln SetVisual.3 Tk_SetWindowVisual.3
+fi
+if test -r GetBitmap.3; then
+ rm -f Tk_SizeOfBitmap.3
+ ln GetBitmap.3 Tk_SizeOfBitmap.3
+fi
+if test -r GetImage.3; then
+ rm -f Tk_SizeOfImage.3
+ ln GetImage.3 Tk_SizeOfImage.3
+fi
+if test -r StrictMotif.3; then
+ rm -f Tk_StrictMotif.3
+ ln StrictMotif.3 Tk_StrictMotif.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_TextLayoutToPostscript.3
+ ln TextLayout.3 Tk_TextLayoutToPostscript.3
+fi
+if test -r MeasureChar.3; then
+ rm -f Tk_TextWidth.3
+ ln MeasureChar.3 Tk_TextWidth.3
+fi
+if test -r GetUid.3; then
+ rm -f Tk_Uid.3
+ ln GetUid.3 Tk_Uid.3
+fi
+if test -r ConfigWind.3; then
+ rm -f Tk_UndefineCursor.3
+ ln ConfigWind.3 Tk_UndefineCursor.3
+fi
+if test -r MeasureChar.3; then
+ rm -f Tk_UnderlineChars.3
+ ln MeasureChar.3 Tk_UnderlineChars.3
+fi
+if test -r TextLayout.3; then
+ rm -f Tk_UnderlineTextLayout.3
+ ln TextLayout.3 Tk_UnderlineTextLayout.3
+fi
+if test -r MaintGeom.3; then
+ rm -f Tk_UnmaintainGeometry.3
+ ln MaintGeom.3 Tk_UnmaintainGeometry.3
+fi
+if test -r MapWindow.3; then
+ rm -f Tk_UnmapWindow.3
+ ln MapWindow.3 Tk_UnmapWindow.3
+fi
+if test -r SetGrid.3; then
+ rm -f Tk_UnsetGrid.3
+ ln SetGrid.3 Tk_UnsetGrid.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Visual.3
+ ln WindowId.3 Tk_Visual.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Width.3
+ ln WindowId.3 Tk_Width.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_WindowId.3
+ ln WindowId.3 Tk_WindowId.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_X.3
+ ln WindowId.3 Tk_X.3
+fi
+if test -r WindowId.3; then
+ rm -f Tk_Y.3
+ ln WindowId.3 Tk_Y.3
+fi
+if test -r menubar.n; then
+ rm -f tk_bindForTraversal.n
+ ln menubar.n tk_bindForTraversal.n
+fi
+if test -r palette.n; then
+ rm -f tk_bisque.n
+ ln palette.n tk_bisque.n
+fi
+if test -r chooseColor.n; then
+ rm -f tk_chooseColor.n
+ ln chooseColor.n tk_chooseColor.n
+fi
+if test -r dialog.n; then
+ rm -f tk_dialog.n
+ ln dialog.n tk_dialog.n
+fi
+if test -r focusNext.n; then
+ rm -f tk_focusFollowsMouse.n
+ ln focusNext.n tk_focusFollowsMouse.n
+fi
+if test -r focusNext.n; then
+ rm -f tk_focusNext.n
+ ln focusNext.n tk_focusNext.n
+fi
+if test -r focusNext.n; then
+ rm -f tk_focusPrev.n
+ ln focusNext.n tk_focusPrev.n
+fi
+if test -r getOpenFile.n; then
+ rm -f tk_getOpenFile.n
+ ln getOpenFile.n tk_getOpenFile.n
+fi
+if test -r getOpenFile.n; then
+ rm -f tk_getSaveFile.n
+ ln getOpenFile.n tk_getSaveFile.n
+fi
+if test -r menubar.n; then
+ rm -f tk_menuBar.n
+ ln menubar.n tk_menuBar.n
+fi
+if test -r messageBox.n; then
+ rm -f tk_messageBox.n
+ ln messageBox.n tk_messageBox.n
+fi
+if test -r optionMenu.n; then
+ rm -f tk_optionMenu.n
+ ln optionMenu.n tk_optionMenu.n
+fi
+if test -r popup.n; then
+ rm -f tk_popup.n
+ ln popup.n tk_popup.n
+fi
+if test -r palette.n; then
+ rm -f tk_setPalette.n
+ ln palette.n tk_setPalette.n
+fi
+exit 0
diff --git a/unix/porting.notes b/unix/porting.notes
new file mode 100644
index 0000000..ecb395d
--- /dev/null
+++ b/unix/porting.notes
@@ -0,0 +1,86 @@
+This file contains a collection of notes that various people have
+provided about porting Tk to various machines and operating systems.
+I don't have personal access to any of these machines, so I make
+no guarantees that the notes are correct, complete, or up-to-date.
+If you see the word "I" in any explanations, it refers to the person
+who contributed the information, not to me; this means that I
+probably can't answer any questions about any of this stuff. In
+some cases, a person has volunteered to act as a contact point for
+questions about porting Tcl to a particular machine; in these
+cases the person's name and e-mail address are listed. I'm very
+interested in getting new porting information to add to the file;
+please mail updates to "john.ousterhout@eng.sun.com".
+
+This file reflects information provided for Tk 4.0 and later releases.
+If there is no information for your configuration in this file, check
+the file "porting.old" too; it contains information that was
+submitted for Tk 3.6 and earlier releases, and some of that information
+may still be valid.
+
+A new porting database has recently become available on the Web at
+the following URL:
+ http://www.sunlabs.com/cgi-bin/tcl/info.4.0
+This page provides information about the platforms on which Tcl 7.4
+and Tk 4.0 have been compiled and what changes were needed to get Tcl
+and Tk to compile. You can also add new entries to that database
+when you install Tcl and Tk on a new platform. The Web database is
+likely to be more up-to-date than this file.
+
+sccsid = SCCS: @(#) porting.notes 1.10 96/04/10 15:38:54
+
+--------------------------------------------
+Solaris, various versions
+--------------------------------------------
+
+1. If typing "make test" results in an error message saying that
+there are no "*.test" files, or you get lots of globbing errors,
+it's probably because your system doesn't have cc installed and
+you used gcc. In order for this to work, you have to set your
+CC environment variable to gcc and your CPP environment variable
+to "gcc -E" before running the configure script.
+
+2. Make sure that /usr/ucb is not in your PATH or LD_LIBRARY_PATH
+environment variables; this will cause confusion between the new
+Solaris libraries and older UCB versions (Tk will expect one version
+and get another).
+
+3. On 486 PCs with Solaris 2.4, when compiling with gcc 2.6.0,
+tkMessage.c appears to hang gcc. If the -O switch is removed
+then it compiles fine.
+
+--------------------------------------------
+486 PCs, Solaris 2.4
+--------------------------------------------
+
+When compiling with gcc 2.6.0, tkMessage.c appears to hang gcc.
+If the -O switch is removed then it compiles fine.
+
+--------------------------------------------
+SGI machines, IRIX 5.2, 5.3, IRIX64 6.0.1
+--------------------------------------------
+
+1. Add "-D_BSD_TIME" to CFLAGS in Makefile. This avoids type conflicts
+in the prototype for the gettimeofday procedure.
+
+2. If you're running under Irix 6.x and wish dumps core, try
+removing -O from the CFLAGS in Makefile and recompiling; compiler
+optimizations seem to cause problems on some machines.
+
+--------------------------------------------
+QNX 4.22
+--------------------------------------------
+
+All of the source files built as is. All I had to do was edit the
+Makefile generated by ./configure to specify where the X11 libraries
+were (ie, I added in -L/usr/X11/lib)
+
+--------------------------------------------
+HP-UX
+--------------------------------------------
+
+There are problems compiling Tk gcc (version 2.7.2) and the HP assembler.
+The problem is that if static functions are called using a pointer reference,
+double arguments are not transferred correctly into the function. That can
+be fixed by making all those functions global. This happens with the
+ScaleXxx() and TranslateXxx() functions for all canvas item types.
+The simplest fix is configure gcc to use the GNU assembler.
diff --git a/unix/porting.old b/unix/porting.old
new file mode 100644
index 0000000..ea8aa5c
--- /dev/null
+++ b/unix/porting.old
@@ -0,0 +1,324 @@
+This is an old version of the file "porting.notes". It contains
+porting information that people submitted for Tk releases numbered
+3.6 and earlier. You may find information in this file useful if
+there is no information available for your machine in the current
+version of "porting.notes".
+
+I don't have personal access to any of these machines, so I make
+no guarantees that the notes are correct, complete, or up-to-date.
+If you see the word "I" in any explanations, it refers to the person
+who contributed the information, not to me; this means that I
+probably can't answer any questions about any of this stuff. In
+some cases, a person has volunteered to act as a contact point for
+questions about porting Tcl to a particular machine; in these
+cases the person's name and e-mail address are listed. I'd be
+happy to receive corrections or updates.
+
+sccsid = SCCS: @(#) porting.old 1.2 96/02/16 10:27:30
+
+---------------------------------------------
+DEC Alphas:
+---------------------------------------------
+
+1. There appears to be a compiler/library bug that prevents tkTrig.c
+from compiling unless you turn off optimization (remove the -O compiler
+switch). The problem appears to have been fixed in the 1.3-4 version
+of the compiler.
+
+---------------------------------------------
+HP-UX systems:
+---------------------------------------------
+
+1. Configuration:
+ HP-UX Release 7.05 on a series 300 (68k) machine.
+ The native cc has been used for production.
+ X11r4 libraries and include files were taken from
+ internet archives, where as the server came with HPUX release 7.05.
+
+ Problems:
+ Symbol table space for cc had to be increased with: -Wc,-Ns3000
+ tkBind.c did not compile under -O:
+ C1 internal error in "GetField": Set Error Detected
+ *** Error code 1
+ tkBind.c did compile without optimization (no -O).
+
+2. Note: if you have trouble getting xauth-style access control to work
+(and you'll need xauth if you want to use "send"), be sure to uncomment
+the line
+
+# Vuelogin*authorize: True
+
+in the file /usr/vue/config/Xconfig, so that the server starts up with
+authorization enabled. Also, you may have to reboot the machine in
+order to force the server to restart.
+
+---------------------------------------------
+SCO Unix:
+---------------------------------------------
+
+Getting Tk to run under SCO Unix:
+
+Add a "#undef select" to tkEvent.c, and remove the reference to TK_EXCEPTION
+around line 460 of main.c.
+
+Tk uses its own scheme for allocating the border colors for its 3D widgets,
+which causes problems when running TK on a system with "PseudoColor"
+display class, and a 16-cell colormap.
+
+If you can't go to eight bitplanes, you can instead start the server with a
+"-static" (Xsco) or "-analog" (Xsight) option, making the display class
+become "StaticColor". This makes the entire colormap read-only, and it will
+return the color that most closely maps to the desired color as possible.
+
+---------------------------------------------
+Silicon Graphics systems:
+---------------------------------------------
+
+1. Change the CC variable in the Makefile to:
+
+CC = cc -xansi -D__STDC__ -signed
+
+2. Change the LIBS variable in the Makefile to use the X11 shared library
+ ("-lX11_s" instead of "-lX11").
+
+3. Under some versions of IRIX (e.g. 4.0.1) you have to turn off
+ optimization (e.g. change "-O" in CFLAGS to "-O0" or remove it
+ entirely) because of faulty code generation. If the Tcl or Tk test
+ suites fail, turn off optimization.
+
+4. Add a "-lsun" switch just before "-lm" in the LIBS definition.
+ Under some versions of IRIX (5.1.1.3?) you'll need to omit the
+ "-lsun" switch, plus remove the "-lsocket" and "-lnsl" switches
+ added by the configure script; otherwise you won't be able to
+ use symbolic host names for the display, only numerical Internet
+ addresses.
+
+5. Rumor has it that you'll also need a "-lmalloc" switch in the
+ LIBS definition.
+
+6. In IRIX 5.2 you'll have to modify Makefile to fix the following problems:
+ - The "-c" option is illegal with this version of install, but
+ the "-F" switch is needed instead. Change this in the "INSTALL ="
+ definition line.
+ - The order of file and directory have to be changed in all the
+ invocations of INSTALL_DATA or INSTALL_PROGRAM.
+
+---------------------------------------------
+IBM RS/6000's:
+---------------------------------------------
+1. To allow ALT- sequences to work on the RS-6000, the following
+line should be changed in tkBind.c:
+
+ OLD LINE:
+ {"Alt", Mod2Mask, 0},
+ NEW LINE:
+ {"Alt", Mod1Mask, 0},
+
+---------------------------------------------
+AT&T SVR4:
+---------------------------------------------
+
+1. The first major hurdle is that SVR4's select() subtly differs
+from BSD select. This impacts Tk in two ways, some of the Xlib calls
+make use of select() and are inherently broken and Tk itself makes
+extensive use of select(). The first problem can't be fixed without
+rebuilding one's Xlib, but can be avoided. I intend to submit part
+of my work the XFree86 guys so that the next version of XFree86 for
+SVR4 will not be broken. Until then, it is necessary to comment out
+this section of code from Tk_DoOneEvent() (which is near line 1227):
+
+#if !defined(SVR4)
+ void (*oldHandler)();
+
+ oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
+ XNoOp(display);
+ XFlush(display);
+ (void) signal(SIGPIPE, oldHandler);
+#endif /* SVR4 */
+
+if you don't comment it out, some scripts cause wish to go into
+an infinite loop of sending no-ops to the X server.
+
+2. As for fixing Tk's calls to select(), I've taken the simple
+approach of writing a wrapper for select and then using #define to
+replace all calls to select with the wrapper. I chose tkConfig.h
+to load the wrapper. So at the very end of tkConfig.h, it now looks
+like:
+
+#if defined(SVR4)
+# include "BSDselect.h"
+#endif
+
+#endif /* _TKCONFIG */
+
+The file BSDselect.h looks like this:
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/select.h>
+
+/* This is a fix for the difference between BSD's select() and
+ * SVR4's select(). SVR4's select() can never return a value larger
+ * than the total number of file descriptors being checked. So, if
+ * you select for read and write on one file descriptor, and both
+ * are true, SVR4 select() will only return 1. BSD select in the
+ * same situation will return 2.
+ *
+ * Additionally, BSD select() on timing out, will zero the masks,
+ * while SVR4 does not. This is fixed here as well.
+ *
+ * Set your tabstops to 4 characters to have this code nicely formatted.
+ *
+ * Jerry Whelan, guru@bradley.edu, June 12th, 1993
+ */
+
+
+int
+BSDselect(nfds, readfds, writefds, exceptfds, timeout)
+int nfds;
+fd_set *readfds, *writefds, *exceptfds;
+struct timeval *timeout;
+{
+ int rval,
+ i;
+
+ rval = select(nfds, readfds, writefds, exceptfds, timeout);
+
+ switch(rval) {
+ case -1: return(rval);
+ break;
+
+ case 0: if(readfds != NULL)
+ FD_ZERO(readfds);
+ if(writefds != NULL)
+ FD_ZERO(writefds);
+ if(exceptfds != NULL)
+ FD_ZERO(exceptfds);
+
+ return(rval);
+ break;
+
+ default: for(i=0, rval=0; i < nfds; i++) {
+ if((readfds != NULL) && FD_ISSET
+(i, readfds)) rval++;
+ if((writefds != NULL) && FD_ISSE
+T(i, writefds)) rval++;
+ if((writefds != NULL) && FD_ISSE
+T(i, exceptfds)) rval++;
+ }
+ return(rval);
+ }
+/* Should never get here */
+}
+
+---------------------------------------------
+CDC 4680MP, EP/IX 1.4.3:
+---------------------------------------------
+
+The installation was done in the System V environment (-systype sysv)
+with the BSD extensions available (-I/usr/include/bsd and -lbsd). It was
+built with the 2.20 level C compiler. The 2.11 level can be used, but
+it is better to match what TCL is built with, which must be 2.20 or
+higher (see the porting notes with TCL for the details).
+
+To make the configure script find the BSD extensions, I set environment
+variable DEFS to "-I/usr/include/bsd" and LIBS to "-lbsd" before
+running it. I would have also set CC to "cc2.20", but that compiler
+driver has a bug that loader errors (e.g. not finding a library routine,
+which the script uses to tell what is available) do not cause an error
+status to be returned to the shell (but see the Tcl 2.1.1 porting notes
+for comments about using "-non_shared").
+
+After running configure, I changed the CC definition line in Makefile
+from:
+ CC=cc
+to
+ CC=cc2.20
+to match the TCL build. Skip this if the default compiler is already 2.20
+(or later).
+
+---------------------------------------------
+CDC 4680MP, EP/IX 2.1.1:
+---------------------------------------------
+
+The installation was done in the System V environment (-systype sysv)
+with the BSD extensions available (-I/usr/include/bsd and -lbsd). It was
+built with the 3.11 level C compiler. Earlier levels can be used, but it
+is better to match what TCL is built with, which must be 2.20 or higher
+(see the porting notes with TCL for the details).
+
+To make the configure script find the BSD extensions, I set environment
+variable DEFS to "-I/usr/include/bsd -non_shared" and LIBS to "-lbsd"
+before running it.
+
+See the Tcl porting notes for comments on why "-non_shared" is needed
+during the configuration step. It was removed from AC_FLAGS before
+building.
+
+-------------------------------------------------
+Pyramid, OSx 5.1a (UCB universe, GCC installed):
+-------------------------------------------------
+
+Instead of typing "./configure" to configure, type
+
+ DEFS="-I/usr/include/X11/attinc" ./configure
+
+to sh to do the configuration.
+
+-------------------------------------------------
+NextSTEP 3.1:
+-------------------------------------------------
+
+1. Run configure with predefined CPP:
+ CPP='cc -E' ./configure
+ (If your shell is [t]csh, do a "setenv CPP 'cc -E'")
+
+2. Edit Makefile:
+ -add the following to AC_FLAGS:
+ -Dstrtod=tcl_strtod
+
+Note: Tk's raise test may fail when running the tvtwm window manager.
+Changing to either twm or even better fvwm ensures that this test will
+succeed.
+
+-------------------------------------------------
+Encore 91, UMAX V 3.0.9.3:
+-------------------------------------------------
+
+1. Modify the CFLAGS definition in Makefile to include -DENCORE:
+
+ CFLAGS = -O -DENCORE
+
+2. "mkdir" does not by default create the parent directories. The mkdir
+directives should be modified to "midir -p".
+
+3. An error of a redeclaration of read, can be resolved by conditionally
+not compiling if an ENCORE system.
+
+#ifndef ENCORE
+extern int read _ANSI_ARGS_((int fd, char *buf, size_t size));
+#endif
+
+-------------------------------------------------
+Sequent machines running Dynix:
+Contact: Andrew Swan (aswan@soda.berkeley.edu)
+-------------------------------------------------
+
+1. Use gcc instead of the cc distributed by Sequent
+
+2. There are problems with the distributed version of
+ <stddef.h>. The easiest solution is probably to create a
+ copy of stddef.h, make sure it comes early in the include
+ path and then edit it as need be to eliminate conflicts
+ with the X11 header files.
+
+3. The same comments about the tanh function from the notes on
+ porting Tcl apply to Tk.
+
+-------------------------------------------------
+Systems running Interactive 4.0:
+-------------------------------------------------
+
+1. Add "-posix" to CFLAGS in Makefile (or Makefile.in).
+
+2. Add "-lnsl_s" to LIBS in Makefile (or Makefile.in).
diff --git a/unix/tkAppInit.c b/unix/tkAppInit.c
new file mode 100644
index 0000000..6b6b2e2
--- /dev/null
+++ b/unix/tkAppInit.c
@@ -0,0 +1,120 @@
+/*
+ * tkAppInit.c --
+ *
+ * Provides a default version of the Tcl_AppInit procedure for
+ * use in wish and similar Tk-based applications.
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * SCCS: @(#) tkAppInit.c 1.22 96/05/29 09:47:08
+ */
+
+#include "tk.h"
+
+/*
+ * The following variable is a special hack that is needed in order for
+ * Sun shared libraries to be used for Tcl.
+ */
+
+extern int matherr();
+int *tclDummyMathPtr = (int *) matherr;
+
+#ifdef TK_TEST
+EXTERN int Tktest_Init _ANSI_ARGS_((Tcl_Interp *interp));
+#endif /* TK_TEST */
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * main --
+ *
+ * This is the main program for the application.
+ *
+ * Results:
+ * None: Tk_Main never returns here, so this procedure never
+ * returns either.
+ *
+ * Side effects:
+ * Whatever the application does.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+main(argc, argv)
+ int argc; /* Number of command-line arguments. */
+ char **argv; /* Values of command-line arguments. */
+{
+ Tk_Main(argc, argv, Tcl_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 interp->result if an error occurs.
+ *
+ * Side effects:
+ * Depends on the startup script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tcl_AppInit(interp)
+ 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);
+#ifdef TK_TEST
+ if (Tktest_Init(interp) == TCL_ERROR) {
+ return TCL_ERROR;
+ }
+ Tcl_StaticPackage(interp, "Tktest", Tktest_Init,
+ (Tcl_PackageInitProc *) NULL);
+#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.
+ */
+
+ /*
+ * Call Tcl_CreateCommand 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_SetVar(interp, "tcl_rcFileName", "~/.wishrc", TCL_GLOBAL_ONLY);
+ return TCL_OK;
+}
diff --git a/unix/tkConfig.sh.in b/unix/tkConfig.sh.in
new file mode 100644
index 0000000..544d658
--- /dev/null
+++ b/unix/tkConfig.sh.in
@@ -0,0 +1,68 @@
+# 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.
+#
+# SCCS: @(#) tkConfig.sh.in 1.11 97/10/30 13:29:13
+
+# 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@
+
+# 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='@XLIBSW@ @DL_LIBS@ @LIBS@ @MATH_LIBS@'
+
+# 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@'
+
+# -I switch(es) to use to make all of the X11 include files accessible:
+TK_XINCLUDES='@XINCLUDES@'
+
+# Linker switch(es) to use to link with the X11 library archive.
+TK_XLIBSW='@XLIBSW@'
+
+# 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@'
+
diff --git a/unix/tkUnix.c b/unix/tkUnix.c
new file mode 100644
index 0000000..ca6fa07
--- /dev/null
+++ b/unix/tkUnix.c
@@ -0,0 +1,79 @@
+/*
+ * tkUnix.c --
+ *
+ * This file contains procedures that are UNIX/X-specific, and
+ * will probably have to be written differently for Windows or
+ * Macintosh platforms.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnix.c 1.5 97/01/07 11:41:39
+ */
+
+#include <tkInt.h>
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetServerInfo --
+ *
+ * Given a window, this procedure returns information about
+ * the window server for that window. This procedure provides
+ * the guts of the "winfo server" command.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkGetServerInfo(interp, tkwin)
+ 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. */
+{
+ char buffer[50], buffer2[50];
+
+ sprintf(buffer, "X%dR%d ", ProtocolVersion(Tk_Display(tkwin)),
+ ProtocolRevision(Tk_Display(tkwin)));
+ sprintf(buffer2, " %d", VendorRelease(Tk_Display(tkwin)));
+ Tcl_AppendResult(interp, buffer, ServerVendor(Tk_Display(tkwin)),
+ buffer2, (char *) NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetDefaultScreenName --
+ *
+ * Returns the name of the screen that Tk should use during
+ * initialization.
+ *
+ * Results:
+ * Returns the argument or a string that should not be freed by
+ * the caller.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *
+TkGetDefaultScreenName(interp, screenName)
+ Tcl_Interp *interp; /* Interp used to find environment variables. */
+ char *screenName; /* Screen name from command line, or NULL. */
+{
+ if ((screenName == NULL) || (screenName[0] == '\0')) {
+ screenName = Tcl_GetVar2(interp, "env", "DISPLAY", TCL_GLOBAL_ONLY);
+ }
+ return screenName;
+}
diff --git a/unix/tkUnix3d.c b/unix/tkUnix3d.c
new file mode 100644
index 0000000..e7584ce
--- /dev/null
+++ b/unix/tkUnix3d.c
@@ -0,0 +1,448 @@
+/*
+ * tkUnix3d.c --
+ *
+ * This file contains the platform specific routines for
+ * drawing 3d borders in the Motif 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.
+ *
+ * SCCS: @(#) tkUnix3d.c 1.3 96/11/20 14:24:38
+ */
+
+#include <tk3d.h>
+
+/*
+ * This structure is used to keep track of the extra colors used
+ * by Unix 3d borders.
+ */
+
+typedef struct {
+ TkBorder info;
+ GC solidGC; /* Used to draw solid relief. */
+} UnixBorder;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetBorder --
+ *
+ * This function allocates a new TkBorder structure.
+ *
+ * Results:
+ * Returns a newly allocated TkBorder.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkBorder *
+TkpGetBorder()
+{
+ UnixBorder *borderPtr = (UnixBorder *) ckalloc(sizeof(UnixBorder));
+ borderPtr->solidGC = None;
+ 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(borderPtr)
+ TkBorder *borderPtr;
+{
+ UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
+ Display *display = DisplayOfScreen(borderPtr->screen);
+
+ if (unixBorderPtr->solidGC != None) {
+ Tk_FreeGC(display, unixBorderPtr->solidGC);
+ }
+}
+/*
+ *--------------------------------------------------------------
+ *
+ * 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(tkwin, drawable, border, x, y, width, height,
+ leftBevel, relief)
+ 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, y, width, 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;
+ GC left, right;
+ Display *display = Tk_Display(tkwin);
+
+ if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT)) {
+ TkpGetShadows(borderPtr, tkwin);
+ }
+
+ if (relief == TK_RELIEF_RAISED) {
+ XFillRectangle(display, drawable,
+ (leftBevel) ? borderPtr->lightGC : borderPtr->darkGC,
+ x, y, (unsigned) width, (unsigned) height);
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ XFillRectangle(display, drawable,
+ (leftBevel) ? borderPtr->darkGC : borderPtr->lightGC,
+ x, y, (unsigned) width, (unsigned) height);
+ } else if (relief == TK_RELIEF_RIDGE) {
+ int half;
+
+ left = borderPtr->lightGC;
+ right = borderPtr->darkGC;
+ ridgeGroove:
+ half = width/2;
+ if (!leftBevel && (width & 1)) {
+ half++;
+ }
+ XFillRectangle(display, drawable, left, x, y, (unsigned) half,
+ (unsigned) height);
+ XFillRectangle(display, drawable, right, x+half, y,
+ (unsigned) (width-half), (unsigned) height);
+ } else if (relief == TK_RELIEF_GROOVE) {
+ left = borderPtr->darkGC;
+ right = borderPtr->lightGC;
+ goto ridgeGroove;
+ } else if (relief == TK_RELIEF_FLAT) {
+ XFillRectangle(display, drawable, borderPtr->bgGC, x, y,
+ (unsigned) width, (unsigned) height);
+ } else if (relief == TK_RELIEF_SOLID) {
+ UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
+ if (unixBorderPtr->solidGC == None) {
+ XGCValues gcValues;
+
+ gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
+ unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+ }
+ XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
+ (unsigned) width, (unsigned) height);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * 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(tkwin, drawable, border, x, y, width, height,
+ leftIn, rightIn, topBevel, relief)
+ 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, y, width, height; /* Bounding box of area of bevel. Height
+ * gives width of border. */
+ int leftIn, 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;
+ UnixBorder *unixBorderPtr = (UnixBorder *) borderPtr;
+ GC topGC = None, bottomGC = None;
+ /* Initializations needed only to prevent
+ * compiler warnings. */
+
+ if ((borderPtr->lightGC == None) && (relief != TK_RELIEF_FLAT) &&
+ (relief != TK_RELIEF_SOLID)) {
+ 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_FLAT:
+ topGC = bottomGC = borderPtr->bgGC;
+ break;
+ case TK_RELIEF_GROOVE:
+ topGC = borderPtr->darkGC;
+ bottomGC = borderPtr->lightGC;
+ break;
+ case TK_RELIEF_RAISED:
+ topGC = bottomGC =
+ (topBevel) ? borderPtr->lightGC : borderPtr->darkGC;
+ break;
+ case TK_RELIEF_RIDGE:
+ topGC = borderPtr->lightGC;
+ bottomGC = borderPtr->darkGC;
+ break;
+ case TK_RELIEF_SOLID:
+ if (unixBorderPtr->solidGC == None) {
+ XGCValues gcValues;
+
+ gcValues.foreground = BlackPixelOfScreen(borderPtr->screen);
+ unixBorderPtr->solidGC = Tk_GetGC(tkwin, GCForeground,
+ &gcValues);
+ }
+ XFillRectangle(display, drawable, unixBorderPtr->solidGC, x, y,
+ (unsigned) width, (unsigned) height);
+ return;
+ case TK_RELIEF_SUNKEN:
+ topGC = bottomGC =
+ (topBevel) ? borderPtr->darkGC : borderPtr->lightGC;
+ break;
+ }
+
+ /*
+ * Compute various other geometry-related stuff.
+ */
+
+ x1 = x;
+ if (!leftIn) {
+ x1 += height;
+ }
+ x2 = x+width;
+ if (!rightIn) {
+ 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) {
+ XFillRectangle(display, drawable,
+ (y < halfway) ? topGC : bottomGC, x1, y,
+ (unsigned) (x2-x1), (unsigned) 1);
+ }
+ x1 += x1Delta;
+ x2 += x2Delta;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(borderPtr, tkwin)
+ TkBorder *borderPtr; /* Information about border. */
+ Tk_Window tkwin; /* Window where border will be used for
+ * drawing. */
+{
+ XColor lightColor, darkColor;
+ int stressed, tmp1, tmp2;
+ XGCValues gcValues;
+
+ if (borderPtr->lightGC != None) {
+ return;
+ }
+ stressed = TkpCmapStressed(tkwin, borderPtr->colormap);
+
+ /*
+ * 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 (!stressed && (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.
+ * 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).
+ */
+
+ darkColor.red = (60 * (int) borderPtr->bgColorPtr->red)/100;
+ darkColor.green = (60 * (int) borderPtr->bgColorPtr->green)/100;
+ darkColor.blue = (60 * (int) borderPtr->bgColorPtr->blue)/100;
+ borderPtr->darkColorPtr = Tk_GetColorByValue(tkwin, &darkColor);
+ gcValues.foreground = borderPtr->darkColorPtr->pixel;
+ borderPtr->darkGC = Tk_GetGC(tkwin, GCForeground, &gcValues);
+
+ /*
+ * Compute the colors using integers, not using lightColor.red
+ * etc.: these are shorts and may have problems with integer
+ * overflow.
+ */
+
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->red)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->red)/2;
+ lightColor.red = (tmp1 > tmp2) ? tmp1 : tmp2;
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->green)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->green)/2;
+ lightColor.green = (tmp1 > tmp2) ? tmp1 : tmp2;
+ tmp1 = (14 * (int) borderPtr->bgColorPtr->blue)/10;
+ if (tmp1 > MAX_INTENSITY) {
+ tmp1 = MAX_INTENSITY;
+ }
+ tmp2 = (MAX_INTENSITY + (int) borderPtr->bgColorPtr->blue)/2;
+ lightColor.blue = (tmp1 > tmp2) ? tmp1 : tmp2;
+ 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) {
+ 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.background = WhitePixelOfScreen(borderPtr->screen);
+ 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);
+ }
+}
diff --git a/unix/tkUnixButton.c b/unix/tkUnixButton.c
new file mode 100644
index 0000000..8c74dcb
--- /dev/null
+++ b/unix/tkUnixButton.c
@@ -0,0 +1,478 @@
+/*
+ * tkUnixButton.c --
+ *
+ * This file implements the Unix specific portion of the button
+ * widgets.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixButton.c 1.4 97/06/06 11:21:40
+ */
+
+#include "tkButton.h"
+
+/*
+ * Declaration of Unix specific button structure.
+ */
+
+typedef struct UnixButton {
+ TkButton info; /* Generic button info. */
+} UnixButton;
+
+/*
+ * The class procedure table for the button widgets.
+ */
+
+TkClassProcs tkpButtonProcs = {
+ NULL, /* createProc. */
+ TkButtonWorldChanged, /* geometryProc. */
+ NULL /* modalProc. */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateButton --
+ *
+ * Allocate a new TkButton structure.
+ *
+ * Results:
+ * Returns a newly allocated TkButton structure.
+ *
+ * Side effects:
+ * Registers an event handler for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkButton *
+TkpCreateButton(tkwin)
+ Tk_Window tkwin;
+{
+ UnixButton *butPtr = (UnixButton *)ckalloc(sizeof(UnixButton));
+ return (TkButton *) butPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayButton --
+ *
+ * This procedure is invoked to display a button widget. It is
+ * normally invoked as an idle handler.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the button in its
+ * current mode. The REDRAW_PENDING flag is cleared.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ 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, height;
+ 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. */
+
+ butPtr->flags &= ~REDRAW_PENDING;
+ if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+
+ border = butPtr->normalBorder;
+ if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
+ gc = butPtr->disabledGC;
+ } else if ((butPtr->state == tkActiveUid)
+ && !Tk_StrictMotif(butPtr->tkwin)) {
+ gc = butPtr->activeTextGC;
+ border = butPtr->activeBorder;
+ } else {
+ gc = butPtr->normalTextGC;
+ }
+ if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
+ && (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.
+ */
+
+ relief = butPtr->relief;
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
+ relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
+ : TK_RELIEF_RAISED;
+ }
+
+ offset = (butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin);
+
+ /*
+ * 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);
+
+ imageOrBitmap:
+ TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
+ butPtr->indicatorSpace + width, height, &x, &y);
+ x += butPtr->indicatorSpace;
+
+ x += offset;
+ y += offset;
+ if (relief == TK_RELIEF_RAISED) {
+ x -= offset;
+ y -= offset;
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ x += offset;
+ y += offset;
+ }
+ if (butPtr->image != NULL) {
+ if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
+ Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height, pixmap,
+ x, y);
+ } else {
+ Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
+ x, y);
+ }
+ } 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);
+ }
+ y += height/2;
+ } else if (butPtr->bitmap != None) {
+ Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
+ butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight,
+ &x, &y);
+
+ x += butPtr->indicatorSpace;
+
+ x += offset;
+ y += offset;
+ if (relief == TK_RELIEF_RAISED) {
+ x -= offset;
+ y -= offset;
+ } else if (relief == TK_RELIEF_SUNKEN) {
+ x += offset;
+ y += offset;
+ }
+ Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
+ x, y, 0, -1);
+ Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
+ butPtr->textLayout, x, y, butPtr->underline);
+ y += butPtr->textHeight/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) {
+ int dim;
+
+ dim = butPtr->indicatorDiameter;
+ x -= butPtr->indicatorSpace;
+ y -= dim/2;
+ if (dim > 2*butPtr->borderWidth) {
+ Tk_Draw3DRectangle(tkwin, pixmap, border, x, y, dim, dim,
+ butPtr->borderWidth,
+ (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
+ TK_RELIEF_RAISED);
+ x += butPtr->borderWidth;
+ y += butPtr->borderWidth;
+ dim -= 2*butPtr->borderWidth;
+ if (butPtr->flags & SELECTED) {
+ GC gc;
+
+ gc = Tk_3DBorderGC(tkwin,(butPtr->selectBorder != NULL)
+ ? butPtr->selectBorder : butPtr->normalBorder,
+ TK_3D_FLAT_GC);
+ XFillRectangle(butPtr->display, pixmap, gc, x, y,
+ (unsigned int) dim, (unsigned int) dim);
+ } else {
+ Tk_Fill3DRectangle(tkwin, pixmap, butPtr->normalBorder, x, y,
+ dim, dim, butPtr->borderWidth, TK_RELIEF_FLAT);
+ }
+ }
+ } else if ((butPtr->type == TYPE_RADIO_BUTTON) && butPtr->indicatorOn) {
+ XPoint points[4];
+ int radius;
+
+ radius = butPtr->indicatorDiameter/2;
+ points[0].x = x - butPtr->indicatorSpace;
+ points[0].y = y;
+ points[1].x = points[0].x + radius;
+ points[1].y = points[0].y + radius;
+ points[2].x = points[1].x + radius;
+ points[2].y = points[0].y;
+ points[3].x = points[1].x;
+ points[3].y = points[0].y - radius;
+ if (butPtr->flags & SELECTED) {
+ GC gc;
+
+ gc = Tk_3DBorderGC(tkwin, (butPtr->selectBorder != NULL)
+ ? butPtr->selectBorder : butPtr->normalBorder,
+ TK_3D_FLAT_GC);
+ XFillPolygon(butPtr->display, pixmap, gc, points, 4, Convex,
+ CoordModeOrigin);
+ } else {
+ Tk_Fill3DPolygon(tkwin, pixmap, butPtr->normalBorder, points,
+ 4, butPtr->borderWidth, TK_RELIEF_FLAT);
+ }
+ Tk_Draw3DPolygon(tkwin, pixmap, border, points, 4, butPtr->borderWidth,
+ (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN :
+ TK_RELIEF_RAISED);
+ }
+
+ /*
+ * 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.
+ */
+
+ if ((butPtr->state == tkDisabledUid)
+ && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
+ if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
+ && (butPtr->selectBorder != NULL)) {
+ XSetForeground(butPtr->display, butPtr->disabledGC,
+ Tk_3DBorderColor(butPtr->selectBorder)->pixel);
+ }
+ XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
+ butPtr->inset, butPtr->inset,
+ (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
+ (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
+ if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
+ && (butPtr->selectBorder != NULL)) {
+ XSetForeground(butPtr->display, butPtr->disabledGC,
+ 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.
+ * This code is complicated by the possible combinations of focus
+ * highlight and default rings. We draw the focus and highlight rings
+ * using the highlight border and highlight foreground color.
+ */
+
+ if (relief != TK_RELIEF_FLAT) {
+ int inset = butPtr->highlightWidth;
+ if (butPtr->defaultState == tkActiveUid) {
+ /*
+ * Draw the default ring with 2 pixels of space between the
+ * default ring and the button and the default ring and the
+ * focus ring. Note that we need to explicitly draw the space
+ * in the highlightBorder color to ensure that we overwrite any
+ * overflow text and/or a different button background color.
+ */
+
+ Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
+ inset, Tk_Width(tkwin) - 2*inset,
+ Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
+ inset += 2;
+ Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
+ inset, Tk_Width(tkwin) - 2*inset,
+ Tk_Height(tkwin) - 2*inset, 1, TK_RELIEF_SUNKEN);
+ inset++;
+ Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, inset,
+ inset, Tk_Width(tkwin) - 2*inset,
+ Tk_Height(tkwin) - 2*inset, 2, TK_RELIEF_FLAT);
+
+ inset += 2;
+ } else if (butPtr->defaultState == tkNormalUid) {
+ /*
+ * Leave room for the default ring and write over any text or
+ * background color.
+ */
+
+ Tk_Draw3DRectangle(tkwin, pixmap, butPtr->highlightBorder, 0,
+ 0, Tk_Width(tkwin),
+ Tk_Height(tkwin), 5, TK_RELIEF_FLAT);
+ inset += 5;
+ }
+
+ /*
+ * Draw the button border.
+ */
+
+ Tk_Draw3DRectangle(tkwin, pixmap, border, inset, inset,
+ Tk_Width(tkwin) - 2*inset, Tk_Height(tkwin) - 2*inset,
+ butPtr->borderWidth, relief);
+ }
+ if (butPtr->highlightWidth != 0) {
+ GC gc;
+
+ if (butPtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(butPtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(Tk_3DBorderColor(butPtr->highlightBorder),
+ pixmap);
+ }
+
+ /*
+ * Make sure the focus ring shrink-wraps the actual button, not the
+ * padding space left for a default ring.
+ */
+
+ if (butPtr->defaultState == tkNormalUid) {
+ TkDrawInsetFocusHighlight(tkwin, gc, butPtr->highlightWidth,
+ pixmap, 5);
+ } else {
+ Tk_DrawFocusHighlight(tkwin, gc, butPtr->highlightWidth, pixmap);
+ }
+ }
+
+ /*
+ * 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(butPtr)
+ register TkButton *butPtr; /* Button whose geometry may have changed. */
+{
+ int width, height, avgWidth;
+ Tk_FontMetrics fm;
+
+ if (butPtr->highlightWidth < 0) {
+ butPtr->highlightWidth = 0;
+ }
+ butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
+
+ /*
+ * Leave room for the default ring if needed.
+ */
+
+ if (butPtr->defaultState != tkDisabledUid) {
+ butPtr->inset += 5;
+ }
+ butPtr->indicatorSpace = 0;
+ if (butPtr->image != NULL) {
+ Tk_SizeOfImage(butPtr->image, &width, &height);
+ imageOrBitmap:
+ if (butPtr->width > 0) {
+ width = butPtr->width;
+ }
+ if (butPtr->height > 0) {
+ height = butPtr->height;
+ }
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
+ butPtr->indicatorSpace = height;
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ butPtr->indicatorDiameter = (65*height)/100;
+ } else {
+ butPtr->indicatorDiameter = (75*height)/100;
+ }
+ }
+ } else if (butPtr->bitmap != None) {
+ Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ Tk_FreeTextLayout(butPtr->textLayout);
+ butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
+ butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
+ &butPtr->textWidth, &butPtr->textHeight);
+
+ width = butPtr->textWidth;
+ height = butPtr->textHeight;
+ avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
+ Tk_GetFontMetrics(butPtr->tkfont, &fm);
+
+ if (butPtr->width > 0) {
+ width = butPtr->width * avgWidth;
+ }
+ if (butPtr->height > 0) {
+ height = butPtr->height * fm.linespace;
+ }
+ if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
+ butPtr->indicatorDiameter = fm.linespace;
+ if (butPtr->type == TYPE_CHECK_BUTTON) {
+ butPtr->indicatorDiameter = (80*butPtr->indicatorDiameter)/100;
+ }
+ butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
+ }
+ }
+
+ /*
+ * When issuing the geometry request, add extra space for the indicator,
+ * if any, and for the border and padding, plus two extra pixels so the
+ * display can be offset by 1 pixel in either direction for the raised
+ * or lowered effect.
+ */
+
+ if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
+ width += 2*butPtr->padX;
+ height += 2*butPtr->padY;
+ }
+ if ((butPtr->type == TYPE_BUTTON) && !Tk_StrictMotif(butPtr->tkwin)) {
+ width += 2;
+ height += 2;
+ }
+ Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
+ + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
+ Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
+}
diff --git a/unix/tkUnixColor.c b/unix/tkUnixColor.c
new file mode 100644
index 0000000..d3a5a27
--- /dev/null
+++ b/unix/tkUnixColor.c
@@ -0,0 +1,424 @@
+/*
+ * tkUnixColor.c --
+ *
+ * This file contains the platform specific color routines
+ * needed for X support.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixColor.c 1.1 96/10/22 16:52:31
+ */
+
+#include <tkColor.h>
+
+/*
+ * If a colormap fills up, attempts to allocate new colors from that
+ * colormap will fail. When that happens, we'll just choose the
+ * closest color from those that are available in the colormap.
+ * One of the following structures will be created for each "stressed"
+ * colormap to keep track of the colors that are available in the
+ * colormap (otherwise we would have to re-query from the server on
+ * each allocation, which would be very slow). These entries are
+ * flushed after a few seconds, since other clients may release or
+ * reallocate colors over time.
+ */
+
+struct TkStressedCmap {
+ Colormap colormap; /* X's token for the colormap. */
+ int numColors; /* Number of entries currently active
+ * at *colorPtr. */
+ XColor *colorPtr; /* Pointer to malloc'ed array of all
+ * colors that seem to be available in
+ * the colormap. Some may not actually
+ * be available, e.g. because they are
+ * read-write for another client; when
+ * we find this out, we remove them
+ * from the array. */
+ struct TkStressedCmap *nextPtr; /* Next in list of all stressed
+ * colormaps for the display. */
+};
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static void DeleteStressedCmap _ANSI_ARGS_((Display *display,
+ Colormap colormap));
+static void FindClosestColor _ANSI_ARGS_((Tk_Window tkwin,
+ XColor *desiredColorPtr, XColor *actualColorPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(tkColPtr)
+ TkColor *tkColPtr; /* Color to be released. Must have been
+ * allocated by TkpGetColor or
+ * TkpGetColorByValue. */
+{
+ Visual *visual;
+ Screen *screen = tkColPtr->screen;
+
+ /*
+ * Careful! Don't free black or white, since this will
+ * make some servers very unhappy. Also, there is a bug in
+ * some servers (such Sun's X11/NeWS server) where reference
+ * counting is performed incorrectly, so that if a color is
+ * allocated twice in different places and then freed twice,
+ * the second free generates an error (this bug existed as of
+ * 10/1/92). To get around this problem, ignore errors that
+ * occur during the free operation.
+ */
+
+ visual = tkColPtr->visual;
+ if ((visual->class != StaticGray) && (visual->class != StaticColor)
+ && (tkColPtr->color.pixel != BlackPixelOfScreen(screen))
+ && (tkColPtr->color.pixel != WhitePixelOfScreen(screen))) {
+ Tk_ErrorHandler handler;
+
+ handler = Tk_CreateErrorHandler(DisplayOfScreen(screen),
+ -1, -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ XFreeColors(DisplayOfScreen(screen), tkColPtr->colormap,
+ &tkColPtr->color.pixel, 1, 0L);
+ Tk_DeleteErrorHandler(handler);
+ }
+ DeleteStressedCmap(DisplayOfScreen(screen), tkColPtr->colormap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(tkwin, name)
+ 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). */
+{
+ Display *display = Tk_Display(tkwin);
+ Colormap colormap = Tk_Colormap(tkwin);
+ XColor color;
+ TkColor *tkColPtr;
+
+ /*
+ * Map from the name to a pixel value. Call XAllocNamedColor rather than
+ * XParseColor for non-# names: this saves a server round-trip for those
+ * names.
+ */
+
+ if (*name != '#') {
+ XColor screen;
+
+ if (XAllocNamedColor(display, colormap, name, &screen,
+ &color) != 0) {
+ DeleteStressedCmap(display, colormap);
+ } else {
+ /*
+ * Couldn't allocate the color. Try translating the name to
+ * a color value, to see whether the problem is a bad color
+ * name or a full colormap. If the colormap is full, then
+ * pick an approximation to the desired color.
+ */
+
+ if (XLookupColor(display, colormap, name, &color,
+ &screen) == 0) {
+ return (TkColor *) NULL;
+ }
+ FindClosestColor(tkwin, &screen, &color);
+ }
+ } else {
+ if (XParseColor(display, colormap, name, &color) == 0) {
+ return (TkColor *) NULL;
+ }
+ if (XAllocColor(display, colormap, &color) != 0) {
+ DeleteStressedCmap(display, colormap);
+ } else {
+ FindClosestColor(tkwin, &color, &color);
+ }
+ }
+
+ tkColPtr = (TkColor *) ckalloc(sizeof(TkColor));
+ tkColPtr->color = color;
+
+ return tkColPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(tkwin, colorPtr)
+ Tk_Window tkwin; /* Window in which color will be used. */
+ XColor *colorPtr; /* Red, green, and blue fields indicate
+ * desired color. */
+{
+ Display *display = Tk_Display(tkwin);
+ Colormap colormap = Tk_Colormap(tkwin);
+ TkColor *tkColPtr = (TkColor *) ckalloc(sizeof(TkColor));
+
+ tkColPtr->color.red = colorPtr->red;
+ tkColPtr->color.green = colorPtr->green;
+ tkColPtr->color.blue = colorPtr->blue;
+ if (XAllocColor(display, colormap, &tkColPtr->color) != 0) {
+ DeleteStressedCmap(display, colormap);
+ } else {
+ FindClosestColor(tkwin, &tkColPtr->color, &tkColPtr->color);
+ }
+
+ return tkColPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * FindClosestColor --
+ *
+ * When Tk can't allocate a color because a colormap has filled
+ * up, this procedure is called to find and allocate the closest
+ * available color in the colormap.
+ *
+ * Results:
+ * There is no return value, but *actualColorPtr is filled in
+ * with information about the closest available color in tkwin's
+ * colormap. This color has been allocated via X, so it must
+ * be released by the caller when the caller is done with it.
+ *
+ * Side effects:
+ * A color is allocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+FindClosestColor(tkwin, desiredColorPtr, actualColorPtr)
+ Tk_Window tkwin; /* Window where color will be used. */
+ XColor *desiredColorPtr; /* RGB values of color that was
+ * wanted (but unavailable). */
+ XColor *actualColorPtr; /* Structure to fill in with RGB and
+ * pixel for closest available
+ * color. */
+{
+ TkStressedCmap *stressPtr;
+ double tmp, distance, closestDistance;
+ int i, closest, numFound;
+ XColor *colorPtr;
+ TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr;
+ Colormap colormap = Tk_Colormap(tkwin);
+ XVisualInfo template, *visInfoPtr;
+
+ /*
+ * Find the TkStressedCmap structure for this colormap, or create
+ * a new one if needed.
+ */
+
+ for (stressPtr = dispPtr->stressPtr; ; stressPtr = stressPtr->nextPtr) {
+ if (stressPtr == NULL) {
+ stressPtr = (TkStressedCmap *) ckalloc(sizeof(TkStressedCmap));
+ stressPtr->colormap = colormap;
+ template.visualid = XVisualIDFromVisual(Tk_Visual(tkwin));
+ visInfoPtr = XGetVisualInfo(Tk_Display(tkwin),
+ VisualIDMask, &template, &numFound);
+ if (numFound < 1) {
+ panic("FindClosestColor couldn't lookup visual");
+ }
+ stressPtr->numColors = visInfoPtr->colormap_size;
+ XFree((char *) visInfoPtr);
+ stressPtr->colorPtr = (XColor *) ckalloc((unsigned)
+ (stressPtr->numColors * sizeof(XColor)));
+ for (i = 0; i < stressPtr->numColors; i++) {
+ stressPtr->colorPtr[i].pixel = (unsigned long) i;
+ }
+ XQueryColors(dispPtr->display, colormap, stressPtr->colorPtr,
+ stressPtr->numColors);
+ stressPtr->nextPtr = dispPtr->stressPtr;
+ dispPtr->stressPtr = stressPtr;
+ break;
+ }
+ if (stressPtr->colormap == colormap) {
+ break;
+ }
+ }
+
+ /*
+ * Find the color that best approximates the desired one, then
+ * try to allocate that color. If that fails, it must mean that
+ * the color was read-write (so we can't use it, since it's owner
+ * might change it) or else it was already freed. Try again,
+ * over and over again, until something succeeds.
+ */
+
+ while (1) {
+ if (stressPtr->numColors == 0) {
+ panic("FindClosestColor ran out of colors");
+ }
+ closestDistance = 1e30;
+ closest = 0;
+ for (colorPtr = stressPtr->colorPtr, i = 0; i < stressPtr->numColors;
+ colorPtr++, i++) {
+ /*
+ * Use Euclidean distance in RGB space, weighted by Y (of YIQ)
+ * as the objective function; this accounts for differences
+ * in the color sensitivity of the eye.
+ */
+
+ tmp = .30*(((int) desiredColorPtr->red) - (int) colorPtr->red);
+ distance = tmp*tmp;
+ tmp = .61*(((int) desiredColorPtr->green) - (int) colorPtr->green);
+ distance += tmp*tmp;
+ tmp = .11*(((int) desiredColorPtr->blue) - (int) colorPtr->blue);
+ distance += tmp*tmp;
+ if (distance < closestDistance) {
+ closest = i;
+ closestDistance = distance;
+ }
+ }
+ if (XAllocColor(dispPtr->display, colormap,
+ &stressPtr->colorPtr[closest]) != 0) {
+ *actualColorPtr = stressPtr->colorPtr[closest];
+ return;
+ }
+
+ /*
+ * Couldn't allocate the color. Remove it from the table and
+ * go back to look for the next best color.
+ */
+
+ stressPtr->colorPtr[closest] =
+ stressPtr->colorPtr[stressPtr->numColors-1];
+ stressPtr->numColors -= 1;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DeleteStressedCmap --
+ *
+ * This procedure releases the information cached for "colormap"
+ * so that it will be refetched from the X server the next time
+ * it is needed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The TkStressedCmap structure for colormap is deleted; the
+ * colormap is no longer considered to be "stressed".
+ *
+ * Note:
+ * This procedure is invoked whenever a color in a colormap is
+ * freed, and whenever a color allocation in a colormap succeeds.
+ * This guarantees that TkStressedCmap structures are always
+ * deleted before the corresponding Colormap is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DeleteStressedCmap(display, colormap)
+ Display *display; /* Xlib's handle for the display
+ * containing the colormap. */
+ Colormap colormap; /* Colormap to flush. */
+{
+ TkStressedCmap *prevPtr, *stressPtr;
+ TkDisplay *dispPtr = TkGetDisplay(display);
+
+ for (prevPtr = NULL, stressPtr = dispPtr->stressPtr; stressPtr != NULL;
+ prevPtr = stressPtr, stressPtr = stressPtr->nextPtr) {
+ if (stressPtr->colormap == colormap) {
+ if (prevPtr == NULL) {
+ dispPtr->stressPtr = stressPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = stressPtr->nextPtr;
+ }
+ ckfree((char *) stressPtr->colorPtr);
+ ckfree((char *) stressPtr);
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCmapStressed --
+ *
+ * Check to see whether a given colormap is known to be out
+ * of entries.
+ *
+ * Results:
+ * 1 is returned if "colormap" is stressed (i.e. it has run out
+ * of entries recently), 0 otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpCmapStressed(tkwin, colormap)
+ Tk_Window tkwin; /* Window that identifies the display
+ * containing the colormap. */
+ Colormap colormap; /* Colormap to check for stress. */
+{
+ TkStressedCmap *stressPtr;
+
+ for (stressPtr = ((TkWindow *) tkwin)->dispPtr->stressPtr;
+ stressPtr != NULL; stressPtr = stressPtr->nextPtr) {
+ if (stressPtr->colormap == colormap) {
+ return 1;
+ }
+ }
+ return 0;
+}
diff --git a/unix/tkUnixCursor.c b/unix/tkUnixCursor.c
new file mode 100644
index 0000000..da75ac6
--- /dev/null
+++ b/unix/tkUnixCursor.c
@@ -0,0 +1,407 @@
+/*
+ * tkUnixCursor.c --
+ *
+ * This file contains X specific cursor manipulation 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.
+ *
+ * SCCS: @(#) tkUnixCursor.c 1.4 96/10/08 09:33:08
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+
+/*
+ * The following data structure is a superset of the TkCursor structure
+ * defined in tkCursor.c. Each system specific cursor module will define
+ * a different cursor structure. All of these structures must have the
+ * same header consisting of the fields in TkCursor.
+ */
+
+
+
+typedef struct {
+ TkCursor info; /* Generic cursor info used by tkCursor.c */
+ Display *display; /* Display for which cursor is valid. */
+} TkUnixCursor;
+
+/*
+ * The table below is used to map from the name of a cursor to its
+ * index in the official cursor font:
+ */
+
+static struct CursorName {
+ char *name;
+ unsigned int shape;
+} cursorNames[] = {
+ {"X_cursor", XC_X_cursor},
+ {"arrow", XC_arrow},
+ {"based_arrow_down", XC_based_arrow_down},
+ {"based_arrow_up", XC_based_arrow_up},
+ {"boat", XC_boat},
+ {"bogosity", XC_bogosity},
+ {"bottom_left_corner", XC_bottom_left_corner},
+ {"bottom_right_corner", XC_bottom_right_corner},
+ {"bottom_side", XC_bottom_side},
+ {"bottom_tee", XC_bottom_tee},
+ {"box_spiral", XC_box_spiral},
+ {"center_ptr", XC_center_ptr},
+ {"circle", XC_circle},
+ {"clock", XC_clock},
+ {"coffee_mug", XC_coffee_mug},
+ {"cross", XC_cross},
+ {"cross_reverse", XC_cross_reverse},
+ {"crosshair", XC_crosshair},
+ {"diamond_cross", XC_diamond_cross},
+ {"dot", XC_dot},
+ {"dotbox", XC_dotbox},
+ {"double_arrow", XC_double_arrow},
+ {"draft_large", XC_draft_large},
+ {"draft_small", XC_draft_small},
+ {"draped_box", XC_draped_box},
+ {"exchange", XC_exchange},
+ {"fleur", XC_fleur},
+ {"gobbler", XC_gobbler},
+ {"gumby", XC_gumby},
+ {"hand1", XC_hand1},
+ {"hand2", XC_hand2},
+ {"heart", XC_heart},
+ {"icon", XC_icon},
+ {"iron_cross", XC_iron_cross},
+ {"left_ptr", XC_left_ptr},
+ {"left_side", XC_left_side},
+ {"left_tee", XC_left_tee},
+ {"leftbutton", XC_leftbutton},
+ {"ll_angle", XC_ll_angle},
+ {"lr_angle", XC_lr_angle},
+ {"man", XC_man},
+ {"middlebutton", XC_middlebutton},
+ {"mouse", XC_mouse},
+ {"pencil", XC_pencil},
+ {"pirate", XC_pirate},
+ {"plus", XC_plus},
+ {"question_arrow", XC_question_arrow},
+ {"right_ptr", XC_right_ptr},
+ {"right_side", XC_right_side},
+ {"right_tee", XC_right_tee},
+ {"rightbutton", XC_rightbutton},
+ {"rtl_logo", XC_rtl_logo},
+ {"sailboat", XC_sailboat},
+ {"sb_down_arrow", XC_sb_down_arrow},
+ {"sb_h_double_arrow", XC_sb_h_double_arrow},
+ {"sb_left_arrow", XC_sb_left_arrow},
+ {"sb_right_arrow", XC_sb_right_arrow},
+ {"sb_up_arrow", XC_sb_up_arrow},
+ {"sb_v_double_arrow", XC_sb_v_double_arrow},
+ {"shuttle", XC_shuttle},
+ {"sizing", XC_sizing},
+ {"spider", XC_spider},
+ {"spraycan", XC_spraycan},
+ {"star", XC_star},
+ {"target", XC_target},
+ {"tcross", XC_tcross},
+ {"top_left_arrow", XC_top_left_arrow},
+ {"top_left_corner", XC_top_left_corner},
+ {"top_right_corner", XC_top_right_corner},
+ {"top_side", XC_top_side},
+ {"top_tee", XC_top_tee},
+ {"trek", XC_trek},
+ {"ul_angle", XC_ul_angle},
+ {"umbrella", XC_umbrella},
+ {"ur_angle", XC_ur_angle},
+ {"watch", XC_watch},
+ {"xterm", XC_xterm},
+ {NULL, 0}
+};
+
+/*
+ * Font to use for cursors:
+ */
+
+#ifndef CURSORFONT
+#define CURSORFONT "cursor"
+#endif
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetCursorByName --
+ *
+ * Retrieve a cursor by name. Parse the cursor name into fields
+ * and create a cursor, either from the standard cursor font or
+ * from bitmap files.
+ *
+ * Results:
+ * Returns a new cursor, or NULL on errors.
+ *
+ * Side effects:
+ * Allocates a new cursor.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkCursor *
+TkGetCursorByName(interp, tkwin, string)
+ 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. */
+{
+ TkUnixCursor *cursorPtr = NULL;
+ Cursor cursor = None;
+ int argc;
+ char **argv = NULL;
+ Pixmap source = None;
+ Pixmap mask = None;
+ Display *display = Tk_Display(tkwin);
+
+ if (Tcl_SplitList(interp, string, &argc, &argv) != TCL_OK) {
+ return NULL;
+ }
+ if (argc == 0) {
+ goto badString;
+ }
+ if (argv[0][0] != '@') {
+ XColor fg, bg;
+ unsigned int maskIndex;
+ register struct CursorName *namePtr;
+ TkDisplay *dispPtr;
+
+ /*
+ * The cursor is to come from the standard cursor font. If one
+ * arg, it is cursor name (use black and white for fg and bg).
+ * If two args, they are name and fg color (ignore mask). If
+ * three args, they are name, fg, bg. Some of the code below
+ * is stolen from the XCreateFontCursor Xlib procedure.
+ */
+
+ if (argc > 3) {
+ goto badString;
+ }
+ for (namePtr = cursorNames; ; namePtr++) {
+ if (namePtr->name == NULL) {
+ goto badString;
+ }
+ if ((namePtr->name[0] == argv[0][0])
+ && (strcmp(namePtr->name, argv[0]) == 0)) {
+ break;
+ }
+ }
+ maskIndex = namePtr->shape + 1;
+ if (argc == 1) {
+ fg.red = fg.green = fg.blue = 0;
+ bg.red = bg.green = bg.blue = 65535;
+ } else {
+ if (XParseColor(display, Tk_Colormap(tkwin), argv[1],
+ &fg) == 0) {
+ Tcl_AppendResult(interp, "invalid color name \"", argv[1],
+ "\"", (char *) NULL);
+ goto cleanup;
+ }
+ if (argc == 2) {
+ bg.red = bg.green = bg.blue = 0;
+ maskIndex = namePtr->shape;
+ } else {
+ if (XParseColor(display, Tk_Colormap(tkwin), argv[2],
+ &bg) == 0) {
+ Tcl_AppendResult(interp, "invalid color name \"", argv[2],
+ "\"", (char *) NULL);
+ goto cleanup;
+ }
+ }
+ }
+ dispPtr = ((TkWindow *) tkwin)->dispPtr;
+ if (dispPtr->cursorFont == None) {
+ dispPtr->cursorFont = XLoadFont(display, CURSORFONT);
+ if (dispPtr->cursorFont == None) {
+ interp->result = "couldn't load cursor font";
+ goto cleanup;
+ }
+ }
+ cursor = XCreateGlyphCursor(display, dispPtr->cursorFont,
+ dispPtr->cursorFont, namePtr->shape, maskIndex,
+ &fg, &bg);
+ } else {
+ int width, height, maskWidth, maskHeight;
+ int xHot, yHot, dummy1, dummy2;
+ XColor fg, bg;
+
+ /*
+ * Prevent file system access in safe interpreters.
+ */
+
+ if (Tcl_IsSafe(interp)) {
+ Tcl_AppendResult(interp, "can't get cursor from a file in",
+ " a safe interpreter", (char *) NULL);
+ cursorPtr = NULL;
+ goto cleanup;
+ }
+
+ /*
+ * The cursor is to be created by reading bitmap files. There
+ * should be either two elements in the list (source, color) or
+ * four (source mask fg bg).
+ */
+
+ if ((argc != 2) && (argc != 4)) {
+ goto badString;
+ }
+ if (XReadBitmapFile(display,
+ RootWindowOfScreen(Tk_Screen(tkwin)), &argv[0][1],
+ (unsigned int *) &width, (unsigned int *) &height,
+ &source, &xHot, &yHot) != BitmapSuccess) {
+ Tcl_AppendResult(interp, "cleanup reading bitmap file \"",
+ &argv[0][1], "\"", (char *) NULL);
+ goto cleanup;
+ }
+ if ((xHot < 0) || (yHot < 0) || (xHot >= width) || (yHot >= height)) {
+ Tcl_AppendResult(interp, "bad hot spot in bitmap file \"",
+ &argv[0][1], "\"", (char *) NULL);
+ goto cleanup;
+ }
+ if (argc == 2) {
+ if (XParseColor(display, Tk_Colormap(tkwin), argv[1],
+ &fg) == 0) {
+ Tcl_AppendResult(interp, "invalid color name \"",
+ argv[1], "\"", (char *) NULL);
+ goto cleanup;
+ }
+ cursor = XCreatePixmapCursor(display, source, source,
+ &fg, &fg, (unsigned) xHot, (unsigned) yHot);
+ } else {
+ if (XReadBitmapFile(display,
+ RootWindowOfScreen(Tk_Screen(tkwin)), argv[1],
+ (unsigned int *) &maskWidth, (unsigned int *) &maskHeight,
+ &mask, &dummy1, &dummy2) != BitmapSuccess) {
+ Tcl_AppendResult(interp, "cleanup reading bitmap file \"",
+ argv[1], "\"", (char *) NULL);
+ goto cleanup;
+ }
+ if ((maskWidth != width) && (maskHeight != height)) {
+ interp->result =
+ "source and mask bitmaps have different sizes";
+ goto cleanup;
+ }
+ if (XParseColor(display, Tk_Colormap(tkwin), argv[2],
+ &fg) == 0) {
+ Tcl_AppendResult(interp, "invalid color name \"", argv[2],
+ "\"", (char *) NULL);
+ goto cleanup;
+ }
+ if (XParseColor(display, Tk_Colormap(tkwin), argv[3],
+ &bg) == 0) {
+ Tcl_AppendResult(interp, "invalid color name \"", argv[3],
+ "\"", (char *) NULL);
+ goto cleanup;
+ }
+ cursor = XCreatePixmapCursor(display, source, mask,
+ &fg, &bg, (unsigned) xHot, (unsigned) yHot);
+ }
+ }
+
+ if (cursor != None) {
+ cursorPtr = (TkUnixCursor *) ckalloc(sizeof(TkUnixCursor));
+ cursorPtr->info.cursor = (Tk_Cursor) cursor;
+ cursorPtr->display = display;
+ }
+
+ cleanup:
+ if (argv != NULL) {
+ ckfree((char *) argv);
+ }
+ if (source != None) {
+ Tk_FreePixmap(display, source);
+ }
+ if (mask != None) {
+ Tk_FreePixmap(display, mask);
+ }
+ return (TkCursor *) cursorPtr;
+
+
+ badString:
+ Tcl_AppendResult(interp, "bad cursor spec \"", string, "\"",
+ (char *) NULL);
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(tkwin, source, mask, width, height, xHot, yHot,
+ fgColor, bgColor)
+ Tk_Window tkwin; /* Window in which cursor will be used. */
+ char *source; /* Bitmap data for cursor shape. */
+ char *mask; /* Bitmap data for cursor mask. */
+ int width, height; /* Dimensions of cursor. */
+ int xHot, yHot; /* Location of hot-spot in cursor. */
+ XColor fgColor; /* Foreground color for cursor. */
+ XColor bgColor; /* Background color for cursor. */
+{
+ Cursor cursor;
+ Pixmap sourcePixmap, maskPixmap;
+ TkUnixCursor *cursorPtr = NULL;
+ Display *display = Tk_Display(tkwin);
+
+ sourcePixmap = XCreateBitmapFromData(display,
+ RootWindowOfScreen(Tk_Screen(tkwin)), source, (unsigned) width,
+ (unsigned) height);
+ maskPixmap = XCreateBitmapFromData(display,
+ RootWindowOfScreen(Tk_Screen(tkwin)), mask, (unsigned) width,
+ (unsigned) height);
+ cursor = XCreatePixmapCursor(display, sourcePixmap,
+ maskPixmap, &fgColor, &bgColor, (unsigned) xHot, (unsigned) yHot);
+ Tk_FreePixmap(display, sourcePixmap);
+ Tk_FreePixmap(display, maskPixmap);
+
+ if (cursor != None) {
+ cursorPtr = (TkUnixCursor *) ckalloc(sizeof(TkUnixCursor));
+ cursorPtr->info.cursor = (Tk_Cursor) cursor;
+ cursorPtr->display = display;
+ }
+ return (TkCursor *) cursorPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkFreeCursor --
+ *
+ * This procedure is called to release a cursor allocated by
+ * TkGetCursorByName.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The cursor data structure is deallocated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkFreeCursor(cursorPtr)
+ TkCursor *cursorPtr;
+{
+ TkUnixCursor *unixCursorPtr = (TkUnixCursor *) cursorPtr;
+ XFreeCursor(unixCursorPtr->display, (Cursor) unixCursorPtr->info.cursor);
+ Tk_FreeXId(unixCursorPtr->display, (XID) unixCursorPtr->info.cursor);
+ ckfree((char *) unixCursorPtr);
+}
diff --git a/unix/tkUnixDefault.h b/unix/tkUnixDefault.h
new file mode 100644
index 0000000..f895d63
--- /dev/null
+++ b/unix/tkUnixDefault.h
@@ -0,0 +1,450 @@
+/*
+ * tkUnixDefault.h --
+ *
+ * This file defines the defaults for all options for all of
+ * the Tk widgets.
+ *
+ * Copyright (c) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * SCCS: @(#) tkUnixDefault.h 1.105 97/10/09 17:45:10
+ */
+
+#ifndef _TKUNIXDEFAULT
+#define _TKUNIXDEFAULT
+
+/*
+ * 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 "#d9d9d9"
+#define ACTIVE_BG "#ececec"
+#define SELECT_BG "#c3c3c3"
+#define TROUGH "#c3c3c3"
+#define INDICATOR "#b03060"
+#define DISABLED "#a3a3a3"
+
+/*
+ * Defaults for labels, buttons, checkbuttons, and radiobuttons:
+ */
+
+#define DEF_BUTTON_ANCHOR "center"
+#define DEF_BUTTON_ACTIVE_BG_COLOR ACTIVE_BG
+#define DEF_BUTTON_ACTIVE_BG_MONO BLACK
+#define DEF_BUTTON_ACTIVE_FG_COLOR BLACK
+#define DEF_CHKRAD_ACTIVE_FG_COLOR DEF_BUTTON_ACTIVE_FG_COLOR
+#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_DEFAULT "disabled"
+#define DEF_BUTTON_DISABLED_FG_COLOR DISABLED
+#define DEF_BUTTON_DISABLED_FG_MONO ""
+#define DEF_BUTTON_FG BLACK
+#define DEF_CHKRAD_FG DEF_BUTTON_FG
+#define DEF_BUTTON_FONT "Helvetica -12 bold"
+#define DEF_BUTTON_HEIGHT "0"
+#define DEF_BUTTON_HIGHLIGHT_BG NORMAL_BG
+#define DEF_BUTTON_HIGHLIGHT BLACK
+#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_PADX "3m"
+#define DEF_LABCHKRAD_PADX "1"
+#define DEF_BUTTON_PADY "1m"
+#define DEF_LABCHKRAD_PADY "1"
+#define DEF_BUTTON_RELIEF "raised"
+#define DEF_LABCHKRAD_RELIEF "flat"
+#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_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 BLACK
+#define DEF_CANVAS_HIGHLIGHT_WIDTH "1"
+#define DEF_CANVAS_INSERT_BG BLACK
+#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 BLACK
+#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 NORMAL_BG
+#define DEF_ENTRY_BG_MONO WHITE
+#define DEF_ENTRY_BORDER_WIDTH "2"
+#define DEF_ENTRY_CURSOR "xterm"
+#define DEF_ENTRY_EXPORT_SELECTION "1"
+#define DEF_ENTRY_FONT "Helvetica -12"
+#define DEF_ENTRY_FG BLACK
+#define DEF_ENTRY_HIGHLIGHT_BG NORMAL_BG
+#define DEF_ENTRY_HIGHLIGHT BLACK
+#define DEF_ENTRY_HIGHLIGHT_WIDTH "1"
+#define DEF_ENTRY_INSERT_BG BLACK
+#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_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 "1"
+#define DEF_ENTRY_SELECT_BD_MONO "0"
+#define DEF_ENTRY_SELECT_FG_COLOR BLACK
+#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 BLACK
+#define DEF_FRAME_HIGHLIGHT_WIDTH "0"
+#define DEF_FRAME_RELIEF "flat"
+#define DEF_FRAME_TAKE_FOCUS "0"
+#define DEF_FRAME_USE ""
+#define DEF_FRAME_VISUAL ""
+#define DEF_FRAME_WIDTH "0"
+
+/*
+ * Defaults for listboxes:
+ */
+
+#define DEF_LISTBOX_BG_COLOR NORMAL_BG
+#define DEF_LISTBOX_BG_MONO WHITE
+#define DEF_LISTBOX_BORDER_WIDTH "2"
+#define DEF_LISTBOX_CURSOR ""
+#define DEF_LISTBOX_EXPORT_SELECTION "1"
+#define DEF_LISTBOX_FONT "Helvetica -12 bold"
+#define DEF_LISTBOX_FG BLACK
+#define DEF_LISTBOX_HEIGHT "10"
+#define DEF_LISTBOX_HIGHLIGHT_BG NORMAL_BG
+#define DEF_LISTBOX_HIGHLIGHT BLACK
+#define DEF_LISTBOX_HIGHLIGHT_WIDTH "1"
+#define DEF_LISTBOX_RELIEF "sunken"
+#define DEF_LISTBOX_SCROLL_COMMAND ""
+#define DEF_LISTBOX_SELECT_COLOR SELECT_BG
+#define DEF_LISTBOX_SELECT_MONO BLACK
+#define DEF_LISTBOX_SELECT_BD "1"
+#define DEF_LISTBOX_SELECT_FG_COLOR BLACK
+#define DEF_LISTBOX_SELECT_FG_MONO WHITE
+#define DEF_LISTBOX_SELECT_MODE "browse"
+#define DEF_LISTBOX_SET_GRID "0"
+#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_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 ACTIVE_BG
+#define DEF_MENU_ACTIVE_BG_MONO BLACK
+#define DEF_MENU_ACTIVE_BORDER_WIDTH "2"
+#define DEF_MENU_ACTIVE_FG_COLOR BLACK
+#define DEF_MENU_ACTIVE_FG_MONO WHITE
+#define DEF_MENU_BG_COLOR NORMAL_BG
+#define DEF_MENU_BG_MONO WHITE
+#define DEF_MENU_BORDER_WIDTH "2"
+#define DEF_MENU_CURSOR "arrow"
+#define DEF_MENU_DISABLED_FG_COLOR DISABLED
+#define DEF_MENU_DISABLED_FG_MONO ""
+#define DEF_MENU_FONT "Helvetica -12 bold"
+#define DEF_MENU_FG BLACK
+#define DEF_MENU_POST_COMMAND ""
+#define DEF_MENU_RELIEF "raised"
+#define DEF_MENU_SELECT_COLOR INDICATOR
+#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 BLACK
+#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 "2"
+#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 "Helvetica -12 bold"
+#define DEF_MENUBUTTON_FG BLACK
+#define DEF_MENUBUTTON_HEIGHT "0"
+#define DEF_MENUBUTTON_HIGHLIGHT_BG NORMAL_BG
+#define DEF_MENUBUTTON_HIGHLIGHT BLACK
+#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 "2"
+#define DEF_MESSAGE_CURSOR ""
+#define DEF_MESSAGE_FG BLACK
+#define DEF_MESSAGE_FONT "Helvetica -12 bold"
+#define DEF_MESSAGE_HIGHLIGHT_BG NORMAL_BG
+#define DEF_MESSAGE_HIGHLIGHT BLACK
+#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 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 "2"
+#define DEF_SCALE_COMMAND ""
+#define DEF_SCALE_CURSOR ""
+#define DEF_SCALE_DIGITS "0"
+#define DEF_SCALE_FONT "Helvetica -12 bold"
+#define DEF_SCALE_FG_COLOR BLACK
+#define DEF_SCALE_FG_MONO BLACK
+#define DEF_SCALE_FROM "0"
+#define DEF_SCALE_HIGHLIGHT_BG NORMAL_BG
+#define DEF_SCALE_HIGHLIGHT BLACK
+#define DEF_SCALE_HIGHLIGHT_WIDTH "1"
+#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 "2"
+#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 BLACK
+#define DEF_SCROLLBAR_HIGHLIGHT_WIDTH "1"
+#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 "15"
+
+/*
+ * Defaults for texts:
+ */
+
+#define DEF_TEXT_BG_COLOR NORMAL_BG
+#define DEF_TEXT_BG_MONO WHITE
+#define DEF_TEXT_BORDER_WIDTH "2"
+#define DEF_TEXT_CURSOR "xterm"
+#define DEF_TEXT_FG BLACK
+#define DEF_TEXT_EXPORT_SELECTION "1"
+#define DEF_TEXT_FONT "Courier -12"
+#define DEF_TEXT_HEIGHT "24"
+#define DEF_TEXT_HIGHLIGHT_BG NORMAL_BG
+#define DEF_TEXT_HIGHLIGHT BLACK
+#define DEF_TEXT_HIGHLIGHT_WIDTH "1"
+#define DEF_TEXT_INSERT_BG BLACK
+#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_WIDTH "2"
+#define DEF_TEXT_PADX "1"
+#define DEF_TEXT_PADY "1"
+#define DEF_TEXT_RELIEF "sunken"
+#define DEF_TEXT_SELECT_COLOR SELECT_BG
+#define DEF_TEXT_SELECT_MONO BLACK
+#define DEF_TEXT_SELECT_BD_COLOR "1"
+#define DEF_TEXT_SELECT_BD_MONO "0"
+#define DEF_TEXT_SELECT_FG_COLOR BLACK
+#define DEF_TEXT_SELECT_FG_MONO WHITE
+#define DEF_TEXT_SELECT_RELIEF "raised"
+#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_TAKE_FOCUS (char *) NULL
+#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 "Helvetica -12"
+
+/*
+ * 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 ""
+
+#endif /* _TKUNIXDEFAULT */
diff --git a/unix/tkUnixDialog.c b/unix/tkUnixDialog.c
new file mode 100644
index 0000000..b8a1ab0
--- /dev/null
+++ b/unix/tkUnixDialog.c
@@ -0,0 +1,207 @@
+/*
+ * tkUnixDialog.c --
+ *
+ * Contains the Unix implementation of the common dialog boxes:
+ *
+ * Copyright (c) 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.
+ *
+ * SCCS: @(#) tkUnixDialog.c 1.5 96/08/28 21:21:01
+ *
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+#include "tkUnixInt.h"
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EvalArgv --
+ *
+ * Invokes the Tcl procedure with the arguments. argv[0] is set by
+ * the caller of this function. It may be different than cmdName.
+ * The TCL command will see argv[0], not cmdName, as its name if it
+ * invokes [lindex [info level 0] 0]
+ *
+ * Results:
+ * TCL_ERROR if the command does not exist and cannot be autoloaded.
+ * Otherwise, return the result of the evaluation of the command.
+ *
+ * Side effects:
+ * The command may be autoloaded.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int EvalArgv(interp, cmdName, argc, argv)
+ Tcl_Interp *interp; /* Current interpreter. */
+ char * cmdName; /* Name of the TCL command to call */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tcl_CmdInfo cmdInfo;
+
+ if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
+ char * cmdArgv[2];
+
+ /*
+ * This comand is not in the interpreter yet -- looks like we
+ * have to auto-load it
+ */
+ if (!Tcl_GetCommandInfo(interp, "auto_load", &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot execute command \"auto_load\"",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ cmdArgv[0] = "auto_load";
+ cmdArgv[1] = cmdName;
+
+ if ((*cmdInfo.proc)(cmdInfo.clientData, interp, 2, cmdArgv)!= TCL_OK){
+ return TCL_ERROR;
+ }
+
+ if (!Tcl_GetCommandInfo(interp, cmdName, &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot auto-load command \"",
+ cmdName, "\"",NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ return (*cmdInfo.proc)(cmdInfo.clientData, interp, argc, argv);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_ChooseColorCmd --
+ *
+ * This procedure implements the color dialog box for the Unix
+ * 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 procedure is called.
+ * This window is not destroyed and will be reused the next time the
+ * application invokes the "tk_chooseColor" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_ChooseColorCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ return EvalArgv(interp, "tkColorDialog", argc, argv);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetOpenFileCmd --
+ *
+ * This procedure implements the "open file" dialog box for the
+ * Unix 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 procedure is called.
+ * This window is not destroyed and will be reused the next time
+ * the application invokes the "tk_getOpenFile" or
+ * "tk_getSaveFile" command.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_GetOpenFileCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window)clientData;
+
+ if (Tk_StrictMotif(tkwin)) {
+ return EvalArgv(interp, "tkMotifFDialog", argc, argv);
+ } else {
+ return EvalArgv(interp, "tkFDialog", argc, argv);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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_GetSaveFileCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window)clientData;
+
+ if (Tk_StrictMotif(tkwin)) {
+ return EvalArgv(interp, "tkMotifFDialog", argc, argv);
+ } else {
+ return EvalArgv(interp, "tkFDialog", argc, argv);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MessageBoxCmd --
+ *
+ * This procedure implements the MessageBox window for the
+ * Unix 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 procedure
+ * returns.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+Tk_MessageBoxCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ return EvalArgv(interp, "tkMessageBox", argc, argv);
+}
+
diff --git a/unix/tkUnixDraw.c b/unix/tkUnixDraw.c
new file mode 100644
index 0000000..65bf2e4
--- /dev/null
+++ b/unix/tkUnixDraw.c
@@ -0,0 +1,171 @@
+/*
+ * tkUnixDraw.c --
+ *
+ * This file contains X specific drawing 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.
+ *
+ * SCCS: @(#) tkUnixDraw.c 1.9 97/03/21 11:16:18
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+
+/*
+ * The following structure is used to pass information to
+ * ScrollRestrictProc from TkScrollWindow.
+ */
+
+typedef struct ScrollInfo {
+ int done; /* Flag is 0 until filtering is done. */
+ Display *display; /* Display to filter. */
+ Window window; /* Window to filter. */
+ TkRegion region; /* Region into which damage is accumulated. */
+ int dx, dy; /* Amount by which window was shifted. */
+} ScrollInfo;
+
+/*
+ * Forward declarations for procedures declared later in this file:
+ */
+
+static Tk_RestrictAction ScrollRestrictProc _ANSI_ARGS_((
+ ClientData arg, XEvent *eventPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkScrollWindow --
+ *
+ * Scroll a rectangle of the specified window and accumulate
+ * damage information in the specified Region.
+ *
+ * Results:
+ * Returns 0 if no damage additional damage was generated. Sets
+ * damageRgn to contain the damaged areas and returns 1 if
+ * GraphicsExpose events were detected.
+ *
+ * Side effects:
+ * Scrolls the bits in the window and enters the event loop
+ * looking for damage events.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkScrollWindow(tkwin, gc, x, y, width, height, dx, dy, damageRgn)
+ Tk_Window tkwin; /* The window to be scrolled. */
+ GC gc; /* GC for window to be scrolled. */
+ int x, y, width, height; /* Position rectangle to be scrolled. */
+ int dx, dy; /* Distance rectangle should be moved. */
+ TkRegion damageRgn; /* Region to accumulate damage in. */
+{
+ Tk_RestrictProc *oldProc;
+ ClientData oldArg, dummy;
+ ScrollInfo info;
+
+ XCopyArea(Tk_Display(tkwin), Tk_WindowId(tkwin), Tk_WindowId(tkwin), gc,
+ x, y, (unsigned int) width, (unsigned int) height, x + dx, y + dy);
+
+ info.done = 0;
+ info.window = Tk_WindowId(tkwin);
+ info.display = Tk_Display(tkwin);
+ info.region = damageRgn;
+ info.dx = dx;
+ info.dy = dy;
+
+ /*
+ * Sync the event stream so all of the expose events will be on the
+ * Tk event queue before we start filtering. This avoids busy waiting
+ * while we filter events.
+ */
+
+ TkpSync(info.display);
+ oldProc = Tk_RestrictEvents(ScrollRestrictProc, (ClientData) &info,
+ &oldArg);
+ while (!info.done) {
+ Tcl_ServiceEvent(TCL_WINDOW_EVENTS);
+ }
+ Tk_RestrictEvents(oldProc, oldArg, &dummy);
+
+ return XEmptyRegion((Region) damageRgn) ? 0 : 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ScrollRestrictProc --
+ *
+ * A Tk_RestrictProc used by TkScrollWindow to gather up Expose
+ * information into a single damage region. It accumulates damage
+ * events on the specified window until a NoExpose or the last
+ * GraphicsExpose event is detected.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Discards Expose events after accumulating damage information
+ * for a particular window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tk_RestrictAction
+ScrollRestrictProc(arg, eventPtr)
+ ClientData arg;
+ XEvent *eventPtr;
+{
+ ScrollInfo *info = (ScrollInfo *) arg;
+ XRectangle rect;
+
+ /*
+ * Defer events which aren't for the specified window.
+ */
+
+ if (info->done || (eventPtr->xany.display != info->display)
+ || (eventPtr->xany.window != info->window)) {
+ return TK_DEFER_EVENT;
+ }
+
+ if (eventPtr->type == NoExpose) {
+ info->done = 1;
+ } else if (eventPtr->type == GraphicsExpose) {
+ rect.x = eventPtr->xgraphicsexpose.x;
+ rect.y = eventPtr->xgraphicsexpose.y;
+ rect.width = eventPtr->xgraphicsexpose.width;
+ rect.height = eventPtr->xgraphicsexpose.height;
+ XUnionRectWithRegion(&rect, (Region) info->region,
+ (Region) info->region);
+
+ if (eventPtr->xgraphicsexpose.count == 0) {
+ info->done = 1;
+ }
+ } else if (eventPtr->type == Expose) {
+
+ /*
+ * This case is tricky. This event was already queued before
+ * the XCopyArea was issued. If this area overlaps the area
+ * being copied, then some of the copied area may be invalid.
+ * The easiest way to handle this case is to mark both the
+ * original area and the shifted area as damaged.
+ */
+
+ rect.x = eventPtr->xexpose.x;
+ rect.y = eventPtr->xexpose.y;
+ rect.width = eventPtr->xexpose.width;
+ rect.height = eventPtr->xexpose.height;
+ XUnionRectWithRegion(&rect, (Region) info->region,
+ (Region) info->region);
+ rect.x += info->dx;
+ rect.y += info->dy;
+ XUnionRectWithRegion(&rect, (Region) info->region,
+ (Region) info->region);
+ } else {
+ return TK_DEFER_EVENT;
+ }
+ return TK_DISCARD_EVENT;
+}
+
diff --git a/unix/tkUnixEmbed.c b/unix/tkUnixEmbed.c
new file mode 100644
index 0000000..fd20a34
--- /dev/null
+++ b/unix/tkUnixEmbed.c
@@ -0,0 +1,1001 @@
+/*
+ * tkUnixEmbed.c --
+ *
+ * This file contains platform-specific procedures for UNIX to provide
+ * basic operations needed for application embedding (where one
+ * application can use as its main window an internal window from
+ * some other 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.
+ *
+ * SCCS: @(#) tkUnixEmbed.c 1.22 97/08/13 11:15:51
+ */
+
+#include "tkInt.h"
+#include "tkUnixInt.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 {
+ Window parent; /* X's window id for the parent of
+ * the pair (the container). */
+ Window parentRoot; /* Id for the root window of parent's
+ * screen. */
+ TkWindow *parentPtr; /* Tk's information about the container,
+ * or NULL if the container isn't
+ * in this process. */
+ Window wrapper; /* X's window id for the wrapper
+ * window for the embedded window.
+ * Starts off as None, but gets
+ * filled in when the window is
+ * eventually created. */
+ TkWindow *embeddedPtr; /* Tk's information about the embedded
+ * window, or NULL if the embedded
+ * application isn't in this process.
+ * Note that this is *not* the
+ * same window as wrapper: wrapper is
+ * the parent of embeddedPtr. */
+ struct Container *nextPtr; /* Next in list of all containers in
+ * this process. */
+} Container;
+
+static Container *firstContainerPtr = NULL;
+ /* First in list of all containers
+ * managed by this process. */
+
+/*
+ * Prototypes for static procedures defined in this file:
+ */
+
+static void ContainerEventProc _ANSI_ARGS_((
+ ClientData clientData, XEvent *eventPtr));
+static void EmbeddedEventProc _ANSI_ARGS_((
+ ClientData clientData, XEvent *eventPtr));
+static int EmbedErrorProc _ANSI_ARGS_((ClientData clientData,
+ XErrorEvent *errEventPtr));
+static void EmbedFocusProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void EmbedGeometryRequest _ANSI_ARGS_((
+ Container * containerPtr, int width, int height));
+static void EmbedSendConfigure _ANSI_ARGS_((
+ Container *containerPtr));
+static void EmbedStructureProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void EmbedWindowDeleted _ANSI_ARGS_((TkWindow *winPtr));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpUseWindow --
+ *
+ * This procedure causes a Tk window to use a given X window as
+ * its parent window, rather than the root window for the screen.
+ * It is invoked by an embedded application to specify the window
+ * in which it is embedded.
+ *
+ * Results:
+ * The return value is normally TCL_OK. If an error occurs (such
+ * as string not being a valid window spec), then the return value
+ * is TCL_ERROR and an error message is left in interp->result if
+ * interp is non-NULL.
+ *
+ * Side effects:
+ * Changes the colormap and other visual information to match that
+ * of the parent window given by "string".
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpUseWindow(interp, tkwin, string)
+ 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. */
+ char *string; /* String identifying an X window to use
+ * for tkwin; must be an integer value. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ int id, anyError;
+ Window parent;
+ Tk_ErrorHandler handler;
+ Container *containerPtr;
+ XWindowAttributes parentAtts;
+
+ if (winPtr->window != None) {
+ panic("TkUseWindow: X window already assigned");
+ }
+ if (Tcl_GetInt(interp, string, &id) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ parent = (Window) id;
+
+ /*
+ * Tk sets the window colormap to the screen default colormap in
+ * tkWindow.c:AllocWindow. This doesn't work well for embedded
+ * windows. So we override the colormap and visual settings to be
+ * the same as the parent window (which is in the container app).
+ */
+
+ anyError = 0;
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ EmbedErrorProc, (ClientData) &anyError);
+ if (!XGetWindowAttributes(winPtr->display, parent, &parentAtts)) {
+ anyError = 1;
+ }
+ XSync(winPtr->display, False);
+ Tk_DeleteErrorHandler(handler);
+ if (anyError) {
+ if (interp != NULL) {
+ Tcl_AppendResult(interp, "couldn't create child of window \"",
+ string, "\"", (char *) NULL);
+ }
+ return TCL_ERROR;
+ }
+ Tk_SetWindowVisual(tkwin, parentAtts.visual, parentAtts.depth,
+ parentAtts.colormap);
+
+ /*
+ * Create an event handler to clean up the Container structure when
+ * tkwin is eventually deleted.
+ */
+
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbeddedEventProc,
+ (ClientData) winPtr);
+
+ /*
+ * Save information about the container and the embedded window
+ * in a Container structure. If there is already an existing
+ * Container structure, it means that both container and embedded
+ * app. are in the same process.
+ */
+
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr->parent == parent) {
+ winPtr->flags |= TK_BOTH_HALVES;
+ containerPtr->parentPtr->flags |= TK_BOTH_HALVES;
+ break;
+ }
+ }
+ if (containerPtr == NULL) {
+ containerPtr = (Container *) ckalloc(sizeof(Container));
+ containerPtr->parent = parent;
+ containerPtr->parentRoot = parentAtts.root;
+ containerPtr->parentPtr = NULL;
+ containerPtr->wrapper = None;
+ containerPtr->nextPtr = firstContainerPtr;
+ firstContainerPtr = containerPtr;
+ }
+ containerPtr->embeddedPtr = winPtr;
+ winPtr->flags |= TK_EMBEDDED;
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMakeWindow --
+ *
+ * Create an actual window system window object based on the
+ * current attributes of the specified TkWindow.
+ *
+ * Results:
+ * Returns the handle to the new window, or None on failure.
+ *
+ * Side effects:
+ * Creates a new X window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Window
+TkpMakeWindow(winPtr, parent)
+ TkWindow *winPtr; /* Tk's information about the window that
+ * is to be instantiated. */
+ Window parent; /* Window system token for the parent in
+ * which the window is to be created. */
+{
+ Container *containerPtr;
+
+ if (winPtr->flags & TK_EMBEDDED) {
+ /*
+ * This window is embedded. Don't create the new window in the
+ * given parent; instead, create it as a child of the root window
+ * of the container's screen. The window will get reparented
+ * into a wrapper window later.
+ */
+
+ for (containerPtr = firstContainerPtr; ;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr == NULL) {
+ panic("TkMakeWindow couldn't find container for window");
+ }
+ if (containerPtr->embeddedPtr == winPtr) {
+ break;
+ }
+ }
+ parent = containerPtr->parentRoot;
+ }
+
+ return XCreateWindow(winPtr->display, parent, winPtr->changes.x,
+ winPtr->changes.y, (unsigned) winPtr->changes.width,
+ (unsigned) winPtr->changes.height,
+ (unsigned) winPtr->changes.border_width, winPtr->depth,
+ InputOutput, winPtr->visual, winPtr->dirtyAtts,
+ &winPtr->atts);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(tkwin)
+ Tk_Window tkwin; /* Token for a window that is about to
+ * become a container. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ Container *containerPtr;
+
+ /*
+ * 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 = (Container *) ckalloc(sizeof(Container));
+ containerPtr->parent = Tk_WindowId(tkwin);
+ containerPtr->parentRoot = RootWindowOfScreen(Tk_Screen(tkwin));
+ containerPtr->parentPtr = winPtr;
+ containerPtr->wrapper = None;
+ containerPtr->embeddedPtr = NULL;
+ containerPtr->nextPtr = firstContainerPtr;
+ firstContainerPtr = containerPtr;
+ winPtr->flags |= TK_CONTAINER;
+
+ /*
+ * Request SubstructureNotify events so that we can find out when
+ * the embedded application creates its window or attempts to
+ * resize it. Also watch Configure events on the container so that
+ * we can resize the child to match.
+ */
+
+ winPtr->atts.event_mask |= SubstructureRedirectMask|SubstructureNotifyMask;
+ XSelectInput(winPtr->display, winPtr->window, winPtr->atts.event_mask);
+ Tk_CreateEventHandler(tkwin,
+ SubstructureNotifyMask|SubstructureRedirectMask,
+ ContainerEventProc, (ClientData) winPtr);
+ Tk_CreateEventHandler(tkwin, StructureNotifyMask, EmbedStructureProc,
+ (ClientData) containerPtr);
+ Tk_CreateEventHandler(tkwin, FocusChangeMask, EmbedFocusProc,
+ (ClientData) containerPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedErrorProc --
+ *
+ * This procedure is invoked if an error occurs while creating
+ * an embedded window.
+ *
+ * Results:
+ * Always returns 0 to indicate that the error has been properly
+ * handled.
+ *
+ * Side effects:
+ * The integer pointed to by the clientData argument is set to 1.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+EmbedErrorProc(clientData, errEventPtr)
+ ClientData clientData; /* Points to integer to set. */
+ XErrorEvent *errEventPtr; /* Points to information about error
+ * (not used). */
+{
+ int *iPtr = (int *) clientData;
+
+ *iPtr = 1;
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbeddedEventProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when various
+ * useful events are received for a window that is embedded in
+ * another application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Our internal state gets cleaned up when an embedded window is
+ * destroyed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbeddedEventProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ TkWindow *winPtr = (TkWindow *) clientData;
+
+ if (eventPtr->type == DestroyNotify) {
+ EmbedWindowDeleted(winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ContainerEventProc --
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ContainerEventProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ TkWindow *winPtr = (TkWindow *) clientData;
+ Container *containerPtr;
+ Tk_ErrorHandler errHandler;
+
+ /*
+ * Ignore any X protocol errors that happen in this procedure
+ * (almost any operation could fail, for example, if the embedded
+ * application has deleted its window).
+ */
+
+ errHandler = Tk_CreateErrorHandler(eventPtr->xfocus.display, -1,
+ -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
+
+ /*
+ * Find the Container structure associated with the parent window.
+ */
+
+ for (containerPtr = firstContainerPtr;
+ containerPtr->parent != eventPtr->xmaprequest.parent;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr == NULL) {
+ panic("ContainerEventProc couldn't find Container record");
+ }
+ }
+
+ if (eventPtr->type == CreateNotify) {
+ /*
+ * A new child window has been created in the container. Record
+ * its id in the Container structure (if more than one child is
+ * created, just remember the last one and ignore the earlier
+ * ones). Also set the child's size to match the container.
+ */
+
+ containerPtr->wrapper = eventPtr->xcreatewindow.window;
+ XMoveResizeWindow(eventPtr->xcreatewindow.display,
+ containerPtr->wrapper, 0, 0,
+ (unsigned int) Tk_Width(
+ (Tk_Window) containerPtr->parentPtr),
+ (unsigned int) Tk_Height(
+ (Tk_Window) containerPtr->parentPtr));
+ } else if (eventPtr->type == ConfigureRequest) {
+ if ((eventPtr->xconfigurerequest.x != 0)
+ || (eventPtr->xconfigurerequest.y != 0)) {
+ /*
+ * The embedded application is trying to move itself, which
+ * isn't legal. At this point, the window hasn't actually
+ * moved, but we need to send it a ConfigureNotify event to
+ * let it know that its request has been denied. If the
+ * embedded application was also trying to resize itself, a
+ * ConfigureNotify will be sent by the geometry management
+ * code below, so we don't need to do anything. Otherwise,
+ * generate a synthetic event.
+ */
+
+ if ((eventPtr->xconfigurerequest.width == winPtr->changes.width)
+ && (eventPtr->xconfigurerequest.height
+ == winPtr->changes.height)) {
+ EmbedSendConfigure(containerPtr);
+ }
+ }
+ EmbedGeometryRequest(containerPtr,
+ eventPtr->xconfigurerequest.width,
+ eventPtr->xconfigurerequest.height);
+ } else if (eventPtr->type == MapRequest) {
+ /*
+ * The embedded application's map request was ignored and simply
+ * passed on to us, so we have to map the window for it to appear
+ * on the screen.
+ */
+
+ XMapWindow(eventPtr->xmaprequest.display,
+ eventPtr->xmaprequest.window);
+ } else if (eventPtr->type == DestroyNotify) {
+ /*
+ * The embedded application is gone. Destroy the container window.
+ */
+
+ Tk_DestroyWindow((Tk_Window) winPtr);
+ }
+ Tk_DeleteErrorHandler(errHandler);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedStructureProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when
+ * a container window owned by this application gets resized
+ * (and also at several other times that we don't care about).
+ * This procedure reflects the size change in the embedded
+ * window that corresponds to the container.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The embedded window gets resized to match the container.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbedStructureProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ Container *containerPtr = (Container *) clientData;
+ Tk_ErrorHandler errHandler;
+
+ if (eventPtr->type == ConfigureNotify) {
+ if (containerPtr->wrapper != None) {
+ /*
+ * Ignore errors, since the embedded application could have
+ * deleted its window.
+ */
+
+ errHandler = Tk_CreateErrorHandler(eventPtr->xfocus.display, -1,
+ -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ XMoveResizeWindow(eventPtr->xconfigure.display,
+ containerPtr->wrapper, 0, 0,
+ (unsigned int) Tk_Width(
+ (Tk_Window) containerPtr->parentPtr),
+ (unsigned int) Tk_Height(
+ (Tk_Window) containerPtr->parentPtr));
+ Tk_DeleteErrorHandler(errHandler);
+ }
+ } else if (eventPtr->type == DestroyNotify) {
+ EmbedWindowDeleted(containerPtr->parentPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedFocusProc --
+ *
+ * This procedure is invoked by the Tk event dispatcher when
+ * FocusIn and FocusOut events occur for a container window owned
+ * by this application. It is responsible for moving the focus
+ * back and forth between a container application and an embedded
+ * application.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The X focus may change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbedFocusProc(clientData, eventPtr)
+ ClientData clientData; /* Token for container window. */
+ XEvent *eventPtr; /* ResizeRequest event. */
+{
+ Container *containerPtr = (Container *) clientData;
+ Tk_ErrorHandler errHandler;
+ Display *display;
+
+ display = Tk_Display(containerPtr->parentPtr);
+ if (eventPtr->type == FocusIn) {
+ /*
+ * The focus just arrived at the container. Change the X focus
+ * to move it to the embedded application, if there is one.
+ * Ignore X errors that occur during this operation (it's
+ * possible that the new focus window isn't mapped).
+ */
+
+ if (containerPtr->wrapper != None) {
+ errHandler = Tk_CreateErrorHandler(eventPtr->xfocus.display, -1,
+ -1, -1, (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ XSetInputFocus(display, containerPtr->wrapper, RevertToParent,
+ CurrentTime);
+ Tk_DeleteErrorHandler(errHandler);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedGeometryRequest --
+ *
+ * This procedure is invoked when an embedded application requests
+ * a particular size. It processes the request (which may or may
+ * not actually honor the request) 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 how big it ought to be.
+ * Events get processed while we're waiting for the geometry
+ * managers to do their thing.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbedGeometryRequest(containerPtr, width, height)
+ Container *containerPtr; /* Information about the embedding. */
+ int width, 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 if we decide not to honor its
+ * request; 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);
+ while (Tcl_DoOneEvent(TCL_IDLE_EVENTS)) {
+ /* Empty loop body. */
+ }
+ if ((winPtr->changes.width != width)
+ || (winPtr->changes.height != height)) {
+ EmbedSendConfigure(containerPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * EmbedSendConfigure --
+ *
+ * This procedure synthesizes a ConfigureNotify event to notify an
+ * embedded application of its current size and location. This
+ * procedure is called when the embedded application made a
+ * geometry request that we did not grant, so that the embedded
+ * application knows that its geometry didn't change after all.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+EmbedSendConfigure(containerPtr)
+ Container *containerPtr; /* Information about the embedding. */
+{
+ TkWindow *winPtr = containerPtr->parentPtr;
+ XEvent event;
+
+ event.xconfigure.type = ConfigureNotify;
+ event.xconfigure.serial =
+ LastKnownRequestProcessed(winPtr->display);
+ event.xconfigure.send_event = True;
+ event.xconfigure.display = winPtr->display;
+ event.xconfigure.event = containerPtr->wrapper;
+ event.xconfigure.window = containerPtr->wrapper;
+ event.xconfigure.x = 0;
+ event.xconfigure.y = 0;
+ event.xconfigure.width = winPtr->changes.width;
+ event.xconfigure.height = winPtr->changes.height;
+ event.xconfigure.above = None;
+ event.xconfigure.override_redirect = False;
+
+ /*
+ * Note: when sending the event below, the ButtonPressMask
+ * causes the event to be sent only to applications that have
+ * selected for ButtonPress events, which should be just the
+ * embedded application.
+ */
+
+ XSendEvent(winPtr->display, containerPtr->wrapper, False,
+ 0, &event);
+
+ /*
+ * The following needs to be done if the embedded window is
+ * not in the same application as the container window.
+ */
+
+ if (containerPtr->embeddedPtr == NULL) {
+ XMoveResizeWindow(winPtr->display, containerPtr->wrapper, 0, 0,
+ (unsigned int) winPtr->changes.width,
+ (unsigned int) winPtr->changes.height);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(winPtr)
+ TkWindow *winPtr; /* Tk's structure for a container or
+ * embedded window. */
+{
+ Container *containerPtr;
+
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr->embeddedPtr == winPtr) {
+ return containerPtr->parentPtr;
+ } else if (containerPtr->parentPtr == winPtr) {
+ return containerPtr->embeddedPtr;
+ }
+ }
+ panic("TkpGetOtherWindow couldn't find window");
+ return NULL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(winPtr, eventPtr)
+ TkWindow *winPtr; /* Window to which the event was originally
+ * reported. */
+ XEvent *eventPtr; /* X event to redirect (should be KeyPress
+ * or KeyRelease). */
+{
+ Container *containerPtr;
+ Window saved;
+
+ /*
+ * First, find the top-level window corresponding to winPtr.
+ */
+
+ while (1) {
+ if (winPtr == NULL) {
+ /*
+ * This window is being deleted. This is too confusing a
+ * case to handle so discard the event.
+ */
+
+ return;
+ }
+ if (winPtr->flags & TK_TOP_LEVEL) {
+ break;
+ }
+ winPtr = winPtr->parentPtr;
+ }
+
+ if (winPtr->flags & TK_EMBEDDED) {
+ /*
+ * This application is embedded. If we got a key event without
+ * officially having the focus, it means that the focus is
+ * really in the container, but the mouse was over the embedded
+ * application. Send the event back to the container.
+ */
+
+ for (containerPtr = firstContainerPtr;
+ containerPtr->embeddedPtr != winPtr;
+ containerPtr = containerPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+ saved = eventPtr->xkey.window;
+ eventPtr->xkey.window = containerPtr->parent;
+ XSendEvent(eventPtr->xkey.display, eventPtr->xkey.window, False,
+ KeyPressMask|KeyReleaseMask, eventPtr);
+ eventPtr->xkey.window = saved;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(topLevelPtr, force)
+ 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. */
+{
+ XEvent event;
+ Container *containerPtr;
+
+ if (!(topLevelPtr->flags & TK_EMBEDDED)) {
+ return;
+ }
+
+ for (containerPtr = firstContainerPtr;
+ containerPtr->embeddedPtr != topLevelPtr;
+ containerPtr = containerPtr->nextPtr) {
+ /* Empty loop body. */
+ }
+
+ event.xfocus.type = FocusIn;
+ event.xfocus.serial = LastKnownRequestProcessed(topLevelPtr->display);
+ event.xfocus.send_event = 1;
+ event.xfocus.display = topLevelPtr->display;
+ event.xfocus.window = containerPtr->parent;
+ event.xfocus.mode = EMBEDDED_APP_WANTS_FOCUS;
+ event.xfocus.detail = force;
+ XSendEvent(event.xfocus.display, event.xfocus.window, False, 0, &event);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpTestembedCmd --
+ *
+ * This procedure implements the "testembed" command. It returns
+ * some or all of the information in the list pointed to by
+ * firstContainerPtr.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpTestembedCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window for application. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ int all;
+ Container *containerPtr;
+ Tcl_DString dString;
+ char buffer[50];
+
+ if ((argc > 1) && (strcmp(argv[1], "all") == 0)) {
+ all = 1;
+ } else {
+ all = 0;
+ }
+ Tcl_DStringInit(&dString);
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ Tcl_DStringStartSublist(&dString);
+ if (containerPtr->parent == None) {
+ Tcl_DStringAppendElement(&dString, "");
+ } else {
+ if (all) {
+ sprintf(buffer, "0x%x", (int) containerPtr->parent);
+ Tcl_DStringAppendElement(&dString, buffer);
+ } else {
+ Tcl_DStringAppendElement(&dString, "XXX");
+ }
+ }
+ if (containerPtr->parentPtr == NULL) {
+ Tcl_DStringAppendElement(&dString, "");
+ } else {
+ Tcl_DStringAppendElement(&dString,
+ containerPtr->parentPtr->pathName);
+ }
+ if (containerPtr->wrapper == None) {
+ Tcl_DStringAppendElement(&dString, "");
+ } else {
+ if (all) {
+ sprintf(buffer, "0x%x", (int) containerPtr->wrapper);
+ Tcl_DStringAppendElement(&dString, buffer);
+ } else {
+ Tcl_DStringAppendElement(&dString, "XXX");
+ }
+ }
+ if (containerPtr->embeddedPtr == NULL) {
+ Tcl_DStringAppendElement(&dString, "");
+ } else {
+ Tcl_DStringAppendElement(&dString,
+ containerPtr->embeddedPtr->pathName);
+ }
+ Tcl_DStringEndSublist(&dString);
+ }
+ Tcl_DStringResult(interp, &dString);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(winPtr)
+ TkWindow *winPtr; /* Tk's information about window that
+ * was deleted. */
+{
+ Container *containerPtr, *prevPtr;
+
+ /*
+ * Find the Container structure for this window work. Delete the
+ * information about the embedded application and free the container's
+ * record.
+ */
+
+ prevPtr = NULL;
+ containerPtr = firstContainerPtr;
+ while (1) {
+ if (containerPtr->embeddedPtr == winPtr) {
+ containerPtr->wrapper = None;
+ containerPtr->embeddedPtr = NULL;
+ break;
+ }
+ if (containerPtr->parentPtr == winPtr) {
+ containerPtr->parentPtr = NULL;
+ break;
+ }
+ prevPtr = containerPtr;
+ containerPtr = containerPtr->nextPtr;
+ }
+ if ((containerPtr->embeddedPtr == NULL)
+ && (containerPtr->parentPtr == NULL)) {
+ if (prevPtr == NULL) {
+ firstContainerPtr = containerPtr->nextPtr;
+ } else {
+ prevPtr->nextPtr = containerPtr->nextPtr;
+ }
+ ckfree((char *) containerPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUnixContainerId --
+ *
+ * Given an embedded window, this procedure returns the X window
+ * identifier for the associated container window.
+ *
+ * Results:
+ * The return value is the X window identifier for winPtr's
+ * container window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Window
+TkUnixContainerId(winPtr)
+ TkWindow *winPtr; /* Tk's structure for an embedded window. */
+{
+ Container *containerPtr;
+
+ for (containerPtr = firstContainerPtr; containerPtr != NULL;
+ containerPtr = containerPtr->nextPtr) {
+ if (containerPtr->embeddedPtr == winPtr) {
+ return containerPtr->parent;
+ }
+ }
+ panic("TkUnixContainerId couldn't find window");
+ return None;
+}
diff --git a/unix/tkUnixEvent.c b/unix/tkUnixEvent.c
new file mode 100644
index 0000000..ace4cc3
--- /dev/null
+++ b/unix/tkUnixEvent.c
@@ -0,0 +1,498 @@
+/*
+ * tkUnixEvent.c --
+ *
+ * This file implements an event source for X displays for the
+ * UNIX version of Tk.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixEvent.c 1.17 97/09/11 12:51:04
+ */
+
+#include "tkInt.h"
+#include "tkUnixInt.h"
+#include <signal.h>
+
+/*
+ * The following static indicates whether this module has been initialized.
+ */
+
+static int initialized = 0;
+
+/*
+ * Prototypes for procedures that are referenced only in this file:
+ */
+
+static void DisplayCheckProc _ANSI_ARGS_((ClientData clientData,
+ int flags));
+static void DisplayExitHandler _ANSI_ARGS_((
+ ClientData clientData));
+static void DisplayFileProc _ANSI_ARGS_((ClientData clientData,
+ int flags));
+static void DisplaySetupProc _ANSI_ARGS_((ClientData clientData,
+ int flags));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkCreateXEventSource --
+ *
+ * This procedure is called during Tk initialization to create
+ * the event source for X Window events.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new event source is created.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkCreateXEventSource()
+{
+ if (!initialized) {
+ initialized = 1;
+ Tcl_CreateEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
+ Tcl_CreateExitHandler(DisplayExitHandler, NULL);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayExitHandler --
+ *
+ * This function is called during finalization to clean up the
+ * display module.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayExitHandler(clientData)
+ ClientData clientData; /* Not used. */
+{
+ Tcl_DeleteEventSource(DisplaySetupProc, DisplayCheckProc, NULL);
+ initialized = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpOpenDisplay --
+ *
+ * Allocates a new TkDisplay, opens the X display, and establishes
+ * the file handler for the connection.
+ *
+ * Results:
+ * A pointer to a Tk display structure.
+ *
+ * Side effects:
+ * Opens a display.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkDisplay *
+TkpOpenDisplay(display_name)
+ char *display_name;
+{
+ TkDisplay *dispPtr;
+ Display *display = XOpenDisplay(display_name);
+
+ if (display == NULL) {
+ return NULL;
+ }
+ dispPtr = (TkDisplay *) ckalloc(sizeof(TkDisplay));
+ dispPtr->display = display;
+ Tcl_CreateFileHandler(ConnectionNumber(display), TCL_READABLE,
+ DisplayFileProc, (ClientData) dispPtr);
+ return dispPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCloseDisplay --
+ *
+ * Cancels notifier callbacks and closes a display.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Deallocates the displayPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpCloseDisplay(displayPtr)
+ TkDisplay *displayPtr;
+{
+ TkDisplay *dispPtr = (TkDisplay *) displayPtr;
+
+ if (dispPtr->display != 0) {
+ Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
+
+ (void) XSync(dispPtr->display, False);
+ (void) XCloseDisplay(dispPtr->display);
+ }
+
+ ckfree((char *) dispPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplaySetupProc --
+ *
+ * This procedure implements the setup part of the UNIX X display
+ * event source. It is invoked by Tcl_DoOneEvent before entering
+ * the notifier to check for events on all displays.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If data is queued on a display inside Xlib, then the maximum
+ * block time will be set to 0 to ensure that the notifier returns
+ * control to Tcl even if there is no more data on the X connection.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplaySetupProc(clientData, flags)
+ ClientData clientData; /* Not used. */
+ int flags;
+{
+ TkDisplay *dispPtr;
+ static Tcl_Time blockTime = { 0, 0 };
+
+ if (!(flags & TCL_WINDOW_EVENTS)) {
+ return;
+ }
+
+ for (dispPtr = tkDisplayList; dispPtr != NULL;
+ dispPtr = dispPtr->nextPtr) {
+
+ /*
+ * Flush the display. If data is pending on the X queue, set
+ * the block time to zero. This ensures that we won't block
+ * in the notifier if there is data in the X queue, but not on
+ * the server socket.
+ */
+
+ XFlush(dispPtr->display);
+ if (XQLength(dispPtr->display) > 0) {
+ Tcl_SetMaxBlockTime(&blockTime);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayCheckProc --
+ *
+ * This procedure checks for events sitting in the X event
+ * queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Moves queued events onto the Tcl event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayCheckProc(clientData, flags)
+ ClientData clientData; /* Not used. */
+ int flags;
+{
+ TkDisplay *dispPtr;
+ XEvent event;
+ int numFound;
+
+ if (!(flags & TCL_WINDOW_EVENTS)) {
+ return;
+ }
+
+ for (dispPtr = tkDisplayList; dispPtr != NULL;
+ dispPtr = dispPtr->nextPtr) {
+ XFlush(dispPtr->display);
+ numFound = XQLength(dispPtr->display);
+
+ /*
+ * Transfer events from the X event queue to the Tk event queue.
+ */
+
+ while (numFound > 0) {
+ XNextEvent(dispPtr->display, &event);
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+ numFound--;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayFileProc --
+ *
+ * This procedure implements the file handler for the X connection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Makes entries on the Tcl event queue for all the events available
+ * from all the displays.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayFileProc(clientData, flags)
+ ClientData clientData; /* The display pointer. */
+ int flags; /* Should be TCL_READABLE. */
+{
+ TkDisplay *dispPtr = (TkDisplay *) clientData;
+ Display *display = dispPtr->display;
+ XEvent event;
+ int numFound;
+
+ XFlush(display);
+ numFound = XEventsQueued(display, QueuedAfterReading);
+ if (numFound == 0) {
+
+ /*
+ * Things are very tricky if there aren't any events readable
+ * at this point (after all, there was supposedly data
+ * available on the connection). A couple of things could
+ * have occurred:
+ *
+ * One possibility is that there were only error events in the
+ * input from the server. If this happens, we should return
+ * (we don't want to go to sleep in XNextEvent below, since
+ * this would block out other sources of input to the
+ * process).
+ *
+ * Another possibility is that our connection to the server
+ * has been closed. This will not necessarily be detected in
+ * XEventsQueued (!!), so if we just return then there will be
+ * an infinite loop. To detect such an error, generate a NoOp
+ * protocol request to exercise the connection to the server,
+ * then return. However, must disable SIGPIPE while sending
+ * the request, or else the process will die from the signal
+ * and won't invoke the X error function to print a nice (?!)
+ * message.
+ */
+
+ void (*oldHandler)();
+
+ oldHandler = (void (*)()) signal(SIGPIPE, SIG_IGN);
+ XNoOp(display);
+ XFlush(display);
+ (void) signal(SIGPIPE, oldHandler);
+ }
+
+ /*
+ * Transfer events from the X event queue to the Tk event queue.
+ */
+
+ while (numFound > 0) {
+ XNextEvent(display, &event);
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+ numFound--;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUnixDoOneXEvent --
+ *
+ * This routine waits for an X event to be processed or for
+ * a timeout to occur. The timeout is specified as an absolute
+ * time. This routine is called when Tk needs to wait for a
+ * particular X event without letting arbitrary events be
+ * processed. The caller will typically call Tk_RestrictEvents
+ * to set up an event filter before calling this routine. This
+ * routine will service at most one event per invocation.
+ *
+ * Results:
+ * Returns 0 if the timeout has expired, otherwise returns 1.
+ *
+ * Side effects:
+ * Can invoke arbitrary Tcl scripts.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkUnixDoOneXEvent(timePtr)
+ Tcl_Time *timePtr; /* Specifies the absolute time when the
+ * call should time out. */
+{
+ TkDisplay *dispPtr;
+ static fd_mask readMask[MASK_SIZE];
+ struct timeval blockTime, *timeoutPtr;
+ Tcl_Time now;
+ int fd, index, bit, numFound, numFdBits = 0;
+
+ /*
+ * Look for queued events first.
+ */
+
+ if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
+ return 1;
+ }
+
+ /*
+ * Compute the next block time and check to see if we have timed out.
+ * Note that HP-UX defines tv_sec to be unsigned so we have to be
+ * careful in our arithmetic.
+ */
+
+ if (timePtr) {
+ TclpGetTime(&now);
+ blockTime.tv_sec = timePtr->sec;
+ blockTime.tv_usec = timePtr->usec - now.usec;
+ if (blockTime.tv_usec < 0) {
+ now.sec += 1;
+ blockTime.tv_usec += 1000000;
+ }
+ if (blockTime.tv_sec < now.sec) {
+ blockTime.tv_sec = 0;
+ blockTime.tv_usec = 0;
+ } else {
+ blockTime.tv_sec -= now.sec;
+ }
+ timeoutPtr = &blockTime;
+ } else {
+ timeoutPtr = NULL;
+ }
+
+ /*
+ * Set up the select mask for all of the displays. If a display has
+ * data pending, then we want to poll instead of blocking.
+ */
+
+ memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
+ for (dispPtr = tkDisplayList; dispPtr != NULL;
+ dispPtr = dispPtr->nextPtr) {
+ XFlush(dispPtr->display);
+ if (XQLength(dispPtr->display) > 0) {
+ blockTime.tv_sec = 0;
+ blockTime.tv_usec = 0;
+ }
+ fd = ConnectionNumber(dispPtr->display);
+ index = fd/(NBBY*sizeof(fd_mask));
+ bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
+ readMask[index] |= bit;
+ if (numFdBits <= fd) {
+ numFdBits = fd+1;
+ }
+ }
+
+ numFound = select(numFdBits, (SELECT_MASK *) &readMask[0], NULL, NULL,
+ timeoutPtr);
+ if (numFound <= 0) {
+ /*
+ * Some systems don't clear the masks after an error, so
+ * we have to do it here.
+ */
+
+ memset((VOID *) readMask, 0, MASK_SIZE*sizeof(fd_mask));
+ }
+
+ /*
+ * Process any new events on the display connections.
+ */
+
+ for (dispPtr = tkDisplayList; dispPtr != NULL;
+ dispPtr = dispPtr->nextPtr) {
+ fd = ConnectionNumber(dispPtr->display);
+ index = fd/(NBBY*sizeof(fd_mask));
+ bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
+ if ((readMask[index] & bit) || (XQLength(dispPtr->display) > 0)) {
+ DisplayFileProc((ClientData)dispPtr, TCL_READABLE);
+ }
+ }
+ if (Tcl_ServiceEvent(TCL_WINDOW_EVENTS)) {
+ return 1;
+ }
+
+ /*
+ * Check to see if we timed out.
+ */
+
+ if (timePtr) {
+ TclpGetTime(&now);
+ if ((now.sec > timePtr->sec) || ((now.sec == timePtr->sec)
+ && (now.usec > timePtr->usec))) {
+ return 0;
+ }
+ }
+
+ /*
+ * We had an event but we did not generate a Tcl event from it. Behave
+ * as though we dealt with it. (JYL&SS)
+ */
+
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSync --
+ *
+ * This routine ensures that all pending X requests have been
+ * seen by the server, and that any pending X events have been
+ * moved onto the Tk event queue.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Places new events on the Tk event queue.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSync(display)
+ Display *display; /* Display to sync. */
+{
+ int numFound = 0;
+ XEvent event;
+
+ XSync(display, False);
+
+ /*
+ * Transfer events from the X event queue to the Tk event queue.
+ */
+
+ numFound = XQLength(display);
+ while (numFound > 0) {
+ XNextEvent(display, &event);
+ Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
+ numFound--;
+ }
+}
diff --git a/unix/tkUnixFocus.c b/unix/tkUnixFocus.c
new file mode 100644
index 0000000..5c1a4f6
--- /dev/null
+++ b/unix/tkUnixFocus.c
@@ -0,0 +1,149 @@
+/*
+ * tkUnixFocus.c --
+ *
+ * This file contains platform specific procedures that manage
+ * focus for Tk.
+ *
+ * Copyright (c) 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.
+ *
+ * SCCS: @(#) tkUnixFocus.c 1.9 97/10/31 09:54:04
+ */
+
+#include "tkInt.h"
+#include "tkPort.h"
+#include "tkUnixInt.h"
+
+extern int tclFocusDebug;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpChangeFocus --
+ *
+ * This procedure is invoked to move the official X 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 X focus window changes; the application's focus
+ * window isn't changed by this procedure.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpChangeFocus(winPtr, force)
+ 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;
+ Tk_ErrorHandler errHandler;
+ Window window, root, parent, *children;
+ unsigned int numChildren, serial;
+ TkWindow *winPtr2;
+ int dummy;
+
+ /*
+ * Don't set the X focus to a window that's marked
+ * override-redirect. This is a hack to avoid problems with menus
+ * under olvwm: if we move the focus then the focus can get lost
+ * during keyboard traversal. Fortunately, we don't really need to
+ * move the focus for menus: events will still find their way to the
+ * focus window, and menus aren't decorated anyway so the window
+ * manager doesn't need to hear about the focus change in order to
+ * redecorate the menu.
+ */
+
+ serial = 0;
+ if (winPtr->atts.override_redirect) {
+ return serial;
+ }
+
+ /*
+ * Check to make sure that the focus is still in one of the windows
+ * of this application or one of their descendants. Furthermore,
+ * grab the server to make sure that the focus doesn't change in the
+ * middle of this operation.
+ */
+
+ XGrabServer(dispPtr->display);
+ if (!force) {
+ /*
+ * Find the focus window, then see if it or one of its ancestors
+ * is a window in our application (it's possible that the focus
+ * window is in an embedded application, which may or may not be
+ * in the same process.
+ */
+
+ XGetInputFocus(dispPtr->display, &window, &dummy);
+ while (1) {
+ winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, window);
+ if ((winPtr2 != NULL) && (winPtr2->mainPtr == winPtr->mainPtr)) {
+ break;
+ }
+ if ((window == PointerRoot) || (window == None)) {
+ goto done;
+ }
+ XQueryTree(dispPtr->display, window, &root, &parent, &children,
+ &numChildren);
+ if (children != NULL) {
+ XFree((void *) children);
+ }
+ if (parent == root) {
+ goto done;
+ }
+ window = parent;
+ }
+ }
+
+ /*
+ * Tell X to change the focus. Ignore errors that occur when changing
+ * the focus: it is still possible that the window we're focussing
+ * to could have gotten unmapped, which will generate an error.
+ */
+
+ errHandler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ if (winPtr->window == None) {
+ panic("ChangeXFocus got null X window");
+ }
+ XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent,
+ CurrentTime);
+ Tk_DeleteErrorHandler(errHandler);
+
+ /*
+ * 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);
+
+ done:
+ XUngrabServer(dispPtr->display);
+
+ /*
+ * After ungrabbing the server, it's important to flush the output
+ * immediately so that the server sees the ungrab command. Otherwise
+ * we might do something else that needs to communicate with the
+ * server (such as invoking a subprocess that needs to do I/O to
+ * the screen); if the ungrab command is still sitting in our
+ * output buffer, we could deadlock.
+ */
+
+ XFlush(dispPtr->display);
+ return serial;
+}
diff --git a/unix/tkUnixFont.c b/unix/tkUnixFont.c
new file mode 100644
index 0000000..d25f157
--- /dev/null
+++ b/unix/tkUnixFont.c
@@ -0,0 +1,979 @@
+/*
+ * tkUnixFont.c --
+ *
+ * Contains the Unix implementation of the platform-independant
+ * font package interface.
+ *
+ * Copyright (c) 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.
+ *
+ * SCCS: @(#) tkUnixFont.c 1.16 97/10/23 12:47:53
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+#include "tkUnixInt.h"
+
+#include "tkFont.h"
+
+#ifndef ABS
+#define ABS(n) (((n) < 0) ? -(n) : (n))
+#endif
+
+/*
+ * The following structure represents Unix's implementation of a font.
+ */
+
+typedef struct UnixFont {
+ TkFont font; /* Stuff used by generic font package. Must
+ * be first in structure. */
+ Display *display; /* The display to which font belongs. */
+ XFontStruct *fontStructPtr; /* X information about font. */
+ char types[256]; /* Array giving types of all characters in
+ * the font, used when displaying control
+ * characters. See below for definition. */
+ int widths[256]; /* Array giving widths of all possible
+ * characters in the font. */
+ int underlinePos; /* Offset from baseline to origin of
+ * underline bar (used for simulating a native
+ * underlined font). */
+ int barHeight; /* Height of underline or overstrike bar
+ * (used for simulating a native underlined or
+ * strikeout font). */
+} UnixFont;
+
+/*
+ * Possible values for entries in the "types" field in a UnixFont structure,
+ * which classifies the types of all characters in the given font. This
+ * information is used when measuring and displaying characters.
+ *
+ * NORMAL: Standard character.
+ * REPLACE: This character doesn't print: instead of
+ * displaying character, display a replacement
+ * sequence like "\n" (for those characters where
+ * ANSI C defines such a sequence) or a sequence
+ * of the form "\xdd" where dd is the hex equivalent
+ * of the character.
+ * SKIP: Don't display anything for this character. This
+ * is only used where the font doesn't contain
+ * all the characters needed to generate
+ * replacement sequences.
+ */
+
+#define NORMAL 0
+#define REPLACE 1
+#define SKIP 2
+
+/*
+ * Characters used when displaying control sequences.
+ */
+
+static char hexChars[] = "0123456789abcdefxtnvr\\";
+
+/*
+ * The following table maps some control characters to sequences like '\n'
+ * rather than '\x10'. A zero entry in the table means no such mapping
+ * exists, and the table only maps characters less than 0x10.
+ */
+
+static char mapChars[] = {
+ 0, 0, 0, 0, 0, 0, 0,
+ 'a', 'b', 't', 'n', 'v', 'f', 'r',
+ 0
+};
+
+
+static UnixFont * AllocFont _ANSI_ARGS_((TkFont *tkFontPtr,
+ Tk_Window tkwin, XFontStruct *fontStructPtr,
+ CONST char *fontName));
+static void DrawChars _ANSI_ARGS_((Display *display,
+ Drawable drawable, GC gc, UnixFont *fontPtr,
+ CONST char *source, int numChars, int x,
+ int y));
+static int GetControlCharSubst _ANSI_ARGS_((int c, char buf[4]));
+
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+TkFont *
+TkpGetNativeFont(tkwin, name)
+ Tk_Window tkwin; /* For display where font will be used. */
+ CONST char *name; /* Platform-specific font name. */
+{
+ XFontStruct *fontStructPtr;
+
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), name);
+ if (fontStructPtr == NULL) {
+ return NULL;
+ }
+
+ return (TkFont *) AllocFont(NULL, tkwin, fontStructPtr, name);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * 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.
+ *
+ * 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:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+TkFont *
+TkpGetFontFromAttributes(tkFontPtr, tkwin, faPtr)
+ 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 numNames, score, i, scaleable, pixelsize, xaPixelsize;
+ int bestIdx, bestScore, bestScaleableIdx, bestScaleableScore;
+ TkXLFDAttributes xa;
+ char buf[256];
+ UnixFont *fontPtr;
+ char **nameList;
+ XFontStruct *fontStructPtr;
+ CONST char *fmt, *family;
+ double d;
+
+ family = faPtr->family;
+ if (family == NULL) {
+ family = "*";
+ }
+
+ pixelsize = -faPtr->pointsize;
+ if (pixelsize < 0) {
+ d = -pixelsize * 25.4 / 72;
+ d *= WidthOfScreen(Tk_Screen(tkwin));
+ d /= WidthMMOfScreen(Tk_Screen(tkwin));
+ d += 0.5;
+ pixelsize = (int) d;
+ }
+
+ /*
+ * Replace the standard Windows and Mac family names with the names that
+ * X likes.
+ */
+
+ if ((strcasecmp("Times New Roman", family) == 0)
+ || (strcasecmp("New York", family) == 0)) {
+ family = "Times";
+ } else if ((strcasecmp("Courier New", family) == 0)
+ || (strcasecmp("Monaco", family) == 0)) {
+ family = "Courier";
+ } else if ((strcasecmp("Arial", family) == 0)
+ || (strcasecmp("Geneva", family) == 0)) {
+ family = "Helvetica";
+ }
+
+ /*
+ * First try for the Q&D exact match.
+ */
+
+#if 0
+ sprintf(buf, "-*-%.200s-%s-%c-normal-*-*-%d-*-*-*-*-iso8859-1", family,
+ ((faPtr->weight > TK_FW_NORMAL) ? "bold" : "medium"),
+ ((faPtr->slant == TK_FS_ROMAN) ? 'r' :
+ (faPtr->slant == TK_FS_ITALIC) ? 'i' : 'o'),
+ faPtr->pointsize * 10);
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf);
+#else
+ fontStructPtr = NULL;
+#endif
+
+ if (fontStructPtr != NULL) {
+ goto end;
+ }
+ /*
+ * Couldn't find exact match. Now fall back to other available
+ * physical fonts.
+ */
+
+ fmt = "-*-%.240s-*-*-*-*-*-*-*-*-*-*-*-*";
+ sprintf(buf, fmt, family);
+ nameList = XListFonts(Tk_Display(tkwin), buf, 10000, &numNames);
+ if (numNames == 0) {
+ /*
+ * Try getting some system font.
+ */
+
+ sprintf(buf, fmt, "fixed");
+ nameList = XListFonts(Tk_Display(tkwin), buf, 10000, &numNames);
+ if (numNames == 0) {
+ getsystem:
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), "fixed");
+ if (fontStructPtr == NULL) {
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), "*");
+ if (fontStructPtr == NULL) {
+ panic("TkpGetFontFromAttributes: cannot get any font");
+ }
+ }
+ goto end;
+ }
+ }
+
+ /*
+ * Inspect each of the XLFDs and pick the one that most closely
+ * matches the desired attributes.
+ */
+
+ bestIdx = 0;
+ bestScore = INT_MAX;
+ bestScaleableIdx = 0;
+ bestScaleableScore = INT_MAX;
+
+ for (i = 0; i < numNames; i++) {
+ score = 0;
+ scaleable = 0;
+ if (TkParseXLFD(nameList[i], &xa) != TCL_OK) {
+ continue;
+ }
+ xaPixelsize = -xa.fa.pointsize;
+
+ /*
+ * Since most people used to use -adobe-* in their XLFDs,
+ * preserve the preference for "adobe" foundry. Otherwise
+ * some applications looks may change slightly if another foundry
+ * is chosen.
+ */
+
+ if (strcasecmp(xa.foundry, "adobe") != 0) {
+ score += 3000;
+ }
+ if (xa.fa.pointsize == 0) {
+ /*
+ * A scaleable font is almost always acceptable, but the
+ * corresponding bitmapped font would be better.
+ */
+
+ score += 10;
+ scaleable = 1;
+ } else {
+ /*
+ * A font that is too small is better than one that is too
+ * big.
+ */
+
+ if (xaPixelsize > pixelsize) {
+ score += (xaPixelsize - pixelsize) * 120;
+ } else {
+ score += (pixelsize - xaPixelsize) * 100;
+ }
+ }
+
+ score += ABS(xa.fa.weight - faPtr->weight) * 30;
+ score += ABS(xa.fa.slant - faPtr->slant) * 25;
+ if (xa.slant == TK_FS_OBLIQUE) {
+ /*
+ * Italic fonts are preferred over oblique. */
+
+ score += 4;
+ }
+
+ if (xa.setwidth != TK_SW_NORMAL) {
+ /*
+ * The normal setwidth is highly preferred.
+ */
+ score += 2000;
+ }
+ if (xa.charset == TK_CS_OTHER) {
+ /*
+ * The standard character set is highly preferred over
+ * foreign languages charsets (because we don't support
+ * other languages yet).
+ */
+ score += 11000;
+ }
+ if ((xa.charset == TK_CS_NORMAL) && (xa.encoding != 1)) {
+ /*
+ * The '1' encoding for the characters above 0x7f is highly
+ * preferred over the other encodings.
+ */
+ score += 8000;
+ }
+
+ if (scaleable) {
+ if (score < bestScaleableScore) {
+ bestScaleableIdx = i;
+ bestScaleableScore = score;
+ }
+ } else {
+ if (score < bestScore) {
+ bestIdx = i;
+ bestScore = score;
+ }
+ }
+ if (score == 0) {
+ break;
+ }
+ }
+
+ /*
+ * Now we know which is the closest matching scaleable font and the
+ * closest matching bitmapped font. If the scaleable font was a
+ * better match, try getting the scaleable font; however, if the
+ * scalable font was not actually available in the desired
+ * pointsize, fall back to the closest bitmapped font.
+ */
+
+ fontStructPtr = NULL;
+ if (bestScaleableScore < bestScore) {
+ char *str, *rest;
+
+ /*
+ * Fill in the desired pointsize info for this font.
+ */
+
+ tryscale:
+ str = nameList[bestScaleableIdx];
+ for (i = 0; i < XLFD_PIXEL_SIZE - 1; i++) {
+ str = strchr(str + 1, '-');
+ }
+ rest = str;
+ for (i = XLFD_PIXEL_SIZE - 1; i < XLFD_REGISTRY; i++) {
+ rest = strchr(rest + 1, '-');
+ }
+ *str = '\0';
+ sprintf(buf, "%.240s-*-%d-*-*-*-*-*%s", nameList[bestScaleableIdx],
+ pixelsize, rest);
+ *str = '-';
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf);
+ bestScaleableScore = INT_MAX;
+ }
+ if (fontStructPtr == NULL) {
+ strcpy(buf, nameList[bestIdx]);
+ fontStructPtr = XLoadQueryFont(Tk_Display(tkwin), buf);
+ if (fontStructPtr == NULL) {
+ /*
+ * This shouldn't happen because the font name is one of the
+ * names that X gave us to use, but it does anyhow.
+ */
+
+ if (bestScaleableScore < INT_MAX) {
+ goto tryscale;
+ } else {
+ XFreeFontNames(nameList);
+ goto getsystem;
+ }
+ }
+ }
+ XFreeFontNames(nameList);
+
+ end:
+ fontPtr = AllocFont(tkFontPtr, tkwin, fontStructPtr, buf);
+ fontPtr->font.fa.underline = faPtr->underline;
+ fontPtr->font.fa.overstrike = faPtr->overstrike;
+
+ 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(tkFontPtr)
+ TkFont *tkFontPtr; /* Token of font to be deleted. */
+{
+ UnixFont *fontPtr;
+
+ fontPtr = (UnixFont *) tkFontPtr;
+
+ XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
+ ckfree((char *) fontPtr);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * TkpGetFontFamilies --
+ *
+ * Return information about the font families that are available
+ * on the display of the given window.
+ *
+ * Results:
+ * interp->result is modified to hold a list of all the available
+ * font families.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+TkpGetFontFamilies(interp, tkwin)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+{
+ int i, new, numNames;
+ char *family, *end, *p;
+ Tcl_HashTable familyTable;
+ Tcl_HashEntry *hPtr;
+ Tcl_HashSearch search;
+ char **nameList;
+
+ Tcl_InitHashTable(&familyTable, TCL_STRING_KEYS);
+
+ nameList = XListFonts(Tk_Display(tkwin), "*", 10000, &numNames);
+ for (i = 0; i < numNames; i++) {
+ if (nameList[i][0] != '-') {
+ continue;
+ }
+ family = strchr(nameList[i] + 1, '-');
+ if (family == NULL) {
+ continue;
+ }
+ family++;
+ end = strchr(family, '-');
+ if (end == NULL) {
+ continue;
+ }
+ *end = '\0';
+ for (p = family; *p != '\0'; p++) {
+ if (isupper(UCHAR(*p))) {
+ *p = tolower(UCHAR(*p));
+ }
+ }
+ Tcl_CreateHashEntry(&familyTable, family, &new);
+ }
+
+ hPtr = Tcl_FirstHashEntry(&familyTable, &search);
+ while (hPtr != NULL) {
+ Tcl_AppendElement(interp, Tcl_GetHashKey(&familyTable, hPtr));
+ hPtr = Tcl_NextHashEntry(&search);
+ }
+
+ Tcl_DeleteHashTable(&familyTable);
+ XFreeFontNames(nameList);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_MeasureChars --
+ *
+ * Determine the number of characters 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 characters 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(tkfont, source, numChars, maxLength, flags, lengthPtr)
+ Tk_Font tkfont; /* Font in which characters will be drawn. */
+ CONST char *source; /* Characters to be displayed. Need not be
+ * '\0' terminated. */
+ int numChars; /* Maximum number of characters to consider
+ * from source string. */
+ 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. */
+ int *lengthPtr; /* Filled with x-location just after the
+ * terminating character. */
+{
+ UnixFont *fontPtr;
+ CONST char *p; /* Current character. */
+ CONST char *term; /* Pointer to most recent character that
+ * may legally be a terminating character. */
+ int termX; /* X-position just after term. */
+ int curX; /* X-position corresponding to p. */
+ int newX; /* X-position corresponding to p+1. */
+ int c, sawNonSpace;
+
+ fontPtr = (UnixFont *) tkfont;
+
+ if (numChars == 0) {
+ *lengthPtr = 0;
+ return 0;
+ }
+
+ if (maxLength <= 0) {
+ maxLength = INT_MAX;
+ }
+
+ newX = curX = termX = 0;
+ p = term = source;
+ sawNonSpace = !isspace(UCHAR(*p));
+
+ /*
+ * Scan the input string one character at a time, calculating width.
+ */
+
+ for (c = UCHAR(*p); ; ) {
+ newX += fontPtr->widths[c];
+ if (newX > maxLength) {
+ break;
+ }
+ curX = newX;
+ numChars--;
+ p++;
+ if (numChars == 0) {
+ term = p;
+ termX = curX;
+ break;
+ }
+
+ c = UCHAR(*p);
+ if (isspace(c)) {
+ if (sawNonSpace) {
+ term = p;
+ termX = curX;
+ sawNonSpace = 0;
+ }
+ } else {
+ sawNonSpace = 1;
+ }
+ }
+
+ /*
+ * P points to the first character that doesn't fit in the desired
+ * span. Use the flags to figure out what to return.
+ */
+
+ if ((flags & TK_PARTIAL_OK) && (numChars > 0) && (curX < maxLength)) {
+ /*
+ * Include the first character that didn't quite fit in the desired
+ * span. The width returned will include the width of that extra
+ * character.
+ */
+
+ numChars--;
+ curX = newX;
+ p++;
+ }
+ if ((flags & TK_AT_LEAST_ONE) && (term == source) && (numChars > 0)) {
+ term = p;
+ termX = curX;
+ if (term == source) {
+ term++;
+ termX = newX;
+ }
+ } else if ((numChars == 0) || !(flags & TK_WHOLE_WORDS)) {
+ term = p;
+ termX = curX;
+ }
+
+ *lengthPtr = termX;
+ return term-source;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * Tk_DrawChars, DrawChars --
+ *
+ * Draw a string of characters on the screen. Tk_DrawChars()
+ * expands control characters that occur in the string to \X or
+ * \xXX sequences. DrawChars() just draws the strings.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets drawn on the screen.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+void
+Tk_DrawChars(display, drawable, gc, tkfont, source, numChars, x, y)
+ 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; /* Characters 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 numChars; /* Number of characters in string. */
+ int x, y; /* Coordinates at which to place origin of
+ * string when drawing. */
+{
+ UnixFont *fontPtr;
+ CONST char *p;
+ int i, type;
+ char buf[4];
+
+ fontPtr = (UnixFont *) tkfont;
+
+ p = source;
+ for (i = 0; i < numChars; i++) {
+ type = fontPtr->types[UCHAR(*p)];
+ if (type != NORMAL) {
+ DrawChars(display, drawable, gc, fontPtr, source, p - source, x, y);
+ x += XTextWidth(fontPtr->fontStructPtr, source, p - source);
+ if (type == REPLACE) {
+ DrawChars(display, drawable, gc, fontPtr, buf,
+ GetControlCharSubst(UCHAR(*p), buf), x, y);
+ x += fontPtr->widths[UCHAR(*p)];
+ }
+ source = p + 1;
+ }
+ p++;
+ }
+
+ DrawChars(display, drawable, gc, fontPtr, source, p - source, x, y);
+}
+
+static void
+DrawChars(display, drawable, gc, fontPtr, source, numChars, x, y)
+ Display *display; /* Display on which to draw. */
+ Drawable drawable; /* Window or pixmap in which to draw. */
+ GC gc; /* Graphics context for drawing characters. */
+ UnixFont *fontPtr; /* Font in which characters will be drawn;
+ * must be the same as font used in GC. */
+ CONST char *source; /* Characters 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 numChars; /* Number of characters in string. */
+ int x, y; /* Coordinates at which to place origin of
+ * string when drawing. */
+{
+ XDrawString(display, drawable, gc, x, y, source, numChars);
+
+ if (fontPtr->font.fa.underline != 0) {
+ XFillRectangle(display, drawable, gc, x,
+ y + fontPtr->underlinePos,
+ (unsigned) XTextWidth(fontPtr->fontStructPtr, source, numChars),
+ (unsigned) fontPtr->barHeight);
+ }
+ if (fontPtr->font.fa.overstrike != 0) {
+ y -= fontPtr->font.fm.descent + (fontPtr->font.fm.ascent) / 10;
+ XFillRectangle(display, drawable, gc, x, y,
+ (unsigned) XTextWidth(fontPtr->fontStructPtr, source, numChars),
+ (unsigned) fontPtr->barHeight);
+ }
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * AllocFont --
+ *
+ * Helper for TkpGetNativeFont() and TkpGetFontFromAttributes().
+ * Allocates and intializes the memory for a new TkFont that
+ * wraps the platform-specific data.
+ *
+ * Results:
+ * Returns pointer to newly constructed TkFont.
+ *
+ * The caller is responsible for initializing the fields of the
+ * TkFont that are used exclusively by the generic TkFont code, and
+ * for releasing those fields before calling TkpDeleteFont().
+ *
+ * Side effects:
+ * Memory allocated.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static UnixFont *
+AllocFont(tkFontPtr, tkwin, fontStructPtr, fontName)
+ 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. */
+ XFontStruct *fontStructPtr; /* X information about font. */
+ CONST char *fontName; /* The string passed to XLoadQueryFont() to
+ * construct the fontStructPtr. */
+{
+ UnixFont *fontPtr;
+ unsigned long value;
+ int i, width, firstChar, lastChar, n, replaceOK;
+ char *name, *p;
+ char buf[4];
+ TkXLFDAttributes xa;
+ double d;
+
+ if (tkFontPtr != NULL) {
+ fontPtr = (UnixFont *) tkFontPtr;
+ XFreeFont(fontPtr->display, fontPtr->fontStructPtr);
+ } else {
+ fontPtr = (UnixFont *) ckalloc(sizeof(UnixFont));
+ }
+
+ /*
+ * Encapsulate the generic stuff in the TkFont.
+ */
+
+ fontPtr->font.fid = fontStructPtr->fid;
+
+ if (XGetFontProperty(fontStructPtr, XA_FONT, &value) && (value != 0)) {
+ name = Tk_GetAtomName(tkwin, (Atom) value);
+ TkInitFontAttributes(&xa.fa);
+ if (TkParseXLFD(name, &xa) == TCL_OK) {
+ goto ok;
+ }
+ }
+ TkInitFontAttributes(&xa.fa);
+ if (TkParseXLFD(fontName, &xa) != TCL_OK) {
+ TkInitFontAttributes(&fontPtr->font.fa);
+ fontPtr->font.fa.family = Tk_GetUid(fontName);
+ } else {
+ ok:
+ fontPtr->font.fa = xa.fa;
+ }
+
+ if (fontPtr->font.fa.pointsize < 0) {
+ d = -fontPtr->font.fa.pointsize * 72 / 25.4;
+ d *= WidthMMOfScreen(Tk_Screen(tkwin));
+ d /= WidthOfScreen(Tk_Screen(tkwin));
+ d += 0.5;
+ fontPtr->font.fa.pointsize = (int) d;
+ }
+
+ fontPtr->font.fm.ascent = fontStructPtr->ascent;
+ fontPtr->font.fm.descent = fontStructPtr->descent;
+ fontPtr->font.fm.maxWidth = fontStructPtr->max_bounds.width;
+ fontPtr->font.fm.fixed = 1;
+ fontPtr->display = Tk_Display(tkwin);
+ fontPtr->fontStructPtr = fontStructPtr;
+
+ /*
+ * Classify the characters.
+ */
+
+ firstChar = fontStructPtr->min_char_or_byte2;
+ lastChar = fontStructPtr->max_char_or_byte2;
+ for (i = 0; i < 256; i++) {
+ if ((i == 0177) || (i < firstChar) || (i > lastChar)) {
+ fontPtr->types[i] = REPLACE;
+ } else {
+ fontPtr->types[i] = NORMAL;
+ }
+ }
+
+ /*
+ * Compute the widths for all the normal characters. Any other
+ * characters are given an initial width of 0. Also, this determines
+ * if this is a fixed or variable width font, by comparing the widths
+ * of all the normal characters.
+ */
+
+ width = 0;
+ for (i = 0; i < 256; i++) {
+ if (fontPtr->types[i] != NORMAL) {
+ n = 0;
+ } else if (fontStructPtr->per_char == NULL) {
+ n = fontStructPtr->max_bounds.width;
+ } else {
+ n = fontStructPtr->per_char[i - firstChar].width;
+ }
+ fontPtr->widths[i] = n;
+ if (n != 0) {
+ if (width == 0) {
+ width = n;
+ } else if (width != n) {
+ fontPtr->font.fm.fixed = 0;
+ }
+ }
+ }
+
+ /*
+ * Compute the widths of the characters that should be replaced with
+ * control character expansions. If the appropriate chars are not
+ * available in this font, then control character expansions will not
+ * be used; control chars will be invisible & zero-width.
+ */
+
+ replaceOK = 1;
+ for (p = hexChars; *p != '\0'; p++) {
+ if ((UCHAR(*p) < firstChar) || (UCHAR(*p) > lastChar)) {
+ replaceOK = 0;
+ break;
+ }
+ }
+ for (i = 0; i < 256; i++) {
+ if (fontPtr->types[i] == REPLACE) {
+ if (replaceOK) {
+ n = GetControlCharSubst(i, buf);
+ for ( ; --n >= 0; ) {
+ fontPtr->widths[i] += fontPtr->widths[UCHAR(buf[n])];
+ }
+ } else {
+ fontPtr->types[i] = SKIP;
+ }
+ }
+ }
+
+ if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_POSITION, &value)) {
+ fontPtr->underlinePos = value;
+ } else {
+ /*
+ * If the XA_UNDERLINE_POSITION property does not exist, the X
+ * manual recommends using the following value:
+ */
+
+ fontPtr->underlinePos = fontStructPtr->descent / 2;
+ }
+ fontPtr->barHeight = 0;
+ if (XGetFontProperty(fontStructPtr, XA_UNDERLINE_THICKNESS, &value)) {
+ /*
+ * Sometimes this is 0 even though it shouldn't be.
+ */
+ fontPtr->barHeight = value;
+ }
+ if (fontPtr->barHeight == 0) {
+ /*
+ * If the XA_UNDERLINE_THICKNESS property does not exist, the X
+ * manual recommends using the width of the stem on a capital
+ * letter. I don't know of a way to get the stem width of a letter,
+ * so guess and use 1/3 the width of a capital I.
+ */
+
+ fontPtr->barHeight = fontPtr->widths['I'] / 3;
+ if (fontPtr->barHeight == 0) {
+ fontPtr->barHeight = 1;
+ }
+ }
+ if (fontPtr->underlinePos + fontPtr->barHeight > fontStructPtr->descent) {
+ /*
+ * If this set of cobbled together values would cause the bottom of
+ * the underline bar to stick below the descent of the font, jack
+ * the underline up a bit higher.
+ */
+
+ fontPtr->barHeight = fontStructPtr->descent - fontPtr->underlinePos;
+ if (fontPtr->barHeight == 0) {
+ fontPtr->underlinePos--;
+ fontPtr->barHeight = 1;
+ }
+ }
+
+ return fontPtr;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ *
+ * GetControlCharSubst --
+ *
+ * When displaying text in a widget, a backslashed escape sequence
+ * is substituted for control characters that occur in the text.
+ * Given a control character, fill in a buffer with the replacement
+ * string that should be displayed.
+ *
+ * Results:
+ * The return value is the length of the substitute string. buf is
+ * filled with the substitute string; it is not '\0' terminated.
+ *
+ * Side effects:
+ * None.
+ *
+ *---------------------------------------------------------------------------
+ */
+
+static int
+GetControlCharSubst(c, buf)
+ int c; /* The control character to be replaced. */
+ char buf[4]; /* Buffer that gets replacement string. It
+ * only needs to be 4 characters long. */
+{
+ buf[0] = '\\';
+ if ((c < sizeof(mapChars)) && (mapChars[c] != 0)) {
+ buf[1] = mapChars[c];
+ return 2;
+ } else {
+ buf[1] = 'x';
+ buf[2] = hexChars[(c >> 4) & 0xf];
+ buf[3] = hexChars[c & 0xf];
+ return 4;
+ }
+}
diff --git a/unix/tkUnixInit.c b/unix/tkUnixInit.c
new file mode 100644
index 0000000..acfd8de
--- /dev/null
+++ b/unix/tkUnixInit.c
@@ -0,0 +1,130 @@
+/*
+ * tkUnixInit.c --
+ *
+ * This file contains Unix-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.
+ *
+ * SCCS: @(#) tkUnixInit.c 1.24 97/07/24 14:46:09
+ */
+
+#include "tkInt.h"
+#include "tkUnixInt.h"
+
+/*
+ * The Init script (common to Windows and Unix platforms) is
+ * defined in tkInitScript.h
+ */
+#include "tkInitScript.h"
+
+
+/*
+ * Default directory in which to look for libraries:
+ */
+
+static char defaultLibraryDir[200] = TK_LIBRARY;
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpInit --
+ *
+ * Performs Unix-specific interpreter initialization related to the
+ * tk_library variable.
+ *
+ * Results:
+ * Returns a standard Tcl result. Leaves an error message or result
+ * in interp->result.
+ *
+ * Side effects:
+ * Sets "tk_library" Tcl variable, runs "tk.tcl" script.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpInit(interp)
+ Tcl_Interp *interp;
+{
+ char *libDir;
+
+ libDir = Tcl_GetVar(interp, "tk_library", TCL_GLOBAL_ONLY);
+ if (libDir == NULL) {
+ Tcl_SetVar(interp, "tk_library", defaultLibraryDir, TCL_GLOBAL_ONLY);
+ }
+ TkCreateXEventSource();
+ return Tcl_Eval(interp, initScript);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetAppName --
+ *
+ * Retrieves the name of the current application from a platform
+ * specific location. For Unix, the application name is 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(interp, namePtr)
+ Tcl_Interp *interp;
+ Tcl_DString *namePtr; /* A previously initialized Tcl_DString. */
+{
+ char *p, *name;
+
+ name = Tcl_GetVar(interp, "argv0", TCL_GLOBAL_ONLY);
+ if ((name == NULL) || (*name == 0)) {
+ name = "tk";
+ } else {
+ p = strrchr(name, '/');
+ if (p != NULL) {
+ name = p+1;
+ }
+ }
+ Tcl_DStringAppend(namePtr, name, -1);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayWarning --
+ *
+ * This routines is called from Tk_Main to display warning
+ * messages that occur during startup.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Generates messages on stdout.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayWarning(msg, title)
+ char *msg; /* Message to be displayed. */
+ char *title; /* Title of warning. */
+{
+ Tcl_Channel errChannel = Tcl_GetStdChannel(TCL_STDERR);
+ if (errChannel) {
+ Tcl_Write(errChannel, title, -1);
+ Tcl_Write(errChannel, ": ", 2);
+ Tcl_Write(errChannel, msg, -1);
+ Tcl_Write(errChannel, "\n", 1);
+ }
+}
diff --git a/unix/tkUnixInt.h b/unix/tkUnixInt.h
new file mode 100644
index 0000000..41bbb66
--- /dev/null
+++ b/unix/tkUnixInt.h
@@ -0,0 +1,32 @@
+/*
+ * tkUnixInt.h --
+ *
+ * This file contains declarations that are shared among the
+ * UNIX-specific parts of Tk but aren't used by the rest of
+ * Tk.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixInt.h 1.9 97/05/08 11:20:12
+ */
+
+#ifndef _TKUNIXINT
+#define _TKUNIXINT
+
+/*
+ * Prototypes for procedures that are referenced in files other
+ * than the ones they're defined in.
+ */
+
+EXTERN void TkCreateXEventSource _ANSI_ARGS_((void));
+EXTERN TkWindow * TkpGetContainer _ANSI_ARGS_((TkWindow *embeddedPtr));
+EXTERN TkWindow * TkpGetWrapperWindow _ANSI_ARGS_((TkWindow *winPtr));
+EXTERN Window TkUnixContainerId _ANSI_ARGS_((TkWindow *winPtr));
+EXTERN int TkUnixDoOneXEvent _ANSI_ARGS_((Tcl_Time *timePtr));
+EXTERN void TkUnixSetMenubar _ANSI_ARGS_((Tk_Window tkwin,
+ Tk_Window menubar));
+
+#endif /* _TKUNIXINT */
diff --git a/unix/tkUnixMenu.c b/unix/tkUnixMenu.c
new file mode 100644
index 0000000..3179a31
--- /dev/null
+++ b/unix/tkUnixMenu.c
@@ -0,0 +1,1603 @@
+/*
+ * tkUnixMenu.c --
+ *
+ * This module implements the UNIX platform-specific features of menus.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixMenu.c 1.76 97/11/05 09:08:22
+ */
+
+#include "tkPort.h"
+#include "default.h"
+#include "tkInt.h"
+#include "tkUnixInt.h"
+#include "tkMenu.h"
+
+/*
+ * Constants used for menu drawing.
+ */
+
+#define MENU_MARGIN_WIDTH 2
+#define MENU_DIVIDER_HEIGHT 2
+
+/*
+ * Platform specific flags for Unix.
+ */
+
+#define ENTRY_HELP_MENU ENTRY_PLATFORM_FLAG1
+
+/*
+ * Procedures used internally.
+ */
+
+static void SetHelpMenu _ANSI_ARGS_((TkMenu *menuPtr));
+static void DrawMenuEntryAccelerator _ANSI_ARGS_((
+ 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, int drawArrow));
+static void DrawMenuEntryBackground _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Drawable d, Tk_3DBorder activeBorder,
+ Tk_3DBorder bgBorder, int x, int y,
+ int width, int heigth));
+static void DrawMenuEntryIndicator _ANSI_ARGS_((
+ 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 _ANSI_ARGS_((
+ 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 DrawMenuSeparator _ANSI_ARGS_((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 _ANSI_ARGS_((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 _ANSI_ARGS_((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 GetMenuAccelGeometry _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int *widthPtr,
+ int *heightPtr));
+static void GetMenuLabelGeometry _ANSI_ARGS_((TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetMenuIndicatorGeometry _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetMenuSeparatorGeometry _ANSI_ARGS_((
+ TkMenu *menuPtr, TkMenuEntry *mePtr,
+ Tk_Font tkfont, CONST Tk_FontMetrics *fmPtr,
+ int *widthPtr, int *heightPtr));
+static void GetTearoffEntryGeometry _ANSI_ARGS_((TkMenu *menuPtr,
+ TkMenuEntry *mePtr, Tk_Font tkfont,
+ CONST Tk_FontMetrics *fmPtr, int *widthPtr,
+ int *heightPtr));
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpNewMenu --
+ *
+ * Gets the platform-specific piece of the menu. Invoked during idle
+ * after the generic part of the menu has been created.
+ *
+ * Results:
+ * Standard TCL error.
+ *
+ * Side effects:
+ * Allocates any platform specific allocations and places them
+ * in the platformData field of the menuPtr.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpNewMenu(menuPtr)
+ TkMenu *menuPtr;
+{
+ SetHelpMenu(menuPtr);
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyMenu --
+ *
+ * Destroys platform-specific menu structures. Called when the
+ * generic menu structure is destroyed for the menu.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All platform-specific allocations are freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyMenu(menuPtr)
+ TkMenu *menuPtr;
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyMenuEntry --
+ *
+ * Cleans up platform-specific menu entry items. Called when entry
+ * is destroyed in the generic code.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All platform specific allocations are freed up.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyMenuEntry(mEntryPtr)
+ TkMenuEntry *mEntryPtr;
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpConfigureMenuEntry --
+ *
+ * Processes configuration options for menu entries. Called when
+ * the generic options are processed for the menu.
+ *
+ * Results:
+ * Returns standard TCL result. If TCL_ERROR is returned, then
+ * interp->result contains an error message.
+ *
+ * Side effects:
+ * Configuration information get set for mePtr; old resources
+ * get freed, if any need it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpConfigureMenuEntry(mePtr)
+ register TkMenuEntry *mePtr; /* Information about menu entry; may
+ * or may not already have values for
+ * some fields. */
+{
+ /*
+ * If this is a cascade menu, and the child menu exists, check to
+ * see if the child menu is a help menu.
+ */
+
+ if ((mePtr->type == CASCADE_ENTRY) && (mePtr->name != NULL)) {
+ TkMenuReferences *menuRefPtr;
+
+ menuRefPtr = TkFindMenuReferences(mePtr->menuPtr->interp,
+ mePtr->name);
+ if ((menuRefPtr != NULL) && (menuRefPtr->menuPtr != NULL)) {
+ SetHelpMenu(menuRefPtr->menuPtr);
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMenuNewEntry --
+ *
+ * Called when a new entry is created in a menu. Fills in platform
+ * specific data for the entry. The platformEntryData field
+ * is used to store the indicator diameter for radio button
+ * and check box entries.
+ *
+ * Results:
+ * Standard TCL error.
+ *
+ * Side effects:
+ * None on Unix.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpMenuNewEntry(mePtr)
+ TkMenuEntry *mePtr;
+{
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetWindowMenuBar --
+ *
+ * Sets up the menu as a menubar in the given window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Recomputes geometry of given window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetWindowMenuBar(tkwin, menuPtr)
+ Tk_Window tkwin; /* The window we are setting */
+ TkMenu *menuPtr; /* The menu we are setting */
+{
+ if (menuPtr == NULL) {
+ TkUnixSetMenubar(tkwin, NULL);
+ } else {
+ TkUnixSetMenubar(tkwin, menuPtr->tkwin);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpSetMainMenuBar --
+ *
+ * Called when a toplevel widget is brought to front. On the
+ * Macintosh, sets up the menubar that goes accross the top
+ * of the main monitor. On other platforms, nothing is necessary.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Recompute geometry of given window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpSetMainMenubar(interp, tkwin, menuName)
+ Tcl_Interp *interp;
+ Tk_Window tkwin;
+ char *menuName;
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuIndicatorGeometry --
+ *
+ * Fills out the geometry of the indicator in a menu item. Note
+ * that the mePtr->height field must have already been filled in
+ * by GetMenuLabelGeometry since this height depends on the label
+ * height.
+ *
+ * Results:
+ * widthPtr and heightPtr point to the new geometry values.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
+ TkMenu *menuPtr; /* The menu we are drawing. */
+ TkMenuEntry *mePtr; /* The entry we are interested in. */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
+ int *widthPtr; /* The resulting width */
+ int *heightPtr; /* The resulting height */
+{
+ if (!mePtr->hideMargin && mePtr->indicatorOn &&
+ ((mePtr->type == CHECK_BUTTON_ENTRY)
+ || (mePtr->type == RADIO_BUTTON_ENTRY))) {
+ if ((mePtr->image != NULL) || (mePtr->bitmap != None)) {
+ *widthPtr = (14 * mePtr->height) / 10;
+ *heightPtr = mePtr->height;
+ if (mePtr->type == CHECK_BUTTON_ENTRY) {
+ mePtr->platformEntryData =
+ (TkMenuPlatformEntryData) ((65 * mePtr->height) / 100);
+ } else {
+ mePtr->platformEntryData =
+ (TkMenuPlatformEntryData) ((75 * mePtr->height) / 100);
+ }
+ } else {
+ *widthPtr = *heightPtr = mePtr->height;
+ if (mePtr->type == CHECK_BUTTON_ENTRY) {
+ mePtr->platformEntryData = (TkMenuPlatformEntryData)
+ ((80 * mePtr->height) / 100);
+ } else {
+ mePtr->platformEntryData = (TkMenuPlatformEntryData)
+ mePtr->height;
+ }
+ }
+ } else {
+ *heightPtr = 0;
+ *widthPtr = menuPtr->borderWidth;
+ }
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuAccelGeometry --
+ *
+ * Get the geometry of the accelerator area of a menu item.
+ *
+ * Results:
+ * heightPtr and widthPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMenuAccelGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
+ TkMenu *menuPtr; /* The menu was are drawing */
+ TkMenuEntry *mePtr; /* The entry we are getting the geometry for */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr;/* The precalculated font metrics */
+ int *widthPtr; /* The width of the acclerator area */
+ int *heightPtr; /* The height of the accelerator area */
+{
+ *heightPtr = fmPtr->linespace;
+ if (mePtr->type == CASCADE_ENTRY) {
+ *widthPtr = 2 * CASCADE_ARROW_WIDTH;
+ } else if ((menuPtr->menuType != MENUBAR) && (mePtr->accel != NULL)) {
+ *widthPtr = Tk_TextWidth(tkfont, mePtr->accel, mePtr->accelLength);
+ } else {
+ *widthPtr = 0;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryBackground --
+ *
+ * This procedure 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(menuPtr, mePtr, d, activeBorder, bgBorder, x, y,
+ width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing. */
+ Drawable d; /* The drawable we are drawing into */
+ Tk_3DBorder activeBorder; /* The border for an active item */
+ Tk_3DBorder bgBorder; /* The background border */
+ int x; /* Left coordinate of entry rect */
+ int y; /* Right coordinate of entry rect */
+ int width; /* Width of entry rect */
+ int height; /* Height of entry rect */
+{
+ if (mePtr->state == tkActiveUid) {
+ int relief;
+ bgBorder = activeBorder;
+
+ if ((menuPtr->menuType == MENUBAR)
+ && ((menuPtr->postedCascade == NULL)
+ || (menuPtr->postedCascade != mePtr))) {
+ relief = TK_RELIEF_FLAT;
+ } else {
+ relief = TK_RELIEF_RAISED;
+ }
+
+ Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
+ menuPtr->activeBorderWidth, relief);
+ } else {
+ Tk_Fill3DRectangle(menuPtr->tkwin, d, bgBorder, x, y, width, height,
+ 0, TK_RELIEF_FLAT);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryAccelerator --
+ *
+ * This procedure 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
+DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr, activeBorder,
+ x, y, width, height, drawArrow)
+ 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 precalculated gc to draw with */
+ Tk_Font tkfont; /* The precalculated font */
+ CONST Tk_FontMetrics *fmPtr; /* The precalculated metrics */
+ Tk_3DBorder activeBorder; /* The border for an active item */
+ int x; /* Left coordinate of entry rect */
+ int y; /* Top coordinate of entry rect */
+ int width; /* Width of entry */
+ int height; /* Height of entry */
+ int drawArrow; /* Whether or not to draw arrow. */
+{
+ XPoint points[3];
+
+ /*
+ * Draw accelerator or cascade arrow.
+ */
+
+ if (menuPtr->menuType == MENUBAR) {
+ return;
+ }
+
+ if ((mePtr->type == CASCADE_ENTRY) && drawArrow) {
+ points[0].x = x + width - menuPtr->borderWidth
+ - menuPtr->activeBorderWidth - CASCADE_ARROW_WIDTH;
+ points[0].y = y + (height - CASCADE_ARROW_HEIGHT)/2;
+ points[1].x = points[0].x;
+ points[1].y = points[0].y + CASCADE_ARROW_HEIGHT;
+ points[2].x = points[0].x + CASCADE_ARROW_WIDTH;
+ points[2].y = points[0].y + CASCADE_ARROW_HEIGHT/2;
+ Tk_Fill3DPolygon(menuPtr->tkwin, d, activeBorder, points, 3,
+ DECORATION_BORDER_WIDTH,
+ (menuPtr->postedCascade == mePtr)
+ ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED);
+ } else if (mePtr->accel != NULL) {
+ int left = x + mePtr->labelWidth + menuPtr->activeBorderWidth
+ + mePtr->indicatorSpace;
+ if (menuPtr->menuType == MENUBAR) {
+ left += 5;
+ }
+ Tk_DrawChars(menuPtr->display, d, gc, tkfont, mePtr->accel,
+ mePtr->accelLength, left,
+ (y + (height + fmPtr->ascent - fmPtr->descent) / 2));
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryIndicator --
+ *
+ * This procedure 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
+DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont, fmPtr,
+ x, y, width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* The drawable to draw into */
+ GC gc; /* The gc to draw with */
+ GC indicatorGC; /* The gc that indicators draw with */
+ Tk_Font tkfont; /* The font to draw with */
+ CONST Tk_FontMetrics *fmPtr; /* The font metrics of the font */
+ int x; /* The left of the entry rect */
+ int y; /* The top of the entry rect */
+ int width; /* Width of menu entry */
+ int height; /* Height of menu entry */
+{
+
+ /*
+ * Draw check-button indicator.
+ */
+
+ if ((mePtr->type == CHECK_BUTTON_ENTRY)
+ && mePtr->indicatorOn) {
+ int dim, top, left;
+
+ dim = (int) mePtr->platformEntryData;
+ left = x + menuPtr->activeBorderWidth
+ + (mePtr->indicatorSpace - dim)/2;
+ if (menuPtr->menuType == MENUBAR) {
+ left += 5;
+ }
+ top = y + (height - dim)/2;
+ Tk_Fill3DRectangle(menuPtr->tkwin, d, menuPtr->border, left, top, dim,
+ dim, DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN);
+ left += DECORATION_BORDER_WIDTH;
+ top += DECORATION_BORDER_WIDTH;
+ dim -= 2*DECORATION_BORDER_WIDTH;
+ if ((dim > 0) && (mePtr->entryFlags
+ & ENTRY_SELECTED)) {
+ XFillRectangle(menuPtr->display, d, indicatorGC, left, top,
+ (unsigned int) dim, (unsigned int) dim);
+ }
+ }
+
+ /*
+ * Draw radio-button indicator.
+ */
+
+ if ((mePtr->type == RADIO_BUTTON_ENTRY)
+ && mePtr->indicatorOn) {
+ XPoint points[4];
+ int radius;
+
+ radius = ((int) mePtr->platformEntryData)/2;
+ points[0].x = x + (mePtr->indicatorSpace
+ - (int) mePtr->platformEntryData)/2;
+ points[0].y = y + (height)/2;
+ points[1].x = points[0].x + radius;
+ points[1].y = points[0].y + radius;
+ points[2].x = points[1].x + radius;
+ points[2].y = points[0].y;
+ points[3].x = points[1].x;
+ points[3].y = points[0].y - radius;
+ if (mePtr->entryFlags & ENTRY_SELECTED) {
+ XFillPolygon(menuPtr->display, d, indicatorGC, points, 4, Convex,
+ CoordModeOrigin);
+ } else {
+ Tk_Fill3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 4,
+ DECORATION_BORDER_WIDTH, TK_RELIEF_FLAT);
+ }
+ Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 4,
+ DECORATION_BORDER_WIDTH, TK_RELIEF_SUNKEN);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuSeparator --
+ *
+ * This procedure draws a separator menu item.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menu in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ TkMenuEntry *mePtr; /* The entry we are drawing */
+ Drawable d; /* The drawable we are using */
+ GC gc; /* The gc to draw into */
+ Tk_Font tkfont; /* The font to draw with */
+ CONST Tk_FontMetrics *fmPtr; /* The font metrics from the font */
+ int x;
+ int y;
+ int width;
+ int height;
+{
+ XPoint points[2];
+ int margin;
+
+ if (menuPtr->menuType == MENUBAR) {
+ return;
+ }
+
+ margin = (fmPtr->ascent + fmPtr->descent)/2;
+ points[0].x = x;
+ points[0].y = y + height/2;
+ points[1].x = width - 1;
+ points[1].y = points[0].y;
+ Tk_Draw3DPolygon(menuPtr->tkwin, d, menuPtr->border, points, 2, 1,
+ TK_RELIEF_RAISED);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawMenuEntryLabel --
+ *
+ * This procedure 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(
+ menuPtr, /* The menu we are drawing */
+ mePtr, /* The entry we are drawing */
+ d, /* What we are drawing into */
+ gc, /* The gc we are drawing into */
+ tkfont, /* The precalculated font */
+ fmPtr, /* The precalculated font metrics */
+ x, /* left edge */
+ y, /* right edge */
+ width, /* width of entry */
+ height) /* height of entry */
+ TkMenu *menuPtr;
+ TkMenuEntry *mePtr;
+ Drawable d;
+ GC gc;
+ Tk_Font tkfont;
+ CONST Tk_FontMetrics *fmPtr;
+ int x, y, width, height;
+{
+ int baseline;
+ int indicatorSpace = mePtr->indicatorSpace;
+ int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth;
+ int imageHeight, imageWidth;
+
+ if (menuPtr->menuType == MENUBAR) {
+ leftEdge += 5;
+ }
+
+ /*
+ * Draw label or bitmap or image for entry.
+ */
+
+ baseline = y + (height + fmPtr->ascent - fmPtr->descent) / 2;
+ if (mePtr->image != NULL) {
+ Tk_SizeOfImage(mePtr->image, &imageWidth, &imageHeight);
+ if ((mePtr->selectImage != NULL)
+ && (mePtr->entryFlags & ENTRY_SELECTED)) {
+ Tk_RedrawImage(mePtr->selectImage, 0, 0,
+ imageWidth, imageHeight, d, leftEdge,
+ (int) (y + (mePtr->height - imageHeight)/2));
+ } else {
+ Tk_RedrawImage(mePtr->image, 0, 0, imageWidth,
+ imageHeight, d, leftEdge,
+ (int) (y + (mePtr->height - imageHeight)/2));
+ }
+ } else if (mePtr->bitmap != None) {
+ int width, height;
+
+ Tk_SizeOfBitmap(menuPtr->display,
+ mePtr->bitmap, &width, &height);
+ XCopyPlane(menuPtr->display,
+ mePtr->bitmap, d,
+ gc, 0, 0, (unsigned) width, (unsigned) height, leftEdge,
+ (int) (y + (mePtr->height - height)/2), 1);
+ } else {
+ if (mePtr->labelLength > 0) {
+ Tk_DrawChars(menuPtr->display, d, gc,
+ tkfont, mePtr->label, mePtr->labelLength,
+ leftEdge, baseline);
+ DrawMenuUnderline(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y,
+ width, height);
+ }
+ }
+
+ if (mePtr->state == tkDisabledUid) {
+ if (menuPtr->disabledFg == 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,
+ (int) (y + (mePtr->height - imageHeight)/2),
+ (unsigned) imageWidth, (unsigned) imageHeight);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
+ 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;
+ int y;
+ int width;
+ int height;
+{
+ int indicatorSpace = mePtr->indicatorSpace;
+ if (mePtr->underline >= 0) {
+ int leftEdge = x + indicatorSpace + menuPtr->activeBorderWidth;
+ if (menuPtr->menuType == MENUBAR) {
+ leftEdge += 5;
+ }
+
+ Tk_UnderlineChars(menuPtr->display, d, gc, tkfont, mePtr->label,
+ leftEdge, y + (height + fmPtr->ascent - fmPtr->descent) / 2,
+ mePtr->underline, mePtr->underline + 1);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpPostMenu --
+ *
+ * Posts a menu on the screen
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu is posted and handled.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpPostMenu(interp, menuPtr, x, y)
+ Tcl_Interp *interp;
+ TkMenu *menuPtr;
+ int x;
+ int y;
+{
+ return TkPostTearoffMenu(interp, menuPtr, x, y);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMenuSeparatorGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr,
+ heightPtr)
+ 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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetTearoffEntryGeometry --
+ *
+ * Gets the width and height of the indicator area of a menu.
+ *
+ * Results:
+ * widthPtr and heightPtr are set.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetTearoffEntryGeometry(menuPtr, mePtr, tkfont, fmPtr, widthPtr, heightPtr)
+ TkMenu *menuPtr; /* The menu we are drawing */
+ 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;
+ *widthPtr = 0;
+ } else {
+ *heightPtr = fmPtr->linespace;
+ *widthPtr = Tk_TextWidth(tkfont, "W", -1);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpComputeMenubarGeometry --
+ *
+ * This procedure 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(menuPtr)
+ TkMenu *menuPtr; /* Structure describing menu. */
+{
+ Tk_Font tkfont;
+ Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
+ int width, height;
+ int i, j;
+ int x, y, currentRowHeight, currentRowWidth, maxWidth;
+ int maxWindowWidth;
+ int lastRowBreak;
+ int helpMenuIndex = -1;
+ TkMenuEntry *mePtr;
+ int lastEntry;
+
+ if (menuPtr->tkwin == NULL) {
+ return;
+ }
+
+ maxWidth = 0;
+ if (menuPtr->numEntries == 0) {
+ height = 0;
+ } else {
+ maxWindowWidth = Tk_Width(menuPtr->tkwin);
+ if (maxWindowWidth == 1) {
+ maxWindowWidth = 0x7ffffff;
+ }
+ currentRowHeight = 0;
+ x = y = menuPtr->borderWidth;
+ lastRowBreak = 0;
+ currentRowWidth = 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 measureing and drawing
+ * routines. We will measure the font metrics of the menu once,
+ * and if an entry has a font set, we will measure it as we come
+ * to it, and then we decide which set to give the geometry routines.
+ */
+
+ Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics);
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ mePtr = menuPtr->entries[i];
+ mePtr->entryFlags &= ~ENTRY_LAST_COLUMN;
+ tkfont = mePtr->tkfont;
+ if (tkfont == NULL) {
+ tkfont = menuPtr->tkfont;
+ fmPtr = &menuMetrics;
+ } else {
+ Tk_GetFontMetrics(tkfont, &entryMetrics);
+ fmPtr = &entryMetrics;
+ }
+
+ /*
+ * For every entry, we need to check to see whether or not we
+ * wrap. If we do wrap, then we have to adjust all of the previous
+ * entries' height and y position, because when we see them
+ * the first time, we don't know how big its neighbor might
+ * be.
+ */
+
+ if ((mePtr->type == SEPARATOR_ENTRY)
+ || (mePtr->type == TEAROFF_ENTRY)) {
+ mePtr->height = mePtr->width = 0;
+ } else {
+
+ GetMenuLabelGeometry(mePtr, tkfont, fmPtr,
+ &width, &height);
+ mePtr->height = height + 2 * menuPtr->activeBorderWidth + 10;
+ mePtr->width = width;
+
+ GetMenuIndicatorGeometry(menuPtr, mePtr,
+ tkfont, fmPtr, &width, &height);
+ mePtr->indicatorSpace = width;
+ if (width > 0) {
+ mePtr->width += width;
+ }
+ mePtr->width += 2 * menuPtr->activeBorderWidth + 10;
+ }
+ if (mePtr->entryFlags & ENTRY_HELP_MENU) {
+ helpMenuIndex = i;
+ } else if (x + mePtr->width + menuPtr->borderWidth
+ > maxWindowWidth) {
+
+ if (i == lastRowBreak) {
+ mePtr->y = y;
+ mePtr->x = x;
+ lastRowBreak++;
+ y += mePtr->height;
+ currentRowHeight = 0;
+ } else {
+ x = menuPtr->borderWidth;
+ for (j = lastRowBreak; j < i; j++) {
+ menuPtr->entries[j]->y = y + currentRowHeight
+ - menuPtr->entries[j]->height;
+ menuPtr->entries[j]->x = x;
+ x += menuPtr->entries[j]->width;
+ }
+ lastRowBreak = i;
+ y += currentRowHeight;
+ currentRowHeight = mePtr->height;
+ }
+ if (x > maxWidth) {
+ maxWidth = x;
+ }
+ x = menuPtr->borderWidth;
+ } else {
+ x += mePtr->width;
+ if (mePtr->height > currentRowHeight) {
+ currentRowHeight = mePtr->height;
+ }
+ }
+ }
+
+ lastEntry = menuPtr->numEntries - 1;
+ if (helpMenuIndex == lastEntry) {
+ lastEntry--;
+ }
+ if ((lastEntry >= 0) && (x + menuPtr->entries[lastEntry]->width
+ + menuPtr->borderWidth > maxWidth)) {
+ maxWidth = x + menuPtr->entries[lastEntry]->width
+ + menuPtr->borderWidth;
+ }
+ x = menuPtr->borderWidth;
+ for (j = lastRowBreak; j < menuPtr->numEntries; j++) {
+ if (j == helpMenuIndex) {
+ continue;
+ }
+ menuPtr->entries[j]->y = y + currentRowHeight
+ - menuPtr->entries[j]->height;
+ menuPtr->entries[j]->x = x;
+ x += menuPtr->entries[j]->width;
+ }
+
+
+ if (helpMenuIndex != -1) {
+ mePtr = menuPtr->entries[helpMenuIndex];
+ if (x + mePtr->width + menuPtr->borderWidth > maxWindowWidth) {
+ y += currentRowHeight;
+ currentRowHeight = mePtr->height;
+ x = menuPtr->borderWidth;
+ } else if (mePtr->height > currentRowHeight) {
+ currentRowHeight = mePtr->height;
+ }
+ mePtr->x = maxWindowWidth - menuPtr->borderWidth - mePtr->width;
+ mePtr->y = y + currentRowHeight - mePtr->height;
+ }
+ height = y + currentRowHeight + menuPtr->borderWidth;
+ }
+ width = Tk_Width(menuPtr->tkwin);
+
+ /*
+ * 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 (width <= 0) {
+ width = 1;
+ }
+ if (height <= 0) {
+ height = 1;
+ }
+ menuPtr->totalWidth = maxWidth;
+ menuPtr->totalHeight = height;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DrawTearoffEntry --
+ *
+ * This procedure 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
+DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, y, width, height)
+ 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 margin, segmentWidth, maxX;
+
+ if (menuPtr->menuType != MASTER_MENU) {
+ return;
+ }
+
+ margin = (fmPtr->ascent + fmPtr->descent)/2;
+ points[0].x = x;
+ points[0].y = y + height/2;
+ points[1].y = points[0].y;
+ segmentWidth = 6;
+ maxX = width - 1;
+
+ 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, menuPtr->border, points, 2, 1,
+ TK_RELIEF_RAISED);
+ points[0].x += 2*segmentWidth;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpInitializeMenuBindings --
+ *
+ * For every interp, initializes the bindings for Windows
+ * menus. Does nothing on Mac or XWindows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * C-level 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(interp, bindingTable)
+ Tcl_Interp *interp; /* The interpreter to set. */
+ Tk_BindingTable bindingTable; /* The table to add to. */
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SetHelpMenu --
+ *
+ * Given a menu, check to see whether or not it is a help menu
+ * cascade in a menubar. If it is, the entry that points to
+ * this menu will be marked.
+ *
+ * RESULTS:
+ * None.
+ *
+ * Side effects:
+ * Will set the ENTRY_HELP_MENU flag appropriately.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SetHelpMenu(menuPtr)
+ TkMenu *menuPtr; /* The menu we are checking */
+{
+ TkMenuEntry *cascadeEntryPtr;
+
+ for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
+ cascadeEntryPtr != NULL;
+ cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
+ if ((cascadeEntryPtr->menuPtr->menuType == MENUBAR)
+ && (cascadeEntryPtr->menuPtr->masterMenuPtr->tkwin != NULL)
+ && (menuPtr->masterMenuPtr->tkwin != NULL)) {
+ TkMenu *masterMenuPtr = cascadeEntryPtr->menuPtr->masterMenuPtr;
+ char *helpMenuName = ckalloc(strlen(Tk_PathName(
+ masterMenuPtr->tkwin)) + strlen(".help") + 1);
+
+ strcpy(helpMenuName, Tk_PathName(masterMenuPtr->tkwin));
+ strcat(helpMenuName, ".help");
+ if (strcmp(helpMenuName,
+ Tk_PathName(menuPtr->masterMenuPtr->tkwin)) == 0) {
+ cascadeEntryPtr->entryFlags |= ENTRY_HELP_MENU;
+ } else {
+ cascadeEntryPtr->entryFlags &= ~ENTRY_HELP_MENU;
+ }
+ ckfree(helpMenuName);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(mePtr, d, tkfont, menuMetricsPtr, x, y, width, height,
+ strictMotif, drawArrow)
+ TkMenuEntry *mePtr; /* The entry to draw */
+ Drawable d; /* What 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 drawArrow; /* Whether or not to draw the cascade
+ * arrow for cascade items. Only applies
+ * to Windows. */
+{
+ GC gc, indicatorGC;
+ TkMenu *menuPtr = mePtr->menuPtr;
+ Tk_3DBorder bgBorder, activeBorder;
+ CONST Tk_FontMetrics *fmPtr;
+ Tk_FontMetrics entryMetrics;
+ int padY = (menuPtr->menuType == MENUBAR) ? 3 : 0;
+ int adjustedY = y + padY;
+ int adjustedHeight = height - 2 * padY;
+
+ /*
+ * Choose the gc for drawing the foreground part of the entry.
+ */
+
+ if ((mePtr->state == tkActiveUid)
+ && !strictMotif) {
+ gc = mePtr->activeGC;
+ if (gc == NULL) {
+ gc = menuPtr->activeGC;
+ }
+ } else {
+ TkMenuEntry *cascadeEntryPtr;
+ int parentDisabled = 0;
+
+ for (cascadeEntryPtr = menuPtr->menuRefPtr->parentEntryPtr;
+ cascadeEntryPtr != NULL;
+ cascadeEntryPtr = cascadeEntryPtr->nextCascadePtr) {
+ if (strcmp(cascadeEntryPtr->name,
+ Tk_PathName(menuPtr->tkwin)) == 0) {
+ if (cascadeEntryPtr->state == tkDisabledUid) {
+ parentDisabled = 1;
+ }
+ break;
+ }
+ }
+
+ if (((parentDisabled || (mePtr->state == tkDisabledUid)))
+ && (menuPtr->disabledFg != 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 = mePtr->border;
+ if (bgBorder == NULL) {
+ bgBorder = menuPtr->border;
+ }
+ if (strictMotif) {
+ activeBorder = bgBorder;
+ } else {
+ activeBorder = mePtr->activeBorder;
+ if (activeBorder == NULL) {
+ activeBorder = menuPtr->activeBorder;
+ }
+ }
+
+ if (mePtr->tkfont == NULL) {
+ fmPtr = menuMetricsPtr;
+ } else {
+ tkfont = mePtr->tkfont;
+ 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, x, y, width, height);
+
+ if (mePtr->type == SEPARATOR_ENTRY) {
+ DrawMenuSeparator(menuPtr, mePtr, d, gc, tkfont,
+ fmPtr, x, adjustedY, width, adjustedHeight);
+ } else if (mePtr->type == TEAROFF_ENTRY) {
+ DrawTearoffEntry(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
+ width, adjustedHeight);
+ } else {
+ DrawMenuEntryLabel(menuPtr, mePtr, d, gc, tkfont, fmPtr, x, adjustedY,
+ width, adjustedHeight);
+ DrawMenuEntryAccelerator(menuPtr, mePtr, d, gc, tkfont, fmPtr,
+ activeBorder, x, adjustedY, width, adjustedHeight, drawArrow);
+ if (!mePtr->hideMargin) {
+ DrawMenuEntryIndicator(menuPtr, mePtr, d, gc, indicatorGC, tkfont,
+ fmPtr, x, adjustedY, width, adjustedHeight);
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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(mePtr, tkfont, fmPtr, widthPtr, heightPtr)
+ 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;
+
+ if (mePtr->image != NULL) {
+ Tk_SizeOfImage(mePtr->image, widthPtr, heightPtr);
+ } else if (mePtr->bitmap != (Pixmap) NULL) {
+ Tk_SizeOfBitmap(menuPtr->display, mePtr->bitmap, widthPtr, heightPtr);
+ } else {
+ *heightPtr = fmPtr->linespace;
+
+ if (mePtr->label != NULL) {
+ *widthPtr = Tk_TextWidth(tkfont, mePtr->label, mePtr->labelLength);
+ } else {
+ *widthPtr = 0;
+ }
+ }
+ *heightPtr += 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpComputeStandardMenuGeometry --
+ *
+ * This procedure 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(
+ menuPtr) /* Structure describing menu. */
+ TkMenu *menuPtr;
+{
+ Tk_Font tkfont;
+ Tk_FontMetrics menuMetrics, entryMetrics, *fmPtr;
+ int x, y, height, width, indicatorSpace, labelWidth, accelWidth;
+ int windowWidth, windowHeight, accelSpace;
+ int i, j, lastColumnBreak = 0;
+ TkMenuEntry *mePtr;
+
+ if (menuPtr->tkwin == NULL) {
+ return;
+ }
+
+ x = y = menuPtr->borderWidth;
+ indicatorSpace = labelWidth = accelWidth = 0;
+ windowHeight = windowWidth = 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.
+ */
+
+ Tk_GetFontMetrics(menuPtr->tkfont, &menuMetrics);
+ accelSpace = Tk_TextWidth(menuPtr->tkfont, "M", 1);
+
+ for (i = 0; i < menuPtr->numEntries; i++) {
+ mePtr = menuPtr->entries[i];
+ tkfont = mePtr->tkfont;
+ if (tkfont == NULL) {
+ tkfont = menuPtr->tkfont;
+ fmPtr = &menuMetrics;
+ } else {
+ Tk_GetFontMetrics(tkfont, &entryMetrics);
+ fmPtr = &entryMetrics;
+ }
+
+ if ((i > 0) && mePtr->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 * menuPtr->activeBorderWidth;
+ menuPtr->entries[j]->x = x;
+ menuPtr->entries[j]->entryFlags &= ~ENTRY_LAST_COLUMN;
+ }
+ x += indicatorSpace + labelWidth + accelWidth
+ + 2 * menuPtr->activeBorderWidth;
+ windowWidth = x;
+ indicatorSpace = labelWidth = accelWidth = 0;
+ lastColumnBreak = i;
+ y = menuPtr->borderWidth;
+ }
+
+ if (mePtr->type == SEPARATOR_ENTRY) {
+ GetMenuSeparatorGeometry(menuPtr, mePtr, tkfont,
+ fmPtr, &width, &height);
+ mePtr->height = height;
+ } else if (mePtr->type == TEAROFF_ENTRY) {
+ GetTearoffEntryGeometry(menuPtr, mePtr, tkfont,
+ fmPtr, &width, &height);
+ mePtr->height = height;
+ labelWidth = width;
+ } 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(mePtr, tkfont, fmPtr, &width,
+ &height);
+ mePtr->height = height;
+ if (!mePtr->hideMargin) {
+ width += MENU_MARGIN_WIDTH;
+ }
+ if (width > labelWidth) {
+ labelWidth = width;
+ }
+
+ GetMenuAccelGeometry(menuPtr, mePtr, tkfont,
+ fmPtr, &width, &height);
+ if (height > mePtr->height) {
+ mePtr->height = height;
+ }
+ if (!mePtr->hideMargin) {
+ width += MENU_MARGIN_WIDTH;
+ }
+ if (width > accelWidth) {
+ accelWidth = width;
+ }
+
+ GetMenuIndicatorGeometry(menuPtr, mePtr, tkfont,
+ fmPtr, &width, &height);
+ if (height > mePtr->height) {
+ mePtr->height = height;
+ }
+ if (!mePtr->hideMargin) {
+ width += MENU_MARGIN_WIDTH;
+ }
+ if (width > indicatorSpace) {
+ indicatorSpace = width;
+ }
+
+ mePtr->height += 2 * menuPtr->activeBorderWidth +
+ MENU_DIVIDER_HEIGHT;
+ }
+ mePtr->y = y;
+ y += mePtr->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 * menuPtr->activeBorderWidth;
+ menuPtr->entries[j]->x = x;
+ menuPtr->entries[j]->entryFlags |= ENTRY_LAST_COLUMN;
+ }
+ windowWidth = x + indicatorSpace + labelWidth + accelWidth
+ + 2 * menuPtr->activeBorderWidth + 2 * menuPtr->borderWidth;
+
+
+ windowHeight += menuPtr->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;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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. Not applicable to UNIX.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * An idle handler is set up to do the reconfiguration.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMenuNotifyToplevelCreate(interp, menuName)
+ Tcl_Interp *interp; /* The interp the menu lives in. */
+ char *menuName; /* The name of the menu to
+ * reconfigure. */
+{
+ /*
+ * Nothing to do.
+ */
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpMenuInit --
+ *
+ * Does platform-specific initialization of menus.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMenuInit()
+{
+ /*
+ * Nothing to do.
+ */
+}
diff --git a/unix/tkUnixMenubu.c b/unix/tkUnixMenubu.c
new file mode 100644
index 0000000..b5f4fd5
--- /dev/null
+++ b/unix/tkUnixMenubu.c
@@ -0,0 +1,307 @@
+/*
+ * tkUnixMenubu.c --
+ *
+ * This file implements the Unix specific portion of the
+ * menubutton widget.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixMenubu.c 1.9 97/05/23 16:25:01
+ */
+
+#include "tkMenubutton.h"
+
+/*
+ * The structure below defines menubutton class behavior by means of
+ * procedures that can be invoked from generic window code.
+ */
+
+TkClassProcs tkpMenubuttonClass = {
+ NULL, /* createProc. */
+ TkMenuButtonWorldChanged, /* geometryProc. */
+ NULL /* modalProc. */
+};
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateMenuButton --
+ *
+ * Allocate a new TkMenuButton structure.
+ *
+ * Results:
+ * Returns a newly allocated TkMenuButton structure.
+ *
+ * Side effects:
+ * Registers an event handler for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkMenuButton *
+TkpCreateMenuButton(tkwin)
+ Tk_Window tkwin;
+{
+ return (TkMenuButton *)ckalloc(sizeof(TkMenuButton));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayMenuButton --
+ *
+ * This procedure is invoked to display a menubutton widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Commands are output to X to display the menubutton in its
+ * current mode.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayMenuButton(clientData)
+ ClientData clientData; /* Information about widget. */
+{
+ register TkMenuButton *mbPtr = (TkMenuButton *) clientData;
+ GC gc;
+ Tk_3DBorder border;
+ Pixmap pixmap;
+ int x = 0; /* Initialization needed only to stop
+ * compiler warning. */
+ int y;
+ register Tk_Window tkwin = mbPtr->tkwin;
+ int width, height;
+
+ mbPtr->flags &= ~REDRAW_PENDING;
+ if ((mbPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ return;
+ }
+
+ if ((mbPtr->state == tkDisabledUid) && (mbPtr->disabledFg != NULL)) {
+ gc = mbPtr->disabledGC;
+ border = mbPtr->normalBorder;
+ } else if ((mbPtr->state == tkActiveUid) && !Tk_StrictMotif(mbPtr->tkwin)) {
+ gc = mbPtr->activeTextGC;
+ border = mbPtr->activeBorder;
+ } else {
+ gc = mbPtr->normalTextGC;
+ border = mbPtr->normalBorder;
+ }
+
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the menu 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(mbPtr->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 (mbPtr->image != None) {
+ Tk_SizeOfImage(mbPtr->image, &width, &height);
+
+ imageOrBitmap:
+ TkComputeAnchor(mbPtr->anchor, tkwin, 0, 0,
+ width + mbPtr->indicatorWidth, height, &x, &y);
+ if (mbPtr->image != NULL) {
+ Tk_RedrawImage(mbPtr->image, 0, 0, width, height, pixmap,
+ x, y);
+ } else {
+ XCopyPlane(mbPtr->display, mbPtr->bitmap, pixmap,
+ gc, 0, 0, (unsigned) width, (unsigned) height, x, y, 1);
+ }
+ } else if (mbPtr->bitmap != None) {
+ Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
+ goto imageOrBitmap;
+ } else {
+ TkComputeAnchor(mbPtr->anchor, tkwin, mbPtr->padX, mbPtr->padY,
+ mbPtr->textWidth + mbPtr->indicatorWidth,
+ mbPtr->textHeight, &x, &y);
+ Tk_DrawTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout, x, y,
+ 0, -1);
+ Tk_UnderlineTextLayout(mbPtr->display, pixmap, gc, mbPtr->textLayout,
+ x, y, mbPtr->underline);
+ }
+
+ /*
+ * If the menu button is disabled with a stipple rather than a special
+ * foreground color, generate the stippled effect.
+ */
+
+ if ((mbPtr->state == tkDisabledUid)
+ && ((mbPtr->disabledFg == NULL) || (mbPtr->image != NULL))) {
+ XFillRectangle(mbPtr->display, pixmap, mbPtr->disabledGC,
+ mbPtr->inset, mbPtr->inset,
+ (unsigned) (Tk_Width(tkwin) - 2*mbPtr->inset),
+ (unsigned) (Tk_Height(tkwin) - 2*mbPtr->inset));
+ }
+
+ /*
+ * Draw the cascade indicator for the menu button on the
+ * right side of the window, if desired.
+ */
+
+ if (mbPtr->indicatorOn) {
+ int borderWidth;
+
+ borderWidth = (mbPtr->indicatorHeight+1)/3;
+ if (borderWidth < 1) {
+ borderWidth = 1;
+ }
+ /*y += mbPtr->textHeight / 2;*/
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ Tk_Width(tkwin) - mbPtr->inset - mbPtr->indicatorWidth
+ + mbPtr->indicatorHeight,
+ ((int) (Tk_Height(tkwin) - mbPtr->indicatorHeight))/2,
+ mbPtr->indicatorWidth - 2*mbPtr->indicatorHeight,
+ mbPtr->indicatorHeight, borderWidth, TK_RELIEF_RAISED);
+ }
+
+ /*
+ * Draw the border and traversal highlight last. This way, if the
+ * menu button's contents overflow onto the border they'll be covered
+ * up by the border.
+ */
+
+ if (mbPtr->relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tkwin, pixmap, border,
+ mbPtr->highlightWidth, mbPtr->highlightWidth,
+ Tk_Width(tkwin) - 2*mbPtr->highlightWidth,
+ Tk_Height(tkwin) - 2*mbPtr->highlightWidth,
+ mbPtr->borderWidth, mbPtr->relief);
+ }
+ if (mbPtr->highlightWidth != 0) {
+ GC gc;
+
+ if (mbPtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(mbPtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(mbPtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, mbPtr->highlightWidth, pixmap);
+ }
+
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(mbPtr->display, pixmap, Tk_WindowId(tkwin),
+ mbPtr->normalTextGC, 0, 0, (unsigned) Tk_Width(tkwin),
+ (unsigned) Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(mbPtr->display, pixmap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyMenuButton --
+ *
+ * Free data structures associated with the menubutton control.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Restores the default control state.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyMenuButton(mbPtr)
+ TkMenuButton *mbPtr;
+{
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpComputeMenuButtonGeometry --
+ *
+ * After changes in a menu button's text or bitmap, this procedure
+ * recomputes the menu button's geometry and passes this information
+ * along to the geometry manager for the window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The menu button's window may change size.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpComputeMenuButtonGeometry(mbPtr)
+ register TkMenuButton *mbPtr; /* Widget record for menu button. */
+{
+ int width, height, mm, pixels;
+
+ mbPtr->inset = mbPtr->highlightWidth + mbPtr->borderWidth;
+ if (mbPtr->image != None) {
+ Tk_SizeOfImage(mbPtr->image, &width, &height);
+ if (mbPtr->width > 0) {
+ width = mbPtr->width;
+ }
+ if (mbPtr->height > 0) {
+ height = mbPtr->height;
+ }
+ } else if (mbPtr->bitmap != None) {
+ Tk_SizeOfBitmap(mbPtr->display, mbPtr->bitmap, &width, &height);
+ if (mbPtr->width > 0) {
+ width = mbPtr->width;
+ }
+ if (mbPtr->height > 0) {
+ height = mbPtr->height;
+ }
+ } else {
+ Tk_FreeTextLayout(mbPtr->textLayout);
+ mbPtr->textLayout = Tk_ComputeTextLayout(mbPtr->tkfont, mbPtr->text,
+ -1, mbPtr->wrapLength, mbPtr->justify, 0, &mbPtr->textWidth,
+ &mbPtr->textHeight);
+ width = mbPtr->textWidth;
+ height = mbPtr->textHeight;
+ if (mbPtr->width > 0) {
+ width = mbPtr->width * Tk_TextWidth(mbPtr->tkfont, "0", 1);
+ }
+ if (mbPtr->height > 0) {
+ Tk_FontMetrics fm;
+
+ Tk_GetFontMetrics(mbPtr->tkfont, &fm);
+ height = mbPtr->height * fm.linespace;
+ }
+ width += 2*mbPtr->padX;
+ height += 2*mbPtr->padY;
+ }
+
+ if (mbPtr->indicatorOn) {
+ mm = WidthMMOfScreen(Tk_Screen(mbPtr->tkwin));
+ pixels = WidthOfScreen(Tk_Screen(mbPtr->tkwin));
+ mbPtr->indicatorHeight= (INDICATOR_HEIGHT * pixels)/(10*mm);
+ mbPtr->indicatorWidth = (INDICATOR_WIDTH * pixels)/(10*mm)
+ + 2*mbPtr->indicatorHeight;
+ width += mbPtr->indicatorWidth;
+ } else {
+ mbPtr->indicatorHeight = 0;
+ mbPtr->indicatorWidth = 0;
+ }
+
+ Tk_GeometryRequest(mbPtr->tkwin, (int) (width + 2*mbPtr->inset),
+ (int) (height + 2*mbPtr->inset));
+ Tk_SetInternalBorder(mbPtr->tkwin, mbPtr->inset);
+}
diff --git a/unix/tkUnixPort.h b/unix/tkUnixPort.h
new file mode 100644
index 0000000..146e60d
--- /dev/null
+++ b/unix/tkUnixPort.h
@@ -0,0 +1,235 @@
+/*
+ * tkUnixPort.h --
+ *
+ * This file is included by all of the Tk C files. It contains
+ * information that may be configuration-dependent, such as
+ * #includes for system include files and a few other things.
+ *
+ * Copyright (c) 1991-1993 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * SCCS: @(#) tkUnixPort.h 1.38 97/05/17 16:48:19
+ */
+
+#ifndef _UNIXPORT
+#define _UNIXPORT
+
+#define __UNIX__ 1
+
+/*
+ * Macro to use instead of "void" for arguments that must have
+ * type "void *" in ANSI C; maps them to type "char *" in
+ * non-ANSI systems. This macro may be used in some of the include
+ * files below, which is why it is defined here.
+ */
+
+#ifndef VOID
+# ifdef __STDC__
+# define VOID void
+# else
+# define VOID char
+# endif
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#else
+# include "../compat/limits.h"
+#endif
+#include <math.h>
+#include <pwd.h>
+#ifdef NO_STDLIB_H
+# include "../compat/stdlib.h"
+#else
+# include <stdlib.h>
+#endif
+#include <string.h>
+#include <sys/types.h>
+#include <sys/file.h>
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+#include <sys/stat.h>
+#ifndef _TCL
+# include <tcl.h>
+#endif
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#else
+# include "../compat/unistd.h"
+#endif
+#include <X11/Xlib.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+#include <X11/Xatom.h>
+#include <X11/Xproto.h>
+#include <X11/Xresource.h>
+#include <X11/Xutil.h>
+
+/*
+ * The following macro defines the type of the mask arguments to
+ * select:
+ */
+
+#ifndef NO_FD_SET
+# define SELECT_MASK fd_set
+#else
+# ifndef _AIX
+ typedef long fd_mask;
+# endif
+# if defined(_IBMR2)
+# define SELECT_MASK void
+# else
+# define SELECT_MASK int
+# endif
+#endif
+
+/*
+ * The following macro defines the number of fd_masks in an fd_set:
+ */
+
+#ifndef FD_SETSIZE
+# ifdef OPEN_MAX
+# define FD_SETSIZE OPEN_MAX
+# else
+# define FD_SETSIZE 256
+# endif
+#endif
+#if !defined(howmany)
+# define howmany(x, y) (((x)+((y)-1))/(y))
+#endif
+#ifndef NFDBITS
+# define NFDBITS NBBY*sizeof(fd_mask)
+#endif
+#define MASK_SIZE howmany(FD_SETSIZE, NFDBITS)
+
+/*
+ * Not all systems declare the errno variable in errno.h. so this
+ * file does it explicitly.
+ */
+
+extern int errno;
+
+/*
+ * Define "NBBY" (number of bits per byte) if it's not already defined.
+ */
+
+#ifndef NBBY
+# define NBBY 8
+#endif
+
+/*
+ * These macros are just wrappers for the equivalent X Region calls.
+ */
+
+#define TkClipBox(rgn, rect) XClipBox((Region) rgn, rect)
+#define TkCreateRegion() (TkRegion) XCreateRegion()
+#define TkDestroyRegion(rgn) XDestroyRegion((Region) rgn)
+#define TkIntersectRegion(a, b, r) XIntersectRegion((Region) a, \
+ (Region) b, (Region) r)
+#define TkRectInRegion(r, x, y, w, h) XRectInRegion((Region) r, x, y, w, h)
+#define TkSetRegion(d, gc, rgn) XSetRegion(d, gc, (Region) rgn)
+#define TkUnionRectWithRegion(rect, src, ret) XUnionRectWithRegion(rect, \
+ (Region) src, (Region) ret)
+
+/*
+ * The TkPutImage macro strips off the color table information, which isn't
+ * needed for X.
+ */
+
+#define TkPutImage(colors, ncolors, display, pixels, gc, image, destx, desty, srcx, srcy, width, height) \
+ XPutImage(display, pixels, gc, image, destx, desty, srcx, \
+ srcy, width, height);
+
+/*
+ * The following Tk functions are implemented as macros under Windows.
+ */
+
+#define TkGetNativeProlog(interp) TkGetProlog(interp)
+
+/*
+ * Supply macros for seek offsets, if they're not already provided by
+ * an include file.
+ */
+
+#ifndef SEEK_SET
+# define SEEK_SET 0
+#endif
+
+#ifndef SEEK_CUR
+# define SEEK_CUR 1
+#endif
+
+#ifndef SEEK_END
+# define SEEK_END 2
+#endif
+
+/*
+ * Declarations for various library procedures that may not be declared
+ * in any other header file.
+ */
+
+extern void panic _ANSI_ARGS_(TCL_VARARGS(char *, string));
+
+/*
+ * These functions do nothing under Unix, so we just eliminate calls to them.
+ */
+
+#define TkpDestroyButton(butPtr) {}
+#define TkSelUpdateClipboard(a,b) {}
+#define TkSetPixmapColormap(p,c) {}
+
+/*
+ * These calls implement native bitmaps which are not supported under
+ * UNIX. The macros eliminate the calls.
+ */
+
+#define TkpDefineNativeBitmaps()
+#define TkpCreateNativeBitmap(display, source) None
+#define TkpGetNativeAppBitmap(display, name, w, h) None
+
+/*
+ * This macro stores a representation of the window handle in a string.
+ */
+
+#define TkpPrintWindowId(buf,w) \
+ sprintf((buf), "0x%x", (unsigned int) (w))
+
+/*
+ * TkpScanWindowId is just an alias for Tcl_GetInt on Unix.
+ */
+
+#define TkpScanWindowId(i,s,wp) \
+ Tcl_GetInt((i),(s),(wp))
+
+/*
+ * This macro indicates that entry and text widgets should display
+ * the selection highlight regardless of which window has the focus.
+ */
+
+#define ALWAYS_SHOW_SELECTION
+
+/*
+ * The following declaration is used to get access to a private Tcl interface
+ * that is needed for portability reasons.
+ */
+
+EXTERN void TclpGetTime _ANSI_ARGS_((Tcl_Time *time));
+
+#endif /* _UNIXPORT */
diff --git a/unix/tkUnixScale.c b/unix/tkUnixScale.c
new file mode 100644
index 0000000..05b2a19
--- /dev/null
+++ b/unix/tkUnixScale.c
@@ -0,0 +1,828 @@
+/*
+ * tkUnixScale.c --
+ *
+ * This file implements the X 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.
+ *
+ * SCCS: @(#) tkUnixScale.c 1.5 96/07/31 14:22:29
+ */
+
+#include "tkScale.h"
+#include "tkInt.h"
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static void DisplayHorizontalScale _ANSI_ARGS_((TkScale *scalePtr,
+ Drawable drawable, XRectangle *drawnAreaPtr));
+static void DisplayHorizontalValue _ANSI_ARGS_((TkScale *scalePtr,
+ Drawable drawable, double value, int top));
+static void DisplayVerticalScale _ANSI_ARGS_((TkScale *scalePtr,
+ Drawable drawable, XRectangle *drawnAreaPtr));
+static void DisplayVerticalValue _ANSI_ARGS_((TkScale *scalePtr,
+ Drawable drawable, double value, int rightEdge));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateScale --
+ *
+ * Allocate a new TkScale structure.
+ *
+ * Results:
+ * Returns a newly allocated TkScale structure.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkScale *
+TkpCreateScale(tkwin)
+ Tk_Window tkwin;
+{
+ return (TkScale *) ckalloc(sizeof(TkScale));
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyScale --
+ *
+ * Destroy a TkScale structure.
+ *
+ * Results:
+ * None
+ *
+ * Side effects:
+ * Memory is freed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyScale(scalePtr)
+ TkScale *scalePtr;
+{
+ ckfree((char *) scalePtr);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayVerticalScale --
+ *
+ * This procedure redraws the contents of a vertical scale
+ * 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:
+ * There is no return value. If only a part of the scale needs
+ * to be redrawn, then drawnAreaPtr is modified to reflect the
+ * area that was actually modified.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayVerticalScale(scalePtr, drawable, drawnAreaPtr)
+ TkScale *scalePtr; /* Widget record for scale. */
+ Drawable drawable; /* Where to display scale (window
+ * or pixmap). */
+ XRectangle *drawnAreaPtr; /* Initally contains area of window;
+ * if only a part of the scale is
+ * redrawn, gets modified to reflect
+ * the part of the window that was
+ * redrawn. */
+{
+ Tk_Window tkwin = scalePtr->tkwin;
+ int x, y, width, height, shadowWidth;
+ double tickValue;
+ Tk_3DBorder sliderBorder;
+
+ /*
+ * Display the information from left to right across the window.
+ */
+
+ if (!(scalePtr->flags & REDRAW_OTHER)) {
+ drawnAreaPtr->x = scalePtr->vertTickRightX;
+ drawnAreaPtr->y = scalePtr->inset;
+ drawnAreaPtr->width = scalePtr->vertTroughX + scalePtr->width
+ + 2*scalePtr->borderWidth - scalePtr->vertTickRightX;
+ drawnAreaPtr->height -= 2*scalePtr->inset;
+ }
+ Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
+ drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
+ drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
+ if (scalePtr->flags & REDRAW_OTHER) {
+ /*
+ * Display the tick marks.
+ */
+
+ if (scalePtr->tickInterval != 0) {
+ for (tickValue = scalePtr->fromValue; ;
+ tickValue += scalePtr->tickInterval) {
+ /*
+ * The TkRoundToResolution call gets rid of accumulated
+ * round-off errors, if any.
+ */
+
+ tickValue = TkRoundToResolution(scalePtr, tickValue);
+ if (scalePtr->toValue >= scalePtr->fromValue) {
+ if (tickValue > scalePtr->toValue) {
+ break;
+ }
+ } else {
+ if (tickValue < scalePtr->toValue) {
+ break;
+ }
+ }
+ DisplayVerticalValue(scalePtr, drawable, tickValue,
+ scalePtr->vertTickRightX);
+ }
+ }
+ }
+
+ /*
+ * Display the value, if it is desired.
+ */
+
+ if (scalePtr->showValue) {
+ DisplayVerticalValue(scalePtr, drawable, scalePtr->value,
+ scalePtr->vertValueRightX);
+ }
+
+ /*
+ * Display the trough and the slider.
+ */
+
+ Tk_Draw3DRectangle(tkwin, drawable,
+ scalePtr->bgBorder, scalePtr->vertTroughX, scalePtr->inset,
+ scalePtr->width + 2*scalePtr->borderWidth,
+ Tk_Height(tkwin) - 2*scalePtr->inset, scalePtr->borderWidth,
+ TK_RELIEF_SUNKEN);
+ XFillRectangle(scalePtr->display, drawable, scalePtr->troughGC,
+ scalePtr->vertTroughX + scalePtr->borderWidth,
+ scalePtr->inset + scalePtr->borderWidth,
+ (unsigned) scalePtr->width,
+ (unsigned) (Tk_Height(tkwin) - 2*scalePtr->inset
+ - 2*scalePtr->borderWidth));
+ if (scalePtr->state == tkActiveUid) {
+ sliderBorder = scalePtr->activeBorder;
+ } else {
+ sliderBorder = scalePtr->bgBorder;
+ }
+ width = scalePtr->width;
+ height = scalePtr->sliderLength/2;
+ x = scalePtr->vertTroughX + scalePtr->borderWidth;
+ y = TkpValueToPixel(scalePtr, scalePtr->value) - height;
+ shadowWidth = scalePtr->borderWidth/2;
+ if (shadowWidth == 0) {
+ shadowWidth = 1;
+ }
+ Tk_Draw3DRectangle(tkwin, drawable, sliderBorder, x, y, width,
+ 2*height, shadowWidth, scalePtr->sliderRelief);
+ x += shadowWidth;
+ y += shadowWidth;
+ width -= 2*shadowWidth;
+ height -= shadowWidth;
+ Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y, width,
+ height, shadowWidth, scalePtr->sliderRelief);
+ Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y+height,
+ width, height, shadowWidth, scalePtr->sliderRelief);
+
+ /*
+ * Draw the label to the right of the scale.
+ */
+
+ if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
+ Tk_FontMetrics fm;
+
+ Tk_GetFontMetrics(scalePtr->tkfont, &fm);
+ Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
+ scalePtr->tkfont, scalePtr->label, scalePtr->labelLength,
+ scalePtr->vertLabelX, scalePtr->inset + (3*fm.ascent)/2);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayVerticalValue --
+ *
+ * This procedure is called to display values (scale readings)
+ * for vertically-oriented scales.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The numerical value corresponding to value is displayed with
+ * its right edge at "rightEdge", and at a vertical position in
+ * the scale that corresponds to "value".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayVerticalValue(scalePtr, drawable, value, rightEdge)
+ register TkScale *scalePtr; /* Information about widget in which to
+ * display value. */
+ Drawable drawable; /* Pixmap or window in which to draw
+ * the value. */
+ double value; /* Y-coordinate of number to display,
+ * specified in application coords, not
+ * in pixels (we'll compute pixels). */
+ int rightEdge; /* X-coordinate of right edge of text,
+ * specified in pixels. */
+{
+ register Tk_Window tkwin = scalePtr->tkwin;
+ int y, width, length;
+ char valueString[PRINT_CHARS];
+ Tk_FontMetrics fm;
+
+ Tk_GetFontMetrics(scalePtr->tkfont, &fm);
+ y = TkpValueToPixel(scalePtr, value) + fm.ascent/2;
+ sprintf(valueString, scalePtr->format, value);
+ length = strlen(valueString);
+ width = Tk_TextWidth(scalePtr->tkfont, valueString, length);
+
+ /*
+ * Adjust the y-coordinate if necessary to keep the text entirely
+ * inside the window.
+ */
+
+ if ((y - fm.ascent) < (scalePtr->inset + SPACING)) {
+ y = scalePtr->inset + SPACING + fm.ascent;
+ }
+ if ((y + fm.descent) > (Tk_Height(tkwin) - scalePtr->inset - SPACING)) {
+ y = Tk_Height(tkwin) - scalePtr->inset - SPACING - fm.descent;
+ }
+ Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
+ scalePtr->tkfont, valueString, length, rightEdge - width, y);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DisplayHorizontalScale --
+ *
+ * This procedure redraws the contents of a horizontal scale
+ * 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:
+ * There is no return value. If only a part of the scale needs
+ * to be redrawn, then drawnAreaPtr is modified to reflect the
+ * area that was actually modified.
+ *
+ * Side effects:
+ * Information appears on the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DisplayHorizontalScale(scalePtr, drawable, drawnAreaPtr)
+ TkScale *scalePtr; /* Widget record for scale. */
+ Drawable drawable; /* Where to display scale (window
+ * or pixmap). */
+ XRectangle *drawnAreaPtr; /* Initally contains area of window;
+ * if only a part of the scale is
+ * redrawn, gets modified to reflect
+ * the part of the window that was
+ * redrawn. */
+{
+ register Tk_Window tkwin = scalePtr->tkwin;
+ int x, y, width, height, shadowWidth;
+ double tickValue;
+ Tk_3DBorder sliderBorder;
+
+ /*
+ * Display the information from bottom to top across the window.
+ */
+
+ if (!(scalePtr->flags & REDRAW_OTHER)) {
+ drawnAreaPtr->x = scalePtr->inset;
+ drawnAreaPtr->y = scalePtr->horizValueY;
+ drawnAreaPtr->width -= 2*scalePtr->inset;
+ drawnAreaPtr->height = scalePtr->horizTroughY + scalePtr->width
+ + 2*scalePtr->borderWidth - scalePtr->horizValueY;
+ }
+ Tk_Fill3DRectangle(tkwin, drawable, scalePtr->bgBorder,
+ drawnAreaPtr->x, drawnAreaPtr->y, drawnAreaPtr->width,
+ drawnAreaPtr->height, 0, TK_RELIEF_FLAT);
+ if (scalePtr->flags & REDRAW_OTHER) {
+ /*
+ * Display the tick marks.
+ */
+
+ if (scalePtr->tickInterval != 0) {
+ for (tickValue = scalePtr->fromValue; ;
+ tickValue += scalePtr->tickInterval) {
+ /*
+ * The TkRoundToResolution call gets rid of accumulated
+ * round-off errors, if any.
+ */
+
+ tickValue = TkRoundToResolution(scalePtr, tickValue);
+ if (scalePtr->toValue >= scalePtr->fromValue) {
+ if (tickValue > scalePtr->toValue) {
+ break;
+ }
+ } else {
+ if (tickValue < scalePtr->toValue) {
+ break;
+ }
+ }
+ DisplayHorizontalValue(scalePtr, drawable, tickValue,
+ scalePtr->horizTickY);
+ }
+ }
+ }
+
+ /*
+ * Display the value, if it is desired.
+ */
+
+ if (scalePtr->showValue) {
+ DisplayHorizontalValue(scalePtr, drawable, scalePtr->value,
+ scalePtr->horizValueY);
+ }
+
+ /*
+ * Display the trough and the slider.
+ */
+
+ y = scalePtr->horizTroughY;
+ Tk_Draw3DRectangle(tkwin, drawable,
+ scalePtr->bgBorder, scalePtr->inset, y,
+ Tk_Width(tkwin) - 2*scalePtr->inset,
+ scalePtr->width + 2*scalePtr->borderWidth,
+ scalePtr->borderWidth, TK_RELIEF_SUNKEN);
+ XFillRectangle(scalePtr->display, drawable, scalePtr->troughGC,
+ scalePtr->inset + scalePtr->borderWidth,
+ y + scalePtr->borderWidth,
+ (unsigned) (Tk_Width(tkwin) - 2*scalePtr->inset
+ - 2*scalePtr->borderWidth),
+ (unsigned) scalePtr->width);
+ if (scalePtr->state == tkActiveUid) {
+ sliderBorder = scalePtr->activeBorder;
+ } else {
+ sliderBorder = scalePtr->bgBorder;
+ }
+ width = scalePtr->sliderLength/2;
+ height = scalePtr->width;
+ x = TkpValueToPixel(scalePtr, scalePtr->value) - width;
+ y += scalePtr->borderWidth;
+ shadowWidth = scalePtr->borderWidth/2;
+ if (shadowWidth == 0) {
+ shadowWidth = 1;
+ }
+ Tk_Draw3DRectangle(tkwin, drawable, sliderBorder,
+ x, y, 2*width, height, shadowWidth, scalePtr->sliderRelief);
+ x += shadowWidth;
+ y += shadowWidth;
+ width -= shadowWidth;
+ height -= 2*shadowWidth;
+ Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x, y, width, height,
+ shadowWidth, scalePtr->sliderRelief);
+ Tk_Fill3DRectangle(tkwin, drawable, sliderBorder, x+width, y,
+ width, height, shadowWidth, scalePtr->sliderRelief);
+
+ /*
+ * Draw the label at the top of the scale.
+ */
+
+ if ((scalePtr->flags & REDRAW_OTHER) && (scalePtr->labelLength != 0)) {
+ Tk_FontMetrics fm;
+
+ Tk_GetFontMetrics(scalePtr->tkfont, &fm);
+ Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
+ scalePtr->tkfont, scalePtr->label, scalePtr->labelLength,
+ scalePtr->inset + fm.ascent/2, scalePtr->horizLabelY + fm.ascent);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * DisplayHorizontalValue --
+ *
+ * This procedure is called to display values (scale readings)
+ * for horizontally-oriented scales.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The numerical value corresponding to value is displayed with
+ * its bottom edge at "bottom", and at a horizontal position in
+ * the scale that corresponds to "value".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+DisplayHorizontalValue(scalePtr, drawable, value, top)
+ register TkScale *scalePtr; /* Information about widget in which to
+ * display value. */
+ Drawable drawable; /* Pixmap or window in which to draw
+ * the value. */
+ double value; /* X-coordinate of number to display,
+ * specified in application coords, not
+ * in pixels (we'll compute pixels). */
+ int top; /* Y-coordinate of top edge of text,
+ * specified in pixels. */
+{
+ register Tk_Window tkwin = scalePtr->tkwin;
+ int x, y, length, width;
+ char valueString[PRINT_CHARS];
+ Tk_FontMetrics fm;
+
+ x = TkpValueToPixel(scalePtr, value);
+ Tk_GetFontMetrics(scalePtr->tkfont, &fm);
+ y = top + fm.ascent;
+ sprintf(valueString, scalePtr->format, value);
+ length = strlen(valueString);
+ width = Tk_TextWidth(scalePtr->tkfont, valueString, length);
+
+ /*
+ * Adjust the x-coordinate if necessary to keep the text entirely
+ * inside the window.
+ */
+
+ x -= (width)/2;
+ if (x < (scalePtr->inset + SPACING)) {
+ x = scalePtr->inset + SPACING;
+ }
+ if (x > (Tk_Width(tkwin) - scalePtr->inset)) {
+ x = Tk_Width(tkwin) - scalePtr->inset - SPACING - width;
+ }
+ Tk_DrawChars(scalePtr->display, drawable, scalePtr->textGC,
+ scalePtr->tkfont, valueString, length, x, y);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDisplayScale --
+ *
+ * This procedure is invoked as an idle handler to redisplay
+ * the contents of a scale widget.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The scale gets redisplayed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDisplayScale(clientData)
+ ClientData clientData; /* Widget record for scale. */
+{
+ TkScale *scalePtr = (TkScale *) clientData;
+ Tk_Window tkwin = scalePtr->tkwin;
+ Tcl_Interp *interp = scalePtr->interp;
+ Pixmap pixmap;
+ int result;
+ char string[PRINT_CHARS];
+ XRectangle drawnArea;
+
+ if ((scalePtr->tkwin == NULL) || !Tk_IsMapped(scalePtr->tkwin)) {
+ goto done;
+ }
+
+ /*
+ * Invoke the scale's command if needed.
+ */
+
+ Tcl_Preserve((ClientData) scalePtr);
+ Tcl_Preserve((ClientData) interp);
+ if ((scalePtr->flags & INVOKE_COMMAND) && (scalePtr->command != NULL)) {
+ sprintf(string, scalePtr->format, scalePtr->value);
+ result = Tcl_VarEval(interp, scalePtr->command, " ", string,
+ (char *) NULL);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (command executed by scale)");
+ Tcl_BackgroundError(interp);
+ }
+ }
+ Tcl_Release((ClientData) interp);
+ scalePtr->flags &= ~INVOKE_COMMAND;
+ if (scalePtr->tkwin == NULL) {
+ Tcl_Release((ClientData) scalePtr);
+ return;
+ }
+ Tcl_Release((ClientData) scalePtr);
+
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the scale 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(scalePtr->display, Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+ drawnArea.x = 0;
+ drawnArea.y = 0;
+ drawnArea.width = Tk_Width(tkwin);
+ drawnArea.height = Tk_Height(tkwin);
+
+ /*
+ * Much of the redisplay is done totally differently for
+ * horizontal and vertical scales. Handle the part that's
+ * different.
+ */
+
+ if (scalePtr->vertical) {
+ DisplayVerticalScale(scalePtr, pixmap, &drawnArea);
+ } else {
+ DisplayHorizontalScale(scalePtr, pixmap, &drawnArea);
+ }
+
+ /*
+ * Now handle the part of redisplay that is the same for
+ * horizontal and vertical scales: border and traversal
+ * highlight.
+ */
+
+ if (scalePtr->flags & REDRAW_OTHER) {
+ if (scalePtr->relief != TK_RELIEF_FLAT) {
+ Tk_Draw3DRectangle(tkwin, pixmap, scalePtr->bgBorder,
+ scalePtr->highlightWidth, scalePtr->highlightWidth,
+ Tk_Width(tkwin) - 2*scalePtr->highlightWidth,
+ Tk_Height(tkwin) - 2*scalePtr->highlightWidth,
+ scalePtr->borderWidth, scalePtr->relief);
+ }
+ if (scalePtr->highlightWidth != 0) {
+ GC gc;
+
+ if (scalePtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(scalePtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(scalePtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, scalePtr->highlightWidth, pixmap);
+ }
+ }
+
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(scalePtr->display, pixmap, Tk_WindowId(tkwin),
+ scalePtr->copyGC, drawnArea.x, drawnArea.y, drawnArea.width,
+ drawnArea.height, drawnArea.x, drawnArea.y);
+ Tk_FreePixmap(scalePtr->display, pixmap);
+
+ done:
+ scalePtr->flags &= ~REDRAW_ALL;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpScaleElement --
+ *
+ * Determine which part of a scale widget lies under a given
+ * point.
+ *
+ * Results:
+ * The return value is either TROUGH1, SLIDER, TROUGH2, or
+ * OTHER, depending on which of the scale's active elements
+ * (if any) is under the point at (x,y).
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpScaleElement(scalePtr, x, y)
+ TkScale *scalePtr; /* Widget record for scale. */
+ int x, y; /* Coordinates within scalePtr's window. */
+{
+ int sliderFirst;
+
+ if (scalePtr->vertical) {
+ if ((x < scalePtr->vertTroughX)
+ || (x >= (scalePtr->vertTroughX + 2*scalePtr->borderWidth +
+ scalePtr->width))) {
+ return OTHER;
+ }
+ if ((y < scalePtr->inset)
+ || (y >= (Tk_Height(scalePtr->tkwin) - scalePtr->inset))) {
+ return OTHER;
+ }
+ sliderFirst = TkpValueToPixel(scalePtr, scalePtr->value)
+ - scalePtr->sliderLength/2;
+ if (y < sliderFirst) {
+ return TROUGH1;
+ }
+ if (y < (sliderFirst+scalePtr->sliderLength)) {
+ return SLIDER;
+ }
+ return TROUGH2;
+ }
+
+ if ((y < scalePtr->horizTroughY)
+ || (y >= (scalePtr->horizTroughY + 2*scalePtr->borderWidth +
+ scalePtr->width))) {
+ return OTHER;
+ }
+ if ((x < scalePtr->inset)
+ || (x >= (Tk_Width(scalePtr->tkwin) - scalePtr->inset))) {
+ return OTHER;
+ }
+ sliderFirst = TkpValueToPixel(scalePtr, scalePtr->value)
+ - scalePtr->sliderLength/2;
+ if (x < sliderFirst) {
+ return TROUGH1;
+ }
+ if (x < (sliderFirst+scalePtr->sliderLength)) {
+ return SLIDER;
+ }
+ return TROUGH2;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkpSetScaleValue --
+ *
+ * This procedure changes the value of a scale and invokes
+ * a Tcl command to reflect the current position of a scale
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A Tcl command is invoked, and an additional error-processing
+ * command may also be invoked. The scale's slider is redrawn.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkpSetScaleValue(scalePtr, value, setVar, invokeCommand)
+ register TkScale *scalePtr; /* Info about widget. */
+ double value; /* New value for scale. Gets adjusted
+ * if it's off the scale. */
+ int setVar; /* Non-zero means reflect new value through
+ * to associated variable, if any. */
+ int invokeCommand; /* Non-zero means invoked -command option
+ * to notify of new value, 0 means don't. */
+{
+ char string[PRINT_CHARS];
+
+ value = TkRoundToResolution(scalePtr, value);
+ if ((value < scalePtr->fromValue)
+ ^ (scalePtr->toValue < scalePtr->fromValue)) {
+ value = scalePtr->fromValue;
+ }
+ if ((value > scalePtr->toValue)
+ ^ (scalePtr->toValue < scalePtr->fromValue)) {
+ value = scalePtr->toValue;
+ }
+ if (scalePtr->flags & NEVER_SET) {
+ scalePtr->flags &= ~NEVER_SET;
+ } else if (scalePtr->value == value) {
+ return;
+ }
+ scalePtr->value = value;
+ if (invokeCommand) {
+ scalePtr->flags |= INVOKE_COMMAND;
+ }
+ TkEventuallyRedrawScale(scalePtr, REDRAW_SLIDER);
+
+ if (setVar && (scalePtr->varName != NULL)) {
+ sprintf(string, scalePtr->format, scalePtr->value);
+ scalePtr->flags |= SETTING_VAR;
+ Tcl_SetVar(scalePtr->interp, scalePtr->varName, string,
+ TCL_GLOBAL_ONLY);
+ scalePtr->flags &= ~SETTING_VAR;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpPixelToValue --
+ *
+ * Given a pixel within a scale window, return the scale
+ * reading corresponding to that pixel.
+ *
+ * Results:
+ * A double-precision scale reading. If the value is outside
+ * the legal range for the scale then it's rounded to the nearest
+ * end of the scale.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+double
+TkpPixelToValue(scalePtr, x, y)
+ register TkScale *scalePtr; /* Information about widget. */
+ int x, y; /* Coordinates of point within
+ * window. */
+{
+ double value, pixelRange;
+
+ if (scalePtr->vertical) {
+ pixelRange = Tk_Height(scalePtr->tkwin) - scalePtr->sliderLength
+ - 2*scalePtr->inset - 2*scalePtr->borderWidth;
+ value = y;
+ } else {
+ pixelRange = Tk_Width(scalePtr->tkwin) - scalePtr->sliderLength
+ - 2*scalePtr->inset - 2*scalePtr->borderWidth;
+ value = x;
+ }
+
+ if (pixelRange <= 0) {
+ /*
+ * Not enough room for the slider to actually slide: just return
+ * the scale's current value.
+ */
+
+ return scalePtr->value;
+ }
+ value -= scalePtr->sliderLength/2 + scalePtr->inset
+ + scalePtr->borderWidth;
+ value /= pixelRange;
+ if (value < 0) {
+ value = 0;
+ }
+ if (value > 1) {
+ value = 1;
+ }
+ value = scalePtr->fromValue +
+ value * (scalePtr->toValue - scalePtr->fromValue);
+ return TkRoundToResolution(scalePtr, value);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpValueToPixel --
+ *
+ * Given a reading of the scale, return the x-coordinate or
+ * y-coordinate corresponding to that reading, depending on
+ * whether the scale is vertical or horizontal, respectively.
+ *
+ * Results:
+ * An integer value giving the pixel location corresponding
+ * to reading. The value is restricted to lie within the
+ * defined range for the scale.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpValueToPixel(scalePtr, value)
+ register TkScale *scalePtr; /* Information about widget. */
+ double value; /* Reading of the widget. */
+{
+ int y, pixelRange;
+ double valueRange;
+
+ valueRange = scalePtr->toValue - scalePtr->fromValue;
+ pixelRange = (scalePtr->vertical ? Tk_Height(scalePtr->tkwin)
+ : Tk_Width(scalePtr->tkwin)) - scalePtr->sliderLength
+ - 2*scalePtr->inset - 2*scalePtr->borderWidth;
+ if (valueRange == 0) {
+ y = 0;
+ } else {
+ y = (int) ((value - scalePtr->fromValue) * pixelRange
+ / valueRange + 0.5);
+ if (y < 0) {
+ y = 0;
+ } else if (y > pixelRange) {
+ y = pixelRange;
+ }
+ }
+ y += scalePtr->sliderLength/2 + scalePtr->inset + scalePtr->borderWidth;
+ return y;
+}
diff --git a/unix/tkUnixScrlbr.c b/unix/tkUnixScrlbr.c
new file mode 100644
index 0000000..74b46e8
--- /dev/null
+++ b/unix/tkUnixScrlbr.c
@@ -0,0 +1,476 @@
+/*
+ * tkUnixScrollbar.c --
+ *
+ * This file implements the Unix 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.
+ *
+ * SCCS: @(#) tkUnixScrlbr.c 1.8 96/12/10 20:05:07
+ */
+
+#include "tkScrollbar.h"
+
+/*
+ * Minimum slider length, in pixels (designed to make sure that the slider
+ * is always easy to grab with the mouse).
+ */
+
+#define MIN_SLIDER_LENGTH 5
+
+/*
+ * Declaration of Unix specific scrollbar structure.
+ */
+
+typedef struct UnixScrollbar {
+ TkScrollbar info; /* Generic scrollbar info. */
+ GC troughGC; /* For drawing trough. */
+ GC copyGC; /* Used for copying from pixmap onto screen. */
+} UnixScrollbar;
+
+/*
+ * The class procedure table for the scrollbar widget.
+ */
+
+TkClassProcs tkpScrollbarProcs = {
+ NULL, /* createProc. */
+ NULL, /* geometryProc. */
+ NULL /* modalProc. */
+};
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpCreateScrollbar --
+ *
+ * Allocate a new TkScrollbar structure.
+ *
+ * Results:
+ * Returns a newly allocated TkScrollbar structure.
+ *
+ * Side effects:
+ * Registers an event handler for the widget.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkScrollbar *
+TkpCreateScrollbar(tkwin)
+ Tk_Window tkwin;
+{
+ UnixScrollbar *scrollPtr = (UnixScrollbar *)ckalloc(sizeof(UnixScrollbar));
+ scrollPtr->troughGC = None;
+ scrollPtr->copyGC = None;
+
+ Tk_CreateEventHandler(tkwin,
+ ExposureMask|StructureNotifyMask|FocusChangeMask,
+ TkScrollbarEventProc, (ClientData) scrollPtr);
+
+ return (TkScrollbar *) scrollPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * 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 clientData; /* Information about window. */
+{
+ register TkScrollbar *scrollPtr = (TkScrollbar *) clientData;
+ register Tk_Window tkwin = scrollPtr->tkwin;
+ XPoint points[7];
+ Tk_3DBorder border;
+ int relief, width, elementBorderWidth;
+ Pixmap pixmap;
+
+ if ((scrollPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
+ goto done;
+ }
+
+ if (scrollPtr->vertical) {
+ width = Tk_Width(tkwin) - 2*scrollPtr->inset;
+ } else {
+ width = Tk_Height(tkwin) - 2*scrollPtr->inset;
+ }
+ elementBorderWidth = scrollPtr->elementBorderWidth;
+ if (elementBorderWidth < 0) {
+ elementBorderWidth = scrollPtr->borderWidth;
+ }
+
+ /*
+ * In order to avoid screen flashes, this procedure redraws
+ * the scrollbar 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(scrollPtr->display, Tk_WindowId(tkwin),
+ Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
+
+ if (scrollPtr->highlightWidth != 0) {
+ GC gc;
+
+ if (scrollPtr->flags & GOT_FOCUS) {
+ gc = Tk_GCForColor(scrollPtr->highlightColorPtr, pixmap);
+ } else {
+ gc = Tk_GCForColor(scrollPtr->highlightBgColorPtr, pixmap);
+ }
+ Tk_DrawFocusHighlight(tkwin, gc, scrollPtr->highlightWidth, pixmap);
+ }
+ Tk_Draw3DRectangle(tkwin, pixmap, scrollPtr->bgBorder,
+ scrollPtr->highlightWidth, scrollPtr->highlightWidth,
+ Tk_Width(tkwin) - 2*scrollPtr->highlightWidth,
+ Tk_Height(tkwin) - 2*scrollPtr->highlightWidth,
+ scrollPtr->borderWidth, scrollPtr->relief);
+ XFillRectangle(scrollPtr->display, pixmap,
+ ((UnixScrollbar*)scrollPtr)->troughGC,
+ scrollPtr->inset, scrollPtr->inset,
+ (unsigned) (Tk_Width(tkwin) - 2*scrollPtr->inset),
+ (unsigned) (Tk_Height(tkwin) - 2*scrollPtr->inset));
+
+ /*
+ * Draw the top or left arrow. The coordinates of the polygon
+ * points probably seem odd, but they were carefully chosen with
+ * respect to X's rules for filling polygons. These point choices
+ * cause the arrows to just fill the narrow dimension of the
+ * scrollbar and be properly centered.
+ */
+
+ if (scrollPtr->activeField == TOP_ARROW) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == TOP_ARROW ? scrollPtr->activeRelief
+ : TK_RELIEF_RAISED;
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ }
+ if (scrollPtr->vertical) {
+ points[0].x = scrollPtr->inset - 1;
+ points[0].y = scrollPtr->arrowLength + scrollPtr->inset - 1;
+ points[1].x = width + scrollPtr->inset;
+ points[1].y = points[0].y;
+ points[2].x = width/2 + scrollPtr->inset;
+ points[2].y = scrollPtr->inset - 1;
+ Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
+ elementBorderWidth, relief);
+ } else {
+ points[0].x = scrollPtr->arrowLength + scrollPtr->inset - 1;
+ points[0].y = scrollPtr->inset - 1;
+ points[1].x = scrollPtr->inset;
+ points[1].y = width/2 + scrollPtr->inset;
+ points[2].x = points[0].x;
+ points[2].y = width + scrollPtr->inset;
+ Tk_Fill3DPolygon(tkwin, pixmap, border, points, 3,
+ elementBorderWidth, relief);
+ }
+
+ /*
+ * Display the bottom or right arrow.
+ */
+
+ if (scrollPtr->activeField == BOTTOM_ARROW) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == BOTTOM_ARROW
+ ? scrollPtr->activeRelief : TK_RELIEF_RAISED;
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ }
+ if (scrollPtr->vertical) {
+ points[0].x = scrollPtr->inset;
+ points[0].y = Tk_Height(tkwin) - scrollPtr->arrowLength
+ - scrollPtr->inset + 1;
+ points[1].x = width/2 + scrollPtr->inset;
+ points[1].y = Tk_Height(tkwin) - scrollPtr->inset;
+ points[2].x = width + scrollPtr->inset;
+ points[2].y = points[0].y;
+ Tk_Fill3DPolygon(tkwin, pixmap, border,
+ points, 3, elementBorderWidth, relief);
+ } else {
+ points[0].x = Tk_Width(tkwin) - scrollPtr->arrowLength
+ - scrollPtr->inset + 1;
+ points[0].y = scrollPtr->inset - 1;
+ points[1].x = points[0].x;
+ points[1].y = width + scrollPtr->inset;
+ points[2].x = Tk_Width(tkwin) - scrollPtr->inset;
+ points[2].y = width/2 + scrollPtr->inset;
+ Tk_Fill3DPolygon(tkwin, pixmap, border,
+ points, 3, elementBorderWidth, relief);
+ }
+
+ /*
+ * Display the slider.
+ */
+
+ if (scrollPtr->activeField == SLIDER) {
+ border = scrollPtr->activeBorder;
+ relief = scrollPtr->activeField == SLIDER ? scrollPtr->activeRelief
+ : TK_RELIEF_RAISED;
+ } else {
+ border = scrollPtr->bgBorder;
+ relief = TK_RELIEF_RAISED;
+ }
+ if (scrollPtr->vertical) {
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ scrollPtr->inset, scrollPtr->sliderFirst,
+ width, scrollPtr->sliderLast - scrollPtr->sliderFirst,
+ elementBorderWidth, relief);
+ } else {
+ Tk_Fill3DRectangle(tkwin, pixmap, border,
+ scrollPtr->sliderFirst, scrollPtr->inset,
+ scrollPtr->sliderLast - scrollPtr->sliderFirst, width,
+ elementBorderWidth, relief);
+ }
+
+ /*
+ * Copy the information from the off-screen pixmap onto the screen,
+ * then delete the pixmap.
+ */
+
+ XCopyArea(scrollPtr->display, pixmap, Tk_WindowId(tkwin),
+ ((UnixScrollbar*)scrollPtr)->copyGC, 0, 0,
+ (unsigned) Tk_Width(tkwin), (unsigned) Tk_Height(tkwin), 0, 0);
+ Tk_FreePixmap(scrollPtr->display, pixmap);
+
+ done:
+ scrollPtr->flags &= ~REDRAW_PENDING;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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.
+ *
+ *----------------------------------------------------------------------
+ */
+
+extern void
+TkpComputeScrollbarGeometry(scrollPtr)
+ register TkScrollbar *scrollPtr; /* Scrollbar whose geometry may
+ * have changed. */
+{
+ int width, fieldLength;
+
+ if (scrollPtr->highlightWidth < 0) {
+ scrollPtr->highlightWidth = 0;
+ }
+ scrollPtr->inset = scrollPtr->highlightWidth + scrollPtr->borderWidth;
+ width = (scrollPtr->vertical) ? Tk_Width(scrollPtr->tkwin)
+ : Tk_Height(scrollPtr->tkwin);
+ scrollPtr->arrowLength = width - 2*scrollPtr->inset + 1;
+ fieldLength = (scrollPtr->vertical ? Tk_Height(scrollPtr->tkwin)
+ : Tk_Width(scrollPtr->tkwin))
+ - 2*(scrollPtr->arrowLength + scrollPtr->inset);
+ if (fieldLength < 0) {
+ fieldLength = 0;
+ }
+ scrollPtr->sliderFirst = fieldLength*scrollPtr->firstFraction;
+ scrollPtr->sliderLast = 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 - 2*scrollPtr->borderWidth)) {
+ scrollPtr->sliderFirst = fieldLength - 2*scrollPtr->borderWidth;
+ }
+ if (scrollPtr->sliderFirst < 0) {
+ scrollPtr->sliderFirst = 0;
+ }
+ if (scrollPtr->sliderLast < (scrollPtr->sliderFirst
+ + MIN_SLIDER_LENGTH)) {
+ scrollPtr->sliderLast = scrollPtr->sliderFirst + MIN_SLIDER_LENGTH;
+ }
+ if (scrollPtr->sliderLast > fieldLength) {
+ scrollPtr->sliderLast = fieldLength;
+ }
+ scrollPtr->sliderFirst += scrollPtr->arrowLength + scrollPtr->inset;
+ scrollPtr->sliderLast += scrollPtr->arrowLength + scrollPtr->inset;
+
+ /*
+ * 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->inset,
+ 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
+ + scrollPtr->inset));
+ } else {
+ Tk_GeometryRequest(scrollPtr->tkwin,
+ 2*(scrollPtr->arrowLength + scrollPtr->borderWidth
+ + scrollPtr->inset), scrollPtr->width + 2*scrollPtr->inset);
+ }
+ Tk_SetInternalBorder(scrollPtr->tkwin, scrollPtr->inset);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpDestroyScrollbar --
+ *
+ * Free data structures associated with the scrollbar control.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Frees the GCs associated with the scrollbar.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpDestroyScrollbar(scrollPtr)
+ TkScrollbar *scrollPtr;
+{
+ UnixScrollbar *unixScrollPtr = (UnixScrollbar *)scrollPtr;
+
+ if (unixScrollPtr->troughGC != None) {
+ Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC);
+ }
+ if (unixScrollPtr->copyGC != None) {
+ Tk_FreeGC(scrollPtr->display, unixScrollPtr->copyGC);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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:
+ * Configuration info may get changed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpConfigureScrollbar(scrollPtr)
+ register TkScrollbar *scrollPtr; /* Information about widget; may or
+ * may not already have values for
+ * some fields. */
+{
+ XGCValues gcValues;
+ GC new;
+ UnixScrollbar *unixScrollPtr = (UnixScrollbar *) scrollPtr;
+
+ Tk_SetBackgroundFromBorder(scrollPtr->tkwin, scrollPtr->bgBorder);
+
+ gcValues.foreground = scrollPtr->troughColorPtr->pixel;
+ new = Tk_GetGC(scrollPtr->tkwin, GCForeground, &gcValues);
+ if (unixScrollPtr->troughGC != None) {
+ Tk_FreeGC(scrollPtr->display, unixScrollPtr->troughGC);
+ }
+ unixScrollPtr->troughGC = new;
+ if (unixScrollPtr->copyGC == None) {
+ gcValues.graphics_exposures = False;
+ unixScrollPtr->copyGC = Tk_GetGC(scrollPtr->tkwin, GCGraphicsExposures,
+ &gcValues);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * 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(scrollPtr, x, y)
+ register TkScrollbar *scrollPtr; /* Scrollbar widget record. */
+ int x, 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;
+}
diff --git a/unix/tkUnixSelect.c b/unix/tkUnixSelect.c
new file mode 100644
index 0000000..404631e
--- /dev/null
+++ b/unix/tkUnixSelect.c
@@ -0,0 +1,1189 @@
+/*
+ * tkUnixSelect.c --
+ *
+ * This file contains X specific routines for manipulating
+ * selections.
+ *
+ * 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.
+ *
+ * SCCS: @(#) tkUnixSelect.c 1.5 96/03/29 14:14:31
+ */
+
+#include "tkInt.h"
+#include "tkSelect.h"
+
+/*
+ * When handling INCR-style selection retrievals, the selection owner
+ * uses the following data structure to communicate between the
+ * ConvertSelection procedure and TkSelPropProc.
+ */
+
+typedef struct IncrInfo {
+ TkWindow *winPtr; /* Window that owns selection. */
+ Atom selection; /* Selection that is being retrieved. */
+ Atom *multAtoms; /* Information about conversions to
+ * perform: one or more pairs of
+ * (target, property). This either
+ * points to a retrieved property (for
+ * MULTIPLE retrievals) or to a static
+ * array. */
+ unsigned long numConversions;
+ /* Number of entries in offsets (same as
+ * # of pairs in multAtoms). */
+ int *offsets; /* One entry for each pair in
+ * multAtoms; -1 means all data has
+ * been transferred for this
+ * conversion. -2 means only the
+ * final zero-length transfer still
+ * has to be done. Otherwise it is the
+ * offset of the next chunk of data
+ * to transfer. This array is malloc-ed. */
+ int numIncrs; /* Number of entries in offsets that
+ * aren't -1 (i.e. # of INCR-mode transfers
+ * not yet completed). */
+ Tcl_TimerToken timeout; /* Token for timer procedure. */
+ int idleTime; /* Number of seconds since we heard
+ * anything from the selection
+ * requestor. */
+ Window reqWindow; /* Requestor's window id. */
+ Time time; /* Timestamp corresponding to
+ * selection at beginning of request;
+ * used to abort transfer if selection
+ * changes. */
+ struct IncrInfo *nextPtr; /* Next in list of all INCR-style
+ * retrievals currently pending. */
+} IncrInfo;
+
+static IncrInfo *pendingIncrs = NULL;
+ /* List of all incr structures
+ * currently active. */
+
+/*
+ * Largest property that we'll accept when sending or receiving the
+ * selection:
+ */
+
+#define MAX_PROP_WORDS 100000
+
+static TkSelRetrievalInfo *pendingRetrievals = NULL;
+ /* List of all retrievals currently
+ * being waited for. */
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static void ConvertSelection _ANSI_ARGS_((TkWindow *winPtr,
+ XSelectionRequestEvent *eventPtr));
+static void IncrTimeoutProc _ANSI_ARGS_((ClientData clientData));
+static char * SelCvtFromX _ANSI_ARGS_((long *propPtr, int numValues,
+ Atom type, Tk_Window tkwin));
+static long * SelCvtToX _ANSI_ARGS_((char *string, Atom type,
+ Tk_Window tkwin, int *numLongsPtr));
+static int SelectionSize _ANSI_ARGS_((TkSelHandler *selPtr));
+static void SelRcvIncrProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void SelTimeoutProc _ANSI_ARGS_((ClientData clientData));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSelGetSelection --
+ *
+ * Retrieve the specified selection from another process.
+ *
+ * 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 interp->result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkSelGetSelection(interp, tkwin, selection, target, proc, clientData)
+ 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. */
+{
+ TkSelRetrievalInfo retr;
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ TkDisplay *dispPtr = winPtr->dispPtr;
+
+ /*
+ * The selection is owned by some other process. To
+ * retrieve it, first record information about the retrieval
+ * in progress. Use an internal window as the requestor.
+ */
+
+ retr.interp = interp;
+ if (dispPtr->clipWindow == NULL) {
+ int result;
+
+ result = TkClipInit(interp, dispPtr);
+ if (result != TCL_OK) {
+ return result;
+ }
+ }
+ retr.winPtr = (TkWindow *) dispPtr->clipWindow;
+ retr.selection = selection;
+ retr.property = selection;
+ retr.target = target;
+ retr.proc = proc;
+ retr.clientData = clientData;
+ retr.result = -1;
+ retr.idleTime = 0;
+ retr.nextPtr = pendingRetrievals;
+ pendingRetrievals = &retr;
+
+ /*
+ * Initiate the request for the selection. Note: can't use
+ * TkCurrentTime for the time. If we do, and this application hasn't
+ * received any X events in a long time, the current time will be way
+ * in the past and could even predate the time when the selection was
+ * made; if this happens, the request will be rejected.
+ */
+
+ XConvertSelection(winPtr->display, retr.selection, retr.target,
+ retr.property, retr.winPtr->window, CurrentTime);
+
+ /*
+ * Enter a loop processing X events until the selection
+ * has been retrieved and processed. If no response is
+ * received within a few seconds, then timeout.
+ */
+
+ retr.timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
+ (ClientData) &retr);
+ while (retr.result == -1) {
+ Tcl_DoOneEvent(0);
+ }
+ Tcl_DeleteTimerHandler(retr.timeout);
+
+ /*
+ * Unregister the information about the selection retrieval
+ * in progress.
+ */
+
+ if (pendingRetrievals == &retr) {
+ pendingRetrievals = retr.nextPtr;
+ } else {
+ TkSelRetrievalInfo *retrPtr;
+
+ for (retrPtr = pendingRetrievals; retrPtr != NULL;
+ retrPtr = retrPtr->nextPtr) {
+ if (retrPtr->nextPtr == &retr) {
+ retrPtr->nextPtr = retr.nextPtr;
+ break;
+ }
+ }
+ }
+ return retr.result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkSelPropProc --
+ *
+ * This procedure is invoked when property-change events
+ * occur on windows not known to the toolkit. Its function
+ * is to implement the sending side of the INCR selection
+ * retrieval protocol when the selection requestor deletes
+ * the property containing a part of the selection.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the property that is receiving the selection was just
+ * deleted, then a new piece of the selection is fetched and
+ * placed in the property, until eventually there's no more
+ * selection to fetch.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkSelPropProc(eventPtr)
+ register XEvent *eventPtr; /* X PropertyChange event. */
+{
+ register IncrInfo *incrPtr;
+ int i, format;
+ Atom target, formatType;
+ register TkSelHandler *selPtr;
+ long buffer[TK_SEL_WORDS_AT_ONCE];
+ int numItems;
+ char *propPtr;
+ Tk_ErrorHandler errorHandler;
+
+ /*
+ * See if this event announces the deletion of a property being
+ * used for an INCR transfer. If so, then add the next chunk of
+ * data to the property.
+ */
+
+ if (eventPtr->xproperty.state != PropertyDelete) {
+ return;
+ }
+ for (incrPtr = pendingIncrs; incrPtr != NULL;
+ incrPtr = incrPtr->nextPtr) {
+ if (incrPtr->reqWindow != eventPtr->xproperty.window) {
+ continue;
+ }
+ for (i = 0; i < incrPtr->numConversions; i++) {
+ if ((eventPtr->xproperty.atom != incrPtr->multAtoms[2*i + 1])
+ || (incrPtr->offsets[i] == -1)){
+ continue;
+ }
+ target = incrPtr->multAtoms[2*i];
+ incrPtr->idleTime = 0;
+ for (selPtr = incrPtr->winPtr->selHandlerList; ;
+ selPtr = selPtr->nextPtr) {
+ if (selPtr == NULL) {
+ incrPtr->multAtoms[2*i + 1] = None;
+ incrPtr->offsets[i] = -1;
+ incrPtr->numIncrs --;
+ return;
+ }
+ if ((selPtr->target == target)
+ && (selPtr->selection == incrPtr->selection)) {
+ formatType = selPtr->format;
+ if (incrPtr->offsets[i] == -2) {
+ numItems = 0;
+ ((char *) buffer)[0] = 0;
+ } else {
+ TkSelInProgress ip;
+ ip.selPtr = selPtr;
+ ip.nextPtr = pendingPtr;
+ pendingPtr = &ip;
+ numItems = (*selPtr->proc)(selPtr->clientData,
+ incrPtr->offsets[i], (char *) buffer,
+ TK_SEL_BYTES_AT_ONCE);
+ pendingPtr = ip.nextPtr;
+ if (ip.selPtr == NULL) {
+ /*
+ * The selection handler deleted itself.
+ */
+
+ return;
+ }
+ if (numItems > TK_SEL_BYTES_AT_ONCE) {
+ panic("selection handler returned too many bytes");
+ } else {
+ if (numItems < 0) {
+ numItems = 0;
+ }
+ }
+ ((char *) buffer)[numItems] = '\0';
+ }
+ if (numItems < TK_SEL_BYTES_AT_ONCE) {
+ if (numItems <= 0) {
+ incrPtr->offsets[i] = -1;
+ incrPtr->numIncrs--;
+ } else {
+ incrPtr->offsets[i] = -2;
+ }
+ } else {
+ incrPtr->offsets[i] += numItems;
+ }
+ if (formatType == XA_STRING) {
+ propPtr = (char *) buffer;
+ format = 8;
+ } else {
+ propPtr = (char *) SelCvtToX((char *) buffer,
+ formatType, (Tk_Window) incrPtr->winPtr,
+ &numItems);
+ format = 32;
+ }
+ errorHandler = Tk_CreateErrorHandler(
+ eventPtr->xproperty.display, -1, -1, -1,
+ (int (*)()) NULL, (ClientData) NULL);
+ XChangeProperty(eventPtr->xproperty.display,
+ eventPtr->xproperty.window,
+ eventPtr->xproperty.atom, formatType,
+ format, PropModeReplace,
+ (unsigned char *) propPtr, numItems);
+ Tk_DeleteErrorHandler(errorHandler);
+ if (propPtr != (char *) buffer) {
+ ckfree(propPtr);
+ }
+ return;
+ }
+ }
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkSelEventProc --
+ *
+ * This procedure is invoked whenever a selection-related
+ * event occurs. It does the lion's share of the work
+ * in implementing the selection protocol.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Lots: depends on the type of event.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkSelEventProc(tkwin, eventPtr)
+ Tk_Window tkwin; /* Window for which event was
+ * targeted. */
+ register XEvent *eventPtr; /* X event: either SelectionClear,
+ * SelectionRequest, or
+ * SelectionNotify. */
+{
+ register TkWindow *winPtr = (TkWindow *) tkwin;
+ TkDisplay *dispPtr = winPtr->dispPtr;
+ Tcl_Interp *interp;
+
+ /*
+ * Case #1: SelectionClear events.
+ */
+
+ if (eventPtr->type == SelectionClear) {
+ TkSelClearSelection(tkwin, eventPtr);
+ }
+
+ /*
+ * Case #2: SelectionNotify events. Call the relevant procedure
+ * to handle the incoming selection.
+ */
+
+ if (eventPtr->type == SelectionNotify) {
+ register TkSelRetrievalInfo *retrPtr;
+ char *propInfo;
+ Atom type;
+ int format, result;
+ unsigned long numItems, bytesAfter;
+
+ for (retrPtr = pendingRetrievals; ; retrPtr = retrPtr->nextPtr) {
+ if (retrPtr == NULL) {
+ return;
+ }
+ if ((retrPtr->winPtr == winPtr)
+ && (retrPtr->selection == eventPtr->xselection.selection)
+ && (retrPtr->target == eventPtr->xselection.target)
+ && (retrPtr->result == -1)) {
+ if (retrPtr->property == eventPtr->xselection.property) {
+ break;
+ }
+ if (eventPtr->xselection.property == None) {
+ Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
+ Tcl_AppendResult(retrPtr->interp,
+ Tk_GetAtomName(tkwin, retrPtr->selection),
+ " selection doesn't exist or form \"",
+ Tk_GetAtomName(tkwin, retrPtr->target),
+ "\" not defined", (char *) NULL);
+ retrPtr->result = TCL_ERROR;
+ return;
+ }
+ }
+ }
+
+ propInfo = NULL;
+ result = XGetWindowProperty(eventPtr->xselection.display,
+ eventPtr->xselection.requestor, retrPtr->property,
+ 0, MAX_PROP_WORDS, False, (Atom) AnyPropertyType,
+ &type, &format, &numItems, &bytesAfter,
+ (unsigned char **) &propInfo);
+ if ((result != Success) || (type == None)) {
+ return;
+ }
+ if (bytesAfter != 0) {
+ Tcl_SetResult(retrPtr->interp, "selection property too large",
+ TCL_STATIC);
+ retrPtr->result = TCL_ERROR;
+ XFree(propInfo);
+ return;
+ }
+ if ((type == XA_STRING) || (type == dispPtr->textAtom)
+ || (type == dispPtr->compoundTextAtom)) {
+ if (format != 8) {
+ sprintf(retrPtr->interp->result,
+ "bad format for string selection: wanted \"8\", got \"%d\"",
+ format);
+ retrPtr->result = TCL_ERROR;
+ return;
+ }
+ interp = retrPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
+ interp, propInfo);
+ Tcl_Release((ClientData) interp);
+ } else if (type == dispPtr->incrAtom) {
+
+ /*
+ * It's a !?#@!?!! INCR-style reception. Arrange to receive
+ * the selection in pieces, using the ICCCM protocol, then
+ * hang around until either the selection is all here or a
+ * timeout occurs.
+ */
+
+ retrPtr->idleTime = 0;
+ Tk_CreateEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
+ (ClientData) retrPtr);
+ XDeleteProperty(Tk_Display(tkwin), Tk_WindowId(tkwin),
+ retrPtr->property);
+ while (retrPtr->result == -1) {
+ Tcl_DoOneEvent(0);
+ }
+ Tk_DeleteEventHandler(tkwin, PropertyChangeMask, SelRcvIncrProc,
+ (ClientData) retrPtr);
+ } else {
+ char *string;
+
+ if (format != 32) {
+ sprintf(retrPtr->interp->result,
+ "bad format for selection: wanted \"32\", got \"%d\"",
+ format);
+ retrPtr->result = TCL_ERROR;
+ return;
+ }
+ string = SelCvtFromX((long *) propInfo, (int) numItems, type,
+ (Tk_Window) winPtr);
+ interp = retrPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ retrPtr->result = (*retrPtr->proc)(retrPtr->clientData,
+ interp, string);
+ Tcl_Release((ClientData) interp);
+ ckfree(string);
+ }
+ XFree(propInfo);
+ return;
+ }
+
+ /*
+ * Case #3: SelectionRequest events. Call ConvertSelection to
+ * do the dirty work.
+ */
+
+ if (eventPtr->type == SelectionRequest) {
+ ConvertSelection(winPtr, &eventPtr->xselectionrequest);
+ return;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelTimeoutProc --
+ *
+ * This procedure is invoked once every second while waiting for
+ * the selection to be returned. After a while it gives up and
+ * aborts the selection retrieval.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new timer callback is created to call us again in another
+ * second, unless time has expired, in which case an error is
+ * recorded for the retrieval.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SelTimeoutProc(clientData)
+ ClientData clientData; /* Information about retrieval
+ * in progress. */
+{
+ register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
+
+ /*
+ * Make sure that the retrieval is still in progress. Then
+ * see how long it's been since any sort of response was received
+ * from the other side.
+ */
+
+ if (retrPtr->result != -1) {
+ return;
+ }
+ retrPtr->idleTime++;
+ if (retrPtr->idleTime >= 5) {
+
+ /*
+ * Use a careful procedure to store the error message, because
+ * the result could already be partially filled in with a partial
+ * selection return.
+ */
+
+ Tcl_SetResult(retrPtr->interp, "selection owner didn't respond",
+ TCL_STATIC);
+ retrPtr->result = TCL_ERROR;
+ } else {
+ retrPtr->timeout = Tcl_CreateTimerHandler(1000, SelTimeoutProc,
+ (ClientData) retrPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConvertSelection --
+ *
+ * This procedure is invoked to handle SelectionRequest events.
+ * It responds to the requests, obeying the ICCCM protocols.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Properties are created for the selection requestor, and a
+ * SelectionNotify event is generated for the selection
+ * requestor. In the event of long selections, this procedure
+ * implements INCR-mode transfers, using the ICCCM protocol.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConvertSelection(winPtr, eventPtr)
+ TkWindow *winPtr; /* Window that received the
+ * conversion request; may not be
+ * selection's current owner, be we
+ * set it to the current owner. */
+ register XSelectionRequestEvent *eventPtr;
+ /* Event describing request. */
+{
+ XSelectionEvent reply; /* Used to notify requestor that
+ * selection info is ready. */
+ int multiple; /* Non-zero means a MULTIPLE request
+ * is being handled. */
+ IncrInfo incr; /* State of selection conversion. */
+ Atom singleInfo[2]; /* incr.multAtoms points here except
+ * for multiple conversions. */
+ int i;
+ Tk_ErrorHandler errorHandler;
+ TkSelectionInfo *infoPtr;
+ TkSelInProgress ip;
+
+ errorHandler = Tk_CreateErrorHandler(eventPtr->display, -1, -1,-1,
+ (int (*)()) NULL, (ClientData) NULL);
+
+ /*
+ * Initialize the reply event.
+ */
+
+ reply.type = SelectionNotify;
+ reply.serial = 0;
+ reply.send_event = True;
+ reply.display = eventPtr->display;
+ reply.requestor = eventPtr->requestor;
+ reply.selection = eventPtr->selection;
+ reply.target = eventPtr->target;
+ reply.property = eventPtr->property;
+ if (reply.property == None) {
+ reply.property = reply.target;
+ }
+ reply.time = eventPtr->time;
+
+ for (infoPtr = winPtr->dispPtr->selectionInfoPtr; infoPtr != NULL;
+ infoPtr = infoPtr->nextPtr) {
+ if (infoPtr->selection == eventPtr->selection)
+ break;
+ }
+ if (infoPtr == NULL) {
+ goto refuse;
+ }
+ winPtr = (TkWindow *) infoPtr->owner;
+
+ /*
+ * Figure out which kind(s) of conversion to perform. If handling
+ * a MULTIPLE conversion, then read the property describing which
+ * conversions to perform.
+ */
+
+ incr.winPtr = winPtr;
+ incr.selection = eventPtr->selection;
+ if (eventPtr->target != winPtr->dispPtr->multipleAtom) {
+ multiple = 0;
+ singleInfo[0] = reply.target;
+ singleInfo[1] = reply.property;
+ incr.multAtoms = singleInfo;
+ incr.numConversions = 1;
+ } else {
+ Atom type;
+ int format, result;
+ unsigned long bytesAfter;
+
+ multiple = 1;
+ incr.multAtoms = NULL;
+ if (eventPtr->property == None) {
+ goto refuse;
+ }
+ result = XGetWindowProperty(eventPtr->display,
+ eventPtr->requestor, eventPtr->property,
+ 0, MAX_PROP_WORDS, False, XA_ATOM,
+ &type, &format, &incr.numConversions, &bytesAfter,
+ (unsigned char **) &incr.multAtoms);
+ if ((result != Success) || (bytesAfter != 0) || (format != 32)
+ || (type == None)) {
+ if (incr.multAtoms != NULL) {
+ XFree((char *) incr.multAtoms);
+ }
+ goto refuse;
+ }
+ incr.numConversions /= 2; /* Two atoms per conversion. */
+ }
+
+ /*
+ * Loop through all of the requested conversions, and either return
+ * the entire converted selection, if it can be returned in a single
+ * bunch, or return INCR information only (the actual selection will
+ * be returned below).
+ */
+
+ incr.offsets = (int *) ckalloc((unsigned)
+ (incr.numConversions*sizeof(int)));
+ incr.numIncrs = 0;
+ for (i = 0; i < incr.numConversions; i++) {
+ Atom target, property, type;
+ long buffer[TK_SEL_WORDS_AT_ONCE];
+ register TkSelHandler *selPtr;
+ int numItems, format;
+ char *propPtr;
+
+ target = incr.multAtoms[2*i];
+ property = incr.multAtoms[2*i + 1];
+ incr.offsets[i] = -1;
+
+ for (selPtr = winPtr->selHandlerList; selPtr != NULL;
+ selPtr = selPtr->nextPtr) {
+ if ((selPtr->target == target)
+ && (selPtr->selection == eventPtr->selection)) {
+ break;
+ }
+ }
+
+ if (selPtr == NULL) {
+ /*
+ * Nobody seems to know about this kind of request. If
+ * it's of a sort that we can handle without any help, do
+ * it. Otherwise mark the request as an errror.
+ */
+
+ numItems = TkSelDefaultSelection(infoPtr, target, (char *) buffer,
+ TK_SEL_BYTES_AT_ONCE, &type);
+ if (numItems < 0) {
+ incr.multAtoms[2*i + 1] = None;
+ continue;
+ }
+ } else {
+ ip.selPtr = selPtr;
+ ip.nextPtr = pendingPtr;
+ pendingPtr = &ip;
+ type = selPtr->format;
+ numItems = (*selPtr->proc)(selPtr->clientData, 0,
+ (char *) buffer, TK_SEL_BYTES_AT_ONCE);
+ pendingPtr = ip.nextPtr;
+ if ((ip.selPtr == NULL) || (numItems < 0)) {
+ incr.multAtoms[2*i + 1] = None;
+ continue;
+ }
+ if (numItems > TK_SEL_BYTES_AT_ONCE) {
+ panic("selection handler returned too many bytes");
+ }
+ ((char *) buffer)[numItems] = '\0';
+ }
+
+ /*
+ * Got the selection; store it back on the requestor's property.
+ */
+
+ if (numItems == TK_SEL_BYTES_AT_ONCE) {
+ /*
+ * Selection is too big to send at once; start an
+ * INCR-mode transfer.
+ */
+
+ incr.numIncrs++;
+ type = winPtr->dispPtr->incrAtom;
+ buffer[0] = SelectionSize(selPtr);
+ if (buffer[0] == 0) {
+ incr.multAtoms[2*i + 1] = None;
+ continue;
+ }
+ numItems = 1;
+ propPtr = (char *) buffer;
+ format = 32;
+ incr.offsets[i] = 0;
+ } else if (type == XA_STRING) {
+ propPtr = (char *) buffer;
+ format = 8;
+ } else {
+ propPtr = (char *) SelCvtToX((char *) buffer,
+ type, (Tk_Window) winPtr, &numItems);
+ format = 32;
+ }
+ XChangeProperty(reply.display, reply.requestor,
+ property, type, format, PropModeReplace,
+ (unsigned char *) propPtr, numItems);
+ if (propPtr != (char *) buffer) {
+ ckfree(propPtr);
+ }
+ }
+
+ /*
+ * Send an event back to the requestor to indicate that the
+ * first stage of conversion is complete (everything is done
+ * except for long conversions that have to be done in INCR
+ * mode).
+ */
+
+ if (incr.numIncrs > 0) {
+ XSelectInput(reply.display, reply.requestor, PropertyChangeMask);
+ incr.timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
+ (ClientData) &incr);
+ incr.idleTime = 0;
+ incr.reqWindow = reply.requestor;
+ incr.time = infoPtr->time;
+ incr.nextPtr = pendingIncrs;
+ pendingIncrs = &incr;
+ }
+ if (multiple) {
+ XChangeProperty(reply.display, reply.requestor, reply.property,
+ XA_ATOM, 32, PropModeReplace,
+ (unsigned char *) incr.multAtoms,
+ (int) incr.numConversions*2);
+ } else {
+
+ /*
+ * Not a MULTIPLE request. The first property in "multAtoms"
+ * got set to None if there was an error in conversion.
+ */
+
+ reply.property = incr.multAtoms[1];
+ }
+ XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
+ Tk_DeleteErrorHandler(errorHandler);
+
+ /*
+ * Handle any remaining INCR-mode transfers. This all happens
+ * in callbacks to TkSelPropProc, so just wait until the number
+ * of uncompleted INCR transfers drops to zero.
+ */
+
+ if (incr.numIncrs > 0) {
+ IncrInfo *incrPtr2;
+
+ while (incr.numIncrs > 0) {
+ Tcl_DoOneEvent(0);
+ }
+ Tcl_DeleteTimerHandler(incr.timeout);
+ errorHandler = Tk_CreateErrorHandler(winPtr->display,
+ -1, -1,-1, (int (*)()) NULL, (ClientData) NULL);
+ XSelectInput(reply.display, reply.requestor, 0L);
+ Tk_DeleteErrorHandler(errorHandler);
+ if (pendingIncrs == &incr) {
+ pendingIncrs = incr.nextPtr;
+ } else {
+ for (incrPtr2 = pendingIncrs; incrPtr2 != NULL;
+ incrPtr2 = incrPtr2->nextPtr) {
+ if (incrPtr2->nextPtr == &incr) {
+ incrPtr2->nextPtr = incr.nextPtr;
+ break;
+ }
+ }
+ }
+ }
+
+ /*
+ * All done. Cleanup and return.
+ */
+
+ ckfree((char *) incr.offsets);
+ if (multiple) {
+ XFree((char *) incr.multAtoms);
+ }
+ return;
+
+ /*
+ * An error occurred. Send back a refusal message.
+ */
+
+ refuse:
+ reply.property = None;
+ XSendEvent(reply.display, reply.requestor, False, 0, (XEvent *) &reply);
+ Tk_DeleteErrorHandler(errorHandler);
+ return;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelRcvIncrProc --
+ *
+ * This procedure handles the INCR protocol on the receiving
+ * side. It is invoked in response to property changes on
+ * the requestor's window (which hopefully are because a new
+ * chunk of the selection arrived).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a new piece of selection has arrived, a procedure is
+ * invoked to deal with that piece. When the whole selection
+ * is here, a flag is left for the higher-level procedure that
+ * initiated the selection retrieval.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SelRcvIncrProc(clientData, eventPtr)
+ ClientData clientData; /* Information about retrieval. */
+ register XEvent *eventPtr; /* X PropertyChange event. */
+{
+ register TkSelRetrievalInfo *retrPtr = (TkSelRetrievalInfo *) clientData;
+ char *propInfo;
+ Atom type;
+ int format, result;
+ unsigned long numItems, bytesAfter;
+ Tcl_Interp *interp;
+
+ if ((eventPtr->xproperty.atom != retrPtr->property)
+ || (eventPtr->xproperty.state != PropertyNewValue)
+ || (retrPtr->result != -1)) {
+ return;
+ }
+ propInfo = NULL;
+ result = XGetWindowProperty(eventPtr->xproperty.display,
+ eventPtr->xproperty.window, retrPtr->property, 0, MAX_PROP_WORDS,
+ True, (Atom) AnyPropertyType, &type, &format, &numItems,
+ &bytesAfter, (unsigned char **) &propInfo);
+ if ((result != Success) || (type == None)) {
+ return;
+ }
+ if (bytesAfter != 0) {
+ Tcl_SetResult(retrPtr->interp, "selection property too large",
+ TCL_STATIC);
+ retrPtr->result = TCL_ERROR;
+ goto done;
+ }
+ if (numItems == 0) {
+ retrPtr->result = TCL_OK;
+ } else if ((type == XA_STRING)
+ || (type == retrPtr->winPtr->dispPtr->textAtom)
+ || (type == retrPtr->winPtr->dispPtr->compoundTextAtom)) {
+ if (format != 8) {
+ Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
+ sprintf(retrPtr->interp->result,
+ "bad format for string selection: wanted \"8\", got \"%d\"",
+ format);
+ retrPtr->result = TCL_ERROR;
+ goto done;
+ }
+ interp = retrPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ result = (*retrPtr->proc)(retrPtr->clientData, interp, propInfo);
+ Tcl_Release((ClientData) interp);
+ if (result != TCL_OK) {
+ retrPtr->result = result;
+ }
+ } else {
+ char *string;
+
+ if (format != 32) {
+ Tcl_SetResult(retrPtr->interp, (char *) NULL, TCL_STATIC);
+ sprintf(retrPtr->interp->result,
+ "bad format for selection: wanted \"32\", got \"%d\"",
+ format);
+ retrPtr->result = TCL_ERROR;
+ goto done;
+ }
+ string = SelCvtFromX((long *) propInfo, (int) numItems, type,
+ (Tk_Window) retrPtr->winPtr);
+ interp = retrPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ result = (*retrPtr->proc)(retrPtr->clientData, interp, string);
+ Tcl_Release((ClientData) interp);
+ if (result != TCL_OK) {
+ retrPtr->result = result;
+ }
+ ckfree(string);
+ }
+
+ done:
+ XFree(propInfo);
+ retrPtr->idleTime = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelectionSize --
+ *
+ * This procedure is called when the selection is too large to
+ * send in a single buffer; it computes the total length of
+ * the selection in bytes.
+ *
+ * Results:
+ * The return value is the number of bytes in the selection
+ * given by selPtr.
+ *
+ * Side effects:
+ * The selection is retrieved from its current owner (this is
+ * the only way to compute its size).
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SelectionSize(selPtr)
+ TkSelHandler *selPtr; /* Information about how to retrieve
+ * the selection whose size is wanted. */
+{
+ char buffer[TK_SEL_BYTES_AT_ONCE+1];
+ int size, chunkSize;
+ TkSelInProgress ip;
+
+ size = TK_SEL_BYTES_AT_ONCE;
+ ip.selPtr = selPtr;
+ ip.nextPtr = pendingPtr;
+ pendingPtr = &ip;
+ do {
+ chunkSize = (*selPtr->proc)(selPtr->clientData, size,
+ (char *) buffer, TK_SEL_BYTES_AT_ONCE);
+ if (ip.selPtr == NULL) {
+ size = 0;
+ break;
+ }
+ size += chunkSize;
+ } while (chunkSize == TK_SEL_BYTES_AT_ONCE);
+ pendingPtr = ip.nextPtr;
+ return size;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * IncrTimeoutProc --
+ *
+ * This procedure is invoked once a second while sending the
+ * selection to a requestor in INCR mode. After a while it
+ * gives up and aborts the selection operation.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * A new timeout gets registered so that this procedure gets
+ * called again in another second, unless too many seconds
+ * have elapsed, in which case incrPtr is marked as "all done".
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+IncrTimeoutProc(clientData)
+ ClientData clientData; /* Information about INCR-mode
+ * selection retrieval for which
+ * we are selection owner. */
+{
+ register IncrInfo *incrPtr = (IncrInfo *) clientData;
+
+ incrPtr->idleTime++;
+ if (incrPtr->idleTime >= 5) {
+ incrPtr->numIncrs = 0;
+ } else {
+ incrPtr->timeout = Tcl_CreateTimerHandler(1000, IncrTimeoutProc,
+ (ClientData) incrPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelCvtToX --
+ *
+ * Given a selection represented as a string (the normal Tcl form),
+ * convert it to the ICCCM-mandated format for X, depending on
+ * the type argument. This procedure and SelCvtFromX are inverses.
+ *
+ * Results:
+ * The return value is a malloc'ed buffer holding a value
+ * equivalent to "string", but formatted as for "type". It is
+ * the caller's responsibility to free the string when done with
+ * it. The word at *numLongsPtr is filled in with the number of
+ * 32-bit words returned in the result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static long *
+SelCvtToX(string, type, tkwin, numLongsPtr)
+ char *string; /* String representation of selection. */
+ Atom type; /* Atom specifying the X format that is
+ * desired for the selection. Should not
+ * be XA_STRING (if so, don't bother calling
+ * this procedure at all). */
+ Tk_Window tkwin; /* Window that governs atom conversion. */
+ int *numLongsPtr; /* Number of 32-bit words contained in the
+ * result. */
+{
+ register char *p;
+ char *field;
+ int numFields;
+ long *propPtr, *longPtr;
+#define MAX_ATOM_NAME_LENGTH 100
+ char atomName[MAX_ATOM_NAME_LENGTH+1];
+
+ /*
+ * The string is assumed to consist of fields separated by spaces.
+ * The property gets generated by converting each field to an
+ * integer number, in one of two ways:
+ * 1. If type is XA_ATOM, convert each field to its corresponding
+ * atom.
+ * 2. If type is anything else, convert each field from an ASCII number
+ * to a 32-bit binary number.
+ */
+
+ numFields = 1;
+ for (p = string; *p != 0; p++) {
+ if (isspace(UCHAR(*p))) {
+ numFields++;
+ }
+ }
+ propPtr = (long *) ckalloc((unsigned) numFields*sizeof(long));
+
+ /*
+ * Convert the fields one-by-one.
+ */
+
+ for (longPtr = propPtr, *numLongsPtr = 0, p = string;
+ ; longPtr++, (*numLongsPtr)++) {
+ while (isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (*p == 0) {
+ break;
+ }
+ field = p;
+ while ((*p != 0) && !isspace(UCHAR(*p))) {
+ p++;
+ }
+ if (type == XA_ATOM) {
+ int length;
+
+ length = p - field;
+ if (length > MAX_ATOM_NAME_LENGTH) {
+ length = MAX_ATOM_NAME_LENGTH;
+ }
+ strncpy(atomName, field, (unsigned) length);
+ atomName[length] = 0;
+ *longPtr = (long) Tk_InternAtom(tkwin, atomName);
+ } else {
+ char *dummy;
+
+ *longPtr = strtol(field, &dummy, 0);
+ }
+ }
+ return propPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SelCvtFromX --
+ *
+ * Given an X property value, formatted as a collection of 32-bit
+ * values according to "type" and the ICCCM conventions, convert
+ * the value to a string suitable for manipulation by Tcl. This
+ * procedure is the inverse of SelCvtToX.
+ *
+ * Results:
+ * The return value is the string equivalent of "property". It is
+ * malloc-ed and should be freed by the caller when no longer
+ * needed.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static char *
+SelCvtFromX(propPtr, numValues, type, tkwin)
+ register long *propPtr; /* Property value from X. */
+ int numValues; /* Number of 32-bit values in property. */
+ Atom type; /* Type of property Should not be
+ * XA_STRING (if so, don't bother calling
+ * this procedure at all). */
+ Tk_Window tkwin; /* Window to use for atom conversion. */
+{
+ char *result;
+ int resultSpace, curSize, fieldSize;
+ char *atomName;
+
+ /*
+ * Convert each long in the property to a string value, which is
+ * either the name of an atom (if type is XA_ATOM) or a hexadecimal
+ * string. Make an initial guess about the size of the result, but
+ * be prepared to enlarge the result if necessary.
+ */
+
+ resultSpace = 12*numValues+1;
+ curSize = 0;
+ atomName = ""; /* Not needed, but eliminates compiler warning. */
+ result = (char *) ckalloc((unsigned) resultSpace);
+ *result = '\0';
+ for ( ; numValues > 0; propPtr++, numValues--) {
+ if (type == XA_ATOM) {
+ atomName = Tk_GetAtomName(tkwin, (Atom) *propPtr);
+ fieldSize = strlen(atomName) + 1;
+ } else {
+ fieldSize = 12;
+ }
+ if (curSize+fieldSize >= resultSpace) {
+ char *newResult;
+
+ resultSpace *= 2;
+ if (curSize+fieldSize >= resultSpace) {
+ resultSpace = curSize + fieldSize + 1;
+ }
+ newResult = (char *) ckalloc((unsigned) resultSpace);
+ strncpy(newResult, result, (unsigned) curSize);
+ ckfree(result);
+ result = newResult;
+ }
+ if (curSize != 0) {
+ result[curSize] = ' ';
+ curSize++;
+ }
+ if (type == XA_ATOM) {
+ strcpy(result+curSize, atomName);
+ } else {
+ sprintf(result+curSize, "0x%x", (unsigned int) *propPtr);
+ }
+ curSize += strlen(result+curSize);
+ }
+ return result;
+}
diff --git a/unix/tkUnixSend.c b/unix/tkUnixSend.c
new file mode 100644
index 0000000..f07c59b
--- /dev/null
+++ b/unix/tkUnixSend.c
@@ -0,0 +1,1851 @@
+/*
+ * tkUnixSend.c --
+ *
+ * This file provides procedures that implement the "send"
+ * command, allowing commands to be passed from interpreter
+ * to interpreter.
+ *
+ * Copyright (c) 1989-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * SCCS: @(#) tkUnixSend.c 1.74 97/11/04 17:12:18
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+#include "tkUnixInt.h"
+
+/*
+ * The following structure is used to keep track of the interpreters
+ * registered by this process.
+ */
+
+typedef struct RegisteredInterp {
+ char *name; /* Interpreter's name (malloc-ed). */
+ Tcl_Interp *interp; /* Interpreter associated with name. NULL
+ * means that the application was unregistered
+ * or deleted while a send was in progress
+ * to it. */
+ TkDisplay *dispPtr; /* Display for the application. Needed
+ * because we may need to unregister the
+ * interpreter after its main window has
+ * been deleted. */
+ struct RegisteredInterp *nextPtr;
+ /* Next in list of names associated
+ * with interps in this process.
+ * NULL means end of list. */
+} RegisteredInterp;
+
+static RegisteredInterp *registry = NULL;
+ /* List of all interpreters
+ * registered by this process. */
+
+/*
+ * A registry of all interpreters for a display is kept in a
+ * property "InterpRegistry" on the root window of the display.
+ * It is organized as a series of zero or more concatenated strings
+ * (in no particular order), each of the form
+ * window space name '\0'
+ * where "window" is the hex id of the comm. window to use to talk
+ * to an interpreter named "name".
+ *
+ * When the registry is being manipulated by an application (e.g. to
+ * add or remove an entry), it is loaded into memory using a structure
+ * of the following type:
+ */
+
+typedef struct NameRegistry {
+ TkDisplay *dispPtr; /* Display from which the registry was
+ * read. */
+ int locked; /* Non-zero means that the display was
+ * locked when the property was read in. */
+ int modified; /* Non-zero means that the property has
+ * been modified, so it needs to be written
+ * out when the NameRegistry is closed. */
+ unsigned long propLength; /* Length of the property, in bytes. */
+ char *property; /* The contents of the property, or NULL
+ * if none. See format description above;
+ * this is *not* terminated by the first
+ * null character. Dynamically allocated. */
+ int allocedByX; /* Non-zero means must free property with
+ * XFree; zero means use ckfree. */
+} NameRegistry;
+
+/*
+ * When a result is being awaited from a sent command, one of
+ * the following structures is present on a list of all outstanding
+ * sent commands. The information in the structure is used to
+ * process the result when it arrives. You're probably wondering
+ * how there could ever be multiple outstanding sent commands.
+ * This could happen if interpreters invoke each other recursively.
+ * It's unlikely, but possible.
+ */
+
+typedef struct PendingCommand {
+ int serial; /* Serial number expected in
+ * result. */
+ TkDisplay *dispPtr; /* Display being used for communication. */
+ char *target; /* Name of interpreter command is
+ * being sent to. */
+ Window commWindow; /* Target's communication window. */
+ Tcl_Interp *interp; /* Interpreter from which the send
+ * was invoked. */
+ int code; /* Tcl return code for command
+ * will be stored here. */
+ char *result; /* String result for command (malloc'ed),
+ * or NULL. */
+ char *errorInfo; /* Information for "errorInfo" variable,
+ * or NULL (malloc'ed). */
+ char *errorCode; /* Information for "errorCode" variable,
+ * or NULL (malloc'ed). */
+ int gotResponse; /* 1 means a response has been received,
+ * 0 means the command is still outstanding. */
+ struct PendingCommand *nextPtr;
+ /* Next in list of all outstanding
+ * commands. NULL means end of
+ * list. */
+} PendingCommand;
+
+static PendingCommand *pendingCommands = NULL;
+ /* List of all commands currently
+ * being waited for. */
+
+/*
+ * The information below is used for communication between processes
+ * during "send" commands. Each process keeps a private window, never
+ * even mapped, with one property, "Comm". When a command is sent to
+ * an interpreter, the command is appended to the comm property of the
+ * communication window associated with the interp's process. Similarly,
+ * when a result is returned from a sent command, it is also appended
+ * to the comm property.
+ *
+ * Each command and each result takes the form of ASCII text. For a
+ * command, the text consists of a zero character followed by several
+ * null-terminated ASCII strings. The first string consists of the
+ * single letter "c". Subsequent strings have the form "option value"
+ * where the following options are supported:
+ *
+ * -r commWindow serial
+ *
+ * This option means that a response should be sent to the window
+ * whose X identifier is "commWindow" (in hex), and the response should
+ * be identified with the serial number given by "serial" (in decimal).
+ * If this option isn't specified then the send is asynchronous and
+ * no response is sent.
+ *
+ * -n name
+ * "Name" gives the name of the application for which the command is
+ * intended. This option must be present.
+ *
+ * -s script
+ *
+ * "Script" is the script to be executed. This option must be present.
+ *
+ * The options may appear in any order. The -n and -s options must be
+ * present, but -r may be omitted for asynchronous RPCs. For compatibility
+ * with future releases that may add new features, there may be additional
+ * options present; as long as they start with a "-" character, they will
+ * be ignored.
+ *
+ * A result also consists of a zero character followed by several null-
+ * terminated ASCII strings. The first string consists of the single
+ * letter "r". Subsequent strings have the form "option value" where
+ * the following options are supported:
+ *
+ * -s serial
+ *
+ * Identifies the command for which this is the result. It is the
+ * same as the "serial" field from the -s option in the command. This
+ * option must be present.
+ *
+ * -c code
+ *
+ * "Code" is the completion code for the script, in decimal. If the
+ * code is omitted it defaults to TCL_OK.
+ *
+ * -r result
+ *
+ * "Result" is the result string for the script, which may be either
+ * a result or an error message. If this field is omitted then it
+ * defaults to an empty string.
+ *
+ * -i errorInfo
+ *
+ * "ErrorInfo" gives a string with which to initialize the errorInfo
+ * variable. This option may be omitted; it is ignored unless the
+ * completion code is TCL_ERROR.
+ *
+ * -e errorCode
+ *
+ * "ErrorCode" gives a string with with to initialize the errorCode
+ * variable. This option may be omitted; it is ignored unless the
+ * completion code is TCL_ERROR.
+ *
+ * Options may appear in any order, and only the -s option must be
+ * present. As with commands, there may be additional options besides
+ * these; unknown options are ignored.
+ */
+
+/*
+ * The following variable is the serial number that was used in the
+ * last "send" command. It is exported only for testing purposes.
+ */
+
+int tkSendSerial = 0;
+
+/*
+ * Maximum size property that can be read at one time by
+ * this module:
+ */
+
+#define MAX_PROP_WORDS 100000
+
+/*
+ * The following variable can be set while debugging to do things like
+ * skip locking the server.
+ */
+
+static int sendDebug = 0;
+
+/*
+ * Forward declarations for procedures defined later in this file:
+ */
+
+static int AppendErrorProc _ANSI_ARGS_((ClientData clientData,
+ XErrorEvent *errorPtr));
+static void AppendPropCarefully _ANSI_ARGS_((Display *display,
+ Window window, Atom property, char *value,
+ int length, PendingCommand *pendingPtr));
+static void DeleteProc _ANSI_ARGS_((ClientData clientData));
+static void RegAddName _ANSI_ARGS_((NameRegistry *regPtr,
+ char *name, Window commWindow));
+static void RegClose _ANSI_ARGS_((NameRegistry *regPtr));
+static void RegDeleteName _ANSI_ARGS_((NameRegistry *regPtr,
+ char *name));
+static Window RegFindName _ANSI_ARGS_((NameRegistry *regPtr,
+ char *name));
+static NameRegistry * RegOpen _ANSI_ARGS_((Tcl_Interp *interp,
+ TkDisplay *dispPtr, int lock));
+static void SendEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int SendInit _ANSI_ARGS_((Tcl_Interp *interp,
+ TkDisplay *dispPtr));
+static Tk_RestrictAction SendRestrictProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int ServerSecure _ANSI_ARGS_((TkDisplay *dispPtr));
+static void UpdateCommWindow _ANSI_ARGS_((TkDisplay *dispPtr));
+static int ValidateName _ANSI_ARGS_((TkDisplay *dispPtr,
+ char *name, Window commWindow, int oldOK));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RegOpen --
+ *
+ * This procedure loads the name registry for a display into
+ * memory so that it can be manipulated.
+ *
+ * Results:
+ * The return value is a pointer to the loaded registry.
+ *
+ * Side effects:
+ * If "lock" is set then the server will be locked. It is the
+ * caller's responsibility to call RegClose when finished with
+ * the registry, so that we can write back the registry if
+ * neeeded, unlock the server if needed, and free memory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static NameRegistry *
+RegOpen(interp, dispPtr, lock)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting
+ * (errors cause a panic so in fact no
+ * error is ever returned, but the interpreter
+ * is needed anyway). */
+ TkDisplay *dispPtr; /* Display whose name registry is to be
+ * opened. */
+ int lock; /* Non-zero means lock the window server
+ * when opening the registry, so no-one
+ * else can use the registry until we
+ * close it. */
+{
+ NameRegistry *regPtr;
+ int result, actualFormat;
+ unsigned long bytesAfter;
+ Atom actualType;
+
+ if (dispPtr->commTkwin == NULL) {
+ SendInit(interp, dispPtr);
+ }
+
+ regPtr = (NameRegistry *) ckalloc(sizeof(NameRegistry));
+ regPtr->dispPtr = dispPtr;
+ regPtr->locked = 0;
+ regPtr->modified = 0;
+ regPtr->allocedByX = 1;
+
+ if (lock && !sendDebug) {
+ XGrabServer(dispPtr->display);
+ regPtr->locked = 1;
+ }
+
+ /*
+ * Read the registry property.
+ */
+
+ result = XGetWindowProperty(dispPtr->display,
+ RootWindow(dispPtr->display, 0),
+ dispPtr->registryProperty, 0, MAX_PROP_WORDS,
+ False, XA_STRING, &actualType, &actualFormat,
+ &regPtr->propLength, &bytesAfter,
+ (unsigned char **) &regPtr->property);
+
+ if (actualType == None) {
+ regPtr->propLength = 0;
+ regPtr->property = NULL;
+ } else if ((result != Success) || (actualFormat != 8)
+ || (actualType != XA_STRING)) {
+ /*
+ * The property is improperly formed; delete it.
+ */
+
+ if (regPtr->property != NULL) {
+ XFree(regPtr->property);
+ regPtr->propLength = 0;
+ regPtr->property = NULL;
+ }
+ XDeleteProperty(dispPtr->display,
+ RootWindow(dispPtr->display, 0),
+ dispPtr->registryProperty);
+ }
+
+ /*
+ * Xlib placed an extra null byte after the end of the property, just
+ * to make sure that it is always NULL-terminated. Be sure to include
+ * this byte in our count if it's needed to ensure null termination
+ * (note: as of 8/95 I'm no longer sure why this code is needed; seems
+ * like it shouldn't be).
+ */
+
+ if ((regPtr->propLength > 0)
+ && (regPtr->property[regPtr->propLength-1] != 0)) {
+ regPtr->propLength++;
+ }
+ return regPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RegFindName --
+ *
+ * Given an open name registry, this procedure finds an entry
+ * with a given name, if there is one, and returns information
+ * about that entry.
+ *
+ * Results:
+ * The return value is the X identifier for the comm window for
+ * the application named "name", or None if there is no such
+ * entry in the registry.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Window
+RegFindName(regPtr, name)
+ NameRegistry *regPtr; /* Pointer to a registry opened with a
+ * previous call to RegOpen. */
+ char *name; /* Name of an application. */
+{
+ char *p, *entry;
+ unsigned int id;
+
+ for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
+ entry = p;
+ while ((*p != 0) && (!isspace(UCHAR(*p)))) {
+ p++;
+ }
+ if ((*p != 0) && (strcmp(name, p+1) == 0)) {
+ if (sscanf(entry, "%x", &id) == 1) {
+ /*
+ * Must cast from an unsigned int to a Window in case we
+ * are on a 64-bit architecture.
+ */
+
+ return (Window) id;
+ }
+ }
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ }
+ return None;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RegDeleteName --
+ *
+ * This procedure deletes the entry for a given name from
+ * an open registry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there used to be an entry named "name" in the registry,
+ * then it is deleted and the registry is marked as modified
+ * so it will be written back when closed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RegDeleteName(regPtr, name)
+ NameRegistry *regPtr; /* Pointer to a registry opened with a
+ * previous call to RegOpen. */
+ char *name; /* Name of an application. */
+{
+ char *p, *entry, *entryName;
+ int count;
+
+ for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
+ entry = p;
+ while ((*p != 0) && (!isspace(UCHAR(*p)))) {
+ p++;
+ }
+ if (*p != 0) {
+ p++;
+ }
+ entryName = p;
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ if ((strcmp(name, entryName) == 0)) {
+ /*
+ * Found the matching entry. Copy everything after it
+ * down on top of it.
+ */
+
+ count = regPtr->propLength - (p - regPtr->property);
+ if (count > 0) {
+ char *src, *dst;
+
+ for (src = p, dst = entry; count > 0; src++, dst++, count--) {
+ *dst = *src;
+ }
+ }
+ regPtr->propLength -= p - entry;
+ regPtr->modified = 1;
+ return;
+ }
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RegAddName --
+ *
+ * Add a new entry to an open registry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The open registry is expanded; it is marked as modified so that
+ * it will be written back when closed.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RegAddName(regPtr, name, commWindow)
+ NameRegistry *regPtr; /* Pointer to a registry opened with a
+ * previous call to RegOpen. */
+ char *name; /* Name of an application. The caller
+ * must ensure that this name isn't
+ * already registered. */
+ Window commWindow; /* X identifier for comm. window of
+ * application. */
+{
+ char id[30];
+ char *newProp;
+ int idLength, newBytes;
+
+ sprintf(id, "%x ", (unsigned int) commWindow);
+ idLength = strlen(id);
+ newBytes = idLength + strlen(name) + 1;
+ newProp = (char *) ckalloc((unsigned) (regPtr->propLength + newBytes));
+ strcpy(newProp, id);
+ strcpy(newProp+idLength, name);
+ if (regPtr->property != NULL) {
+ memcpy((VOID *) (newProp + newBytes), (VOID *) regPtr->property,
+ regPtr->propLength);
+ if (regPtr->allocedByX) {
+ XFree(regPtr->property);
+ } else {
+ ckfree(regPtr->property);
+ }
+ }
+ regPtr->modified = 1;
+ regPtr->propLength += newBytes;
+ regPtr->property = newProp;
+ regPtr->allocedByX = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * RegClose --
+ *
+ * This procedure is called to end a series of operations on
+ * a name registry.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The registry is written back if it has been modified, and the
+ * X server is unlocked if it was locked. Memory for the
+ * registry is freed, so the caller should never use regPtr
+ * again.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+RegClose(regPtr)
+ NameRegistry *regPtr; /* Pointer to a registry opened with a
+ * previous call to RegOpen. */
+{
+ if (regPtr->modified) {
+ if (!regPtr->locked && !sendDebug) {
+ panic("The name registry was modified without being locked!");
+ }
+ XChangeProperty(regPtr->dispPtr->display,
+ RootWindow(regPtr->dispPtr->display, 0),
+ regPtr->dispPtr->registryProperty, XA_STRING, 8,
+ PropModeReplace, (unsigned char *) regPtr->property,
+ (int) regPtr->propLength);
+ }
+
+ if (regPtr->locked) {
+ XUngrabServer(regPtr->dispPtr->display);
+ }
+
+ /*
+ * After ungrabbing the server, it's important to flush the output
+ * immediately so that the server sees the ungrab command. Otherwise
+ * we might do something else that needs to communicate with the
+ * server (such as invoking a subprocess that needs to do I/O to
+ * the screen); if the ungrab command is still sitting in our
+ * output buffer, we could deadlock.
+ */
+
+ XFlush(regPtr->dispPtr->display);
+
+ if (regPtr->property != NULL) {
+ if (regPtr->allocedByX) {
+ XFree(regPtr->property);
+ } else {
+ ckfree(regPtr->property);
+ }
+ }
+ ckfree((char *) regPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ValidateName --
+ *
+ * This procedure checks to see if an entry in the registry
+ * is still valid.
+ *
+ * Results:
+ * The return value is 1 if the given commWindow exists and its
+ * name is "name". Otherwise 0 is returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ValidateName(dispPtr, name, commWindow, oldOK)
+ TkDisplay *dispPtr; /* Display for which to perform the
+ * validation. */
+ char *name; /* The name of an application. */
+ Window commWindow; /* X identifier for the application's
+ * comm. window. */
+ int oldOK; /* Non-zero means that we should consider
+ * an application to be valid even if it
+ * looks like an old-style (pre-4.0) one;
+ * 0 means consider these invalid. */
+{
+ int result, actualFormat, argc, i;
+ unsigned long length, bytesAfter;
+ Atom actualType;
+ char *property;
+ Tk_ErrorHandler handler;
+ char **argv;
+
+ property = NULL;
+
+ /*
+ * Ignore X errors when reading the property (e.g., the window
+ * might not exist). If an error occurs, result will be some
+ * value other than Success.
+ */
+
+ handler = Tk_CreateErrorHandler(dispPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ result = XGetWindowProperty(dispPtr->display, commWindow,
+ dispPtr->appNameProperty, 0, MAX_PROP_WORDS,
+ False, XA_STRING, &actualType, &actualFormat,
+ &length, &bytesAfter, (unsigned char **) &property);
+
+ if ((result == Success) && (actualType == None)) {
+ XWindowAttributes atts;
+
+ /*
+ * The comm. window exists but the property we're looking for
+ * doesn't exist. This probably means that the application
+ * comes from an older version of Tk (< 4.0) that didn't set the
+ * property; if this is the case, then assume for compatibility's
+ * sake that everything's OK. However, it's also possible that
+ * some random application has re-used the window id for something
+ * totally unrelated. Check a few characteristics of the window,
+ * such as its dimensions and mapped state, to be sure that it
+ * still "smells" like a commWindow.
+ */
+
+ if (!oldOK
+ || !XGetWindowAttributes(dispPtr->display, commWindow, &atts)
+ || (atts.width != 1) || (atts.height != 1)
+ || (atts.map_state != IsUnmapped)) {
+ result = 0;
+ } else {
+ result = 1;
+ }
+ } else if ((result == Success) && (actualFormat == 8)
+ && (actualType == XA_STRING)) {
+ result = 0;
+ if (Tcl_SplitList((Tcl_Interp *) NULL, property, &argc, &argv)
+ == TCL_OK) {
+ for (i = 0; i < argc; i++) {
+ if (strcmp(argv[i], name) == 0) {
+ result = 1;
+ break;
+ }
+ }
+ ckfree((char *) argv);
+ }
+ } else {
+ result = 0;
+ }
+ Tk_DeleteErrorHandler(handler);
+ if (property != NULL) {
+ XFree(property);
+ }
+ return result;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ServerSecure --
+ *
+ * Check whether a server is secure enough for us to trust
+ * Tcl scripts arriving via that server.
+ *
+ * Results:
+ * The return value is 1 if the server is secure, which means
+ * that host-style authentication is turned on but there are
+ * no hosts in the enabled list. This means that some other
+ * form of authorization (presumably more secure, such as xauth)
+ * is in use.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ServerSecure(dispPtr)
+ TkDisplay *dispPtr; /* Display to check. */
+{
+#ifdef TK_NO_SECURITY
+ return 1;
+#else
+ XHostAddress *addrPtr;
+ int numHosts, secure;
+ Bool enabled;
+
+ secure = 0;
+ addrPtr = XListHosts(dispPtr->display, &numHosts, &enabled);
+ if (enabled && (numHosts == 0)) {
+ secure = 1;
+ }
+ if (addrPtr != NULL) {
+ XFree((char *) addrPtr);
+ }
+ return secure;
+#endif /* TK_NO_SECURITY */
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_SetAppName --
+ *
+ * This procedure 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.
+ *
+ *--------------------------------------------------------------
+ */
+
+char *
+Tk_SetAppName(tkwin, name)
+ Tk_Window tkwin; /* Token for any window in the application
+ * to be named: it is just used to identify
+ * the application and the display. */
+ char *name; /* The name that will be used to
+ * refer to the interpreter in later
+ * "send" commands. Must be globally
+ * unique. */
+{
+ RegisteredInterp *riPtr, *riPtr2;
+ Window w;
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ TkDisplay *dispPtr;
+ NameRegistry *regPtr;
+ Tcl_Interp *interp;
+ char *actualName;
+ Tcl_DString dString;
+ int offset, i;
+
+#ifdef __WIN32__
+ return name;
+#endif /* __WIN32__ */
+
+ dispPtr = winPtr->dispPtr;
+ interp = winPtr->mainPtr->interp;
+ if (dispPtr->commTkwin == NULL) {
+ SendInit(interp, winPtr->dispPtr);
+ }
+
+ /*
+ * See if the application is already registered; if so, remove its
+ * current name from the registry.
+ */
+
+ regPtr = RegOpen(interp, winPtr->dispPtr, 1);
+ for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
+ if (riPtr == NULL) {
+
+ /*
+ * This interpreter isn't currently registered; create
+ * the data structure that will be used to register it locally,
+ * plus add the "send" command to the interpreter.
+ */
+
+ riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
+ riPtr->interp = interp;
+ riPtr->dispPtr = winPtr->dispPtr;
+ riPtr->nextPtr = registry;
+ registry = riPtr;
+ Tcl_CreateCommand(interp, "send", Tk_SendCmd, (ClientData) riPtr,
+ DeleteProc);
+ if (Tcl_IsSafe(interp)) {
+ Tcl_HideCommand(interp, "send", "send");
+ }
+ break;
+ }
+ if (riPtr->interp == interp) {
+ /*
+ * The interpreter is currently registered; remove it from
+ * the name registry.
+ */
+
+ RegDeleteName(regPtr, riPtr->name);
+ ckfree(riPtr->name);
+ break;
+ }
+ }
+
+ /*
+ * Pick a name to use for the application. Use "name" if it's not
+ * already in use. Otherwise add a suffix such as " #2", trying
+ * larger and larger numbers until we eventually find one that is
+ * unique.
+ */
+
+ actualName = name;
+ offset = 0; /* Needed only to avoid "used before
+ * set" compiler warnings. */
+ for (i = 1; ; 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+10);
+ actualName = Tcl_DStringValue(&dString);
+ }
+ sprintf(actualName + offset, "%d", i);
+ }
+ w = RegFindName(regPtr, actualName);
+ if (w == None) {
+ break;
+ }
+
+ /*
+ * The name appears to be in use already, but double-check to
+ * be sure (perhaps the application died without removing its
+ * name from the registry?).
+ */
+
+ if (w == Tk_WindowId(dispPtr->commTkwin)) {
+ for (riPtr2 = registry; riPtr2 != NULL; riPtr2 = riPtr2->nextPtr) {
+ if ((riPtr2->interp != interp) &&
+ (strcmp(riPtr2->name, actualName) == 0)) {
+ goto nextSuffix;
+ }
+ }
+ RegDeleteName(regPtr, actualName);
+ break;
+ } else if (!ValidateName(winPtr->dispPtr, actualName, w, 1)) {
+ RegDeleteName(regPtr, actualName);
+ break;
+ }
+ nextSuffix:
+ continue;
+ }
+
+ /*
+ * We've now got a name to use. Store it in the name registry and
+ * in the local entry for this application, plus put it in a property
+ * on the commWindow.
+ */
+
+ RegAddName(regPtr, actualName, Tk_WindowId(dispPtr->commTkwin));
+ RegClose(regPtr);
+ riPtr->name = (char *) ckalloc((unsigned) (strlen(actualName) + 1));
+ strcpy(riPtr->name, actualName);
+ if (actualName != name) {
+ Tcl_DStringFree(&dString);
+ }
+ UpdateCommWindow(dispPtr);
+
+ return riPtr->name;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * Tk_SendCmd --
+ *
+ * This procedure 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_SendCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Information about sender (only
+ * dispPtr field is used). */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ TkWindow *winPtr;
+ Window commWindow;
+ PendingCommand pending;
+ register RegisteredInterp *riPtr;
+ char *destName, buffer[30];
+ int result, c, async, i, firstArg;
+ size_t length;
+ Tk_RestrictProc *prevRestrictProc;
+ ClientData prevArg;
+ TkDisplay *dispPtr;
+ Tcl_Time timeout;
+ NameRegistry *regPtr;
+ Tcl_DString request;
+ Tcl_Interp *localInterp; /* Used when the interpreter to
+ * send the command to is within
+ * the same process. */
+
+ /*
+ * Process options, if any.
+ */
+
+ async = 0;
+ winPtr = (TkWindow *) Tk_MainWindow(interp);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ for (i = 1; i < (argc-1); ) {
+ if (argv[i][0] != '-') {
+ break;
+ }
+ c = argv[i][1];
+ length = strlen(argv[i]);
+ if ((c == 'a') && (strncmp(argv[i], "-async", length) == 0)) {
+ async = 1;
+ i++;
+ } else if ((c == 'd') && (strncmp(argv[i], "-displayof",
+ length) == 0)) {
+ winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[i+1],
+ (Tk_Window) winPtr);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ i += 2;
+ } else if (strcmp(argv[i], "--") == 0) {
+ i++;
+ break;
+ } else {
+ Tcl_AppendResult(interp, "bad option \"", argv[i],
+ "\": must be -async, -displayof, or --", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ if (argc < (i+2)) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
+ " ?options? interpName arg ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ destName = argv[i];
+ firstArg = i+1;
+
+ dispPtr = winPtr->dispPtr;
+ if (dispPtr->commTkwin == NULL) {
+ SendInit(interp, winPtr->dispPtr);
+ }
+
+ /*
+ * See if the target interpreter is local. If so, execute
+ * the command directly without going through the X server.
+ * The only tricky thing is passing the result from the target
+ * interpreter to the invoking interpreter. Watch out: they
+ * could be the same!
+ */
+
+ for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
+ if ((riPtr->dispPtr != dispPtr)
+ || (strcmp(riPtr->name, destName) != 0)) {
+ continue;
+ }
+ Tcl_Preserve((ClientData) riPtr);
+ localInterp = riPtr->interp;
+ Tcl_Preserve((ClientData) localInterp);
+ if (firstArg == (argc-1)) {
+ result = Tcl_GlobalEval(localInterp, argv[firstArg]);
+ } else {
+ Tcl_DStringInit(&request);
+ Tcl_DStringAppend(&request, argv[firstArg], -1);
+ for (i = firstArg+1; i < argc; i++) {
+ Tcl_DStringAppend(&request, " ", 1);
+ Tcl_DStringAppend(&request, argv[i], -1);
+ }
+ result = Tcl_GlobalEval(localInterp, Tcl_DStringValue(&request));
+ Tcl_DStringFree(&request);
+ }
+ if (interp != localInterp) {
+ if (result == TCL_ERROR) {
+
+ /*
+ * An error occurred, so transfer error information from the
+ * destination interpreter back to our interpreter. Must clear
+ * interp's result before calling Tcl_AddErrorInfo, since
+ * Tcl_AddErrorInfo will store the interp's result in errorInfo
+ * before appending riPtr's $errorInfo; we've already got
+ * everything we need in riPtr's $errorInfo.
+ */
+
+ Tcl_ResetResult(interp);
+ Tcl_AddErrorInfo(interp, Tcl_GetVar2(localInterp,
+ "errorInfo", (char *) NULL, TCL_GLOBAL_ONLY));
+ Tcl_SetVar2(interp, "errorCode", (char *) NULL,
+ Tcl_GetVar2(localInterp, "errorCode", (char *) NULL,
+ TCL_GLOBAL_ONLY), TCL_GLOBAL_ONLY);
+ }
+ if (localInterp->freeProc != TCL_STATIC) {
+ interp->result = localInterp->result;
+ interp->freeProc = localInterp->freeProc;
+ localInterp->freeProc = TCL_STATIC;
+ } else {
+ Tcl_SetResult(interp, localInterp->result, TCL_VOLATILE);
+ }
+ Tcl_ResetResult(localInterp);
+ }
+ Tcl_Release((ClientData) riPtr);
+ Tcl_Release((ClientData) localInterp);
+ return result;
+ }
+
+ /*
+ * Bind the interpreter name to a communication window.
+ */
+
+ regPtr = RegOpen(interp, winPtr->dispPtr, 0);
+ commWindow = RegFindName(regPtr, destName);
+ RegClose(regPtr);
+ if (commWindow == None) {
+ Tcl_AppendResult(interp, "no application named \"",
+ destName, "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+
+ /*
+ * Send the command to the target interpreter by appending it to the
+ * comm window in the communication window.
+ */
+
+ tkSendSerial++;
+ Tcl_DStringInit(&request);
+ Tcl_DStringAppend(&request, "\0c\0-n ", 6);
+ Tcl_DStringAppend(&request, destName, -1);
+ if (!async) {
+ sprintf(buffer, "%x %d",
+ (unsigned int) Tk_WindowId(dispPtr->commTkwin),
+ tkSendSerial);
+ Tcl_DStringAppend(&request, "\0-r ", 4);
+ Tcl_DStringAppend(&request, buffer, -1);
+ }
+ Tcl_DStringAppend(&request, "\0-s ", 4);
+ Tcl_DStringAppend(&request, argv[firstArg], -1);
+ for (i = firstArg+1; i < argc; i++) {
+ Tcl_DStringAppend(&request, " ", 1);
+ Tcl_DStringAppend(&request, argv[i], -1);
+ }
+ (void) AppendPropCarefully(dispPtr->display, commWindow,
+ dispPtr->commProperty, Tcl_DStringValue(&request),
+ Tcl_DStringLength(&request) + 1,
+ (async) ? (PendingCommand *) NULL : &pending);
+ Tcl_DStringFree(&request);
+ if (async) {
+ /*
+ * This is an asynchronous send: return immediately without
+ * waiting for a response.
+ */
+
+ return TCL_OK;
+ }
+
+ /*
+ * Register the fact that we're waiting for a command to complete
+ * (this is needed by SendEventProc and by AppendErrorProc to pass
+ * back the command's results). Set up a timeout handler so that
+ * we can check during long sends to make sure that the destination
+ * application is still alive.
+ */
+
+ pending.serial = tkSendSerial;
+ pending.dispPtr = dispPtr;
+ pending.target = destName;
+ pending.commWindow = commWindow;
+ pending.interp = interp;
+ pending.result = NULL;
+ pending.errorInfo = NULL;
+ pending.errorCode = NULL;
+ pending.gotResponse = 0;
+ pending.nextPtr = pendingCommands;
+ pendingCommands = &pending;
+
+ /*
+ * Enter a loop processing X events until the result comes
+ * in or the target is declared to be dead. While waiting
+ * for a result, look only at send-related events so that
+ * the send is synchronous with respect to other events in
+ * the application.
+ */
+
+ prevRestrictProc = Tk_RestrictEvents(SendRestrictProc,
+ (ClientData) NULL, &prevArg);
+ TclpGetTime(&timeout);
+ timeout.sec += 2;
+ while (!pending.gotResponse) {
+ if (!TkUnixDoOneXEvent(&timeout)) {
+ /*
+ * An unusually long amount of time has elapsed during the
+ * processing of a sent command. Check to make sure that the
+ * target application still exists. If it does, reset the timeout.
+ */
+
+ if (!ValidateName(pending.dispPtr, pending.target,
+ pending.commWindow, 0)) {
+ char *msg;
+ if (ValidateName(pending.dispPtr, pending.target,
+ pending.commWindow, 1)) {
+ msg = "target application died or uses a Tk version before 4.0";
+ } else {
+ msg = "target application died";
+ }
+ pending.code = TCL_ERROR;
+ pending.result = (char *) ckalloc((unsigned) (strlen(msg) + 1));
+ strcpy(pending.result, msg);
+ pending.gotResponse = 1;
+ } else {
+ TclpGetTime(&timeout);
+ timeout.sec += 2;
+ }
+ }
+ }
+ (void) Tk_RestrictEvents(prevRestrictProc, prevArg, &prevArg);
+
+ /*
+ * Unregister the information about the pending command
+ * and return the result.
+ */
+
+ if (pendingCommands != &pending) {
+ panic("Tk_SendCmd: corrupted send stack");
+ }
+ pendingCommands = pending.nextPtr;
+ if (pending.errorInfo != NULL) {
+ /*
+ * Special trick: must clear the interp's result before calling
+ * Tcl_AddErrorInfo, since Tcl_AddErrorInfo will store the interp's
+ * result in errorInfo before appending pending.errorInfo; we've
+ * already got everything we need in pending.errorInfo.
+ */
+
+ Tcl_ResetResult(interp);
+ Tcl_AddErrorInfo(interp, pending.errorInfo);
+ ckfree(pending.errorInfo);
+ }
+ if (pending.errorCode != NULL) {
+ Tcl_SetVar2(interp, "errorCode", (char *) NULL, pending.errorCode,
+ TCL_GLOBAL_ONLY);
+ ckfree(pending.errorCode);
+ }
+ Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
+ return pending.code;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetInterpNames --
+ *
+ * This procedure 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(interp, tkwin)
+ Tcl_Interp *interp; /* Interpreter for returning a result. */
+ Tk_Window tkwin; /* Window whose display is to be used
+ * for the lookup. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ char *p, *entry, *entryName;
+ NameRegistry *regPtr;
+ Window commWindow;
+ int count;
+ unsigned int id;
+
+ /*
+ * Read the registry property, then scan through all of its entries.
+ * Validate each entry to be sure that its application still exists.
+ */
+
+ regPtr = RegOpen(interp, winPtr->dispPtr, 1);
+ for (p = regPtr->property; (p-regPtr->property) < (int) regPtr->propLength; ) {
+ entry = p;
+ if (sscanf(p, "%x",(unsigned int *) &id) != 1) {
+ commWindow = None;
+ } else {
+ commWindow = id;
+ }
+ while ((*p != 0) && (!isspace(UCHAR(*p)))) {
+ p++;
+ }
+ if (*p != 0) {
+ p++;
+ }
+ entryName = p;
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ if (ValidateName(winPtr->dispPtr, entryName, commWindow, 1)) {
+ /*
+ * The application still exists; add its name to the result.
+ */
+
+ Tcl_AppendElement(interp, entryName);
+ } else {
+ /*
+ * This name is bogus (perhaps the application died without
+ * cleaning up its entry in the registry?). Delete the name.
+ */
+
+ count = regPtr->propLength - (p - regPtr->property);
+ if (count > 0) {
+ char *src, *dst;
+
+ for (src = p, dst = entry; count > 0; src++, dst++, count--) {
+ *dst = *src;
+ }
+ }
+ regPtr->propLength -= p - entry;
+ regPtr->modified = 1;
+ p = entry;
+ }
+ }
+ RegClose(regPtr);
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SendInit --
+ *
+ * This procedure is called to initialize the
+ * communication channels for sending commands and
+ * receiving results.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Sets up various data structures and windows.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+SendInit(interp, dispPtr)
+ Tcl_Interp *interp; /* Interpreter to use for error reporting
+ * (no errors are ever returned, but the
+ * interpreter is needed anyway). */
+ TkDisplay *dispPtr; /* Display to initialize. */
+{
+ XSetWindowAttributes atts;
+
+ /*
+ * Create the window used for communication, and set up an
+ * event handler for it.
+ */
+
+ dispPtr->commTkwin = Tk_CreateWindow(interp, (Tk_Window) NULL,
+ "_comm", DisplayString(dispPtr->display));
+ if (dispPtr->commTkwin == NULL) {
+ panic("Tk_CreateWindow failed in SendInit!");
+ }
+ atts.override_redirect = True;
+ Tk_ChangeWindowAttributes(dispPtr->commTkwin,
+ CWOverrideRedirect, &atts);
+ Tk_CreateEventHandler(dispPtr->commTkwin, PropertyChangeMask,
+ SendEventProc, (ClientData) dispPtr);
+ Tk_MakeWindowExist(dispPtr->commTkwin);
+
+ /*
+ * Get atoms used as property names.
+ */
+
+ dispPtr->commProperty = Tk_InternAtom(dispPtr->commTkwin, "Comm");
+ dispPtr->registryProperty = Tk_InternAtom(dispPtr->commTkwin,
+ "InterpRegistry");
+ dispPtr->appNameProperty = Tk_InternAtom(dispPtr->commTkwin,
+ "TK_APPLICATION");
+
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * SendEventProc --
+ *
+ * This procedure is invoked automatically by the toolkit
+ * event manager when a property changes on the communication
+ * window. This procedure reads the property and handles
+ * command requests and responses.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If there are command requests in the property, they
+ * are executed. If there are responses in the property,
+ * their information is saved for the (ostensibly waiting)
+ * "send" commands. The property is deleted.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+SendEventProc(clientData, eventPtr)
+ ClientData clientData; /* Display information. */
+ XEvent *eventPtr; /* Information about event. */
+{
+ TkDisplay *dispPtr = (TkDisplay *) clientData;
+ char *propInfo;
+ register char *p;
+ int result, actualFormat;
+ unsigned long numItems, bytesAfter;
+ Atom actualType;
+ Tcl_Interp *remoteInterp; /* Interp in which to execute the command. */
+
+ if ((eventPtr->xproperty.atom != dispPtr->commProperty)
+ || (eventPtr->xproperty.state != PropertyNewValue)) {
+ return;
+ }
+
+ /*
+ * Read the comm property and delete it.
+ */
+
+ propInfo = NULL;
+ result = XGetWindowProperty(dispPtr->display,
+ Tk_WindowId(dispPtr->commTkwin),
+ dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
+ XA_STRING, &actualType, &actualFormat,
+ &numItems, &bytesAfter, (unsigned char **) &propInfo);
+
+ /*
+ * If the property doesn't exist or is improperly formed
+ * then ignore it.
+ */
+
+ if ((result != Success) || (actualType != XA_STRING)
+ || (actualFormat != 8)) {
+ if (propInfo != NULL) {
+ XFree(propInfo);
+ }
+ return;
+ }
+
+ /*
+ * Several commands and results could arrive in the property at
+ * one time; each iteration through the outer loop handles a
+ * single command or result.
+ */
+
+ for (p = propInfo; (p-propInfo) < (int) numItems; ) {
+ /*
+ * Ignore leading NULLs; each command or result starts with a
+ * NULL so that no matter how badly formed a preceding command
+ * is, we'll be able to tell that a new command/result is
+ * starting.
+ */
+
+ if (*p == 0) {
+ p++;
+ continue;
+ }
+
+ if ((*p == 'c') && (p[1] == 0)) {
+ Window commWindow;
+ char *interpName, *script, *serial, *end;
+ Tcl_DString reply;
+ RegisteredInterp *riPtr;
+
+ /*
+ *----------------------------------------------------------
+ * This is an incoming command from some other application.
+ * Iterate over all of its options. Stop when we reach
+ * the end of the property or something that doesn't look
+ * like an option.
+ *----------------------------------------------------------
+ */
+
+ p += 2;
+ interpName = NULL;
+ commWindow = None;
+ serial = "";
+ script = NULL;
+ while (((p-propInfo) < (int) numItems) && (*p == '-')) {
+ switch (p[1]) {
+ case 'r':
+ commWindow = (Window) strtoul(p+2, &end, 16);
+ if ((end == p+2) || (*end != ' ')) {
+ commWindow = None;
+ } else {
+ p = serial = end+1;
+ }
+ break;
+ case 'n':
+ if (p[2] == ' ') {
+ interpName = p+3;
+ }
+ break;
+ case 's':
+ if (p[2] == ' ') {
+ script = p+3;
+ }
+ break;
+ }
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ }
+
+ if ((script == NULL) || (interpName == NULL)) {
+ continue;
+ }
+
+ /*
+ * Initialize the result property, so that we're ready at any
+ * time if we need to return an error.
+ */
+
+ if (commWindow != None) {
+ Tcl_DStringInit(&reply);
+ Tcl_DStringAppend(&reply, "\0r\0-s ", 6);
+ Tcl_DStringAppend(&reply, serial, -1);
+ Tcl_DStringAppend(&reply, "\0-r ", 4);
+ }
+
+ if (!ServerSecure(dispPtr)) {
+ if (commWindow != None) {
+ Tcl_DStringAppend(&reply, "X server insecure (must use xauth-style authorization); command ignored", -1);
+ }
+ result = TCL_ERROR;
+ goto returnResult;
+ }
+
+ /*
+ * Locate the application, then execute the script.
+ */
+
+ for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
+ if (riPtr == NULL) {
+ if (commWindow != None) {
+ Tcl_DStringAppend(&reply,
+ "receiver never heard of interpreter \"", -1);
+ Tcl_DStringAppend(&reply, interpName, -1);
+ Tcl_DStringAppend(&reply, "\"", 1);
+ }
+ result = TCL_ERROR;
+ goto returnResult;
+ }
+ if (strcmp(riPtr->name, interpName) == 0) {
+ break;
+ }
+ }
+ Tcl_Preserve((ClientData) riPtr);
+
+ /*
+ * We must protect the interpreter because the script may
+ * enter another event loop, which might call Tcl_DeleteInterp.
+ */
+
+ remoteInterp = riPtr->interp;
+ Tcl_Preserve((ClientData) remoteInterp);
+
+ result = Tcl_GlobalEval(remoteInterp, script);
+
+ /*
+ * The call to Tcl_Release may have released the interpreter
+ * which will cause the "send" command for that interpreter
+ * to be deleted. The command deletion callback will set the
+ * riPtr->interp field to NULL, hence the check below for NULL.
+ */
+
+ if (commWindow != None) {
+ Tcl_DStringAppend(&reply, remoteInterp->result, -1);
+ if (result == TCL_ERROR) {
+ char *varValue;
+
+ varValue = Tcl_GetVar2(remoteInterp, "errorInfo",
+ (char *) NULL, TCL_GLOBAL_ONLY);
+ if (varValue != NULL) {
+ Tcl_DStringAppend(&reply, "\0-i ", 4);
+ Tcl_DStringAppend(&reply, varValue, -1);
+ }
+ varValue = Tcl_GetVar2(remoteInterp, "errorCode",
+ (char *) NULL, TCL_GLOBAL_ONLY);
+ if (varValue != NULL) {
+ Tcl_DStringAppend(&reply, "\0-e ", 4);
+ Tcl_DStringAppend(&reply, varValue, -1);
+ }
+ }
+ }
+ Tcl_Release((ClientData) remoteInterp);
+ Tcl_Release((ClientData) riPtr);
+
+ /*
+ * Return the result to the sender if a commWindow was
+ * specified (if none was specified then this is an asynchronous
+ * call). Right now reply has everything but the completion
+ * code, but it needs the NULL to terminate the current option.
+ */
+
+ returnResult:
+ if (commWindow != None) {
+ if (result != TCL_OK) {
+ char buffer[20];
+
+ sprintf(buffer, "%d", result);
+ Tcl_DStringAppend(&reply, "\0-c ", 4);
+ Tcl_DStringAppend(&reply, buffer, -1);
+ }
+ (void) AppendPropCarefully(dispPtr->display, commWindow,
+ dispPtr->commProperty, Tcl_DStringValue(&reply),
+ Tcl_DStringLength(&reply) + 1,
+ (PendingCommand *) NULL);
+ XFlush(dispPtr->display);
+ Tcl_DStringFree(&reply);
+ }
+ } else if ((*p == 'r') && (p[1] == 0)) {
+ int serial, code, gotSerial;
+ char *errorInfo, *errorCode, *resultString;
+ PendingCommand *pcPtr;
+
+ /*
+ *----------------------------------------------------------
+ * This is a reply to some command that we sent out. Iterate
+ * over all of its options. Stop when we reach the end of the
+ * property or something that doesn't look like an option.
+ *----------------------------------------------------------
+ */
+
+ p += 2;
+ code = TCL_OK;
+ gotSerial = 0;
+ errorInfo = NULL;
+ errorCode = NULL;
+ resultString = "";
+ while (((p-propInfo) < (int) numItems) && (*p == '-')) {
+ switch (p[1]) {
+ case 'c':
+ if (sscanf(p+2, " %d", &code) != 1) {
+ code = TCL_OK;
+ }
+ break;
+ case 'e':
+ if (p[2] == ' ') {
+ errorCode = p+3;
+ }
+ break;
+ case 'i':
+ if (p[2] == ' ') {
+ errorInfo = p+3;
+ }
+ break;
+ case 'r':
+ if (p[2] == ' ') {
+ resultString = p+3;
+ }
+ break;
+ case 's':
+ if (sscanf(p+2, " %d", &serial) == 1) {
+ gotSerial = 1;
+ }
+ break;
+ }
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ }
+
+ if (!gotSerial) {
+ continue;
+ }
+
+ /*
+ * Give the result information to anyone who's
+ * waiting for it.
+ */
+
+ for (pcPtr = pendingCommands; pcPtr != NULL;
+ pcPtr = pcPtr->nextPtr) {
+ if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
+ continue;
+ }
+ pcPtr->code = code;
+ if (resultString != NULL) {
+ pcPtr->result = (char *) ckalloc((unsigned)
+ (strlen(resultString) + 1));
+ strcpy(pcPtr->result, resultString);
+ }
+ if (code == TCL_ERROR) {
+ if (errorInfo != NULL) {
+ pcPtr->errorInfo = (char *) ckalloc((unsigned)
+ (strlen(errorInfo) + 1));
+ strcpy(pcPtr->errorInfo, errorInfo);
+ }
+ if (errorCode != NULL) {
+ pcPtr->errorCode = (char *) ckalloc((unsigned)
+ (strlen(errorCode) + 1));
+ strcpy(pcPtr->errorCode, errorCode);
+ }
+ }
+ pcPtr->gotResponse = 1;
+ break;
+ }
+ } else {
+ /*
+ * Didn't recognize this thing. Just skip through the next
+ * null character and try again.
+ */
+
+ while (*p != 0) {
+ p++;
+ }
+ p++;
+ }
+ }
+ XFree(propInfo);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * AppendPropCarefully --
+ *
+ * Append a given property to a given window, but set up
+ * an X error handler so that if the append fails this
+ * procedure can return an error code rather than having
+ * Xlib panic.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The given property on the given window is appended to.
+ * If this operation fails and if pendingPtr is non-NULL,
+ * then the pending operation is marked as complete with
+ * an error.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+AppendPropCarefully(display, window, property, value, length, pendingPtr)
+ Display *display; /* Display on which to operate. */
+ Window window; /* Window whose property is to
+ * be modified. */
+ Atom property; /* Name of property. */
+ char *value; /* Characters to append to property. */
+ int length; /* Number of bytes to append. */
+ PendingCommand *pendingPtr; /* Pending command to mark complete
+ * if an error occurs during the
+ * property op. NULL means just
+ * ignore the error. */
+{
+ Tk_ErrorHandler handler;
+
+ handler = Tk_CreateErrorHandler(display, -1, -1, -1, AppendErrorProc,
+ (ClientData) pendingPtr);
+ XChangeProperty(display, window, property, XA_STRING, 8,
+ PropModeAppend, (unsigned char *) value, length);
+ Tk_DeleteErrorHandler(handler);
+}
+
+/*
+ * The procedure below is invoked if an error occurs during
+ * the XChangeProperty operation above.
+ */
+
+ /* ARGSUSED */
+static int
+AppendErrorProc(clientData, errorPtr)
+ ClientData clientData; /* Command to mark complete, or NULL. */
+ XErrorEvent *errorPtr; /* Information about error. */
+{
+ PendingCommand *pendingPtr = (PendingCommand *) clientData;
+ register PendingCommand *pcPtr;
+
+ if (pendingPtr == NULL) {
+ return 0;
+ }
+
+ /*
+ * Make sure this command is still pending.
+ */
+
+ for (pcPtr = pendingCommands; pcPtr != NULL;
+ pcPtr = pcPtr->nextPtr) {
+ if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
+ pcPtr->result = (char *) ckalloc((unsigned)
+ (strlen(pcPtr->target) + 50));
+ sprintf(pcPtr->result, "no application named \"%s\"",
+ pcPtr->target);
+ pcPtr->code = TCL_ERROR;
+ pcPtr->gotResponse = 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * DeleteProc --
+ *
+ * This procedure 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.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+DeleteProc(clientData)
+ ClientData clientData; /* Info about registration, passed
+ * as ClientData. */
+{
+ RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
+ register RegisteredInterp *riPtr2;
+ NameRegistry *regPtr;
+
+ regPtr = RegOpen(riPtr->interp, riPtr->dispPtr, 1);
+ RegDeleteName(regPtr, riPtr->name);
+ RegClose(regPtr);
+
+ if (registry == riPtr) {
+ registry = riPtr->nextPtr;
+ } else {
+ for (riPtr2 = registry; riPtr2 != NULL;
+ riPtr2 = riPtr2->nextPtr) {
+ if (riPtr2->nextPtr == riPtr) {
+ riPtr2->nextPtr = riPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ ckfree((char *) riPtr->name);
+ riPtr->interp = NULL;
+ UpdateCommWindow(riPtr->dispPtr);
+ Tcl_EventuallyFree((ClientData) riPtr, TCL_DYNAMIC);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SendRestrictProc --
+ *
+ * This procedure filters incoming events when a "send" command
+ * is outstanding. It defers all events except those containing
+ * send commands and results.
+ *
+ * Results:
+ * False is returned except for property-change events on a
+ * commWindow.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static Tk_RestrictAction
+SendRestrictProc(clientData, eventPtr)
+ ClientData clientData; /* Not used. */
+ register XEvent *eventPtr; /* Event that just arrived. */
+{
+ TkDisplay *dispPtr;
+
+ if (eventPtr->type != PropertyNotify) {
+ return TK_DEFER_EVENT;
+ }
+ for (dispPtr = tkDisplayList; dispPtr != NULL; dispPtr = dispPtr->nextPtr) {
+ if ((eventPtr->xany.display == dispPtr->display)
+ && (eventPtr->xproperty.window
+ == Tk_WindowId(dispPtr->commTkwin))) {
+ return TK_PROCESS_EVENT;
+ }
+ }
+ return TK_DEFER_EVENT;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateCommWindow --
+ *
+ * This procedure updates the list of application names stored
+ * on our commWindow. It is typically called when interpreters
+ * are registered and unregistered.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The TK_APPLICATION property on the comm window is updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateCommWindow(dispPtr)
+ TkDisplay *dispPtr; /* Display whose commWindow is to be
+ * updated. */
+{
+ Tcl_DString names;
+ RegisteredInterp *riPtr;
+
+ Tcl_DStringInit(&names);
+ for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
+ Tcl_DStringAppendElement(&names, riPtr->name);
+ }
+ XChangeProperty(dispPtr->display, Tk_WindowId(dispPtr->commTkwin),
+ dispPtr->appNameProperty, XA_STRING, 8, PropModeReplace,
+ (unsigned char *) Tcl_DStringValue(&names),
+ Tcl_DStringLength(&names));
+ Tcl_DStringFree(&names);
+}
diff --git a/unix/tkUnixWm.c b/unix/tkUnixWm.c
new file mode 100644
index 0000000..0c26c9b
--- /dev/null
+++ b/unix/tkUnixWm.c
@@ -0,0 +1,4813 @@
+/*
+ * tkUnixWm.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) 1991-1994 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * SCCS: @(#) tkUnixWm.c 1.155 97/10/28 08:35:19
+ */
+
+#include "tkPort.h"
+#include "tkInt.h"
+#include "tkUnixInt.h"
+#include <errno.h>
+
+/*
+ * 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[4]; /* 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) (sizeof(ProtocolHandler) - 3 + cmdLength))
+
+/*
+ * 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. */
+ Window reparent; /* If the window has been reparented, this
+ * gives the ID of the ancestor of the window
+ * that is a child of the root window (may
+ * not be window's immediate parent). If
+ * the window isn't reparented, this has the
+ * value None. */
+ char *title; /* Title to display in window caption. If
+ * NULL, use name of widget. Malloced. */
+ char *iconName; /* Name to display in icon. Malloced. */
+ Window master; /* Master window for TRANSIENT_FOR property,
+ * or None. */
+ 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. */
+ char *masterWindowName; /* Path name of window specified as master
+ * in "wm transient" command, or NULL.
+ * Malloc-ed. Note: this field doesn't
+ * get updated if masterWindowName is
+ * destroyed. */
+ 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. */
+ int withdrawn; /* Non-zero means window has been withdrawn. */
+
+ /*
+ * In order to support menubars transparently under X, each toplevel
+ * window is encased in an additional window, called the wrapper,
+ * that holds the toplevel and the menubar, if any. The information
+ * below is used to keep track of the wrapper and the menubar.
+ */
+
+ TkWindow *wrapperPtr; /* Pointer to information about the wrapper.
+ * This is the "real" toplevel window as
+ * seen by the window manager. Although
+ * this is an official Tk window, it
+ * doesn't appear in the application's
+ * window hierarchy. NULL means that
+ * the wrapper hasn't been created yet. */
+ Tk_Window menubar; /* Pointer to information about the
+ * menubar, or NULL if there is no
+ * menubar for this toplevel. */
+ int menuHeight; /* Amount of vertical space needed for
+ * menubar, measured in pixels. If
+ * menubar is non-NULL, this is >= 1 (X
+ * servers don't like dimensions of 0). */
+
+ /*
+ * Information used to construct an XSizeHints structure for
+ * the window manager:
+ */
+
+ 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
+ * grid units, not pixels. */
+ int maxWidth, maxHeight; /* Maximum dimensions of window, in
+ * grid units, not pixels. */
+ 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 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 parentWidth, parentHeight;
+ /* Width and height of reparent, in pixels
+ * *including border*. If window hasn't been
+ * reparented then these will be the outer
+ * dimensions of the window, including
+ * border. */
+ int xInParent, yInParent; /* Offset of wrapperPtr within reparent,
+ * measured in pixels from upper-left outer
+ * corner of reparent's border to upper-left
+ * outer corner of wrapperPtr's border. If
+ * not reparented then these are zero. */
+ int configWidth, configHeight;
+ /* Dimensions passed to last request that we
+ * issued to change geometry of the wrapper.
+ * Used to eliminate redundant resize
+ * operations. */
+
+ /*
+ * Information about the virtual root window for this top-level,
+ * if there is one.
+ */
+
+ Window vRoot; /* Virtual root window for this top-level,
+ * or None if there is no virtual root
+ * window (i.e. just use the screen's root). */
+ int vRootX, vRootY; /* Position of the virtual root inside the
+ * root window. If the WM_VROOT_OFFSET_STALE
+ * flag is set then this information may be
+ * incorrect and needs to be refreshed from
+ * the X server. If vRoot is None then these
+ * values are both 0. */
+ int vRootWidth, vRootHeight;/* Dimensions of the virtual root window.
+ * If vRoot is None, gives the dimensions
+ * of the containing screen. This information
+ * is never stale, even though vRootX and
+ * vRootY can be. */
+
+ /*
+ * Miscellaneous information.
+ */
+
+ ProtocolHandler *protPtr; /* First in list of protocol handlers for
+ * this window (NULL means none). */
+ int cmdArgc; /* Number of elements in cmdArgv below. */
+ 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. */
+ 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.
+ * WM_SYNC_PENDING - set to non-zero while waiting for the window
+ * manager to respond to some state change.
+ * WM_VROOT_OFFSET_STALE - non-zero means that (x,y) offset information
+ * about the virtual root window is stale and
+ * needs to be fetched fresh from the X server.
+ * WM_ABOUT_TO_MAP - non-zero means that the window is about to
+ * be mapped by TkWmMapWindow. This is used
+ * by UpdateGeometryInfo to modify its behavior.
+ * 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).
+ */
+
+#define WM_NEVER_MAPPED 1
+#define WM_UPDATE_PENDING 2
+#define WM_NEGATIVE_X 4
+#define WM_NEGATIVE_Y 8
+#define WM_UPDATE_SIZE_HINTS 0x10
+#define WM_SYNC_PENDING 0x20
+#define WM_VROOT_OFFSET_STALE 0x40
+#define WM_ABOUT_TO_MAP 0x100
+#define WM_MOVE_PENDING 0x200
+#define WM_COLORMAPS_EXPLICIT 0x400
+#define WM_ADDED_TOPLEVEL_COLORMAP 0x800
+#define WM_WIDTH_NOT_RESIZABLE 0x1000
+#define WM_HEIGHT_NOT_RESIZABLE 0x2000
+
+/*
+ * This module keeps a list of all top-level windows, primarily to
+ * simplify the job of Tk_CoordsToWindow.
+ */
+
+static WmInfo *firstWmPtr = NULL; /* Points to first top-level window. */
+
+
+/*
+ * The variable below is used to enable or disable tracing in this
+ * module. If tracing is enabled, then information is printed on
+ * standard output about interesting interactions with the window
+ * manager.
+ */
+
+static int wmTracing = 0;
+
+/*
+ * The following structures are the official type records for geometry
+ * management of top-level and menubar windows.
+ */
+
+static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr wmMgrType = {
+ "wm", /* name */
+ TopLevelReqProc, /* requestProc */
+ (Tk_GeomLostSlaveProc *) NULL, /* lostSlaveProc */
+};
+
+static void MenubarReqProc _ANSI_ARGS_((ClientData clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr menubarMgrType = {
+ "menubar", /* name */
+ MenubarReqProc, /* requestProc */
+ (Tk_GeomLostSlaveProc *) NULL, /* lostSlaveProc */
+};
+
+/*
+ * Structures of the following type are used for communication between
+ * WaitForEvent, WaitRestrictProc, and WaitTimeoutProc.
+ */
+
+typedef struct WaitRestrictInfo {
+ Display *display; /* Window belongs to this display. */
+ Window window; /* We're waiting for events on this window. */
+ int type; /* We only care about this type of event. */
+ XEvent *eventPtr; /* Where to store the event when it's found. */
+ int foundEvent; /* Non-zero means that an event of the
+ * desired type has been found. */
+} WaitRestrictInfo;
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static int ComputeReparentGeometry _ANSI_ARGS_((WmInfo *wmPtr));
+static void ConfigureEvent _ANSI_ARGS_((WmInfo *wmPtr,
+ XConfigureEvent *eventPtr));
+static void CreateWrapper _ANSI_ARGS_((WmInfo *wmPtr));
+static void GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
+ int *maxWidthPtr, int *maxHeightPtr));
+static void MenubarDestroyProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static int ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
+ char *string, TkWindow *winPtr));
+static void ReparentEvent _ANSI_ARGS_((WmInfo *wmPtr,
+ XReparentEvent *eventPtr));
+static void TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
+ Tk_Window tkwin));
+static void UpdateGeometryInfo _ANSI_ARGS_((
+ ClientData clientData));
+static void UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
+static void UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
+static void UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
+static void UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));
+static void WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,
+ unsigned long serial));
+static int WaitForEvent _ANSI_ARGS_((Display *display,
+ Window window, int type, XEvent *eventPtr));
+static void WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,
+ int mapped));
+static Tk_RestrictAction
+ WaitRestrictProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+static void WrapperEventProc _ANSI_ARGS_((ClientData clientData,
+ XEvent *eventPtr));
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmNewWindow --
+ *
+ * This procedure 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(winPtr)
+ TkWindow *winPtr; /* Newly-created top-level window. */
+{
+ register WmInfo *wmPtr;
+
+ wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
+ wmPtr->winPtr = winPtr;
+ wmPtr->reparent = None;
+ wmPtr->title = NULL;
+ wmPtr->iconName = NULL;
+ wmPtr->master = None;
+ 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;
+ wmPtr->leaderName = NULL;
+ wmPtr->masterWindowName = NULL;
+ wmPtr->icon = NULL;
+ wmPtr->iconFor = NULL;
+ wmPtr->withdrawn = 0;
+ wmPtr->wrapperPtr = NULL;
+ wmPtr->menubar = NULL;
+ wmPtr->menuHeight = 0;
+ wmPtr->sizeHintsFlags = 0;
+ wmPtr->minWidth = wmPtr->minHeight = 1;
+
+ /*
+ * Default the maximum dimensions to the size of the display, minus
+ * a guess about how space is needed for window manager decorations.
+ */
+
+ wmPtr->maxWidth = 0;
+ wmPtr->maxHeight = 0;
+ wmPtr->gridWin = NULL;
+ 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->parentWidth = winPtr->changes.width
+ + 2*winPtr->changes.border_width;
+ wmPtr->parentHeight = winPtr->changes.height
+ + 2*winPtr->changes.border_width;
+ wmPtr->xInParent = wmPtr->yInParent = 0;
+ wmPtr->configWidth = -1;
+ wmPtr->configHeight = -1;
+ wmPtr->vRoot = None;
+ wmPtr->protPtr = NULL;
+ wmPtr->cmdArgv = NULL;
+ wmPtr->clientMachine = NULL;
+ wmPtr->flags = WM_NEVER_MAPPED;
+ wmPtr->nextPtr = firstWmPtr;
+ firstWmPtr = wmPtr;
+ winPtr->wmInfoPtr = wmPtr;
+
+ UpdateVRootGeometry(wmPtr);
+
+ /*
+ * Arrange for geometry requests to be reflected from the window
+ * to the window manager.
+ */
+
+ Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmMapWindow --
+ *
+ * This procedure 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 procedure decides that
+ * isn't appropriate (e.g. because the window is withdrawn).
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmMapWindow(winPtr)
+ TkWindow *winPtr; /* Top-level window that's about to
+ * be mapped. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ XTextProperty textProp;
+ char *string;
+
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ wmPtr->flags &= ~WM_NEVER_MAPPED;
+
+ /*
+ * This is the first time this window has ever been mapped.
+ * First create the wrapper window that provides space for a
+ * menubar.
+ */
+
+ if (wmPtr->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr);
+ }
+
+ /*
+ * Store all the window-manager-related information for the
+ * window.
+ */
+
+ string = (wmPtr->title != NULL) ? wmPtr->title : winPtr->nameUid;
+ if (XStringListToTextProperty(&string, 1, &textProp) != 0) {
+ XSetWMName(winPtr->display, wmPtr->wrapperPtr->window, &textProp);
+ XFree((char *) textProp.value);
+ }
+
+ TkWmSetClass(winPtr);
+
+ if (wmPtr->iconName != NULL) {
+ XSetIconName(winPtr->display, wmPtr->wrapperPtr->window,
+ wmPtr->iconName);
+ }
+
+ if (wmPtr->master != None) {
+ XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
+ wmPtr->master);
+ }
+
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ UpdateHints(winPtr);
+ UpdateWmProtocols(wmPtr);
+ if (wmPtr->cmdArgv != NULL) {
+ XSetCommand(winPtr->display, wmPtr->wrapperPtr->window,
+ wmPtr->cmdArgv, wmPtr->cmdArgc);
+ }
+ if (wmPtr->clientMachine != NULL) {
+ if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
+ != 0) {
+ XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
+ &textProp);
+ XFree((char *) textProp.value);
+ }
+ }
+ }
+ if (wmPtr->hints.initial_state == WithdrawnState) {
+ return;
+ }
+ if (wmPtr->iconFor != NULL) {
+ /*
+ * This window is an icon for somebody else. Make sure that
+ * the geometry is up-to-date, then return without mapping
+ * the window.
+ */
+
+ if (wmPtr->flags & WM_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
+ }
+ UpdateGeometryInfo((ClientData) winPtr);
+ return;
+ }
+ wmPtr->flags |= WM_ABOUT_TO_MAP;
+ if (wmPtr->flags & WM_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
+ }
+ UpdateGeometryInfo((ClientData) winPtr);
+ wmPtr->flags &= ~WM_ABOUT_TO_MAP;
+
+ /*
+ * Map the window, then wait to be sure that the window manager has
+ * processed the map operation.
+ */
+
+ XMapWindow(winPtr->display, wmPtr->wrapperPtr->window);
+ if (wmPtr->hints.initial_state == NormalState) {
+ WaitForMapNotify(winPtr, 1);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmUnmapWindow --
+ *
+ * This procedure is invoked to unmap a top-level window. The
+ * only thing it does special is to wait for the window actually
+ * to be unmapped.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Unmaps the window.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+TkWmUnmapWindow(winPtr)
+ TkWindow *winPtr; /* Top-level window that's about to
+ * be mapped. */
+{
+ /*
+ * It seems to be important to wait after unmapping a top-level
+ * window until the window really gets unmapped. I don't completely
+ * understand all the interactions with the window manager, but if
+ * we go on without waiting, and if the window is then mapped again
+ * quickly, events seem to get lost so that we think the window isn't
+ * mapped when in fact it is mapped. I suspect that this has something
+ * to do with the window manager filtering Map events (and possily not
+ * filtering Unmap events?).
+ */
+ XUnmapWindow(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window);
+ WaitForMapNotify(winPtr, 0);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmDeadWindow --
+ *
+ * This procedure 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(winPtr)
+ TkWindow *winPtr; /* Top-level window that's being deleted. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ WmInfo *wmPtr2;
+
+ if (wmPtr == NULL) {
+ return;
+ }
+ if (firstWmPtr == wmPtr) {
+ firstWmPtr = wmPtr->nextPtr;
+ } else {
+ register WmInfo *prevPtr;
+
+ for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
+ if (prevPtr == NULL) {
+ panic("couldn't unlink window in TkWmDeadWindow");
+ }
+ if (prevPtr->nextPtr == wmPtr) {
+ prevPtr->nextPtr = wmPtr->nextPtr;
+ break;
+ }
+ }
+ }
+ 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->masterWindowName != NULL) {
+ ckfree(wmPtr->masterWindowName);
+ }
+ if (wmPtr->icon != NULL) {
+ wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr2->iconFor = NULL;
+ wmPtr2->withdrawn = 1;
+ }
+ if (wmPtr->iconFor != NULL) {
+ wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
+ wmPtr2->icon = NULL;
+ wmPtr2->hints.flags &= ~IconWindowHint;
+ UpdateHints((TkWindow *) wmPtr->iconFor);
+ }
+ if (wmPtr->menubar != NULL) {
+ Tk_DestroyWindow(wmPtr->menubar);
+ }
+ if (wmPtr->wrapperPtr != NULL) {
+ /*
+ * The rest of Tk doesn't know that we reparent the toplevel
+ * inside the wrapper, so reparent it back out again before
+ * deleting the wrapper; otherwise the toplevel will get deleted
+ * twice (once implicitly by the deletion of the wrapper).
+ */
+
+ XUnmapWindow(winPtr->display, winPtr->window);
+ XReparentWindow(winPtr->display, winPtr->window,
+ XRootWindow(winPtr->display, winPtr->screenNum), 0, 0);
+ Tk_DestroyWindow((Tk_Window) wmPtr->wrapperPtr);
+ }
+ while (wmPtr->protPtr != NULL) {
+ ProtocolHandler *protPtr;
+
+ protPtr = wmPtr->protPtr;
+ wmPtr->protPtr = protPtr->nextPtr;
+ Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
+ }
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ }
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ }
+ if (wmPtr->flags & WM_UPDATE_PENDING) {
+ Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
+ }
+ ckfree((char *) wmPtr);
+ winPtr->wmInfoPtr = NULL;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * TkWmSetClass --
+ *
+ * This procedure is invoked whenever a top-level window's
+ * class is changed. If the window has been mapped then this
+ * procedure 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(winPtr)
+ TkWindow *winPtr; /* Newly-created top-level window. */
+{
+ if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
+ return;
+ }
+
+ if (winPtr->classUid != NULL) {
+ XClassHint *classPtr;
+
+ classPtr = XAllocClassHint();
+ classPtr->res_name = winPtr->nameUid;
+ classPtr->res_class = winPtr->classUid;
+ XSetClassHint(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window,
+ classPtr);
+ XFree((char *) classPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_WmCmd --
+ *
+ * This procedure 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_WmCmd(clientData, interp, argc, argv)
+ ClientData clientData; /* Main window associated with
+ * interpreter. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. */
+{
+ Tk_Window tkwin = (Tk_Window) clientData;
+ TkWindow *winPtr;
+ register WmInfo *wmPtr;
+ int c;
+ size_t length;
+
+ if (argc < 2) {
+ wrongNumArgs:
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " option window ?arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ c = argv[1][0];
+ length = strlen(argv[1]);
+ if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
+ && (length >= 3)) {
+ if ((argc != 2) && (argc != 3)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " tracing ?boolean?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 2) {
+ interp->result = (wmTracing) ? "on" : "off";
+ return TCL_OK;
+ }
+ return Tcl_GetBoolean(interp, argv[2], &wmTracing);
+ }
+
+ if (argc < 3) {
+ goto wrongNumArgs;
+ }
+ winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
+ if (winPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ Tcl_AppendResult(interp, "window \"", winPtr->pathName,
+ "\" isn't a top-level window", (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr = winPtr->wmInfoPtr;
+ if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
+ int numer1, denom1, numer2, denom2;
+
+ if ((argc != 3) && (argc != 7)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " aspect window ?minNumer minDenom ",
+ "maxNumer maxDenom?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & PAspect) {
+ sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
+ wmPtr->minAspect.y, wmPtr->maxAspect.x,
+ wmPtr->maxAspect.y);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~PAspect;
+ } else {
+ if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
+ (denom2 <= 0)) {
+ interp->result = "aspect number can't be <= 0";
+ return TCL_ERROR;
+ }
+ wmPtr->minAspect.x = numer1;
+ wmPtr->minAspect.y = denom1;
+ wmPtr->maxAspect.x = numer2;
+ wmPtr->maxAspect.y = denom2;
+ wmPtr->sizeHintsFlags |= PAspect;
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " client window ?name?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->clientMachine != NULL) {
+ interp->result = wmPtr->clientMachine;
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == 0) {
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ wmPtr->clientMachine = NULL;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
+ Tk_InternAtom((Tk_Window) winPtr,
+ "WM_CLIENT_MACHINE"));
+ }
+ }
+ return TCL_OK;
+ }
+ if (wmPtr->clientMachine != NULL) {
+ ckfree((char *) wmPtr->clientMachine);
+ }
+ wmPtr->clientMachine = (char *)
+ ckalloc((unsigned) (strlen(argv[3]) + 1));
+ strcpy(wmPtr->clientMachine, argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XTextProperty textProp;
+ if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
+ != 0) {
+ XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
+ &textProp);
+ XFree((char *) textProp.value);
+ }
+ }
+ } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
+ && (length >= 3)) {
+ Window *cmapList;
+ TkWindow *winPtr2;
+ int count, i, windowArgc, gotToplevel;
+ char buffer[20], **windowArgv;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " colormapwindows window ?windowList?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ Tk_MakeWindowExist((Tk_Window) winPtr);
+ if (wmPtr->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr);
+ }
+ if (argc == 3) {
+ if (XGetWMColormapWindows(winPtr->display,
+ wmPtr->wrapperPtr->window, &cmapList, &count) == 0) {
+ return TCL_OK;
+ }
+ for (i = 0; i < count; i++) {
+ if ((i == (count-1))
+ && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
+ break;
+ }
+ winPtr2 = (TkWindow *) Tk_IdToWindow(winPtr->display,
+ cmapList[i]);
+ if (winPtr2 == NULL) {
+ sprintf(buffer, "0x%lx", cmapList[i]);
+ Tcl_AppendElement(interp, buffer);
+ } else {
+ Tcl_AppendElement(interp, winPtr2->pathName);
+ }
+ }
+ XFree((char *) cmapList);
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ cmapList = (Window *) ckalloc((unsigned)
+ ((windowArgc+1)*sizeof(Window)));
+ gotToplevel = 0;
+ for (i = 0; i < windowArgc; i++) {
+ winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
+ tkwin);
+ if (winPtr2 == NULL) {
+ ckfree((char *) cmapList);
+ ckfree((char *) windowArgv);
+ return TCL_ERROR;
+ }
+ if (winPtr2 == winPtr) {
+ gotToplevel = 1;
+ }
+ if (winPtr2->window == None) {
+ Tk_MakeWindowExist((Tk_Window) winPtr2);
+ }
+ cmapList[i] = winPtr2->window;
+ }
+ if (!gotToplevel) {
+ wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
+ cmapList[windowArgc] = wmPtr->wrapperPtr->window;
+ windowArgc++;
+ } else {
+ wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
+ }
+ wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
+ XSetWMColormapWindows(winPtr->display, wmPtr->wrapperPtr->window,
+ cmapList, windowArgc);
+ ckfree((char *) cmapList);
+ ckfree((char *) windowArgv);
+ return TCL_OK;
+ } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
+ && (length >= 3)) {
+ int cmdArgc;
+ char **cmdArgv;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " command window ?value?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->cmdArgv != NULL) {
+ interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
+ interp->freeProc = TCL_DYNAMIC;
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == 0) {
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ wmPtr->cmdArgv = NULL;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
+ Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
+ }
+ }
+ return TCL_OK;
+ }
+ if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (wmPtr->cmdArgv != NULL) {
+ ckfree((char *) wmPtr->cmdArgv);
+ }
+ wmPtr->cmdArgc = cmdArgc;
+ wmPtr->cmdArgv = cmdArgv;
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XSetCommand(winPtr->display, wmPtr->wrapperPtr->window,
+ cmdArgv, cmdArgc);
+ }
+ } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " deiconify window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't deiconify ", argv[2],
+ ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr->hints.initial_state = NormalState;
+ wmPtr->withdrawn = 0;
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ return TCL_OK;
+ }
+ UpdateHints(winPtr);
+ Tk_MapWindow((Tk_Window) winPtr);
+ } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " focusmodel window ?active|passive?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = wmPtr->hints.input ? "passive" : "active";
+ return TCL_OK;
+ }
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
+ wmPtr->hints.input = False;
+ } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
+ wmPtr->hints.input = True;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be active or passive", (char *) NULL);
+ return TCL_ERROR;
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
+ && (length >= 2)) {
+ Window window;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " frame window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ window = wmPtr->reparent;
+ if (window == None) {
+ window = Tk_WindowId((Tk_Window) winPtr);
+ }
+ sprintf(interp->result, "0x%x", (unsigned int) window);
+ } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
+ && (length >= 2)) {
+ char xSign, ySign;
+ int width, height;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " geometry window ?newGeometry?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 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;
+ }
+ sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
+ xSign, wmPtr->x, ySign, wmPtr->y);
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->width = -1;
+ wmPtr->height = -1;
+ goto updateGeom;
+ }
+ return ParseGeometry(interp, argv[3], winPtr);
+ } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
+ && (length >= 3)) {
+ int reqWidth, reqHeight, widthInc, heightInc;
+
+ if ((argc != 3) && (argc != 7)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " grid window ?baseWidth baseHeight ",
+ "widthInc heightInc?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & PBaseSize) {
+ sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
+ wmPtr->reqGridHeight, wmPtr->widthInc,
+ wmPtr->heightInc);
+ }
+ return TCL_OK;
+ }
+ if (*argv[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_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ if (reqWidth < 0) {
+ interp->result = "baseWidth can't be < 0";
+ return TCL_ERROR;
+ }
+ if (reqHeight < 0) {
+ interp->result = "baseHeight can't be < 0";
+ return TCL_ERROR;
+ }
+ if (widthInc < 0) {
+ interp->result = "widthInc can't be < 0";
+ return TCL_ERROR;
+ }
+ if (heightInc < 0) {
+ interp->result = "heightInc can't be < 0";
+ return TCL_ERROR;
+ }
+ Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
+ heightInc);
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
+ && (length >= 3)) {
+ Tk_Window tkwin2;
+ WmInfo *wmPtr2;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " group window ?pathName?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & WindowGroupHint) {
+ interp->result = wmPtr->leaderName;
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~WindowGroupHint;
+ if (wmPtr->leaderName != NULL) {
+ ckfree(wmPtr->leaderName);
+ }
+ wmPtr->leaderName = NULL;
+ } else {
+ tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ while (!Tk_IsTopLevel(tkwin2)) {
+ /*
+ * Ensure that the group leader is actually a Tk toplevel.
+ */
+
+ tkwin2 = Tk_Parent(tkwin2);
+ }
+ Tk_MakeWindowExist(tkwin2);
+ wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
+ if (wmPtr2->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr2);
+ }
+ wmPtr->hints.window_group = Tk_WindowId(wmPtr2->wrapperPtr);
+ wmPtr->hints.flags |= WindowGroupHint;
+ wmPtr->leaderName = ckalloc((unsigned) (strlen(argv[3])+1));
+ strcpy(wmPtr->leaderName, argv[3]);
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
+ && (length >= 5)) {
+ Pixmap pixmap;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconbitmap window ?bitmap?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconPixmapHint) {
+ interp->result = Tk_NameOfBitmap(winPtr->display,
+ wmPtr->hints.icon_pixmap);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ if (wmPtr->hints.icon_pixmap != None) {
+ Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
+ wmPtr->hints.icon_pixmap = None;
+ }
+ wmPtr->hints.flags &= ~IconPixmapHint;
+ } else {
+ pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
+ Tk_GetUid(argv[3]));
+ if (pixmap == None) {
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_pixmap = pixmap;
+ wmPtr->hints.flags |= IconPixmapHint;
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
+ && (length >= 5)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconify window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
+ Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
+ "\": override-redirect flag is set", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->master != None) {
+ Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
+ "\": it is a transient", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't iconify ", argv[2],
+ ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr->hints.initial_state = IconicState;
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ return TCL_OK;
+ }
+ if (wmPtr->withdrawn) {
+ UpdateHints(winPtr);
+ Tk_MapWindow((Tk_Window) winPtr);
+ wmPtr->withdrawn = 0;
+ } else {
+ if (XIconifyWindow(winPtr->display, wmPtr->wrapperPtr->window,
+ winPtr->screenNum) == 0) {
+ interp->result =
+ "couldn't send iconify message to window manager";
+ return TCL_ERROR;
+ }
+ WaitForMapNotify(winPtr, 0);
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
+ && (length >= 5)) {
+ Pixmap pixmap;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconmask window ?bitmap?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconMaskHint) {
+ interp->result = Tk_NameOfBitmap(winPtr->display,
+ wmPtr->hints.icon_mask);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\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, Tk_GetUid(argv[3]));
+ if (pixmap == None) {
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_mask = pixmap;
+ wmPtr->hints.flags |= IconMaskHint;
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
+ && (length >= 5)) {
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconname window ?newName?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
+ return TCL_OK;
+ } else {
+ wmPtr->iconName = ckalloc((unsigned) (strlen(argv[3]) + 1));
+ strcpy(wmPtr->iconName, argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XSetIconName(winPtr->display, wmPtr->wrapperPtr->window,
+ wmPtr->iconName);
+ }
+ }
+ } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
+ && (length >= 5)) {
+ int x, y;
+
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconposition window ?x y?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->hints.flags & IconPositionHint) {
+ sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
+ wmPtr->hints.icon_y);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~IconPositionHint;
+ } else {
+ if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
+ return TCL_ERROR;
+ }
+ wmPtr->hints.icon_x = x;
+ wmPtr->hints.icon_y = y;
+ wmPtr->hints.flags |= IconPositionHint;
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
+ && (length >= 5)) {
+ Tk_Window tkwin2;
+ WmInfo *wmPtr2;
+ XSetWindowAttributes atts;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " iconwindow window ?pathName?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->icon != NULL) {
+ interp->result = Tk_PathName(wmPtr->icon);
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->hints.flags &= ~IconWindowHint;
+ if (wmPtr->icon != NULL) {
+ /*
+ * Remove the icon window relationship. In principle we
+ * should also re-enable button events for the window, but
+ * this doesn't work in general because the window manager
+ * is probably selecting on them (we'll get an error if
+ * we try to re-enable the events). So, just leave the
+ * icon window event-challenged; the user will have to
+ * recreate it if they want button events.
+ */
+
+ wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr2->iconFor = NULL;
+ wmPtr2->withdrawn = 1;
+ wmPtr2->hints.initial_state = WithdrawnState;
+ }
+ wmPtr->icon = NULL;
+ } else {
+ tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
+ if (tkwin2 == NULL) {
+ return TCL_ERROR;
+ }
+ if (!Tk_IsTopLevel(tkwin2)) {
+ Tcl_AppendResult(interp, "can't use ", argv[3],
+ " as icon window: not at top level", (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
+ if (wmPtr2->iconFor != NULL) {
+ Tcl_AppendResult(interp, argv[3], " is already an icon for ",
+ Tk_PathName(wmPtr2->iconFor), (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->icon != NULL) {
+ WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
+ wmPtr3->iconFor = NULL;
+ wmPtr3->withdrawn = 1;
+ wmPtr3->hints.initial_state = WithdrawnState;
+ }
+
+ /*
+ * 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);
+ if (wmPtr2->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr2);
+ }
+ wmPtr->hints.icon_window = Tk_WindowId(wmPtr2->wrapperPtr);
+ wmPtr->hints.flags |= IconWindowHint;
+ wmPtr->icon = tkwin2;
+ wmPtr2->iconFor = (Tk_Window) winPtr;
+ if (!wmPtr2->withdrawn && !(wmPtr2->flags & WM_NEVER_MAPPED)) {
+ wmPtr2->withdrawn = 0;
+ if (XWithdrawWindow(Tk_Display(tkwin2),
+ Tk_WindowId(wmPtr2->wrapperPtr),
+ Tk_ScreenNumber(tkwin2)) == 0) {
+ interp->result =
+ "couldn't send withdraw message to window manager";
+ return TCL_ERROR;
+ }
+ WaitForMapNotify((TkWindow *) tkwin2, 0);
+ }
+ }
+ UpdateHints(winPtr);
+ } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
+ && (length >= 2)) {
+ int width, height;
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " maxsize window ?width height?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ GetMaxSize(wmPtr, &width, &height);
+ sprintf(interp->result, "%d %d", width, height);
+ return TCL_OK;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ wmPtr->maxWidth = width;
+ wmPtr->maxHeight = height;
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
+ && (length >= 2)) {
+ int width, height;
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " minsize window ?width height?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ sprintf(interp->result, "%d %d", wmPtr->minWidth,
+ wmPtr->minHeight);
+ return TCL_OK;
+ }
+ if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
+ return TCL_ERROR;
+ }
+ wmPtr->minWidth = width;
+ wmPtr->minHeight = height;
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 'o')
+ && (strncmp(argv[1], "overrideredirect", length) == 0)) {
+ int boolean;
+ XSetWindowAttributes atts;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " overrideredirect window ?boolean?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
+ interp->result = "1";
+ } else {
+ interp->result = "0";
+ }
+ return TCL_OK;
+ }
+ if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ atts.override_redirect = (boolean) ? True : False;
+ Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
+ &atts);
+ if (winPtr->wmInfoPtr->wrapperPtr != NULL) {
+ Tk_ChangeWindowAttributes(
+ (Tk_Window) winPtr->wmInfoPtr->wrapperPtr,
+ CWOverrideRedirect, &atts);
+ }
+ } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " positionfrom window ?user/program?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & USPosition) {
+ interp->result = "user";
+ } else if (wmPtr->sizeHintsFlags & PPosition) {
+ interp->result = "program";
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
+ } else {
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~PPosition;
+ wmPtr->sizeHintsFlags |= USPosition;
+ } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~USPosition;
+ wmPtr->sizeHintsFlags |= PPosition;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be program or user", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
+ && (length >= 2)) {
+ register ProtocolHandler *protPtr, *prevPtr;
+ Atom protocol;
+ int cmdLength;
+
+ if ((argc < 3) || (argc > 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " protocol window ?name? ?command?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ /*
+ * Return a list of all defined protocols for the window.
+ */
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ Tcl_AppendElement(interp,
+ Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
+ }
+ return TCL_OK;
+ }
+ protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
+ if (argc == 4) {
+ /*
+ * Return the command to handle a given protocol.
+ */
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ if (protPtr->protocol == protocol) {
+ interp->result = protPtr->command;
+ 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((ClientData) protPtr, TCL_DYNAMIC);
+ break;
+ }
+ }
+ cmdLength = strlen(argv[4]);
+ if (cmdLength > 0) {
+ protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
+ protPtr->protocol = protocol;
+ protPtr->nextPtr = wmPtr->protPtr;
+ wmPtr->protPtr = protPtr;
+ protPtr->interp = interp;
+ strcpy(protPtr->command, argv[4]);
+ }
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ UpdateWmProtocols(wmPtr);
+ }
+ } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
+ int width, height;
+
+ if ((argc != 3) && (argc != 5)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " resizable window ?width height?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ sprintf(interp->result, "%d %d",
+ (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
+ (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
+ return TCL_OK;
+ }
+ if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
+ || (Tcl_GetBoolean(interp, argv[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;
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
+ && (length >= 2)) {
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " sizefrom window ?user|program?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->sizeHintsFlags & USSize) {
+ interp->result = "user";
+ } else if (wmPtr->sizeHintsFlags & PSize) {
+ interp->result = "program";
+ }
+ return TCL_OK;
+ }
+ if (*argv[3] == '\0') {
+ wmPtr->sizeHintsFlags &= ~(USSize|PSize);
+ } else {
+ c = argv[3][0];
+ length = strlen(argv[3]);
+ if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~PSize;
+ wmPtr->sizeHintsFlags |= USSize;
+ } else if ((c == 'p')
+ && (strncmp(argv[3], "program", length) == 0)) {
+ wmPtr->sizeHintsFlags &= ~USSize;
+ wmPtr->sizeHintsFlags |= PSize;
+ } else {
+ Tcl_AppendResult(interp, "bad argument \"", argv[3],
+ "\": must be program or user", (char *) NULL);
+ return TCL_ERROR;
+ }
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ goto updateGeom;
+ } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " state window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ interp->result = "icon";
+ } else if (wmPtr->withdrawn) {
+ interp->result = "withdrawn";
+ } else if (Tk_IsMapped((Tk_Window) winPtr)
+ || ((wmPtr->flags & WM_NEVER_MAPPED)
+ && (wmPtr->hints.initial_state == NormalState))) {
+ interp->result = "normal";
+ } else {
+ interp->result = "iconic";
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
+ && (length >= 2)) {
+ if (argc > 4) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " title window ?newTitle?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ interp->result = (wmPtr->title != NULL) ? wmPtr->title
+ : winPtr->nameUid;
+ return TCL_OK;
+ } else {
+ wmPtr->title = ckalloc((unsigned) (strlen(argv[3]) + 1));
+ strcpy(wmPtr->title, argv[3]);
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XTextProperty textProp;
+
+ if (XStringListToTextProperty(&wmPtr->title, 1,
+ &textProp) != 0) {
+ XSetWMName(winPtr->display, wmPtr->wrapperPtr->window,
+ &textProp);
+ XFree((char *) textProp.value);
+ }
+ }
+ }
+ } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
+ && (length >= 3)) {
+ Tk_Window master;
+ WmInfo *wmPtr2;
+
+ if ((argc != 3) && (argc != 4)) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " transient window ?master?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 3) {
+ if (wmPtr->master != None) {
+ interp->result = wmPtr->masterWindowName;
+ }
+ return TCL_OK;
+ }
+ if (argv[3][0] == '\0') {
+ wmPtr->master = None;
+ if (wmPtr->masterWindowName != NULL) {
+ ckfree(wmPtr->masterWindowName);
+ }
+ wmPtr->masterWindowName = NULL;
+ } else {
+ master = Tk_NameToWindow(interp, argv[3], tkwin);
+ if (master == NULL) {
+ return TCL_ERROR;
+ }
+ while (!Tk_IsTopLevel(master)) {
+ /*
+ * Ensure that the master window is actually a Tk toplevel.
+ */
+
+ master = Tk_Parent(master);
+ }
+ Tk_MakeWindowExist(master);
+ wmPtr2 = ((TkWindow *) master)->wmInfoPtr;
+ if (wmPtr2->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr2);
+ }
+ wmPtr->master = Tk_WindowId(wmPtr2->wrapperPtr);
+ wmPtr->masterWindowName = ckalloc((unsigned) (strlen(argv[3])+1));
+ strcpy(wmPtr->masterWindowName, argv[3]);
+ }
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
+ wmPtr->master);
+ }
+ } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)
+ && (length >= 2)) {
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # arguments: must be \"",
+ argv[0], " withdraw window\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (wmPtr->iconFor != NULL) {
+ Tcl_AppendResult(interp, "can't withdraw ", argv[2],
+ ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ wmPtr->hints.initial_state = WithdrawnState;
+ wmPtr->withdrawn = 1;
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ return TCL_OK;
+ }
+ if (XWithdrawWindow(winPtr->display, wmPtr->wrapperPtr->window,
+ winPtr->screenNum) == 0) {
+ interp->result =
+ "couldn't send withdraw message to window manager";
+ return TCL_ERROR;
+ }
+ WaitForMapNotify(winPtr, 0);
+ } else {
+ Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
+ "\": must be aspect, client, command, deiconify, ",
+ "focusmodel, frame, geometry, grid, group, iconbitmap, ",
+ "iconify, iconmask, iconname, iconposition, ",
+ "iconwindow, maxsize, minsize, overrideredirect, ",
+ "positionfrom, protocol, resizable, sizefrom, state, title, ",
+ "transient, or withdraw",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+
+ updateGeom:
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_SetGrid --
+ *
+ * This procedure 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 asscoiated 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 procedure call has no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
+ 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, heightInc; /* Pixel increments corresponding to a
+ * change of one grid unit. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ register WmInfo *wmPtr;
+
+ /*
+ * Find the top-level window for tkwin, plus the window manager
+ * information.
+ */
+
+ while (!(winPtr->flags & TK_TOP_LEVEL)) {
+ winPtr = winPtr->parentPtr;
+ if (winPtr == NULL) {
+ /*
+ * The window is being deleted... just skip this operation.
+ */
+
+ return;
+ }
+ }
+ wmPtr = winPtr->wmInfoPtr;
+
+ 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 procedure 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;
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_UnsetGrid --
+ *
+ * This procedure 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 procedure has no effect.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_UnsetGrid(tkwin)
+ 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_LEVEL)) {
+ winPtr = winPtr->parentPtr;
+ if (winPtr == NULL) {
+ /*
+ * The window is being deleted... just skip this operation.
+ */
+
+ return;
+ }
+ }
+ wmPtr = winPtr->wmInfoPtr;
+ 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;
+
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ConfigureEvent --
+ *
+ * This procedure is called to handle ConfigureNotify events on
+ * wrapper windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets updated in the WmInfo structure for the window
+ * and the toplevel itself gets repositioned within the wrapper.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ConfigureEvent(wmPtr, configEventPtr)
+ WmInfo *wmPtr; /* Information about toplevel window. */
+ XConfigureEvent *configEventPtr; /* Event that just occurred for
+ * wmPtr->wrapperPtr. */
+{
+ TkWindow *wrapperPtr = wmPtr->wrapperPtr;
+ TkWindow *winPtr = wmPtr->winPtr;
+
+ /*
+ * 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 (WM_SYNC_PENDING is set), then don't set wmPtr->width
+ * or wmPtr->height if they were previously -1 (otherwise the
+ * window will stop tracking geometry manager requests).
+ */
+
+ if (((wrapperPtr->changes.width != configEventPtr->width)
+ || (wrapperPtr->changes.height != configEventPtr->height))
+ && !(wmPtr->flags & WM_SYNC_PENDING)){
+ if (wmTracing) {
+ printf("TopLevelEventProc: user changed %s size to %dx%d\n",
+ winPtr->pathName, configEventPtr->width,
+ configEventPtr->height);
+ }
+ if ((wmPtr->width == -1)
+ && (configEventPtr->width == winPtr->reqWidth)) {
+ /*
+ * Don't set external width, since the user didn't change it
+ * from what the widgets asked for.
+ */
+ } else {
+ /*
+ * Note: if this window is embedded then don't set the external
+ * size, since it came from the containing application, not the
+ * user. In this case we want to keep sending our size requests
+ * to the containing application; if the user fixes the size
+ * of that application then it will still percolate down to us
+ * in the right way.
+ */
+
+ if (!(winPtr->flags & TK_EMBEDDED)) {
+ if (wmPtr->gridWin != NULL) {
+ wmPtr->width = wmPtr->reqGridWidth
+ + (configEventPtr->width
+ - winPtr->reqWidth)/wmPtr->widthInc;
+ if (wmPtr->width < 0) {
+ wmPtr->width = 0;
+ }
+ } else {
+ wmPtr->width = configEventPtr->width;
+ }
+ }
+ }
+ if ((wmPtr->height == -1)
+ && (configEventPtr->height ==
+ (winPtr->reqHeight + wmPtr->menuHeight))) {
+ /*
+ * Don't set external height, since the user didn't change it
+ * from what the widgets asked for.
+ */
+ } else {
+ /*
+ * See note for wmPtr->width about not setting external size
+ * for embedded windows.
+ */
+
+ if (!(winPtr->flags & TK_EMBEDDED)) {
+ if (wmPtr->gridWin != NULL) {
+ wmPtr->height = wmPtr->reqGridHeight
+ + (configEventPtr->height - wmPtr->menuHeight
+ - winPtr->reqHeight)/wmPtr->heightInc;
+ if (wmPtr->height < 0) {
+ wmPtr->height = 0;
+ }
+ } else {
+ wmPtr->height = configEventPtr->height - wmPtr->menuHeight;
+ }
+ }
+ }
+ wmPtr->configWidth = configEventPtr->width;
+ wmPtr->configHeight = configEventPtr->height;
+ }
+
+ if (wmTracing) {
+ printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d",
+ winPtr->pathName, configEventPtr->x, configEventPtr->y,
+ configEventPtr->width, configEventPtr->height);
+ printf(" send_event = %d, serial = %ld\n", configEventPtr->send_event,
+ configEventPtr->serial);
+ }
+ wrapperPtr->changes.width = configEventPtr->width;
+ wrapperPtr->changes.height = configEventPtr->height;
+ wrapperPtr->changes.border_width = configEventPtr->border_width;
+ wrapperPtr->changes.sibling = configEventPtr->above;
+ wrapperPtr->changes.stack_mode = Above;
+
+ /*
+ * Reparenting window managers make life difficult. If the
+ * window manager reparents a top-level window then the x and y
+ * information that comes in events for the window is wrong:
+ * it gives the location of the window inside its decorative
+ * parent, rather than the location of the window in root
+ * coordinates, which is what we want. Window managers
+ * are supposed to send synthetic events with the correct
+ * information, but ICCCM doesn't require them to do this
+ * under all conditions, and the information provided doesn't
+ * include everything we need here. So, the code below
+ * maintains a bunch of information about the parent window.
+ * If the window hasn't been reparented, we pretend that
+ * there is a parent shrink-wrapped around the window.
+ */
+
+ if ((wmPtr->reparent == None) || !ComputeReparentGeometry(wmPtr)) {
+ wmPtr->parentWidth = configEventPtr->width
+ + 2*configEventPtr->border_width;
+ wmPtr->parentHeight = configEventPtr->height
+ + 2*configEventPtr->border_width;
+ wrapperPtr->changes.x = wmPtr->x = configEventPtr->x;
+ wrapperPtr->changes.y = wmPtr->y = configEventPtr->y;
+ if (wmPtr->flags & WM_NEGATIVE_X) {
+ wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
+ }
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
+ }
+ }
+
+ /*
+ * Make sure that the toplevel and menubar are properly positioned within
+ * the wrapper.
+ */
+
+ XMoveResizeWindow(winPtr->display, winPtr->window, 0,
+ wmPtr->menuHeight, (unsigned) wrapperPtr->changes.width,
+ (unsigned) (wrapperPtr->changes.height - wmPtr->menuHeight));
+ if ((wmPtr->menubar != NULL)
+ && ((Tk_Width(wmPtr->menubar) != wrapperPtr->changes.width)
+ || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
+ Tk_MoveResizeWindow(wmPtr->menubar, 0, 0, wrapperPtr->changes.width,
+ wmPtr->menuHeight);
+ }
+
+ /*
+ * Update the coordinates in the toplevel (they should refer to the
+ * position in root window coordinates, not the coordinates of the
+ * wrapper window). Then synthesize a ConfigureNotify event to tell
+ * the application about the change.
+ */
+
+ winPtr->changes.x = wrapperPtr->changes.x;
+ winPtr->changes.y = wrapperPtr->changes.y + wmPtr->menuHeight;
+ winPtr->changes.width = wrapperPtr->changes.width;
+ winPtr->changes.height = wrapperPtr->changes.height - wmPtr->menuHeight;
+ TkDoConfigureNotify(winPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ReparentEvent --
+ *
+ * This procedure is called to handle ReparentNotify events on
+ * wrapper windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Information gets updated in the WmInfo structure for the window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+ReparentEvent(wmPtr, reparentEventPtr)
+ WmInfo *wmPtr; /* Information about toplevel window. */
+ XReparentEvent *reparentEventPtr; /* Event that just occurred for
+ * wmPtr->wrapperPtr. */
+{
+ TkWindow *wrapperPtr = wmPtr->wrapperPtr;
+ Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;
+ Atom actualType;
+ int actualFormat;
+ unsigned long numItems, bytesAfter;
+ unsigned int dummy;
+ Tk_ErrorHandler handler;
+
+ /*
+ * Identify the root window for wrapperPtr. This is tricky because of
+ * virtual root window managers like tvtwm. If the window has a
+ * property named __SWM_ROOT or __WM_ROOT then this property gives
+ * the id for a virtual root window that should be used instead of
+ * the root window of the screen.
+ */
+
+ vRoot = RootWindow(wrapperPtr->display, wrapperPtr->screenNum);
+ wmPtr->vRoot = None;
+ handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ if (((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
+ Tk_InternAtom((Tk_Window) wrapperPtr, "__WM_ROOT"), 0, (long) 1,
+ False, XA_WINDOW, &actualType, &actualFormat, &numItems,
+ &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
+ && (actualType == XA_WINDOW))
+ || ((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
+ Tk_InternAtom((Tk_Window) wrapperPtr, "__SWM_ROOT"), 0, (long) 1,
+ False, XA_WINDOW, &actualType, &actualFormat, &numItems,
+ &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
+ && (actualType == XA_WINDOW))) {
+ if ((actualFormat == 32) && (numItems == 1)) {
+ vRoot = wmPtr->vRoot = *virtualRootPtr;
+ } else if (wmTracing) {
+ printf("%s format %d numItems %ld\n",
+ "ReparentEvent got bogus VROOT property:", actualFormat,
+ numItems);
+ }
+ XFree((char *) virtualRootPtr);
+ }
+ Tk_DeleteErrorHandler(handler);
+
+ if (wmTracing) {
+ printf("ReparentEvent: %s reparented to 0x%x, vRoot = 0x%x\n",
+ wmPtr->winPtr->pathName,
+ (unsigned int) reparentEventPtr->parent, (unsigned int) vRoot);
+ }
+
+ /*
+ * Fetch correct geometry information for the new virtual root.
+ */
+
+ UpdateVRootGeometry(wmPtr);
+
+ /*
+ * If the window's new parent is the root window, then mark it as
+ * no longer reparented.
+ */
+
+ if (reparentEventPtr->parent == vRoot) {
+ noReparent:
+ wmPtr->reparent = None;
+ wmPtr->parentWidth = wrapperPtr->changes.width;
+ wmPtr->parentHeight = wrapperPtr->changes.height;
+ wmPtr->xInParent = wmPtr->yInParent = 0;
+ wrapperPtr->changes.x = reparentEventPtr->x;
+ wrapperPtr->changes.y = reparentEventPtr->y;
+ return;
+ }
+
+ /*
+ * Search up the window hierarchy to find the ancestor of this
+ * window that is just below the (virtual) root. This is tricky
+ * because it's possible that things have changed since the event
+ * was generated so that the ancestry indicated by the event no
+ * longer exists. If this happens then an error will occur and
+ * we just discard the event (there will be a more up-to-date
+ * ReparentNotify event coming later).
+ */
+
+ handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ wmPtr->reparent = reparentEventPtr->parent;
+ while (1) {
+ if (XQueryTree(wrapperPtr->display, wmPtr->reparent, &dummy2, &ancestor,
+ &children, &dummy) == 0) {
+ Tk_DeleteErrorHandler(handler);
+ goto noReparent;
+ }
+ XFree((char *) children);
+ if ((ancestor == vRoot) ||
+ (ancestor == RootWindow(wrapperPtr->display,
+ wrapperPtr->screenNum))) {
+ break;
+ }
+ wmPtr->reparent = ancestor;
+ }
+ Tk_DeleteErrorHandler(handler);
+
+ if (!ComputeReparentGeometry(wmPtr)) {
+ goto noReparent;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * ComputeReparentGeometry --
+ *
+ * This procedure is invoked to recompute geometry information
+ * related to a reparented top-level window, such as the position
+ * and total size of the parent and the position within it of
+ * the top-level window.
+ *
+ * Results:
+ * The return value is 1 if everything completed successfully
+ * and 0 if an error occurred while querying information about
+ * winPtr's parents. In this case winPtr is marked as no longer
+ * being reparented.
+ *
+ * Side effects:
+ * Geometry information in wmPtr, wmPtr->winPtr, and
+ * wmPtr->wrapperPtr gets updated.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+ComputeReparentGeometry(wmPtr)
+ WmInfo *wmPtr; /* Information about toplevel window
+ * whose reparent info is to be recomputed. */
+{
+ TkWindow *wrapperPtr = wmPtr->wrapperPtr;
+ int width, height, bd;
+ unsigned int dummy;
+ int xOffset, yOffset, x, y;
+ Window dummy2;
+ Status status;
+ Tk_ErrorHandler handler;
+
+ handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ (void) XTranslateCoordinates(wrapperPtr->display, wrapperPtr->window,
+ wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
+ status = XGetGeometry(wrapperPtr->display, wmPtr->reparent,
+ &dummy2, &x, &y, (unsigned int *) &width,
+ (unsigned int *) &height, (unsigned int *) &bd, &dummy);
+ Tk_DeleteErrorHandler(handler);
+ if (status == 0) {
+ /*
+ * It appears that the reparented parent went away and
+ * no-one told us. Reset the window to indicate that
+ * it's not reparented.
+ */
+ wmPtr->reparent = None;
+ wmPtr->xInParent = wmPtr->yInParent = 0;
+ return 0;
+ }
+ wmPtr->xInParent = xOffset + bd;
+ wmPtr->yInParent = yOffset + bd;
+ wmPtr->parentWidth = width + 2*bd;
+ wmPtr->parentHeight = height + 2*bd;
+
+ /*
+ * Some tricky issues in updating wmPtr->x and wmPtr->y:
+ *
+ * 1. Don't update them if the event occurred because of something
+ * we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).
+ * This is because window managers treat coords differently than Tk,
+ * and no two window managers are alike. If the window manager moved
+ * the window because we told it to, remember the coordinates we told
+ * it, not the ones it actually moved it to. This allows us to move
+ * the window back to the same coordinates later and get the same
+ * result. Without this check, windows can "walk" across the screen
+ * under some conditions.
+ *
+ * 2. Don't update wmPtr->x and wmPtr->y unless wrapperPtr->changes.x
+ * or wrapperPtr->changes.y has changed (otherwise a size change can
+ * spoof us into thinking that the position changed too and defeat
+ * the intent of (1) above.
+ *
+ * (As of 9/96 the above 2 comments appear to be stale. They're
+ * being left in place as a reminder of what was once true (and
+ * perhaps should still be true?)).
+ *
+ * 3. Ignore size changes coming from the window system if we're
+ * about to change the size ourselves but haven't seen the event for
+ * it yet: our size change is supposed to take priority.
+ */
+
+ if (!(wmPtr->flags & WM_MOVE_PENDING)
+ && ((wmPtr->wrapperPtr->changes.x != (x + wmPtr->xInParent))
+ || (wmPtr->wrapperPtr->changes.y != (y + wmPtr->yInParent)))) {
+ wmPtr->x = x;
+ if (wmPtr->flags & WM_NEGATIVE_X) {
+ wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
+ }
+ wmPtr->y = y;
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
+ }
+ }
+
+ wmPtr->wrapperPtr->changes.x = x + wmPtr->xInParent;
+ wmPtr->wrapperPtr->changes.y = y + wmPtr->yInParent;
+ if (wmTracing) {
+ printf("wrapperPtr coords %d,%d, wmPtr coords %d,%d, offsets %d %d\n",
+ wrapperPtr->changes.x, wrapperPtr->changes.y,
+ wmPtr->x, wmPtr->y, wmPtr->xInParent, wmPtr->yInParent);
+ }
+ return 1;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WrapperEventProc --
+ *
+ * This procedure is invoked by the event loop when a wrapper window
+ * is restructured.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Tk's internal data structures for the window get modified to
+ * reflect the structural change.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+WrapperEventProc(clientData, eventPtr)
+ ClientData clientData; /* Information about toplevel window. */
+ XEvent *eventPtr; /* Event that just happened. */
+{
+ WmInfo *wmPtr = (WmInfo *) clientData;
+ XEvent mapEvent;
+
+ wmPtr->flags |= WM_VROOT_OFFSET_STALE;
+ if (eventPtr->type == DestroyNotify) {
+ Tk_ErrorHandler handler;
+
+ if (!(wmPtr->wrapperPtr->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(wmPtr->winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ Tk_DestroyWindow((Tk_Window) wmPtr->winPtr);
+ Tk_DeleteErrorHandler(handler);
+ }
+ if (wmTracing) {
+ printf("TopLevelEventProc: %s deleted\n", wmPtr->winPtr->pathName);
+ }
+ } else if (eventPtr->type == ConfigureNotify) {
+ /*
+ * Ignore the event if the window has never been mapped yet.
+ * Such an event occurs only in weird cases like changing the
+ * internal border width of a top-level window, which results
+ * in a synthetic Configure event. These events are not relevant
+ * to us, and if we process them confusion may result (e.g. we
+ * may conclude erroneously that the user repositioned or resized
+ * the window).
+ */
+
+ if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
+ ConfigureEvent(wmPtr, &eventPtr->xconfigure);
+ }
+ } else if (eventPtr->type == MapNotify) {
+ wmPtr->wrapperPtr->flags |= TK_MAPPED;
+ wmPtr->winPtr->flags |= TK_MAPPED;
+ XMapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
+ goto doMapEvent;
+ } else if (eventPtr->type == UnmapNotify) {
+ wmPtr->wrapperPtr->flags &= ~TK_MAPPED;
+ wmPtr->winPtr->flags &= ~TK_MAPPED;
+ XUnmapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
+ goto doMapEvent;
+ } else if (eventPtr->type == ReparentNotify) {
+ ReparentEvent(wmPtr, &eventPtr->xreparent);
+ }
+ return;
+
+ doMapEvent:
+ mapEvent = *eventPtr;
+ mapEvent.xmap.event = wmPtr->winPtr->window;
+ mapEvent.xmap.window = wmPtr->winPtr->window;
+ Tk_HandleEvent(&mapEvent);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TopLevelReqProc --
+ *
+ * This procedure 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(dummy, tkwin)
+ ClientData dummy; /* Not used. */
+ Tk_Window tkwin; /* Information about window. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ WmInfo *wmPtr;
+
+ wmPtr = winPtr->wmInfoPtr;
+
+ if ((wmPtr->width >= 0) && (wmPtr->height >= 0)) {
+ /*
+ * Explicit dimensions have been set for this window, so we
+ * should ignore the geometry request. It's actually important
+ * to ignore the geometry request because, due to quirks in
+ * window managers, invoking UpdateGeometryInfo may cause the
+ * window to move. For example, if "wm geometry -10-20" was
+ * invoked, the window may be positioned incorrectly the first
+ * time it appears (because we didn't know the proper width of
+ * the window manager borders); if we invoke UpdateGeometryInfo
+ * again, the window will be positioned correctly, which may
+ * cause it to jump on the screen.
+ */
+
+ return;
+ }
+
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+
+ /*
+ * If the window isn't being positioned by its upper left corner
+ * then we have to move it as well.
+ */
+
+ if (wmPtr->flags & (WM_NEGATIVE_X | WM_NEGATIVE_Y)) {
+ wmPtr->flags |= WM_MOVE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateGeometryInfo --
+ *
+ * This procedure is invoked when a top-level window is first
+ * mapped, and also as a when-idle procedure, 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 procedure doesn't return until the window manager has
+ * responded to the geometry change.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size and location of both the toplevel window and its wrapper
+ * may change, unless the WM prevents that from happening.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateGeometryInfo(clientData)
+ ClientData clientData; /* Pointer to the window's record. */
+{
+ register TkWindow *winPtr = (TkWindow *) clientData;
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ int x, y, width, height;
+ unsigned long serial;
+
+ wmPtr->flags &= ~WM_UPDATE_PENDING;
+
+ /*
+ * 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), and (c) whether or not the window is
+ * gridded. 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;
+ }
+ 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;
+ }
+
+ /*
+ * 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 = wmPtr->vRootWidth - wmPtr->x
+ - (width + (wmPtr->parentWidth - winPtr->changes.width));
+ } else {
+ x = wmPtr->x;
+ }
+ if (wmPtr->flags & WM_NEGATIVE_Y) {
+ y = wmPtr->vRootHeight - wmPtr->y
+ - (height + (wmPtr->parentHeight - winPtr->changes.height));
+ } else {
+ y = wmPtr->y;
+ }
+
+ /*
+ * If the window's size is going to change and the window is
+ * supposed to not be resizable by the user, then we have to
+ * update the size hints. There may also be a size-hint-update
+ * request pending from somewhere else, too.
+ */
+
+ if (((width != winPtr->changes.width)
+ || (height != winPtr->changes.height))
+ && (wmPtr->gridWin == NULL)
+ && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ }
+ if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
+ UpdateSizeHints(winPtr);
+ }
+
+ /*
+ * Reconfigure the wrapper if it isn't already configured correctly.
+ * A few tricky points:
+ *
+ * 1. If the window is embeddedand the container is also in this
+ * process, don't actually reconfigure the window; just pass the
+ * desired size on to the container. Also, zero out any position
+ * information, since embedded windows are not allowed to move.
+ * 2. Sometimes the window manager will give us a different size
+ * than we asked for (e.g. mwm has a minimum size for windows), so
+ * base the size check on what we *asked for* last time, not what we
+ * got.
+ * 3. Can't just reconfigure always, because we may not get a
+ * ConfigureNotify event back if nothing changed, so
+ * WaitForConfigureNotify will hang a long time.
+ * 4. Don't move window unless a new position has been requested for
+ * it. This is because of "features" in some window managers (e.g.
+ * twm, as of 4/24/91) where they don't interpret coordinates
+ * according to ICCCM. Moving a window to its current location may
+ * cause it to shift position on the screen.
+ */
+
+ if ((winPtr->flags & (TK_EMBEDDED|TK_BOTH_HALVES))
+ == (TK_EMBEDDED|TK_BOTH_HALVES)) {
+ /*
+ * This window is embedded and the container is also in this
+ * process, so we don't need to do anything special about the
+ * geometry, except to make sure that the desired size is known
+ * by the container. Also, zero out any position information,
+ * since embedded windows are not allowed to move.
+ */
+
+ wmPtr->x = wmPtr->y = 0;
+ wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
+ height += wmPtr->menuHeight;
+ Tk_GeometryRequest((Tk_Window) TkpGetOtherWindow(winPtr),
+ width, height);
+ return;
+ }
+ serial = NextRequest(winPtr->display);
+ height += wmPtr->menuHeight;
+ if (wmPtr->flags & WM_MOVE_PENDING) {
+ if ((x == winPtr->changes.x) && (y == winPtr->changes.y)
+ && (width == wmPtr->wrapperPtr->changes.width)
+ && (height == wmPtr->wrapperPtr->changes.height)) {
+ /*
+ * The window already has the correct geometry, so don't bother
+ * to configure it; the X server appears to ignore these
+ * requests, so we won't get back a ConfigureNotify and the
+ * WaitForConfigureNotify call below will hang for a while.
+ */
+
+ wmPtr->flags &= ~WM_MOVE_PENDING;
+ return;
+ }
+ wmPtr->configWidth = width;
+ wmPtr->configHeight = height;
+ if (wmTracing) {
+ printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
+ x, y, width, height);
+ }
+ XMoveResizeWindow(winPtr->display, wmPtr->wrapperPtr->window, x, y,
+ (unsigned) width, (unsigned) height);
+ } else if ((width != wmPtr->configWidth)
+ || (height != wmPtr->configHeight)) {
+ if ((width == wmPtr->wrapperPtr->changes.width)
+ && (height == wmPtr->wrapperPtr->changes.height)) {
+ /*
+ * The window is already just the size we want, so don't bother
+ * to configure it; the X server appears to ignore these
+ * requests, so we won't get back a ConfigureNotify and the
+ * WaitForConfigureNotify call below will hang for a while.
+ */
+
+ return;
+ }
+ wmPtr->configWidth = width;
+ wmPtr->configHeight = height;
+ if (wmTracing) {
+ printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);
+ }
+ XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
+ (unsigned) width, (unsigned) height);
+ } else if ((wmPtr->menubar != NULL)
+ && ((Tk_Width(wmPtr->menubar) != wmPtr->wrapperPtr->changes.width)
+ || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
+ /*
+ * It is possible that the window's overall size has not changed
+ * but the menu size has.
+ */
+
+ Tk_MoveResizeWindow(wmPtr->menubar, 0, 0,
+ wmPtr->wrapperPtr->changes.width, wmPtr->menuHeight);
+ XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
+ (unsigned) width, (unsigned) height);
+ } else {
+ return;
+ }
+
+ /*
+ * Wait for the configure operation to complete. Don't need to do
+ * this, however, if the window is about to be mapped: it will be
+ * taken care of elsewhere.
+ */
+
+ if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {
+ WaitForConfigureNotify(winPtr, serial);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * UpdateSizeHints --
+ *
+ * This procedure is called to update the window manager's
+ * size hints information from the information in a WmInfo
+ * structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Properties get changed for winPtr.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+UpdateSizeHints(winPtr)
+ TkWindow *winPtr;
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+ XSizeHints *hintsPtr;
+ int maxWidth, maxHeight;
+
+ wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
+
+ hintsPtr = XAllocSizeHints();
+ if (hintsPtr == NULL) {
+ return;
+ }
+
+ /*
+ * Compute the pixel-based sizes for the various fields in the
+ * size hints structure, based on the grid-based sizes in
+ * our structure.
+ */
+
+ GetMaxSize(wmPtr, &maxWidth, &maxHeight);
+ if (wmPtr->gridWin != NULL) {
+ hintsPtr->base_width = winPtr->reqWidth
+ - (wmPtr->reqGridWidth * wmPtr->widthInc);
+ if (hintsPtr->base_width < 0) {
+ hintsPtr->base_width = 0;
+ }
+ hintsPtr->base_height = winPtr->reqHeight + wmPtr->menuHeight
+ - (wmPtr->reqGridHeight * wmPtr->heightInc);
+ if (hintsPtr->base_height < 0) {
+ hintsPtr->base_height = 0;
+ }
+ hintsPtr->min_width = hintsPtr->base_width
+ + (wmPtr->minWidth * wmPtr->widthInc);
+ hintsPtr->min_height = hintsPtr->base_height
+ + (wmPtr->minHeight * wmPtr->heightInc);
+ hintsPtr->max_width = hintsPtr->base_width
+ + (maxWidth * wmPtr->widthInc);
+ hintsPtr->max_height = hintsPtr->base_height
+ + (maxHeight * wmPtr->heightInc);
+ } else {
+ hintsPtr->min_width = wmPtr->minWidth;
+ hintsPtr->min_height = wmPtr->minHeight;
+ hintsPtr->max_width = maxWidth;
+ hintsPtr->max_height = maxHeight;
+ hintsPtr->base_width = 0;
+ hintsPtr->base_height = 0;
+ }
+ hintsPtr->width_inc = wmPtr->widthInc;
+ hintsPtr->height_inc = wmPtr->heightInc;
+ hintsPtr->min_aspect.x = wmPtr->minAspect.x;
+ hintsPtr->min_aspect.y = wmPtr->minAspect.y;
+ hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
+ hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
+ hintsPtr->win_gravity = wmPtr->gravity;
+ hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize | PMaxSize;
+
+ /*
+ * If the window isn't supposed to be resizable, then set the
+ * minimum and maximum dimensions to be the same.
+ */
+
+ if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
+ if (wmPtr->width >= 0) {
+ hintsPtr->min_width = wmPtr->width;
+ } else {
+ hintsPtr->min_width = winPtr->reqWidth;
+ }
+ hintsPtr->max_width = hintsPtr->min_width;
+ }
+ if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
+ if (wmPtr->height >= 0) {
+ hintsPtr->min_height = wmPtr->height;
+ } else {
+ hintsPtr->min_height = winPtr->reqHeight + wmPtr->menuHeight;
+ }
+ hintsPtr->max_height = hintsPtr->min_height;
+ }
+
+ XSetWMNormalHints(winPtr->display, wmPtr->wrapperPtr->window, hintsPtr);
+
+ XFree((char *) hintsPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitForConfigureNotify --
+ *
+ * This procedure is invoked in order to synchronize with the
+ * window manager. It waits for a ConfigureNotify event to
+ * arrive, signalling that the window manager has seen an attempt
+ * on our part to move or resize a top-level window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Delays the execution of the process until a ConfigureNotify event
+ * arrives with serial number at least as great as serial. This
+ * is useful for two reasons:
+ *
+ * 1. It's important to distinguish ConfigureNotify events that are
+ * coming in response to a request we've made from those generated
+ * spontaneously by the user. The reason for this is that if the
+ * user resizes the window we take that as an order to ignore
+ * geometry requests coming from inside the window hierarchy. If
+ * we accidentally interpret a response to our request as a
+ * user-initiated action, the window will stop responding to
+ * new geometry requests. To make this distinction, (a) this
+ * procedure sets a flag for TopLevelEventProc to indicate that
+ * we're waiting to sync with the wm, and (b) all changes to
+ * the size of a top-level window are followed by calls to this
+ * procedure.
+ * 2. Races and confusion can come about if there are multiple
+ * operations outstanding at a time (e.g. two different resizes
+ * of the top-level window: it's hard to tell which of the
+ * ConfigureNotify events coming back is for which request).
+ * While waiting, all events covered by StructureNotifyMask are
+ * processed and all others are deferred.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+WaitForConfigureNotify(winPtr, serial)
+ TkWindow *winPtr; /* Top-level window for which we want
+ * to see a ConfigureNotify. */
+ unsigned long serial; /* Serial number of resize request. Want to
+ * be sure wm has seen this. */
+{
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+ XEvent event;
+ int diff, code;
+ int gotConfig = 0;
+
+ /*
+ * One more tricky detail about this procedure. In some cases the
+ * window manager will decide to ignore a configure request (e.g.
+ * because it thinks the window is already in the right place).
+ * To avoid hanging in this situation, only wait for a few seconds,
+ * then give up.
+ */
+
+ while (!gotConfig) {
+ wmPtr->flags |= WM_SYNC_PENDING;
+ code = WaitForEvent(winPtr->display, wmPtr->wrapperPtr->window,
+ ConfigureNotify, &event);
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+ if (code != TCL_OK) {
+ if (wmTracing) {
+ printf("WaitForConfigureNotify giving up on %s\n",
+ winPtr->pathName);
+ }
+ break;
+ }
+ diff = event.xconfigure.serial - serial;
+ if (diff >= 0) {
+ gotConfig = 1;
+ }
+ }
+ wmPtr->flags &= ~WM_MOVE_PENDING;
+ if (wmTracing) {
+ printf("WaitForConfigureNotify finished with %s, serial %ld\n",
+ winPtr->pathName, serial);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitForEvent --
+ *
+ * This procedure is used by WaitForConfigureNotify and
+ * WaitForMapNotify to wait for an event of a certain type
+ * to arrive.
+ *
+ * Results:
+ * Under normal conditions, TCL_OK is returned and an event for
+ * display and window that matches "mask" is stored in *eventPtr.
+ * This event has already been processed by Tk before this procedure
+ * returns. If a long time goes by with no event of the right type
+ * arriving, or if an error occurs while waiting for the event to
+ * arrive, then TCL_ERROR is returned.
+ *
+ * Side effects:
+ * While waiting for the desired event to occur, Configurenotify
+ * events for window are processed, as are all ReparentNotify events,
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+WaitForEvent(display, window, type, eventPtr)
+ Display *display; /* Display event is coming from. */
+ Window window; /* Window for which event is desired. */
+ int type; /* Type of event that is wanted. */
+ XEvent *eventPtr; /* Place to store event. */
+{
+ WaitRestrictInfo info;
+ Tk_RestrictProc *oldRestrictProc;
+ ClientData oldRestrictData;
+ Tcl_Time timeout;
+
+ /*
+ * Set up an event filter to select just the events we want, and
+ * a timer handler, then wait for events until we get the event
+ * we want or a timeout happens.
+ */
+
+ info.display = display;
+ info.window = window;
+ info.type = type;
+ info.eventPtr = eventPtr;
+ info.foundEvent = 0;
+ oldRestrictProc = Tk_RestrictEvents(WaitRestrictProc, (ClientData) &info,
+ &oldRestrictData);
+
+ TclpGetTime(&timeout);
+ timeout.sec += 2;
+
+ while (!info.foundEvent) {
+ if (!TkUnixDoOneXEvent(&timeout)) {
+ break;
+ }
+ }
+ (void) Tk_RestrictEvents(oldRestrictProc, oldRestrictData,
+ &oldRestrictData);
+ if (info.foundEvent) {
+ return TCL_OK;
+ }
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitRestrictProc --
+ *
+ * This procedure is a Tk_RestrictProc that is used to filter
+ * events while WaitForEvent is active.
+ *
+ * Results:
+ * Returns TK_PROCESS_EVENT if the right event is found. Also
+ * returns TK_PROCESS_EVENT if any ReparentNotify event is found
+ * for window or if the event is a ConfigureNotify for window.
+ * Otherwise returns TK_DEFER_EVENT.
+ *
+ * Side effects:
+ * An event may get stored in the area indicated by the caller
+ * of WaitForEvent.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tk_RestrictAction
+WaitRestrictProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to WaitRestrictInfo structure. */
+ XEvent *eventPtr; /* Event that is about to be handled. */
+{
+ WaitRestrictInfo *infoPtr = (WaitRestrictInfo *) clientData;
+
+ if (eventPtr->type == ReparentNotify) {
+ return TK_PROCESS_EVENT;
+ }
+ if ((eventPtr->xany.window != infoPtr->window)
+ || (eventPtr->xany.display != infoPtr->display)) {
+ return TK_DEFER_EVENT;
+ }
+ if (eventPtr->type == infoPtr->type) {
+ *infoPtr->eventPtr = *eventPtr;
+ infoPtr->foundEvent = 1;
+ return TK_PROCESS_EVENT;
+ }
+ if (eventPtr->type == ConfigureNotify) {
+ return TK_PROCESS_EVENT;
+ }
+ return TK_DEFER_EVENT;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WaitForMapNotify --
+ *
+ * This procedure is invoked in order to synchronize with the
+ * window manager. It waits for the window's mapped state to
+ * reach the value given by mapped.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Delays the execution of the process until winPtr becomes mapped
+ * or unmapped, depending on the "mapped" argument. This allows us
+ * to synchronize with the window manager, and allows us to
+ * identify changes in window size that come about when the window
+ * manager first starts managing the window (as opposed to those
+ * requested interactively by the user later). See the comments
+ * for WaitForConfigureNotify and WM_SYNC_PENDING. While waiting,
+ * all events covered by StructureNotifyMask are processed and all
+ * others are deferred.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+WaitForMapNotify(winPtr, mapped)
+ TkWindow *winPtr; /* Top-level window for which we want
+ * to see a particular mapping state. */
+ int mapped; /* If non-zero, wait for window to become
+ * mapped, otherwise wait for it to become
+ * unmapped. */
+{
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+ XEvent event;
+ int code;
+
+ while (1) {
+ if (mapped) {
+ if (winPtr->flags & TK_MAPPED) {
+ break;
+ }
+ } else if (!(winPtr->flags & TK_MAPPED)) {
+ break;
+ }
+ wmPtr->flags |= WM_SYNC_PENDING;
+ code = WaitForEvent(winPtr->display, wmPtr->wrapperPtr->window,
+ mapped ? MapNotify : UnmapNotify, &event);
+ wmPtr->flags &= ~WM_SYNC_PENDING;
+ if (code != TCL_OK) {
+ /*
+ * There are some bizarre situations in which the window
+ * manager can't respond or chooses not to (e.g. if we've
+ * got a grab set it can't respond). If this happens then
+ * just quit.
+ */
+
+ if (wmTracing) {
+ printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);
+ }
+ break;
+ }
+ }
+ wmPtr->flags &= ~WM_MOVE_PENDING;
+ if (wmTracing) {
+ printf("WaitForMapNotify finished with %s\n", winPtr->pathName);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * UpdateHints --
+ *
+ * This procedure is called to update the window manager's
+ * hints information from the information in a WmInfo
+ * structure.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Properties get changed for winPtr.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+UpdateHints(winPtr)
+ TkWindow *winPtr;
+{
+ WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ if (wmPtr->flags & WM_NEVER_MAPPED) {
+ return;
+ }
+ XSetWMHints(winPtr->display, wmPtr->wrapperPtr->window, &wmPtr->hints);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * ParseGeometry --
+ *
+ * This procedure 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
+ * interp->result if an error occurs.
+ *
+ * Side effects:
+ * The size and/or location of winPtr may change.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+ParseGeometry(interp, string, winPtr)
+ Tcl_Interp *interp; /* Used for error reporting. */
+ 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 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)) == 0) {
+ wmPtr->sizeHintsFlags |= USPosition;
+ flags |= WM_UPDATE_SIZE_HINTS;
+ }
+ }
+
+ /*
+ * 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, (ClientData) winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+ return TCL_OK;
+
+ error:
+ Tcl_AppendResult(interp, "bad geometry specifier \"",
+ string, "\"", (char *) NULL);
+ return TCL_ERROR;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetRootCoords --
+ *
+ * Given a token for a window, this procedure 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. If a virtual
+ * root window is in effect for the window, then the coordinates
+ * in the virtual root are returned.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_GetRootCoords(tkwin, xPtr, yPtr)
+ 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). */
+{
+ int x, y;
+ register TkWindow *winPtr = (TkWindow *) tkwin;
+
+ /*
+ * Search back through this window's parents all the way to a
+ * top-level window, combining the offsets of each window within
+ * its parent.
+ */
+
+ x = y = 0;
+ while (1) {
+ x += winPtr->changes.x + winPtr->changes.border_width;
+ y += winPtr->changes.y + winPtr->changes.border_width;
+ if ((winPtr->wmInfoPtr != NULL)
+ && (winPtr->wmInfoPtr->menubar == (Tk_Window) winPtr)) {
+ /*
+ * This window is a special menubar; switch over to its
+ * associated toplevel, compensate for their differences in
+ * y coordinates, then continue with the toplevel (in case
+ * it's embedded).
+ */
+
+ y -= winPtr->wmInfoPtr->menuHeight;
+ winPtr = winPtr->wmInfoPtr->winPtr;
+ continue;
+ }
+ if (winPtr->flags & TK_TOP_LEVEL) {
+ TkWindow *otherPtr;
+
+ if (!(winPtr->flags & TK_EMBEDDED)) {
+ break;
+ }
+ otherPtr = TkpGetOtherWindow(winPtr);
+ if (otherPtr == NULL) {
+ /*
+ * The container window is not in the same application.
+ * Query the X server.
+ */
+
+ Window root, dummyChild;
+ int rootX, rootY;
+
+ root = winPtr->wmInfoPtr->vRoot;
+ if (root == None) {
+ root = RootWindowOfScreen(Tk_Screen((Tk_Window)winPtr));
+ }
+ XTranslateCoordinates(winPtr->display, winPtr->window,
+ root, 0, 0, &rootX, &rootY, &dummyChild);
+ x += rootX;
+ y += rootY;
+ break;
+ } else {
+ /*
+ * The container window is in the same application.
+ * Let's query its coordinates.
+ */
+
+ winPtr = otherPtr;
+ continue;
+ }
+ }
+ winPtr = winPtr->parentPtr;
+ if (winPtr == NULL) {
+ break;
+ }
+ }
+ *xPtr = x;
+ *yPtr = y;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_CoordsToWindow --
+ *
+ * Given the (virtual) root coordinates of a point, this procedure
+ * 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(rootX, rootY, tkwin)
+ int rootX, 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. */
+{
+ Window window, parent, child;
+ int x, y, childX, childY, tmpx, tmpy, bd;
+ WmInfo *wmPtr;
+ TkWindow *winPtr, *childPtr, *nextPtr;
+
+ /*
+ * Step 1: scan the list of toplevel windows to see if there is a
+ * virtual root for the screen we're interested in. If so, we have
+ * to translate the coordinates from virtual root to root
+ * coordinates.
+ */
+
+ parent = window = RootWindowOfScreen(Tk_Screen(tkwin));
+ x = rootX;
+ y = rootY;
+ for (wmPtr = firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
+ if (Tk_Screen(wmPtr->winPtr) != Tk_Screen(tkwin)) {
+ continue;
+ }
+ if (wmPtr->vRoot == None) {
+ continue;
+ }
+ UpdateVRootGeometry(wmPtr);
+ parent = wmPtr->vRoot;
+ break;
+ }
+
+ /*
+ * Step 2: work down through the window hierarchy starting at the
+ * root. For each window, find the child that contains the given
+ * point and then see if this child is either a wrapper for one of
+ * our toplevel windows or a window manager decoration window for
+ * one of our toplevels. This approach handles several tricky
+ * cases:
+ *
+ * 1. There may be a virtual root window between the root and one of
+ * our toplevels.
+ * 2. If a toplevel is embedded, we may have to search through the
+ * windows of the container application(s) before getting to
+ * the toplevel.
+ */
+
+ while (1) {
+ if (XTranslateCoordinates(Tk_Display(tkwin), parent, window,
+ x, y, &childX, &childY, &child) == False) {
+ panic("Tk_CoordsToWindow got False return from XTranslateCoordinates");
+ }
+ if (child == None) {
+ return NULL;
+ }
+ for (wmPtr = firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
+ if (wmPtr->reparent == child) {
+ goto gotToplevel;
+ }
+ if (wmPtr->wrapperPtr != NULL) {
+ if (child == wmPtr->wrapperPtr->window) {
+ goto gotToplevel;
+ }
+ } else if (child == wmPtr->winPtr->window) {
+ goto gotToplevel;
+ }
+ }
+ x = childX;
+ y = childY;
+ parent = window;
+ window = child;
+ }
+
+ gotToplevel:
+ winPtr = wmPtr->winPtr;
+ if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
+ return NULL;
+ }
+
+ /*
+ * Step 3: at this point winPtr and wmPtr refer to the toplevel that
+ * contains the given coordinates, and childX and childY give the
+ * translated coordinates in the *parent* of the toplevel. Now
+ * decide whether the coordinates are in the menubar or the actual
+ * toplevel, and translate the coordinates into the coordinate
+ * system of that window.
+ */
+
+ x = childX - winPtr->changes.x;
+ y = childY - winPtr->changes.y;
+ if ((x < 0) || (x >= winPtr->changes.width)
+ || (y >= winPtr->changes.height)) {
+ return NULL;
+ }
+ if (y < 0) {
+ winPtr = (TkWindow *) wmPtr->menubar;
+ if (winPtr == NULL) {
+ return NULL;
+ }
+ y += wmPtr->menuHeight;
+ if (y < 0) {
+ return NULL;
+ }
+ }
+
+ /*
+ * Step 4: work down through the hierarchy underneath the current
+ * window. At each level, scan through all the children to find the
+ * highest one in the stacking order that contains the point. Then
+ * repeat the whole process on that child.
+ */
+
+ while (1) {
+ nextPtr = NULL;
+ for (childPtr = winPtr->childList; childPtr != NULL;
+ childPtr = childPtr->nextPtr) {
+ if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
+ continue;
+ }
+ if (childPtr->flags & TK_REPARENTED) {
+ continue;
+ }
+ tmpx = x - childPtr->changes.x;
+ tmpy = y - childPtr->changes.y;
+ bd = childPtr->changes.border_width;
+ if ((tmpx >= -bd) && (tmpy >= -bd)
+ && (tmpx < (childPtr->changes.width + bd))
+ && (tmpy < (childPtr->changes.height + bd))) {
+ nextPtr = childPtr;
+ }
+ }
+ if (nextPtr == NULL) {
+ break;
+ }
+ winPtr = nextPtr;
+ x -= winPtr->changes.x;
+ y -= winPtr->changes.y;
+ if ((winPtr->flags & TK_CONTAINER)
+ && (winPtr->flags & TK_BOTH_HALVES)) {
+ /*
+ * The window containing the point is a container, and the
+ * embedded application is in this same process. Switch
+ * over to the toplevel for the embedded application and
+ * start processing that toplevel from scratch.
+ */
+
+ winPtr = TkpGetOtherWindow(winPtr);
+ wmPtr = winPtr->wmInfoPtr;
+ childX = x;
+ childY = y;
+ goto gotToplevel;
+ }
+ }
+ return (Tk_Window) winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateVRootGeometry --
+ *
+ * This procedure is called to update all the virtual root
+ * geometry information in wmPtr.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The vRootX, vRootY, vRootWidth, and vRootHeight fields in
+ * wmPtr are filled with the most up-to-date information.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateVRootGeometry(wmPtr)
+ WmInfo *wmPtr; /* Window manager information to be
+ * updated. The wmPtr->vRoot field must
+ * be valid. */
+{
+ TkWindow *winPtr = wmPtr->winPtr;
+ int bd;
+ unsigned int dummy;
+ Window dummy2;
+ Status status;
+ Tk_ErrorHandler handler;
+
+ /*
+ * If this isn't a virtual-root window manager, just return information
+ * about the screen.
+ */
+
+ wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
+ if (wmPtr->vRoot == None) {
+ noVRoot:
+ wmPtr->vRootX = wmPtr->vRootY = 0;
+ wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
+ wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
+ return;
+ }
+
+ /*
+ * Refresh the virtual root information if it's out of date.
+ */
+
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ status = XGetGeometry(winPtr->display, wmPtr->vRoot,
+ &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
+ (unsigned int *) &wmPtr->vRootWidth,
+ (unsigned int *) &wmPtr->vRootHeight, (unsigned int *) &bd,
+ &dummy);
+ if (wmTracing) {
+ printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
+ wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
+ printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
+ }
+ Tk_DeleteErrorHandler(handler);
+ if (status == 0) {
+ /*
+ * The virtual root is gone! Pretend that it never existed.
+ */
+
+ wmPtr->vRoot = None;
+ goto noVRoot;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetVRootGeometry --
+ *
+ * This procedure 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(tkwin, xPtr, yPtr, widthPtr, heightPtr)
+ Tk_Window tkwin; /* Window whose virtual root is to be
+ * queried. */
+ int *xPtr, *yPtr; /* Store x and y offsets of virtual root
+ * here. */
+ int *widthPtr, *heightPtr; /* Store dimensions of virtual root here. */
+{
+ WmInfo *wmPtr;
+ TkWindow *winPtr = (TkWindow *) tkwin;
+
+ /*
+ * Find the top-level window for tkwin, and locate the window manager
+ * information for that window.
+ */
+
+ while (!(winPtr->flags & TK_TOP_LEVEL) && (winPtr->parentPtr != NULL)) {
+ winPtr = winPtr->parentPtr;
+ }
+ wmPtr = winPtr->wmInfoPtr;
+
+ /*
+ * Make sure that the geometry information is up-to-date, then copy
+ * it out to the caller.
+ */
+
+ if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
+ UpdateVRootGeometry(wmPtr);
+ }
+ *xPtr = wmPtr->vRootX;
+ *yPtr = wmPtr->vRootY;
+ *widthPtr = wmPtr->vRootWidth;
+ *heightPtr = wmPtr->vRootHeight;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_MoveToplevelWindow --
+ *
+ * This procedure 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(tkwin, x, y)
+ Tk_Window tkwin; /* Window to move. */
+ int x, y; /* New location for window (within
+ * parent). */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ if (!(winPtr->flags & TK_TOP_LEVEL)) {
+ 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)) == 0) {
+ wmPtr->sizeHintsFlags |= USPosition;
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ }
+
+ /*
+ * 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, (ClientData) winPtr);
+ }
+ UpdateGeometryInfo((ClientData) winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * UpdateWmProtocols --
+ *
+ * This procedure transfers the most up-to-date information about
+ * window manager protocols from the WmInfo structure to the actual
+ * property on the top-level window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The WM_PROTOCOLS property gets changed for wmPtr's window.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+UpdateWmProtocols(wmPtr)
+ register WmInfo *wmPtr; /* Information about top-level window. */
+{
+ register ProtocolHandler *protPtr;
+ Atom deleteWindowAtom;
+ int count;
+ Atom *arrayPtr, *atomPtr;
+
+ /*
+ * There are only two tricky parts here. First, there could be any
+ * number of atoms for the window, so count them and malloc an array
+ * to hold all of their atoms. Second, we *always* want to respond
+ * to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.
+ */
+
+ for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;
+ protPtr = protPtr->nextPtr, count++) {
+ /* Empty loop body; we're just counting the handlers. */
+ }
+ arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));
+ deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,
+ "WM_DELETE_WINDOW");
+ arrayPtr[0] = deleteWindowAtom;
+ for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];
+ protPtr != NULL; protPtr = protPtr->nextPtr) {
+ if (protPtr->protocol != deleteWindowAtom) {
+ *atomPtr = protPtr->protocol;
+ atomPtr++;
+ }
+ }
+ XChangeProperty(wmPtr->winPtr->display, wmPtr->wrapperPtr->window,
+ Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),
+ XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,
+ atomPtr-arrayPtr);
+ ckfree((char *) arrayPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmProtocolEventProc --
+ *
+ * This procedure is called by the Tk_HandleEvent whenever a
+ * ClientMessage event arrives whose type is "WM_PROTOCOLS".
+ * This procedure 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(winPtr, eventPtr)
+ TkWindow *winPtr; /* Window to which the event was sent. */
+ XEvent *eventPtr; /* X event. */
+{
+ WmInfo *wmPtr;
+ register ProtocolHandler *protPtr;
+ Atom protocol;
+ int result;
+ char *protocolName;
+ Tcl_Interp *interp;
+
+ wmPtr = winPtr->wmInfoPtr;
+ if (wmPtr == NULL) {
+ return;
+ }
+ protocol = (Atom) eventPtr->xclient.data.l[0];
+
+ /*
+ * Note: it's very important to retrieve the protocol name now,
+ * before invoking the command, even though the name won't be used
+ * until after the command returns. This is because the command
+ * could delete winPtr, making it impossible for us to use it
+ * later in the call to Tk_GetAtomName.
+ */
+
+ protocolName = Tk_GetAtomName((Tk_Window) winPtr, protocol);
+ for (protPtr = wmPtr->protPtr; protPtr != NULL;
+ protPtr = protPtr->nextPtr) {
+ if (protocol == protPtr->protocol) {
+ Tcl_Preserve((ClientData) protPtr);
+ interp = protPtr->interp;
+ Tcl_Preserve((ClientData) interp);
+ result = Tcl_GlobalEval(interp, protPtr->command);
+ if (result != TCL_OK) {
+ Tcl_AddErrorInfo(interp, "\n (command for \"");
+ Tcl_AddErrorInfo(interp, protocolName);
+ Tcl_AddErrorInfo(interp,
+ "\" window manager protocol)");
+ Tcl_BackgroundError(interp);
+ }
+ Tcl_Release((ClientData) interp);
+ Tcl_Release((ClientData) 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) wmPtr->winPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmRestackToplevel --
+ *
+ * This procedure restacks a top-level window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * WinPtr gets restacked as specified by aboveBelow and otherPtr.
+ * This procedure doesn't return until the restack has taken
+ * effect and the ConfigureNotify event for it has been received.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
+ 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. */
+{
+ XWindowChanges changes;
+ XWindowAttributes atts;
+ unsigned int mask;
+ Window window, dummy1, dummy2, vRoot;
+ Window *children;
+ unsigned int numChildren;
+ int i;
+ int desiredIndex = 0; /* Initialized to stop gcc warnings. */
+ int ourIndex = 0; /* Initialized to stop gcc warnings. */
+ unsigned long serial;
+ XEvent event;
+ int diff;
+ Tk_ErrorHandler handler;
+ TkWindow *wrapperPtr;
+
+ changes.stack_mode = aboveBelow;
+ changes.sibling = None;
+ mask = CWStackMode;
+ if (winPtr->window == None) {
+ Tk_MakeWindowExist((Tk_Window) winPtr);
+ }
+ if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
+ /*
+ * Can't set stacking order properly until the window is on the
+ * screen (mapping it may give it a reparent window), so make sure
+ * it's on the screen.
+ */
+
+ TkWmMapWindow(winPtr);
+ }
+ wrapperPtr = winPtr->wmInfoPtr->wrapperPtr;
+ window = (winPtr->wmInfoPtr->reparent != None)
+ ? winPtr->wmInfoPtr->reparent : wrapperPtr->window;
+ if (otherPtr != NULL) {
+ if (otherPtr->window == None) {
+ Tk_MakeWindowExist((Tk_Window) otherPtr);
+ }
+ if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
+ TkWmMapWindow(otherPtr);
+ }
+ changes.sibling = (otherPtr->wmInfoPtr->reparent != None)
+ ? otherPtr->wmInfoPtr->reparent
+ : otherPtr->wmInfoPtr->wrapperPtr->window;
+ mask = CWStackMode|CWSibling;
+ }
+
+ /*
+ * Before actually reconfiguring the window, see if it's already
+ * in the right place. If so then don't reconfigure it. The
+ * reason for this extra work is that some window managers will
+ * ignore the reconfigure request if the window is already in
+ * the right place, causing a long delay in WaitForConfigureNotify
+ * while it times out. Special note: if the window is almost in
+ * the right place, and the only windows between it and the right
+ * place aren't mapped, then we don't reconfigure it either, for
+ * the same reason.
+ */
+
+ vRoot = winPtr->wmInfoPtr->vRoot;
+ if (vRoot == None) {
+ vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) winPtr));
+ }
+ if (XQueryTree(winPtr->display, vRoot, &dummy1, &dummy2,
+ &children, &numChildren) != 0) {
+ /*
+ * Find where our window is in the stacking order, and
+ * compute the desired location in the stacking order.
+ */
+
+ for (i = 0; i < numChildren; i++) {
+ if (children[i] == window) {
+ ourIndex = i;
+ }
+ if (children[i] == changes.sibling) {
+ desiredIndex = i;
+ }
+ }
+ if (mask & CWSibling) {
+ if (aboveBelow == Above) {
+ if (desiredIndex < ourIndex) {
+ desiredIndex += 1;
+ }
+ } else {
+ if (desiredIndex > ourIndex) {
+ desiredIndex -= 1;
+ }
+ }
+ } else {
+ if (aboveBelow == Above) {
+ desiredIndex = numChildren-1;
+ } else {
+ desiredIndex = 0;
+ }
+ }
+
+ /*
+ * See if there are any mapped windows between where we are
+ * and where we want to be.
+ */
+
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ while (desiredIndex != ourIndex) {
+ if ((XGetWindowAttributes(winPtr->display, children[desiredIndex],
+ &atts) != 0) && (atts.map_state != IsUnmapped)) {
+ break;
+ }
+ if (desiredIndex < ourIndex) {
+ desiredIndex++;
+ } else {
+ desiredIndex--;
+ }
+ }
+ Tk_DeleteErrorHandler(handler);
+ XFree((char *) children);
+ if (ourIndex == desiredIndex) {
+ return;
+ }
+ }
+
+ /*
+ * Reconfigure the window. This tricky because of two things:
+ * (a) Some window managers, like olvwm, insist that we raise
+ * or lower the toplevel window itself, as opposed to its
+ * decorative frame. Attempts to raise or lower the frame
+ * are ignored.
+ * (b) If the raise or lower is relative to a sibling, X will
+ * generate an error unless we work with the frames (the
+ * toplevels themselves aren't siblings).
+ * Fortunately, the procedure XReconfigureWMWindow is supposed
+ * to handle all of this stuff, so be careful to use it instead
+ * of XConfigureWindow.
+ */
+
+ serial = NextRequest(winPtr->display);
+ if (window != wrapperPtr->window) {
+ /*
+ * We're going to have to wait for events on a window that
+ * Tk doesn't own, so we have to tell X specially that we
+ * want to get events on that window. To make matters worse,
+ * it's possible that the window doesn't exist anymore (e.g.
+ * the toplevel could have been withdrawn) so ignore events
+ * occurring during the request.
+ */
+
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ XSelectInput(winPtr->display, window, StructureNotifyMask);
+ Tk_DeleteErrorHandler(handler);
+ }
+ XReconfigureWMWindow(winPtr->display, wrapperPtr->window,
+ Tk_ScreenNumber((Tk_Window) winPtr), mask, &changes);
+
+ /*
+ * Wait for the reconfiguration to complete. If we don't wait, then
+ * the window may not restack for a while and the application might
+ * observe it before it has restacked. Waiting for the reconfiguration
+ * is tricky if winPtr has been reparented, since the window getting
+ * the event isn't one that Tk owns.
+ */
+
+ if (window == wrapperPtr->window) {
+ WaitForConfigureNotify(winPtr, serial);
+ } else {
+ while (1) {
+ if (WaitForEvent(winPtr->display, window, ConfigureNotify,
+ &event) != TCL_OK) {
+ break;
+ }
+ diff = event.xconfigure.serial - serial;
+ if (diff >= 0) {
+ break;
+ }
+ }
+
+ /*
+ * Ignore errors that occur when we are de-selecting events on
+ * window, since it's possible that the window doesn't exist
+ * anymore (see comment above previous call to XSelectInput).
+ */
+
+ handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
+ (Tk_ErrorProc *) NULL, (ClientData) NULL);
+ XSelectInput(winPtr->display, window, (long) 0);
+ Tk_DeleteErrorHandler(handler);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmAddToColormapWindows --
+ *
+ * This procedure 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(winPtr)
+ TkWindow *winPtr; /* Window with a non-default colormap.
+ * Should not be a top-level window. */
+{
+ TkWindow *wrapperPtr;
+ TkWindow *topPtr;
+ Window *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_LEVEL) {
+ break;
+ }
+ }
+ if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
+ return;
+ }
+ if (topPtr->wmInfoPtr->wrapperPtr == NULL) {
+ CreateWrapper(topPtr->wmInfoPtr);
+ }
+ wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
+
+ /*
+ * Fetch the old value of the property.
+ */
+
+ if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
+ &oldPtr, &count) == 0) {
+ oldPtr = NULL;
+ count = 0;
+ }
+
+ /*
+ * Make sure that the window isn't already in the list.
+ */
+
+ for (i = 0; i < count; i++) {
+ if (oldPtr[i] == winPtr->window) {
+ 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 = (Window *) ckalloc((unsigned) ((count+2)*sizeof(Window)));
+ for (i = 0; i < count; i++) {
+ newPtr[i] = oldPtr[i];
+ }
+ if (count == 0) {
+ count++;
+ }
+ newPtr[count-1] = winPtr->window;
+ newPtr[count] = topPtr->window;
+ XSetWMColormapWindows(topPtr->display, wrapperPtr->window, newPtr,
+ count+1);
+ ckfree((char *) newPtr);
+ if (oldPtr != NULL) {
+ XFree((char *) oldPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmRemoveFromColormapWindows --
+ *
+ * This procedure 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(winPtr)
+ TkWindow *winPtr; /* Window that may be present in
+ * WM_COLORMAP_WINDOWS property for its
+ * top-level. Should not be a top-level
+ * window. */
+{
+ TkWindow *wrapperPtr;
+ TkWindow *topPtr;
+ Window *oldPtr;
+ int count, i, j;
+
+ if (winPtr->window == None) {
+ return;
+ }
+
+ 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->wrapperPtr == NULL) {
+ CreateWrapper(topPtr->wmInfoPtr);
+ }
+ wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
+ if (wrapperPtr == NULL) {
+ return;
+ }
+
+ /*
+ * Fetch the old value of the property.
+ */
+
+ if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
+ &oldPtr, &count) == 0) {
+ return;
+ }
+
+ /*
+ * Find the window and slide the following ones down to cover
+ * it up.
+ */
+
+ for (i = 0; i < count; i++) {
+ if (oldPtr[i] == winPtr->window) {
+ for (j = i ; j < count-1; j++) {
+ oldPtr[j] = oldPtr[j+1];
+ }
+ XSetWMColormapWindows(topPtr->display, wrapperPtr->window,
+ oldPtr, count-1);
+ break;
+ }
+ }
+ XFree((char *) oldPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkGetPointerCoords --
+ *
+ * Fetch the position of the mouse pointer.
+ *
+ * Results:
+ * *xPtr and *yPtr are filled in with the (virtual) root coordinates
+ * of the mouse pointer for tkwin's display. If the pointer isn't
+ * on tkwin's screen, then -1 values are returned for both
+ * coordinates. The argument tkwin must be a toplevel window.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkGetPointerCoords(tkwin, xPtr, yPtr)
+ Tk_Window tkwin; /* Toplevel window that identifies screen
+ * on which lookup is to be done. */
+ int *xPtr, *yPtr; /* Store pointer coordinates here. */
+{
+ TkWindow *winPtr = (TkWindow *) tkwin;
+ WmInfo *wmPtr;
+ Window w, root, child;
+ int rootX, rootY;
+ unsigned int mask;
+
+ wmPtr = winPtr->wmInfoPtr;
+
+ w = wmPtr->vRoot;
+ if (w == None) {
+ w = RootWindow(winPtr->display, winPtr->screenNum);
+ }
+ if (XQueryPointer(winPtr->display, w, &root, &child, &rootX, &rootY,
+ xPtr, yPtr, &mask) != True) {
+ *xPtr = -1;
+ *yPtr = -1;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * GetMaxSize --
+ *
+ * This procedure 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 procedure computes the largest sizes that
+ * will fit on the screen.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)
+ 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 = DisplayWidth(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
+ - 15;
+ 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 = DisplayHeight(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
+ - 30;
+ if (wmPtr->gridWin != NULL) {
+ tmp = wmPtr->reqGridHeight
+ + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;
+ }
+ *maxHeightPtr = tmp;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * 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 Mac toplevel.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkpMakeMenuWindow(tkwin, transient)
+ 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. */
+{
+ WmInfo *wmPtr;
+ XSetWindowAttributes atts;
+ TkWindow *wrapperPtr;
+
+ if (!Tk_IsTopLevel(tkwin)) {
+ return;
+ }
+ wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
+ if (wmPtr->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr);
+ }
+ wrapperPtr = wmPtr->wrapperPtr;
+ if (transient) {
+ atts.override_redirect = True;
+ atts.save_under = True;
+ } else {
+ atts.override_redirect = False;
+ atts.save_under = False;
+ }
+
+ /*
+ * The override-redirect and save-under bits must be set on the
+ * wrapper window in order to have the desired effect. However,
+ * also set the override-redirect bit on the window itself, so
+ * that the "wm overrideredirect" command will see it.
+ */
+
+ if ((atts.override_redirect != Tk_Attributes(wrapperPtr)->override_redirect)
+ || (atts.save_under != Tk_Attributes(wrapperPtr)->save_under)) {
+ Tk_ChangeWindowAttributes((Tk_Window) wrapperPtr,
+ CWOverrideRedirect|CWSaveUnder, &atts);
+ }
+ if (atts.override_redirect != Tk_Attributes(tkwin)->override_redirect) {
+ Tk_ChangeWindowAttributes(tkwin, CWOverrideRedirect, &atts);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CreateWrapper --
+ *
+ * This procedure is invoked to create the wrapper window for a
+ * toplevel window. It is called just before a toplevel is mapped
+ * for the first time.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The wrapper is created and the toplevel is reparented inside it.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+CreateWrapper(wmPtr)
+ WmInfo *wmPtr; /* Window manager information for the
+ * window. */
+{
+ TkWindow *winPtr, *wrapperPtr;
+ Window parent;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ winPtr = wmPtr->winPtr;
+ if (winPtr->window == None) {
+ Tk_MakeWindowExist((Tk_Window) winPtr);
+ }
+
+ /*
+ * The code below is copied from CreateTopLevelWindow,
+ * Tk_MakeWindowExist, and TkpMakeWindow; The idea is to create an
+ * "official" Tk window (so that we can get events on it), but to
+ * hide the window outside the official Tk hierarchy so that it
+ * isn't visible to the application. See the comments for the other
+ * procedures if you have questions about this code.
+ */
+
+ wmPtr->wrapperPtr = wrapperPtr = TkAllocWindow(winPtr->dispPtr,
+ Tk_ScreenNumber((Tk_Window) winPtr), winPtr);
+ wrapperPtr->dirtyAtts |= CWBorderPixel;
+
+ /*
+ * Tk doesn't normally select for StructureNotifyMask events because
+ * the events are synthesized internally. However, for wrapper
+ * windows we need to know when the window manager modifies the
+ * window configuration. We also need to select on focus change
+ * events; these are the only windows for which we care about focus
+ * changes.
+ */
+
+ wrapperPtr->flags |= TK_WRAPPER;
+ wrapperPtr->atts.event_mask |= StructureNotifyMask|FocusChangeMask;
+ wrapperPtr->atts.override_redirect = winPtr->atts.override_redirect;
+ if (winPtr->flags & TK_EMBEDDED) {
+ parent = TkUnixContainerId(winPtr);
+ } else {
+ parent = XRootWindow(wrapperPtr->display, wrapperPtr->screenNum);
+ }
+ wrapperPtr->window = XCreateWindow(wrapperPtr->display,
+ parent, wrapperPtr->changes.x, wrapperPtr->changes.y,
+ (unsigned) wrapperPtr->changes.width,
+ (unsigned) wrapperPtr->changes.height,
+ (unsigned) wrapperPtr->changes.border_width, wrapperPtr->depth,
+ InputOutput, wrapperPtr->visual,
+ wrapperPtr->dirtyAtts|CWOverrideRedirect, &wrapperPtr->atts);
+ hPtr = Tcl_CreateHashEntry(&wrapperPtr->dispPtr->winTable,
+ (char *) wrapperPtr->window, &new);
+ Tcl_SetHashValue(hPtr, wrapperPtr);
+ wrapperPtr->mainPtr = winPtr->mainPtr;
+ wrapperPtr->mainPtr->refCount++;
+ wrapperPtr->dirtyAtts = 0;
+ wrapperPtr->dirtyChanges = 0;
+#ifdef TK_USE_INPUT_METHODS
+ wrapperPtr->inputContext = NULL;
+#endif /* TK_USE_INPUT_METHODS */
+ wrapperPtr->wmInfoPtr = wmPtr;
+
+ /*
+ * Reparent the toplevel window inside the wrapper.
+ */
+
+ XReparentWindow(wrapperPtr->display, winPtr->window, wrapperPtr->window,
+ 0, 0);
+
+ /*
+ * Tk must monitor structure events for wrapper windows in order
+ * to detect changes made by window managers such as resizing,
+ * mapping, unmapping, etc..
+ */
+
+ Tk_CreateEventHandler((Tk_Window) wmPtr->wrapperPtr, StructureNotifyMask,
+ WrapperEventProc, (ClientData) wmPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkWmFocusToplevel --
+ *
+ * This is a utility procedure invoked by focus-management code.
+ * The focus code responds to externally generated focus-related
+ * events on wrapper windows but ignores those events for any other
+ * windows. This procedure determines whether a given window is a
+ * wrapper window and, if so, returns the toplevel window
+ * corresponding to the wrapper.
+ *
+ * Results:
+ * If winPtr is a wrapper window, returns a pointer to the
+ * corresponding toplevel window; otherwise returns NULL.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkWindow *
+TkWmFocusToplevel(winPtr)
+ TkWindow *winPtr; /* Window that received a focus-related
+ * event. */
+{
+ if (!(winPtr->flags & TK_WRAPPER)) {
+ return NULL;
+ }
+ return winPtr->wmInfoPtr->winPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkUnixSetMenubar --
+ *
+ * This procedure is invoked by menu management code to specify the
+ * window to use as a menubar for a given toplevel window.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window given by menubar will be mapped and positioned inside
+ * the wrapper for tkwin and above tkwin. Menubar will
+ * automatically be resized to maintain the height specified by
+ * TkUnixSetMenuHeight the same width as tkwin. Any previous
+ * menubar specified for tkwin will be unmapped and ignored from
+ * now on.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkUnixSetMenubar(tkwin, menubar)
+ Tk_Window tkwin; /* Token for toplevel window. */
+ Tk_Window menubar; /* Token for window that is to serve as
+ * menubar for tkwin. Must not be a
+ * toplevel window. If NULL, any
+ * existing menubar is canceled and the
+ * menu height is reset to 0. */
+{
+ WmInfo *wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
+ Tk_Window parent;
+ TkWindow *menubarPtr = (TkWindow *) menubar;
+
+ if (wmPtr->menubar != NULL) {
+ /*
+ * There's already a menubar for this toplevel. If it isn't the
+ * same as the new menubar, unmap it so that it is out of the
+ * way, and reparent it back to its original parent.
+ */
+
+ if (wmPtr->menubar == menubar) {
+ return;
+ }
+ ((TkWindow *) wmPtr->menubar)->wmInfoPtr = NULL;
+ ((TkWindow *) wmPtr->menubar)->flags &= ~TK_REPARENTED;
+ Tk_UnmapWindow(wmPtr->menubar);
+ parent = Tk_Parent(wmPtr->menubar);
+ if (parent != NULL) {
+ Tk_MakeWindowExist(parent);
+ XReparentWindow(Tk_Display(wmPtr->menubar),
+ Tk_WindowId(wmPtr->menubar), Tk_WindowId(parent), 0, 0);
+ }
+ Tk_DeleteEventHandler(wmPtr->menubar, StructureNotifyMask,
+ MenubarDestroyProc, (ClientData) wmPtr->menubar);
+ Tk_ManageGeometry(wmPtr->menubar, NULL, (ClientData) NULL);
+ }
+
+ wmPtr->menubar = menubar;
+ if (menubar == NULL) {
+ wmPtr->menuHeight = 0;
+ } else {
+ if ((menubarPtr->flags & TK_TOP_LEVEL)
+ || (Tk_Screen(menubar) != Tk_Screen(tkwin))) {
+ panic("TkUnixSetMenubar got bad menubar");
+ }
+ wmPtr->menuHeight = Tk_ReqHeight(menubar);
+ if (wmPtr->menuHeight == 0) {
+ wmPtr->menuHeight = 1;
+ }
+ Tk_MakeWindowExist(tkwin);
+ Tk_MakeWindowExist(menubar);
+ if (wmPtr->wrapperPtr == NULL) {
+ CreateWrapper(wmPtr);
+ }
+ XReparentWindow(Tk_Display(menubar), Tk_WindowId(menubar),
+ wmPtr->wrapperPtr->window, 0, 0);
+ menubarPtr->wmInfoPtr = wmPtr;
+ Tk_MoveResizeWindow(menubar, 0, 0, Tk_Width(tkwin), wmPtr->menuHeight);
+ Tk_MapWindow(menubar);
+ Tk_CreateEventHandler(menubar, StructureNotifyMask, MenubarDestroyProc,
+ (ClientData) menubar);
+ Tk_ManageGeometry(menubar, &menubarMgrType, (ClientData) wmPtr);
+ menubarPtr->flags |= TK_REPARENTED;
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) tkwin);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenubarDestroyProc --
+ *
+ * This procedure is invoked by the event dispatcher whenever a
+ * menubar window is destroyed (it's also invoked for a few other
+ * kinds of events, but we ignore those).
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The association between the window and its toplevel is broken,
+ * so that the window is no longer considered to be a menubar.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenubarDestroyProc(clientData, eventPtr)
+ ClientData clientData; /* TkWindow pointer for menubar. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ WmInfo *wmPtr;
+
+ if (eventPtr->type != DestroyNotify) {
+ return;
+ }
+ wmPtr = ((TkWindow *) clientData)->wmInfoPtr;
+ wmPtr->menubar = NULL;
+ wmPtr->menuHeight = 0;
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * MenubarReqProc --
+ *
+ * This procedure is invoked by the Tk geometry management code
+ * whenever a menubar calls Tk_GeometryRequest to request a new
+ * size.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+MenubarReqProc(clientData, tkwin)
+ ClientData clientData; /* Pointer to the window manager
+ * information for tkwin's toplevel. */
+ Tk_Window tkwin; /* Handle for menubar window. */
+{
+ WmInfo *wmPtr = (WmInfo *) clientData;
+
+ wmPtr->menuHeight = Tk_ReqHeight(tkwin);
+ if (wmPtr->menuHeight <= 0) {
+ wmPtr->menuHeight = 1;
+ }
+ wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
+ if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
+ Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
+ wmPtr->flags |= WM_UPDATE_PENDING;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpGetWrapperWindow --
+ *
+ * Given a toplevel window return the hidden wrapper window for
+ * the toplevel window if available.
+ *
+ * Results:
+ * The wrapper window. NULL is we were not passed a toplevel
+ * window or the wrapper has yet to be created.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+TkWindow *
+TkpGetWrapperWindow(winPtr)
+ TkWindow *winPtr; /* A toplevel window pointer. */
+{
+ register WmInfo *wmPtr = winPtr->wmInfoPtr;
+
+ if ((winPtr == NULL) || (wmPtr == NULL)) {
+ return NULL;
+ }
+
+ return wmPtr->wrapperPtr;
+}
diff --git a/unix/tkUnixXId.c b/unix/tkUnixXId.c
new file mode 100644
index 0000000..f67c35c
--- /dev/null
+++ b/unix/tkUnixXId.c
@@ -0,0 +1,537 @@
+/*
+ * tkUnixXId.c --
+ *
+ * This file provides a replacement function for the default X
+ * resource allocator (_XAllocID). The problem with the default
+ * allocator is that it never re-uses ids, which causes long-lived
+ * applications to crash when X resource identifiers wrap around.
+ * The replacement functions in this file re-use old identifiers
+ * to prevent this problem.
+ *
+ * The code in this file is based on similar implementations by
+ * George C. Kaplan and Michael Hoegeman.
+ *
+ * Copyright (c) 1993 The Regents of the University of California.
+ * Copyright (c) 1994-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.
+ *
+ * SCCS: @(#) tkUnixXId.c 1.22 97/06/25 13:16:47
+ */
+
+/*
+ * The definition below is needed on some systems so that we can access
+ * the resource_alloc field of Display structures in order to replace
+ * the resource allocator.
+ */
+
+#define XLIB_ILLEGAL_ACCESS 1
+
+#include "tkInt.h"
+#include "tkPort.h"
+#include "tkUnixInt.h"
+
+/*
+ * A structure of the following type is used to hold one or more
+ * available resource identifiers. There is a list of these structures
+ * for each display.
+ */
+
+#define IDS_PER_STACK 10
+typedef struct TkIdStack {
+ XID ids[IDS_PER_STACK]; /* Array of free identifiers. */
+ int numUsed; /* Indicates how many of the entries
+ * in ids are currently in use. */
+ TkDisplay *dispPtr; /* Display to which ids belong. */
+ struct TkIdStack *nextPtr; /* Next bunch of free identifiers
+ * for the same display. */
+} TkIdStack;
+
+/*
+ * Forward declarations for procedures defined in this file:
+ */
+
+static XID AllocXId _ANSI_ARGS_((Display *display));
+static Tk_RestrictAction CheckRestrictProc _ANSI_ARGS_((
+ ClientData clientData, XEvent *eventPtr));
+static void WindowIdCleanup _ANSI_ARGS_((ClientData clientData));
+static void WindowIdCleanup2 _ANSI_ARGS_((ClientData clientData));
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkInitXId --
+ *
+ * This procedure is called to initialize the id allocator for
+ * a given display.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The official allocator for the display is set up to be Tk_AllocXID.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkInitXId(dispPtr)
+ TkDisplay *dispPtr; /* Tk's information about the
+ * display. */
+{
+ dispPtr->idStackPtr = NULL;
+ dispPtr->defaultAllocProc = (XID (*) _ANSI_ARGS_((Display *display)))
+ dispPtr->display->resource_alloc;
+ dispPtr->display->resource_alloc = AllocXId;
+ dispPtr->windowStackPtr = NULL;
+ dispPtr->idCleanupScheduled = 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * AllocXId --
+ *
+ * This procedure is invoked by Xlib as the resource allocator
+ * for a display.
+ *
+ * Results:
+ * The return value is an X resource identifier that isn't currently
+ * in use.
+ *
+ * Side effects:
+ * The identifier is removed from the stack of free identifiers,
+ * if it was previously on the stack.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static XID
+AllocXId(display)
+ Display *display; /* Display for which to allocate. */
+{
+ TkDisplay *dispPtr;
+ TkIdStack *stackPtr;
+
+ /*
+ * Find Tk's information about the display.
+ */
+
+ dispPtr = TkGetDisplay(display);
+
+ /*
+ * If the topmost chunk on the stack is empty then free it. Then
+ * check for a free id on the stack and return it if it exists.
+ */
+
+ stackPtr = dispPtr->idStackPtr;
+ if (stackPtr != NULL) {
+ while (stackPtr->numUsed == 0) {
+ dispPtr->idStackPtr = stackPtr->nextPtr;
+ ckfree((char *) stackPtr);
+ stackPtr = dispPtr->idStackPtr;
+ if (stackPtr == NULL) {
+ goto defAlloc;
+ }
+ }
+ stackPtr->numUsed--;
+ return stackPtr->ids[stackPtr->numUsed];
+ }
+
+ /*
+ * No free ids in the stack: just get one from the default
+ * allocator.
+ */
+
+ defAlloc:
+ return (*dispPtr->defaultAllocProc)(display);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_FreeXId --
+ *
+ * This procedure is called to indicate that an X resource identifier
+ * is now free.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The identifier is added to the stack of free identifiers for its
+ * display, so that it can be re-used.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_FreeXId(display, xid)
+ Display *display; /* Display for which xid was
+ * allocated. */
+ XID xid; /* Identifier that is no longer
+ * in use. */
+{
+ TkDisplay *dispPtr;
+ TkIdStack *stackPtr;
+
+ /*
+ * Find Tk's information about the display.
+ */
+
+ dispPtr = TkGetDisplay(display);
+
+ /*
+ * Add a new chunk to the stack if the current chunk is full.
+ */
+
+ stackPtr = dispPtr->idStackPtr;
+ if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
+ stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
+ stackPtr->numUsed = 0;
+ stackPtr->dispPtr = dispPtr;
+ stackPtr->nextPtr = dispPtr->idStackPtr;
+ dispPtr->idStackPtr = stackPtr;
+ }
+
+ /*
+ * Add the id to the current chunk.
+ */
+
+ stackPtr->ids[stackPtr->numUsed] = xid;
+ stackPtr->numUsed++;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkFreeWindowId --
+ *
+ * This procedure is invoked instead of TkFreeXId for window ids.
+ * See below for the reason why.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The id given by w will eventually be freed, so that it can be
+ * reused for other resources.
+ *
+ * Design:
+ * Freeing window ids is very tricky because there could still be
+ * events pending for a window in the event queue (or even in the
+ * server) at the time the window is destroyed. If the window
+ * id were to get reused immediately for another window, old
+ * events could "drop in" on the new window, causing unexpected
+ * behavior.
+ *
+ * Thus we have to wait to re-use a window id until we know that
+ * there are no events left for it. Right now this is done in
+ * two steps. First, we wait until we know that the server
+ * has seen the XDestroyWindow request, so we can be sure that
+ * it won't generate more events for the window and that any
+ * existing events are in our queue. Second, we make sure that
+ * there are no events whatsoever in our queue (this is conservative
+ * but safe).
+ *
+ * The first step is done by remembering the request id of the
+ * XDestroyWindow request and using LastKnownRequestProcessed to
+ * see what events the server has processed. If multiple windows
+ * get destroyed at about the same time, we just remember the
+ * most recent request number for any of them (again, conservative
+ * but safe).
+ *
+ * There are a few other complications as well. When Tk destroys a
+ * sub-tree of windows, it only issues a single XDestroyWindow call,
+ * at the very end for the root of the subtree. We can't free any of
+ * the window ids until the final XDestroyWindow call. To make sure
+ * that this happens, we have to keep track of deletions in progress,
+ * hence the need for the "destroyCount" field of the display.
+ *
+ * One final problem. Some servers, like Sun X11/News servers still
+ * seem to have problems with ids getting reused too quickly. I'm
+ * not completely sure why this is a problem, but delaying the
+ * recycling of ids appears to eliminate it. Therefore, we wait
+ * an additional few seconds, even after "the coast is clear"
+ * before reusing the ids.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TkFreeWindowId(dispPtr, w)
+ TkDisplay *dispPtr; /* Display that w belongs to. */
+ Window w; /* X identifier for window on dispPtr. */
+{
+ TkIdStack *stackPtr;
+
+ /*
+ * Put the window id on a separate stack of window ids, rather
+ * than the main stack, so it won't get reused right away. Add
+ * a new chunk to the stack if the current chunk is full.
+ */
+
+ stackPtr = dispPtr->windowStackPtr;
+ if ((stackPtr == NULL) || (stackPtr->numUsed >= IDS_PER_STACK)) {
+ stackPtr = (TkIdStack *) ckalloc(sizeof(TkIdStack));
+ stackPtr->numUsed = 0;
+ stackPtr->dispPtr = dispPtr;
+ stackPtr->nextPtr = dispPtr->windowStackPtr;
+ dispPtr->windowStackPtr = stackPtr;
+ }
+
+ /*
+ * Add the id to the current chunk.
+ */
+
+ stackPtr->ids[stackPtr->numUsed] = w;
+ stackPtr->numUsed++;
+
+ /*
+ * Schedule a call to WindowIdCleanup if one isn't already
+ * scheduled.
+ */
+
+ if (!dispPtr->idCleanupScheduled) {
+ dispPtr->idCleanupScheduled = 1;
+ Tcl_CreateTimerHandler(100, WindowIdCleanup, (ClientData) dispPtr);
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowIdCleanup --
+ *
+ * See if we can now free up all the accumulated ids of
+ * deleted windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If it's safe to move the window ids back to the main free
+ * list, we schedule this to happen after a few mores seconds
+ * of delay. If it's not safe to move them yet, a timer handler
+ * gets invoked to try again later.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+WindowIdCleanup(clientData)
+ ClientData clientData; /* Pointer to TkDisplay for display */
+{
+ TkDisplay *dispPtr = (TkDisplay *) clientData;
+ int anyEvents, delta;
+ Tk_RestrictProc *oldProc;
+ ClientData oldData;
+ static Tcl_Time timeout = {0, 0};
+
+ dispPtr->idCleanupScheduled = 0;
+
+ /*
+ * See if it's safe to recycle the window ids. It's safe if:
+ * (a) no deletions are in progress.
+ * (b) the server has seen all of the requests up to the last
+ * XDestroyWindow request.
+ * (c) there are no events in the event queue; the only way to
+ * test for this right now is to create a restrict proc that
+ * will filter the events, then call Tcl_DoOneEvent to see if
+ * the procedure gets invoked.
+ */
+
+ if (dispPtr->destroyCount > 0) {
+ goto tryAgain;
+ }
+ delta = LastKnownRequestProcessed(dispPtr->display)
+ - dispPtr->lastDestroyRequest;
+ if (delta < 0) {
+ XSync(dispPtr->display, False);
+ }
+ anyEvents = 0;
+ oldProc = Tk_RestrictEvents(CheckRestrictProc, (ClientData) &anyEvents,
+ &oldData);
+ TkUnixDoOneXEvent(&timeout);
+ Tk_RestrictEvents(oldProc, oldData, &oldData);
+ if (anyEvents) {
+ goto tryAgain;
+ }
+
+ /*
+ * These ids look safe to recycle, but we still need to delay a bit
+ * more (see comments for TkFreeWindowId). Schedule the final freeing.
+ */
+
+ if (dispPtr->windowStackPtr != NULL) {
+ Tcl_CreateTimerHandler(5000, WindowIdCleanup2,
+ (ClientData) dispPtr->windowStackPtr);
+ dispPtr->windowStackPtr = NULL;
+ }
+ return;
+
+ /*
+ * It's still not safe to free up the ids. Try again a bit later.
+ */
+
+ tryAgain:
+ dispPtr->idCleanupScheduled = 1;
+ Tcl_CreateTimerHandler(500, WindowIdCleanup, (ClientData) dispPtr);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * WindowIdCleanup2 --
+ *
+ * This procedure is the last one in the chain that recycles
+ * window ids. It takes all of the ids indicated by its
+ * argument and adds them back to the main id free list.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Window ids get added to the main free list for their display.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+WindowIdCleanup2(clientData)
+ ClientData clientData; /* Pointer to TkIdStack list. */
+{
+ TkIdStack *stackPtr = (TkIdStack *) clientData;
+ TkIdStack *lastPtr;
+
+ lastPtr = stackPtr;
+ while (lastPtr->nextPtr != NULL) {
+ lastPtr = lastPtr->nextPtr;
+ }
+ lastPtr->nextPtr = stackPtr->dispPtr->idStackPtr;
+ stackPtr->dispPtr->idStackPtr = stackPtr;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * CheckRestrictProc --
+ *
+ * This procedure is a restrict procedure, called by Tcl_DoOneEvent
+ * to filter X events. All it does is to set a flag to indicate
+ * that there are X events present.
+ *
+ * Results:
+ * Sets the integer pointed to by the argument, then returns
+ * TK_DEFER_EVENT.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static Tk_RestrictAction
+CheckRestrictProc(clientData, eventPtr)
+ ClientData clientData; /* Pointer to flag to set. */
+ XEvent *eventPtr; /* Event to filter; not used. */
+{
+ int *flag = (int *) clientData;
+ *flag = 1;
+ return TK_DEFER_EVENT;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_GetPixmap --
+ *
+ * Same as the XCreatePixmap procedure except that it manages
+ * resource identifiers better.
+ *
+ * Results:
+ * Returns a new pixmap.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+Pixmap
+Tk_GetPixmap(display, d, width, height, depth)
+ Display *display; /* Display for new pixmap. */
+ Drawable d; /* Drawable where pixmap will be used. */
+ int width, height; /* Dimensions of pixmap. */
+ int depth; /* Bits per pixel for pixmap. */
+{
+ return XCreatePixmap(display, d, (unsigned) width, (unsigned) height,
+ (unsigned) depth);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tk_FreePixmap --
+ *
+ * Same as the XFreePixmap procedure except that it also marks
+ * the resource identifier as free.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The pixmap is freed in the X server and its resource identifier
+ * is saved for re-use.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+Tk_FreePixmap(display, pixmap)
+ Display *display; /* Display for which pixmap was allocated. */
+ Pixmap pixmap; /* Identifier for pixmap. */
+{
+ XFreePixmap(display, pixmap);
+ Tk_FreeXId(display, (XID) pixmap);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * TkpWindowWasRecentlyDeleted --
+ *
+ * Checks whether the window was recently deleted. This is called
+ * by the generic error handler to detect asynchronous notification
+ * of errors due to operations by Tk on a window that was already
+ * deleted by the server.
+ *
+ * Results:
+ * 1 if the window was deleted recently, 0 otherwise.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+TkpWindowWasRecentlyDeleted(win, dispPtr)
+ Window win; /* The window to check for. */
+ TkDisplay *dispPtr; /* The window belongs to this display. */
+{
+ TkIdStack *stackPtr;
+ int i;
+
+ for (stackPtr = dispPtr->windowStackPtr;
+ stackPtr != NULL;
+ stackPtr = stackPtr->nextPtr) {
+ for (i = 0; i < stackPtr->numUsed; i++) {
+ if ((Window) stackPtr->ids[i] == win) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}