diff options
author | rjohnson <rjohnson@noemail.net> | 1998-04-01 09:51:45 (GMT) |
---|---|---|
committer | rjohnson <rjohnson@noemail.net> | 1998-04-01 09:51:45 (GMT) |
commit | 9c5b7f2b7e472536ed2e7c915ead05e2aa264182 (patch) | |
tree | 8fb30cb152c4dc191be47fa043d2e6f5ea38c7ba /unix | |
parent | 1d0efcbe267f2c0eb73869862522fb20fb2d63ca (diff) | |
download | tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.zip tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.tar.gz tk-9c5b7f2b7e472536ed2e7c915ead05e2aa264182.tar.bz2 |
Initial revision
FossilOrigin-Name: 2bf55ca9aa942b581137b9f474da5ad9c1480de4
Diffstat (limited to 'unix')
-rw-r--r-- | unix/Makefile.in | 1003 | ||||
-rw-r--r-- | unix/README | 125 | ||||
-rw-r--r-- | unix/configure.in | 407 | ||||
-rw-r--r-- | unix/install-sh | 119 | ||||
-rw-r--r-- | unix/mkLinks | 878 | ||||
-rw-r--r-- | unix/porting.notes | 86 | ||||
-rw-r--r-- | unix/porting.old | 324 | ||||
-rw-r--r-- | unix/tkAppInit.c | 120 | ||||
-rw-r--r-- | unix/tkConfig.sh.in | 68 | ||||
-rw-r--r-- | unix/tkUnix.c | 79 | ||||
-rw-r--r-- | unix/tkUnix3d.c | 448 | ||||
-rw-r--r-- | unix/tkUnixButton.c | 478 | ||||
-rw-r--r-- | unix/tkUnixColor.c | 424 | ||||
-rw-r--r-- | unix/tkUnixCursor.c | 407 | ||||
-rw-r--r-- | unix/tkUnixDefault.h | 450 | ||||
-rw-r--r-- | unix/tkUnixDialog.c | 207 | ||||
-rw-r--r-- | unix/tkUnixDraw.c | 171 | ||||
-rw-r--r-- | unix/tkUnixEmbed.c | 1001 | ||||
-rw-r--r-- | unix/tkUnixEvent.c | 498 | ||||
-rw-r--r-- | unix/tkUnixFocus.c | 149 | ||||
-rw-r--r-- | unix/tkUnixFont.c | 979 | ||||
-rw-r--r-- | unix/tkUnixInit.c | 130 | ||||
-rw-r--r-- | unix/tkUnixInt.h | 32 | ||||
-rw-r--r-- | unix/tkUnixMenu.c | 1603 | ||||
-rw-r--r-- | unix/tkUnixMenubu.c | 307 | ||||
-rw-r--r-- | unix/tkUnixPort.h | 235 | ||||
-rw-r--r-- | unix/tkUnixScale.c | 828 | ||||
-rw-r--r-- | unix/tkUnixScrlbr.c | 476 | ||||
-rw-r--r-- | unix/tkUnixSelect.c | 1189 | ||||
-rw-r--r-- | unix/tkUnixSend.c | 1851 | ||||
-rw-r--r-- | unix/tkUnixWm.c | 4813 | ||||
-rw-r--r-- | unix/tkUnixXId.c | 537 |
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, + ®Ptr->propLength, &bytesAfter, + (unsigned char **) ®Ptr->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; +} |