diff --git a/Recipe b/Recipe index ce9d052b..9fc231a2 100644 --- a/Recipe +++ b/Recipe @@ -19,11 +19,13 @@ !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile mpw mac/Makefile.mpw +!makefile osx macosx/Makefile # Source directories. !srcdir charset/ !srcdir windows/ !srcdir unix/ !srcdir mac/ +!srcdir macosx/ # Help text added to the top of each Makefile, with /D converted # into -D as appropriate for the particular Makefile. @@ -174,6 +176,9 @@ install: install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" !end +!begin osx +CFLAGS += -DMACOSX +!end # ------------------------------------------------------------ # Definitions of object groups. A group name, followed by an =, @@ -190,8 +195,9 @@ GUITERM = TERMINAL window windlg winctrls sizetip winucs winprint + winutils wincfg # Same thing on Unix. -UXTERM = TERMINAL gtkwin gtkdlg gtkcols gtkpanel gtkcfg uxcfg uxucs uxprint - + xkeysym timing +UXTERM = TERMINAL uxcfg uxucs uxprint timing +GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkcols gtkpanel xkeysym +OSXTERM = UXTERM osxwin osxdlg osxctrls # Non-SSH back ends (putty, puttytel, plink). NONSSH = telnet raw rlogin ldisc pinger @@ -212,6 +218,7 @@ SFTP = sftp int64 logging MISC = timing misc version settings tree234 proxy WINMISC = MISC winstore winnet cmdline windefs winmisc pproxy wintime UXMISC = MISC uxstore uxsel uxnet cmdline uxmisc uxproxy time +OSXMISC = MISC uxstore uxsel osxsel uxnet uxmisc uxproxy time MACMISC = MISC macstore macnet mtcpnet otnet macmisc macabout pproxy # Character set library, for use in pterm. @@ -251,11 +258,11 @@ puttygen : [G] winpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshpubk sshaes sshsh512 import winutils puttygen.res tree234 + notiming LIBS wintime -pterm : [X] UXTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore +pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time -putty : [X] UXTERM uxmisc misc ldisc settings uxsel BE_ALL uxstore +putty : [X] GTKTERM uxmisc misc ldisc settings uxsel BE_ALL uxstore + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 -puttytel : [X] UXTERM uxmisc misc ldisc settings uxsel BE_NOSSH +puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel BE_NOSSH + uxstore uxsignal CHARSET uxputty NONSSH UXMISC plink : [U] uxplink uxcons NONSSH UXSSH BE_ALL logging UXMISC uxsignal ux_x11 @@ -277,3 +284,6 @@ PuTTYtel : [M] terminal wcwidth ldiscucs logging BE_NOSSH mac macdlg PuTTYgen : [M] macpgen sshrsag sshdssg sshprime sshdes sshbn sshmd5 version + sshrand macnoise sshsha macstore misc sshrsa sshdss macmisc sshpubk + sshaes sshsh512 import macpgen.rsrc macpgkey macabout + +PuTTY : [MX] osxmain OSXTERM OSXMISC CHARSET BE_ALL NONSSH UXSSH + + ux_x11 uxpty uxsignal testback diff --git a/macosx/Makefile b/macosx/Makefile new file mode 100644 index 00000000..3fb6f092 --- /dev/null +++ b/macosx/Makefile @@ -0,0 +1,789 @@ +# Makefile for putty under Mac OS X. +# +# This file was created by `mkfiles.pl' from the `Recipe' file. +# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. +# +# Extra options you can set: +# +# - VER=-DSNAPSHOT=1999-01-25 +# Generates executables whose About box report them as being a +# development snapshot. +# +# - VER=-DRELEASE=0.43 +# Generates executables whose About box report them as being a +# release version. +# +# - COMPAT=-DAUTO_WINSOCK +# Causes PuTTY to assume that includes its own WinSock +# header file, so that it won't try to include . +# +# - COMPAT=-DWINSOCK_TWO +# Causes the PuTTY utilities to include instead of +# , except Plink which _needs_ WinSock 2 so it already +# does this. +# +# - COMPAT=-DNO_SECURITY +# Disables Pageant's use of , which is not available +# with some development environments (such as older versions of +# the Cygwin/mingw GNU toolchain). This means that Pageant +# won't care about the local user ID of processes accessing it; a +# version of Pageant built with this option will therefore refuse +# to run under NT-series OSes on security grounds (although it +# will run fine on Win95-series OSes where there is no access +# control anyway). +# +# - COMPAT=-DNO_MULTIMON +# Disables PuTTY's use of , which is not available +# with some development environments. This means that PuTTY's +# full-screen mode (configurable to work on Alt-Enter) will +# not behave usefully in a multi-monitor environment. +# +# Note that this definition is always enabled in the Cygwin +# build, since at the time of writing this is +# known not to be available in Cygwin. +# +# - COMPAT=-DNO_IPV6 +# Disables PuTTY's ability to make IPv6 connections, enabling +# it to compile under development environments which do not +# support IPv6 in their header files. +# +# - COMPAT=-DMSVC4 +# - RCFL=-DMSVC4 +# Makes a couple of minor changes so that PuTTY compiles using +# MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON. +# +# - RCFL=-DASCIICTLS +# Uses ASCII rather than Unicode to specify the tab control in +# the resource file. Probably most useful when compiling with +# Cygnus/mingw32, whose resource compiler may have less of a +# problem with it. +# +# - XFLAGS=-DTELNET_DEFAULT +# Causes PuTTY to default to the Telnet protocol (in the absence +# of Default Settings and so on to the contrary). Normally PuTTY +# will default to SSH. +# +# - XFLAGS=-DDEBUG +# Causes PuTTY to enable internal debugging. +# +# - XFLAGS=-DMALLOC_LOG +# Causes PuTTY to emit a file called putty_mem.log, logging every +# memory allocation and free, so you can track memory leaks. +# +# - XFLAGS=-DMINEFIELD +# Causes PuTTY to use a custom memory allocator, similar in +# concept to Electric Fence, in place of regular malloc(). Wastes +# huge amounts of RAM, but should cause heap-corruption bugs to +# show up as GPFs at the point of failure rather than appearing +# later on as second-level damage. +# +CC = $(TOOLPATH)gcc + +CFLAGS = -O2 -Wall -Werror -g -I.././ -I../charset/ -I../windows/ -I../unix/ \ + -I../mac/ -I../macosx/ +MLDFLAGS = -framework Cocoa +ULDFLAGS = +all: PuTTY plink pscp psftp puttygen +CFLAGS += -DMACOSX + +PuTTY.app: + mkdir -p $@ +PuTTY.app/Contents: PuTTY.app + mkdir -p $@ +PuTTY.app/Contents/MacOS: PuTTY.app/Contents + mkdir -p $@ +PuTTY: PuTTY.app/Contents/MacOS/PuTTY $(PuTTY_extra) + +PuTTY.app/Contents/MacOS/PuTTY: PuTTY.app/Contents/MacOS be_all.o config.o \ + cproxy.o dialog.o fromucs.o ldisc.o ldiscucs.o localenc.o \ + logging.o macenc.o mimeenc.o minibidi.o misc.o osxctrls.o \ + osxdlg.o osxmain.o osxsel.o osxwin.o pinger.o portfwd.o \ + proxy.o raw.o rlogin.o sbcs.o sbcsdat.o settings.o slookup.o \ + ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o sshcrcda.o \ + sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o sshrand.o \ + sshrsa.o sshsh512.o sshsha.o sshzlib.o telnet.o terminal.o \ + testback.o time.o timing.o toucs.o tree234.o utf8.o ux_x11.o \ + uxagentc.o uxcfg.o uxmisc.o uxnet.o uxnoise.o uxprint.o \ + uxproxy.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ + version.o wcwidth.o wildcard.o x11fwd.o xenc.o + $(CC) $(MLDFLAGS) -o $@ be_all.o config.o cproxy.o dialog.o \ + fromucs.o ldisc.o ldiscucs.o localenc.o logging.o macenc.o \ + mimeenc.o minibidi.o misc.o osxctrls.o osxdlg.o osxmain.o \ + osxsel.o osxwin.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + sbcs.o sbcsdat.o settings.o slookup.o ssh.o sshaes.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o terminal.o testback.o time.o \ + timing.o toucs.o tree234.o utf8.o ux_x11.o uxagentc.o \ + uxcfg.o uxmisc.o uxnet.o uxnoise.o uxprint.o uxproxy.o \ + uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o version.o \ + wcwidth.o wildcard.o x11fwd.o xenc.o + +plink: be_all.o cmdline.o cproxy.o ldisc.o logging.o misc.o pinger.o \ + portfwd.o proxy.o raw.o rlogin.o settings.o ssh.o sshaes.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \ + sshsha.o sshzlib.o telnet.o time.o timing.o tree234.o \ + ux_x11.o uxagentc.o uxcons.o uxmisc.o uxnet.o uxnoise.o \ + uxplink.o uxproxy.o uxsel.o uxsignal.o uxstore.o version.o \ + wildcard.o x11fwd.o + $(CC) $(ULDFLAGS) -o $@ be_all.o cmdline.o cproxy.o ldisc.o \ + logging.o misc.o pinger.o portfwd.o proxy.o raw.o rlogin.o \ + settings.o ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o \ + sshrand.o sshrsa.o sshsh512.o sshsha.o sshzlib.o telnet.o \ + time.o timing.o tree234.o ux_x11.o uxagentc.o uxcons.o \ + uxmisc.o uxnet.o uxnoise.o uxplink.o uxproxy.o uxsel.o \ + uxsignal.o uxstore.o version.o wildcard.o x11fwd.o + +pscp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pinger.o \ + portfwd.o proxy.o pscp.o settings.o sftp.o ssh.o sshaes.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \ + sshsha.o sshzlib.o time.o timing.o tree234.o uxagentc.o \ + uxcons.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ + uxsftp.o uxstore.o version.o wildcard.o x11fwd.o + $(CC) $(ULDFLAGS) -o $@ be_none.o cmdline.o cproxy.o int64.o \ + logging.o misc.o pinger.o portfwd.o proxy.o pscp.o \ + settings.o sftp.o ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o \ + sshrand.o sshrsa.o sshsh512.o sshsha.o sshzlib.o time.o \ + timing.o tree234.o uxagentc.o uxcons.o uxmisc.o uxnet.o \ + uxnoise.o uxproxy.o uxsel.o uxsftp.o uxstore.o version.o \ + wildcard.o x11fwd.o + +psftp: be_none.o cmdline.o cproxy.o int64.o logging.o misc.o pinger.o \ + portfwd.o proxy.o psftp.o settings.o sftp.o ssh.o sshaes.o \ + sshblowf.o sshbn.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ + sshdss.o sshmd5.o sshpubk.o sshrand.o sshrsa.o sshsh512.o \ + sshsha.o sshzlib.o time.o timing.o tree234.o uxagentc.o \ + uxcons.o uxmisc.o uxnet.o uxnoise.o uxproxy.o uxsel.o \ + uxsftp.o uxstore.o version.o wildcard.o x11fwd.o + $(CC) $(ULDFLAGS) -o $@ be_none.o cmdline.o cproxy.o int64.o \ + logging.o misc.o pinger.o portfwd.o proxy.o psftp.o \ + settings.o sftp.o ssh.o sshaes.o sshblowf.o sshbn.o sshcrc.o \ + sshcrcda.o sshdes.o sshdh.o sshdss.o sshmd5.o sshpubk.o \ + sshrand.o sshrsa.o sshsh512.o sshsha.o sshzlib.o time.o \ + timing.o tree234.o uxagentc.o uxcons.o uxmisc.o uxnet.o \ + uxnoise.o uxproxy.o uxsel.o uxsftp.o uxstore.o version.o \ + wildcard.o x11fwd.o + +puttygen: cmdgen.o import.o misc.o notiming.o sshaes.o sshbn.o sshdes.o \ + sshdss.o sshdssg.o sshmd5.o sshprime.o sshpubk.o sshrand.o \ + sshrsa.o sshrsag.o sshsh512.o sshsha.o time.o tree234.o \ + uxcons.o uxgen.o uxmisc.o uxnoise.o uxstore.o version.o + $(CC) $(ULDFLAGS) -o $@ cmdgen.o import.o misc.o notiming.o sshaes.o \ + sshbn.o sshdes.o sshdss.o sshdssg.o sshmd5.o sshprime.o \ + sshpubk.o sshrand.o sshrsa.o sshrsag.o sshsh512.o sshsha.o \ + time.o tree234.o uxcons.o uxgen.o uxmisc.o uxnoise.o \ + uxstore.o version.o + +be_all.o: ../be_all.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +be_none.o: ../be_none.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +be_nossh.o: ../be_nossh.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ + ../misc.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +cmdline.o: ../cmdline.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ + ../puttyps.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +dialog.o: ../dialog.c ../putty.h ../dialog.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +gtkcols.o: ../unix/gtkcols.c ../unix/gtkcols.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +gtkdlg.o: ../unix/gtkdlg.c ../unix/gtkcols.h ../unix/gtkpanel.h ../putty.h \ + ../storage.h ../dialog.h ../tree234.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +gtkpanel.o: ../unix/gtkpanel.c ../unix/gtkpanel.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../puttyps.h \ + ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +import.o: ../import.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ + ../network.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +int64.o: ../int64.c ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ + ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +ldiscucs.o: ../ldiscucs.c ../putty.h ../terminal.h ../ldisc.h ../puttyps.h \ + ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +logging.o: ../logging.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +mac.o: ../mac/mac.c ../mac/macresid.h ../putty.h ../ssh.h ../terminal.h \ + ../mac/mac.h ../puttyps.h ../network.h ../misc.h \ + ../puttymem.h ../int64.h ../tree234.h ../charset/charset.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macabout.o: ../mac/macabout.c ../putty.h ../mac/mac.h ../mac/macresid.h \ + ../puttyps.h ../network.h ../misc.h ../charset/charset.h \ + ../tree234.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macctrls.o: ../mac/macctrls.c ../putty.h ../mac/mac.h ../mac/macresid.h \ + ../dialog.h ../tree234.h ../puttyps.h ../network.h ../misc.h \ + ../charset/charset.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macdlg.o: ../mac/macdlg.c ../putty.h ../dialog.h ../mac/mac.h \ + ../mac/macresid.h ../storage.h ../puttyps.h ../network.h \ + ../misc.h ../charset/charset.h ../tree234.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macevlog.o: ../mac/macevlog.c ../putty.h ../mac/mac.h ../mac/macresid.h \ + ../terminal.h ../puttyps.h ../network.h ../misc.h \ + ../charset/charset.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macmisc.o: ../mac/macmisc.c ../putty.h ../mac/mac.h ../ssh.h ../puttyps.h \ + ../network.h ../misc.h ../charset/charset.h ../tree234.h \ + ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macnet.o: ../mac/macnet.c ../putty.h ../network.h ../mac/mac.h ../ssh.h \ + ../puttyps.h ../misc.h ../charset/charset.h ../tree234.h \ + ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macnoise.o: ../mac/macnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macpgen.o: ../mac/macpgen.c ../mac/macpgrid.h ../putty.h ../ssh.h \ + ../mac/mac.h ../puttyps.h ../network.h ../misc.h \ + ../puttymem.h ../int64.h ../charset/charset.h ../tree234.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macpgkey.o: ../mac/macpgkey.c ../putty.h ../mac/mac.h ../mac/macpgrid.h \ + ../ssh.h ../puttyps.h ../network.h ../misc.h \ + ../charset/charset.h ../tree234.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macstore.o: ../mac/macstore.c ../putty.h ../storage.h ../mac/mac.h \ + ../mac/macresid.h ../puttyps.h ../network.h ../misc.h \ + ../charset/charset.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macterm.o: ../mac/macterm.c ../mac/macresid.h ../putty.h \ + ../charset/charset.h ../mac/mac.h ../terminal.h ../puttyps.h \ + ../network.h ../misc.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +macucs.o: ../mac/macucs.c ../putty.h ../terminal.h ../misc.h ../mac/mac.h \ + ../puttyps.h ../network.h ../tree234.h ../puttymem.h \ + ../charset/charset.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +minibidi.o: ../minibidi.c ../misc.h ../puttymem.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +misc.o: ../misc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +mtcpnet.o: ../mac/mtcpnet.c ../putty.h ../network.h ../mac/mac.h \ + ../puttyps.h ../misc.h ../charset/charset.h ../tree234.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +notiming.o: ../notiming.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +osxctrls.o: ../macosx/osxctrls.m ../putty.h ../dialog.h ../macosx/osxclass.h \ + ../tree234.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) -x objective-c $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +osxdlg.o: ../macosx/osxdlg.m ../putty.h ../storage.h ../dialog.h \ + ../macosx/osxclass.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) -x objective-c $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +osxmain.o: ../macosx/osxmain.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) -x objective-c $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +osxsel.o: ../macosx/osxsel.m ../putty.h ../macosx/osxclass.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) -x objective-c $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +osxwin.o: ../macosx/osxwin.m ../putty.h ../terminal.h ../macosx/osxclass.h \ + ../puttyps.h ../network.h ../misc.h ../tree234.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) -x objective-c $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +otnet.o: ../mac/otnet.c ../putty.h ../network.h ../mac/mac.h ../puttyps.h \ + ../misc.h ../charset/charset.h ../tree234.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +pinger.o: ../pinger.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ + ../misc.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +pproxy.o: ../pproxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../puttyps.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ + ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ + ../int64.h ../puttyps.h ../network.h ../misc.h ../puttymem.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +raw.o: ../raw.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +rlogin.o: ../rlogin.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +settings.o: ../settings.c ../putty.h ../storage.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sftp.o: ../sftp.c ../misc.h ../int64.h ../tree234.h ../sftp.h ../puttymem.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sizetip.o: ../windows/sizetip.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ + ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +ssh.o: ../ssh.c ../putty.h ../tree234.h ../ssh.h ../puttyps.h ../network.h \ + ../misc.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshaes.o: ../sshaes.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshblowf.o: ../sshblowf.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshbn.o: ../sshbn.c ../misc.h ../ssh.h ../puttymem.h ../network.h ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../puttymem.h ../network.h \ + ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshdes.o: ../sshdes.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshdh.o: ../sshdh.c ../ssh.h ../puttymem.h ../network.h ../int64.h ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshdss.o: ../sshdss.c ../ssh.h ../misc.h ../puttymem.h ../network.h \ + ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../puttymem.h ../network.h \ + ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshprime.o: ../sshprime.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshpubk.o: ../sshpubk.c ../putty.h ../ssh.h ../misc.h ../puttyps.h \ + ../network.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ + ../misc.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshrsa.o: ../sshrsa.c ../ssh.h ../misc.h ../puttymem.h ../network.h \ + ../int64.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshrsag.o: ../sshrsag.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +sshzlib.o: ../sshzlib.c ../ssh.h ../puttymem.h ../network.h ../int64.h \ + ../misc.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +stricmp.o: ../mac/stricmp.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +telnet.o: ../telnet.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +terminal.o: ../terminal.c ../putty.h ../terminal.h ../puttyps.h ../network.h \ + ../misc.h ../tree234.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +testback.o: ../testback.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +time.o: ../time.c + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +timing.o: ../timing.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +tree234.o: ../tree234.c ../puttymem.h ../tree234.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../puttyps.h ../network.h \ + ../misc.h ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ + ../puttymem.h ../puttyps.h ../network.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../puttyps.h \ + ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxgen.o: ../unix/uxgen.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxmisc.o: ../unix/uxmisc.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../puttyps.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxplink.o: ../unix/uxplink.c ../putty.h ../storage.h ../tree234.h \ + ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxprint.o: ../unix/uxprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ + ../puttyps.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxpterm.o: ../unix/uxpterm.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxpty.o: ../unix/uxpty.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxputty.o: ../unix/uxputty.c ../putty.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxsftp.o: ../unix/uxsftp.c ../putty.h ../psftp.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxsignal.o: ../unix/uxsignal.c + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h \ + ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ + ../misc.h ../puttyps.h ../network.h ../tree234.h \ + ../puttymem.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../windows/winhelp.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +version.o: ../version.c + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +vsnprint.o: ../mac/vsnprint.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +wcwidth.o: ../wcwidth.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +wildcard.o: ../wildcard.c ../putty.h ../puttyps.h ../network.h ../misc.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h \ + ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ + ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h \ + ../puttyps.h ../network.h ../puttymem.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +windefs.o: ../windows/windefs.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ + ../storage.h ../dialog.h ../puttyps.h ../network.h ../misc.h \ + ../puttymem.h ../int64.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../tree234.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ + ../windows/win_res.h ../puttyps.h ../network.h ../misc.h \ + ../tree234.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winmisc.o: ../windows/winmisc.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h \ + ../puttyps.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h \ + ../puttyps.h ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../puttyps.h \ + ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ + ../puttyps.h ../network.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winpgntc.o: ../windows/winpgntc.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ + ../puttyps.h ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winprint.o: ../windows/winprint.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../puttyps.h \ + ../network.h ../misc.h ../windows/winstuff.h \ + ../mac/macstuff.h ../macosx/osx.h ../unix/unix.h \ + ../puttymem.h ../tree234.h ../windows/winhelp.h \ + ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +wintime.o: ../windows/wintime.c ../putty.h ../puttyps.h ../network.h \ + ../misc.h ../windows/winstuff.h ../mac/macstuff.h \ + ../macosx/osx.h ../unix/unix.h ../puttymem.h ../tree234.h \ + ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h \ + ../puttyps.h ../network.h ../tree234.h ../puttymem.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +winutils.o: ../windows/winutils.c ../misc.h ../puttymem.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../tree234.h ../puttyps.h \ + ../network.h ../misc.h ../puttymem.h ../int64.h \ + ../windows/winstuff.h ../mac/macstuff.h ../macosx/osx.h \ + ../unix/unix.h ../windows/winhelp.h ../charset/charset.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< +xkeysym.o: ../unix/xkeysym.c ../misc.h ../puttymem.h + $(CC) $(COMPAT) $(FWHACK) $(XFLAGS) $(CFLAGS) -c $< + +clean: + rm -f *.o *.dmg + rm -rf *.app diff --git a/macosx/README.OSX b/macosx/README.OSX new file mode 100644 index 00000000..9dded051 --- /dev/null +++ b/macosx/README.OSX @@ -0,0 +1,78 @@ +This directory contains a Mac OS X port of PuTTY/pterm, running as a +native Aqua GUI application. + +THIS PORT IS CURRENTLY UNFINISHED AND EXPERIMENTAL. You are welcome +to use it, but don't be surprised at unexpected behaviour. I'm not +kidding. + +In particular, I have not yet decided where OS X PuTTY should store +its configuration data. Options include storing it in ~/.putty to be +compatible with Unix PuTTY, storing it wherever is compatible with +Mac Classic PuTTY, storing it in a natively OS X location, or +sorting out the `config-locations' wishlist item and doing all +three. Therefore, if you start using this port and create a whole +load of saved sessions, you should not be surprised if a future +version of the port decides to look somewhere completely different +for the data and therefore loses them all. If that happens, don't +say you weren't warned! + +Even more importantly, the alert box that confirms host keys is not +yet implemented, and the application will bomb out and exit if it +should be needed. This means you cannot make an SSH connection to a +new host using the GUI PuTTY in this port: you must first run +`plink' (which should be exactly identical to the version in the +Unix port) and tell it to confirm the host key. + +Other ways in which the port is currently unfinished include: + + - terminal display is horribly slow + + - fatal errors are currently output via printf, which is obviously + wrong for a GUI application + + - fonts aren't configurable + + - several features are unimplemented in the terminal display: + underlining, non-solid-block cursors, double-width and + double-height line attributes, bold as font rather than as + colour, wide (CJK) characters, combining characters. + + - there's no scrollbar + + - terminal window resizing isn't implemented yet + + - proper window placement (cascading down and right from the + starting position, plus remembering previous window positions per + the Apple HIG) is not implemented + + - close-on-exit isn't implemented + + - warn-on-close isn't implemented + + - SessionWindow's dealloc method does nothing yet, so leaks memory + + - use of Alt+numberpad to enter arbitrary numeric character codes + is not yet supported + + - cut and paste isn't supported + + - there's no Meta key yet. (I think it will have to be Command + rather than Option since the latter is necessary to send some + characters, including the rather important # on Apple UK + keyboards; but trapping Command- and sending it to the + window rather than the application menu requires me to make a + positive effort of some sort and I haven't got round to it yet.) + + - there's no specials menu + + - currently no support for server-side window management requests + (i.e. escape sequences to minimise or maximise the window, + request or change its position and size, change its title etc) + + - window title is currently fixed + + - no Event Log + + - no mid-session Change Settings + + - no icon (surprisingly important in an OS X app!) diff --git a/macosx/osx.h b/macosx/osx.h new file mode 100644 index 00000000..165539fb --- /dev/null +++ b/macosx/osx.h @@ -0,0 +1,34 @@ +#ifndef PUTTY_OSX_H +#define PUTTY_OSX_H + +/* + * Cocoa defines `FontSpec' itself, so we must change its name. + * (Arrgh.) + */ +#define FontSpec FontSpec_OSX_Proof + +/* + * Define the various compatibility symbols to make uxpty.c compile + * correctly on OS X. + */ +#define BSD_PTYS +#define OMIT_UTMP +#define HAVE_NO_SETRESUID +#define NOT_X_WINDOWS + +/* + * OS X is largely just Unix, so we can include most of this + * unchanged. + */ +#include "unix.h" + +/* + * Functions exported by osxsel.m. (Both of these functions are + * expected to be called in the _main_ thread: the select subthread + * is an implementation detail of osxsel.m and ideally should not + * be visible at all outside it.) + */ +void osxsel_init(void); /* call this to kick things off */ +void osxsel_process_results(void); /* call this on receipt of a netevent */ + +#endif diff --git a/macosx/osxclass.h b/macosx/osxclass.h new file mode 100644 index 00000000..5f009a91 --- /dev/null +++ b/macosx/osxclass.h @@ -0,0 +1,86 @@ +/* + * Header file for the Objective-C parts of Mac OS X PuTTY. This + * file contains the class definitions, which would cause compile + * failures in the pure C modules if they appeared in osx.h. + */ + +#ifndef PUTTY_OSXCLASS_H +#define PUTTY_OSXCLASS_H + +#include "putty.h" + +/* + * The application controller class, defined in osxmain.m. + */ +@interface AppController : NSObject +{ + NSTimer *timer; +} +- (void)newSessionConfig:(id)sender; +- (void)newTerminal:(id)sender; +- (void)newSessionWithConfig:(id)cfg; +- (void)setTimer:(long)next; +@end +extern AppController *controller; + +/* + * The SessionWindow class, defined in osxwin.m. + */ + +@class SessionWindow; +@class TerminalView; + +@interface SessionWindow : NSWindow +{ + Terminal *term; + TerminalView *termview; + struct unicode_data ucsdata; + void *logctx; + Config cfg; + void *ldisc; + Backend *back; + void *backhandle; +} +- (id)initWithConfig:(Config)cfg; +- (void)drawStartFinish:(BOOL)start; +- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b; +- (Config *)cfg; +- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y + attr:(unsigned long)attr lattr:(int)lattr; +- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr; +@end + +/* + * The ConfigWindow class, defined in osxdlg.m. + */ + +@class ConfigWindow; + +@interface ConfigWindow : NSWindow +{ + NSOutlineView *treeview; + struct controlbox *ctrlbox; + struct sesslist sl; + void *dv; + Config cfg; +} +- (id)initWithConfig:(Config)cfg; +@end + +/* + * Functions exported by osxctrls.m. (They have to go in this + * header file and not osx.h, because some of them have Cocoa class + * types in their prototypes.) + */ +#define HSPACING 12 /* needed in osxdlg.m and osxctrls.m */ +#define VSPACING 8 + +void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action); +void fe_dlg_free(void *dv); +void create_ctrls(void *dv, NSView *parent, struct controlset *s, + int *minw, int *minh); +int place_ctrls(void *dv, struct controlset *s, int leftx, int topy, + int width); /* returns height used */ +void select_panel(void *dv, struct controlbox *b, const char *name); + +#endif /* PUTTY_OSXCLASS_H */ diff --git a/macosx/osxctrls.m b/macosx/osxctrls.m new file mode 100644 index 00000000..e3780ff6 --- /dev/null +++ b/macosx/osxctrls.m @@ -0,0 +1,1804 @@ +/* + * osxctrls.m: OS X implementation of the dialog.h interface. + */ + +#import +#include "putty.h" +#include "dialog.h" +#include "osxclass.h" +#include "tree234.h" + +/* + * Still to be implemented: + * + * - file selectors (NSOpenPanel / NSSavePanel) + * + * - font selectors + * - colour selectors + * * both of these have a conceptual oddity in Cocoa that + * you're only supposed to have one per application. But I + * currently expect to be able to have multiple PuTTY config + * boxes on screen at once; what happens if you trigger the + * font selector in each one at the same time? + * * if it comes to that, the _font_ selector can probably be + * managed by other means: nobody is forcing me to implement + * a font selector using a `Change...' button. The portable + * dialog interface gives me the flexibility to do this how I + * want. + * * The colour selector interface, in its present form, is + * more interesting and _if_ a radical change of plan is + * required then it may stretch across the interface into the + * portable side. + * * Before I do anything rash I should start by looking at the + * Mac Classic port and see how it's done there, on the basis + * that Apple seem reasonably unlikely to have invented this + * crazy restriction specifically for OS X. + * + * - focus management + * * I tried using makeFirstResponder to give keyboard focus, + * but it appeared not to work. Try again, and work out how + * it should be done. + * * also look into tab order. Currently pressing Tab suggests + * that only edit boxes and list boxes can get the keyboard + * focus, and that buttons (in all their forms) are unable to + * be driven by the keyboard. Find out for sure. + * + * - dlg_error_msg + * * this may run into the usual aggro with modal dialog boxes. + */ + +/* + * For Cocoa control layout, I need a two-stage process. In stage + * one, I allocate all the controls and measure their natural + * sizes, which allows me to compute the _minimum_ width and height + * of a given section of dialog. Then, in stage two, I lay out the + * dialog box as a whole, decide how much each section of the box + * needs to receive, and assign it its final size. + */ + +/* + * As yet unsolved issues [FIXME]: + * + * - Sometimes the height returned from create_ctrls and the + * height returned from place_ctrls differ. Find out why. It may + * be harmless (e.g. results of NSTextView being odd), but I + * want to know. + * + * - NSTextViews are indented a bit. It'd be nice to put their + * left margin at the same place as everything else's. + * + * - I don't yet know whether we even _can_ support tab order or + * keyboard shortcuts. If we can't, then fair enough, we can't. + * But if we can, we should. + * + * - I would _really_ like to know of a better way to correct + * NSButton's stupid size estimates than by subclassing it and + * overriding sizeToFit with hard-wired sensible values! + * + * - Speaking of stupid size estimates, the amount by which I'm + * adjusting a titled NSBox (currently equal to the point size + * of its title font) looks as if it isn't _quite_ enough. + * Figure out what the real amount should be and use it. + * + * - I don't understand why there's always a scrollbar displayed + * in each list box. I thought I told it to autohide scrollers? + * + * - Why do I have to fudge list box heights by adding one? (Might + * it be to do with the missing header view?) + */ + +/* + * Subclass of NSButton which corrects the fact that the normal + * one's sizeToFit method persistently returns 32 as its height, + * which is simply a lie. I have yet to work out a better + * alternative than hard-coding the real heights. + */ +@interface MyButton : NSButton +{ + int minht; +} +@end +@implementation MyButton +- (id)initWithFrame:(NSRect)r +{ + self = [super initWithFrame:r]; + minht = 25; + return self; +} +- (void)setButtonType:(NSButtonType)t +{ + if (t == NSRadioButton || t == NSSwitchButton) + minht = 18; + else + minht = 25; + [super setButtonType:t]; +} +- (void)sizeToFit +{ + NSRect r; + [super sizeToFit]; + r = [self frame]; + r.size.height = minht; + [self setFrame:r]; +} +@end + +/* + * Class used as the data source for NSTableViews. + */ +@interface MyTableSource : NSObject +{ + tree234 *tree; +} +- (id)init; +- (void)add:(const char *)str withId:(int)id; +- (int)getid:(int)index; +- (void)swap:(int)index1 with:(int)index2; +- (void)removestr:(int)index; +- (void)clear; +@end +@implementation MyTableSource +- (id)init +{ + self = [super init]; + tree = newtree234(NULL); + return self; +} +- (void)dealloc +{ + char *p; + while ((p = delpos234(tree, 0)) != NULL) + sfree(p); + freetree234(tree); + [super dealloc]; +} +- (void)add:(const char *)str withId:(int)id +{ + addpos234(tree, dupprintf("%d\t%s", id, str), count234(tree)); +} +- (int)getid:(int)index +{ + char *p = index234(tree, index); + return atoi(p); +} +- (void)removestr:(int)index +{ + char *p = delpos234(tree, index); + sfree(p); +} +- (void)swap:(int)index1 with:(int)index2 +{ + char *p1, *p2; + + if (index1 > index2) { + int t = index1; index1 = index2; index2 = t; + } + + /* delete later one first so it doesn't affect index of earlier one */ + p2 = delpos234(tree, index2); + p1 = delpos234(tree, index1); + + /* now insert earlier one before later one for the inverse reason */ + addpos234(tree, p2, index1); + addpos234(tree, p1, index2); +} +- (void)clear +{ + char *p; + while ((p = delpos234(tree, 0)) != NULL) + sfree(p); +} +- (int)numberOfRowsInTableView:(NSTableView *)aTableView +{ + return count234(tree); +} +- (id)tableView:(NSTableView *)aTableView + objectValueForTableColumn:(NSTableColumn *)aTableColumn + row:(int)rowIndex +{ + int j = [[aTableColumn identifier] intValue]; + char *p = index234(tree, rowIndex); + + while (j >= 0) { + p += strcspn(p, "\t"); + if (*p) p++; + j--; + } + + return [NSString stringWithCString:p length:strcspn(p, "\t")]; +} +@end + +/* + * Object to receive messages from various control classes. + */ +@class Receiver; + +struct fe_dlg { + NSWindow *window; + NSObject *target; + SEL action; + tree234 *byctrl; + tree234 *bywidget; + tree234 *boxes; + void *data; /* passed to portable side */ + Receiver *rec; +}; + +@interface Receiver : NSObject +{ + struct fe_dlg *d; +} +- (id)initWithStruct:(struct fe_dlg *)aStruct; +@end + +struct fe_ctrl { + union control *ctrl; + NSButton *button, *button2; + NSTextField *label, *editbox; + NSComboBox *combobox; + NSButton **radiobuttons; + NSTextView *textview; + NSPopUpButton *popupbutton; + NSTableView *tableview; + NSScrollView *scrollview; + int nradiobuttons; + void *privdata; + int privdata_needs_free; +}; + +static int fe_ctrl_cmp_by_ctrl(void *av, void *bv) +{ + struct fe_ctrl *a = (struct fe_ctrl *)av; + struct fe_ctrl *b = (struct fe_ctrl *)bv; + + if (a->ctrl < b->ctrl) + return -1; + if (a->ctrl > b->ctrl) + return +1; + return 0; +} + +static int fe_ctrl_find_by_ctrl(void *av, void *bv) +{ + union control *a = (union control *)av; + struct fe_ctrl *b = (struct fe_ctrl *)bv; + + if (a < b->ctrl) + return -1; + if (a > b->ctrl) + return +1; + return 0; +} + +struct fe_box { + struct controlset *s; + id box; +}; + +static int fe_boxcmp(void *av, void *bv) +{ + struct fe_box *a = (struct fe_box *)av; + struct fe_box *b = (struct fe_box *)bv; + + if (a->s < b->s) + return -1; + if (a->s > b->s) + return +1; + return 0; +} + +static int fe_boxfind(void *av, void *bv) +{ + struct controlset *a = (struct controlset *)av; + struct fe_box *b = (struct fe_box *)bv; + + if (a < b->s) + return -1; + if (a > b->s) + return +1; + return 0; +} + +struct fe_backwards { /* map Cocoa widgets back to fe_ctrls */ + id widget; + struct fe_ctrl *c; +}; + +static int fe_backwards_cmp_by_widget(void *av, void *bv) +{ + struct fe_backwards *a = (struct fe_backwards *)av; + struct fe_backwards *b = (struct fe_backwards *)bv; + + if (a->widget < b->widget) + return -1; + if (a->widget > b->widget) + return +1; + return 0; +} + +static int fe_backwards_find_by_widget(void *av, void *bv) +{ + id a = (id)av; + struct fe_backwards *b = (struct fe_backwards *)bv; + + if (a < b->widget) + return -1; + if (a > b->widget) + return +1; + return 0; +} + +static struct fe_ctrl *fe_ctrl_new(union control *ctrl) +{ + struct fe_ctrl *c; + + c = snew(struct fe_ctrl); + c->ctrl = ctrl; + + c->button = c->button2 = nil; + c->label = nil; + c->editbox = nil; + c->combobox = nil; + c->textview = nil; + c->popupbutton = nil; + c->tableview = nil; + c->scrollview = nil; + c->radiobuttons = NULL; + c->nradiobuttons = 0; + c->privdata = NULL; + c->privdata_needs_free = FALSE; + + return c; +} + +static void fe_ctrl_free(struct fe_ctrl *c) +{ + if (c->privdata_needs_free) + sfree(c->privdata); + sfree(c->radiobuttons); + sfree(c); +} + +static struct fe_ctrl *fe_ctrl_byctrl(struct fe_dlg *d, union control *ctrl) +{ + return find234(d->byctrl, ctrl, fe_ctrl_find_by_ctrl); +} + +static void add_box(struct fe_dlg *d, struct controlset *s, id box) +{ + struct fe_box *b = snew(struct fe_box); + b->box = box; + b->s = s; + add234(d->boxes, b); +} + +static id find_box(struct fe_dlg *d, struct controlset *s) +{ + struct fe_box *b = find234(d->boxes, s, fe_boxfind); + return b ? b->box : NULL; +} + +static void add_widget(struct fe_dlg *d, struct fe_ctrl *c, id widget) +{ + struct fe_backwards *b = snew(struct fe_backwards); + b->widget = widget; + b->c = c; + add234(d->bywidget, b); +} + +static struct fe_ctrl *find_widget(struct fe_dlg *d, id widget) +{ + struct fe_backwards *b = find234(d->bywidget, widget, + fe_backwards_find_by_widget); + return b ? b->c : NULL; +} + +void *fe_dlg_init(void *data, NSWindow *window, NSObject *target, SEL action) +{ + struct fe_dlg *d; + + d = snew(struct fe_dlg); + d->window = window; + d->target = target; + d->action = action; + d->byctrl = newtree234(fe_ctrl_cmp_by_ctrl); + d->bywidget = newtree234(fe_backwards_cmp_by_widget); + d->boxes = newtree234(fe_boxcmp); + d->data = data; + d->rec = [[Receiver alloc] initWithStruct:d]; + + return d; +} + +void fe_dlg_free(void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c; + struct fe_box *b; + + while ( (c = delpos234(d->byctrl, 0)) != NULL ) + fe_ctrl_free(c); + freetree234(d->byctrl); + + while ( (c = delpos234(d->bywidget, 0)) != NULL ) + sfree(c); + freetree234(d->bywidget); + + while ( (b = delpos234(d->boxes, 0)) != NULL ) + sfree(b); + freetree234(d->boxes); + + [d->rec release]; + + sfree(d); +} + +@implementation Receiver +- (id)initWithStruct:(struct fe_dlg *)aStruct +{ + self = [super init]; + d = aStruct; + return self; +} +- (void)buttonPushed:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + + assert(c && c->ctrl->generic.type == CTRL_BUTTON); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION); +} +- (void)checkboxChanged:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + + assert(c && c->ctrl->generic.type == CTRL_CHECKBOX); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE); +} +- (void)radioChanged:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + int j; + + assert(c && c->radiobuttons); + for (j = 0; j < c->nradiobuttons; j++) + if (sender != c->radiobuttons[j]) + [c->radiobuttons[j] setState:NSOffState]; + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE); +} +- (void)popupMenuSelected:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE); +} +- (void)controlTextDidChange:(NSNotification *)notification +{ + id widget = [notification object]; + struct fe_ctrl *c = find_widget(d, widget); + assert(c && c->ctrl->generic.type == CTRL_EDITBOX); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE); +} +- (void)controlTextDidEndEditing:(NSNotification *)notification +{ + id widget = [notification object]; + struct fe_ctrl *c = find_widget(d, widget); + assert(c && c->ctrl->generic.type == CTRL_EDITBOX); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_REFRESH); +} +- (void)tableViewSelectionDidChange:(NSNotification *)notification +{ + id widget = [notification object]; + struct fe_ctrl *c = find_widget(d, widget); + assert(c && c->ctrl->generic.type == CTRL_LISTBOX); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_SELCHANGE); +} +- (BOOL)tableView:(NSTableView *)aTableView + shouldEditTableColumn:(NSTableColumn *)aTableColumn + row:(int)rowIndex +{ + return NO; /* no editing permitted */ +} +- (void)listDoubleClicked:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + assert(c && c->ctrl->generic.type == CTRL_LISTBOX); + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_ACTION); +} +- (void)dragListButton:(id)sender +{ + struct fe_ctrl *c = find_widget(d, sender); + int direction, row, nrows; + assert(c && c->ctrl->generic.type == CTRL_LISTBOX && + c->ctrl->listbox.draglist); + + if (sender == c->button) + direction = -1; /* up */ + else + direction = +1; /* down */ + + row = [c->tableview selectedRow]; + nrows = [c->tableview numberOfRows]; + + if (row + direction < 0 || row + direction >= nrows) { + NSBeep(); + return; + } + + [[c->tableview dataSource] swap:row with:row+direction]; + [c->tableview reloadData]; + [c->tableview selectRow:row+direction byExtendingSelection:NO]; + + c->ctrl->generic.handler(c->ctrl, d, d->data, EVENT_VALCHANGE); +} +@end + +void create_ctrls(void *dv, NSView *parent, struct controlset *s, + int *minw, int *minh) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + int ccw[100]; /* cumulative column widths */ + int cypos[100]; + int ncols; + int wmin = 0, hmin = 0; + int i, j, cw, ch; + NSRect rect; + NSFont *textviewfont = nil; + int boxh = 0, boxw = 0; + + if (!s->boxname && s->boxtitle) { + /* This controlset is a panel title. */ + + NSTextField *tf; + + tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + [tf setStringValue:[NSString stringWithCString:s->boxtitle]]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + + /* + * I'm going to store this NSTextField in the boxes tree, + * because I really can't face having a special tree234 + * mapping controlsets to panel titles. + */ + add_box(d, s, tf); + + *minw = rect.size.width; + *minh = rect.size.height; + + return; + } + + if (*s->boxname) { + /* + * Create an NSBox to contain this subset of controls. + */ + NSBox *box; + NSRect tmprect; + + box = [[NSBox alloc] initWithFrame:NSMakeRect(0,0,1,1)]; + if (s->boxtitle) + [box setTitle:[NSString stringWithCString:s->boxtitle]]; + else + [box setTitlePosition:NSNoTitle]; + add_box(d, s, box); + tmprect = [box frame]; + [box setContentViewMargins:NSMakeSize(20,20)]; + [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)]; + rect = [box frame]; + [box setFrame:tmprect]; + boxh = (int)(rect.size.height - 100); + boxw = (int)(rect.size.width - 100); + [parent addSubview:box]; + + if (s->boxtitle) + boxh += [[box titleFont] pointSize]; + + /* + * All subsequent controls will be placed within this box. + */ + parent = box; + } + + ncols = 1; + ccw[0] = 0; + ccw[1] = 100; + cypos[0] = 0; + + /* + * Now iterate through the controls themselves, create them, + * and add their width and height to the overall width/height + * calculation. + */ + for (i = 0; i < s->ncontrols; i++) { + union control *ctrl = s->ctrls[i]; + struct fe_ctrl *c; + int colstart = COLUMN_START(ctrl->generic.column); + int colspan = COLUMN_SPAN(ctrl->generic.column); + int colend = colstart + colspan; + int ytop, wthis; + + switch (ctrl->generic.type) { + case CTRL_COLUMNS: + for (j = 1; j < ncols; j++) + if (cypos[0] < cypos[j]) + cypos[0] = cypos[j]; + + assert(ctrl->columns.ncols < lenof(ccw)); + + ccw[0] = 0; + for (j = 0; j < ctrl->columns.ncols; j++) { + ccw[j+1] = ccw[j] + (ctrl->columns.percentages ? + ctrl->columns.percentages[j] : 100); + cypos[j] = cypos[0]; + } + + ncols = ctrl->columns.ncols; + + continue; /* no actual control created */ + case CTRL_TABDELAY: + /* + * I'm currently uncertain that we can implement tab + * order in OS X. + */ + continue; /* no actual control created */ + } + + c = fe_ctrl_new(ctrl); + add234(d->byctrl, c); + + cw = ch = 0; + + switch (ctrl->generic.type) { + case CTRL_BUTTON: + case CTRL_CHECKBOX: + { + NSButton *b; + + b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]; + [b setBezelStyle:NSRoundedBezelStyle]; + if (ctrl->generic.type == CTRL_CHECKBOX) + [b setButtonType:NSSwitchButton]; + [b setTitle:[NSString stringWithCString:ctrl->generic.label]]; + if (ctrl->button.isdefault) + [b setKeyEquivalent:@"\r"]; + else if (ctrl->button.iscancel) + [b setKeyEquivalent:@"\033"]; + [b sizeToFit]; + rect = [b frame]; + + [parent addSubview:b]; + + [b setTarget:d->rec]; + if (ctrl->generic.type == CTRL_CHECKBOX) + [b setAction:@selector(checkboxChanged:)]; + else + [b setAction:@selector(buttonPushed:)]; + add_widget(d, c, b); + + c->button = b; + + cw = rect.size.width; + ch = rect.size.height; + } + break; + case CTRL_EDITBOX: + { + int editp = ctrl->editbox.percentwidth; + int labelp = editp == 100 ? 100 : 100 - editp; + NSTextField *tf; + NSComboBox *cb; + + tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + [tf setStringValue:[NSString + stringWithCString:ctrl->generic.label]]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->label = tf; + + cw = rect.size.width * 100 / labelp; + ch = rect.size.height; + + if (ctrl->editbox.has_list) { + cb = [[NSComboBox alloc] + initWithFrame:NSMakeRect(0,0,1,1)]; + [cb setStringValue:@"x"]; + [cb sizeToFit]; + rect = [cb frame]; + [parent addSubview:cb]; + c->combobox = cb; + } else { + if (ctrl->editbox.password) + tf = [NSSecureTextField alloc]; + else + tf = [NSTextField alloc]; + + tf = [tf initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:YES]; + [tf setSelectable:YES]; + [tf setBordered:YES]; + [tf setStringValue:@"x"]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->editbox = tf; + + [tf setDelegate:d->rec]; + add_widget(d, c, tf); + } + + if (editp == 100) { + /* the edit box and its label are vertically separated */ + ch += VSPACING + rect.size.height; + } else { + /* the edit box and its label are horizontally separated */ + if (ch < rect.size.height) + ch = rect.size.height; + } + + if (cw < rect.size.width * 100 / editp) + cw = rect.size.width * 100 / editp; + } + break; + case CTRL_TEXT: + { + NSTextView *tv; + int testwid; + + if (!textviewfont) { + NSTextField *tf; + tf = [[NSTextField alloc] init]; + textviewfont = [tf font]; + [tf release]; + } + + testwid = (ccw[colend] - ccw[colstart]) * 3; + + tv = [[NSTextView alloc] + initWithFrame:NSMakeRect(0,0,testwid,1)]; + [tv setEditable:NO]; + [tv setSelectable:NO]; + //[tv setBordered:NO]; + [tv setDrawsBackground:NO]; + [tv setFont:textviewfont]; + [tv setString: + [NSString stringWithCString:ctrl->generic.label]]; + rect = [tv frame]; + [tv sizeToFit]; + [parent addSubview:tv]; + c->textview = tv; + + cw = rect.size.width; + ch = rect.size.height; + } + break; + case CTRL_RADIO: + { + NSTextField *tf; + int j; + + if (ctrl->generic.label) { + tf = [[NSTextField alloc] + initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + [tf setStringValue: + [NSString stringWithCString:ctrl->generic.label]]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->label = tf; + + cw = rect.size.width; + ch = rect.size.height; + } else { + cw = 0; + ch = -VSPACING; /* compensate for next advance */ + } + + c->nradiobuttons = ctrl->radio.nbuttons; + c->radiobuttons = snewn(ctrl->radio.nbuttons, NSButton *); + + for (j = 0; j < ctrl->radio.nbuttons; j++) { + NSButton *b; + int ncols; + + b = [[MyButton alloc] initWithFrame:NSMakeRect(0,0,1,1)]; + [b setBezelStyle:NSRoundedBezelStyle]; + [b setButtonType:NSRadioButton]; + [b setTitle:[NSString + stringWithCString:ctrl->radio.buttons[j]]]; + [b sizeToFit]; + rect = [b frame]; + [parent addSubview:b]; + + c->radiobuttons[j] = b; + + [b setTarget:d->rec]; + [b setAction:@selector(radioChanged:)]; + add_widget(d, c, b); + + /* + * Add to the height every time we place a + * button in column 0. + */ + if (j % ctrl->radio.ncolumns == 0) { + ch += rect.size.height + VSPACING; + } + + /* + * Add to the width by working out how many + * columns this button spans. + */ + if (j == ctrl->radio.nbuttons - 1) + ncols = (ctrl->radio.ncolumns - + (j % ctrl->radio.ncolumns)); + else + ncols = 1; + + if (cw < rect.size.width * ctrl->radio.ncolumns / ncols) + cw = rect.size.width * ctrl->radio.ncolumns / ncols; + } + } + break; + case CTRL_FILESELECT: + case CTRL_FONTSELECT: + { + NSTextField *tf; + NSButton *b; + int kh; + + tf = [[NSTextField alloc] initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + [tf setStringValue:[NSString + stringWithCString:ctrl->generic.label]]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->label = tf; + + cw = rect.size.width; + ch = rect.size.height; + + tf = [NSTextField alloc]; + tf = [tf initWithFrame:NSMakeRect(0,0,1,1)]; + if (ctrl->generic.type == CTRL_FILESELECT) { + [tf setEditable:YES]; + [tf setSelectable:YES]; + [tf setBordered:YES]; + } else { + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + } + [tf setStringValue:@"x"]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->editbox = tf; + + kh = rect.size.height; + if (cw < rect.size.width * 4 / 3) + cw = rect.size.width * 4 / 3; + + b = [[MyButton alloc] initWithFrame:NSMakeRect(0, 0, 1, 1)]; + [b setBezelStyle:NSRoundedBezelStyle]; + if (ctrl->generic.type == CTRL_FILESELECT) + [b setTitle:@"Browse..."]; + else + [b setTitle:@"Change..."]; + // [b setKeyEquivalent:somethingorother]; + // [b setTarget:somethingorother]; + // [b setAction:somethingorother]; + [b sizeToFit]; + rect = [b frame]; + [parent addSubview:b]; + + c->button = b; + + if (kh < rect.size.height) + kh = rect.size.height; + ch += VSPACING + kh; + if (cw < rect.size.width * 4) + cw = rect.size.width * 4; + } + break; + case CTRL_LISTBOX: + { + int listp = ctrl->listbox.percentwidth; + int labelp = listp == 100 ? 100 : 100 - listp; + NSTextField *tf; + NSPopUpButton *pb; + NSTableView *tv; + NSScrollView *sv; + + if (ctrl->generic.label) { + tf = [[NSTextField alloc] + initWithFrame:NSMakeRect(0,0,1,1)]; + [tf setEditable:NO]; + [tf setSelectable:NO]; + [tf setBordered:NO]; + [tf setDrawsBackground:NO]; + [tf setStringValue: + [NSString stringWithCString:ctrl->generic.label]]; + [tf sizeToFit]; + rect = [tf frame]; + [parent addSubview:tf]; + c->label = tf; + + cw = rect.size.width; + ch = rect.size.height; + } else { + cw = 0; + ch = -VSPACING; /* compensate for next advance */ + } + + if (ctrl->listbox.height == 0) { + pb = [[NSPopUpButton alloc] + initWithFrame:NSMakeRect(0,0,1,1)]; + [pb sizeToFit]; + rect = [pb frame]; + [parent addSubview:pb]; + c->popupbutton = pb; + + [pb setTarget:d->rec]; + [pb setAction:@selector(popupMenuSelected:)]; + add_widget(d, c, pb); + } else { + assert(listp == 100); + if (ctrl->listbox.draglist) { + int bi; + + listp = 75; + + for (bi = 0; bi < 2; bi++) { + NSButton *b; + b = [[MyButton alloc] + initWithFrame:NSMakeRect(0, 0, 1, 1)]; + [b setBezelStyle:NSRoundedBezelStyle]; + if (bi == 0) + [b setTitle:@"Up"]; + else + [b setTitle:@"Down"]; + [b sizeToFit]; + rect = [b frame]; + [parent addSubview:b]; + + if (bi == 0) + c->button = b; + else + c->button2 = b; + + [b setTarget:d->rec]; + [b setAction:@selector(dragListButton:)]; + add_widget(d, c, b); + + if (cw < rect.size.width * 4) + cw = rect.size.width * 4; + } + } + + sv = [[NSScrollView alloc] initWithFrame: + NSMakeRect(20,20,10,10)]; + [sv setBorderType:NSLineBorder]; + tv = [[NSTableView alloc] initWithFrame:[sv frame]]; + [[tv headerView] setFrame:NSMakeRect(0,0,0,0)]; + [sv setDocumentView:tv]; + [parent addSubview:sv]; + [sv setHasVerticalScroller:YES]; + [sv setAutohidesScrollers:YES]; + [tv setAllowsColumnReordering:NO]; + [tv setAllowsColumnResizing:NO]; + [tv setAllowsMultipleSelection:ctrl->listbox.multisel]; + [tv setAllowsEmptySelection:YES]; + [tv setAllowsColumnSelection:YES]; + [tv setDataSource:[[MyTableSource alloc] init]]; + rect = [tv frame]; + /* + * For some reason this consistently comes out + * one short. Add one. + */ + rect.size.height = (ctrl->listbox.height+1)*[tv rowHeight]; + [sv setFrame:rect]; + c->tableview = tv; + c->scrollview = sv; + + [tv setDelegate:d->rec]; + [tv setTarget:d->rec]; + [tv setDoubleAction:@selector(listDoubleClicked:)]; + add_widget(d, c, tv); + } + + if (c->tableview) { + int ncols, *percentages; + int hundred = 100; + + if (ctrl->listbox.ncols) { + ncols = ctrl->listbox.ncols; + percentages = ctrl->listbox.percentages; + } else { + ncols = 1; + percentages = &hundred; + } + + for (j = 0; j < ncols; j++) { + NSTableColumn *col; + + col = [[NSTableColumn alloc] initWithIdentifier: + [NSNumber numberWithInt:j]]; + [c->tableview addTableColumn:col]; + } + } + + if (labelp == 100) { + /* the list and its label are vertically separated */ + ch += VSPACING + rect.size.height; + } else { + /* the list and its label are horizontally separated */ + if (ch < rect.size.height) + ch = rect.size.height; + } + + if (cw < rect.size.width * 100 / listp) + cw = rect.size.width * 100 / listp; + } + break; + } + + /* + * Update the width and height data for the control we've + * just created. + */ + ytop = 0; + + for (j = colstart; j < colend; j++) { + if (ytop < cypos[j]) + ytop = cypos[j]; + } + + for (j = colstart; j < colend; j++) + cypos[j] = ytop + ch + VSPACING; + + if (hmin < ytop + ch) + hmin = ytop + ch; + + wthis = (cw + HSPACING) * 100 / (ccw[colend] - ccw[colstart]); + wthis -= HSPACING; + + if (wmin < wthis) + wmin = wthis; + } + + if (*s->boxname) { + /* + * Add a bit to the width and height for the box. + */ + wmin += boxw; + hmin += boxh; + } + + //printf("For controlset %s/%s, returning w=%d h=%d\n", + // s->pathname, s->boxname, wmin, hmin); + *minw = wmin; + *minh = hmin; +} + +int place_ctrls(void *dv, struct controlset *s, int leftx, int topy, + int width) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + int ccw[100]; /* cumulative column widths */ + int cypos[100]; + int ncols; + int i, j, ret; + int boxh = 0, boxw = 0; + + if (!s->boxname && s->boxtitle) { + /* Size and place the panel title. */ + + NSTextField *tf = find_box(d, s); + NSRect rect; + + rect = [tf frame]; + [tf setFrame:NSMakeRect(leftx, topy-rect.size.height, + width, rect.size.height)]; + return rect.size.height; + } + + if (*s->boxname) { + NSRect rect, tmprect; + NSBox *box = find_box(d, s); + + assert(box != NULL); + tmprect = [box frame]; + [box setFrameFromContentFrame:NSMakeRect(100,100,100,100)]; + rect = [box frame]; + [box setFrame:tmprect]; + boxw = rect.size.width - 100; + boxh = rect.size.height - 100; + if (s->boxtitle) + boxh += [[box titleFont] pointSize]; + topy -= boxh; + width -= boxw; + } + + ncols = 1; + ccw[0] = 0; + ccw[1] = 100; + cypos[0] = topy; + ret = 0; + + /* + * Now iterate through the controls themselves, placing them + * appropriately. + */ + for (i = 0; i < s->ncontrols; i++) { + union control *ctrl = s->ctrls[i]; + struct fe_ctrl *c; + int colstart = COLUMN_START(ctrl->generic.column); + int colspan = COLUMN_SPAN(ctrl->generic.column); + int colend = colstart + colspan; + int xthis, ythis, wthis, ch; + NSRect rect; + + switch (ctrl->generic.type) { + case CTRL_COLUMNS: + for (j = 1; j < ncols; j++) + if (cypos[0] > cypos[j]) + cypos[0] = cypos[j]; + + assert(ctrl->columns.ncols < lenof(ccw)); + + ccw[0] = 0; + for (j = 0; j < ctrl->columns.ncols; j++) { + ccw[j+1] = ccw[j] + (ctrl->columns.percentages ? + ctrl->columns.percentages[j] : 100); + cypos[j] = cypos[0]; + } + + ncols = ctrl->columns.ncols; + + continue; /* no actual control created */ + case CTRL_TABDELAY: + continue; /* nothing to do here, move along */ + } + + c = fe_ctrl_byctrl(d, ctrl); + + ch = 0; + ythis = topy; + + for (j = colstart; j < colend; j++) { + if (ythis > cypos[j]) + ythis = cypos[j]; + } + + xthis = (width + HSPACING) * ccw[colstart] / 100; + wthis = (width + HSPACING) * ccw[colend] / 100 - HSPACING - xthis; + xthis += leftx; + + switch (ctrl->generic.type) { + case CTRL_BUTTON: + case CTRL_CHECKBOX: + rect = [c->button frame]; + [c->button setFrame:NSMakeRect(xthis,ythis-rect.size.height,wthis, + rect.size.height)]; + ch = rect.size.height; + break; + case CTRL_EDITBOX: + { + int editp = ctrl->editbox.percentwidth; + int labelp = editp == 100 ? 100 : 100 - editp; + int lheight, theight, rheight, ynext, editw; + NSControl *edit = (c->editbox ? c->editbox : c->combobox); + + rect = [c->label frame]; + lheight = rect.size.height; + rect = [edit frame]; + theight = rect.size.height; + + if (editp == 100) + rheight = lheight; + else + rheight = (lheight < theight ? theight : lheight); + + [c->label setFrame: + NSMakeRect(xthis, ythis-(rheight+lheight)/2, + (wthis + HSPACING) * labelp / 100 - HSPACING, + lheight)]; + if (editp == 100) { + ynext = ythis - rheight - VSPACING; + rheight = theight; + } else { + ynext = ythis; + } + + editw = (wthis + HSPACING) * editp / 100 - HSPACING; + + [edit setFrame: + NSMakeRect(xthis+wthis-editw, ynext-(rheight+theight)/2, + editw, theight)]; + + ch = (ythis - ynext) + theight; + } + break; + case CTRL_TEXT: + [c->textview setFrame:NSMakeRect(xthis, 0, wthis, 1)]; + [c->textview sizeToFit]; + rect = [c->textview frame]; + [c->textview setFrame:NSMakeRect(xthis, ythis-rect.size.height, + wthis, rect.size.height)]; + ch = rect.size.height; + break; + case CTRL_RADIO: + { + int j, ynext; + + if (c->label) { + rect = [c->label frame]; + [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height, + wthis,rect.size.height)]; + ynext = ythis - rect.size.height - VSPACING; + } else + ynext = ythis; + + for (j = 0; j < ctrl->radio.nbuttons; j++) { + int col = j % ctrl->radio.ncolumns; + int ncols; + int lx,rx; + + if (j == ctrl->radio.nbuttons - 1) + ncols = ctrl->radio.ncolumns - col; + else + ncols = 1; + + lx = (wthis + HSPACING) * col / ctrl->radio.ncolumns; + rx = ((wthis + HSPACING) * + (col+ncols) / ctrl->radio.ncolumns) - HSPACING; + + /* + * Set the frame size. + */ + rect = [c->radiobuttons[j] frame]; + [c->radiobuttons[j] setFrame: + NSMakeRect(lx+xthis, ynext-rect.size.height, + rx-lx, rect.size.height)]; + + /* + * Advance to next line if we're in the last + * column. + */ + if (col + ncols == ctrl->radio.ncolumns) + ynext -= rect.size.height + VSPACING; + } + ch = (ythis - ynext) - VSPACING; + } + break; + case CTRL_FILESELECT: + case CTRL_FONTSELECT: + { + int ynext, eh, bh, th, mx; + + rect = [c->label frame]; + [c->label setFrame:NSMakeRect(xthis,ythis-rect.size.height, + wthis,rect.size.height)]; + ynext = ythis - rect.size.height - VSPACING; + + rect = [c->editbox frame]; + eh = rect.size.height; + rect = [c->button frame]; + bh = rect.size.height; + th = (eh > bh ? eh : bh); + + mx = (wthis + HSPACING) * 3 / 4 - HSPACING; + + [c->editbox setFrame: + NSMakeRect(xthis, ynext-(th+eh)/2, mx, eh)]; + [c->button setFrame: + NSMakeRect(xthis+mx+HSPACING, ynext-(th+bh)/2, + wthis-mx-HSPACING, bh)]; + + ch = (ythis - ynext) + th + VSPACING; + } + break; + case CTRL_LISTBOX: + { + int listp = ctrl->listbox.percentwidth; + int labelp = listp == 100 ? 100 : 100 - listp; + int lheight, theight, rheight, ynext, listw, xlist; + NSControl *list = (c->scrollview ? (id)c->scrollview : + (id)c->popupbutton); + + if (ctrl->listbox.draglist) { + assert(listp == 100); + listp = 75; + } + + rect = [list frame]; + theight = rect.size.height; + + if (c->label) { + rect = [c->label frame]; + lheight = rect.size.height; + + if (labelp == 100) + rheight = lheight; + else + rheight = (lheight < theight ? theight : lheight); + + [c->label setFrame: + NSMakeRect(xthis, ythis-(rheight+lheight)/2, + (wthis + HSPACING) * labelp / 100 - HSPACING, + lheight)]; + if (labelp == 100) { + ynext = ythis - rheight - VSPACING; + rheight = theight; + } else { + ynext = ythis; + } + } else { + ynext = ythis; + rheight = theight; + } + + listw = (wthis + HSPACING) * listp / 100 - HSPACING; + + if (labelp == 100) + xlist = xthis; + else + xlist = xthis+wthis-listw; + + [list setFrame: NSMakeRect(xlist, ynext-(rheight+theight)/2, + listw, theight)]; + + /* + * Size the columns for the table view. + */ + if (c->tableview) { + int ncols, *percentages; + int hundred = 100; + int cpercent = 0, cpixels = 0; + NSArray *cols; + + if (ctrl->listbox.ncols) { + ncols = ctrl->listbox.ncols; + percentages = ctrl->listbox.percentages; + } else { + ncols = 1; + percentages = &hundred; + } + + cols = [c->tableview tableColumns]; + + for (j = 0; j < ncols; j++) { + NSTableColumn *col = [cols objectAtIndex:j]; + int newcpixels; + + cpercent += percentages[j]; + newcpixels = listw * cpercent / 100; + [col setWidth:newcpixels-cpixels]; + cpixels = newcpixels; + } + } + + ch = (ythis - ynext) + theight; + + if (c->button) { + int b2height, centre; + int bx, bw; + + /* + * Place the Up and Down buttons for a drag list. + */ + assert(c->button2); + + rect = [c->button frame]; + b2height = VSPACING + 2 * rect.size.height; + + centre = ynext - rheight/2; + + bx = (wthis + HSPACING) * 3 / 4; + bw = wthis - bx; + bx += leftx; + + [c->button setFrame: + NSMakeRect(bx, centre+b2height/2-rect.size.height, + bw, rect.size.height)]; + [c->button2 setFrame: + NSMakeRect(bx, centre-b2height/2, + bw, rect.size.height)]; + } + } + break; + } + + for (j = colstart; j < colend; j++) + cypos[j] = ythis - ch - VSPACING; + if (ret < topy - (ythis - ch)) + ret = topy - (ythis - ch); + } + + if (*s->boxname) { + NSBox *box = find_box(d, s); + assert(box != NULL); + [box sizeToFit]; + + if (s->boxtitle) { + NSRect rect = [box frame]; + rect.size.height += [[box titleFont] pointSize]; + [box setFrame:rect]; + } + + ret += boxh; + } + + //printf("For controlset %s/%s, returning ret=%d\n", + // s->pathname, s->boxname, ret); + return ret; +} + +void select_panel(void *dv, struct controlbox *b, const char *name) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + int i, j, hidden; + struct controlset *s; + union control *ctrl; + struct fe_ctrl *c; + NSBox *box; + + for (i = 0; i < b->nctrlsets; i++) { + s = b->ctrlsets[i]; + + if (*s->pathname) { + hidden = !strcmp(s->pathname, name) ? NO : YES; + + if ((box = find_box(d, s)) != NULL) { + [box setHidden:hidden]; + } else { + for (j = 0; j < s->ncontrols; j++) { + ctrl = s->ctrls[j]; + c = fe_ctrl_byctrl(d, ctrl); + + if (!c) + continue; + + if (c->label) + [c->label setHidden:hidden]; + if (c->button) + [c->button setHidden:hidden]; + if (c->button2) + [c->button2 setHidden:hidden]; + if (c->editbox) + [c->editbox setHidden:hidden]; + if (c->combobox) + [c->combobox setHidden:hidden]; + if (c->textview) + [c->textview setHidden:hidden]; + if (c->tableview) + [c->tableview setHidden:hidden]; + if (c->scrollview) + [c->scrollview setHidden:hidden]; + if (c->popupbutton) + [c->popupbutton setHidden:hidden]; + if (c->radiobuttons) { + int j; + for (j = 0; j < c->nradiobuttons; j++) + [c->radiobuttons[j] setHidden:hidden]; + } + break; + } + } + } + } +} + +void dlg_radiobutton_set(union control *ctrl, void *dv, int whichbutton) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + int j; + + assert(c->radiobuttons); + for (j = 0; j < c->nradiobuttons; j++) + [c->radiobuttons[j] setState: + (j == whichbutton ? NSOnState : NSOffState)]; +} + +int dlg_radiobutton_get(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + int j; + + assert(c->radiobuttons); + for (j = 0; j < c->nradiobuttons; j++) + if ([c->radiobuttons[j] state] == NSOnState) + return j; + + return 0; /* should never reach here */ +} + +void dlg_checkbox_set(union control *ctrl, void *dv, int checked) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + assert(c->button); + [c->button setState:(checked ? NSOnState : NSOffState)]; +} + +int dlg_checkbox_get(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + assert(c->button); + return ([c->button state] == NSOnState); +} + +void dlg_editbox_set(union control *ctrl, void *dv, char const *text) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->editbox) { + [c->editbox setStringValue:[NSString stringWithCString:text]]; + } else { + assert(c->combobox); + [c->combobox setStringValue:[NSString stringWithCString:text]]; + } +} + +void dlg_editbox_get(union control *ctrl, void *dv, char *buffer, int length) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + NSString *str; + + if (c->editbox) { + str = [c->editbox stringValue]; + } else { + assert(c->combobox); + str = [c->combobox stringValue]; + } + if (!str) + str = @""; + + /* The length parameter to this method doesn't include a trailing NUL */ + [str getCString:buffer maxLength:length-1]; +} + +void dlg_listbox_clear(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + [[c->tableview dataSource] clear]; + [c->tableview reloadData]; + } else { + [c->popupbutton removeAllItems]; + } +} + +void dlg_listbox_del(union control *ctrl, void *dv, int index) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + [[c->tableview dataSource] removestr:index]; + [c->tableview reloadData]; + } else { + [c->popupbutton removeItemAtIndex:index]; + } +} + +void dlg_listbox_addwithid(union control *ctrl, void *dv, + char const *text, int id) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + [[c->tableview dataSource] add:text withId:id]; + [c->tableview reloadData]; + } else { + [c->popupbutton addItemWithTitle:[NSString stringWithCString:text]]; + [[c->popupbutton lastItem] setTag:id]; + } +} + +void dlg_listbox_add(union control *ctrl, void *dv, char const *text) +{ + dlg_listbox_addwithid(ctrl, dv, text, -1); +} + +int dlg_listbox_getid(union control *ctrl, void *dv, int index) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + return [[c->tableview dataSource] getid:index]; + } else { + return [[c->popupbutton itemAtIndex:index] tag]; + } +} + +int dlg_listbox_index(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + return [c->tableview selectedRow]; + } else { + return [c->popupbutton indexOfSelectedItem]; + } +} + +int dlg_listbox_issel(union control *ctrl, void *dv, int index) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + return [c->tableview isRowSelected:index]; + } else { + return [c->popupbutton indexOfSelectedItem] == index; + } +} + +void dlg_listbox_select(union control *ctrl, void *dv, int index) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + if (c->tableview) { + [c->tableview selectRow:index byExtendingSelection:NO]; + } else { + [c->popupbutton selectItemAtIndex:index]; + } +} + +void dlg_text_set(union control *ctrl, void *dv, char const *text) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + + assert(c->textview); + [c->textview setString:[NSString stringWithCString:text]]; +} + +void dlg_filesel_set(union control *ctrl, void *dv, Filename fn) +{ + /* FIXME */ +} + +void dlg_filesel_get(union control *ctrl, void *dv, Filename *fn) +{ + /* FIXME */ +} + +void dlg_fontsel_set(union control *ctrl, void *dv, FontSpec fn) +{ + /* FIXME */ +} + +void dlg_fontsel_get(union control *ctrl, void *dv, FontSpec *fn) +{ + /* FIXME */ +} + +void dlg_update_start(union control *ctrl, void *dv) +{ + /* FIXME */ +} + +void dlg_update_done(union control *ctrl, void *dv) +{ + /* FIXME */ +} + +void dlg_set_focus(union control *ctrl, void *dv) +{ + /* FIXME */ +} + +union control *dlg_last_focused(union control *ctrl, void *dv) +{ + return NULL; /* FIXME */ +} + +void dlg_beep(void *dv) +{ + NSBeep(); +} + +void dlg_error_msg(void *dv, char *msg) +{ + /* FIXME */ +} + +void dlg_end(void *dv, int value) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + [d->target performSelector:d->action + withObject:[NSNumber numberWithInt:value]]; +} + +void dlg_coloursel_start(union control *ctrl, void *dv, + int r, int g, int b) +{ + /* FIXME */ +} + +int dlg_coloursel_results(union control *ctrl, void *dv, + int *r, int *g, int *b) +{ + return 0; /* FIXME */ +} + +void dlg_refresh(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c; + + if (ctrl) { + if (ctrl->generic.handler != NULL) + ctrl->generic.handler(ctrl, d, d->data, EVENT_REFRESH); + } else { + int i; + + for (i = 0; (c = index234(d->byctrl, i)) != NULL; i++) { + assert(c->ctrl != NULL); + if (c->ctrl->generic.handler != NULL) + c->ctrl->generic.handler(c->ctrl, d, + d->data, EVENT_REFRESH); + } + } +} + +void *dlg_get_privdata(union control *ctrl, void *dv) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + return c->privdata; +} + +void dlg_set_privdata(union control *ctrl, void *dv, void *ptr) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + c->privdata = ptr; + c->privdata_needs_free = FALSE; +} + +void *dlg_alloc_privdata(union control *ctrl, void *dv, size_t size) +{ + struct fe_dlg *d = (struct fe_dlg *)dv; + struct fe_ctrl *c = fe_ctrl_byctrl(d, ctrl); + /* + * This is an internal allocation routine, so it's allowed to + * use smalloc directly. + */ + c->privdata = smalloc(size); + c->privdata_needs_free = TRUE; + return c->privdata; +} diff --git a/macosx/osxdlg.m b/macosx/osxdlg.m new file mode 100644 index 00000000..5bc13a43 --- /dev/null +++ b/macosx/osxdlg.m @@ -0,0 +1,367 @@ +/* + * osxdlg.m: various PuTTY dialog boxes for OS X. + */ + +#import +#include "putty.h" +#include "storage.h" +#include "dialog.h" +#include "osxclass.h" + +/* + * The `ConfigWindow' class is used to start up a new PuTTY + * session. + */ + +@class ConfigTree; +@interface ConfigTree : NSObject +{ + NSString **paths; + int *levels; + int nitems, itemsize; +} +- (void)addPath:(char *)path; +@end + +@implementation ConfigTree +- (id)init +{ + self = [super init]; + paths = NULL; + levels = NULL; + nitems = itemsize = 0; + return self; +} +- (void)addPath:(char *)path +{ + if (nitems >= itemsize) { + itemsize += 32; + paths = sresize(paths, itemsize, NSString *); + levels = sresize(levels, itemsize, int); + } + paths[nitems] = [[NSString stringWithCString:path] retain]; + levels[nitems] = ctrl_path_elements(path) - 1; + nitems++; +} +- (void)dealloc +{ + int i; + + for (i = 0; i < nitems; i++) + [paths[i] release]; + + sfree(paths); + sfree(levels); + + [super dealloc]; +} +- (id)iterateChildren:(int)index ofItem:(id)item count:(int *)count +{ + int i, plevel; + + if (item) { + for (i = 0; i < nitems; i++) + if (paths[i] == item) + break; + assert(i < nitems); + plevel = levels[i]; + i++; + } else { + i = 0; + plevel = -1; + } + + if (count) + *count = 0; + + while (index > 0) { + if (i >= nitems || levels[i] != plevel+1) + return nil; + if (count) + (*count)++; + do { + i++; + } while (i < nitems && levels[i] > plevel+1); + index--; + } + + return paths[i]; +} +- (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item +{ + return [self iterateChildren:index ofItem:item count:NULL]; +} +- (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item +{ + int count = 0; + /* pass nitems+1 to ensure we run off the end */ + [self iterateChildren:nitems+1 ofItem:item count:&count]; + return count; +} +- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item +{ + return [self outlineView:outlineView numberOfChildrenOfItem:item] > 0; +} +- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item +{ + /* + * Trim off all path elements except the last one. + */ + NSArray *components = [item componentsSeparatedByString:@"/"]; + return [components objectAtIndex:[components count]-1]; +} +@end + +@implementation ConfigWindow +- (id)initWithConfig:(Config)aCfg +{ + NSScrollView *scrollview; + NSTableColumn *col; + ConfigTree *treedata; + int by = 0, mby = 0; + int wmin = 0; + int hmin = 0; + int panelht = 0; + + get_sesslist(&sl, TRUE); + + ctrlbox = ctrl_new_box(); + setup_config_box(ctrlbox, &sl, FALSE /*midsession*/, aCfg.protocol, + 0 /* protcfginfo */); + unix_setup_config_box(ctrlbox, FALSE /*midsession*/); + + cfg = aCfg; /* structure copy */ + + self = [super initWithContentRect:NSMakeRect(0,0,300,300) + styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | + NSClosableWindowMask) + backing:NSBackingStoreBuffered + defer:YES]; + [self setTitle:@"PuTTY Configuration"]; + + [self setIgnoresMouseEvents:NO]; + + dv = fe_dlg_init(&cfg, self, self, @selector(configBoxFinished:)); + + scrollview = [[NSScrollView alloc] initWithFrame:NSMakeRect(20,20,10,10)]; + treeview = [[NSOutlineView alloc] initWithFrame:[scrollview frame]]; + [scrollview setBorderType:NSLineBorder]; + [scrollview setDocumentView:treeview]; + [[self contentView] addSubview:scrollview]; + [scrollview setHasVerticalScroller:YES]; + [scrollview setAutohidesScrollers:YES]; + /* FIXME: the below is untested. Test it then remove this notice. */ + [treeview setAllowsColumnReordering:NO]; + [treeview setAllowsColumnResizing:NO]; + [treeview setAllowsMultipleSelection:NO]; + [treeview setAllowsEmptySelection:NO]; + [treeview setAllowsColumnSelection:YES]; + + treedata = [[[ConfigTree alloc] init] retain]; + + col = [[NSTableColumn alloc] initWithIdentifier:nil]; + [treeview addTableColumn:col]; + [treeview setOutlineTableColumn:col]; + + [[treeview headerView] setFrame:NSMakeRect(0,0,0,0)]; + + /* + * Create the controls. + */ + { + int i; + char *path = NULL; + + for (i = 0; i < ctrlbox->nctrlsets; i++) { + struct controlset *s = ctrlbox->ctrlsets[i]; + int mw, mh; + + if (!*s->pathname) { + + create_ctrls(dv, [self contentView], s, &mw, &mh); + + by += 20 + mh; + + if (wmin < mw + 40) + wmin = mw + 40; + } else { + int j = path ? ctrl_path_compare(s->pathname, path) : 0; + + if (j != INT_MAX) { /* add to treeview, start new panel */ + char *c; + + /* + * We expect never to find an implicit path + * component. For example, we expect never to + * see A/B/C followed by A/D/E, because that + * would _implicitly_ create A/D. All our path + * prefixes are expected to contain actual + * controls and be selectable in the treeview; + * so we would expect to see A/D _explicitly_ + * before encountering A/D/E. + */ + assert(j == ctrl_path_elements(s->pathname) - 1); + + c = strrchr(s->pathname, '/'); + if (!c) + c = s->pathname; + else + c++; + + [treedata addPath:s->pathname]; + path = s->pathname; + + panelht = 0; + } + + create_ctrls(dv, [self contentView], s, &mw, &mh); + if (wmin < mw + 3*20+150) + wmin = mw + 3*20+150; + panelht += mh + 20; + if (hmin < panelht - 20) + hmin = panelht - 20; + } + } + } + + { + int i; + NSRect r; + + [treeview setDataSource:treedata]; + for (i = [treeview numberOfRows]; i-- ;) + [treeview expandItem:[treeview itemAtRow:i] expandChildren:YES]; + + [treeview sizeToFit]; + r = [treeview frame]; + if (hmin < r.size.height) + hmin = r.size.height; + } + + [self setContentSize:NSMakeSize(wmin, hmin+60+by)]; + [scrollview setFrame:NSMakeRect(20, 40+by, 150, hmin)]; + [treeview setDelegate:self]; + mby = by; + + /* + * Now place the controls. + */ + { + int i; + char *path = NULL; + panelht = 0; + + for (i = 0; i < ctrlbox->nctrlsets; i++) { + struct controlset *s = ctrlbox->ctrlsets[i]; + + if (!*s->pathname) { + by -= VSPACING + place_ctrls(dv, s, 20, by, wmin-40); + } else { + if (!path || strcmp(s->pathname, path)) + panelht = 0; + + panelht += VSPACING + place_ctrls(dv, s, 2*20+150, + 40+mby+hmin-panelht, + wmin - (3*20+150)); + + path = s->pathname; + } + } + } + + select_panel(dv, ctrlbox, [[treeview itemAtRow:0] cString]); + + [treeview reloadData]; + + dlg_refresh(NULL, dv); + + [self center]; /* :-) */ + + return self; +} +- (void)configBoxFinished:(id)object +{ + int ret = [object intValue]; /* it'll be an NSNumber */ + if (ret) { + [controller performSelectorOnMainThread: + @selector(newSessionWithConfig:) + withObject:[NSData dataWithBytes:&cfg length:sizeof(cfg)] + waitUntilDone:NO]; + } + [self close]; +} +- (void)outlineViewSelectionDidChange:(NSNotification *)notification +{ + const char *path = [[treeview itemAtRow:[treeview selectedRow]] cString]; + select_panel(dv, ctrlbox, path); +} +- (BOOL)outlineView:(NSOutlineView *)outlineView + shouldEditTableColumn:(NSTableColumn *)tableColumn item:(id)item +{ + return NO; /* no editing! */ +} +@end + +/* ---------------------------------------------------------------------- + * Various special-purpose dialog boxes. + */ + +int askappend(void *frontend, Filename filename) +{ + return 0; /* FIXME */ +} + +void askalg(void *frontend, const char *algtype, const char *algname) +{ + fatalbox("Cipher algorithm dialog box not supported yet"); + return; /* FIXME */ +} + +void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, + char *keystr, char *fingerprint) +{ + int ret; + + /* + * Verify the key. + */ + ret = verify_host_key(host, port, keytype, keystr); + + if (ret == 0) + return; + + /* + * FIXME FIXME FIXME. I currently lack any sensible means of + * asking the user for a verification non-application-modally, + * _or_ any means of closing just this connection if the answer + * is no (the Unix and Windows ports just exit() in this + * situation since they're one-connection-per-process). + * + * What I need to do is to make this function optionally- + * asynchronous, much like the interface to agent_query(). It + * can either run modally and return a result directly, _or_ it + * can kick off a non-modal dialog, return a `please wait' + * status, and the dialog can call the backend back when the + * result comes in. Also, in either case, the aye/nay result + * wants to be passed to the backend so that it can tear down + * the connection if the answer was nay. + * + * For the moment, I simply bomb out if we have an unrecognised + * host key. This makes this port safe but not very useful: you + * can only use it at all if you already have a host key cache + * set up by running the Unix port. + */ + fatalbox("Host key dialog box not supported yet"); +} + +void old_keyfile_warning(void) +{ + /* + * This should never happen on OS X. We hope. + */ +} + +void about_box(void *window) +{ + /* FIXME */ +} diff --git a/macosx/osxmain.m b/macosx/osxmain.m new file mode 100644 index 00000000..bd579852 --- /dev/null +++ b/macosx/osxmain.m @@ -0,0 +1,391 @@ +/* + * osxmain.m: main-program file of Mac OS X PuTTY. + */ + +#import + +#define PUTTY_DO_GLOBALS /* actually _define_ globals */ + +#include "putty.h" +#include "osxclass.h" + +/* ---------------------------------------------------------------------- + * Global variables. + */ + +AppController *controller; + +/* ---------------------------------------------------------------------- + * Miscellaneous elements of the interface to the cross-platform + * and Unix PuTTY code. + */ + +const char platform_x11_best_transport[] = "unix"; + +char *platform_get_x_display(void) { + return NULL; +} + +FontSpec platform_default_fontspec(const char *name) +{ + FontSpec ret; + /* FIXME */ + return ret; +} + +Filename platform_default_filename(const char *name) +{ + Filename ret; + if (!strcmp(name, "LogFileName")) + strcpy(ret.path, "putty.log"); + else + *ret.path = '\0'; + return ret; +} + +char *platform_default_s(const char *name) +{ + return NULL; +} + +int platform_default_i(const char *name, int def) +{ + if (!strcmp(name, "CloseOnExit")) + return 2; /* maps to FORCE_ON after painful rearrangement :-( */ + return def; +} + +char *x_get_default(const char *key) +{ + return NULL; /* this is a stub */ +} + +void modalfatalbox(char *p, ...) +{ + /* FIXME: proper OS X GUI stuff */ + va_list ap; + fprintf(stderr, "FATAL ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void fatalbox(char *p, ...) +{ + /* FIXME: proper OS X GUI stuff */ + va_list ap; + fprintf(stderr, "FATAL ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void cmdline_error(char *p, ...) +{ + va_list ap; + fprintf(stderr, "%s: ", appname); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +/* + * Clean up and exit. + */ +void cleanup_exit(int code) +{ + /* + * Clean up. + */ + sk_cleanup(); + random_save_seed(); + exit(code); +} + +/* ---------------------------------------------------------------------- + * Tiny extension to NSMenuItem which carries a payload of a `void + * *', allowing several menu items to invoke the same message but + * pass different data through it. + */ +@interface DataMenuItem : NSMenuItem +{ + void *payload; +} +- (void)setPayload:(void *)d; +- (void *)getPayload; +@end +@implementation DataMenuItem +- (void)setPayload:(void *)d +{ + payload = d; +} +- (void *)getPayload +{ + return payload; +} +@end + +/* ---------------------------------------------------------------------- + * Utility routines for constructing OS X menus. + */ + +NSMenu *newmenu(const char *title) +{ + return [[[NSMenu allocWithZone:[NSMenu menuZone]] + initWithTitle:[NSString stringWithCString:title]] + autorelease]; +} + +NSMenu *newsubmenu(NSMenu *parent, const char *title) +{ + NSMenuItem *item; + NSMenu *child; + + item = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] + initWithTitle:[NSString stringWithCString:title] + action:NULL + keyEquivalent:@""] + autorelease]; + child = newmenu(title); + [item setEnabled:YES]; + [item setSubmenu:child]; + [parent addItem:item]; + return child; +} + +id initnewitem(NSMenuItem *item, NSMenu *parent, const char *title, + const char *key, id target, SEL action) +{ + unsigned mask = NSCommandKeyMask; + + if (key[strcspn(key, "-")]) { + while (*key && *key != '-') { + int c = tolower((unsigned char)*key); + if (c == 's') { + mask |= NSShiftKeyMask; + } else if (c == 'o' || c == 'a') { + mask |= NSAlternateKeyMask; + } + key++; + } + if (*key) + key++; + } + + item = [[item initWithTitle:[NSString stringWithCString:title] + action:NULL + keyEquivalent:[NSString stringWithCString:key]] + autorelease]; + + if (*key) + [item setKeyEquivalentModifierMask: mask]; + + [item setEnabled:YES]; + [item setTarget:target]; + [item setAction:action]; + + [parent addItem:item]; + + return item; +} + +NSMenuItem *newitem(NSMenu *parent, char *title, char *key, + id target, SEL action) +{ + return initnewitem([NSMenuItem allocWithZone:[NSMenu menuZone]], + parent, title, key, target, action); +} + +/* ---------------------------------------------------------------------- + * AppController: the object which receives the messages from all + * menu selections that aren't standard OS X functions. + */ +@implementation AppController + +- (id)init +{ + self = [super init]; + timer = NULL; + return self; +} + +- (void)newTerminal:(id)sender +{ + id win; + Config cfg; + + do_defaults(NULL, &cfg); + + cfg.protocol = -1; /* PROT_TERMINAL */ + + win = [[SessionWindow alloc] initWithConfig:cfg]; + [win makeKeyAndOrderFront:self]; +} + +- (void)newSessionConfig:(id)sender +{ + id win; + Config cfg; + + do_defaults(NULL, &cfg); + + win = [[ConfigWindow alloc] initWithConfig:cfg]; + [win makeKeyAndOrderFront:self]; +} + +- (void)newSessionWithConfig:(id)vdata +{ + id win; + Config cfg; + NSData *data = (NSData *)vdata; + + assert([data length] == sizeof(cfg)); + [data getBytes:&cfg]; + + win = [[SessionWindow alloc] initWithConfig:cfg]; + [win makeKeyAndOrderFront:self]; +} + +- (NSMenu *)applicationDockMenu:(NSApplication *)sender +{ + NSMenu *menu = newmenu("Dock Menu"); + /* + * FIXME: Add some useful things to this, probably including + * the saved session list. + */ + return menu; +} + +- (void)timerFired:(id)sender +{ + long now, next; + + assert(sender == timer); + + /* `sender' is the timer itself, so its userInfo is an NSNumber. */ + now = [(NSNumber *)[sender userInfo] longValue]; + + [sender invalidate]; + + timer = NULL; + + if (run_timers(now, &next)) + [self setTimer:next]; +} + +- (void)setTimer:(long)next +{ + long interval = next - GETTICKCOUNT(); + float finterval; + + if (interval <= 0) + interval = 1; /* just in case */ + + finterval = interval / (float)TICKSPERSEC; + + if (timer) { + [timer invalidate]; + } + + timer = [NSTimer scheduledTimerWithTimeInterval:finterval + target:self selector:@selector(timerFired:) + userInfo:[NSNumber numberWithLong:next] repeats:NO]; +} + +@end + +void timer_change_notify(long next) +{ + [controller setTimer:next]; +} + +/* ---------------------------------------------------------------------- + * Annoyingly, it looks as if I have to actually subclass + * NSApplication if I want to catch NSApplicationDefined events. So + * here goes. + */ +@interface MyApplication : NSApplication +{ +} +@end +@implementation MyApplication +- (void)sendEvent:(NSEvent *)ev +{ + if ([ev type] == NSApplicationDefined) + osxsel_process_results(); + + [super sendEvent:ev]; +} +@end + +/* ---------------------------------------------------------------------- + * Main program. Constructs the menus and runs the application. + */ +int main(int argc, char **argv) +{ + NSAutoreleasePool *pool; + NSMenu *menu; + NSMenuItem *item; + NSImage *icon; + + pool = [[NSAutoreleasePool alloc] init]; + + icon = [NSImage imageNamed:@"NSApplicationIcon"]; + [MyApplication sharedApplication]; + [NSApp setApplicationIconImage:icon]; + + controller = [[[AppController alloc] init] autorelease]; + [NSApp setDelegate:controller]; + + [NSApp setMainMenu: newmenu("Main Menu")]; + + menu = newsubmenu([NSApp mainMenu], "Apple Menu"); + [NSApp setServicesMenu:newsubmenu(menu, "Services")]; + [menu addItem:[NSMenuItem separatorItem]]; + item = newitem(menu, "Hide PuTTY", "h", NSApp, @selector(hide:)); + item = newitem(menu, "Hide Others", "o-h", NSApp, @selector(hideOtherApplications:)); + item = newitem(menu, "Show All", "", NSApp, @selector(unhideAllApplications:)); + [menu addItem:[NSMenuItem separatorItem]]; + item = newitem(menu, "Quit", "q", NSApp, @selector(terminate:)); + [NSApp setAppleMenu: menu]; + + menu = newsubmenu([NSApp mainMenu], "File"); + item = newitem(menu, "New", "n", NULL, @selector(newSessionConfig:)); + item = newitem(menu, "New Terminal", "t", NULL, @selector(newTerminal:)); + item = newitem(menu, "Close", "w", NULL, @selector(performClose:)); + + menu = newsubmenu([NSApp mainMenu], "Window"); + [NSApp setWindowsMenu: menu]; + item = newitem(menu, "Minimise Window", "m", NULL, @selector(performMiniaturize:)); + +// menu = newsubmenu([NSApp mainMenu], "Help"); +// item = newitem(menu, "PuTTY Help", "?", NSApp, @selector(showHelp:)); + + /* + * Start up the sub-thread doing select(). + */ + osxsel_init(); + + /* + * Start up networking. + */ + sk_init(); + + /* + * FIXME: To make initial debugging more convenient I'm going + * to start by opening a session window unconditionally. This + * will probably change later on. + */ + [controller newSessionConfig:nil]; + + [NSApp run]; + [pool release]; + + return 0; +} diff --git a/macosx/osxsel.m b/macosx/osxsel.m new file mode 100644 index 00000000..eac60282 --- /dev/null +++ b/macosx/osxsel.m @@ -0,0 +1,308 @@ +/* + * osxsel.m: OS X implementation of the front end interface to uxsel. + */ + +#import +#include +#include "putty.h" +#include "osxclass.h" + +/* + * The unofficial Cocoa FAQ at + * + * http://www.alastairs-place.net/cocoa/faq.txt + * + * says that Cocoa has the native ability to be given an fd and + * tell you when it becomes readable, but cannot tell you when it + * becomes _writable_. This is unacceptable to PuTTY, which depends + * for correct functioning on being told both. Therefore, I can't + * use the Cocoa native mechanism. + * + * Instead, I'm going to resort to threads. I start a second thread + * whose job is to do selects. At the termination of every select, + * it posts a Cocoa event into the main thread's event queue, so + * that the main thread gets select results interleaved with other + * GUI operations. Communication from the main thread _to_ the + * select thread is performed by writing to a pipe whose other end + * is one of the file descriptors being selected on. (This is the + * only sensible way, because we have to be able to interrupt a + * select in order to provide a new fd list.) + */ + +/* + * In more detail, the select thread must: + * + * - start off by listening to _just_ the pipe, waiting to be told + * to begin a select. + * + * - when it receives the `start' command, it should read the + * shared uxsel data (which is protected by a mutex), set up its + * select, and begin it. + * + * - when the select terminates, it should write the results + * (perhaps minus the inter-thread pipe if it's there) into + * shared memory and dispatch a GUI event to let the main thread + * know. + * + * - the main thread will then think about it, do some processing, + * and _then_ send a command saying `now restart select'. Before + * sending that command it might easily have tinkered with the + * uxsel structures, which is why it waited before sending it. + * + * - EOF on the inter-thread pipe, of course, means the process + * has finished completely, so the select thread terminates. + * + * - The main thread may wish to adjust the uxsel settings in the + * middle of a select. In this situation it first writes the new + * data to the shared memory area, then notifies the select + * thread by writing to the inter-thread pipe. + * + * So the upshot is that the sequence of operations performed in + * the select thread must be: + * + * - read a byte from the pipe (which may block) + * + * - read the shared uxsel data and perform a select + * + * - notify the main thread of interesting select results (if any) + * + * - loop round again from the top. + * + * This is sufficient. Notifying the select thread asynchronously + * by writing to the pipe will cause its select to terminate and + * another to begin immediately without blocking. If the select + * thread's select terminates due to network data, its subsequent + * pipe read will block until the main thread is ready to let it + * loose again. + */ + +static int osxsel_pipe[2]; + +static NSLock *osxsel_inlock; +static fd_set osxsel_rfds_in; +static fd_set osxsel_wfds_in; +static fd_set osxsel_xfds_in; +static int osxsel_inmax; + +static NSLock *osxsel_outlock; +static fd_set osxsel_rfds_out; +static fd_set osxsel_wfds_out; +static fd_set osxsel_xfds_out; +static int osxsel_outmax; + +static int inhibit_start_select; + +/* + * NSThread requires an object method as its thread procedure, so + * here I define a trivial holding class. + */ +@class OSXSel; +@interface OSXSel : NSObject +{ +} +- (void)runThread:(id)arg; +@end +@implementation OSXSel +- (void)runThread:(id)arg +{ + char c; + fd_set r, w, x; + int n, ret; + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + while (1) { + /* + * Read one byte from the pipe. + */ + ret = read(osxsel_pipe[0], &c, 1); + + if (ret <= 0) + return; /* terminate the thread */ + + /* + * Now set up the select data. + */ + [osxsel_inlock lock]; + memcpy(&r, &osxsel_rfds_in, sizeof(fd_set)); + memcpy(&w, &osxsel_wfds_in, sizeof(fd_set)); + memcpy(&x, &osxsel_xfds_in, sizeof(fd_set)); + n = osxsel_inmax; + [osxsel_inlock unlock]; + FD_SET(osxsel_pipe[0], &r); + if (n < osxsel_pipe[0]+1) + n = osxsel_pipe[0]+1; + + /* + * Perform the select. + */ + ret = select(n, &r, &w, &x, NULL); + + /* + * Detect the one special case in which the only + * interesting fd was the inter-thread pipe. In that + * situation only we are interested - the main thread will + * not be! + */ + if (ret == 1 && FD_ISSET(osxsel_pipe[0], &r)) + continue; /* just loop round again */ + + /* + * Write the select results to shared data. + * + * I _think_ we don't need this data to be lock-protected: + * it won't be read by the main thread until after we send + * a message indicating that we've finished writing it, and + * we won't start another select (hence potentially writing + * it again) until the main thread notifies us in return. + * + * However, I'm scared of multithreading and not totally + * convinced of my reasoning, so I'm going to lock it + * anyway. + */ + [osxsel_outlock lock]; + memcpy(&osxsel_rfds_out, &r, sizeof(fd_set)); + memcpy(&osxsel_wfds_out, &w, sizeof(fd_set)); + memcpy(&osxsel_xfds_out, &x, sizeof(fd_set)); + osxsel_outmax = n; + [osxsel_outlock unlock]; + + /* + * Post a message to the main thread's message queue + * telling it that select data is available. + */ + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0,0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0] + atStart:NO]; + } + + [pool release]; +} +@end + +void osxsel_init(void) +{ + uxsel_init(); + + if (pipe(osxsel_pipe) < 0) { + fatalbox("Unable to set up inter-thread pipe for select"); + } + [NSThread detachNewThreadSelector:@selector(runThread:) + toTarget:[[[OSXSel alloc] init] retain] withObject:nil]; + /* + * Also initialise (i.e. clear) the input fd_sets. Need not + * start a select just yet - the select thread will block until + * we have at least one fd for it! + */ + FD_ZERO(&osxsel_rfds_in); + FD_ZERO(&osxsel_wfds_in); + FD_ZERO(&osxsel_xfds_in); + osxsel_inmax = 0; + /* + * Initialise the mutex locks used to protect the data passed + * between threads. + */ + osxsel_inlock = [[[NSLock alloc] init] retain]; + osxsel_outlock = [[[NSLock alloc] init] retain]; +} + +static void osxsel_start_select(void) +{ + char c = 'g'; /* for `Go!' :-) but it's never used */ + + if (!inhibit_start_select) + write(osxsel_pipe[1], &c, 1); +} + +int uxsel_input_add(int fd, int rwx) +{ + /* + * Add the new fd to the appropriate input fd_sets, then write + * to the inter-thread pipe. + */ + [osxsel_inlock lock]; + if (rwx & 1) + FD_SET(fd, &osxsel_rfds_in); + else + FD_CLR(fd, &osxsel_rfds_in); + if (rwx & 2) + FD_SET(fd, &osxsel_wfds_in); + else + FD_CLR(fd, &osxsel_wfds_in); + if (rwx & 4) + FD_SET(fd, &osxsel_xfds_in); + else + FD_CLR(fd, &osxsel_xfds_in); + if (osxsel_inmax < fd+1) + osxsel_inmax = fd+1; + [osxsel_inlock unlock]; + osxsel_start_select(); + + /* + * We must return an `id' which will be passed back to us at + * the time of uxsel_input_remove. Since we have no need to + * store ids in that sense, we might as well go with the fd + * itself. + */ + return fd; +} + +void uxsel_input_remove(int id) +{ + /* + * Remove the fd from all the input fd_sets. In this + * implementation, the simplest way to do that is to call + * uxsel_input_add with rwx==0! + */ + uxsel_input_add(id, 0); +} + +/* + * Function called in the main thread to process results. It will + * have to read the output fd_sets, go through them, call back to + * uxsel with the results, and then write to the inter-thread pipe. + * + * This function will have to be called from an event handler in + * osxmain.m, which will therefore necessarily contain a small part + * of this mechanism (along with calling osxsel_init). + */ +void osxsel_process_results(void) +{ + int i; + + /* + * We must write to the pipe to start a fresh select _even if_ + * there were no changes. So for efficiency, we set a flag here + * which inhibits uxsel_input_{add,remove} from writing to the + * pipe; then once we finish processing, we clear the flag + * again and write a single byte ourselves. It's cleaner, + * because it wakes up the select thread fewer times. + */ + inhibit_start_select = TRUE; + + [osxsel_outlock lock]; + + for (i = 0; i < osxsel_outmax; i++) { + if (FD_ISSET(i, &osxsel_xfds_out)) + select_result(i, 4); + } + for (i = 0; i < osxsel_outmax; i++) { + if (FD_ISSET(i, &osxsel_rfds_out)) + select_result(i, 1); + } + for (i = 0; i < osxsel_outmax; i++) { + if (FD_ISSET(i, &osxsel_wfds_out)) + select_result(i, 2); + } + + [osxsel_outlock unlock]; + + inhibit_start_select = FALSE; + osxsel_start_select(); +} diff --git a/macosx/osxwin.m b/macosx/osxwin.m new file mode 100644 index 00000000..a54f771f --- /dev/null +++ b/macosx/osxwin.m @@ -0,0 +1,1122 @@ +/* + * osxwin.m: code to manage a session window in Mac OS X PuTTY. + */ + +#import +#include "putty.h" +#include "terminal.h" +#include "osxclass.h" + +/* Colours come in two flavours: configurable, and xterm-extended. */ +#define NCFGCOLOURS (lenof(((Config *)0)->colours)) +#define NEXTCOLOURS 240 /* 216 colour-cube plus 24 shades of grey */ +#define NALLCOLOURS (NCFGCOLOURS + NEXTCOLOURS) + +/* + * The key component of the per-session data is the SessionWindow + * class. A pointer to this is used as the frontend handle, to be + * passed to all the platform-independent subsystems that require + * one. + */ + +@interface TerminalView : NSImageView +{ + NSFont *font; + NSImage *image; + Terminal *term; + Config cfg; + NSColor *colours[NALLCOLOURS]; + float fw, fasc, fdesc, fh; +} +- (void)drawStartFinish:(BOOL)start; +- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b; +- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y + attr:(unsigned long)attr lattr:(int)lattr; +@end + +@implementation TerminalView +- (BOOL)isFlipped +{ + return YES; +} +- (id)initWithTerminal:(Terminal *)aTerm config:(Config)aCfg +{ + float w, h; + + self = [self initWithFrame:NSMakeRect(0,0,100,100)]; + + term = aTerm; + cfg = aCfg; + + /* + * Initialise the fonts we're going to use. + * + * FIXME: for the moment I'm sticking with exactly one default font. + */ + font = [NSFont userFixedPitchFontOfSize:0]; + + /* + * Now determine the size of the primary font. + * + * FIXME: If we have multiple fonts, we may need to set fasc + * and fdesc to the _maximum_ asc and desc out of all the + * fonts, _before_ adding them together to get fh. + */ + fw = [font widthOfString:@"A"]; + fasc = [font ascender]; + fdesc = -[font descender]; + fh = fasc + fdesc; + fh = (int)fh + (fh > (int)fh); /* round up, ickily */ + + /* + * Use this to figure out the size of the terminal view. + */ + w = fw * term->cols; + h = fh * term->rows; + + /* + * And set our size and subimage. + */ + image = [[NSImage alloc] initWithSize:NSMakeSize(w,h)]; + [image setFlipped:YES]; + [self setImage:image]; + [self setFrame:NSMakeRect(0,0,w,h)]; + + term_invalidate(term); + + return self; +} +- (void)drawStartFinish:(BOOL)start +{ + if (start) + [image lockFocus]; + else + [image unlockFocus]; +} +- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y + attr:(unsigned long)attr lattr:(int)lattr +{ + int nfg, nbg, rlen, widefactor; + float ox, oy, tw, th; + NSDictionary *attrdict; + + /* FIXME: TATTR_COMBINING */ + + nfg = ((attr & ATTR_FGMASK) >> ATTR_FGSHIFT); + nbg = ((attr & ATTR_BGMASK) >> ATTR_BGSHIFT); + if (attr & ATTR_REVERSE) { + int t = nfg; + nfg = nbg; + nbg = t; + } + if (cfg.bold_colour && (attr & ATTR_BOLD)) { + if (nfg < 16) nfg |= 8; + else if (nfg >= 256) nfg |= 1; + } + if (cfg.bold_colour && (attr & ATTR_BLINK)) { + if (nbg < 16) nbg |= 8; + else if (nbg >= 256) nbg |= 1; + } + if (attr & TATTR_ACTCURS) { + nfg = 260; + nbg = 261; + } + + if (attr & ATTR_WIDE) { + widefactor = 2; + /* FIXME: what do we actually have to do about wide characters? */ + } else { + widefactor = 1; + } + + /* FIXME: ATTR_BOLD without cfg.bold_colour */ + + if ((lattr & LATTR_MODE) != LATTR_NORM) { + x *= 2; + if (x >= term->cols) + return; + if (x + len*2*widefactor > term->cols) + len = (term->cols-x)/2/widefactor;/* trim to LH half */ + rlen = len * 2; + } else + rlen = len; + + /* FIXME: how do we actually implement double-{width,height} lattrs? */ + + ox = x * fw; + oy = y * fh; + tw = rlen * widefactor * fw; + th = fh; + + /* + * Set the clipping rectangle. + */ + [[NSGraphicsContext currentContext] saveGraphicsState]; + [NSBezierPath clipRect:NSMakeRect(ox, oy, tw, th)]; + + attrdict = [NSDictionary dictionaryWithObjectsAndKeys: + colours[nfg], NSForegroundColorAttributeName, + colours[nbg], NSBackgroundColorAttributeName, + font, NSFontAttributeName, nil]; + + /* + * Create an NSString and draw it. + * + * Annoyingly, although our input is wchar_t which is four + * bytes wide on OS X and terminal.c supports 32-bit Unicode, + * we must convert into the two-byte type `unichar' to store in + * NSString, so we lose display capability for extra-BMP stuff + * at this point. + */ + { + NSString *string; + unichar *utext; + int i; + + utext = snewn(len, unichar); + for (i = 0; i < len; i++) + utext[i] = (text[i] >= 0x10000 ? 0xFFFD : text[i]); + + string = [NSString stringWithCharacters:utext length:len]; + [string drawAtPoint:NSMakePoint(ox, oy) withAttributes:attrdict]; + + sfree(utext); + } + + /* + * Restore the graphics state from before the clipRect: call. + */ + [[NSGraphicsContext currentContext] restoreGraphicsState]; + + /* + * And flag this area as needing display. + */ + [self setNeedsDisplayInRect:NSMakeRect(ox, oy, tw, th)]; +} + +- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b +{ + assert(n >= 0 && n < lenof(colours)); + colours[n] = [[NSColor colorWithDeviceRed:r green:g blue:b alpha:1.0] + retain]; +} +@end + +@implementation SessionWindow +- (id)initWithConfig:(Config)aCfg +{ + NSRect rect = { {0,0}, {0,0} }; + + cfg = aCfg; /* structure copy */ + + init_ucs(&ucsdata, cfg.line_codepage, cfg.utf8_override, + CS_UTF8, cfg.vtmode); + term = term_init(&cfg, &ucsdata, self); + logctx = log_init(self, &cfg); + term_provide_logctx(term, logctx); + term_size(term, cfg.height, cfg.width, cfg.savelines); + + termview = [[[TerminalView alloc] initWithTerminal:term config:cfg] + autorelease]; + + /* + * Now work out the size of the window. + */ + rect = [termview frame]; + rect.origin = NSMakePoint(0,0); + rect.size.width += 2 * cfg.window_border; + rect.size.height += 2 * cfg.window_border; + + /* + * Set up a backend. + */ + { + int i; + back = &pty_backend; + for (i = 0; backends[i].backend != NULL; i++) + if (backends[i].protocol == cfg.protocol) { + back = backends[i].backend; + break; + } + } + + { + const char *error; + char *realhost = NULL; + error = back->init(self, &backhandle, &cfg, cfg.host, cfg.port, + &realhost, cfg.tcp_nodelay, cfg.tcp_keepalives); + if (error) { + fatalbox("%s\n", error); /* FIXME: connection_fatal at worst */ + } + + if (realhost) + sfree(realhost); /* FIXME: do something with this */ + } + + /* + * Create a line discipline. (This must be done after creating + * the terminal _and_ the backend, since it needs to be passed + * pointers to both.) + */ + ldisc = ldisc_create(&cfg, term, back, backhandle, self); + + /* + * FIXME: Set up a scrollbar. + */ + + self = [super initWithContentRect:rect + styleMask:(NSTitledWindowMask | NSMiniaturizableWindowMask | + NSClosableWindowMask) + backing:NSBackingStoreBuffered + defer:YES]; + [self setTitle:@"PuTTY"]; + + [self setIgnoresMouseEvents:NO]; + + /* + * Put the terminal view in the window. + */ + rect = [termview frame]; + rect.origin = NSMakePoint(cfg.window_border, cfg.window_border); + [termview setFrame:rect]; + [[self contentView] addSubview:termview]; + + /* + * Set up the colour palette. + */ + palette_reset(self); + + /* + * FIXME: Only the _first_ document window should be centred. + * The subsequent ones should appear down and to the right of + * it, probably using the cascade function provided by Cocoa. + * Also we're apparently required by the HIG to remember and + * reuse previous positions of windows, although I'm not sure + * how that works if the user opens more than one of the same + * session type. + */ + [self center]; /* :-) */ + + return self; +} + +- (void)dealloc +{ + /* + * FIXME: Here we must deallocate all sorts of stuff: the + * terminal, the backend, the ldisc, the logctx, you name it. + * Do so. + */ + [super dealloc]; +} + +- (void)drawStartFinish:(BOOL)start +{ + [termview drawStartFinish:start]; +} + +- (void)setColour:(int)n r:(float)r g:(float)g b:(float)b +{ + [termview setColour:n r:r g:g b:b]; +} + +- (void)doText:(wchar_t *)text len:(int)len x:(int)x y:(int)y + attr:(unsigned long)attr lattr:(int)lattr +{ + /* Pass this straight on to the TerminalView. */ + [termview doText:text len:len x:x y:y attr:attr lattr:lattr]; +} + +- (Config *)cfg +{ + return &cfg; +} + +- (void)keyDown:(NSEvent *)ev +{ + NSString *s = [ev characters]; + int i; + int n = [s length], c = [s characterAtIndex:0], m = [ev modifierFlags]; + int cm = [[ev charactersIgnoringModifiers] characterAtIndex:0]; + wchar_t output[32]; + char coutput[32]; + int use_coutput = FALSE, special = FALSE, start, end; + +printf("n=%d c=U+%04x cm=U+%04x m=%08x\n", n, c, cm, m); + + /* + * FIXME: Alt+numberpad codes. + */ + + /* + * Shift and Ctrl with PageUp/PageDown for scrollback. + */ + if (n == 1 && c == NSPageUpFunctionKey && (m & NSShiftKeyMask)) { + term_scroll(term, 0, -term->rows/2); + return; + } + if (n == 1 && c == NSPageUpFunctionKey && (m & NSControlKeyMask)) { + term_scroll(term, 0, -1); + return; + } + if (n == 1 && c == NSPageDownFunctionKey && (m & NSShiftKeyMask)) { + term_scroll(term, 0, +term->rows/2); + return; + } + if (n == 1 && c == NSPageDownFunctionKey && (m & NSControlKeyMask)) { + term_scroll(term, 0, +1); + return; + } + + /* + * FIXME: Shift-Ins for paste? Or is that not Maccy enough? + */ + + /* + * FIXME: Alt (Option? Command?) prefix in general. + * + * (Note that Alt-Shift-thing will work just by looking at + * charactersIgnoringModifiers; but Alt-Ctrl-thing will need + * processing properly, and Alt-as-in-Option won't happen at + * all. Hmmm.) + * + * (Note also that we need to be able to override menu key + * equivalents before this is particularly useful.) + */ + start = 1; + end = start; + + /* + * Ctrl-` is the same as Ctrl-\, unless we already have a + * better idea. + */ + if ((m & NSControlKeyMask) && n == 1 && cm == '`' && c == '`') { + output[1] = '\x1c'; + end = 2; + } + + /* We handle Return ourselves, because it needs to be flagged as + * special to ldisc. */ + if (n == 1 && c == '\015') { + coutput[1] = '\015'; + use_coutput = TRUE; + end = 2; + special = TRUE; + } + + /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */ + if (n == 1 && (m & NSControlKeyMask) && (m & NSShiftKeyMask) && + cm == ' ') { + output[1] = '\240'; + end = 2; + } + + /* Control-2, Control-Space and Control-@ are all NUL. */ + if ((m & NSControlKeyMask) && n == 1 && + (cm == '2' || cm == '@' || cm == ' ') && c == cm) { + output[1] = '\0'; + end = 2; + } + + /* We don't let MacOS tell us what Backspace is! We know better. */ + if (cm == 0x7F && !(m & NSShiftKeyMask)) { + coutput[1] = cfg.bksp_is_delete ? '\x7F' : '\x08'; + end = 2; + use_coutput = special = TRUE; + } + /* For Shift Backspace, do opposite of what is configured. */ + if (cm == 0x7F && (m & NSShiftKeyMask)) { + coutput[1] = cfg.bksp_is_delete ? '\x08' : '\x7F'; + end = 2; + use_coutput = special = TRUE; + } + + /* Shift-Tab is ESC [ Z. Oddly, this combination generates ^Y by + * default on MacOS! */ + if (cm == 0x19 && (m & NSShiftKeyMask) && !(m & NSControlKeyMask)) { + end = 1; + output[end++] = '\033'; + output[end++] = '['; + output[end++] = 'Z'; + } + + /* + * NetHack keypad mode. + */ + if (cfg.nethack_keypad && (m & NSNumericPadKeyMask)) { + wchar_t *keys = NULL; + switch (cm) { + case '1': keys = L"bB"; break; + case '2': keys = L"jJ"; break; + case '3': keys = L"nN"; break; + case '4': keys = L"hH"; break; + case '5': keys = L".."; break; + case '6': keys = L"lL"; break; + case '7': keys = L"yY"; break; + case '8': keys = L"kK"; break; + case '9': keys = L"uU"; break; + } + if (keys) { + end = 2; + if (m & NSShiftKeyMask) + output[1] = keys[1]; + else + output[1] = keys[0]; + goto done; + } + } + + /* + * Application keypad mode. + */ + if (term->app_keypad_keys && !cfg.no_applic_k && + (m & NSNumericPadKeyMask)) { + int xkey = 0; + switch (cm) { + case NSClearLineFunctionKey: xkey = 'P'; break; + case '=': xkey = 'Q'; break; + case '/': xkey = 'R'; break; + case '*': xkey = 'S'; break; + /* + * FIXME: keypad - and + need to be mapped to ESC O l + * and ESC O k, or ESC O l and ESC O m, depending on + * xterm function key mode, and I can't remember which + * goes where. + */ + case '\003': xkey = 'M'; break; + case '0': xkey = 'p'; break; + case '1': xkey = 'q'; break; + case '2': xkey = 'r'; break; + case '3': xkey = 's'; break; + case '4': xkey = 't'; break; + case '5': xkey = 'u'; break; + case '6': xkey = 'v'; break; + case '7': xkey = 'w'; break; + case '8': xkey = 'x'; break; + case '9': xkey = 'y'; break; + case '.': xkey = 'n'; break; + } + if (xkey) { + if (term->vt52_mode) { + if (xkey >= 'P' && xkey <= 'S') { + output[end++] = '\033'; + output[end++] = xkey; + } else { + output[end++] = '\033'; + output[end++] = '?'; + output[end++] = xkey; + } + } else { + output[end++] = '\033'; + output[end++] = 'O'; + output[end++] = xkey; + } + goto done; + } + } + + /* + * Next, all the keys that do tilde codes. (ESC '[' nn '~', + * for integer decimal nn.) + * + * We also deal with the weird ones here. Linux VCs replace F1 + * to F5 by ESC [ [ A to ESC [ [ E. rxvt doesn't do _that_, but + * does replace Home and End (1~ and 4~) by ESC [ H and ESC O w + * respectively. + */ + { + int code = 0; + switch (cm) { + case NSF1FunctionKey: + code = (m & NSShiftKeyMask ? 23 : 11); + break; + case NSF2FunctionKey: + code = (m & NSShiftKeyMask ? 24 : 12); + break; + case NSF3FunctionKey: + code = (m & NSShiftKeyMask ? 25 : 13); + break; + case NSF4FunctionKey: + code = (m & NSShiftKeyMask ? 26 : 14); + break; + case NSF5FunctionKey: + code = (m & NSShiftKeyMask ? 28 : 15); + break; + case NSF6FunctionKey: + code = (m & NSShiftKeyMask ? 29 : 17); + break; + case NSF7FunctionKey: + code = (m & NSShiftKeyMask ? 31 : 18); + break; + case NSF8FunctionKey: + code = (m & NSShiftKeyMask ? 32 : 19); + break; + case NSF9FunctionKey: + code = (m & NSShiftKeyMask ? 33 : 20); + break; + case NSF10FunctionKey: + code = (m & NSShiftKeyMask ? 34 : 21); + break; + case NSF11FunctionKey: + code = 23; + break; + case NSF12FunctionKey: + code = 24; + break; + case NSF13FunctionKey: + code = 25; + break; + case NSF14FunctionKey: + code = 26; + break; + case NSF15FunctionKey: + code = 28; + break; + case NSF16FunctionKey: + code = 29; + break; + case NSF17FunctionKey: + code = 31; + break; + case NSF18FunctionKey: + code = 32; + break; + case NSF19FunctionKey: + code = 33; + break; + case NSF20FunctionKey: + code = 34; + break; + } + if (!(m & NSControlKeyMask)) switch (cm) { + case NSHomeFunctionKey: + code = 1; + break; +#ifdef FIXME + case GDK_Insert: case GDK_KP_Insert: + code = 2; + break; +#endif + case NSDeleteFunctionKey: + code = 3; + break; + case NSEndFunctionKey: + code = 4; + break; + case NSPageUpFunctionKey: + code = 5; + break; + case NSPageDownFunctionKey: + code = 6; + break; + } + /* Reorder edit keys to physical order */ + if (cfg.funky_type == FUNKY_VT400 && code <= 6) + code = "\0\2\1\4\5\3\6"[code]; + + if (term->vt52_mode && code > 0 && code <= 6) { + output[end++] = '\033'; + output[end++] = " HLMEIG"[code]; + goto done; + } + + if (cfg.funky_type == FUNKY_SCO && /* SCO function keys */ + code >= 11 && code <= 34) { + char codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; + int index = 0; + switch (cm) { + case NSF1FunctionKey: index = 0; break; + case NSF2FunctionKey: index = 1; break; + case NSF3FunctionKey: index = 2; break; + case NSF4FunctionKey: index = 3; break; + case NSF5FunctionKey: index = 4; break; + case NSF6FunctionKey: index = 5; break; + case NSF7FunctionKey: index = 6; break; + case NSF8FunctionKey: index = 7; break; + case NSF9FunctionKey: index = 8; break; + case NSF10FunctionKey: index = 9; break; + case NSF11FunctionKey: index = 10; break; + case NSF12FunctionKey: index = 11; break; + } + if (m & NSShiftKeyMask) index += 12; + if (m & NSControlKeyMask) index += 24; + output[end++] = '\033'; + output[end++] = '['; + output[end++] = codes[index]; + goto done; + } + if (cfg.funky_type == FUNKY_SCO && /* SCO small keypad */ + code >= 1 && code <= 6) { + char codes[] = "HL.FIG"; + if (code == 3) { + output[1] = '\x7F'; + end = 2; + } else { + output[end++] = '\033'; + output[end++] = '['; + output[end++] = codes[code-1]; + } + goto done; + } + if ((term->vt52_mode || cfg.funky_type == FUNKY_VT100P) && + code >= 11 && code <= 24) { + int offt = 0; + if (code > 15) + offt++; + if (code > 21) + offt++; + if (term->vt52_mode) { + output[end++] = '\033'; + output[end++] = code + 'P' - 11 - offt; + } else { + output[end++] = '\033'; + output[end++] = 'O'; + output[end++] = code + 'P' - 11 - offt; + } + goto done; + } + if (cfg.funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { + output[end++] = '\033'; + output[end++] = '['; + output[end++] = '['; + output[end++] = code + 'A' - 11; + goto done; + } + if (cfg.funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { + if (term->vt52_mode) { + output[end++] = '\033'; + output[end++] = code + 'P' - 11; + } else { + output[end++] = '\033'; + output[end++] = 'O'; + output[end++] = code + 'P' - 11; + } + goto done; + } + if (cfg.rxvt_homeend && (code == 1 || code == 4)) { + if (code == 1) { + output[end++] = '\033'; + output[end++] = '['; + output[end++] = 'H'; + } else { + output[end++] = '\033'; + output[end++] = 'O'; + output[end++] = 'w'; + } + goto done; + } + if (code) { + char buf[20]; + sprintf(buf, "\x1B[%d~", code); + for (i = 0; buf[i]; i++) + output[end++] = buf[i]; + goto done; + } + } + + /* + * Cursor keys. (This includes the numberpad cursor keys, + * if we haven't already done them due to app keypad mode.) + */ + { + int xkey = 0; + switch (cm) { + case NSUpArrowFunctionKey: xkey = 'A'; break; + case NSDownArrowFunctionKey: xkey = 'B'; break; + case NSRightArrowFunctionKey: xkey = 'C'; break; + case NSLeftArrowFunctionKey: xkey = 'D'; break; + } + if (xkey) { + /* + * The arrow keys normally do ESC [ A and so on. In + * app cursor keys mode they do ESC O A instead. + * Ctrl toggles the two modes. + */ + if (term->vt52_mode) { + output[end++] = '\033'; + output[end++] = xkey; + } else if (!term->app_cursor_keys ^ !(m & NSControlKeyMask)) { + output[end++] = '\033'; + output[end++] = 'O'; + output[end++] = xkey; + } else { + output[end++] = '\033'; + output[end++] = '['; + output[end++] = xkey; + } + goto done; + } + } + + done: + + /* + * Failing everything else, send the exact Unicode we got from + * OS X. + */ + if (end == start) { + if (n > lenof(output)-start) + n = lenof(output)-start; /* _shouldn't_ happen! */ + for (i = 0; i < n; i++) { + output[i+start] = [s characterAtIndex:i]; + } + end = n+start; + } + + if (use_coutput) { + assert(special); + assert(end < lenof(coutput)); + coutput[end] = '\0'; + ldisc_send(ldisc, coutput+start, -2, TRUE); + } else { + luni_send(ldisc, output+start, end-start, TRUE); + } +} + +- (int)fromBackend:(const char *)data len:(int)len isStderr:(int)is_stderr +{ + return term_data(term, is_stderr, data, len); +} + +@end + +int from_backend(void *frontend, int is_stderr, const char *data, int len) +{ + SessionWindow *win = (SessionWindow *)frontend; + return [win fromBackend:data len:len isStderr:is_stderr]; +} + +void frontend_keypress(void *handle) +{ + /* FIXME */ +} + +void connection_fatal(void *frontend, char *p, ...) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME: proper OS X GUI stuff */ + va_list ap; + fprintf(stderr, "FATAL ERROR: "); + va_start(ap, p); + vfprintf(stderr, p, ap); + va_end(ap); + fputc('\n', stderr); + exit(1); +} + +void notify_remote_exit(void *frontend) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void ldisc_update(void *frontend, int echo, int edit) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* + * In a GUI front end, this need do nothing. + */ +} + +void update_specials_menu(void *frontend) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * This is still called when mode==BELL_VISUAL, even though the + * visual bell is handled entirely within terminal.c, because we + * may want to perform additional actions on any kind of bell (for + * example, taskbar flashing in Windows). + */ +void beep(void *frontend, int mode) +{ + //SessionWindow *win = (SessionWindow *)frontend; + if (mode != BELL_VISUAL) + NSBeep(); +} + +int char_width(Context ctx, int uc) +{ + /* + * Under X, any fixed-width font really _is_ fixed-width. + * Double-width characters will be dealt with using a separate + * font. For the moment we can simply return 1. + */ + return 1; +} + +void palette_set(void *frontend, int n, int r, int g, int b) +{ + SessionWindow *win = (SessionWindow *)frontend; + + if (n >= 16) + n += 256 - 16; + if (n > NALLCOLOURS) + return; + [win setColour:n r:r/255.0 g:g/255.0 b:b/255.0]; + + /* + * FIXME: do we need an OS X equivalent of set_window_background? + */ +} + +void palette_reset(void *frontend) +{ + SessionWindow *win = (SessionWindow *)frontend; + Config *cfg = [win cfg]; + + /* This maps colour indices in cfg to those used in colours[]. */ + static const int ww[] = { + 256, 257, 258, 259, 260, 261, + 0, 8, 1, 9, 2, 10, 3, 11, + 4, 12, 5, 13, 6, 14, 7, 15 + }; + + int i; + + for (i = 0; i < NCFGCOLOURS; i++) { + [win setColour:ww[i] r:cfg->colours[i][0]/255.0 + g:cfg->colours[i][1]/255.0 b:cfg->colours[i][2]/255.0]; + } + + for (i = 0; i < NEXTCOLOURS; i++) { + if (i < 216) { + int r = i / 36, g = (i / 6) % 6, b = i % 6; + [win setColour:i+16 r:r/5.0 g:g/5.0 b:b/5.0]; + } else { + int shade = i - 216; + float fshade = (shade + 1) / (float)(NEXTCOLOURS - 216 + 1); + [win setColour:i+16 r:fshade g:fshade b:fshade]; + } + } + + /* + * FIXME: do we need an OS X equivalent of set_window_background? + */ +} + +Context get_ctx(void *frontend) +{ + SessionWindow *win = (SessionWindow *)frontend; + + /* + * Lock the drawing focus on the image inside the TerminalView. + */ + [win drawStartFinish:YES]; + + [[NSGraphicsContext currentContext] setShouldAntialias:YES]; + + /* + * Cocoa drawing functions don't take a graphics context: that + * parameter is implicit. Therefore, we'll use the frontend + * handle itself as the context, on the grounds that it's as + * good a thing to use as any. + */ + return frontend; +} + +void free_ctx(Context ctx) +{ + SessionWindow *win = (SessionWindow *)ctx; + + [win drawStartFinish:NO]; +} + +void do_text(Context ctx, int x, int y, wchar_t *text, int len, + unsigned long attr, int lattr) +{ + SessionWindow *win = (SessionWindow *)ctx; + + [win doText:text len:len x:x y:y attr:attr lattr:lattr]; +} + +void do_cursor(Context ctx, int x, int y, wchar_t *text, int len, + unsigned long attr, int lattr) +{ + SessionWindow *win = (SessionWindow *)ctx; + Config *cfg = [win cfg]; + int active, passive; + + if (attr & TATTR_PASCURS) { + attr &= ~TATTR_PASCURS; + passive = 1; + } else + passive = 0; + if ((attr & TATTR_ACTCURS) && cfg->cursor_type != 0) { + attr &= ~TATTR_ACTCURS; + active = 1; + } else + active = 0; + + [win doText:text len:len x:x y:y attr:attr lattr:lattr]; + + /* + * FIXME: now draw the various cursor types (both passive and + * active underlines and vertical lines, plus passive blocks). + */ +} + +/* + * Minimise or restore the window in response to a server-side + * request. + */ +void set_iconic(void *frontend, int iconic) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Move the window in response to a server-side request. + */ +void move_window(void *frontend, int x, int y) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Move the window to the top or bottom of the z-order in response + * to a server-side request. + */ +void set_zorder(void *frontend, int top) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Refresh the window in response to a server-side request. + */ +void refresh_window(void *frontend) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Maximise or restore the window in response to a server-side + * request. + */ +void set_zoomed(void *frontend, int zoomed) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Report whether the window is iconic, for terminal reports. + */ +int is_iconic(void *frontend) +{ + //SessionWindow *win = (SessionWindow *)frontend; + return NO; /* FIXME */ +} + +/* + * Report the window's position, for terminal reports. + */ +void get_window_pos(void *frontend, int *x, int *y) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Report the window's pixel size, for terminal reports. + */ +void get_window_pixels(void *frontend, int *x, int *y) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +/* + * Return the window or icon title. + */ +char *get_window_title(void *frontend, int icon) +{ + //SessionWindow *win = (SessionWindow *)frontend; + return NULL; /* FIXME */ +} + +void set_title(void *frontend, char *title) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void set_icon(void *frontend, char *title) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void set_sbar(void *frontend, int total, int start, int page) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void get_clip(void *frontend, wchar_t ** p, int *len) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void write_clip(void *frontend, wchar_t * data, int len, int must_deselect) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void request_paste(void *frontend) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void set_raw_mouse_mode(void *frontend, int activate) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void request_resize(void *frontend, int w, int h) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +} + +void sys_cursor(void *frontend, int x, int y) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* + * This is probably meaningless under OS X. FIXME: find out for + * sure. + */ +} + +void logevent(void *frontend, const char *string) +{ + //SessionWindow *win = (SessionWindow *)frontend; + /* FIXME */ +printf("logevent: %s\n", string); +} + +int font_dimension(void *frontend, int which)/* 0 for width, 1 for height */ +{ + //SessionWindow *win = (SessionWindow *)frontend; + return 1; /* FIXME */ +} + +void set_busy_status(void *frontend, int status) +{ + /* + * We need do nothing here: the OS X `application is busy' + * beachball pointer appears _automatically_ when the + * application isn't responding to GUI messages. + */ +} diff --git a/mkfiles.pl b/mkfiles.pl index 1860b5f4..17a63a6b 100755 --- a/mkfiles.pl +++ b/mkfiles.pl @@ -92,8 +92,8 @@ while () { if ($groups{$i}) { foreach $j (@{$groups{$i}}) { unshift @objs, $j; } } elsif (($i eq "[G]" or $i eq "[C]" or $i eq "[M]" or - $i eq "[X]" or $i eq "[U]") and defined $prog) { - $type = substr($i,1,1); + $i eq "[X]" or $i eq "[U]" or $i eq "[MX]") and defined $prog) { + $type = substr($i,1,(length $i)-2); } else { push @$listref, $i; } @@ -122,7 +122,8 @@ foreach $i (@prognames) { sort @{$programs{$i}}; $programs{$i} = [@list]; foreach $j (@list) { - # Dependencies for "x" start with "x.c". + # Dependencies for "x" start with "x.c" or "x.m" (depending on + # which one exists). # Dependencies for "x.res" start with "x.rc". # Dependencies for "x.rsrc" start with "x.r". # Both types of file are pushed on the list of files to scan. @@ -135,10 +136,9 @@ foreach $i (@prognames) { $file = "$1.r"; $depends{$j} = [$file]; push @scanlist, $file; - } elsif ($j =~ /\.lib$/) { - # libraries don't have dependencies - } else { + } elsif ($j !~ /\./) { $file = "$j.c"; + $file = "$j.m" unless &findfile($file); $depends{$j} = [$file]; push @scanlist, $file; } @@ -209,7 +209,7 @@ sub mfval($) { # Returns true if the argument is a known makefile type. Otherwise, # prints a warning and returns false; if (grep { $type eq $_ } - ("vc","vcproj","cygwin","borland","lcc","gtk","mpw")) { + ("vc","vcproj","cygwin","borland","lcc","gtk","mpw","osx")) { return 1; } warn "$.:unknown makefile type '$type'\n"; @@ -237,7 +237,9 @@ sub dirpfx { sub findfile { my ($name) = @_; - my $dir, $i, $outdir = ""; + my $dir; + my $i; + my $outdir = undef; unless (defined $findfilecache{$name}) { $i = 0; foreach $dir (@srcdirs) { @@ -245,7 +247,7 @@ sub findfile { $outdir=~s/^\.\///; } die "multiple instances of source file $name\n" if $i > 1; - $findfilecache{$name} = $outdir . $name; + $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef); } return $findfilecache{$name}; } @@ -263,7 +265,7 @@ sub objects { } elsif ($i =~ /^(.*)\.lib/) { $y = $1; ($x = $ltmpl) =~ s/X/$y/; - } else { + } elsif ($i !~ /\./) { ($x = $otmpl) =~ s/X/$i/; } push @ret, $x if $x ne ""; @@ -271,6 +273,19 @@ sub objects { return join " ", @ret; } +sub special { + my ($prog, $suffix) = @_; + my @ret; + my ($i, $x, $y); + @ret = (); + foreach $i (@{$programs{$prog}}) { + if (substr($i, (length $i) - (length $suffix)) eq $suffix) { + push @ret, $i; + } + } + return (scalar @ret) ? (join " ", @ret) : undef; +} + sub splitline { my ($line, $width, $splitchar) = @_; my ($result, $len); @@ -318,7 +333,7 @@ sub prognames { @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; - push @ret, $n if index($types, $type) >= 0; + push @ret, $n if index(":$types:", ":$type:") >= 0; } return @ret; } @@ -330,7 +345,7 @@ sub progrealnames { @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; - push @ret, $prog if index($types, $type) >= 0; + push @ret, $prog if index(":$types:", ":$type:") >= 0; } return @ret; } @@ -339,7 +354,7 @@ sub manpages { my ($types,$suffix) = @_; # assume that all UNIX programs have a man page - if($suffix eq "1" && $types =~ /X/) { + if($suffix eq "1" && $types =~ /:X:/) { return map("$_.1", &progrealnames($types)); } return (); @@ -382,9 +397,9 @@ if (defined $makefiles{'cygwin'}) { "\n". ".SUFFIXES:\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", "X.res.o", undef); print &splitline($prog . ".exe: " . $objstr), "\n"; @@ -459,16 +474,16 @@ if (defined $makefiles{'borland'}) { &splitline("\tbrcc32 \$(RCFL) -i \$(BCB)\\include -r". " -DNO_WINRESRC_H -DWIN32 -D_WIN32 -DWINVER=0x0401 \$*.rc",69)."\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; my $ap = ($type eq "G") ? "-aa" : "-ap"; print "\tilink32 $ap -Gn -L\$(BCB)\\lib \@$prog.rsp\n\n"; } - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; print $prog, ".rsp: \$(MAKEFILE)\n"; $objstr = &objects($p, "X.obj", undef, undef); @@ -539,15 +554,15 @@ if (defined $makefiles{'vc'}) { "LFLAGS = /incremental:no /fixed\n". "\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr . " $prog.rsp"), "\n"; print "\tlink \$(LFLAGS) -out:$prog.exe -map:$prog.map \@$prog.rsp\n\n"; } - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; print $prog, ".rsp: \$(MAKEFILE)\n"; $objstr = &objects($p, "X.obj", "X.res", "X.lib"); @@ -620,7 +635,7 @@ if (defined $makefiles{'vcproj'}) { %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; # Create the project files # Get names of all Windows projects (GUI and console) - my @prognames = &prognames("GC"); + my @prognames = &prognames("G:C"); foreach $progname (@prognames) { create_project(\%all_object_deps, $progname); } @@ -897,9 +912,9 @@ if (defined $makefiles{'gtk'}) { ".SUFFIXES:\n". "\n". "\n"; - print &splitline("all:" . join "", map { " $_" } &progrealnames("XU")); + print &splitline("all:" . join "", map { " $_" } &progrealnames("X:U")); print "\n\n"; - foreach $p (&prognames("XU")) { + foreach $p (&prognames("X:U")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; @@ -915,7 +930,7 @@ if (defined $makefiles{'gtk'}) { print "\n"; print $makefile_extra{'gtk'}; print "\nclean:\n". - "\trm -f *.o". (join "", map { " $_" } &progrealnames("XU")) . "\n"; + "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:U")) . "\n"; select STDOUT; close OUT; } @@ -1084,9 +1099,9 @@ if (defined $makefiles{'lcc'}) { "\n". "# Get include directory for resource compiler\n". "\n"; - print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("GC")); + print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; - foreach $p (&prognames("GC")) { + foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr ), "\n"; @@ -1116,3 +1131,76 @@ if (defined $makefiles{'lcc'}) { select STDOUT; close OUT; } + +if (defined $makefiles{'osx'}) { + $dirpfx = &dirpfx($makefiles{'osx'}, "/"); + + ##-- Mac OS X makefile + open OUT, ">$makefiles{'osx'}"; select OUT; + print + "# Makefile for $project_name under Mac OS X.\n". + "#\n# This file was created by `mkfiles.pl' from the `Recipe' file.\n". + "# DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead.\n"; + # gcc command line option is -D not /D + ($_ = $help) =~ s/=\/D/=-D/gs; + print $_; + print + "CC = \$(TOOLPATH)gcc\n". + "\n". + &splitline("CFLAGS = -O2 -Wall -Werror -g " . + (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". + "MLDFLAGS = -framework Cocoa\n". + "ULDFLAGS =\n". + &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U")) . + "\n" . + $makefile_extra{'osx'} . + "\n"; + foreach $p (&prognames("MX")) { + ($prog, $type) = split ",", $p; + $objstr = &objects($p, "X.o", undef, undef); + $icon = &special($p, ".icns"); + $infoplist = &special($p, "info.plist"); + print "${prog}.app:\n\tmkdir -p \$\@\n"; + print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n"; + print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; + $targets = "${prog}.app/Contents/MacOS/$prog"; + if (defined $icon) { + print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; + print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n"; + $targets .= " ${prog}.app/Contents/Resources/${prog}.icns"; + } + if (defined $infoplist) { + print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n"; + $targets .= " ${prog}.app/Contents/Info.plist"; + } + $targets .= " \$(${prog}_extra)"; + print &splitline("${prog}: $targets", 69) . "\n\n"; + print &splitline("${prog}.app/Contents/MacOS/$prog: ". + "${prog}.app/Contents/MacOS " . $objstr), "\n"; + $libstr = &objects($p, undef, undef, "-lX"); + print &splitline("\t\$(CC)" . $mw . " \$(MLDFLAGS) -o \$@ " . + $objstr . " $libstr", 69), "\n\n"; + } + foreach $p (&prognames("U")) { + ($prog, $type) = split ",", $p; + $objstr = &objects($p, "X.o", undef, undef); + print &splitline($prog . ": " . $objstr), "\n"; + $libstr = &objects($p, undef, undef, "-lX"); + print &splitline("\t\$(CC)" . $mw . " \$(ULDFLAGS) -o \$@ " . + $objstr . " $libstr", 69), "\n\n"; + } + foreach $d (&deps("X.o", undef, $dirpfx, "/")) { + print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), + "\n"; + $firstdep = $d->{deps}->[0]; + if ($firstdep =~ /\.c$/) { + print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n"; + } elsif ($firstdep =~ /\.m$/) { + print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(XFLAGS) \$(CFLAGS) -c \$<\n"; + } + } + print "\nclean:\n". + "\trm -f *.o *.dmg\n". + "\trm -rf *.app\n"; + select STDOUT; close OUT; +} diff --git a/puttyps.h b/puttyps.h index 976f598e..1370bfd2 100644 --- a/puttyps.h +++ b/puttyps.h @@ -9,6 +9,10 @@ #include "macstuff.h" +#elif defined(MACOSX) + +#include "osx.h" + #else #include "unix.h"