From d082ac49971a1c1a9e84c5b201c30aab675f154f Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 19 Nov 2002 02:13:46 +0000 Subject: [PATCH] Tentative merge of ben-mac-port (only dead for three years!) into the trunk. This doesn't include any mkfiles.pl glue, and is missing one or two other fixes. The terminal emulator is kind of working, though, as, I believe, is the store module. Everything else is yet to be done. git-svn-id: svn://svn.tartarus.org/sgt/putty@2226 cda61777-01e9-0310-a592-d414129be87e --- mac/mac.c | 544 +++++++++++++++++++++++ mac/mac.h | 79 ++++ mac/macresid.h | 49 ++ mac/macstore.c | 348 +++++++++++++++ mac/macstuff.h | 29 ++ mac/macterm.c | 1353 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mac/macucs.c | 76 ++++ putty.h | 7 + puttyps.h | 4 + settings.c | 2 + 10 files changed, 2491 insertions(+) create mode 100644 mac/mac.c create mode 100644 mac/mac.h create mode 100644 mac/macresid.h create mode 100644 mac/macstore.c create mode 100644 mac/macstuff.h create mode 100644 mac/macterm.c create mode 100644 mac/macucs.c diff --git a/mac/mac.c b/mac/mac.c new file mode 100644 index 00000000..add219b7 --- /dev/null +++ b/mac/mac.c @@ -0,0 +1,544 @@ +/* $Id: mac.c,v 1.1 2002/11/19 02:13:46 ben Exp $ */ +/* + * Copyright (c) 1999 Ben Harris + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +/* + * mac.c -- miscellaneous Mac-specific routines + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include /* putty.h needs size_t */ +#include /* for vsprintf */ + +#define PUTTY_DO_GLOBALS + +#include "macresid.h" +#include "putty.h" +#include "mac.h" + +QDGlobals qd; + +static int cold = 1; +struct mac_gestalts mac_gestalts; + +static void mac_startup(void); +static void mac_eventloop(void); +#pragma noreturn (mac_eventloop) +static void mac_event(EventRecord *); +static void mac_contentclick(WindowPtr, EventRecord *); +static void mac_growwindow(WindowPtr, EventRecord *); +static void mac_activatewindow(WindowPtr, EventRecord *); +static void mac_activateabout(WindowPtr, EventRecord *); +static void mac_updatewindow(WindowPtr); +static void mac_keypress(EventRecord *); +static int mac_windowtype(WindowPtr); +static void mac_menucommand(long); +static void mac_openabout(void); +static void mac_adjustcursor(RgnHandle); +static void mac_adjustmenus(void); +static void mac_closewindow(WindowPtr); +static void mac_zoomwindow(WindowPtr, short); +static void mac_shutdown(void); +#pragma noreturn (mac_shutdown) + +struct mac_windows { + WindowPtr about; + WindowPtr licence; +}; + +struct mac_windows windows; + +int main (int argc, char **argv) { + + mac_startup(); + mac_eventloop(); +} + +#pragma noreturn (main) + +static void mac_startup(void) { + Handle menuBar; + + /* Init Memory Manager */ + MaxApplZone(); + /* Init QuickDraw */ + InitGraf(&qd.thePort); + /* Init Font Manager */ + InitFonts(); + /* Init Window Manager */ + InitWindows(); + /* Init Menu Manager */ + InitMenus(); + /* Init TextEdit */ + TEInit(); + /* Init Dialog Manager */ + InitDialogs(nil); + cold = 0; + + /* Check for the Thread Manager. Bail out if it's not there. */ + if (Gestalt(gestaltThreadMgrAttr, &mac_gestalts.thdsattr) != noErr || + !(mac_gestalts.thdsattr & (1 << gestaltThreadMgrPresent)) || + &NewThread == kUnresolvedCFragSymbolAddress) + fatalbox("PuTTY requires the Thread Manager in order to operate. " + "The Thread Manager can be obtained from Apple Software " + "Updates."); + /* Find out if we've got Color Quickdraw */ + if (Gestalt(gestaltQuickdrawVersion, &mac_gestalts.qdvers) != noErr) + mac_gestalts.qdvers = gestaltOriginalQD; + /* ... and the Appearance Manager? */ + if (Gestalt(gestaltAppearanceVersion, &mac_gestalts.apprvers) != noErr) + if (Gestalt(gestaltAppearanceAttr, NULL) == noErr) + mac_gestalts.apprvers = 0x0100; + else + mac_gestalts.apprvers = 0; +#if TARGET_RT_MAC_CFM + /* Paranoia: Did we manage to pull in AppearanceLib? */ + if (&RegisterAppearanceClient == kUnresolvedCFragSymbolAddress) + mac_gestalts.apprvers = 0; +#endif + /* Mac OS 8.5 Control Manager (proportional scrollbars)? */ + if (Gestalt(gestaltControlMgrAttr, &mac_gestalts.cntlattr) != noErr) + mac_gestalts.cntlattr = 0; + /* Mac OS 8.5 Window Manager? */ + if (Gestalt(gestaltWindowMgrAttr, &mac_gestalts.windattr) != noErr) + mac_gestalts.windattr = 0; + + /* We've been tested with the Appearance Manager */ + if (mac_gestalts.apprvers != 0) + RegisterAppearanceClient(); + + menuBar = GetNewMBar(128); + if (menuBar == NULL) + fatalbox("Unable to create menu bar."); + SetMenuBar(menuBar); + AppendResMenu(GetMenuHandle(mApple), 'DRVR'); + mac_adjustmenus(); + DrawMenuBar(); + InitCursor(); + windows.about = NULL; + windows.licence = NULL; + + init_ucs(); +} + +static void mac_eventloop(void) { + Boolean gotevent; + EventRecord event; + RgnHandle cursrgn; + + cursrgn = NewRgn(); + for (;;) { + mac_adjustcursor(cursrgn); + gotevent = WaitNextEvent(everyEvent, &event, LONG_MAX, cursrgn); + mac_adjustcursor(cursrgn); + if (gotevent) + mac_event(&event); + YieldToAnyThread(); + } + DisposeRgn(cursrgn); +} + +static void mac_event(EventRecord *event) { + short part; + WindowPtr window; + Point pt; + + switch (event->what) { + case mouseDown: + part = FindWindow(event->where, &window); + switch (part) { + case inMenuBar: + mac_adjustmenus(); + mac_menucommand(MenuSelect(event->where)); + break; + case inSysWindow: + SystemClick(event, window); + break; + case inContent: + if (window != FrontWindow()) + /* XXX: check for movable modal dboxes? */ + SelectWindow(window); + else + mac_contentclick(window, event); + break; + case inGoAway: + if (TrackGoAway(window, event->where)) + mac_closewindow(window); + break; + case inDrag: + /* XXX: moveable modal check? */ + DragWindow(window, event->where, &qd.screenBits.bounds); + break; + case inGrow: + mac_growwindow(window, event); + break; + case inZoomIn: + case inZoomOut: + if (TrackBox(window, event->where, part)) + mac_zoomwindow(window, part); + break; + } + break; + case keyDown: + case autoKey: + mac_keypress(event); + break; + case activateEvt: + mac_activatewindow((WindowPtr)event->message, event); + break; + case updateEvt: + mac_updatewindow((WindowPtr)event->message); + break; + case diskEvt: + if (HiWord(event->message) != noErr) { + SetPt(&pt, 120, 120); + DIBadMount(pt, event->message); + } + break; + } +} + +static void mac_contentclick(WindowPtr window, EventRecord *event) { + short item; + + switch (mac_windowtype(window)) { + case wTerminal: + mac_clickterm(window, event); + break; + case wAbout: + if (DialogSelect(event, &(DialogPtr)window, &item)) + switch (item) { + case wiAboutLicence: + /* XXX: Do something */ + break; + } + break; + } +} + +static void mac_growwindow(WindowPtr window, EventRecord *event) { + + switch (mac_windowtype(window)) { + case wTerminal: + mac_growterm(window, event); + } +} + +static void mac_activatewindow(WindowPtr window, EventRecord *event) { + int active; + + active = (event->modifiers & activeFlag) != 0; + mac_adjustmenus(); + switch (mac_windowtype(window)) { + case wTerminal: + mac_activateterm(window, active); + break; + case wAbout: + mac_activateabout(window, event); + break; + } +} + +static void mac_activateabout(WindowPtr window, EventRecord *event) { + DialogItemType itemtype; + Handle itemhandle; + short item; + Rect itemrect; + int active; + + active = (event->modifiers & activeFlag) != 0; + GetDialogItem(window, wiAboutLicence, &itemtype, &itemhandle, &itemrect); + HiliteControl((ControlHandle)itemhandle, active ? 0 : 255); + DialogSelect(event, &window, &item); +} + +static void mac_updatewindow(WindowPtr window) { + + switch (mac_windowtype(window)) { + case wTerminal: + mac_updateterm(window); + break; + case wAbout: + BeginUpdate(window); + UpdateDialog(window, window->visRgn); + EndUpdate(window); + break; + case wLicence: + /* Do something */ + break; + } +} + +/* + * Work out what kind of window we're dealing with. + * Concept shamelessly nicked from SurfWriter. + */ +static int mac_windowtype(WindowPtr window) { + int kind; + + if (window == NULL) + return wNone; + kind = ((WindowPeek)window)->windowKind; + if (kind < 0) + return wDA; + if (GetWVariant(window) == zoomDocProc) + return wTerminal; + return GetWRefCon(window); +} + +/* + * Handle a key press + */ +static void mac_keypress(EventRecord *event) { + WindowPtr window; + + window = FrontWindow(); + /* + * Check for a command-key combination, but ignore it if it counts + * as a meta-key combination and we're in a terminal window. + */ + if (event->what == keyDown && (event->modifiers & cmdKey) /*&& + !((event->modifiers & cfg.meta_modifiers) == cfg.meta_modifiers && + mac_windowtype(window) == wTerminal)*/) { + mac_adjustmenus(); + mac_menucommand(MenuKey(event->message & charCodeMask)); + } else { + switch (mac_windowtype(window)) { + case wTerminal: + mac_keyterm(window, event); + break; + } + } +} + +static void mac_menucommand(long result) { + short menu, item; + Str255 da; + WindowPtr window; + + menu = HiWord(result); + item = LoWord(result); + window = FrontWindow(); + /* Things which do the same whatever window we're in. */ + switch (menu) { + case mApple: + switch (item) { + case iAbout: + mac_openabout(); + goto done; + default: + GetMenuItemText(GetMenuHandle(mApple), item, da); + OpenDeskAcc(da); + goto done; + } + break; + case mFile: + switch (item) { + case iNew: + mac_newsession(); + goto done; + case iClose: + mac_closewindow(window); + goto done; + case iQuit: + mac_shutdown(); + goto done; + } + break; + } + /* If we get here, handling is up to window-specific code. */ + switch (mac_windowtype(window)) { + case wTerminal: + mac_menuterm(window, menu, item); + break; + } + done: + HiliteMenu(0); +} + +static void mac_openabout(void) { + DialogItemType itemtype; + Handle item; + VersRecHndl vers; + Rect box; + StringPtr longvers; + + if (windows.about) + SelectWindow(windows.about); + else { + windows.about = GetNewDialog(wAbout, NULL, (WindowPtr)-1); + /* XXX check we're using the right resource file? */ + vers = (VersRecHndl)GetResource('vers', 1); + assert(vers != NULL && *vers != NULL); + longvers = (*vers)->shortVersion + (*vers)->shortVersion[0] + 1; + GetDialogItem(windows.about, wiAboutVersion, &itemtype, &item, &box); + assert(itemtype & kStaticTextDialogItem); + SetDialogItemText(item, longvers); + ShowWindow(windows.about); + } +} + +static void mac_closewindow(WindowPtr window) { + + switch (mac_windowtype(window)) { + case wDA: + CloseDeskAcc(((WindowPeek)window)->windowKind); + break; + case wTerminal: + /* FIXME: end session and stuff */ + break; + case wAbout: + windows.about = NULL; + CloseWindow(window); + break; + default: + CloseWindow(window); + break; + } +} + +static void mac_zoomwindow(WindowPtr window, short part) { + + /* FIXME: do something */ +} + +/* + * Make the menus look right before the user gets to see them. + */ +static void mac_adjustmenus(void) { + WindowPtr window; + MenuHandle menu; + + window = FrontWindow(); + menu = GetMenuHandle(mApple); + EnableItem(menu, 0); + EnableItem(menu, iAbout); + + menu = GetMenuHandle(mFile); + EnableItem(menu, 0); + EnableItem(menu, iNew); + if (window != NULL) + EnableItem(menu, iClose); + else + DisableItem(menu, iClose); + EnableItem(menu, iQuit); + + switch (mac_windowtype(window)) { + case wTerminal: + mac_adjusttermmenus(window); + break; + default: + menu = GetMenuHandle(mEdit); + DisableItem(menu, 0); + break; + } + DrawMenuBar(); +} + +/* + * Make sure the right cursor's being displayed. + */ +static void mac_adjustcursor(RgnHandle cursrgn) { + Point mouse; + WindowPtr window, front; + short part; + + GetMouse(&mouse); + LocalToGlobal(&mouse); + part = FindWindow(mouse, &window); + front = FrontWindow(); + if (part != inContent || window == NULL || window != front) { + /* Cursor isn't in the front window, so switch to arrow */ + SetCursor(&qd.arrow); + SetRectRgn(cursrgn, SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX); + if (front != NULL) + DiffRgn(cursrgn, front->visRgn, cursrgn); + } else { + switch (mac_windowtype(window)) { + case wTerminal: + mac_adjusttermcursor(window, mouse, cursrgn); + break; + default: + SetCursor(&qd.arrow); + CopyRgn(window->visRgn, cursrgn); + break; + } + } +} + +static void mac_shutdown(void) { + + exit(0); +} + +void fatalbox(char *fmt, ...) { + va_list ap; + Str255 stuff; + + va_start(ap, fmt); + /* We'd like stuff to be a Pascal string */ + stuff[0] = vsprintf((char *)(&stuff[1]), fmt, ap); + va_end(ap); + ParamText(stuff, NULL, NULL, NULL); + StopAlert(128, nil); + exit(1); +} + +void modalfatalbox(char *fmt, ...) { + va_list ap; + Str255 stuff; + + va_start(ap, fmt); + /* We'd like stuff to be a Pascal string */ + stuff[0] = vsprintf((char *)(&stuff[1]), fmt, ap); + va_end(ap); + ParamText(stuff, NULL, NULL, NULL); + StopAlert(128, nil); + exit(1); +} + +/* + * Local Variables: + * c-file-style: "simon" + * End: + */ diff --git a/mac/mac.h b/mac/mac.h new file mode 100644 index 00000000..1c1c64e1 --- /dev/null +++ b/mac/mac.h @@ -0,0 +1,79 @@ +/* + * mac.h -- macintosh-specific declarations + */ + +#ifndef PUTTY_MAC_H +#define PUTTY_MAC_H + +#include +#include +#include +#include +#include + +struct mac_gestalts { + long qdvers; + long apprvers; + long cntlattr; + long windattr; + long thdsattr; +}; + +extern struct mac_gestalts mac_gestalts; + +#define HAVE_COLOR_QD() (mac_gestalts.qdvers > gestaltOriginalQD) + +/* from macterm.c */ +extern void mac_newsession(void); +extern void mac_activateterm(WindowPtr, Boolean); +extern void mac_adjusttermcursor(WindowPtr, Point, RgnHandle); +extern void mac_adjusttermmenus(WindowPtr); +extern void mac_updateterm(WindowPtr); +extern void mac_clickterm(WindowPtr, EventRecord *); +extern void mac_growterm(WindowPtr, EventRecord *); +extern void mac_keyterm(WindowPtr, EventRecord *); +extern void mac_menuterm(WindowPtr, short, short); +/* from maccfg.c */ +extern void mac_loadconfig(Config *); +/* from macnet.c */ +extern void macnet_eventcheck(void); + +typedef struct { + /* Config that created this session */ + Config cfg; + /* Terminal emulator internal state */ + Terminal *term; + /* Display state */ + int font_width, font_height; + int has_focus; + /* Line discipline */ + void *ldisc; + /* Backend */ + Backend *back; + void *backhandle; + char *realhost; + /* Logging */ + void *logctx; + /* Conveniences */ + unsigned long attr_mask; /* Mask of attributes to display */ + + /* Mac-specific elements */ + short fontnum; + int font_ascent; + int font_leading; + int font_boldadjust; + WindowPtr window; + PaletteHandle palette; + ControlHandle scrollbar; + WCTabHandle wctab; + ThreadID thread; + int raw_mouse; +} Session; + +#endif + +/* + * Local Variables: + * c-file-style: "simon" + * End: + */ diff --git a/mac/macresid.h b/mac/macresid.h new file mode 100644 index 00000000..b98346b1 --- /dev/null +++ b/mac/macresid.h @@ -0,0 +1,49 @@ +/* $Id: macresid.h,v 1.1 2002/11/19 02:13:46 ben Exp $ */ + +/* + * macresid.h -- Mac resource IDs + * + * This file is shared by C and Rez source files + */ + + +/* Menu bar IDs */ +#define MBAR_Main 128 + +/* Menu IDs */ +#define mApple 128 +#define mFile 129 +#define mEdit 130 + +/* Menu Items */ +/* Apple menu */ +#define iAbout 1 +/* File menu */ +#define iNew 1 +#define iClose 2 +#define iQuit 4 +/* Edit menu */ +#define iUndo 1 +#define iCut 3 +#define iCopy 4 +#define iPaste 5 +#define iClear 6 +#define iSelectAll 7 + +/* Window types (and resource IDs) */ +#define wNone 0 /* Dummy value for no window */ +#define wDA 1 /* Dummy value for desk accessory */ +#define wFatal 128 +#define wAbout 129 +#define wiAboutLicence 1 +#define wiAboutVersion 3 +#define wTerminal 130 +#define wLicence 131 + +/* Controls */ +#define cVScroll 128 + +/* Preferences */ +#define PREF_wordness_type 'wORD' + +#define PREF_settings 1024 diff --git a/mac/macstore.c b/mac/macstore.c new file mode 100644 index 00000000..fa6493a0 --- /dev/null +++ b/mac/macstore.c @@ -0,0 +1,348 @@ +/* $Id: macstore.c,v 1.1 2002/11/19 02:13:46 ben Exp $ */ + +/* + * macstore.c: Macintosh-specific impementation of the interface + * defined in storage.h + */ + +#include +#include +#include +#include +#include + +#include + +#include "putty.h" +#include "storage.h" + +#define PUTTY_CREATOR FOUR_CHAR_CODE('pTTY') +#define SESS_TYPE FOUR_CHAR_CODE('Sess') + + +OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID); +OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit); + +/* + * We store each session as a file in the "PuTTY" sub-directory of the + * preferences folder. Each (key,value) pair is stored as a resource. + */ + +OSErr get_session_dir(Boolean makeit, short *pVRefNum, long *pDirID) { + OSErr error = noErr; + short prefVRefNum; + FSSpec puttydir, sessdir; + long prefDirID, puttyDirID, sessDirID; + + error = FindFolder(kOnSystemDisk, kPreferencesFolderType, makeit, + &prefVRefNum, &prefDirID); + if (error != noErr) goto out; + + error = FSMakeFSSpec(prefVRefNum, prefDirID, "\pPuTTY", &puttydir); + if (error != noErr && error != fnfErr) goto out; + error = FSpGetDirID(&puttydir, &puttyDirID, makeit); + if (error != noErr) goto out; + + error = FSMakeFSSpec(prefVRefNum, puttyDirID, "\pSaved Sessions", + &sessdir); + if (error != noErr && error != fnfErr) goto out; + error = FSpGetDirID(&sessdir, &sessDirID, makeit); + if (error != noErr) goto out; + + *pVRefNum = prefVRefNum; + *pDirID = sessDirID; + + out: + return error; +} + +OSErr FSpGetDirID(FSSpec *f, long *idp, Boolean makeit) { + CInfoPBRec pb; + OSErr error = noErr; + + pb.dirInfo.ioNamePtr = f->name; + pb.dirInfo.ioVRefNum = f->vRefNum; + pb.dirInfo.ioDrDirID = f->parID; + pb.dirInfo.ioFDirIndex = 0; + error = PBGetCatInfoSync(&pb); + if (error == fnfErr && makeit) + return FSpDirCreate(f, smSystemScript, idp); + if (error != noErr) goto out; + if ((pb.dirInfo.ioFlAttrib & ioDirMask) == 0) { + error = dirNFErr; + goto out; + } + *idp = pb.dirInfo.ioDrDirID; + + out: + return error; +} + +struct write_settings { + int fd; + FSSpec tmpfile; + FSSpec dstfile; +}; + +void *open_settings_w(char *sessionname) { + short sessVRefNum, tmpVRefNum; + long sessDirID, tmpDirID; + FSSpec sessfile; + OSErr error; + Str255 psessionname; + struct write_settings *ws; + + ws = safemalloc(sizeof *ws); + error = get_session_dir(kCreateFolder, &sessVRefNum, &sessDirID); + if (error != noErr) goto out; + + c2pstrcpy(psessionname, sessionname); + error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &ws->dstfile); + if (error != noErr && error != fnfErr) goto out; + if (error == fnfErr) { + FSpCreateResFile(&ws->dstfile, PUTTY_CREATOR, SESS_TYPE, + smSystemScript); + if ((error = ResError()) != noErr) goto out; + } + + /* Create a temporary file to save to first. */ + error = FindFolder(sessVRefNum, kTemporaryFolderType, kCreateFolder, + &tmpVRefNum, &tmpDirID); + if (error != noErr) goto out; + error = FSMakeFSSpec(tmpVRefNum, tmpDirID, psessionname, &ws->tmpfile); + if (error != noErr && error != fnfErr) goto out; + if (error == noErr) { + error = FSpDelete(&ws->tmpfile); + if (error != noErr) goto out; + } + FSpCreateResFile(&ws->tmpfile, PUTTY_CREATOR, SESS_TYPE, smSystemScript); + if ((error = ResError()) != noErr) goto out; + + ws->fd = FSpOpenResFile(&ws->tmpfile, fsWrPerm); + if (ws->fd == -1) {error = ResError(); goto out;} + + return ws; + + out: + safefree(ws); + fatalbox("Failed to open session for write (%d)", error); +} + +void write_setting_s(void *handle, char *key, char *value) { + int fd = *(int *)handle; + Handle h; + int id; + OSErr error; + + UseResFile(fd); + if (ResError() != noErr) + fatalbox("Failed to open saved session (%d)", ResError()); + + error = PtrToHand(value, &h, strlen(value)); + if (error != noErr) + fatalbox("Failed to allocate memory"); + /* Put the data in a resource. */ + id = Unique1ID(FOUR_CHAR_CODE('TEXT')); + if (ResError() != noErr) + fatalbox("Failed to get ID for resource %s (%d)", key, ResError()); + addresource(h, FOUR_CHAR_CODE('TEXT'), id, key); + if (ResError() != noErr) + fatalbox("Failed to add resource %s (%d)", key, ResError()); +} + +void write_setting_i(void *handle, char *key, int value) { + int fd = *(int *)handle; + Handle h; + int id; + OSErr error; + + UseResFile(fd); + if (ResError() != noErr) + fatalbox("Failed to open saved session (%d)", ResError()); + + /* XXX assume all systems have the same "int" format */ + error = PtrToHand(&value, &h, sizeof(int)); + if (error != noErr) + fatalbox("Failed to allocate memory (%d)", error); + + /* Put the data in a resource. */ + id = Unique1ID(FOUR_CHAR_CODE('Int ')); + if (ResError() != noErr) + fatalbox("Failed to get ID for resource %s (%d)", key, ResError()); + addresource(h, FOUR_CHAR_CODE('Int '), id, key); + if (ResError() != noErr) + fatalbox("Failed to add resource %s (%d)", key, ResError()); +} + +void close_settings_w(void *handle) { + struct write_settings *ws = handle; + OSErr error; + + CloseResFile(ws->fd); + if ((error = ResError()) != noErr) + goto out; + error = FSpExchangeFiles(&ws->tmpfile, &ws->dstfile); + if (error != noErr) goto out; + error = FSpDelete(&ws->tmpfile); + if (error != noErr) goto out; + return; + + out: + fatalbox("Close of saved session failed (%d)", error); + safefree(handle); +} + +void *open_settings_r(char *sessionname) { + short sessVRefNum; + long sessDirID; + FSSpec sessfile; + OSErr error; + Str255 psessionname; + int fd; + int *handle; + + error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID); + + c2pstrcpy(psessionname, sessionname); + error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile); + if (error != noErr) goto out; + fd = FSpOpenResFile(&sessfile, fsRdPerm); + if (fd == 0) {error = ResError(); goto out;} + + handle = safemalloc(sizeof *handle); + *handle = fd; + return handle; + + out: + return NULL; +} + + +char *read_setting_s(void *handle, char *key, char *buffer, int buflen) { + int fd; + Handle h; + OSErr error; + + if (handle == NULL) goto out; + fd = *(int *)handle; + UseResFile(fd); + if (ResError() != noErr) goto out; + h = get1namedresource(FOUR_CHAR_CODE('TEXT'), key); + if (h == NULL) goto out; + + if (GetHandleSize(h) > buflen) goto out; + p2cstrcpy(buffer, (StringPtr)*h); + ReleaseResource(h); + if (ResError() != noErr) goto out; + return buffer; + + out: + return NULL; +} + +int read_setting_i(void *handle, char *key, int defvalue) { + int fd; + Handle h; + OSErr error; + int value; + + if (handle == NULL) goto out; + fd = *(int *)handle; + UseResFile(fd); + if (ResError() != noErr) goto out; + h = get1namedresource(FOUR_CHAR_CODE('Int '), key); + if (h == NULL) goto out; + value = *(int *)*h; + ReleaseResource(h); + if (ResError() != noErr) goto out; + return value; + + out: + return defvalue; +} + +void close_settings_r(void *handle) { + int fd; + + if (handle == NULL) return; + fd = *(int *)handle; + CloseResFile(fd); + if (ResError() != noErr) + fatalbox("Close of saved session failed (%d)", ResError()); + safefree(handle); +} + +void del_settings(char *sessionname) { + OSErr error; + FSSpec sessfile; + short sessVRefNum; + long sessDirID; + Str255 psessionname; + + error = get_session_dir(kDontCreateFolder, &sessVRefNum, &sessDirID); + + c2pstrcpy(psessionname, sessionname); + error = FSMakeFSSpec(sessVRefNum, sessDirID, psessionname, &sessfile); + if (error != noErr) goto out; + + error = FSpDelete(&sessfile); + return; + out: + fatalbox("Delete session failed (%d)", error); +} + +struct enum_settings_state { + short vRefNum; + long dirID; + int index; +}; + +void *enum_settings_start(void) { + OSErr error; + struct enum_settings_state *state; + + state = safemalloc(sizeof(*state)); + error = get_session_dir(kDontCreateFolder, &state->vRefNum, &state->dirID); + if (error != noErr) { + safefree(state); + return NULL; + } + state->index = 1; + return state; +} + +char *enum_settings_next(void *handle, char *buffer, int buflen) { + struct enum_settings_state *e = handle; + CInfoPBRec pb; + OSErr error = noErr; + Str255 name; + + if (e == NULL) return NULL; + do { + pb.hFileInfo.ioNamePtr = name; + pb.hFileInfo.ioVRefNum = e->vRefNum; + pb.hFileInfo.ioDirID = e->dirID; + pb.hFileInfo.ioFDirIndex = e->index++; + error = PBGetCatInfoSync(&pb); + if (error != noErr) return NULL; + } while (!((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 && + pb.hFileInfo.ioFlFndrInfo.fdCreator == PUTTY_CREATOR && + pb.hFileInfo.ioFlFndrInfo.fdType == SESS_TYPE && + name[0] < buflen)); + + p2cstrcpy(buffer, name); + return buffer; +} + +void enum_settings_finish(void *handle) { + + safefree(handle); +} + + +/* + * Emacs magic: + * Local Variables: + * c-file-style: "simon" + * End: + */ diff --git a/mac/macstuff.h b/mac/macstuff.h new file mode 100644 index 00000000..e8a8fac8 --- /dev/null +++ b/mac/macstuff.h @@ -0,0 +1,29 @@ +/* + * macstuff.h -- Mac-specific definitions visible to the rest of PuTTY. + */ + +typedef void *Context; /* FIXME */ + +/* + * On the Mac, Unicode text copied to the clipboard has U+2028 line separators. + * Non-Unicode text will have these converted to CR along with the rest of the + * content. + */ +#define SEL_NL { 0x2028 } + + +#include /* Timing related goo */ + +#define GETTICKCOUNT TickCount +#define CURSORBLINK GetCaretTime() +#define TICKSPERSEC 60 + +#define DEFAULT_CODEPAGE 0 /* FIXME: no idea how to do this */ + +#define WCHAR wchar_t +#define BYTE unsigned char + +/* To make it compile */ + +#include +extern int vsnprintf(char *, size_t, char const *, va_list); diff --git a/mac/macterm.c b/mac/macterm.c new file mode 100644 index 00000000..ebbc8c71 --- /dev/null +++ b/mac/macterm.c @@ -0,0 +1,1353 @@ +/* $Id: macterm.c,v 1.1 2002/11/19 02:13:46 ben Exp $ */ +/* + * Copyright (c) 1999 Simon Tatham + * Copyright (c) 1999, 2002 Ben Harris + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * macterm.c -- Macintosh terminal front-end + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "macresid.h" +#include "putty.h" +#include "mac.h" +#include "terminal.h" + +#define NCOLOURS (lenof(((Config *)0)->colours)) + +#define DEFAULT_FG 16 +#define DEFAULT_FG_BOLD 17 +#define DEFAULT_BG 18 +#define DEFAULT_BG_BOLD 19 +#define CURSOR_FG 20 +#define CURSOR_FG_BOLD 21 +#define CURSOR_BG 22 +#define CURSOR_BG_BOLD 23 + +#define PTOCC(x) ((x) < 0 ? -(-(x - s->font_width - 1) / s->font_width) : \ + (x) / s->font_width) +#define PTOCR(y) ((y) < 0 ? -(-(y - s->font_height - 1) / s->font_height) : \ + (y) / s->font_height) + +static void mac_initfont(Session *); +static void mac_initpalette(Session *); +static void mac_adjustwinbg(Session *); +static void mac_adjustsize(Session *, int, int); +static void mac_drawgrowicon(Session *s); +static pascal void mac_scrolltracker(ControlHandle, short); +static pascal void do_text_for_device(short, short, GDHandle, long); +static pascal void mac_set_attr_mask(short, short, GDHandle, long); +static int mac_keytrans(Session *, EventRecord *, unsigned char *); +static void text_click(Session *, EventRecord *); + +void pre_paint(Session *s); +void post_paint(Session *s); + +#if TARGET_RT_MAC_CFM +static RoutineDescriptor mac_scrolltracker_upp = + BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, + (ProcPtr)mac_scrolltracker); +static RoutineDescriptor do_text_for_device_upp = + BUILD_ROUTINE_DESCRIPTOR(uppDeviceLoopDrawingProcInfo, + (ProcPtr)do_text_for_device); +static RoutineDescriptor mac_set_attr_mask_upp = + BUILD_ROUTINE_DESCRIPTOR(uppDeviceLoopDrawingProcInfo, + (ProcPtr)mac_set_attr_mask); +#else /* not TARGET_RT_MAC_CFM */ +#define mac_scrolltracker_upp mac_scrolltracker +#define do_text_for_device_upp do_text_for_device +#define mac_set_attr_mask_upp mac_set_attr_mask +#endif /* not TARGET_RT_MAC_CFM */ + +static void inbuf_putc(Session *s, int c) { + char ch = c; + + from_backend(s->term, 0, &ch, 1); +} + +static void inbuf_putstr(Session *s, const char *c) { + + from_backend(s->term, 0, (char *)c, strlen(c)); +} + +static void display_resource(Session *s, unsigned long type, short id) { + Handle h; + int len, i; + char *t; + + h = GetResource(type, id); + if (h == NULL) + fatalbox("Can't get test resource"); + len = GetResourceSizeOnDisk(h); + DetachResource(h); + HNoPurge(h); + HLock(h); + t = *h; + from_backend(s->term, 0, t, len); + term_out(s->term); + DisposeHandle(h); +} + + +void mac_newsession(void) { + Session *s; + UInt32 starttime; + char msg[128]; + OSErr err; + + /* This should obviously be initialised by other means */ + s = smalloc(sizeof(*s)); + memset(s, 0, sizeof(*s)); + do_defaults(NULL, &s->cfg); + s->back = &loop_backend; + + /* XXX: Own storage management? */ + if (HAVE_COLOR_QD()) + s->window = GetNewCWindow(wTerminal, NULL, (WindowPtr)-1); + else + s->window = GetNewWindow(wTerminal, NULL, (WindowPtr)-1); + SetWRefCon(s->window, (long)s); + s->scrollbar = GetNewControl(cVScroll, s->window); + s->term = term_init(s); + + s->logctx = log_init(s); + term_provide_logctx(s->term, s->logctx); + + s->back->init(s->term, &s->backhandle, "localhost", 23, &s->realhost, 0); + s->back->provide_logctx(s->backhandle, s->logctx); + + term_provide_resize_fn(s->term, s->back->size, s->backhandle); + + mac_adjustsize(s, s->cfg.height, s->cfg.width); + term_size(s->term, s->cfg.height, s->cfg.width, s->cfg.savelines); + + s->ldisc = ldisc_create(s->term, s->back, s->backhandle, s); + ldisc_send(s->ldisc, NULL, 0, 0);/* cause ldisc to notice changes */ + + mac_initfont(s); + mac_initpalette(s); + s->attr_mask = ATTR_MASK; + if (HAVE_COLOR_QD()) { + /* Set to FALSE to not get palette updates in the background. */ + SetPalette(s->window, s->palette, TRUE); + ActivatePalette(s->window); + } + ShowWindow(s->window); + starttime = TickCount(); + display_resource(s, 'pTST', 128); + sprintf(msg, "Elapsed ticks: %d\015\012", TickCount() - starttime); + inbuf_putstr(s, msg); + term_out(s->term); +} + +static void mac_initfont(Session *s) { + Str255 macfont; + FontInfo fi; + + SetPort(s->window); + macfont[0] = sprintf((char *)&macfont[1], "%s", s->cfg.font); + GetFNum(macfont, &s->fontnum); + TextFont(s->fontnum); + TextFace(s->cfg.fontisbold ? bold : 0); + TextSize(s->cfg.fontheight); + GetFontInfo(&fi); + s->font_width = CharWidth('W'); /* Well, it's what NCSA uses. */ + s->font_ascent = fi.ascent; + s->font_leading = fi.leading; + s->font_height = s->font_ascent + fi.descent + s->font_leading; + if (!s->cfg.bold_colour) { + TextFace(bold); + s->font_boldadjust = s->font_width - CharWidth('W'); + } else + s->font_boldadjust = 0; + mac_adjustsize(s, s->term->rows, s->term->cols); +} + +/* + * To be called whenever the window size changes. + * rows and cols should be desired values. + * It's assumed the terminal emulator will be informed, and will set rows + * and cols for us. + */ +static void mac_adjustsize(Session *s, int newrows, int newcols) { + int winwidth, winheight; + + winwidth = newcols * s->font_width + 15; + winheight = newrows * s->font_height; + SizeWindow(s->window, winwidth, winheight, true); + HideControl(s->scrollbar); + MoveControl(s->scrollbar, winwidth - 15, -1); + SizeControl(s->scrollbar, 16, winheight - 13); + ShowControl(s->scrollbar); +} + +static void mac_initpalette(Session *s) { + int i; + + /* + * Most colours should be inhibited on 2bpp displays. + * Palette manager documentation suggests inhibiting all tolerant colours + * on greyscale displays. + */ +#define PM_NORMAL pmTolerant | pmInhibitC2 | \ + pmInhibitG2 | pmInhibitG4 | pmInhibitG8 | pmInhibitC2 +#define PM_TOLERANCE 0x2000 + s->palette = NewPalette(22, NULL, PM_NORMAL, PM_TOLERANCE); + if (s->palette == NULL) + fatalbox("Unable to create palette"); + /* In 2bpp, these are the colours we want most. */ + SetEntryUsage(s->palette, DEFAULT_BG, + PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE); + SetEntryUsage(s->palette, DEFAULT_FG, + PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE); + SetEntryUsage(s->palette, DEFAULT_FG_BOLD, + PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE); + SetEntryUsage(s->palette, CURSOR_FG, + PM_NORMAL &~ pmInhibitC2, PM_TOLERANCE); + palette_reset(s); +} + +/* + * Set the background colour of the window correctly. Should be + * called whenever the default background changes. + */ +static void mac_adjustwinbg(Session *s) { + + if (!HAVE_COLOR_QD()) + return; +#if TARGET_RT_CFM /* XXX doesn't link (at least for 68k) */ + if (mac_gestalts.windattr & gestaltWindowMgrPresent) + SetWindowContentColor(s->window, + &(*s->palette)->pmInfo[DEFAULT_BG].ciRGB); + else +#endif + { + if (s->wctab == NULL) + s->wctab = (WCTabHandle)NewHandle(sizeof(**s->wctab)); + if (s->wctab == NULL) + return; /* do without */ + (*s->wctab)->wCSeed = 0; + (*s->wctab)->wCReserved = 0; + (*s->wctab)->ctSize = 0; + (*s->wctab)->ctTable[0].value = wContentColor; + (*s->wctab)->ctTable[0].rgb = (*s->palette)->pmInfo[DEFAULT_BG].ciRGB; + SetWinColor(s->window, s->wctab); + } +} + +/* + * Set the cursor shape correctly + */ +void mac_adjusttermcursor(WindowPtr window, Point mouse, RgnHandle cursrgn) { + Session *s; + ControlHandle control; + short part; + int x, y; + + SetPort(window); + s = (Session *)GetWRefCon(window); + GlobalToLocal(&mouse); + part = FindControl(mouse, window, &control); + if (control == s->scrollbar) { + SetCursor(&qd.arrow); + RectRgn(cursrgn, &(*s->scrollbar)->contrlRect); + SectRgn(cursrgn, window->visRgn, cursrgn); + } else { + x = mouse.h / s->font_width; + y = mouse.v / s->font_height; + if (s->raw_mouse) + SetCursor(&qd.arrow); + else + SetCursor(*GetCursor(iBeamCursor)); + /* Ask for shape changes if we leave this character cell. */ + SetRectRgn(cursrgn, x * s->font_width, y * s->font_height, + (x + 1) * s->font_width, (y + 1) * s->font_height); + SectRgn(cursrgn, window->visRgn, cursrgn); + } +} + +/* + * Enable/disable menu items based on the active terminal window. + */ +void mac_adjusttermmenus(WindowPtr window) { + Session *s; + MenuHandle menu; + long offset; + + s = (Session *)GetWRefCon(window); + menu = GetMenuHandle(mEdit); + EnableItem(menu, 0); + DisableItem(menu, iUndo); + DisableItem(menu, iCut); + if (1/*s->term->selstate == SELECTED*/) + EnableItem(menu, iCopy); + else + DisableItem(menu, iCopy); + if (GetScrap(NULL, 'TEXT', &offset) == noTypeErr) + DisableItem(menu, iPaste); + else + EnableItem(menu, iPaste); + DisableItem(menu, iClear); + EnableItem(menu, iSelectAll); +} + +void mac_menuterm(WindowPtr window, short menu, short item) { + Session *s; + + s = (Session *)GetWRefCon(window); + switch (menu) { + case mEdit: + switch (item) { + case iCopy: + /* term_copy(s); */ + break; + case iPaste: + term_do_paste(s->term); + break; + } + } +} + +void mac_clickterm(WindowPtr window, EventRecord *event) { + Session *s; + Point mouse; + ControlHandle control; + int part; + + s = (Session *)GetWRefCon(window); + SetPort(window); + mouse = event->where; + GlobalToLocal(&mouse); + part = FindControl(mouse, window, &control); + if (control == s->scrollbar) { + switch (part) { + case kControlIndicatorPart: + if (TrackControl(control, mouse, NULL) == kControlIndicatorPart) + term_scroll(s->term, +1, GetControlValue(control)); + break; + case kControlUpButtonPart: + case kControlDownButtonPart: + case kControlPageUpPart: + case kControlPageDownPart: + TrackControl(control, mouse, &mac_scrolltracker_upp); + break; + } + } else { + text_click(s, event); + } +} + +static void text_click(Session *s, EventRecord *event) { + Point localwhere; + int row, col; + static UInt32 lastwhen = 0; + static Session *lastsess = NULL; + static int lastrow = -1, lastcol = -1; + static Mouse_Action lastact = MA_NOTHING; + + SetPort(s->window); + localwhere = event->where; + GlobalToLocal(&localwhere); + + col = PTOCC(localwhere.h); + row = PTOCR(localwhere.v); + if (event->when - lastwhen < GetDblTime() && + row == lastrow && col == lastcol && s == lastsess) + lastact = (lastact == MA_CLICK ? MA_2CLK : + lastact == MA_2CLK ? MA_3CLK : + lastact == MA_3CLK ? MA_CLICK : MA_NOTHING); + else + lastact = MA_CLICK; + /* Fake right button with shift key */ + term_mouse(s->term, event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + lastact, col, row, event->modifiers & shiftKey, + event->modifiers & controlKey, event->modifiers & optionKey); + lastsess = s; + lastrow = row; + lastcol = col; + while (StillDown()) { + GetMouse(&localwhere); + col = PTOCC(localwhere.h); + row = PTOCR(localwhere.v); + term_mouse(s->term, + event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + MA_DRAG, col, row, event->modifiers & shiftKey, + event->modifiers & controlKey, + event->modifiers & optionKey); + if (row > s->term->rows - 1) + term_scroll(s->term, 0, row - (s->term->rows - 1)); + else if (row < 0) + term_scroll(s->term, 0, row); + } + term_mouse(s->term, event->modifiers & shiftKey ? MBT_RIGHT : MBT_LEFT, + MA_RELEASE, col, row, event->modifiers & shiftKey, + event->modifiers & controlKey, event->modifiers & optionKey); + lastwhen = TickCount(); +} + +Mouse_Button translate_button(void *frontend, Mouse_Button button) +{ + + switch (button) { + case MBT_LEFT: + return MBT_SELECT; + case MBT_RIGHT: + return MBT_EXTEND; + default: + return 0; + } +} + +void write_clip(void *cookie, wchar_t *data, int len, int must_deselect) { + + /* + * See "Programming with the Text Encoding Conversion Manager" + * Appendix E for Unicode scrap conventions. + * + * XXX Need to support TEXT/styl scrap as well. + * See STScrpRec in TextEdit (Inside Macintosh: Text) for styl details. + * XXX Maybe PICT scrap too. + */ + if (ZeroScrap() != noErr) + return; + PutScrap(len * sizeof(*data), 'utxt', data); +} + +void get_clip(void *frontend, wchar_t **p, int *lenp) { + Session *s = frontend; + static Handle h = NULL; + long offset; + + if (p == NULL) { + /* release memory */ + if (h != NULL) + DisposeHandle(h); + h = NULL; + } else + /* XXX Support TEXT-format scrap as well. */ + if (GetScrap(NULL, 'utxt', &offset) > 0) { + h = NewHandle(0); + *lenp = GetScrap(h, 'utxt', &offset) / sizeof(**p); + HLock(h); + *p = (wchar_t *)*h; + if (*p == NULL || *lenp <= 0) + fatalbox("Empty scrap"); + } else { + *p = NULL; + *lenp = 0; + } +} + +static pascal void mac_scrolltracker(ControlHandle control, short part) { + Session *s; + + s = (Session *)GetWRefCon((*control)->contrlOwner); + switch (part) { + case kControlUpButtonPart: + term_scroll(s->term, 0, -1); + break; + case kControlDownButtonPart: + term_scroll(s->term, 0, +1); + break; + case kControlPageUpPart: + term_scroll(s->term, 0, -(s->term->rows - 1)); + break; + case kControlPageDownPart: + term_scroll(s->term, 0, +(s->term->rows - 1)); + break; + } +} + +#define K_BS 0x3300 +#define K_F1 0x7a00 +#define K_F2 0x7800 +#define K_F3 0x6300 +#define K_F4 0x7600 +#define K_F5 0x6000 +#define K_F6 0x6100 +#define K_F7 0x6200 +#define K_F8 0x6400 +#define K_F9 0x6500 +#define K_F10 0x6d00 +#define K_F11 0x6700 +#define K_F12 0x6f00 +#define K_F13 0x6900 +#define K_F14 0x6b00 +#define K_F15 0x7100 +#define K_INSERT 0x7200 +#define K_HOME 0x7300 +#define K_PRIOR 0x7400 +#define K_DELETE 0x7500 +#define K_END 0x7700 +#define K_NEXT 0x7900 +#define K_LEFT 0x7b00 +#define K_RIGHT 0x7c00 +#define K_DOWN 0x7d00 +#define K_UP 0x7e00 +#define KP_0 0x5200 +#define KP_1 0x5300 +#define KP_2 0x5400 +#define KP_3 0x5500 +#define KP_4 0x5600 +#define KP_5 0x5700 +#define KP_6 0x5800 +#define KP_7 0x5900 +#define KP_8 0x5b00 +#define KP_9 0x5c00 +#define KP_CLEAR 0x4700 +#define KP_EQUAL 0x5100 +#define KP_SLASH 0x4b00 +#define KP_STAR 0x4300 +#define KP_PLUS 0x4500 +#define KP_MINUS 0x4e00 +#define KP_DOT 0x4100 +#define KP_ENTER 0x4c00 + +void mac_keyterm(WindowPtr window, EventRecord *event) { + unsigned char buf[20]; + int len; + Session *s; + + s = (Session *)GetWRefCon(window); + len = mac_keytrans(s, event, buf); + s->back->send(s, (char *)buf, len); +} + +static int mac_keytrans(Session *s, EventRecord *event, + unsigned char *output) { + unsigned char *p = output; + int code; + + /* No meta key yet -- that'll be rather fun. */ + + /* Keys that we handle locally */ + if (event->modifiers & shiftKey) { + switch (event->message & keyCodeMask) { + case K_PRIOR: /* shift-pageup */ + term_scroll(s->term, 0, -(s->term->rows - 1)); + return 0; + case K_NEXT: /* shift-pagedown */ + term_scroll(s->term, 0, +(s->term->rows - 1)); + return 0; + } + } + + /* + * Control-2 should return ^@ (0x00), Control-6 should return + * ^^ (0x1E), and Control-Minus should return ^_ (0x1F). Since + * the DOS keyboard handling did it, and we have nothing better + * to do with the key combo in question, we'll also map + * Control-Backquote to ^\ (0x1C). + */ + + if (event->modifiers & controlKey) { + switch (event->message & charCodeMask) { + case ' ': case '2': + *p++ = 0x00; + return p - output; + case '`': + *p++ = 0x1c; + return p - output; + case '6': + *p++ = 0x1e; + return p - output; + case '/': + *p++ = 0x1f; + return p - output; + } + } + + /* + * First, 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. + */ + code = 0; + switch (event->message & keyCodeMask) { + case K_F1: code = (event->modifiers & shiftKey ? 23 : 11); break; + case K_F2: code = (event->modifiers & shiftKey ? 24 : 12); break; + case K_F3: code = (event->modifiers & shiftKey ? 25 : 13); break; + case K_F4: code = (event->modifiers & shiftKey ? 26 : 14); break; + case K_F5: code = (event->modifiers & shiftKey ? 28 : 15); break; + case K_F6: code = (event->modifiers & shiftKey ? 29 : 17); break; + case K_F7: code = (event->modifiers & shiftKey ? 31 : 18); break; + case K_F8: code = (event->modifiers & shiftKey ? 32 : 19); break; + case K_F9: code = (event->modifiers & shiftKey ? 33 : 20); break; + case K_F10: code = (event->modifiers & shiftKey ? 34 : 21); break; + case K_F11: code = 23; break; + case K_F12: code = 24; break; + case K_HOME: code = 1; break; + case K_INSERT: code = 2; break; + case K_DELETE: code = 3; break; + case K_END: code = 4; break; + case K_PRIOR: code = 5; break; + case K_NEXT: code = 6; break; + } + if (s->cfg.funky_type == 1 && code >= 11 && code <= 15) { + p += sprintf((char *)p, "\x1B[[%c", code + 'A' - 11); + return p - output; + } + if (s->cfg.rxvt_homeend && (code == 1 || code == 4)) { + p += sprintf((char *)p, code == 1 ? "\x1B[H" : "\x1BOw"); + return p - output; + } + if (code) { + p += sprintf((char *)p, "\x1B[%d~", code); + return p - output; + } + + if (s->term->app_keypad_keys) { + switch (event->message & keyCodeMask) { + case KP_ENTER: p += sprintf((char *)p, "\x1BOM"); return p - output; + case KP_CLEAR: p += sprintf((char *)p, "\x1BOP"); return p - output; + case KP_EQUAL: p += sprintf((char *)p, "\x1BOQ"); return p - output; + case KP_SLASH: p += sprintf((char *)p, "\x1BOR"); return p - output; + case KP_STAR: p += sprintf((char *)p, "\x1BOS"); return p - output; + case KP_PLUS: p += sprintf((char *)p, "\x1BOl"); return p - output; + case KP_MINUS: p += sprintf((char *)p, "\x1BOm"); return p - output; + case KP_DOT: p += sprintf((char *)p, "\x1BOn"); return p - output; + case KP_0: p += sprintf((char *)p, "\x1BOp"); return p - output; + case KP_1: p += sprintf((char *)p, "\x1BOq"); return p - output; + case KP_2: p += sprintf((char *)p, "\x1BOr"); return p - output; + case KP_3: p += sprintf((char *)p, "\x1BOs"); return p - output; + case KP_4: p += sprintf((char *)p, "\x1BOt"); return p - output; + case KP_5: p += sprintf((char *)p, "\x1BOu"); return p - output; + case KP_6: p += sprintf((char *)p, "\x1BOv"); return p - output; + case KP_7: p += sprintf((char *)p, "\x1BOw"); return p - output; + case KP_8: p += sprintf((char *)p, "\x1BOx"); return p - output; + case KP_9: p += sprintf((char *)p, "\x1BOy"); return p - output; + } + } + + switch (event->message & keyCodeMask) { + case K_UP: + p += sprintf((char *)p, + s->term->app_cursor_keys ? "\x1BOA" : "\x1B[A"); + return p - output; + case K_DOWN: + p += sprintf((char *)p, + s->term->app_cursor_keys ? "\x1BOB" : "\x1B[B"); + return p - output; + case K_RIGHT: + p += sprintf((char *)p, + s->term->app_cursor_keys ? "\x1BOC" : "\x1B[C"); + return p - output; + case K_LEFT: + p += sprintf((char *)p, + s->term->app_cursor_keys ? "\x1BOD" : "\x1B[D"); + return p - output; + case KP_ENTER: + *p++ = 0x0d; + return p - output; + case K_BS: + *p++ = (s->cfg.bksp_is_delete ? 0x7f : 0x08); + return p - output; + default: + *p++ = event->message & charCodeMask; + return p - output; + } +} + +void request_paste(void *frontend) +{ + Session *s = frontend; + + /* + * In the Mac OS, pasting is synchronous: we can read the + * clipboard with no difficulty, so request_paste() can just go + * ahead and paste. + */ + term_do_paste(s->term); +} + +void mac_growterm(WindowPtr window, EventRecord *event) { + Rect limits; + long grow_result; + int newrows, newcols; + Session *s; + + s = (Session *)GetWRefCon(window); + SetRect(&limits, s->font_width + 15, s->font_height, SHRT_MAX, SHRT_MAX); + grow_result = GrowWindow(window, event->where, &limits); + if (grow_result != 0) { + newrows = HiWord(grow_result) / s->font_height; + newcols = (LoWord(grow_result) - 15) / s->font_width; + mac_adjustsize(s, newrows, newcols); + term_size(s->term, newrows, newcols, s->cfg.savelines); + } +} + +void mac_activateterm(WindowPtr window, Boolean active) { + Session *s; + + s = (Session *)GetWRefCon(window); + s->has_focus = active; + term_update(s->term); + if (active) + ShowControl(s->scrollbar); + else { + if (HAVE_COLOR_QD()) + PmBackColor(DEFAULT_BG);/* HideControl clears behind the control */ + else + BackColor(blackColor); + HideControl(s->scrollbar); + } + mac_drawgrowicon(s); +} + +void mac_updateterm(WindowPtr window) { + Session *s; + + s = (Session *)GetWRefCon(window); + SetPort(window); + BeginUpdate(window); + pre_paint(s); + term_paint(s->term, s, + (*window->visRgn)->rgnBBox.left, + (*window->visRgn)->rgnBBox.top, + (*window->visRgn)->rgnBBox.right, + (*window->visRgn)->rgnBBox.bottom, 1); + /* Restore default colours in case the Window Manager uses them */ + if (HAVE_COLOR_QD()) { + PmForeColor(DEFAULT_FG); + PmBackColor(DEFAULT_BG); + } else { + ForeColor(whiteColor); + BackColor(blackColor); + } + if (FrontWindow() != window) + EraseRect(&(*s->scrollbar)->contrlRect); + UpdateControls(window, window->visRgn); + mac_drawgrowicon(s); + post_paint(s); + EndUpdate(window); +} + +static void mac_drawgrowicon(Session *s) { + Rect clip; + + SetPort(s->window); + /* Stop DrawGrowIcon giving us space for a horizontal scrollbar */ + SetRect(&clip, s->window->portRect.right - 15, SHRT_MIN, + SHRT_MAX, SHRT_MAX); + ClipRect(&clip); + DrawGrowIcon(s->window); + clip.left = SHRT_MIN; + ClipRect(&clip); +} + +struct do_text_args { + Session *s; + Rect textrect; + Rect leadrect; + char *text; + int len; + unsigned long attr; + int lattr; +}; + +/* + * Call from the terminal emulator to draw a bit of text + * + * x and y are text row and column (zero-based) + */ +void do_text(Context ctx, int x, int y, char *text, int len, + unsigned long attr, int lattr) { + Session *s = ctx; + int style = 0; + struct do_text_args a; + RgnHandle textrgn; +#if 0 + int i; +#endif + + SetPort(s->window); + +#if 0 + fprintf(stderr, "printing at (%d,%d) %d chars (attr=%x, lattr=%x):\n", + x, y, len, attr, lattr); + for (i = 0; i < len; i++) + fprintf(stderr, "%c", text[i]); + fprintf(stderr, "\n"); +#endif + + /* First check this text is relevant */ + a.textrect.top = y * s->font_height; + a.textrect.bottom = (y + 1) * s->font_height; + a.textrect.left = x * s->font_width; + a.textrect.right = (x + len) * s->font_width; + if (!RectInRgn(&a.textrect, s->window->visRgn)) + return; + + a.s = s; + a.text = text; + a.len = len; + a.attr = attr; + a.lattr = lattr; + if (s->font_leading > 0) + SetRect(&a.leadrect, + a.textrect.left, a.textrect.bottom - s->font_leading, + a.textrect.right, a.textrect.bottom); + else + SetRect(&a.leadrect, 0, 0, 0, 0); + SetPort(s->window); + TextFont(s->fontnum); + if (s->cfg.fontisbold || (attr & ATTR_BOLD) && !s->cfg.bold_colour) + style |= bold; + if (attr & ATTR_UNDER) + style |= underline; + TextFace(style); + TextSize(s->cfg.fontheight); + SetFractEnable(FALSE); /* We want characters on pixel boundaries */ + if (HAVE_COLOR_QD()) + if (style & bold) { + SpaceExtra(s->font_boldadjust << 16); + CharExtra(s->font_boldadjust << 16); + } else { + SpaceExtra(0); + CharExtra(0); + } + textrgn = NewRgn(); + RectRgn(textrgn, &a.textrect); + if (HAVE_COLOR_QD()) + DeviceLoop(textrgn, &do_text_for_device_upp, (long)&a, 0); + else + do_text_for_device(1, 0, NULL, (long)&a); + DisposeRgn(textrgn); + /* Tell the window manager about it in case this isn't an update */ + ValidRect(&a.textrect); +} + +static pascal void do_text_for_device(short depth, short devflags, + GDHandle device, long cookie) { + struct do_text_args *a; + int bgcolour, fgcolour, bright; + + a = (struct do_text_args *)cookie; + + bright = (a->attr & ATTR_BOLD) && a->s->cfg.bold_colour; + + TextMode(a->attr & ATTR_REVERSE ? notSrcCopy : srcCopy); + + switch (depth) { + case 1: + /* XXX This should be done with a _little_ more configurability */ + ForeColor(whiteColor); + BackColor(blackColor); + if (a->attr & TATTR_ACTCURS) + TextMode(a->attr & ATTR_REVERSE ? srcCopy : notSrcCopy); + break; + case 2: + if (a->attr & TATTR_ACTCURS) { + PmForeColor(bright ? CURSOR_FG_BOLD : CURSOR_FG); + PmBackColor(CURSOR_BG); + TextMode(srcCopy); + } else { + PmForeColor(bright ? DEFAULT_FG_BOLD : DEFAULT_FG); + PmBackColor(DEFAULT_BG); + } + break; + default: + if (a->attr & TATTR_ACTCURS) { + fgcolour = bright ? CURSOR_FG_BOLD : CURSOR_FG; + bgcolour = CURSOR_BG; + TextMode(srcCopy); + } else { + fgcolour = ((a->attr & ATTR_FGMASK) >> ATTR_FGSHIFT) * 2; + bgcolour = ((a->attr & ATTR_BGMASK) >> ATTR_BGSHIFT) * 2; + if (bright) + if (a->attr & ATTR_REVERSE) + bgcolour++; + else + fgcolour++; + } + PmForeColor(fgcolour); + PmBackColor(bgcolour); + break; + } + + if (a->attr & ATTR_REVERSE) + PaintRect(&a->leadrect); + else + EraseRect(&a->leadrect); + MoveTo(a->textrect.left, a->textrect.top + a->s->font_ascent); + /* FIXME: Sort out bold width adjustments on Original QuickDraw. */ + DrawText(a->text, 0, a->len); + + if (a->attr & TATTR_PASCURS) { + PenNormal(); + switch (depth) { + case 1: + PenMode(patXor); + break; + default: + PmForeColor(CURSOR_BG); + break; + } + FrameRect(&a->textrect); + } +} + +void do_cursor(Context ctx, int x, int y, char *text, int len, + unsigned long attr, int lattr) +{ + + /* FIXME: Should do something here! */ +} + +/* + * Call from the terminal emulator to get its graphics context. + * Should probably be called start_redraw or something. + */ +void pre_paint(Session *s) { + + s->attr_mask = ATTR_INVALID; + if (HAVE_COLOR_QD()) + DeviceLoop(s->window->visRgn, &mac_set_attr_mask_upp, (long)s, 0); + else + mac_set_attr_mask(1, 0, NULL, (long)s); +} + +Context get_ctx(void *frontend) { + Session *s = frontend; + + pre_paint(s); + return s; +} + +void free_ctx(Context ctx) { + +} + +static pascal void mac_set_attr_mask(short depth, short devflags, + GDHandle device, long cookie) { + + Session *s = (Session *)cookie; + + switch (depth) { + default: + s->attr_mask |= ATTR_FGMASK | ATTR_BGMASK; + /* FALLTHROUGH */ + case 2: + s->attr_mask |= ATTR_BOLD; + /* FALLTHROUGH */ + case 1: + s->attr_mask |= ATTR_UNDER | ATTR_REVERSE | TATTR_ACTCURS | + TATTR_PASCURS | ATTR_ASCII | ATTR_GBCHR | ATTR_LINEDRW | + (s->cfg.bold_colour ? 0 : ATTR_BOLD); + break; + } +} + +/* + * Presumably this does something in Windows + */ +void post_paint(Session *s) { + +} + +/* + * Set the scroll bar position + * + * total is the line number of the bottom of the working screen + * start is the line number of the top of the display + * page is the length of the displayed page + */ +void set_sbar(void *frontend, int total, int start, int page) { + Session *s = frontend; + + /* We don't redraw until we've set everything up, to avoid glitches */ + (*s->scrollbar)->contrlMin = 0; + (*s->scrollbar)->contrlMax = total - page; + SetControlValue(s->scrollbar, start); +#if TARGET_RT_CFM + /* XXX: This doesn't link for me. */ + if (mac_gestalts.cntlattr & gestaltControlMgrPresent) + SetControlViewSize(s->scrollbar, page); +#endif +} + +void sys_cursor(void *frontend, int x, int y) +{ + /* + * I think his is meaningless under Mac OS. + */ +} + +/* + * 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) +{ + if (mode != BELL_VISUAL) + SysBeep(30); + /* + * XXX We should indicate the relevant window and/or use the + * Notification Manager + */ +} + +int char_width(Context ctx, int uc) +{ + /* + * Until we support exciting character-set stuff, assume all chars are + * single-width. + */ + return 1; +} + +/* + * Set icon string -- a no-op here (Windowshade?) + */ +void set_icon(void *frontend, char *icon) { + Session *s = frontend; + +} + +/* + * Set the window title + */ +void set_title(void *frontend, char *title) { + Session *s = frontend; + Str255 mactitle; + + mactitle[0] = sprintf((char *)&mactitle[1], "%s", title); + SetWTitle(s->window, mactitle); +} + +/* + * set or clear the "raw mouse message" mode + */ +void set_raw_mouse_mode(void *frontend, int activate) +{ + Session *s = frontend; + + s->raw_mouse = activate; + /* FIXME: Should call mac_updatetermcursor as appropriate. */ +} + +/* + * Resize the window at the emulator's request + */ +void request_resize(void *frontend, int w, int h) { + Session *s = frontend; + + s->term->cols = w; + s->term->rows = h; + mac_initfont(s); +} + +/* + * Iconify (actually collapse) the window at the emulator's request. + */ +void set_iconic(void *frontend, int iconic) +{ + Session *s = frontend; + UInt32 features; + + if (mac_gestalts.apprvers >= 0x0100 && + GetWindowFeatures(s->window, &features) == noErr && + (features & kWindowCanCollapse)) + CollapseWindow(s->window, iconic); +} + +/* + * Move the window in response to a server-side request. + */ +void move_window(void *frontend, int x, int y) +{ + Session *s = frontend; + + MoveWindow(s->window, x, y, FALSE); +} + +/* + * 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) +{ + Session *s = frontend; + + /* + * We also change the input focus to point to the topmost window, + * since that's probably what the Human Interface Guidelines would + * like us to do. + */ + if (top) + SelectWindow(s->window); + else + SendBehind(s->window, NULL); +} + +/* + * Refresh the window in response to a server-side request. + */ +void refresh_window(void *frontend) +{ + Session *s = frontend; + + term_invalidate(s->term); +} + +/* + * Maximise or restore the window in response to a server-side + * request. + */ +void set_zoomed(void *frontend, int zoomed) +{ + Session *s = frontend; + + ZoomWindow(s->window, zoomed ? inZoomOut : inZoomIn, FALSE); +} + +/* + * Report whether the window is iconic, for terminal reports. + */ +int is_iconic(void *frontend) +{ + Session *s = frontend; + UInt32 features; + + if (mac_gestalts.apprvers >= 0x0100 && + GetWindowFeatures(s->window, &features) == noErr && + (features & kWindowCanCollapse)) + return IsWindowCollapsed(s->window); + return FALSE; +} + +/* + * Report the window's position, for terminal reports. + */ +void get_window_pos(void *frontend, int *x, int *y) +{ + Session *s = frontend; + + *x = s->window->portRect.left; + *y = s->window->portRect.top; +} + +/* + * Report the window's pixel size, for terminal reports. + */ +void get_window_pixels(void *frontend, int *x, int *y) +{ + Session *s = frontend; + + *x = s->window->portRect.right - s->window->portRect.left; + *y = s->window->portRect.bottom - s->window->portRect.top; +} + +/* + * Return the window or icon title. + */ +char *get_window_title(void *frontend, int icon) +{ + Session *s = frontend; + + /* Erm, we don't save this at the moment */ + return ""; +} + +/* + * real_palette_set(): This does the actual palette-changing work on behalf + * of palette_set(). Does _not_ call ActivatePalette() in case the caller + * is doing a batch of updates. + */ +static void real_palette_set(Session *s, int n, int r, int g, int b) +{ + RGBColor col; + + if (!HAVE_COLOR_QD()) + return; + col.red = r * 0x0101; + col.green = g * 0x0101; + col.blue = b * 0x0101; + fprintf(stderr, "p%d <- (0x%x, 0x%x, 0x%x)\n", n, col.red, col.green, + col.blue); + SetEntryColor(s->palette, n, &col); +} + +/* + * Set the logical palette. Called by the terminal emulator. + */ +void palette_set(void *frontend, int n, int r, int g, int b) { + Session *s = frontend; + static const int first[21] = { + 0, 2, 4, 6, 8, 10, 12, 14, + 1, 3, 5, 7, 9, 11, 13, 15, + 16, 17, 18, 20, 22 + }; + + if (!HAVE_COLOR_QD()) + return; + real_palette_set(s, first[n], r, g, b); + if (first[n] >= 18) + real_palette_set(s, first[n]+1, r, g, b); + if (first[n] == DEFAULT_BG) + mac_adjustwinbg(s); + ActivatePalette(s->window); +} + +/* + * Reset to the default palette + */ +void palette_reset(void *frontend) { + Session *s = frontend; + /* This maps colour indices in cfg to those used in our palette. */ + static const int ww[] = { + 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 21, + 0, 1, 2, 3, 4, 5 + }; + int i; + + if (!HAVE_COLOR_QD()) + return; + + assert(lenof(ww) == NCOLOURS); + + for (i = 0; i < NCOLOURS; i++) { + real_palette_set(s, i, + s->cfg.colours[ww[i]][0], + s->cfg.colours[ww[i]][1], + s->cfg.colours[ww[i]][2]); + } + mac_adjustwinbg(s); + ActivatePalette(s->window); + /* Palette Manager will generate update events as required. */ +} + +/* + * Scroll the screen. (`lines' is +ve for scrolling forward, -ve + * for backward.) + */ +void do_scroll(void *frontend, int topline, int botline, int lines) { + Session *s = frontend; + Rect r; + RgnHandle update; + + /* FIXME: This is seriously broken on Original QuickDraw. No idea why. */ + SetPort(s->window); + if (HAVE_COLOR_QD()) + PmBackColor(DEFAULT_BG); + else + BackColor(blackColor); + update = NewRgn(); + SetRect(&r, 0, topline * s->font_height, + s->term->cols * s->font_width, (botline + 1) * s->font_height); + ScrollRect(&r, 0, - lines * s->font_height, update); + /* XXX: move update region? */ + InvalRgn(update); + DisposeRgn(update); +} + +void logevent(void *frontend, char *str) { + + /* XXX Do something */ +} + +/* Dummy routine, only required in plink. */ +void ldisc_update(void *frontend, int echo, int edit) +{ +} + +/* + * Mac PuTTY doesn't support printing yet. + */ +printer_job *printer_start_job(char *printer) +{ + + return NULL; +} + +void printer_job_data(printer_job *pj, void *data, int len) +{ +} + +void printer_finish_job(printer_job *pj) +{ +} + +void frontend_keypress(void *handle) +{ + /* + * Keypress termination in non-Close-On-Exit mode is not + * currently supported in PuTTY proper, because the window + * always has a perfectly good Close button anyway. So we do + * nothing here. + */ + return; +} + +/* + * Ask whether to wipe a session log file before writing to it. + * Returns 2 for wipe, 1 for append, 0 for cancel (don't log). + */ +int askappend(void *frontend, char *filename) +{ + + /* FIXME: not implemented yet. */ + return 2; +} + +/* + * Emacs magic: + * Local Variables: + * c-file-style: "simon" + * End: + */ + diff --git a/mac/macucs.c b/mac/macucs.c new file mode 100644 index 00000000..dfb3f63f --- /dev/null +++ b/mac/macucs.c @@ -0,0 +1,76 @@ +/* $Id: macucs.c,v 1.1 2002/11/19 02:13:46 ben Exp $ */ + +#include +#include +#include + +#include +#include "putty.h" +#include "terminal.h" +#include "misc.h" + +/* + * Mac Unicode-handling routines. + * + * FIXME: currently trivial stub versions assuming all codepages + * are ISO8859-1. + * + * What we _should_ do is to use the Text Encoding Conversion Manager + * when it's available, and have our own routines for converting to + * standard Mac OS scripts when it's not. Support for ATSUI might be + * nice, too. + */ + +int is_dbcs_leadbyte(int codepage, char byte) +{ + return 0; /* we don't do DBCS */ +} + +int mb_to_wc(int codepage, int flags, char *mbstr, int mblen, + wchar_t *wcstr, int wclen) +{ + int ret = 0; + while (mblen > 0 && wclen > 0) { + *wcstr++ = (unsigned char) *mbstr++; + mblen--, wclen--, ret++; + } + return ret; /* FIXME: check error codes! */ +} + +int wc_to_mb(int codepage, int flags, wchar_t *wcstr, int wclen, + char *mbstr, int mblen, char *defchr, int *defused) +{ + int ret = 0; + if (defused) + *defused = 0; + while (mblen > 0 && wclen > 0) { + if (*wcstr >= 0x100) { + if (defchr) + *mbstr++ = *defchr; + else + *mbstr++ = '.'; + if (defused) + *defused = 1; + } else + *mbstr++ = (unsigned char) *wcstr; + wcstr++; + mblen--, wclen--, ret++; + } + return ret; /* FIXME: check error codes! */ +} + +void init_ucs(void) +{ + int i; + /* Find the line control characters. FIXME: this is not right. */ + for (i = 0; i < 256; i++) + if (i < ' ' || (i >= 0x7F && i < 0xA0)) + unitab_ctrl[i] = i; + else + unitab_ctrl[i] = 0xFF; + + for (i = 0; i < 256; i++) { + unitab_line[i] = unitab_scoacs[i] = i; + unitab_xterm[i] = (i >= 0x5F && i < 0x7F) ? ((i+1) & 0x1F) : i; + } +} diff --git a/putty.h b/putty.h index ad7e3903..af9c13e4 100644 --- a/putty.h +++ b/putty.h @@ -503,6 +503,13 @@ void log_packet(void *logctx, int direction, int type, char *texttype, void *data, int len); /* + * Exports from testback.c + */ + +extern Backend null_backend; +extern Backend loop_backend; + +/* * Exports from raw.c. */ diff --git a/puttyps.h b/puttyps.h index b64ecbd4..64c38c73 100644 --- a/puttyps.h +++ b/puttyps.h @@ -6,6 +6,10 @@ #include #include "winstuff.h" +#elif defined(macintosh) + +#include "macstuff.h" + #else #include "unix.h" diff --git a/settings.c b/settings.c index 47524db6..9530b4ae 100644 --- a/settings.c +++ b/settings.c @@ -484,6 +484,8 @@ void load_settings(char *section, int do_host, Config * cfg) gppi(sesskey, "TermHeight", 24, &cfg->height); #ifdef _WINDOWS gpps(sesskey, "Font", "Courier New", cfg->font, sizeof(cfg->font)); +#elif defined(macintosh) + gpps(sesskey, "Font", "Monaco", cfg->font, sizeof(cfg->font)); #else gpps(sesskey, "Font", "fixed", cfg->font, sizeof(cfg->font)); #endif -- 2.11.0