X-Git-Url: https://git.distorted.org.uk/u/mdw/putty/blobdiff_plain/deed9e2553ba99e7cd912c924482ba468cab61a1..33f07e9636e80bb17f25f97145ca2e0eb262db30:/mac/mac.c diff --git a/mac/mac.c b/mac/mac.c index 632b6a41..4f7c2fcf 100644 --- a/mac/mac.c +++ b/mac/mac.c @@ -1,6 +1,6 @@ -/* $Id: mac.c,v 1.24 2003/01/11 19:43:59 ben Exp $ */ +/* $Id: mac.c,v 1.59 2003/05/10 12:27:38 ben Exp $ */ /* - * Copyright (c) 1999 Ben Harris + * Copyright (c) 1999, 2003 Ben Harris * All rights reserved. * * Permission is hereby granted, free of charge, to any person @@ -29,6 +29,9 @@ */ #include +#include +#include +#include #include #include #include @@ -41,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -58,14 +62,15 @@ #include "macresid.h" #include "putty.h" #include "ssh.h" +#include "terminal.h" #include "mac.h" -QDGlobals qd; - Session *sesslist; static int cold = 1; +static int borednow = FALSE; struct mac_gestalts mac_gestalts; +UInt32 sleeptime; static void mac_startup(void); static void mac_eventloop(void); @@ -74,19 +79,18 @@ 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_suspendresume(EventRecord *); static void mac_activateabout(WindowPtr, EventRecord *); static void mac_updatewindow(WindowPtr); static void mac_updatelicence(WindowPtr); static void mac_keypress(EventRecord *); static int mac_windowtype(WindowPtr); static void mac_menucommand(long); -static void mac_openabout(void); static void mac_openlicence(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 (cleanup_exit) struct mac_windows { @@ -108,6 +112,7 @@ static void mac_startup(void) { Handle menuBar; TECInfoHandle ti; +#if !TARGET_API_MAC_CARBON /* Init Memory Manager */ MaxApplZone(); /* Init QuickDraw */ @@ -122,6 +127,7 @@ static void mac_startup(void) { TEInit(); /* Init Dialog Manager */ InitDialogs(NULL); +#endif cold = 0; /* Get base system version (only used if there's no better selector) */ @@ -154,6 +160,9 @@ static void mac_startup(void) { if (Gestalt(gestaltWindowMgrAttr, &mac_gestalts.windattr) != noErr || &SetWindowContentColor == kUnresolvedCFragSymbolAddress) mac_gestalts.windattr = 0; + /* Mac OS 8.5 Menu Manager? */ + if (Gestalt(gestaltMenuMgrAttr, &mac_gestalts.menuattr) != noErr) + mac_gestalts.menuattr = 0; #endif /* Text Encoding Conversion Manager? */ if ( @@ -169,13 +178,13 @@ static void mac_startup(void) { mac_gestalts.uncvattr = (*ti)->tecUnicodeConverterFeatures; DisposeHandle((Handle)ti); } - /* MacTCP? */ - if (Gestalt(FOUR_CHAR_CODE('mtcp'), &mac_gestalts.mtcpvers) != noErr) - mac_gestalts.mtcpvers = 0; - if (mac_gestalts.mtcpvers > 0) { - if (mactcp_init() != noErr) - mac_gestalts.mtcpvers = 0; - } + /* Navigation Services? */ + if (NavServicesAvailable()) + mac_gestalts.navsvers = NavLibraryVersion(); + else + mac_gestalts.navsvers = 0; + + sk_init(); /* We've been tested with the Appearance Manager */ if (mac_gestalts.apprvers != 0) @@ -186,24 +195,31 @@ static void mac_startup(void) { fatalbox("Unable to create menu bar."); SetMenuBar(menuBar); AppendResMenu(GetMenuHandle(mApple), 'DRVR'); + if (mac_gestalts.menuattr & gestaltMenuMgrAquaLayoutMask) { + DeleteMenuItem(GetMenuHandle(mFile), iQuit); + /* Also delete the separator above the Quit item. */ + DeleteMenuItem(GetMenuHandle(mFile), iQuit - 1); + } mac_adjustmenus(); DrawMenuBar(); InitCursor(); windows.about = NULL; windows.licence = NULL; - default_protocol = DEFAULT_PROTOCOL; - default_port = DEFAULT_PORT; + default_protocol = be_default_protocol; + /* Find the appropriate default port. */ + { + int i; + default_port = 0; /* illegal */ + for (i = 0; backends[i].backend != NULL; i++) + if (backends[i].protocol == default_protocol) { + default_port = backends[i].backend->default_port; + break; + } + } flags = FLAG_INTERACTIVE; - /* - * Really grotty hack to ensure that anything that looks at the - * global "cfg" variable gets something vaguely sensible. - * Obviously, nothing should actually be using it, but that will - * take a while to arrange. - */ - do_defaults(NULL, &cfg); - +#if !TARGET_API_MAC_CARBON { short vol; long dirid; @@ -215,7 +231,17 @@ static void mac_startup(void) { LMSetCurDirStore(dirid); } } - init_ucs(); +#endif + + /* Install Apple Event handlers. */ + AEInstallEventHandler(kCoreEventClass, kAEOpenApplication, + NewAEEventHandlerUPP(&mac_aevt_oapp), 0, FALSE); + AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, + NewAEEventHandlerUPP(&mac_aevt_odoc), 0, FALSE); + AEInstallEventHandler(kCoreEventClass, kAEPrintDocuments, + NewAEEventHandlerUPP(&mac_aevt_pdoc), 0, FALSE); + AEInstallEventHandler(kCoreEventClass, kAEQuitApplication, + NewAEEventHandlerUPP(&mac_aevt_quit), 0, FALSE); } static void mac_eventloop(void) { @@ -224,15 +250,28 @@ static void mac_eventloop(void) { RgnHandle cursrgn; cursrgn = NewRgn(); + sleeptime = 0; for (;;) { mac_adjustcursor(cursrgn); - gotevent = WaitNextEvent(everyEvent, &event, LONG_MAX, cursrgn); + gotevent = WaitNextEvent(everyEvent, &event, sleeptime, cursrgn); + /* + * XXX For now, limit sleep time to 1/10 s to work around + * wake-before-sleep race in MacTCP code. + */ + sleeptime = 6; mac_adjustcursor(cursrgn); - if (gotevent) + if (gotevent) { + /* Ensure we get a null event when the real ones run out. */ + sleeptime = 0; mac_event(&event); - if (mac_gestalts.mtcpvers != 0) - mactcp_poll(); - mac_pollterm(); + if (borednow) + cleanup_exit(0); + } + sk_poll(); + if (!gotevent) + mac_pollterm(); + if (mac_gestalts.apprvers >= 0x100 && mac_frontwindow() != NULL) + IdleControls(mac_frontwindow()); } DisposeRgn(cursrgn); } @@ -240,7 +279,6 @@ static void mac_eventloop(void) { static void mac_event(EventRecord *event) { short part; WindowPtr window; - Point pt; switch (event->what) { case mouseDown: @@ -250,9 +288,11 @@ static void mac_event(EventRecord *event) { mac_adjustmenus(); mac_menucommand(MenuSelect(event->where)); break; +#if !TARGET_API_MAC_CARBON case inSysWindow: SystemClick(event, window); break; +#endif case inContent: if (window != FrontWindow()) /* XXX: check for movable modal dboxes? */ @@ -266,7 +306,16 @@ static void mac_event(EventRecord *event) { break; case inDrag: /* XXX: moveable modal check? */ +#if TARGET_API_MAC_CARBON + { + BitMap screenBits; + + GetQDGlobalsScreenBits(&screenBits); + DragWindow(window, event->where, &screenBits.bounds); + } +#else DragWindow(window, event->where, &qd.screenBits.bounds); +#endif break; case inGrow: mac_growwindow(window, event); @@ -288,134 +337,69 @@ static void mac_event(EventRecord *event) { case updateEvt: mac_updatewindow((WindowPtr)event->message); break; +#if !TARGET_API_MAC_CARBON case diskEvt: if (HiWord(event->message) != noErr) { + Point pt; + 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, &window, &item)) - switch (item) { - case wiAboutLicence: - mac_openlicence(); - break; - } +#endif + case osEvt: + switch ((event->message & osEvtMessageMask) >> 24) { + case suspendResumeMessage: + mac_suspendresume(event); + break; + } break; - case wSettings: - mac_clickdlg(window, event); + case kHighLevelEvent: + AEProcessAppleEvent(event); /* errors? */ break; } } -static void mac_growwindow(WindowPtr window, EventRecord *event) { +static void mac_contentclick(WindowPtr window, EventRecord *event) +{ - switch (mac_windowtype(window)) { - case wTerminal: - mac_growterm(window, event); - } + if (mac_wininfo(window)->click != NULL) + (*mac_wininfo(window)->click)(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 wSettings: - mac_activatedlg(window, event); - break; - case wAbout: - mac_activateabout(window, event); - break; - } -} +static void mac_growwindow(WindowPtr window, EventRecord *event) +{ -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); + if (mac_wininfo(window)->grow != NULL) + (*mac_wininfo(window)->grow)(window, event); } -static void mac_updatewindow(WindowPtr window) { +static void mac_activatewindow(WindowPtr window, EventRecord *event) +{ - switch (mac_windowtype(window)) { - case wTerminal: - mac_updateterm(window); - break; - case wAbout: - case wSettings: - BeginUpdate(window); - UpdateDialog(window, window->visRgn); - EndUpdate(window); - break; - case wLicence: - mac_updatelicence(window); - break; - } + mac_adjustmenus(); + if (mac_wininfo(window)->activate != NULL) + (*mac_wininfo(window)->activate)(window, event); } -static void mac_updatelicence(WindowPtr window) +static void mac_updatewindow(WindowPtr window) { - Handle h; - int len; - long fondsize; - - SetPort(window); - BeginUpdate(window); - fondsize = GetScriptVariable(smRoman, smScriptSmallFondSize); - TextFont(HiWord(fondsize)); - TextSize(LoWord(fondsize)); - h = Get1Resource('TEXT', wLicence); - len = GetResourceSizeOnDisk(h); - if (h != NULL) { - HLock(h); - TETextBox(*h, len, &window->portRect, teFlushDefault); - HUnlock(h); - } - EndUpdate(window); + + if (mac_wininfo(window)->update != NULL) + (*mac_wininfo(window)->update)(window); } /* * Work out what kind of window we're dealing with. - * Concept shamelessly nicked from SurfWriter. */ -static int mac_windowtype(WindowPtr window) { - int kind; - long refcon; - - if (window == NULL) - return wNone; - kind = ((WindowPeek)window)->windowKind; - if (kind < 0) +static int mac_windowtype(WindowPtr window) +{ + +#if !TARGET_API_MAC_CARBON + if (GetWindowKind(window) < 0) return wDA; - if (GetWVariant(window) == zoomDocProc) - return wTerminal; - refcon = GetWRefCon(window); - if (refcon < 1024) - return refcon; - else - return wSettings; +#endif + return ((WinInfo *)GetWRefCon(window))->wtype; } /* @@ -424,7 +408,7 @@ static int mac_windowtype(WindowPtr window) { static void mac_keypress(EventRecord *event) { WindowPtr window; - window = FrontWindow(); + window = mac_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. @@ -435,22 +419,21 @@ static void mac_keypress(EventRecord *event) { mac_adjustmenus(); mac_menucommand(MenuKey(event->message & charCodeMask)); } else { - switch (mac_windowtype(window)) { - case wTerminal: - mac_keyterm(window, event); - break; - } + if (window != NULL && mac_wininfo(window)->key != NULL) + (*mac_wininfo(window)->key)(window, event); } } static void mac_menucommand(long result) { short menu, item; - Str255 da; WindowPtr window; +#if !TARGET_API_MAC_CARBON + Str255 da; +#endif menu = HiWord(result); item = LoWord(result); - window = FrontWindow(); + window = mac_frontwindow(); /* Things which do the same whatever window we're in. */ switch (menu) { case mApple: @@ -458,10 +441,12 @@ static void mac_menucommand(long result) { case iAbout: mac_openabout(); goto done; +#if !TARGET_API_MAC_CARBON default: GetMenuItemText(GetMenuHandle(mApple), item, da); OpenDeskAcc(da); goto done; +#endif } break; case mFile: @@ -475,6 +460,15 @@ static void mac_menucommand(long result) { case iClose: mac_closewindow(window); goto done; + case iSave: + mac_savesession(); + goto done; + case iSaveAs: + mac_savesessionas(); + goto done; + case iDuplicate: + mac_dupsession(); + goto done; case iQuit: cleanup_exit(0); goto done; @@ -482,71 +476,49 @@ static void mac_menucommand(long result) { break; } /* If we get here, handling is up to window-specific code. */ - switch (mac_windowtype(window)) { - case wTerminal: - mac_menuterm(window, menu, item); - break; - } + if (window != NULL && mac_wininfo(window)->menu != NULL) + (*mac_wininfo(window)->menu)(window, menu, item); + 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); - vers = (VersRecHndl)Get1Resource('vers', 1); - if (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_openlicence(void) { - - if (windows.licence) - SelectWindow(windows.licence); - else { - windows.licence = GetNewWindow(wLicence, NULL, (WindowPtr)-1); - ShowWindow(windows.licence); - } -} - static void mac_closewindow(WindowPtr window) { switch (mac_windowtype(window)) { +#if !TARGET_API_MAC_CARBON case wDA: - CloseDeskAcc(((WindowPeek)window)->windowKind); - break; - case wTerminal: - /* FIXME: end session and stuff */ - break; - case wAbout: - windows.about = NULL; - CloseWindow(window); - break; - case wLicence: - windows.licence = NULL; - CloseWindow(window); + CloseDeskAcc(GetWindowKind(window)); break; +#endif default: - CloseWindow(window); + if (mac_wininfo(window)->close != NULL) + (*mac_wininfo(window)->close)(window); break; } } +static void mac_suspendresume(EventRecord *event) +{ + WindowPtr front; + EventRecord fakeevent; + + /* + * We're called either before we're suspended or after we're + * resumed, so we're the front application at this point. + */ + front = FrontWindow(); + if (front != NULL) { + fakeevent.what = activateEvt; + fakeevent.message = (UInt32)front; + fakeevent.when = event->when; + fakeevent.where = event->where; + fakeevent.modifiers = + (event->message & resumeFlag) ? activeFlag : 0; + mac_activatewindow(front, &fakeevent); + } +} + static void mac_zoomwindow(WindowPtr window, short part) { /* FIXME: do something */ @@ -555,11 +527,15 @@ static void mac_zoomwindow(WindowPtr window, short part) { /* * Make the menus look right before the user gets to see them. */ +#if TARGET_API_MAC_CARBON +#define EnableItem EnableMenuItem +#define DisableItem DisableMenuItem +#endif static void mac_adjustmenus(void) { WindowPtr window; MenuHandle menu; - window = FrontWindow(); + window = mac_frontwindow(); menu = GetMenuHandle(mApple); EnableItem(menu, 0); EnableItem(menu, iAbout); @@ -573,14 +549,16 @@ static void mac_adjustmenus(void) { DisableItem(menu, iClose); EnableItem(menu, iQuit); - switch (mac_windowtype(window)) { - case wTerminal: - mac_adjusttermmenus(window); - break; - default: + if (window != NULL && mac_wininfo(window)->adjustmenus != NULL) + (*mac_wininfo(window)->adjustmenus)(window); + else { + DisableItem(menu, iSave); + DisableItem(menu, iSaveAs); + DisableItem(menu, iDuplicate); menu = GetMenuHandle(mEdit); DisableItem(menu, 0); - break; + menu = GetMenuHandle(mWindow); + DisableItem(menu, 0); /* Until we get more than 1 item on it. */ } DrawMenuBar(); } @@ -592,6 +570,10 @@ static void mac_adjustcursor(RgnHandle cursrgn) { Point mouse; WindowPtr window, front; short part; +#if TARGET_API_MAC_CARBON + Cursor arrow; + RgnHandle visrgn; +#endif GetMouse(&mouse); LocalToGlobal(&mouse); @@ -599,23 +581,53 @@ static void mac_adjustcursor(RgnHandle cursrgn) { front = FrontWindow(); if (part != inContent || window == NULL || window != front) { /* Cursor isn't in the front window, so switch to arrow */ +#if TARGET_API_MAC_CARBON + GetQDGlobalsArrow(&arrow); + SetCursor(&arrow); +#else SetCursor(&qd.arrow); +#endif SetRectRgn(cursrgn, SHRT_MIN, SHRT_MIN, SHRT_MAX, SHRT_MAX); - if (front != NULL) + if (front != NULL) { +#if TARGET_API_MAC_CARBON + visrgn = NewRgn(); + GetPortVisibleRegion(GetWindowPort(front), visrgn); + DiffRgn(cursrgn, visrgn, cursrgn); + DisposeRgn(visrgn); +#else DiffRgn(cursrgn, front->visRgn, cursrgn); +#endif + } } else { - switch (mac_windowtype(window)) { - case wTerminal: - mac_adjusttermcursor(window, mouse, cursrgn); - break; - default: + if (mac_wininfo(window)->adjustcursor != NULL) + (*mac_wininfo(window)->adjustcursor)(window, mouse, cursrgn); + else { +#if TARGET_API_MAC_CARBON + GetQDGlobalsArrow(&arrow); + SetCursor(&arrow); + GetPortVisibleRegion(GetWindowPort(window), cursrgn); +#else SetCursor(&qd.arrow); CopyRgn(window->visRgn, cursrgn); - break; +#endif } } } +pascal OSErr mac_aevt_quit(const AppleEvent *req, AppleEvent *reply, + long refcon) +{ + DescType type; + Size size; + + if (AEGetAttributePtr(req, keyMissedKeywordAttr, typeWildCard, + &type, NULL, 0, &size) == noErr) + return errAEParamMissed; + + borednow = 1; + return noErr; +} + void cleanup_exit(int status) { @@ -623,37 +635,10 @@ void cleanup_exit(int status) if (mac_gestalts.encvvers != 0) TerminateUnicodeConverter(); #endif - if (mac_gestalts.mtcpvers != 0) - mactcp_shutdown(); + sk_cleanup(); exit(status); } -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, NULL); - 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, NULL); - exit(1); -} - /* This should only kill the current session, not the whole application. */ void connection_fatal(void *fontend, char *fmt, ...) { va_list ap; @@ -665,7 +650,7 @@ void connection_fatal(void *fontend, char *fmt, ...) { va_end(ap); ParamText(stuff, NULL, NULL, NULL); StopAlert(128, NULL); - exit(1); + cleanup_exit(1); } /* Null SSH agent client -- never finds an agent. */ @@ -676,11 +661,13 @@ int agent_exists(void) return FALSE; } -void agent_query(void *in, int inlen, void **out, int *outlen) +int agent_query(void *in, int inlen, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) { *out = NULL; *outlen = 0; + return 1; } /* Temporary null routines for testing. */ @@ -688,7 +675,33 @@ void agent_query(void *in, int inlen, void **out, int *outlen) void verify_ssh_host_key(void *frontend, char *host, int port, char *keytype, char *keystr, char *fingerprint) { + Str255 stuff; + Session *s = frontend; + + /* + * This function is horribly wrong. For one thing, the alert + * shouldn't be modal, it should be movable modal, or a sheet in + * Aqua. Also, PuTTY might be in the background, in which case we + * should use the Notification Manager to wake up the user. In + * any case, we shouldn't hold up processing of other connections' + * data just because this one's waiting for the user. It should + * also handle a host key cache, of course, and see the note below + * about closing the connection. All in all, a bit of a mess + * really. + */ + stuff[0] = sprintf((char *)(&stuff[1]), + "The server's key fingerprint is: %s\n" + "Continue connecting?", fingerprint); + ParamText(stuff, NULL, NULL, NULL); + if (CautionAlert(wQuestion, NULL) == 2) { + /* + * User chose "Cancel". Unfortunately, if I tear the + * connection down here, Bad Things happen when I return. I + * think this function should actually return something + * telling the SSH code to abandon the connection. + */ + } } void askcipher(void *frontend, char *ciphername, int cs) @@ -701,41 +714,49 @@ void old_keyfile_warning(void) } -char *platform_default_s(char *name) +FontSpec platform_default_fontspec(char const *name) { + FontSpec ret; long smfs; - Str255 pname; - static char cname[256]; if (!strcmp(name, "Font")) { smfs = GetScriptVariable(smSystemScript, smScriptMonoFondSize); if (smfs == 0) smfs = GetScriptVariable(smRoman, smScriptMonoFondSize); if (smfs != 0) { - GetFontName(HiWord(smfs), pname); - if (pname[0] == 0) - return "Monaco"; - p2cstrcpy(cname, pname); - return cname; - } else - return "Monaco"; + GetFontName(HiWord(smfs), ret.name); + if (ret.name[0] == 0) + memcpy(ret.name, "\pMonaco", 7); + ret.size = LoWord(smfs); + } else { + memcpy(ret.name, "\pMonaco", 7); + ret.size = 9; + } + ret.face = 0; + } else { + ret.name[0] = 0; } - return NULL; + + return ret; } -int platform_default_i(char *name, int def) +Filename platform_default_filename(const char *name) { - long smfs; + Filename ret; + if (!strcmp(name, "LogFileName")) + FSMakeFSSpec(0, 0, "\pputty.log", &ret.fss); + else + memset(&ret, 0, sizeof(ret)); + return ret; +} - if (!strcmp(name, "FontHeight")) { - smfs = GetScriptVariable(smSystemScript, smScriptMonoFondSize); - if (smfs == 0) - smfs = GetScriptVariable(smRoman, smScriptMonoFondSize); - if (smfs != 0) - return LoWord(smfs); - else - return 9; - } +char *platform_default_s(char const *name) +{ + return NULL; +} + +int platform_default_i(char const *name, int def) +{ /* Non-raw cut and paste of line-drawing chars works badly on the * current Unix stub implementation of the Unicode functions. @@ -753,6 +774,16 @@ void platform_get_x11_auth(char *display, int *proto, /* SGT: I have no idea whether Mac X servers need anything here. */ } +void update_specials_menu(void *frontend) +{ + Session *s = frontend; + WindowPtr front; + + front = mac_frontwindow(); + if (front != NULL && mac_windowsession(front) == s) + mac_adjustmenus(); +} + /* * Local Variables: * c-file-style: "simon"