├── .gitignore ├── LICENSE.md ├── NewTerm.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── NewTermMarzipan ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── External │ ├── CompactConstraint │ │ ├── CompactConstraint.h │ │ ├── LICENSE │ │ ├── NSLayoutConstraint+CompactConstraint.h │ │ ├── NSLayoutConstraint+CompactConstraint.m │ │ ├── UIView+CompactConstraint.h │ │ └── UIView+CompactConstraint.m │ ├── UIColor+HBAdditions.h │ ├── UIColor+HBAdditions.m │ └── ncurses │ │ ├── ncurses.h │ │ ├── ncurses_dll.h │ │ └── term.h ├── FontMetrics.h ├── FontMetrics.m ├── Fonts.plist ├── HBNTAppDelegate.h ├── HBNTAppDelegate.m ├── HBNTHUDView.h ├── HBNTHUDView.m ├── HBNTKeyboardButton.h ├── HBNTKeyboardButton.m ├── HBNTKeyboardToolbar.h ├── HBNTKeyboardToolbar.m ├── HBNTPTY.h ├── HBNTPTY.m ├── HBNTPreferences.h ├── HBNTPreferences.m ├── HBNTPreferencesRootController.h ├── HBNTPreferencesRootController.m ├── HBNTRootViewController.h ├── HBNTRootViewController.m ├── HBNTSubProcess.h ├── HBNTSubProcess.m ├── HBNTTabCollectionViewCell.h ├── HBNTTabCollectionViewCell.m ├── HBNTTabToolbar.h ├── HBNTTabToolbar.m ├── HBNTTerminalController.h ├── HBNTTerminalController.m ├── HBNTTerminalKeyInput.h ├── HBNTTerminalKeyInput.m ├── HBNTTerminalSessionViewController.h ├── HBNTTerminalSessionViewController.m ├── HBNTTerminalTextView.h ├── HBNTTerminalTextView.m ├── HBNTTextInputBase.h ├── HBNTTextInputBase.m ├── Info.plist ├── Main.storyboard ├── Main.storyboardc │ ├── Info.plist │ ├── U5R-jr-i31-view-bFg-2Y-cSI.nib │ └── UINavigationController-a88-9D-dcr.nib ├── NSCharacterSet+iTerm.h ├── NSCharacterSet+iTerm.m ├── NSStringITerm.h ├── NSStringITerm.m ├── NewTermMarzipan.entitlements ├── ScreenChar.h ├── ScreenChar.m ├── Themes.plist ├── VT100.h ├── VT100.m ├── VT100ColorMap.h ├── VT100ColorMap.m ├── VT100Screen.h ├── VT100Screen.m ├── VT100StringSupplier.h ├── VT100StringSupplier.m ├── VT100Terminal.h ├── VT100Terminal.m ├── VT100Token.h ├── VT100Token.m ├── VT100Types.h ├── bell-hud.png ├── bell-hud@2x.png ├── bell-hud@3x.png ├── charmaps.h ├── colortest.txt ├── en.lproj │ ├── InfoPlist.strings │ └── Localizable.strings ├── iTermParser.h ├── main.m ├── settings.png ├── settings@2x.png └── settings@3x.png ├── README.md └── UIKit └── PUT_PATCHED_HEADERS_HERE /.gitignore: -------------------------------------------------------------------------------- 1 | # Crap 2 | .DS_Store 3 | 4 | # Xcode 5 | *.pbxuser 6 | !default.pbxuser 7 | xcuserdata 8 | *.xccheckout 9 | *.xcuserstate 10 | 11 | # Carthage 12 | Carthage/ 13 | 14 | # Theos 15 | .theos 16 | packages/ 17 | 18 | # Repo specific 19 | UIKit/*.h 20 | UIKit/*.apinotes 21 | -------------------------------------------------------------------------------- /NewTerm.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /NewTerm.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /NewTermMarzipan/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /NewTermMarzipan/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /NewTermMarzipan/External/CompactConstraint/CompactConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marco Arment on 2014-04-06. 3 | // Copyright (c) 2014 Marco Arment. See included LICENSE file. 4 | // 5 | 6 | #import "NSLayoutConstraint+CompactConstraint.h" 7 | #import "UIView+CompactConstraint.h" 8 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/CompactConstraint/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 marcoarment 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /NewTermMarzipan/External/CompactConstraint/NSLayoutConstraint+CompactConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marco Arment on 2014-04-06. 3 | // Copyright (c) 2014 Marco Arment. See included LICENSE file. 4 | // 5 | 6 | #import "TargetConditionals.h" 7 | 8 | //#if TARGET_OS_IPHONE 9 | #import 10 | //#else 11 | //#import 12 | //#endif 13 | 14 | @interface NSLayoutConstraint (CompactConstraint) 15 | 16 | + (instancetype)compactConstraint:(NSString *)relationship metrics:(NSDictionary *)metrics views:(NSDictionary *)views self:(id)selfView; 17 | + (NSArray *)compactConstraints:(NSArray *)relationshipStrings metrics:(NSDictionary *)metrics views:(NSDictionary *)views self:(id)selfView; 18 | 19 | // And a convenient shortcut for creating constraints with the visualFormat string as the identifier 20 | + (NSArray *)identifiedConstraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views; 21 | 22 | // Deprecated, will be removed shortly: 23 | + (instancetype)compactConstraint:(NSString *)relationship metrics:(NSDictionary *)metrics views:(NSDictionary *)views __attribute__ ((deprecated)); 24 | + (NSArray *)compactConstraints:(NSArray *)relationshipStrings metrics:(NSDictionary *)metrics views:(NSDictionary *)views __attribute__ ((deprecated)); 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/CompactConstraint/UIView+CompactConstraint.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marco Arment on 2014-04-06. 3 | // Copyright (c) 2014 Marco Arment. See included LICENSE file. 4 | // 5 | 6 | #import "NSLayoutConstraint+CompactConstraint.h" 7 | 8 | //#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED 9 | #define CCView UIView 10 | //#elif TARGET_OS_MAC 11 | // #define CCView NSView 12 | //#endif 13 | 14 | @interface CCView (CompactConstraint) 15 | 16 | // Add a single constraint with the compact syntax 17 | - (NSLayoutConstraint *)addCompactConstraint:(NSString *)relationship metrics:(NSDictionary *)metrics views:(NSDictionary *)views; 18 | 19 | // Add any number of constraints. Can also mix in Visual Format Language strings. 20 | - (NSArray *)addCompactConstraints:(NSArray *)relationshipStrings metrics:(NSDictionary *)metrics views:(NSDictionary *)views; 21 | 22 | // And a convenient shortcut for what we always end up doing with the visualFormat call. 23 | - (void)addConstraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/CompactConstraint/UIView+CompactConstraint.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Marco Arment on 2014-04-06. 3 | // Copyright (c) 2014 Marco Arment. See included LICENSE file. 4 | // 5 | 6 | #import "UIView+CompactConstraint.h" 7 | 8 | @implementation CCView (CompactConstraint) 9 | 10 | - (NSLayoutConstraint *)addCompactConstraint:(NSString *)relationship metrics:(NSDictionary *)metrics views:(NSDictionary *)views 11 | { 12 | NSLayoutConstraint *constraint = [NSLayoutConstraint compactConstraint:relationship metrics:metrics views:views self:self]; 13 | [self addConstraint:constraint]; 14 | return constraint; 15 | } 16 | 17 | - (NSArray *)addCompactConstraints:(NSArray *)relationshipStrings metrics:(NSDictionary *)metrics views:(NSDictionary *)views; 18 | { 19 | NSMutableArray *mConstraints = [NSMutableArray arrayWithCapacity:relationshipStrings.count]; 20 | for (NSString *relationship in relationshipStrings) { 21 | if ([relationship hasPrefix:@"H:"] || [relationship hasPrefix:@"V:"] || [relationship hasPrefix:@"|"] || [relationship hasPrefix:@"["]) { 22 | [mConstraints addObjectsFromArray:[NSLayoutConstraint identifiedConstraintsWithVisualFormat:relationship options:0 metrics:metrics views:views]]; 23 | } else { 24 | [mConstraints addObject:[NSLayoutConstraint compactConstraint:relationship metrics:metrics views:views self:self]]; 25 | } 26 | } 27 | NSArray *constraints = [mConstraints copy]; 28 | [self addConstraints:constraints]; 29 | return constraints; 30 | } 31 | 32 | - (void)addConstraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views 33 | { 34 | [self addConstraints:[NSLayoutConstraint identifiedConstraintsWithVisualFormat:format options:opts metrics:metrics views:views]]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/UIColor+HBAdditions.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | /// UIColor (HBAdditions) is a class category in `Cephei` that provides some convenience methods. 7 | @interface UIColor (HBAdditions) 8 | 9 | /// Creates and returns a color object using data from the specified object. 10 | /// 11 | /// The value is expected to either be an array of 3 or 4 integer RGB or RGBA color components 12 | /// (respectively), with values between 0 and 255, or a CSS hexadecimal color code string. 13 | /// 14 | /// @param value The object to retrieve data from. See the discussion for the supported object 15 | /// types. 16 | /// @returns The color object. The color information represented by this object is in the device RGB 17 | /// colorspace. 18 | + (instancetype)hb_colorWithPropertyListValue:(id)value; 19 | 20 | /// Initializes and returns a color object using data from the specified object. 21 | /// 22 | /// The value is expected to either be an array of 3 or 4 integer RGB or RGBA color components 23 | /// (respectively), with values between 0 and 255, or a CSS hexadecimal color code string. 24 | /// 25 | /// @param value The object to retrieve data from. See the discussion for the supported object 26 | /// types. 27 | /// @returns An initialized color object. The color information represented by this object is in the 28 | /// device RGB colorspace. 29 | - (instancetype)hb_initWithPropertyListValue:(id)value; 30 | 31 | @end 32 | 33 | NS_ASSUME_NONNULL_END 34 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/UIColor+HBAdditions.m: -------------------------------------------------------------------------------- 1 | #import "UIColor+HBAdditions.h" 2 | 3 | @implementation UIColor (HBAdditions) 4 | 5 | + (instancetype)hb_colorWithPropertyListValue:(id)value { 6 | return [[self alloc] hb_initWithPropertyListValue:value]; 7 | } 8 | 9 | - (instancetype)hb_initWithPropertyListValue:(id)value { 10 | if (!value) { 11 | return nil; 12 | } else if ([value isKindOfClass:NSArray.class] && ((NSArray *)value).count == 3) { 13 | NSArray *array = value; 14 | return [UIColor colorWithRed:((NSNumber *)array[0]).integerValue / 255.f green:((NSNumber *)array[1]).integerValue / 255.f blue:((NSNumber *)array[2]).integerValue / 255.f alpha:1]; 15 | } else if ([value isKindOfClass:NSString.class] && [((NSString *)value) hasPrefix:@"#"] && ((NSString *)value).length == 7) { 16 | unsigned int hexInteger = 0; 17 | NSScanner *scanner = [NSScanner scannerWithString:value]; 18 | scanner.charactersToBeSkipped = [NSCharacterSet characterSetWithCharactersInString:@"#"]; 19 | [scanner scanHexInt:&hexInteger]; 20 | 21 | return [UIColor colorWithRed:((hexInteger & 0xFF0000) >> 16) / 255.f green:((hexInteger & 0xFF00) >> 8) / 255.f blue:(hexInteger & 0xFF) / 255.f alpha:1]; 22 | } else { 23 | return nil; 24 | } 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/ncurses/ncurses.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * 3 | * * 4 | * Permission is hereby granted, free of charge, to any person obtaining a * 5 | * copy of this software and associated documentation files (the * 6 | * "Software"), to deal in the Software without restriction, including * 7 | * without limitation the rights to use, copy, modify, merge, publish, * 8 | * distribute, distribute with modifications, sublicense, and/or sell * 9 | * copies of the Software, and to permit persons to whom the Software is * 10 | * furnished to do so, subject to the following conditions: * 11 | * * 12 | * The above copyright notice and this permission notice shall be included * 13 | * in all copies or substantial portions of the Software. * 14 | * * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 | * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 | * * 23 | * Except as contained in this notice, the name(s) of the above copyright * 24 | * holders shall not be used in advertising or otherwise to promote the * 25 | * sale, use or other dealings in this Software without prior written * 26 | * authorization. * 27 | ****************************************************************************/ 28 | /* $Id: ncurses_dll.h,v 1.6 2007/03/10 19:21:49 tom Exp $ */ 29 | 30 | #ifndef NCURSES_DLL_H_incl 31 | #define NCURSES_DLL_H_incl 1 32 | 33 | /* no longer needed on cygwin or mingw, thanks to auto-import */ 34 | /* but this structure may be useful at some point for an MSVC build */ 35 | /* so, for now unconditionally define the important flags */ 36 | /* "the right way" for proper static and dll+auto-import behavior */ 37 | #undef NCURSES_DLL 38 | #define NCURSES_STATIC 39 | 40 | #if defined(__CYGWIN__) 41 | # if defined(NCURSES_DLL) 42 | # if defined(NCURSES_STATIC) 43 | # undef NCURSES_STATIC 44 | # endif 45 | # endif 46 | # undef NCURSES_IMPEXP 47 | # undef NCURSES_API 48 | # undef NCURSES_EXPORT 49 | # undef NCURSES_EXPORT_VAR 50 | # if defined(NCURSES_DLL) 51 | /* building a DLL */ 52 | # define NCURSES_IMPEXP __declspec(dllexport) 53 | # elif defined(NCURSES_STATIC) 54 | /* building or linking to a static library */ 55 | # define NCURSES_IMPEXP /* nothing */ 56 | # else 57 | /* linking to the DLL */ 58 | # define NCURSES_IMPEXP __declspec(dllimport) 59 | # endif 60 | # define NCURSES_API __cdecl 61 | # define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API 62 | # define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type 63 | #endif 64 | 65 | /* Take care of non-cygwin platforms */ 66 | #if !defined(NCURSES_IMPEXP) 67 | # define NCURSES_IMPEXP /* nothing */ 68 | #endif 69 | #if !defined(NCURSES_API) 70 | # define NCURSES_API /* nothing */ 71 | #endif 72 | #if !defined(NCURSES_EXPORT) 73 | # define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API 74 | #endif 75 | #if !defined(NCURSES_EXPORT_VAR) 76 | # define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type 77 | #endif 78 | 79 | /* 80 | * For reentrant code, we map the various global variables into SCREEN by 81 | * using functions to access them. 82 | */ 83 | #define NCURSES_PUBLIC_VAR(name) _nc_##name 84 | #define NCURSES_WRAPPED_VAR(type,name) extern type NCURSES_PUBLIC_VAR(name)(void) 85 | 86 | #endif /* NCURSES_DLL_H_incl */ 87 | -------------------------------------------------------------------------------- /NewTermMarzipan/External/ncurses/ncurses_dll.h: -------------------------------------------------------------------------------- 1 | /**************************************************************************** 2 | * Copyright (c) 1998-2006,2007 Free Software Foundation, Inc. * 3 | * * 4 | * Permission is hereby granted, free of charge, to any person obtaining a * 5 | * copy of this software and associated documentation files (the * 6 | * "Software"), to deal in the Software without restriction, including * 7 | * without limitation the rights to use, copy, modify, merge, publish, * 8 | * distribute, distribute with modifications, sublicense, and/or sell * 9 | * copies of the Software, and to permit persons to whom the Software is * 10 | * furnished to do so, subject to the following conditions: * 11 | * * 12 | * The above copyright notice and this permission notice shall be included * 13 | * in all copies or substantial portions of the Software. * 14 | * * 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 16 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 17 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 18 | * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 19 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 20 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 21 | * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 22 | * * 23 | * Except as contained in this notice, the name(s) of the above copyright * 24 | * holders shall not be used in advertising or otherwise to promote the * 25 | * sale, use or other dealings in this Software without prior written * 26 | * authorization. * 27 | ****************************************************************************/ 28 | /* $Id: ncurses_dll.h,v 1.6 2007/03/10 19:21:49 tom Exp $ */ 29 | 30 | #ifndef NCURSES_DLL_H_incl 31 | #define NCURSES_DLL_H_incl 1 32 | 33 | /* no longer needed on cygwin or mingw, thanks to auto-import */ 34 | /* but this structure may be useful at some point for an MSVC build */ 35 | /* so, for now unconditionally define the important flags */ 36 | /* "the right way" for proper static and dll+auto-import behavior */ 37 | #undef NCURSES_DLL 38 | #define NCURSES_STATIC 39 | 40 | #if defined(__CYGWIN__) 41 | # if defined(NCURSES_DLL) 42 | # if defined(NCURSES_STATIC) 43 | # undef NCURSES_STATIC 44 | # endif 45 | # endif 46 | # undef NCURSES_IMPEXP 47 | # undef NCURSES_API 48 | # undef NCURSES_EXPORT 49 | # undef NCURSES_EXPORT_VAR 50 | # if defined(NCURSES_DLL) 51 | /* building a DLL */ 52 | # define NCURSES_IMPEXP __declspec(dllexport) 53 | # elif defined(NCURSES_STATIC) 54 | /* building or linking to a static library */ 55 | # define NCURSES_IMPEXP /* nothing */ 56 | # else 57 | /* linking to the DLL */ 58 | # define NCURSES_IMPEXP __declspec(dllimport) 59 | # endif 60 | # define NCURSES_API __cdecl 61 | # define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API 62 | # define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type 63 | #endif 64 | 65 | /* Take care of non-cygwin platforms */ 66 | #if !defined(NCURSES_IMPEXP) 67 | # define NCURSES_IMPEXP /* nothing */ 68 | #endif 69 | #if !defined(NCURSES_API) 70 | # define NCURSES_API /* nothing */ 71 | #endif 72 | #if !defined(NCURSES_EXPORT) 73 | # define NCURSES_EXPORT(type) NCURSES_IMPEXP type NCURSES_API 74 | #endif 75 | #if !defined(NCURSES_EXPORT_VAR) 76 | # define NCURSES_EXPORT_VAR(type) NCURSES_IMPEXP type 77 | #endif 78 | 79 | /* 80 | * For reentrant code, we map the various global variables into SCREEN by 81 | * using functions to access them. 82 | */ 83 | #define NCURSES_PUBLIC_VAR(name) _nc_##name 84 | #define NCURSES_WRAPPED_VAR(type,name) extern type NCURSES_PUBLIC_VAR(name)(void) 85 | 86 | #endif /* NCURSES_DLL_H_incl */ 87 | -------------------------------------------------------------------------------- /NewTermMarzipan/FontMetrics.h: -------------------------------------------------------------------------------- 1 | // FontMetrics.h 2 | // MobileTerminal 3 | // 4 | // 5 | 6 | #import 7 | @import CoreText; 8 | 9 | @interface FontMetrics : NSObject 10 | 11 | - (instancetype)initWithFont:(UIFont *)font boldFont:(UIFont *)boldFont; 12 | 13 | @property (nonatomic, strong, readonly) UIFont *regularFont; 14 | @property (nonatomic, strong, readonly) UIFont *boldFont; 15 | 16 | // The dimensions of a single glyph on the screen 17 | @property (nonatomic, readonly) CGSize boundingBox; 18 | @property (nonatomic, readonly) CGFloat descent; 19 | @property (nonatomic, readonly) CGFloat ascent; 20 | @property (nonatomic, readonly) CGFloat leading; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /NewTermMarzipan/FontMetrics.m: -------------------------------------------------------------------------------- 1 | // FontMetrics.m 2 | // MobileTerminal 3 | 4 | #import "FontMetrics.h" 5 | 6 | @implementation FontMetrics 7 | 8 | - (instancetype)initWithFont:(UIFont *)uiFont boldFont:(UIFont *)boldUIFont { 9 | self = [super init]; 10 | 11 | if (self) { 12 | _regularFont = uiFont; 13 | _boldFont = boldUIFont; 14 | 15 | CTFontRef ctFont = (__bridge CTFontRef)_regularFont; 16 | NSAssert(ctFont != NULL, @"Error in CTFontCreateWithName"); 17 | 18 | // This creates a CoreText line that isn't drawn, but used to get the 19 | // size of a single character. This will probably fail miserably if used 20 | // with a non-monospaced font. 21 | CFStringRef string = CFSTR("A"); 22 | CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); 23 | CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), string); 24 | CFAttributedStringSetAttribute(attrString, CFRangeMake(0, CFStringGetLength(string)), kCTFontAttributeName, ctFont); 25 | CTLineRef line = CTLineCreateWithAttributedString(attrString); 26 | 27 | CGFloat width = CTLineGetTypographicBounds(line, &_ascent, &_descent, &_leading); 28 | 29 | CFRelease(line); 30 | CFRelease(attrString); 31 | CFRelease(ctFont); 32 | 33 | _boundingBox = CGSizeMake(width, _ascent + _descent + _leading); 34 | } 35 | 36 | return self; 37 | } 38 | 39 | @end 40 | -------------------------------------------------------------------------------- /NewTermMarzipan/Fonts.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Courier 6 | 7 | Regular 8 | Courier 9 | Bold 10 | Courier-Bold 11 | 12 | Courier New 13 | 14 | Regular 15 | CourierNewPSMT 16 | Bold 17 | CourierNew-BoldMT 18 | 19 | Fira Code 20 | 21 | Regular 22 | FiraCode-Retina 23 | Bold 24 | FiraCode-Bold 25 | 26 | Hack 27 | 28 | Regular 29 | Hack-Regular 30 | Bold 31 | Hack-Bold 32 | 33 | Menlo 34 | 35 | Regular 36 | Menlo-Regular 37 | Bold 38 | Menlo-Bold 39 | 40 | Source Code Pro 41 | 42 | Regular 43 | SourceCodePro-Regular 44 | Bold 45 | SourceCodePro-Bold 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTAppDelegate.h 3 | // NewTerm 4 | // 5 | // Created by Adam D on 20/07/2014. 6 | // Copyright (c) 2014 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HBNTAppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTAppDelegate.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 20/07/2014. 6 | // Copyright (c) 2014 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTAppDelegate.h" 10 | #import "HBNTRootViewController.h" 11 | 12 | @implementation HBNTAppDelegate 13 | 14 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 15 | // UIColor *textColor = [UIColor whiteColor]; 16 | // UIColor *tintColor = [UIColor colorWithRed:76.f / 255.f green:161.f / 255.f blue:1 alpha:1]; 17 | // UIColor *backgroundColor = [UIColor colorWithWhite:26.f / 255.f alpha:1]; 18 | // UIColor *lightTintColor = [UIColor colorWithWhite:60.f / 255.f alpha:1]; 19 | // 20 | // [UINavigationBar appearance].barStyle = UIBarStyleBlack; 21 | // [UIToolbar appearance].barStyle = UIBarStyleBlack; 22 | // 23 | // [UITableView appearance].backgroundColor = backgroundColor; 24 | // [UITableViewCell appearance].backgroundColor = backgroundColor; 25 | //#pragma clang diagnostic push 26 | //#pragma clang diagnostic ignored "-Wdeprecated-declarations" 27 | // // this is deprecated but apple doesn’t exactly provide an easy, supported way to do this 28 | // [UITableViewCell appearance].textColor = textColor; 29 | //#pragma clang diagnostic pop 30 | // 31 | // [UINavigationBar appearance].titleTextAttributes = @{ NSForegroundColorAttributeName: textColor }; 32 | // [UITextField appearance].textColor = textColor; 33 | // 34 | // [UITableView appearance].separatorColor = lightTintColor; 35 | // [UITextField appearance].keyboardAppearance = UIKeyboardAppearanceAlert; 36 | // 37 | // if ([UIScrollView instancesRespondToSelector:@selector(setKeyboardDismissMode:)]) { 38 | // [UIScrollView appearance].keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive; 39 | // } 40 | 41 | _window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | 43 | // if ([_window respondsToSelector:@selector(setTintColor:)]) { 44 | // _window.tintColor = tintColor; 45 | // } 46 | 47 | _window.rootViewController = [[UINavigationController alloc] initWithRootViewController:[[HBNTRootViewController alloc] init]]; 48 | [_window makeKeyAndVisible]; 49 | 50 | return YES; 51 | } 52 | 53 | @end 54 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTHUDView.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HBNTHUDView : UIView 4 | 5 | - (instancetype)initWithImage:(UIImage *)image; 6 | 7 | - (void)animate; 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTHUDView.m: -------------------------------------------------------------------------------- 1 | #import "HBNTHUDView.h" 2 | 3 | @implementation HBNTHUDView { 4 | UIImageView *_imageView; 5 | UIVisualEffectView *_backdropView; 6 | } 7 | 8 | - (instancetype)initWithImage:(UIImage *)image { 9 | self = [super initWithFrame:(CGRect){ CGPointZero, self.intrinsicContentSize }]; 10 | 11 | if (self) { 12 | self.alpha = 0; 13 | self.clipsToBounds = YES; 14 | self.layer.cornerRadius = 16; 15 | 16 | _backdropView = [[UIVisualEffectView alloc] initWithEffect:[UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; 17 | _backdropView.frame = self.bounds; 18 | _backdropView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 19 | [self addSubview:_backdropView]; 20 | 21 | _imageView = [[UIImageView alloc] initWithImage:image]; 22 | _imageView.center = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2); 23 | _imageView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleLeftMargin; 24 | [self addSubview:_imageView]; 25 | } 26 | 27 | return self; 28 | } 29 | 30 | - (CGSize)intrinsicContentSize { 31 | return CGSizeMake(54.f, 54.f); 32 | } 33 | 34 | - (void)animate { 35 | // if our alpha is non-zero, we’re already visible. maybe we should extend the visible duration 36 | // but eh. just do nothing 37 | if (self.alpha != 0) { 38 | return; 39 | } 40 | 41 | // display for 1.5 secs, fade out in 0.3 secs, then remove from superview 42 | self.alpha = 1; 43 | 44 | [UIView animateWithDuration:0.3f delay:0.75f options:kNilOptions animations:^{ 45 | self.alpha = 0; 46 | } completion:nil]; 47 | } 48 | 49 | @end 50 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTKeyboardButton.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HBNTKeyboardButton : UIButton 4 | 5 | + (instancetype)buttonWithTitle:(NSString *)title; 6 | + (instancetype)buttonWithTitle:(NSString *)title target:(id)target action:(SEL)action; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTKeyboardButton.m: -------------------------------------------------------------------------------- 1 | #import "HBNTKeyboardButton.h" 2 | 3 | @implementation HBNTKeyboardButton 4 | 5 | + (instancetype)buttonWithTitle:(NSString *)title { 6 | HBNTKeyboardButton *button = [[self alloc] initWithFrame:CGRectZero]; 7 | [button setTitle:title forState:UIControlStateNormal]; 8 | return button; 9 | } 10 | 11 | + (instancetype)buttonWithTitle:(NSString *)title target:(id)target action:(SEL)action { 12 | HBNTKeyboardButton *button = [[self alloc] initWithFrame:CGRectZero]; 13 | [button setTitle:title forState:UIControlStateNormal]; 14 | [button addTarget:target action:action forControlEvents:UIControlEventTouchUpInside]; 15 | return button; 16 | } 17 | 18 | - (instancetype)initWithFrame:(CGRect)frame { 19 | self = [super initWithFrame:frame]; 20 | 21 | if (self) { 22 | self.titleLabel.font = [UIFont systemFontOfSize:IS_IPAD ? 18.f : 15.f]; 23 | [self setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal]; 24 | [self setTitleColor:[UIColor blackColor] forState:UIControlStateSelected]; 25 | [self setBackgroundImage:[self _imageWithColor:[UIColor colorWithWhite:0.3529411765f alpha:1]] forState:UIControlStateNormal]; 26 | [self setBackgroundImage:[self _imageWithColor:[UIColor colorWithWhite:0.2078431373f alpha:1]] forState:UIControlStateHighlighted]; 27 | [self setBackgroundImage:[self _imageWithColor:[UIColor colorWithWhite:0.6784313725f alpha:1]] forState:UIControlStateSelected]; 28 | self.layer.cornerRadius = IS_IPAD ? 6.f : 4.f; 29 | self.clipsToBounds = YES; 30 | 31 | // make a click when tapped 32 | [self addTarget:[UIDevice currentDevice] action:@selector(playInputClick) forControlEvents:UIControlEventTouchUpInside]; 33 | } 34 | 35 | return self; 36 | } 37 | 38 | - (CGSize)intrinsicContentSize { 39 | CGSize contentSize = [super intrinsicContentSize]; 40 | contentSize.width += 16.f; 41 | contentSize.height = IS_IPAD ? 40.f : UIViewNoIntrinsicMetric; 42 | return contentSize; 43 | } 44 | 45 | - (UIImage *)_imageWithColor:(UIColor *)color { 46 | // https://stackoverflow.com/a/14525049/709376 47 | CGRect rect = CGRectMake(0, 0, 1.f, 1.f); 48 | UIGraphicsBeginImageContext(rect.size); 49 | CGContextRef context = UIGraphicsGetCurrentContext(); 50 | 51 | CGContextSetFillColorWithColor(context, color.CGColor); 52 | CGContextFillRect(context, rect); 53 | 54 | UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); 55 | UIGraphicsEndImageContext(); 56 | 57 | return image; 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTKeyboardToolbar.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @class HBNTKeyboardButton; 4 | 5 | @interface HBNTKeyboardToolbar : UIToolbar 6 | 7 | @property (nonatomic, strong) HBNTKeyboardButton *ctrlKey, *metaKey, *tabKey; 8 | @property (nonatomic, strong) HBNTKeyboardButton *upKey, *downKey, *leftKey, *rightKey; 9 | 10 | @end 11 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTKeyboardToolbar.m: -------------------------------------------------------------------------------- 1 | #import "HBNTKeyboardToolbar.h" 2 | #import "HBNTKeyboardButton.h" 3 | #import "UIView+CompactConstraint.h" 4 | 5 | @interface HBNTKeyboardToolbar () 6 | 7 | @end 8 | 9 | @implementation HBNTKeyboardToolbar 10 | 11 | - (instancetype)init { 12 | self = [super init]; 13 | 14 | if (self) { 15 | _ctrlKey = [HBNTKeyboardButton buttonWithTitle:@"Ctrl"]; 16 | _metaKey = [HBNTKeyboardButton buttonWithTitle:@"Esc"]; 17 | _tabKey = [HBNTKeyboardButton buttonWithTitle:@"Tab"]; 18 | 19 | UIView *spacerView = [[UIView alloc] init]; 20 | 21 | _upKey = [HBNTKeyboardButton buttonWithTitle:@"▲"]; 22 | _downKey = [HBNTKeyboardButton buttonWithTitle:@"▼"]; 23 | _leftKey = [HBNTKeyboardButton buttonWithTitle:@"◀"]; 24 | _rightKey = [HBNTKeyboardButton buttonWithTitle:@"▶"]; 25 | 26 | for (UIView *view in @[ _ctrlKey, _metaKey, _tabKey, spacerView, _upKey, _downKey, _leftKey, _rightKey ]) { 27 | view.translatesAutoresizingMaskIntoConstraints = NO; 28 | [self addSubview:view]; 29 | 30 | [self addConstraintsWithVisualFormat:@"V:|-margin-[key]-margin-|" options:kNilOptions metrics:@{ 31 | @"margin": self._isSmallDevice ? @2.f : @4.f 32 | } views:@{ 33 | @"key": view 34 | }]; 35 | } 36 | 37 | [self addConstraintsWithVisualFormat:@"H:|-outerMargin-[ctrlKey]-margin-[metaKey]-margin-[tabKey][spacerView(>=margin)][_upKey]-margin-[_downKey]-margin-[_leftKey]-margin-[_rightKey]-outerMargin-|" options:kNilOptions metrics:@{ 38 | @"outerMargin": @3.f, 39 | @"margin": @6.f 40 | } views:@{ 41 | @"ctrlKey": _ctrlKey, 42 | @"metaKey": _metaKey, 43 | @"tabKey": _tabKey, 44 | @"spacerView": spacerView, 45 | @"_upKey": _upKey, 46 | @"_downKey": _downKey, 47 | @"_leftKey": _leftKey, 48 | @"_rightKey": _rightKey, 49 | }]; 50 | } 51 | 52 | return self; 53 | } 54 | 55 | - (BOOL)enableInputClicksWhenVisible { 56 | // conforming to allows the buttons to make the click sound when tapped 57 | return YES; 58 | } 59 | 60 | - (BOOL)_isSmallDevice { 61 | return [UIScreen mainScreen].bounds.size.height < 600.f; 62 | } 63 | 64 | - (CGSize)intrinsicContentSize { 65 | CGSize size = [super intrinsicContentSize]; 66 | size.height = self._isSmallDevice ? 32.f : 44.f; 67 | return size; 68 | } 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPTY.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | // PTY.h 4 | // MobileTerminal 5 | 6 | // Controls settings on PTY, currently just width and height. 7 | @interface HBNTPTY : NSObject 8 | 9 | - (instancetype)initWithFileHandle:(NSFileHandle *)fileHandle; 10 | 11 | // Adjust the height and width of the subprocess terminal. 12 | - (void)setWidth:(int)terminalWidth withHeight:(int)terminalHeight; 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPTY.m: -------------------------------------------------------------------------------- 1 | // PTY.m 2 | // MobileTerminal 3 | 4 | #import "HBNTPTY.h" 5 | 6 | #include 7 | #include 8 | 9 | @implementation HBNTPTY { 10 | NSFileHandle *_handle; 11 | int _width; 12 | int _height; 13 | } 14 | 15 | - (instancetype)init { 16 | return [self initWithFileHandle:nil]; 17 | } 18 | 19 | - (instancetype)initWithFileHandle:(NSFileHandle *)fileHandle { 20 | self = [super init]; 21 | 22 | if (self) { 23 | _handle = fileHandle; 24 | 25 | // Initialize with the current window size 26 | struct winsize window_size; 27 | 28 | if (ioctl(_handle.fileDescriptor, TIOCGWINSZ, &window_size) == -1) { 29 | [NSException raise:@"IOException" format:@"Unable to read the terminal size: (%d: %s)", errno, strerror(errno)]; 30 | } 31 | 32 | _width = window_size.ws_col; 33 | _height = window_size.ws_row; 34 | } 35 | 36 | return self; 37 | } 38 | 39 | - (void)setWidth:(int)terminalWidth withHeight:(int)terminalHeight { 40 | if (_width == terminalWidth && _height == terminalHeight) { 41 | // Nothing changed 42 | return; 43 | } 44 | 45 | _width = terminalWidth; 46 | _height = terminalHeight; 47 | 48 | // Update the window size of the forked pty 49 | struct winsize window_size; 50 | window_size.ws_col = _width; 51 | window_size.ws_row = _height; 52 | 53 | if (ioctl(_handle.fileDescriptor, TIOCSWINSZ, &window_size) == -1) { 54 | [NSException raise:@"IOException" format:@"Unable to write the terminal size (%d: %s)", errno, strerror(errno)]; 55 | } 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPreferences.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTPreferences.h 3 | // NewTerm 4 | // 5 | // Created by Adam Demasi on 21/11/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | @import Foundation; 10 | //#import 11 | 12 | @class FontMetrics, VT100ColorMap; 13 | 14 | @interface HBNTPreferences : NSObject 15 | 16 | + (instancetype)sharedInstance; 17 | 18 | @property (strong, nonatomic, readonly) FontMetrics *fontMetrics; 19 | @property (strong, nonatomic, readonly) VT100ColorMap *colorMap; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPreferences.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTPreferences.m 3 | // NewTerm 4 | // 5 | // Created by Adam Demasi on 21/11/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTPreferences.h" 10 | #import "FontMetrics.h" 11 | #import "VT100ColorMap.h" 12 | 13 | @implementation HBNTPreferences { 14 | // HBPreferences *_preferences; 15 | NSMutableDictionary *_preferences; 16 | 17 | NSDictionary *_fontsPlist; 18 | NSDictionary *_themesPlist; 19 | 20 | NSString *_fontName; 21 | CGFloat _fontSize; 22 | NSString *_themeName; 23 | } 24 | 25 | + (instancetype)sharedInstance { 26 | static HBNTPreferences *sharedInstance; 27 | static dispatch_once_t onceToken; 28 | dispatch_once(&onceToken, ^{ 29 | sharedInstance = [[self alloc] init]; 30 | }); 31 | 32 | return sharedInstance; 33 | } 34 | 35 | - (instancetype)init { 36 | self = [super init]; 37 | 38 | if (self) { 39 | // _preferences = [[HBPreferences alloc] initWithIdentifier:@"ws.hbang.Terminal"]; 40 | // 41 | // [_preferences registerObject:&_fontName default:@"Fira Code" forKey:@"fontName"]; 42 | // [_preferences registerFloat:&_fontSize default:13.f forKey:IS_IPAD ? @"fontSizePad" : @"fontSizePhone"]; 43 | // [_preferences registerObject:&_themeName default:@"kirb" forKey:@"theme"]; 44 | 45 | _preferences = [@{ 46 | @"fontName": @"Menlo", 47 | @"fontSizePad": @13.f, 48 | @"fontSizePhone": @13.f, 49 | @"themeName": @"VSCode" 50 | } mutableCopy]; 51 | _fontName = @"Menlo"; 52 | _fontSize = 13.f; 53 | _themeName = @"VSCode"; 54 | 55 | _fontsPlist = @{ 56 | @"Menlo": @{ 57 | @"Regular": @"Menlo-Regular", 58 | @"Bold": @"Menlo-Bold", 59 | } 60 | }; 61 | // _fontsPlist = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"Fonts" withExtension:@"plist"]]; 62 | _themesPlist = [NSDictionary dictionaryWithContentsOfURL:[[NSBundle mainBundle] URLForResource:@"Themes" withExtension:@"plist"]]; 63 | 64 | // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(preferencesUpdated) name:HBPreferencesDidChangeNotification object:nil]; 65 | [self preferencesUpdated]; 66 | } 67 | 68 | return self; 69 | } 70 | 71 | #pragma mark - Callbacks 72 | 73 | - (void)preferencesUpdated { 74 | [self _fontMetricsChanged]; 75 | [self _colorMapChanged]; 76 | } 77 | 78 | #pragma mark - Create model objects from preferences 79 | 80 | - (void)_fontMetricsChanged { 81 | NSDictionary *family = _fontsPlist[_fontName]; 82 | UIFont *regularFont, *boldFont; 83 | 84 | if (family) { 85 | // if we have the font name as a known family, use its regular and bold names 86 | regularFont = [UIFont fontWithName:family[@"Regular"] size:_fontSize]; 87 | boldFont = [UIFont fontWithName:family[@"Bold"] size:_fontSize]; 88 | } else { 89 | // fallback for older style: raw font name stored in preferences. bold not supported 90 | regularFont = [UIFont fontWithName:_fontName size:_fontSize]; 91 | boldFont = [UIFont fontWithName:_fontName size:_fontSize]; 92 | } 93 | 94 | if (!regularFont || !boldFont) { 95 | NSLog(@"font %@ size %f could not be initialised", _fontName, _fontSize); 96 | regularFont = [UIFont fontWithName:@"Courier" size:13.f]; 97 | boldFont = [UIFont fontWithName:@"Courier-Bold" size:13.f]; 98 | } 99 | 100 | _fontMetrics = [[FontMetrics alloc] initWithFont:regularFont boldFont:boldFont]; 101 | } 102 | 103 | - (void)_colorMapChanged { 104 | // if the theme doesn’t exist… how did we get here? force it to the default, which will call this 105 | // method again 106 | if (!_themesPlist[_themeName]) { 107 | _preferences[@"theme"] = @"kirb"; 108 | return; 109 | } 110 | 111 | _colorMap = [[VT100ColorMap alloc] initWithDictionary:_themesPlist[_themeName]]; 112 | } 113 | 114 | @end 115 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPreferencesRootController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTPreferencesRootController.h 3 | // NewTerm 4 | // 5 | // Created by Adam Demasi on 30/10/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HBNTPreferencesRootController : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTPreferencesRootController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTPreferencesRootController.m 3 | // NewTerm 4 | // 5 | // Created by Adam Demasi on 30/10/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTPreferencesRootController.h" 10 | //#import "../prefs/HBNTPreferencesRootListController.h" 11 | //#import 12 | //#include 13 | 14 | @implementation HBNTPreferencesRootController 15 | 16 | #pragma mark - UIViewController 17 | 18 | - (void)loadView { 19 | [super loadView]; 20 | 21 | self.title = @"Preferences!"; 22 | } 23 | 24 | - (UIStatusBarStyle)preferredStatusBarStyle { 25 | return UIStatusBarStyleLightContent; 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTRootViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTRootViewController.h 3 | // NewTerm 4 | // 5 | // Created by Adam D on 26/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class HBNTTerminalSessionViewController; 12 | 13 | @interface HBNTRootViewController : UIViewController 14 | 15 | - (void)addTerminal; 16 | - (void)removeTerminal:(HBNTTerminalSessionViewController *)viewController; 17 | 18 | @property (nonatomic, assign) NSUInteger selectedTabIndex; 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTRootViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTRootViewController.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 26/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTRootViewController.h" 10 | #import "HBNTPreferencesRootController.h" 11 | #import "HBNTTerminalSessionViewController.h" 12 | #import "HBNTTabToolbar.h" 13 | #import "HBNTTabCollectionViewCell.h" 14 | //#import 15 | 16 | @interface HBNTRootViewController () 17 | 18 | @end 19 | 20 | @implementation HBNTRootViewController { 21 | NSMutableArray *_terminals; 22 | NSUInteger _selectedTabIndex; 23 | 24 | HBNTTabToolbar *_tabToolbar; 25 | UIToolbar *_bottomToolbar; 26 | UICollectionView *_tabsCollectionView; 27 | } 28 | 29 | - (void)loadView { 30 | [super loadView]; 31 | 32 | self.navigationController.navigationBarHidden = YES; 33 | 34 | _terminals = [NSMutableArray array]; 35 | 36 | _tabToolbar = [[HBNTTabToolbar alloc] init]; 37 | _tabToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth; 38 | [_tabToolbar.addButton addTarget:self action:@selector(addTerminal) forControlEvents:UIControlEventTouchUpInside]; 39 | 40 | _tabsCollectionView = _tabToolbar.tabsCollectionView; 41 | _tabsCollectionView.dataSource = self; 42 | _tabsCollectionView.delegate = self; 43 | 44 | [self.view addSubview:_tabToolbar]; 45 | 46 | _bottomToolbar = [[UIToolbar alloc] init]; 47 | _bottomToolbar.items = @[ 48 | [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"settings"] style:UIBarButtonItemStylePlain target:self action:@selector(showSettings:)], 49 | [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil], 50 | [[UIBarButtonItem alloc] initWithTitle:@"▲" style:UIBarButtonItemStylePlain target:self action:@selector(showKeyboard)] 51 | ]; 52 | [self.view addSubview:_bottomToolbar]; 53 | 54 | [self addTerminal]; 55 | } 56 | 57 | - (void)viewWillLayoutSubviews { 58 | [super viewWillLayoutSubviews]; 59 | 60 | CGFloat barHeight = [UIScreen mainScreen].bounds.size.height < 600.f ? 32.f : 40.f; 61 | CGFloat statusBarHeight = /*IS_IOS_OR_NEWER(iOS_7_0) ? [UIApplication sharedApplication].statusBarFrame.size.height : */22.f; 62 | CGFloat statusBarOffset = /*IS_IOS_OR_NEWER(iOS_7_0) ? 0 : -[UIApplication sharedApplication].statusBarFrame.size.height*/-22.f; 63 | 64 | _tabToolbar.frame = CGRectMake(0, 0, self.view.frame.size.width, statusBarHeight + barHeight); 65 | _bottomToolbar.frame = CGRectMake(0, self.view.frame.size.height - barHeight, self.view.frame.size.width, barHeight); 66 | 67 | UIEdgeInsets barInsets = UIEdgeInsetsMake(_tabToolbar.frame.size.height + statusBarOffset, 0, _bottomToolbar.frame.size.height, 0); 68 | 69 | for (HBNTTerminalSessionViewController *viewController in _terminals) { 70 | viewController.barInsets = barInsets; 71 | } 72 | } 73 | 74 | #pragma mark - Tab management 75 | 76 | - (void)addTerminal { 77 | HBNTTerminalSessionViewController *terminalViewController = [[HBNTTerminalSessionViewController alloc] init]; 78 | 79 | [self addChildViewController:terminalViewController]; 80 | [terminalViewController willMoveToParentViewController:self]; 81 | [self.view insertSubview:terminalViewController.view belowSubview:_tabToolbar]; 82 | [terminalViewController didMoveToParentViewController:self]; 83 | 84 | [_terminals addObject:terminalViewController]; 85 | 86 | [_tabsCollectionView reloadData]; 87 | [_tabsCollectionView layoutIfNeeded]; 88 | self.selectedTabIndex = _terminals.count - 1; 89 | [_tabsCollectionView reloadData]; 90 | } 91 | 92 | - (void)removeTerminalAtIndex:(NSUInteger)index { 93 | HBNTTerminalSessionViewController *terminalViewController = _terminals[index]; 94 | 95 | [terminalViewController removeFromParentViewController]; 96 | [terminalViewController.view removeFromSuperview]; 97 | 98 | [_terminals removeObjectAtIndex:index]; 99 | 100 | // if this was the last tab, make a new tab. otherwise select the closest tab we have available 101 | if (_terminals.count == 0) { 102 | [self addTerminal]; 103 | } else { 104 | [_tabsCollectionView reloadData]; 105 | [_tabsCollectionView layoutIfNeeded]; 106 | self.selectedTabIndex = index >= _terminals.count ? index - 1 : index; 107 | } 108 | } 109 | 110 | - (void)removeTerminal:(HBNTTerminalSessionViewController *)viewController { 111 | NSUInteger index = [_terminals indexOfObject:viewController]; 112 | 113 | if (index == NSNotFound) { 114 | NSLog(@"asked to remove terminal that doesn’t exist? %@", viewController); 115 | } else { 116 | [self removeTerminalAtIndex:index]; 117 | } 118 | } 119 | 120 | - (void)removeTerminalButtonTapped:(UIButton *)button { 121 | [self removeTerminalAtIndex:button.tag]; 122 | } 123 | 124 | - (NSUInteger)selectedTabIndex { 125 | return _selectedTabIndex; 126 | } 127 | 128 | - (void)setSelectedTabIndex:(NSUInteger)selectedTabIndex { 129 | // if this is what’s already selected, just select it again and return 130 | if (selectedTabIndex == _selectedTabIndex) { 131 | [_tabsCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedTabIndex inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredVertically]; 132 | return; 133 | } 134 | 135 | NSUInteger oldSelectedTabIndex = _selectedTabIndex < _terminals.count ? _selectedTabIndex : NSUIntegerMax; 136 | 137 | // if the previous index is now out of bounds, just use nil as our previous. the tab and view 138 | // controller were removed so we don’t need to do anything 139 | HBNTTerminalSessionViewController *previousViewController = _selectedTabIndex < _terminals.count ? _terminals[_selectedTabIndex] : nil; 140 | HBNTTerminalSessionViewController *newViewController = _terminals[selectedTabIndex]; 141 | 142 | _selectedTabIndex = selectedTabIndex; 143 | 144 | // call the appropriate view controller lifecycle methods on the previous and new view controllers 145 | [previousViewController viewWillDisappear:NO]; 146 | previousViewController.view.hidden = YES; 147 | [previousViewController viewDidDisappear:NO]; 148 | 149 | [newViewController viewWillAppear:NO]; 150 | newViewController.view.hidden = NO; 151 | [newViewController viewDidAppear:NO]; 152 | 153 | [_tabsCollectionView performBatchUpdates:^{ 154 | if (oldSelectedTabIndex != NSUIntegerMax) { 155 | [_tabsCollectionView deselectItemAtIndexPath:[NSIndexPath indexPathForItem:oldSelectedTabIndex inSection:0] animated:NO]; 156 | } 157 | 158 | [_tabsCollectionView selectItemAtIndexPath:[NSIndexPath indexPathForItem:selectedTabIndex inSection:0] animated:YES scrollPosition:UICollectionViewScrollPositionCenteredHorizontally]; 159 | } completion:^(BOOL finished) { 160 | // TODO: hack because the previous tab doesn’t deselect for some reason and ugh i hate this 161 | [_tabsCollectionView reloadData]; 162 | }]; 163 | } 164 | 165 | #pragma mark - Callbacks 166 | 167 | - (void)showSettings:(UIBarButtonItem *)sender { 168 | HBNTPreferencesRootController *rootController = [[HBNTPreferencesRootController alloc] init];// initWithTitle:NSLocalizedString(@"SETTINGS", @"Title of Settings page.") identifier:[NSBundle mainBundle].infoDictionary[@"CFBundleIdentifier"]]; 169 | rootController.modalPresentationStyle = UIModalPresentationFormSheet; 170 | [self.navigationController presentViewController:rootController animated:YES completion:nil]; 171 | } 172 | 173 | - (void)showKeyboard { 174 | // HBNTTerminalSessionViewController *viewController = _terminals[_selectedTabIndex]; 175 | // [viewController becomeFirstResponder]; 176 | } 177 | 178 | #pragma mark - Collection view 179 | 180 | - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { 181 | return _terminals.count; 182 | } 183 | 184 | - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { 185 | HBNTTerminalSessionViewController *terminalViewController = _terminals[indexPath.row]; 186 | 187 | HBNTTabCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"TabCell" forIndexPath:indexPath]; 188 | cell.textLabel.text = terminalViewController.title; 189 | cell.selected = _selectedTabIndex == indexPath.row; 190 | cell.closeButton.tag = indexPath.row; 191 | [cell.closeButton addTarget:self action:@selector(removeTerminalButtonTapped:) forControlEvents:UIControlEventTouchUpInside]; 192 | return cell; 193 | } 194 | 195 | - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { 196 | self.selectedTabIndex = indexPath.row; 197 | } 198 | 199 | - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { 200 | // hardcode a width for now, just so we work on ios 6. to be worked on… 201 | return CGSizeMake(100, _tabsCollectionView.frame.size.height); 202 | } 203 | 204 | @end 205 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTSubProcess.h: -------------------------------------------------------------------------------- 1 | // SubProcess.h 2 | // MobileTerminal 3 | 4 | #import "HBNTPTY.h" 5 | 6 | // Forks a terminal subprocess. 7 | @interface HBNTSubProcess : NSObject 8 | 9 | // Forks a terminal subprocess and initializes the fileHandle for communication. 10 | - (void)start; 11 | 12 | // Kills the forked subprocess, blocking until it ends. 13 | - (void)stop; 14 | 15 | // Communication channel with the terminal subprocess. Only valid after the 16 | // sub process is started. 17 | @property (strong, nonatomic, readonly) NSFileHandle *fileHandle; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTSubProcess.m: -------------------------------------------------------------------------------- 1 | // SubProcess.m 2 | // MobileTerminal 3 | 4 | #import "HBNTSubProcess.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // These are simply used to initialize the terminal and are probably thrown 11 | // away immediately after startup. 12 | static const int kDefaultWidth = 80; 13 | static const int kDefaultHeight = 25; 14 | 15 | // Default username if we can't tell from the environment 16 | static const char kDefaultUsername[] = "mobile"; 17 | 18 | @implementation HBNTSubProcess { 19 | pid_t _childPID; 20 | int _fileDescriptor; 21 | } 22 | 23 | - (void)dealloc { 24 | if (_childPID != 0) { 25 | [NSException raise:@"IllegalStateException" format:@"SubProcess was deallocated while running"]; 26 | } 27 | } 28 | 29 | #pragma mark - Start/stop 30 | 31 | - (void)start { 32 | if (_childPID != 0) { 33 | [NSException raise:@"IllegalStateException" format:@"SubProcess was already started"]; 34 | return; 35 | } 36 | 37 | const char *username = getenv("USER"); 38 | 39 | if (username == NULL) { 40 | username = kDefaultUsername; 41 | } 42 | 43 | struct winsize window_size; 44 | window_size.ws_col = kDefaultWidth; 45 | window_size.ws_row = kDefaultHeight; 46 | pid_t pid = forkpty(&_fileDescriptor, NULL, NULL, &window_size); 47 | 48 | if (pid == -1) { 49 | if (errno == EPERM) { 50 | [NSException raise:@"ForkException" format:@"Not allowed to fork from inside Sandbox"]; 51 | } else { 52 | [NSException raise:@"ForkException" format:@"Failed to fork child process (%d: %s)", errno, strerror(errno)]; 53 | } 54 | } else if (pid == 0) { 55 | // Handle the child subprocess 56 | // First try to use /bin/login since its a little nicer. Fall back to 57 | // /bin/sh if that is available. 58 | char *login_args[] = { "login", "-fp", (char *)username, NULL }; 59 | char *sh_args[] = { "sh", NULL }; 60 | 61 | // TODO: these should be configurable 62 | char *env[] = { 63 | "TERM=xterm-color", 64 | "LANG=en_US.UTF-8", 65 | NULL 66 | }; 67 | 68 | // NOTE: These should never return if successful 69 | [self _startProcess:@"/usr/bin/login" arguments:login_args environment:env]; 70 | [self _startProcess:@"/bin/login" arguments:login_args environment:env]; 71 | [self _startProcess:@"/bin/sh" arguments:sh_args environment:env]; 72 | [self _startProcess:@"/bootstrap/bin/bash" arguments:sh_args environment:env]; 73 | } else { 74 | NSLog(@"process forked: %d", pid); 75 | _childPID = pid; 76 | 77 | // We're the parent process (still). 78 | _fileHandle = [[NSFileHandle alloc] initWithFileDescriptor:_fileDescriptor closeOnDealloc:YES]; 79 | } 80 | } 81 | 82 | - (void)stop { 83 | if (_childPID == 0) { 84 | [NSException raise:@"IllegalStateException" format:@"SubProcess was never started"]; 85 | return; 86 | } 87 | 88 | kill(_childPID, SIGKILL); 89 | int stat; 90 | waitpid(_childPID, &stat, WUNTRACED); 91 | 92 | _fileDescriptor = 0; 93 | _childPID = 0; 94 | } 95 | 96 | - (int)_startProcess:(NSString *)path arguments:(char *const[])args environment:(char *const[])env { 97 | NSFileManager *fileManager = [NSFileManager defaultManager]; 98 | 99 | if (![fileManager fileExistsAtPath:path]) { 100 | return -1; 101 | } 102 | 103 | // Notably, we don't test group or other bits so this still might not always 104 | // notice if the binary is not executable by us. 105 | if (![fileManager isExecutableFileAtPath:path]) { 106 | return -1; 107 | } 108 | 109 | if (execve(path.UTF8String, args, env) == -1) { 110 | NSLog(@"%@: exec failed: %s", path, strerror(errno)); 111 | return -1; 112 | } 113 | 114 | // execve never returns if successful 115 | return 0; 116 | } 117 | 118 | @end 119 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTabCollectionViewCell.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HBNTTabCollectionViewCell : UICollectionViewCell 4 | 5 | @property (nonatomic, strong) UILabel *textLabel; 6 | @property (nonatomic, strong) UIButton *closeButton; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTabCollectionViewCell.m: -------------------------------------------------------------------------------- 1 | #import "HBNTTabCollectionViewCell.h" 2 | #import "UIView+CompactConstraint.h" 3 | 4 | @implementation HBNTTabCollectionViewCell 5 | 6 | - (instancetype)initWithFrame:(CGRect)frame { 7 | self = [super initWithFrame:frame]; 8 | 9 | if (self) { 10 | // self.contentView.translatesAutoresizingMaskIntoConstraints = NO; 11 | 12 | self.selectedBackgroundView = [[UIView alloc] init]; 13 | self.selectedBackgroundView.backgroundColor = [UIColor colorWithWhite:85.f / 255.f alpha:0.7f]; 14 | 15 | _textLabel = [[UILabel alloc] init]; 16 | _textLabel.translatesAutoresizingMaskIntoConstraints = NO; 17 | _textLabel.font = [UIFont systemFontOfSize:16.f]; 18 | _textLabel.textColor = [UIColor whiteColor]; 19 | _textLabel.backgroundColor = [UIColor clearColor]; 20 | [self.contentView addSubview:_textLabel]; 21 | 22 | _closeButton = [[UIButton alloc] init]; 23 | _closeButton.translatesAutoresizingMaskIntoConstraints = NO; 24 | _closeButton.accessibilityLabel = NSLocalizedString(@"CLOSE_TAB", @"VoiceOver label for the close tab button."); 25 | _closeButton.titleLabel.font = [UIFont systemFontOfSize:16.f]; 26 | [_closeButton setTitle:@"×" forState:UIControlStateNormal]; 27 | [self.contentView addSubview:_closeButton]; 28 | 29 | [self.contentView addCompactConstraints:@[ 30 | @"textLabel.centerY = contentView.centerY", 31 | @"textLabel.left = contentView.left + 6", 32 | @"closeButton.width = 24", 33 | @"closeButton.height = contentView.height", 34 | @"closeButton.left = textLabel.right", 35 | @"closeButton.right = contentView.right" 36 | ] metrics:@{} views:@{ 37 | @"contentView": self.contentView, 38 | @"textLabel": _textLabel, 39 | @"closeButton": _closeButton 40 | }]; 41 | } 42 | 43 | return self; 44 | } 45 | 46 | - (CGSize)intrinsicContentSize { 47 | CGSize size = [super intrinsicContentSize]; 48 | size.height = [UIScreen mainScreen].bounds.size.height < 600 ? 32.f : 40.f; 49 | return size; 50 | } 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTabToolbar.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface HBNTTabToolbar : UIToolbar 4 | 5 | @property (nonatomic, strong) UICollectionView *tabsCollectionView; 6 | @property (nonatomic, strong) UIButton *addButton; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTabToolbar.m: -------------------------------------------------------------------------------- 1 | #import "HBNTTabToolbar.h" 2 | #import "HBNTTabCollectionViewCell.h" 3 | //#import 4 | 5 | @implementation HBNTTabToolbar 6 | 7 | - (instancetype)init { 8 | self = [super init]; 9 | 10 | if (self) { 11 | UICollectionViewFlowLayout *collectionViewLayout = [[UICollectionViewFlowLayout alloc] init]; 12 | collectionViewLayout.scrollDirection = UICollectionViewScrollDirectionHorizontal; 13 | collectionViewLayout.minimumInteritemSpacing = 0; 14 | collectionViewLayout.minimumLineSpacing = 0; 15 | 16 | // the weird frame is to appease ios 6 UICollectionView 17 | _tabsCollectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(0, 0, 100, 100) collectionViewLayout:collectionViewLayout]; 18 | _tabsCollectionView.backgroundColor = nil; 19 | _tabsCollectionView.showsVerticalScrollIndicator = NO; 20 | _tabsCollectionView.showsHorizontalScrollIndicator = NO; 21 | // _tabsCollectionView.indicatorStyle = UIScrollViewIndicatorStyleWhite; 22 | _tabsCollectionView.allowsMultipleSelection = NO; 23 | [self addSubview:_tabsCollectionView]; 24 | 25 | [_tabsCollectionView registerClass:HBNTTabCollectionViewCell.class forCellWithReuseIdentifier:@"TabCell"]; 26 | 27 | // TODO: maybe this should be moved to the toolbar as a UIBarButtonItem to appease iOS 6 28 | _addButton = [UIButton buttonWithType:/*IS_IOS_OR_NEWER(iOS_7_0) ?*/ UIButtonTypeSystem /*: UIButtonTypeCustom*/]; 29 | _addButton.titleLabel.font = [UIFont systemFontOfSize:18.f]; 30 | [_addButton setTitle:@"+" forState:UIControlStateNormal]; 31 | _addButton.accessibilityLabel = NSLocalizedString(@"NEW_TAB", @"VoiceOver label for the new tab button."); 32 | [self addSubview:_addButton]; 33 | 34 | CGFloat shadowHeight = 1 / [UIScreen mainScreen].scale; 35 | UIView *shadowView = [[UIView alloc] initWithFrame:CGRectMake(0, self.frame.size.height - shadowHeight, self.frame.size.width, shadowHeight)]; 36 | shadowView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin; 37 | shadowView.backgroundColor = [UIColor colorWithWhite:64.f / 255.f alpha:1]; 38 | [self addSubview:shadowView]; 39 | } 40 | 41 | return self; 42 | } 43 | 44 | - (void)layoutSubviews { 45 | [super layoutSubviews]; 46 | 47 | CGFloat statusBarHeight = /*IS_IOS_OR_NEWER(iOS_7_0) ? [UIApplication sharedApplication].statusBarFrame.size.height : */22.f; 48 | CGFloat addButtonWidth = 44.f; 49 | 50 | _addButton.frame = CGRectMake(self.frame.size.width - addButtonWidth, statusBarHeight, addButtonWidth, self.frame.size.height - statusBarHeight); 51 | _tabsCollectionView.frame = CGRectMake(0, statusBarHeight, _addButton.frame.origin.x, _addButton.frame.size.height); 52 | 53 | CGFloat newButtonSize = _addButton.frame.size.height < 44.f ? 18.f : 24.f; 54 | 55 | if (_addButton.titleLabel.font.pointSize != newButtonSize) { 56 | _addButton.titleLabel.font = [UIFont systemFontOfSize:newButtonSize]; 57 | } 58 | } 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalController.h 3 | // NewTerm 4 | // 5 | // Created by Adam D on 22/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTTerminalKeyInput.h" 10 | #import "VT100Types.h" 11 | 12 | @class HBNTTerminalSessionViewController, VT100ColorMap, FontMetrics; 13 | 14 | @protocol HBNTTerminalControllerDelegate 15 | 16 | @required 17 | - (void)refreshWithAttributedString:(NSAttributedString *)attributedString backgroundColor:(UIColor *)backgroundColor; 18 | - (void)activateBell; 19 | - (void)close; 20 | 21 | @end 22 | 23 | @interface HBNTTerminalController : NSObject 24 | 25 | - (void)startSubProcess; 26 | - (void)stopSubProcess; 27 | 28 | @property (nonatomic, strong) HBNTTerminalSessionViewController *viewController; 29 | @property (nonatomic) ScreenSize screenSize; 30 | @property (nonatomic, strong, readonly) VT100ColorMap *colorMap; 31 | @property (nonatomic, strong, readonly) FontMetrics *fontMetrics; 32 | @property (nonatomic, readonly) int scrollbackLines; 33 | 34 | @property (nonatomic, weak) id delegate; 35 | 36 | @end 37 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalController.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 22/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTTerminalController.h" 10 | #import "HBNTTerminalSessionViewController.h" 11 | #import "HBNTSubProcess.h" 12 | #import "HBNTPTY.h" 13 | #import "HBNTPreferences.h" 14 | #import "VT100.h" 15 | #import "VT100StringSupplier.h" 16 | #import "VT100ColorMap.h" 17 | #import "FontMetrics.h" 18 | 19 | @interface HBNTTerminalController () 20 | 21 | @end 22 | 23 | @implementation HBNTTerminalController { 24 | VT100 *_buffer; 25 | VT100StringSupplier *_stringSupplier; 26 | dispatch_queue_t _updateQueue; 27 | 28 | HBNTSubProcess *_subProcess; 29 | HBNTPTY *_pty; 30 | 31 | BOOL _processEnded; 32 | } 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | 37 | if (self) { 38 | // create a serial background queue for updating the text view, using the address of self to 39 | // make the name unique 40 | _updateQueue = dispatch_queue_create([NSString stringWithFormat:@"ws.hbang.Terminal.update-queue-%p", self].UTF8String, DISPATCH_QUEUE_SERIAL); 41 | 42 | _buffer = [[VT100 alloc] init]; 43 | _buffer.refreshDelegate = self; 44 | 45 | _stringSupplier = [[VT100StringSupplier alloc] init]; 46 | _stringSupplier.screenBuffer = _buffer; 47 | 48 | // [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(preferencesUpdated) name:HBPreferencesDidChangeNotification object:nil]; 49 | [self preferencesUpdated]; 50 | } 51 | 52 | return self; 53 | } 54 | 55 | #pragma mark - Screen Buffer 56 | 57 | // TODO: this is pretty ugly. maybe we should just be a VT100 subclass? 58 | 59 | - (id )refreshDelegate { 60 | return _buffer.refreshDelegate; 61 | } 62 | 63 | - (void)setRefreshDelegate:(id )refreshDelegate { 64 | _buffer.refreshDelegate = refreshDelegate; 65 | } 66 | 67 | - (ScreenSize)screenSize { 68 | return _buffer.screenSize; 69 | } 70 | 71 | - (void)setScreenSize:(ScreenSize)screenSize { 72 | // Send the terminal the actual size of our vt100 view. This should be called any time we change 73 | // the size of the view. This should be a no-op if the size has not changed since the last time we 74 | // called it. 75 | _buffer.screenSize = screenSize; 76 | [_pty setWidth:screenSize.width withHeight:screenSize.height]; 77 | } 78 | 79 | - (void)readInputStream:(NSData *)data { 80 | // Simply forward the input stream down the VT100 processor. When it notices 81 | // changes to the screen, it should invoke our refresh delegate below. 82 | [_buffer readInputStream:data]; 83 | } 84 | 85 | - (void)clearScreen { 86 | [_buffer clearScreen]; 87 | } 88 | 89 | - (int)scrollbackLines { 90 | return _buffer.scrollbackLines; 91 | } 92 | 93 | - (VT100ColorMap *)colorMap { 94 | return _stringSupplier.colorMap; 95 | } 96 | 97 | - (FontMetrics *)fontMetrics { 98 | return _stringSupplier.fontMetrics; 99 | } 100 | 101 | #pragma mark - Screen Buffer Delegate 102 | 103 | - (void)refresh { 104 | // TODO: we should handle the scrollback separately so it only appears if the user scrolls 105 | dispatch_async(_updateQueue, ^{ 106 | NSAttributedString *attributedString = _stringSupplier.attributedString; 107 | UIColor *backgroundColor = _stringSupplier.colorMap.background; 108 | 109 | dispatch_async(dispatch_get_main_queue(), ^{ 110 | [_delegate refreshWithAttributedString:attributedString backgroundColor:backgroundColor]; 111 | }); 112 | }); 113 | } 114 | 115 | - (void)activateBell { 116 | [_delegate activateBell]; 117 | } 118 | 119 | #pragma mark - Sub Process 120 | 121 | - (void)startSubProcess { 122 | _processEnded = NO; 123 | 124 | _subProcess = [[HBNTSubProcess alloc] init]; 125 | [_subProcess start]; 126 | 127 | // The PTY will be sized correctly on the first call to layoutSubViews 128 | _pty = [[HBNTPTY alloc] initWithFileHandle:_subProcess.fileHandle]; 129 | 130 | // Schedule an async read of the subprocess. Invokes our callback when data becomes available. 131 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(dataAvailable:) name:NSFileHandleReadCompletionNotification object:_subProcess.fileHandle]; 132 | [_subProcess.fileHandle readInBackgroundAndNotify]; 133 | } 134 | 135 | - (void)dataAvailable:(NSNotification *)notification { 136 | NSData *data = notification.userInfo[NSFileHandleNotificationDataItem]; 137 | 138 | if (data.length == 0) { 139 | // zero-length data is an indicator of EOF. this can happen if the user exits the terminal by 140 | // typing `exit`, or if there’s a catastrophic failure (e.g. /bin/login is broken). we print a 141 | // message to the terminal saying the process ended, and then when the user presses any key, the 142 | // tab will close 143 | NSString *message = [NSString stringWithFormat:@"[%@]\r\n%@\r\n", NSLocalizedString(@"PROCESS_COMPLETED_TITLE", @"Title displayed when the terminal’s process has ended."), NSLocalizedString(@"PROCESS_COMPLETED_MESSAGE", @"Message indicating the user can press any key to close the tab.")]; 144 | [self readInputStream:[message dataUsingEncoding:NSUTF8StringEncoding]]; 145 | 146 | _processEnded = YES; 147 | [_subProcess stop]; 148 | } else { 149 | // Forward the subprocess data into the terminal character handler 150 | [self readInputStream:data]; 151 | 152 | // Queue another read 153 | [_subProcess.fileHandle readInBackgroundAndNotify]; 154 | } 155 | } 156 | 157 | - (void)stopSubProcess { 158 | _processEnded = YES; 159 | [_subProcess stop]; 160 | } 161 | 162 | - (void)receiveKeyboardInput:(NSData *)data { 163 | if (_processEnded) { 164 | // The sub process previously exited, close it at the users request. 165 | [_viewController close]; 166 | } else { 167 | // Forward the data from the keyboard directly to the subprocess 168 | [_subProcess.fileHandle writeData:data]; 169 | } 170 | } 171 | 172 | #pragma mark - Preferences 173 | 174 | - (void)preferencesUpdated { 175 | HBNTPreferences *preferences = [HBNTPreferences sharedInstance]; 176 | _stringSupplier.colorMap = preferences.colorMap; 177 | _stringSupplier.fontMetrics = preferences.fontMetrics; 178 | 179 | [self refresh]; 180 | } 181 | 182 | #pragma mark - NSObject 183 | 184 | - (void)dealloc { 185 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 186 | } 187 | 188 | @end 189 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalKeyInput.h: -------------------------------------------------------------------------------- 1 | #import "HBNTTextInputBase.h" 2 | 3 | // Protocol implemented by listener of keyboard events 4 | @protocol HBNTTerminalInputProtocol 5 | 6 | @required 7 | - (void)receiveKeyboardInput:(NSData *)data; 8 | 9 | @end 10 | 11 | @protocol HBNTTerminalKeyboardProtocol 12 | 13 | @end 14 | 15 | @interface HBNTTerminalKeyInput : HBNTTextInputBase 16 | 17 | @property (nonatomic, strong) UITextView *textView; 18 | 19 | // TODO: i think this should be weak? 20 | @property (nonatomic, strong) id terminalInputDelegate; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalKeyInput.m: -------------------------------------------------------------------------------- 1 | #import "HBNTTerminalKeyInput.h" 2 | #import "HBNTKeyboardButton.h" 3 | #import "HBNTKeyboardToolbar.h" 4 | 5 | @implementation HBNTTerminalKeyInput { 6 | HBNTKeyboardToolbar *_toolbar; 7 | HBNTKeyboardButton *_ctrlKey, *_metaKey; 8 | BOOL _ctrlDown, _metaDown; 9 | 10 | NSData *_backspaceData, *_tabKeyData, *_upKeyData, *_downKeyData, *_leftKeyData, *_rightKeyData; 11 | } 12 | 13 | - (instancetype)init { 14 | self = [super init]; 15 | 16 | if (self) { 17 | self.autocapitalizationType = UITextAutocapitalizationTypeNone; 18 | self.autocorrectionType = UITextAutocorrectionTypeNo; 19 | self.spellCheckingType = UITextSpellCheckingTypeNo; 20 | 21 | if (@available(iOS 11.0, *)) { 22 | self.smartQuotesType = UITextSmartQuotesTypeNo; 23 | self.smartDashesType = UITextSmartDashesTypeNo; 24 | self.smartInsertDeleteType = UITextSmartInsertDeleteTypeNo; 25 | } 26 | 27 | // TODO: this should be themable 28 | self.keyboardAppearance = UIKeyboardAppearanceDark; 29 | 30 | // TODO: this is kinda ugly and causes duped code for these buttons 31 | if (IS_IPAD && [self respondsToSelector:@selector(inputAssistantItem)]) { 32 | _ctrlKey = [HBNTKeyboardButton buttonWithTitle:@"Ctrl" target:self action:@selector(ctrlKeyPressed:)]; 33 | _metaKey = [HBNTKeyboardButton buttonWithTitle:@"Esc" target:self action:@selector(metaKeyPressed:)]; 34 | 35 | self.inputAssistantItem.allowsHidingShortcuts = NO; 36 | self.inputAssistantItem.leadingBarButtonGroups = [self.inputAssistantItem.leadingBarButtonGroups arrayByAddingObject: 37 | [[UIBarButtonItemGroup alloc] initWithBarButtonItems:@[ 38 | [[UIBarButtonItem alloc] initWithCustomView:_ctrlKey], 39 | [[UIBarButtonItem alloc] initWithCustomView:_metaKey], 40 | [[UIBarButtonItem alloc] initWithCustomView:[HBNTKeyboardButton buttonWithTitle:@"Tab" target:self action:@selector(tabKeyPressed:)]] 41 | ] representativeItem:nil]]; 42 | self.inputAssistantItem.trailingBarButtonGroups = [self.inputAssistantItem.trailingBarButtonGroups arrayByAddingObject: 43 | [[UIBarButtonItemGroup alloc] initWithBarButtonItems:@[ 44 | [[UIBarButtonItem alloc] initWithCustomView:[HBNTKeyboardButton buttonWithTitle:@"▲" target:self action:@selector(upKeyPressed:)]], 45 | [[UIBarButtonItem alloc] initWithCustomView:[HBNTKeyboardButton buttonWithTitle:@"▼" target:self action:@selector(downKeyPressed:)]], 46 | [[UIBarButtonItem alloc] initWithCustomView:[HBNTKeyboardButton buttonWithTitle:@"◀" target:self action:@selector(leftKeyPressed:)]], 47 | [[UIBarButtonItem alloc] initWithCustomView:[HBNTKeyboardButton buttonWithTitle:@"▶" target:self action:@selector(rightKeyPressed:)]] 48 | ] representativeItem:nil]]; 49 | } else { 50 | _toolbar = [[HBNTKeyboardToolbar alloc] init]; 51 | _toolbar.translatesAutoresizingMaskIntoConstraints = NO; 52 | [_toolbar.ctrlKey addTarget:self action:@selector(ctrlKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 53 | [_toolbar.metaKey addTarget:self action:@selector(metaKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 54 | [_toolbar.tabKey addTarget:self action:@selector(tabKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 55 | [_toolbar.upKey addTarget:self action:@selector(upKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 56 | [_toolbar.downKey addTarget:self action:@selector(downKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 57 | [_toolbar.leftKey addTarget:self action:@selector(leftKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 58 | [_toolbar.rightKey addTarget:self action:@selector(rightKeyPressed:) forControlEvents:UIControlEventTouchUpInside]; 59 | 60 | _ctrlKey = _toolbar.ctrlKey; 61 | _metaKey = _toolbar.metaKey; 62 | } 63 | 64 | _backspaceData = [NSData dataWithBytes:"\x7F" length:1]; 65 | _tabKeyData = [NSData dataWithBytes:"\t" length:1]; 66 | _upKeyData = [NSData dataWithBytes:"\e[A" length:3]; 67 | _downKeyData = [NSData dataWithBytes:"\e[B" length:3]; 68 | _leftKeyData = [NSData dataWithBytes:"\e[D" length:3]; 69 | _rightKeyData = [NSData dataWithBytes:"\e[C" length:3]; 70 | } 71 | 72 | return self; 73 | } 74 | 75 | - (UIView *)inputAccessoryView { 76 | return _toolbar; 77 | } 78 | 79 | #pragma mark - Callbacks 80 | 81 | - (void)ctrlKeyPressed:(UIButton *)button { 82 | _ctrlDown = !_ctrlDown; 83 | button.selected = _ctrlDown; 84 | } 85 | 86 | - (void)metaKeyPressed:(UIButton *)button { 87 | _metaDown = !_metaDown; 88 | button.selected = _metaDown; 89 | } 90 | 91 | - (void)tabKeyPressed:(UIButton *)button { 92 | [_terminalInputDelegate receiveKeyboardInput:_tabKeyData]; 93 | } 94 | 95 | - (void)upKeyPressed:(UIButton *)button { 96 | [_terminalInputDelegate receiveKeyboardInput:_upKeyData]; 97 | } 98 | 99 | - (void)downKeyPressed:(UIButton *)button { 100 | [_terminalInputDelegate receiveKeyboardInput:_downKeyData]; 101 | } 102 | 103 | - (void)leftKeyPressed:(UIButton *)button { 104 | [_terminalInputDelegate receiveKeyboardInput:_leftKeyData]; 105 | } 106 | 107 | - (void)rightKeyPressed:(UIButton *)button { 108 | [_terminalInputDelegate receiveKeyboardInput:_rightKeyData]; 109 | } 110 | 111 | #pragma mark - UITextInput 112 | 113 | - (BOOL)hasText { 114 | return YES; 115 | } 116 | 117 | - (void)insertText:(NSString *)input { 118 | // mobile terminal used to use the bullet key • as a ctrl equivalent. continue to support that 119 | // TODO: is this just a nuisance? what if you actually want to type a bullet? 120 | if ([input characterAtIndex:0] == 0x2022) { 121 | _ctrlDown = YES; 122 | _ctrlKey.selected = YES; 123 | return; 124 | } 125 | 126 | NSMutableData *data = [NSMutableData data]; 127 | 128 | unichar characters[input.length]; 129 | [input getCharacters:characters range:NSMakeRange(0, input.length)]; 130 | 131 | for (int i = 0; i < input.length; i++) { 132 | unichar character = characters[i]; 133 | 134 | if (_ctrlDown) { 135 | // translate capital to lowercase 136 | if (character >= 'A' && character <= 'Z') { 137 | character += 'a' - 'A'; 138 | } 139 | 140 | // convert to the matching control character 141 | if (character >= 'a' && character <= 'z') { 142 | character -= 'a' - 1; 143 | } 144 | } else if (_metaDown) { 145 | // prepend the escape character 146 | [data appendBytes:"\e" length:1]; 147 | } 148 | 149 | if (character == 0x0a) { 150 | // Convert newline to a carraige return 151 | character = 0x0d; 152 | } 153 | 154 | // Re-encode as UTF8 155 | [data appendBytes:&character length:1]; 156 | } 157 | 158 | [_terminalInputDelegate receiveKeyboardInput:data]; 159 | 160 | if (_ctrlDown) { 161 | _ctrlDown = NO; 162 | _ctrlKey.selected = NO; 163 | } 164 | 165 | if (_metaDown) { 166 | _metaDown = NO; 167 | _metaKey.selected = NO; 168 | } 169 | } 170 | 171 | - (void)deleteBackward { 172 | [_terminalInputDelegate receiveKeyboardInput:_backspaceData]; 173 | } 174 | 175 | #pragma mark - UIResponder 176 | 177 | - (BOOL)becomeFirstResponder { 178 | [super becomeFirstResponder]; 179 | return YES; 180 | } 181 | 182 | - (BOOL)canBecomeFirstResponder { 183 | return YES; 184 | } 185 | 186 | - (BOOL)canPerformAction:(SEL)action withSender:(id)sender { 187 | if (action == @selector(paste:)) { 188 | // Only paste if the board contains plain text 189 | return [[UIPasteboard generalPasteboard] containsPasteboardTypes:UIPasteboardTypeListString]; 190 | } else if (action == @selector(cut:)) { 191 | // ensure cut is never allowed 192 | return NO; 193 | } else if (action == @selector(copy) || action == @selector(select:) || action == @selector(selectAll:)) { 194 | // allow copy, select, select all based on what the text view feels like doing 195 | return [_textView canPerformAction:action withSender:sender]; 196 | } 197 | 198 | // consult the super implementation’s opinion 199 | return [super canPerformAction:action withSender:sender]; 200 | } 201 | 202 | - (void)copy:(id)sender { 203 | // forward the operation to the text view 204 | [_textView copy:sender]; 205 | } 206 | 207 | - (void)select:(id)sender { 208 | [_textView select:sender]; 209 | } 210 | 211 | - (void)selectAll:(id)sender { 212 | [_textView selectAll:sender]; 213 | } 214 | 215 | - (void)paste:(id)sender { 216 | UIPasteboard *pasteboard = [UIPasteboard generalPasteboard]; 217 | 218 | if (![pasteboard containsPasteboardTypes:UIPasteboardTypeListString]) { 219 | return; 220 | } 221 | 222 | [_terminalInputDelegate receiveKeyboardInput:[pasteboard.string dataUsingEncoding:NSUTF8StringEncoding]]; 223 | } 224 | 225 | @end 226 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalSessionViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalSessionViewController.h 3 | // NewTerm 4 | // 5 | // Created by Adam D on 12/12/2014. 6 | // Copyright (c) 2014 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "VT100Types.h" 12 | #import "HBNTTerminalController.h" 13 | 14 | @class VT100ColorMap; 15 | 16 | @interface HBNTTerminalSessionViewController : UIViewController 17 | 18 | @property (nonatomic, strong, readonly) UITextView *textView; 19 | @property (nonatomic) UIEdgeInsets barInsets; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalSessionViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalSessionViewController.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 12/12/2014. 6 | // Copyright (c) 2014 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTTerminalSessionViewController.h" 10 | #import "HBNTTerminalController.h" 11 | #import "HBNTTerminalKeyInput.h" 12 | #import "HBNTTerminalTextView.h" 13 | #import "HBNTHUDView.h" 14 | #import "HBNTPreferences.h" 15 | #import "HBNTRootViewController.h" 16 | #import "VT100ColorMap.h" 17 | #import "FontMetrics.h" 18 | #import "UIView+CompactConstraint.h" 19 | 20 | @implementation HBNTTerminalSessionViewController { 21 | HBNTTerminalController *_terminalController; 22 | HBNTTerminalKeyInput *_keyInput; 23 | HBNTTerminalTextView *_textView; 24 | HBNTHUDView *_bellHUDView; 25 | 26 | NSException *_failureException; 27 | 28 | BOOL _hasAppeared; 29 | BOOL _hasStarted; 30 | CGFloat _keyboardHeight; 31 | CGPoint _lastAutomaticScrollOffset; 32 | } 33 | 34 | - (instancetype)init { 35 | self = [super init]; 36 | 37 | if (self) { 38 | _terminalController = [[HBNTTerminalController alloc] init]; 39 | _terminalController.viewController = self; 40 | _terminalController.delegate = self; 41 | 42 | @try { 43 | [_terminalController startSubProcess]; 44 | _hasStarted = YES; 45 | } @catch (NSException *exception) { 46 | _failureException = exception; 47 | } 48 | } 49 | 50 | return self; 51 | } 52 | 53 | - (void)loadView { 54 | [super loadView]; 55 | 56 | self.title = NSLocalizedString(@"TERMINAL", @"Generic title displayed before the terminal sets a proper title."); 57 | 58 | _textView = [[HBNTTerminalTextView alloc] initWithFrame:self.view.bounds]; 59 | _textView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; 60 | _textView.showsVerticalScrollIndicator = NO; 61 | _textView.showsHorizontalScrollIndicator = NO; 62 | // TODO: this breaks stuff sorta 63 | // [_textView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTextViewTap:)]]; 64 | [self.view addSubview:_textView]; 65 | 66 | _keyInput = [[HBNTTerminalKeyInput alloc] init]; 67 | _keyInput.textView = _textView; 68 | _keyInput.terminalInputDelegate = _terminalController; 69 | [self.view addSubview:_keyInput]; 70 | } 71 | 72 | - (void)viewWillAppear:(BOOL)animated { 73 | [super viewWillAppear:animated]; 74 | 75 | // [self registerForKeyboardNotifications]; 76 | // [self becomeFirstResponder]; 77 | } 78 | 79 | - (void)viewWillDisappear:(BOOL)animated { 80 | [super viewWillDisappear:animated]; 81 | 82 | // [self unregisterForKeyboardNotifications]; 83 | // [self resignFirstResponder]; 84 | } 85 | 86 | - (void)viewWillLayoutSubviews { 87 | [super viewWillLayoutSubviews]; 88 | [self updateScreenSize]; 89 | } 90 | 91 | - (void)viewDidAppear:(BOOL)animated { 92 | [super viewDidAppear:animated]; 93 | 94 | if (_failureException) { 95 | NSString *ok = NSLocalizedStringFromTableInBundle(@"OK", @"Localizable", [NSBundle bundleForClass:UIView.class], nil); 96 | 97 | UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"TERMINAL_LAUNCH_FAILED", @"Alert title displayed when a terminal could not be launched.") message:_failureException.reason preferredStyle:UIAlertControllerStyleAlert]; 98 | [alertController addAction:[UIAlertAction actionWithTitle:ok style:UIAlertActionStyleCancel handler:nil]]; 99 | [self presentViewController:alertController animated:YES completion:nil]; 100 | } 101 | } 102 | 103 | - (void)removeFromParentViewController { 104 | if (_hasStarted) { 105 | // TODO: for some reason this always throws “subprocess was never started”? groannnn 106 | // [_terminalController stopSubProcess]; 107 | } 108 | 109 | [super removeFromParentViewController]; 110 | } 111 | 112 | #pragma mark - Screen 113 | 114 | - (void)updateScreenSize { 115 | // update the text view insets. if the keyboard height is non-zero, keyboard is visible and that’s 116 | // our bottom inset. else, it’s not and the bottom toolbar height is the bottom inset 117 | UIEdgeInsets barInsets = _barInsets; 118 | barInsets.bottom = _keyboardHeight ?: barInsets.bottom; 119 | 120 | _textView.contentInset = barInsets; 121 | _textView.scrollIndicatorInsets = _textView.contentInset; 122 | 123 | CGSize glyphSize = _terminalController.fontMetrics.boundingBox; 124 | 125 | // Determine the screen size based on the font size 126 | CGFloat width = _textView.frame.size.width; 127 | CGFloat height = _textView.frame.size.height - barInsets.top - barInsets.bottom; 128 | 129 | ScreenSize size; 130 | size.width = floorf(width / glyphSize.width); 131 | size.height = floorf(height / glyphSize.height); 132 | 133 | // The font size should not be too small that it overflows the glyph buffers. It is not worth the 134 | // effort to fail gracefully (increasing the buffer size would be better). 135 | NSParameterAssert(size.width < kMaxRowBufferSize); 136 | 137 | if (size.width != _terminalController.screenSize.width || size.height != _terminalController.screenSize.height) { 138 | _terminalController.screenSize = size; 139 | } 140 | } 141 | 142 | - (void)refreshWithAttributedString:(NSAttributedString *)attributedString backgroundColor:(UIColor *)backgroundColor { 143 | _textView.attributedText = attributedString; 144 | 145 | if (backgroundColor != _textView.backgroundColor) { 146 | _textView.backgroundColor = backgroundColor; 147 | } 148 | 149 | // TODO: not sure why this is needed all of a sudden? what did i break? 150 | dispatch_async(dispatch_get_main_queue(), ^{ 151 | [self scrollToBottom]; 152 | }); 153 | } 154 | 155 | - (void)activateBell { 156 | // display the bell HUD, lazily initialising it if it hasn’t been yet 157 | if (!_bellHUDView) { 158 | _bellHUDView = [[HBNTHUDView alloc] initWithImage:[UIImage imageNamed:@"bell-hud"]]; 159 | _bellHUDView.translatesAutoresizingMaskIntoConstraints = NO; 160 | [self.view addSubview:_bellHUDView]; 161 | [self.view addCompactConstraints:@[ 162 | @"hudView.centerX = self.centerX", 163 | @"hudView.top = self.top + 100" 164 | ] metrics:nil views:@{ 165 | @"self": self.view, 166 | @"hudView": _bellHUDView 167 | }]; 168 | } 169 | 170 | [_bellHUDView animate]; 171 | } 172 | 173 | - (void)close { 174 | // TODO: i guess this is kind of the wrong spot 175 | if (self.parentViewController && self.parentViewController.class == HBNTRootViewController.class) { 176 | [(HBNTRootViewController *)self.parentViewController removeTerminal:self]; 177 | } 178 | } 179 | 180 | - (void)scrollToBottom { 181 | // if the user has scrolled up far enough on their own, don’t rudely scroll them back to the 182 | // bottom. when they scroll back, the automatic scrolling will continue 183 | // TODO: buggy 184 | // if (_textView.contentOffset.y < _lastAutomaticScrollOffset.y - 20) { 185 | // return; 186 | // } 187 | 188 | // if there is no scrollback, use the top of the scroll view. if there is, calculate the bottom 189 | UIEdgeInsets insets = _textView.scrollIndicatorInsets; 190 | CGPoint offset = _textView.contentOffset; 191 | CGFloat bottom = _keyboardHeight ?: insets.bottom; 192 | offset.y = _terminalController.scrollbackLines == 0 ? -insets.top : bottom + _textView.contentSize.height - _textView.frame.size.height; 193 | 194 | // if the offset has changed, update it and our lastAutomaticScrollOffset 195 | if (_textView.contentOffset.y != offset.y) { 196 | _textView.contentOffset = offset; 197 | _lastAutomaticScrollOffset = offset; 198 | } 199 | } 200 | 201 | #pragma mark - Keyboard management 202 | 203 | - (void)registerForKeyboardNotifications { 204 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardVisibilityChanged:) name:UIKeyboardWillShowNotification object:nil]; 205 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardVisibilityChanged:) name:UIKeyboardWillHideNotification object:nil]; 206 | } 207 | 208 | - (void)unregisterForKeyboardNotifications { 209 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillShowNotification object:nil]; 210 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIKeyboardWillHideNotification object:nil]; 211 | } 212 | 213 | - (void)keyboardVisibilityChanged:(NSNotification *)notification { 214 | // we do this to avoid the scroll indicator from appearing as soon as the terminal appears. we 215 | // only want to see it after the keyboard has appeared 216 | if (!_hasAppeared) { 217 | _hasAppeared = YES; 218 | // _textView.showsVerticalScrollIndicator = YES; 219 | } 220 | 221 | // YES when showing, NO when hiding 222 | BOOL direction = [notification.name isEqualToString:UIKeyboardWillShowNotification]; 223 | 224 | CGFloat animationDuration = ((NSNumber *)notification.userInfo[UIKeyboardAnimationDurationUserInfoKey]).doubleValue; 225 | 226 | // determine the final keyboard height. we still get a height if hiding, so force it to 0 if this 227 | // isn’t a show notification 228 | CGRect keyboardFrame = ((NSValue *)notification.userInfo[UIKeyboardFrameEndUserInfoKey]).CGRectValue; 229 | _keyboardHeight = direction ? keyboardFrame.size.height : 0; 230 | 231 | // we call updateScreenSize in an animation block to force it to be animated with the exact 232 | // parameters given to us in the notification 233 | [UIView animateWithDuration:animationDuration animations:^{ 234 | [self updateScreenSize]; 235 | }]; 236 | } 237 | 238 | - (BOOL)becomeFirstResponder { 239 | return NO; 240 | // return [_keyInput becomeFirstResponder]; 241 | } 242 | 243 | - (BOOL)resignFirstResponder { 244 | return NO; 245 | // return [_keyInput resignFirstResponder]; 246 | } 247 | 248 | - (BOOL)isFirstResponder { 249 | return _keyInput.isFirstResponder; 250 | } 251 | 252 | - (void)toggleKeyboard:(id)sender { 253 | if (self.isFirstResponder) { 254 | [self resignFirstResponder]; 255 | } else { 256 | [self becomeFirstResponder]; 257 | } 258 | } 259 | 260 | - (void)handleTextViewTap:(UITapGestureRecognizer *)gestureRecognizer { 261 | if (gestureRecognizer.state == UIGestureRecognizerStateEnded && !self.isFirstResponder) { 262 | [self becomeFirstResponder]; 263 | } 264 | } 265 | 266 | @end 267 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalTextView.h: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalTextView.h 3 | // NewTerm 4 | // 5 | // Created by Adam D on 26/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface HBNTTerminalTextView : UITextView 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTerminalTextView.m: -------------------------------------------------------------------------------- 1 | // 2 | // HBNTTerminalTextView.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 26/01/2015. 6 | // Copyright (c) 2015 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTTerminalTextView.h" 10 | 11 | @implementation HBNTTerminalTextView 12 | 13 | - (instancetype)initWithFrame:(CGRect)frame textContainer:(NSTextContainer *)textContainer { 14 | self = [super initWithFrame:frame textContainer:textContainer]; 15 | 16 | if (self) { 17 | [self _commonInit]; 18 | } 19 | 20 | return self; 21 | } 22 | 23 | - (instancetype)initWithFrame:(CGRect)frame { 24 | self = [super initWithFrame:frame]; 25 | 26 | if (self) { 27 | [self _commonInit]; 28 | } 29 | 30 | return self; 31 | } 32 | 33 | - (void)_commonInit { 34 | self.backgroundColor = [UIColor blackColor]; 35 | self.indicatorStyle = UIScrollViewIndicatorStyleWhite; 36 | self.showsHorizontalScrollIndicator = NO; 37 | self.showsVerticalScrollIndicator = NO; 38 | // self.dataDetectorTypes = UIDataDetectorTypeLink; 39 | self.editable = NO; 40 | 41 | if ([self respondsToSelector:@selector(setLinkTextAttributes:)]) { 42 | self.linkTextAttributes = @{ 43 | NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle) 44 | }; 45 | } 46 | 47 | if ([self respondsToSelector:@selector(textContainer)]) { 48 | self.textContainerInset = UIEdgeInsetsZero; 49 | self.textContainer.lineFragmentPadding = 0; 50 | } 51 | } 52 | 53 | #pragma mark - UIResponder 54 | 55 | - (BOOL)becomeFirstResponder { 56 | // we aren’t meant to ever become first responder. that’s the job of HBNTTerminalKeyInput 57 | return NO; 58 | } 59 | 60 | #pragma mark - UITextInput 61 | 62 | - (CGRect)caretRectForPosition:(UITextPosition *)position { 63 | // TODO: should we take advantage of this? 64 | return CGRectZero; 65 | } 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTextInputBase.h: -------------------------------------------------------------------------------- 1 | // UITextInputBase.h 2 | // MobileTerminal 3 | // 4 | // This file contains a base view for implementing keyboard handling routines 5 | // via the UITextInput protocol. That protocol is a grab bag of other protocols 6 | // that are mostly unused by the terminal keyboard handling code. 7 | 8 | #import 9 | 10 | #ifndef __IPHONE_11_0 11 | #define UITextSmartQuotesType NSInteger 12 | #define UITextSmartDashesType NSInteger 13 | #define UITextSmartInsertDeleteType NSInteger 14 | #define UITextSmartQuotesTypeNo 1 15 | #define UITextSmartDashesTypeNo 1 16 | #define UITextSmartInsertDeleteTypeNo 1 17 | #endif 18 | 19 | @interface HBNTTextPosition : UITextPosition 20 | 21 | @property (nonatomic, strong) NSNumber *position; 22 | 23 | @end 24 | 25 | @interface HBNTTextInputBase : UIView 26 | 27 | // UIKeyInput 28 | - (BOOL)hasText; 29 | - (void)insertText:(NSString *)text; 30 | - (void)deleteBackward; 31 | 32 | 33 | /* Methods for manipulating text. */ 34 | - (NSString *)textInRange:(UITextRange *)range; 35 | - (void)replaceRange:(UITextRange *)range withText:(NSString *)text; 36 | 37 | /* Text may have a selection, either zero-length (a caret) or ranged. Editing operations are 38 | * always performed on the text from this selection. nil corresponds to no selection. */ 39 | 40 | @property (readwrite, copy) UITextRange *selectedTextRange; 41 | 42 | /* If text can be selected, it can be marked. Marked text represents provisionally 43 | * inserted text that has yet to be confirmed by the user. It requires unique visual 44 | * treatment in its display. If there is any marked text, the selection, whether a 45 | * caret or an extended range, always resides witihin. 46 | * 47 | * Setting marked text either replaces the existing marked text or, if none is present, 48 | * inserts it from the current selection. */ 49 | 50 | @property (nonatomic, readonly) UITextRange *markedTextRange; // Nil if no marked text. 51 | @property (nonatomic, copy) NSDictionary *markedTextStyle; // Describes how the marked text should be drawn. 52 | - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange; // selectedRange is a range within the markedText 53 | - (void)unmarkText; 54 | 55 | /* The end and beginning of the the text document. */ 56 | @property (nonatomic, readonly) UITextPosition *beginningOfDocument; 57 | @property (nonatomic, readonly) UITextPosition *endOfDocument; 58 | 59 | /* Methods for creating ranges and positions. */ 60 | - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition; 61 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset; 62 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset; 63 | 64 | /* Simple evaluation of positions */ 65 | - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other; 66 | - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition; 67 | 68 | /* A system-provied input delegate is assigned when the system is interested in input changes. */ 69 | @property (nonatomic, assign) id inputDelegate; 70 | 71 | /* A tokenizer must be provided to inform the text input system about text units of varying granularity. */ 72 | @property (nonatomic, readonly) id tokenizer; 73 | 74 | /* Layout questions. */ 75 | - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction; 76 | - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction; 77 | 78 | /* Writing direction */ 79 | - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction; 80 | - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range; 81 | 82 | /* Geometry used to provide, for example, a correction rect. */ 83 | - (CGRect)firstRectForRange:(UITextRange *)range; 84 | - (CGRect)caretRectForPosition:(UITextPosition *)position; 85 | - (NSArray *)selectionRectsForRange:(UITextRange *)range; 86 | 87 | /* Hit testing. */ 88 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point; 89 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range; 90 | - (UITextRange *)characterRangeAtPoint:(CGPoint)point; 91 | 92 | /* Text styling information can affect, for example, the appearance of a correction rect. */ 93 | - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction; 94 | 95 | /* To be implemented if there is not a one-to-one correspondence between text positions within range and character offsets into the associated string. */ 96 | - (UITextPosition *)positionWithinRange:(UITextRange *)range atCharacterOffset:(NSInteger)offset; 97 | - (NSInteger)characterOffsetOfPosition:(UITextPosition *)position withinRange:(UITextRange *)range; 98 | 99 | /* An affiliated view that provides a coordinate system for all geometric values in this protocol. 100 | * If unimplmeented, the first view in the responder chain will be selected. */ 101 | @property (nonatomic, readonly) UIView *textInputView; 102 | 103 | /* Selection affinity determines whether, for example, the insertion point appears after the last 104 | * character on a line or before the first character on the following line in cases where text 105 | * wraps across line boundaries. */ 106 | @property (nonatomic) UITextStorageDirection selectionAffinity; 107 | 108 | // traits: 109 | 110 | @property (nonatomic) UITextAutocapitalizationType autocapitalizationType; // default is UITextAutocapitalizationTypeSentences 111 | @property (nonatomic) UITextAutocorrectionType autocorrectionType; // default is UITextAutocorrectionTypeDefault 112 | @property (nonatomic) UITextSpellCheckingType spellCheckingType; // default is UITextSpellCheckingTypeDefault 113 | @property (nonatomic) UITextSmartQuotesType smartQuotesType /*NS_AVAILABLE_IOS(11_0)*/; // default is UITextSmartQuotesTypeDefault 114 | @property (nonatomic) UITextSmartDashesType smartDashesType /*NS_AVAILABLE_IOS(11_0)*/; // default is UITextSmartDashesTypeDefault 115 | @property (nonatomic) UITextSmartInsertDeleteType smartInsertDeleteType /*NS_AVAILABLE_IOS(11_0)*/; // default is UITextSmartInsertDeleteTypeDefault 116 | @property (nonatomic) UIKeyboardType keyboardType; // default is UIKeyboardTypeDefault 117 | @property (nonatomic) UIKeyboardAppearance keyboardAppearance; // default is UIKeyboardAppearanceDefault 118 | @property (nonatomic) UIReturnKeyType returnKeyType; // default is UIReturnKeyDefault 119 | 120 | @end 121 | -------------------------------------------------------------------------------- /NewTermMarzipan/HBNTTextInputBase.m: -------------------------------------------------------------------------------- 1 | // UITextInputBase.m 2 | // MobileTerminal 3 | 4 | #import "HBNTTextInputBase.h" 5 | 6 | @implementation HBNTTextPosition 7 | 8 | @end 9 | 10 | @implementation HBNTTextInputBase 11 | 12 | - (BOOL)hasText { 13 | return NO; 14 | } 15 | 16 | - (void)insertText:(NSString *)text {} 17 | 18 | - (void)deleteBackward {} 19 | 20 | - (NSString *)textInRange:(UITextRange *)range { 21 | return nil; 22 | } 23 | 24 | - (void)replaceRange:(UITextRange *)range withText:(NSString *)text {} 25 | 26 | - (void)setMarkedText:(NSString *)markedText selectedRange:(NSRange)selectedRange {} 27 | 28 | - (void)unmarkText {} 29 | 30 | /* Methods for creating ranges and positions. */ 31 | - (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition toPosition:(UITextPosition *)toPosition { 32 | return nil; 33 | } 34 | 35 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position offset:(NSInteger)offset { 36 | return nil; 37 | } 38 | 39 | - (UITextPosition *)positionFromPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction offset:(NSInteger)offset { 40 | return nil; 41 | } 42 | 43 | - (NSComparisonResult)comparePosition:(UITextPosition *)position toPosition:(UITextPosition *)other { 44 | return [((HBNTTextPosition *)position).position compare:((HBNTTextPosition *)other).position]; 45 | } 46 | 47 | - (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition { 48 | return 0; 49 | } 50 | 51 | - (UITextPosition *)positionWithinRange:(UITextRange *)range farthestInDirection:(UITextLayoutDirection)direction { 52 | return nil; 53 | } 54 | 55 | - (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position inDirection:(UITextLayoutDirection)direction { 56 | return nil; 57 | } 58 | 59 | - (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction { 60 | return UITextWritingDirectionNatural; 61 | } 62 | 63 | - (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection forRange:(UITextRange *)range {} 64 | 65 | - (CGRect)firstRectForRange:(UITextRange *)range { 66 | return CGRectZero; 67 | } 68 | 69 | - (CGRect)caretRectForPosition:(UITextPosition *)position { 70 | return CGRectZero; 71 | } 72 | 73 | - (NSArray *)selectionRectsForRange:(UITextRange *)range { 74 | return nil; 75 | } 76 | 77 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point { 78 | HBNTTextPosition *position = [[HBNTTextPosition alloc] init]; 79 | position.position = @(point.x); 80 | return position; 81 | } 82 | 83 | - (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range { 84 | HBNTTextPosition *position = [[HBNTTextPosition alloc] init]; 85 | position.position = @(point.x); 86 | return position; 87 | } 88 | 89 | - (UITextRange *)characterRangeAtPoint:(CGPoint)point { 90 | return nil; 91 | } 92 | 93 | - (NSDictionary *)textStylingAtPosition:(UITextPosition *)position inDirection:(UITextStorageDirection)direction { 94 | return nil; 95 | } 96 | 97 | /* To be implemented if there is not a one-to-one correspondence between text positions within range and character offsets into the associated string. */ 98 | - (UITextPosition *)positionWithinRange:(UITextRange *)range atCharacterOffset:(NSInteger)offset { 99 | return nil; 100 | } 101 | 102 | - (NSInteger)characterOffsetOfPosition:(UITextPosition *)position withinRange:(UITextRange *)range { 103 | return 0; 104 | } 105 | 106 | @end 107 | -------------------------------------------------------------------------------- /NewTermMarzipan/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 HASHBANG Productions. All rights reserved. 27 | 28 | 29 | -------------------------------------------------------------------------------- /NewTermMarzipan/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /NewTermMarzipan/Main.storyboardc/Info.plist: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/Main.storyboardc/Info.plist -------------------------------------------------------------------------------- /NewTermMarzipan/Main.storyboardc/U5R-jr-i31-view-bFg-2Y-cSI.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/Main.storyboardc/U5R-jr-i31-view-bFg-2Y-cSI.nib -------------------------------------------------------------------------------- /NewTermMarzipan/Main.storyboardc/UINavigationController-a88-9D-dcr.nib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/Main.storyboardc/UINavigationController-a88-9D-dcr.nib -------------------------------------------------------------------------------- /NewTermMarzipan/NSCharacterSet+iTerm.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSCharacterSet+iTerm.h 3 | // iTerm2 4 | // 5 | // Created by George Nachman on 3/29/15. 6 | // 7 | // 8 | 9 | @import Foundation; 10 | 11 | @interface NSCharacterSet (iTerm) 12 | 13 | + (instancetype)fullWidthCharacterSetForUnicodeVersion:(NSInteger)version; 14 | + (instancetype)ambiguousWidthCharacterSetForUnicodeVersion:(NSInteger)version; 15 | + (instancetype)zeroWidthSpaceCharacterSet; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /NewTermMarzipan/NSStringITerm.h: -------------------------------------------------------------------------------- 1 | // $Id: NSStringITerm.h,v 1.2 2006-11-13 06:57:47 yfabian Exp $ 2 | // 3 | // NSStringJTerminal.h 4 | // 5 | // Additional function to NSString Class by Category 6 | // 2001.11.13 by Y.Hanahara 7 | // 2002.05.18 by Kiichi Kusama 8 | /* 9 | ** NSStringIterm.h 10 | ** 11 | ** Copyright (c) 2002, 2003 12 | ** 13 | ** Author: Fabian 14 | ** Initial code by Kiichi Kusama 15 | ** 16 | ** Project: iTerm 17 | ** 18 | ** Description: Implements NSString extensions. 19 | ** 20 | ** This program is free software; you can redistribute it and/or modify 21 | ** it under the terms of the GNU General Public License as published by 22 | ** the Free Software Foundation; either version 2 of the License, or 23 | ** (at your option) any later version. 24 | ** 25 | ** This program is distributed in the hope that it will be useful, 26 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 27 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 28 | ** GNU General Public License for more details. 29 | ** 30 | ** You should have received a copy of the GNU General Public License 31 | ** along with this program; if not, write to the Free Software 32 | ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 33 | */ 34 | 35 | @import Foundation; 36 | 37 | // This is the standard unicode replacement character for when input couldn't 38 | // be parsed properly but we need to render something there. 39 | #define UNICODE_REPLACEMENT_CHAR 0xfffd 40 | 41 | @interface NSString (iTerm) 42 | 43 | + (BOOL)isDoubleWidthCharacter:(int)unicode 44 | ambiguousIsDoubleWidth:(BOOL)ambiguousIsDoubleWidth 45 | unicodeVersion:(NSInteger)version; 46 | 47 | // Call |block| for each composed character in the string. If it is a single base character or a 48 | // high surrogate, then |simple| will be valid and |complex| will be nil. Otherwise, |complex| will 49 | // be non-nil. 50 | - (void)enumerateComposedCharacters:(void (^)(NSRange range, 51 | unichar simple, 52 | NSString *complexString, 53 | BOOL *stop))block; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /NewTermMarzipan/NSStringITerm.m: -------------------------------------------------------------------------------- 1 | /* 2 | ** NSStringIterm.m 3 | ** 4 | ** Copyright (c) 2002, 2003 5 | ** 6 | ** Author: Fabian 7 | ** Initial code by Kiichi Kusama 8 | ** 9 | ** Project: iTerm 10 | ** 11 | ** Description: Implements NSString extensions. 12 | ** 13 | ** This program is free software; you can redistribute it and/or modify 14 | ** it under the terms of the GNU General Public License as published by 15 | ** the Free Software Foundation; either version 2 of the License, or 16 | ** (at your option) any later version. 17 | ** 18 | ** This program is distributed in the hope that it will be useful, 19 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | ** GNU General Public License for more details. 22 | ** 23 | ** You should have received a copy of the GNU General Public License 24 | ** along with this program; if not, write to the Free Software 25 | ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 26 | */ 27 | 28 | #import "NSStringITerm.h" 29 | #import "NSCharacterSet+iTerm.h" 30 | 31 | @implementation NSString (iTerm) 32 | 33 | + (BOOL)isDoubleWidthCharacter:(int)unicode 34 | ambiguousIsDoubleWidth:(BOOL)ambiguousIsDoubleWidth 35 | unicodeVersion:(NSInteger)version { 36 | if (unicode <= 0xa0 || 37 | (unicode > 0x452 && unicode < 0x1100)) { 38 | // Quickly cover the common cases. 39 | return NO; 40 | } 41 | 42 | if ([[NSCharacterSet fullWidthCharacterSetForUnicodeVersion:version] longCharacterIsMember:unicode]) { 43 | return YES; 44 | } 45 | if (ambiguousIsDoubleWidth && 46 | [[NSCharacterSet ambiguousWidthCharacterSetForUnicodeVersion:version] longCharacterIsMember:unicode]) { 47 | return YES; 48 | } 49 | return NO; 50 | } 51 | 52 | - (void)enumerateComposedCharacters:(void (^)(NSRange, unichar, NSString *, BOOL *))block { 53 | if (self.length == 0) { 54 | return; 55 | } 56 | static dispatch_once_t onceToken; 57 | static NSCharacterSet *exceptions; 58 | dispatch_once(&onceToken, ^{ 59 | // These characters are forced to be base characters. 60 | exceptions = [NSCharacterSet characterSetWithCharactersInString:@"\uff9e\uff9f"]; 61 | }); 62 | CFIndex index = 0; 63 | NSInteger minimumLocation = 0; 64 | NSRange range; 65 | do { 66 | CFRange tempRange = CFStringGetRangeOfComposedCharactersAtIndex((CFStringRef)self, index); 67 | if (tempRange.location < minimumLocation) { 68 | NSInteger diff = minimumLocation - tempRange.location; 69 | tempRange.location += diff; 70 | if (diff > tempRange.length) { 71 | tempRange.length = 0; 72 | } else { 73 | tempRange.length -= diff; 74 | } 75 | } 76 | range = NSMakeRange(tempRange.location, tempRange.length); 77 | if (range.length > 0) { 78 | // CFStringGetRangeOfComposedCharactersAtIndex thinks that U+FF9E and U+FF9F are 79 | // combining marks. Terminal.app and the person in issue 6048 disagree. Prevent them 80 | // from combining. 81 | NSRange rangeOfFirstException = [self rangeOfCharacterFromSet:exceptions 82 | options:NSLiteralSearch 83 | range:range]; 84 | if (rangeOfFirstException.location != NSNotFound && 85 | rangeOfFirstException.location > range.location) { 86 | range.length = rangeOfFirstException.location - range.location; 87 | minimumLocation = NSMaxRange(range); 88 | } 89 | 90 | unichar simple = range.length == 1 ? [self characterAtIndex:range.location] : 0; 91 | NSString *complexString = range.length == 1 ? nil : [self substringWithRange:range]; 92 | BOOL stop = NO; 93 | block(range, simple, complexString, &stop); 94 | if (stop) { 95 | return; 96 | } 97 | } 98 | index = NSMaxRange(range); 99 | } while (NSMaxRange(range) < self.length); 100 | } 101 | 102 | @end 103 | -------------------------------------------------------------------------------- /NewTermMarzipan/NewTermMarzipan.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.private.iosmac 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /NewTermMarzipan/Themes.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Basic 6 | 7 | Background 8 | #ffffff 9 | BoldText 10 | #000000 11 | Cursor 12 | #7f7f7f 13 | IsDark 14 | 15 | Text 16 | #000000 17 | 18 | Grass 19 | 20 | Background 21 | #13773d 22 | BoldText 23 | #ffb03b 24 | Cursor 25 | #8e2800 26 | IsDark 27 | 28 | Text 29 | #fff0a5 30 | 31 | Homebrew 32 | 33 | Background 34 | #000000 35 | BoldText 36 | #00ff00 37 | Cursor 38 | #38fe27 39 | IsDark 40 | 41 | Text 42 | #13773d 43 | 44 | kirb 45 | 46 | Background 47 | #0f0f0f 48 | BoldText 49 | #ffffff 50 | Cursor 51 | #00d900 52 | IsDark 53 | 54 | Text 55 | #f2f2f2 56 | 57 | Man Page 58 | 59 | Background 60 | #fef49c 61 | BoldText 62 | #000000 63 | Cursor 64 | #7f7f7f 65 | IsDark 66 | 67 | Text 68 | #000000 69 | 70 | Novel 71 | 72 | Background 73 | #dfdbc3 74 | BoldText 75 | #7f2a19 76 | Cursor 77 | #3a2322 78 | IsDark 79 | 80 | Text 81 | #3b2322 82 | 83 | Ocean 84 | 85 | Background 86 | #224fbc 87 | BoldText 88 | #ffffff 89 | Cursor 90 | #7f7f7f 91 | IsDark 92 | 93 | Text 94 | #ffffff 95 | 96 | Pro 97 | 98 | Background 99 | #000000 100 | BoldText 101 | #ffffff 102 | Cursor 103 | #4d4d4d 104 | IsDark 105 | 106 | Text 107 | #f2f2f2 108 | 109 | Red Sands 110 | 111 | Background 112 | #7a251e 113 | BoldText 114 | #dfbd22 115 | Cursor 116 | #ffffff 117 | IsDark 118 | 119 | Text 120 | #d7c9a7 121 | 122 | Silver Aerogel 123 | 124 | Background 125 | #808080 126 | BoldText 127 | #ffffff 128 | Cursor 129 | #d9d9d9 130 | IsDark 131 | 132 | Text 133 | #000000 134 | 135 | VSCode 136 | 137 | Background 138 | #1e1e1e 139 | BoldText 140 | #cccccc 141 | ColorTable 142 | 143 | #000000 144 | #cd3131 145 | #0dbc79 146 | #e5e510 147 | #2472c8 148 | #bc3fbc 149 | #11a8cd 150 | #e5e5e5 151 | #666666 152 | #f14c4c 153 | #23d18b 154 | #f5f543 155 | #3b8eea 156 | #d670d6 157 | #29b8db 158 | #e5e5e5 159 | 160 | Cursor 161 | #0dbc79 162 | IsDark 163 | 164 | Text 165 | #ffffff 166 | 167 | 168 | 169 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100.h: -------------------------------------------------------------------------------- 1 | // VT100.h 2 | // MobileTerminal 3 | // 4 | // This file contains the bridge between the higher level text drawing 5 | // controls and the lower level terminal state pieces (screen and terminal). 6 | // The VT100 interface should be the only access from those components to the 7 | // VT100 subsystem. This layer mostly exists to keep the complexity/mess of 8 | // the VT100Terminal and VT100 screen away from everything else. 9 | 10 | #import "VT100Types.h" 11 | 12 | // Forward declarations 13 | @class VT100Terminal; 14 | @class VT100Screen; 15 | 16 | // VT100 is the public interface that combines the terminal subcomponents. The 17 | // caller is expected to provide the raw terminal data into the VT100 object 18 | // via calls to handleInputStream. VT100 exposes the contents of the screen by 19 | // implementing the ScreenBuffer protocol. 20 | @interface VT100 : NSObject 21 | 22 | @property (nonatomic, retain) id refreshDelegate; 23 | 24 | // Initialize a VT100 25 | - (instancetype)init; 26 | 27 | // Reads raw character data into the terminal character processor. This will 28 | // almost certainly cause updates to the screen buffer. 29 | - (void)readInputStream:(NSData *)data; 30 | 31 | // ScreenBuffer methods for obtaining information about the characters 32 | // currently on the screen. 33 | @property (nonatomic) ScreenSize screenSize; 34 | 35 | // The row specified here also includes the scrollback buffer. 36 | - (screen_char_t*)bufferForRow:(int)row; 37 | - (int)numberOfRows; 38 | - (unsigned)scrollbackLines; 39 | 40 | - (ScreenPosition)cursorPosition; 41 | 42 | - (void)clearScreen; 43 | 44 | @end 45 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100.m: -------------------------------------------------------------------------------- 1 | // VT100.m 2 | // MobileTerminal 3 | 4 | #import "VT100.h" 5 | 6 | #import "VT100Terminal.h" 7 | #import "VT100Screen.h" 8 | 9 | // The default width and height are basically thrown away as soon as the text view is initialized 10 | // and determines the right height and width for the current font. 11 | static const int kDefaultWidth = 80; 12 | static const int kDefaultHeight = 25; 13 | 14 | @implementation VT100 { 15 | VT100Terminal *_terminal; 16 | } 17 | 18 | - (instancetype)init { 19 | self = [super init]; 20 | 21 | if (self) { 22 | _terminal = [[VT100Terminal alloc] init]; 23 | _terminal.encoding = NSUTF8StringEncoding; 24 | 25 | _terminal.primaryScreen.refreshDelegate = self; 26 | _terminal.alternateScreen.refreshDelegate = self; 27 | [_terminal.primaryScreen resizeWidth:kDefaultWidth height:kDefaultHeight]; 28 | [_terminal.alternateScreen resizeWidth:kDefaultWidth height:kDefaultHeight]; 29 | } 30 | 31 | return self; 32 | } 33 | 34 | // This object itself is the refresh delegate for the screen. When we're 35 | // invoked, invoke our refresh delegate and then reset the dirty bits on the 36 | // screen since we should have now refreshed the screen. 37 | - (void)refresh { 38 | [_refreshDelegate refresh]; 39 | [_terminal.currentScreen resetDirty]; 40 | } 41 | 42 | - (void)activateBell { 43 | // tell the refresh delegate to activate the bell 44 | [_refreshDelegate activateBell]; 45 | } 46 | 47 | - (void)readInputStream:(NSData *)data { 48 | // Push the input stream into the terminal, then parse the stream back out as 49 | // a series of tokens and feed them back to the screen 50 | [_terminal putStreamData:data]; 51 | VT100Token *token; 52 | while((token = [_terminal getNextToken]), 53 | token.type != VT100_WAIT && token.type != VT100CC_NULL) { 54 | // process token 55 | if (token.type != VT100_SKIP) { 56 | if (token.type == VT100_NOTSUPPORT) { 57 | NSLog(@"not support token"); 58 | } else { 59 | [_terminal.currentScreen putToken:token]; 60 | } 61 | } else { 62 | NSLog(@"skip token"); 63 | } 64 | } 65 | // Cause the text display to determine if it should re-draw anything 66 | [_refreshDelegate refresh]; 67 | } 68 | 69 | - (ScreenSize)screenSize { 70 | ScreenSize size; 71 | size.width = _terminal.currentScreen.width; 72 | size.height = _terminal.currentScreen.height; 73 | return size; 74 | } 75 | 76 | - (void)setScreenSize:(ScreenSize)size { 77 | [_terminal.primaryScreen resizeWidth:size.width height:size.height]; 78 | [_terminal.alternateScreen resizeWidth:size.width height:size.height]; 79 | } 80 | 81 | - (ScreenPosition)cursorPosition { 82 | ScreenPosition position; 83 | position.x = _terminal.currentScreen.cursorX; 84 | position.y = _terminal.currentScreen.cursorY; 85 | return position; 86 | } 87 | 88 | - (screen_char_t*)bufferForRow:(int)row { 89 | return [_terminal.currentScreen getLineAtIndex:row]; 90 | } 91 | 92 | - (void)clearScreen { 93 | // Clears both the screen and scrollback buffer 94 | [_terminal.currentScreen clearBuffer]; 95 | } 96 | 97 | - (int)numberOfRows { 98 | return [_terminal.currentScreen numberOfLines]; 99 | } 100 | 101 | - (unsigned)scrollbackLines { 102 | return [_terminal.currentScreen numberOfScrollbackLines]; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100ColorMap.h: -------------------------------------------------------------------------------- 1 | // ColorMap.h 2 | // MobileTerminal 3 | 4 | #import 5 | 6 | // 16 terminal color slots available 7 | #define COLOR_MAP_MAX_COLORS 16 8 | 9 | @interface VT100ColorMap : NSObject 10 | 11 | @property (nonatomic, retain, readonly) UIColor *background; 12 | @property (nonatomic, retain, readonly) UIColor *foreground; 13 | @property (nonatomic, retain, readonly) UIColor *foregroundBold; 14 | @property (nonatomic, retain, readonly) UIColor *foregroundCursor; 15 | @property (nonatomic, retain, readonly) UIColor *backgroundCursor; 16 | 17 | @property (nonatomic, readonly) BOOL isDark; 18 | 19 | - (instancetype)init; 20 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary; 21 | 22 | // Terminal color index 23 | - (UIColor *)colorAtIndex:(unsigned)index; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100ColorMap.m: -------------------------------------------------------------------------------- 1 | // ColorMap.m 2 | // MobileTerminal 3 | 4 | #import "VT100ColorMap.h" 5 | #import "VT100Terminal.h" 6 | #import "UIColor+HBAdditions.h" 7 | 8 | @interface VT100ColorMap () { 9 | UIColor *_table[COLOR_MAP_MAX_COLORS]; 10 | } 11 | 12 | @end 13 | 14 | @implementation VT100ColorMap 15 | 16 | - (instancetype)init { 17 | self = [super init]; 18 | 19 | if (self) { 20 | _background = [UIColor colorWithWhite:0.f alpha:1.f]; 21 | _foreground = [UIColor colorWithWhite:0.95f alpha:1.f]; 22 | _foregroundBold = [UIColor colorWithWhite:1.f alpha:1.f]; 23 | _foregroundCursor = [UIColor colorWithWhite:0.95f alpha:1.f]; 24 | _backgroundCursor = [UIColor colorWithWhite:0.4f alpha:1.f]; 25 | _isDark = YES; 26 | 27 | // System 7.5 colors, why not? 28 | _table[kiTermScreenCharAnsiColorBlack] = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.f]; 29 | _table[kiTermScreenCharAnsiColorRed] = [UIColor colorWithRed:0.6f green:0.0f blue:0.0f alpha:1.f]; 30 | _table[kiTermScreenCharAnsiColorGreen] = [UIColor colorWithRed:0.0f green:0.6f blue:0.0f alpha:1.f]; 31 | _table[kiTermScreenCharAnsiColorYellow] = [UIColor colorWithRed:0.6f green:0.4f blue:0.0f alpha:1.f]; 32 | _table[kiTermScreenCharAnsiColorBlue] = [UIColor colorWithRed:0.0f green:0.0f blue:0.6f alpha:1.f]; 33 | _table[kiTermScreenCharAnsiColorMagenta] = [UIColor colorWithRed:0.6f green:0.0f blue:0.6f alpha:1.f]; 34 | _table[kiTermScreenCharAnsiColorCyan] = [UIColor colorWithRed:0.0f green:0.6f blue:0.6f alpha:1.f]; 35 | _table[kiTermScreenCharAnsiColorWhite] = [UIColor colorWithRed:0.6f green:0.6f blue:0.6f alpha:1.f]; 36 | _table[kiTermScreenCharAnsiColorBrightBlack] = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.f]; 37 | _table[kiTermScreenCharAnsiColorBrightRed] = [UIColor colorWithRed:1.0f green:0.0f blue:0.0f alpha:1.f]; 38 | _table[kiTermScreenCharAnsiColorBrightGreen] = [UIColor colorWithRed:0.0f green:1.0f blue:0.0f alpha:1.f]; 39 | _table[kiTermScreenCharAnsiColorBrightYellow] = [UIColor colorWithRed:1.0f green:1.0f blue:0.0f alpha:1.f]; 40 | _table[kiTermScreenCharAnsiColorBrightBlue] = [UIColor colorWithRed:0.0f green:0.0f blue:1.0f alpha:1.f]; 41 | _table[kiTermScreenCharAnsiColorBrightMagenta] = [UIColor colorWithRed:1.0f green:0.0f blue:1.0f alpha:1.f]; 42 | _table[kiTermScreenCharAnsiColorBrightCyan] = [UIColor colorWithRed:0.0f green:1.0f blue:1.0f alpha:1.f]; 43 | _table[kiTermScreenCharAnsiColorBrightWhite] = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.f]; 44 | } 45 | 46 | return self; 47 | } 48 | 49 | - (instancetype)initWithDictionary:(NSDictionary *)dictionary { 50 | self = [self init]; 51 | 52 | if (self) { 53 | if (dictionary[@"Background"]) { 54 | _background = [UIColor hb_colorWithPropertyListValue:dictionary[@"Background"]]; 55 | } 56 | 57 | if (dictionary[@"Text"]) { 58 | _foreground = [UIColor hb_colorWithPropertyListValue:dictionary[@"Text"]]; 59 | } 60 | 61 | if (dictionary[@"BoldText"]) { 62 | _foregroundBold = [UIColor hb_colorWithPropertyListValue:dictionary[@"BoldText"]]; 63 | } 64 | 65 | if (dictionary[@"Cursor"]) { 66 | _foregroundCursor = [UIColor hb_colorWithPropertyListValue:dictionary[@"Cursor"]]; 67 | _backgroundCursor = [UIColor hb_colorWithPropertyListValue:dictionary[@"Cursor"]]; 68 | } 69 | 70 | if (dictionary[@"IsDark"]) { 71 | _isDark = ((NSNumber *)dictionary[@"IsDark"]).boolValue; 72 | } 73 | 74 | if (dictionary[@"ColorTable"] && [dictionary[@"ColorTable"] isKindOfClass:NSArray.class] && ((NSDictionary *)dictionary[@"ColorTable"]).count == COLOR_MAP_MAX_COLORS) { 75 | NSArray *colors = dictionary[@"ColorTable"]; 76 | for (int i = 0; i < colors.count; i++) { 77 | _table[i] = [UIColor hb_colorWithPropertyListValue:colors[i]]; 78 | } 79 | } 80 | } 81 | 82 | return self; 83 | } 84 | 85 | - (UIColor *)colorAtIndex:(unsigned int)index { 86 | // TODO(allen): The logic here is pretty ad hoc and could use some some helpful comments 87 | // describing whats its doing. It seems to work? 88 | if (index == -1) { 89 | return [UIColor clearColor]; 90 | } else if (index & COLOR_CODE_MASK) { 91 | switch (index) { 92 | case CURSOR_TEXT: 93 | return _foregroundCursor; 94 | case CURSOR_BG: 95 | return _backgroundCursor; 96 | case BG_COLOR_CODE: 97 | return [UIColor clearColor]; 98 | default: 99 | if (index & BOLD_MASK) { 100 | if (index - BOLD_MASK == BG_COLOR_CODE) { 101 | return [UIColor clearColor]; 102 | } else { 103 | return _foregroundBold; 104 | } 105 | } else { 106 | return _foreground; 107 | } 108 | } 109 | } else { 110 | index &= 0xff; 111 | if (index < 16) { 112 | // predefined color 113 | return _table[index]; 114 | } else if (index < 232) { 115 | // 256-color 116 | index -= 16; 117 | CGFloat components[] = { 118 | (index / 36) ? ((index / 36) * 40 + 55) / 255.0 : 0.0, 119 | (index % 36) / 6 ? (((index % 36) / 6) * 40 + 55) / 255.0 : 0.0, 120 | (index % 6) ? ((index % 6) * 40 + 55) / 255.0 : 0.0, 121 | 1.0 122 | }; 123 | return [UIColor colorWithRed:components[0] green:components[1] blue:components[2] alpha:1.0f]; 124 | } else if (index < 256) { 125 | // grayscale 126 | index -= 232; 127 | CGFloat gray = (index * 10 + 8) / 255.0; 128 | return [UIColor colorWithWhite:gray alpha:1.0f]; 129 | } else { 130 | return _foreground; 131 | } 132 | } 133 | } 134 | 135 | @end 136 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100Screen.h: -------------------------------------------------------------------------------- 1 | // -*- mode:objc -*- 2 | /* 3 | ** VT100Screen.h 4 | ** 5 | ** Copyright (c) 2002, 2003, 2007 6 | ** 7 | ** Author: Fabian, Ujwal S. Setlur 8 | ** Initial code by Kiichi Kusama 9 | ** Ported to MobileTerminal (from iTerm) by Allen Porter 10 | ** 11 | ** This program is free software; you can redistribute it and/or modify 12 | ** it under the terms of the GNU General Public License as published by 13 | ** the Free Software Foundation; either version 2 of the License, or 14 | ** (at your option) any later version. 15 | ** 16 | ** This program is distributed in the hope that it will be useful, 17 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | ** GNU General Public License for more details. 20 | ** 21 | ** You should have received a copy of the GNU General Public License 22 | ** along with this program; if not, write to the Free Software 23 | ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 24 | */ 25 | 26 | #import "VT100Terminal.h" 27 | #import "VT100Types.h" 28 | 29 | //_______________________________________________________________________________ 30 | //_______________________________________________________________________________ 31 | 32 | #define TABWINDOW 300 33 | 34 | @interface VT100Screen : NSObject 35 | 36 | @property (nonatomic, strong) VT100Terminal *terminal; 37 | @property (nonatomic, weak) id refreshDelegate; 38 | 39 | @property (nonatomic) int width, height; 40 | 41 | - (void)resizeWidth:(int)width height:(int)height; 42 | - (void)reset; 43 | - (void)setWidth:(int)width height:(int)height; 44 | 45 | @property (nonatomic) unsigned int maxScrollbackLines; 46 | 47 | @property (nonatomic) BOOL cursorVisible; 48 | @property (nonatomic) BOOL blinkingCursor; 49 | 50 | // line access 51 | - (screen_char_t *)getLineAtIndex:(int)theIndex; 52 | - (screen_char_t *)getLineAtScreenIndex:(int)theIndex; 53 | 54 | // edit screen buffer 55 | - (void)putToken:(VT100Token *)token; 56 | - (void)clearBuffer; 57 | - (void)clearScrollbackBuffer; 58 | 59 | // internal 60 | - (void)setString:(NSString *)s ascii:(BOOL)ascii; 61 | - (void)setNewLine; 62 | - (void)deleteCharacters:(int)n; 63 | - (void)backSpace; 64 | - (void)setTab; 65 | - (void)clearTabStop; 66 | - (void)clearScreen; 67 | - (void)eraseInDisplay:(VT100Token *)token; 68 | - (void)eraseInLine:(VT100Token *)token; 69 | - (void)selectGraphicRendition:(VT100Token *)token; 70 | - (void)cursorLeft:(int)n; 71 | - (void)cursorRight:(int)n; 72 | - (void)cursorUp:(int)n; 73 | - (void)cursorDown:(int)n; 74 | - (void)cursorToX:(int)x; 75 | - (void)cursorToX:(int)x Y:(int)y; 76 | - (void)saveCursorPosition; 77 | - (void)restoreCursorPosition; 78 | - (void)setTopBottom:(VT100Token *)token; 79 | - (void)scrollUp; 80 | - (void)scrollDown; 81 | - (void)activateBell; 82 | - (void)insertBlank:(int)n; 83 | - (void)insertLines:(int)n; 84 | - (void)deleteLines:(int)n; 85 | 86 | @property (nonatomic, readonly) int cursorX, cursorY; 87 | 88 | - (void)resetDirty; 89 | - (void)setDirty; 90 | 91 | - (int)numberOfLines; 92 | - (unsigned int)numberOfScrollbackLines; 93 | 94 | // print to ansi... 95 | @property (nonatomic) BOOL printToAnsi; 96 | 97 | - (void)printStringToAnsi:(NSString *)aString; 98 | 99 | // UI stuff 100 | @property (nonatomic, readonly) int newWidth, newHeight; 101 | @property (nonatomic, readonly) int scrollUpLines; 102 | 103 | - (void)resetScrollUpLines; 104 | 105 | @end 106 | 107 | /* vim: set syntax=objc sw=4 ts=4 sts=4 expandtab textwidth=80 ff=unix: */ 108 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100StringSupplier.h: -------------------------------------------------------------------------------- 1 | // VT100RowStringSupplier.h 2 | // MobileTerminal 3 | 4 | #import "VT100Types.h" 5 | 6 | @class VT100ColorMap, FontMetrics; 7 | 8 | @interface VT100StringSupplier : NSObject 9 | 10 | @property (nonatomic, weak) id screenBuffer; 11 | @property (nonatomic, strong) VT100ColorMap *colorMap; 12 | @property (nonatomic, strong) FontMetrics *fontMetrics; 13 | 14 | - (int)rowCount; 15 | 16 | - (NSString *)stringForLine:(int)rowIndex; 17 | - (NSMutableAttributedString *)attributedString; 18 | 19 | @end 20 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100StringSupplier.m: -------------------------------------------------------------------------------- 1 | // VT100RowStringSupplier.m 2 | // MobileTerminal 3 | 4 | #import "VT100StringSupplier.h" 5 | #import "VT100ColorMap.h" 6 | #import "VT100Types.h" 7 | #import "FontMetrics.h" 8 | 9 | @implementation VT100StringSupplier 10 | 11 | - (int)rowCount { 12 | return _screenBuffer.numberOfRows; 13 | } 14 | 15 | - (int)columnCount { 16 | return _screenBuffer.screenSize.width; 17 | } 18 | 19 | - (NSString *)stringForLine:(int)rowIndex { 20 | // Buffer of characters to draw on the screen, holds up to one row 21 | unichar unicharBuffer[kMaxRowBufferSize]; 22 | 23 | // TODO(aporter): Make the screen object itself return an attributed string? 24 | int width = self.columnCount; 25 | screen_char_t *row = [_screenBuffer bufferForRow:rowIndex]; 26 | 27 | for (int j = 0; j < width; ++j) { 28 | if (row[j].code == '\0') { 29 | unicharBuffer[j] = ' '; 30 | } else { 31 | unicharBuffer[j] = row[j].code; 32 | } 33 | } 34 | 35 | // UITextView won’t render a massive line of spaces (e.g. an empty nano screen), so add a newline 36 | // if the line ends with a space 37 | if (rowIndex != self.rowCount - 1 && unicharBuffer[width - 1] == ' ') { 38 | unicharBuffer[width - 1] = '\n'; 39 | } 40 | 41 | return [[NSString alloc] initWithCharacters:unicharBuffer length:width]; 42 | } 43 | 44 | - (NSMutableAttributedString *)attributedString { 45 | NSParameterAssert(_fontMetrics); 46 | NSParameterAssert(_fontMetrics.regularFont); 47 | NSParameterAssert(_fontMetrics.boldFont); 48 | NSParameterAssert(_screenBuffer); 49 | NSParameterAssert(_colorMap); 50 | 51 | int width = self.columnCount; 52 | ScreenPosition cursorPosition = _screenBuffer.cursorPosition; 53 | 54 | // The cursor is initially relative to the screen, not the position in the 55 | // scrollback buffer. 56 | if (_screenBuffer.numberOfRows > _screenBuffer.screenSize.height) { 57 | cursorPosition.y += _screenBuffer.numberOfRows - _screenBuffer.screenSize.height; 58 | } 59 | 60 | NSMutableString *allLines = [NSMutableString string]; 61 | 62 | for (int i = 0; i < self.rowCount; i++) { 63 | [allLines appendString:[self stringForLine:i]]; 64 | } 65 | 66 | NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle alloc] init]; 67 | paragraphStyle.alignment = NSTextAlignmentLeft; 68 | paragraphStyle.lineBreakMode = NSLineBreakByCharWrapping; 69 | 70 | NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:allLines attributes:@{ 71 | NSFontAttributeName: _fontMetrics.regularFont, 72 | NSParagraphStyleAttributeName: paragraphStyle 73 | }]; 74 | 75 | NSUInteger startOffset = 0; 76 | 77 | for (int i = 0; i < self.rowCount; i++) { 78 | // Update the string with background/foreground color attributes. This loop compares the colors 79 | // of characters and sets the attribute when it runs into a character of a different color. It 80 | // runs one extra time to set the attribute for the run of characters at the end of the line. 81 | NSUInteger lastColorIndex = NSUIntegerMax; 82 | UIColor *lastColor = nil; 83 | screen_char_t *row = [_screenBuffer bufferForRow:i]; 84 | 85 | // TODO(aporter): This looks a lot more complicated than it needs to be. Try 86 | // this again with fewer lines of code. 87 | for (int j = 0; j <= width; ++j) { 88 | BOOL eol = (j == width); // reached end of line 89 | UIColor *color = nil; 90 | 91 | if (!eol) { 92 | color = [_colorMap colorAtIndex:row[j].backgroundColor]; 93 | 94 | if (cursorPosition.x == j && cursorPosition.y == i) { 95 | color = _colorMap.backgroundCursor; 96 | } 97 | } 98 | 99 | if (eol || ![color isEqual:lastColor]) { 100 | int length = j - lastColorIndex; 101 | 102 | // TODO: the less than length check shouldn’t really be here, there’s clearly a bug 103 | // elsewhere in this logic 104 | if (lastColorIndex != NSUIntegerMax && startOffset + lastColorIndex + length <= attributedString.string.length) { 105 | [attributedString addAttribute:NSBackgroundColorAttributeName value:lastColor range:NSMakeRange(startOffset + lastColorIndex, length)]; 106 | } 107 | 108 | if (!eol) { 109 | lastColorIndex = j; 110 | lastColor = color; 111 | } 112 | } 113 | } 114 | 115 | // Same thing again for foreground color 116 | lastColorIndex = NSUIntegerMax; 117 | lastColor = nil; 118 | 119 | for (int j = 0; j <= width; ++j) { 120 | BOOL eol = (j == width); // reached end of line 121 | UIColor *color = nil; 122 | 123 | if (!eol) { 124 | color = [_colorMap colorAtIndex:row[j].foregroundColor]; 125 | 126 | if (cursorPosition.x == j && cursorPosition.y == i) { 127 | color = _colorMap.foregroundCursor; 128 | } 129 | } 130 | 131 | if (eol || ![color isEqual:lastColor]) { 132 | int length = j - lastColorIndex; 133 | 134 | // TODO: the less than length check shouldn’t really be here, there’s clearly a bug 135 | // elsewhere in this logic 136 | if (lastColorIndex != NSUIntegerMax && startOffset + lastColorIndex + length <= attributedString.string.length) { 137 | [attributedString addAttribute:NSForegroundColorAttributeName value:lastColor range:NSMakeRange(startOffset + lastColorIndex, length)]; 138 | } 139 | 140 | if (!eol) { 141 | lastColorIndex = j; 142 | lastColor = color; 143 | } 144 | } 145 | } 146 | 147 | startOffset += width; 148 | } 149 | 150 | return attributedString; 151 | } 152 | 153 | @end 154 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100Terminal.h: -------------------------------------------------------------------------------- 1 | // VT100Terminal.h 2 | // MobileTeterminal 3 | // 4 | // This header file originally came from the iTerm project, but has been 5 | // modified heavily for use in MobileTerminal. See the original copyright 6 | // below. 7 | // 8 | // VT100Terminal contains the logic for parsing streams of character data and 9 | // modifying the current state of the VT100Screen. This low-level component 10 | // should have no direct dependencies on other parts of the VT100 system. 11 | // 12 | // The VT100Terminal is wrapped by the VT100 class which provides a simpler 13 | // interface to use by the higher level text drawing components. 14 | /*` 15 | ** VT100Terminal.h 16 | ** 17 | ** Copyright (c) 2002, 2003, 2007 18 | ** 19 | ** Author: Fabian, Ujwal S. Setlur 20 | ** Initial code by Kiichi Kusama 21 | ** 22 | ** This program is free software; you can redistribute it and/or modify 23 | ** it under the terms of the GNU General Public License as published by 24 | ** the Free Software Foundation; either version 2 of the License, or 25 | ** (at your option) any later version. 26 | ** 27 | ** This program is distributed in the hope that it will be useful, 28 | ** but WITHOUT ANY WARRANTY; without even the implied warranty of 29 | ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 30 | ** GNU General Public License for more details. 31 | ** 32 | ** You should have received a copy of the GNU General Public License 33 | ** along with this program; if not, write to the Free Software 34 | ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 35 | */ 36 | 37 | #import "VT100Types.h" 38 | #import "VT100Token.h" 39 | 40 | #define VT100CSIPARAM_MAX 16 41 | 42 | @class VT100Screen; 43 | 44 | // character attributes 45 | #define VT100CHARATTR_ALLOFF 0 46 | #define VT100CHARATTR_BOLD 1 47 | #define VT100CHARATTR_UNDER 4 48 | #define VT100CHARATTR_BLINK 5 49 | #define VT100CHARATTR_REVERSE 7 50 | 51 | // xterm additions 52 | #define VT100CHARATTR_NORMAL 22 53 | #define VT100CHARATTR_NOT_UNDER 24 54 | #define VT100CHARATTR_STEADY 25 55 | #define VT100CHARATTR_POSITIVE 27 56 | 57 | typedef enum { 58 | COLORCODE_BLACK=0, 59 | COLORCODE_RED=1, 60 | COLORCODE_GREEN=2, 61 | COLORCODE_YELLOW=3, 62 | COLORCODE_BLUE=4, 63 | COLORCODE_PURPLE=5, 64 | COLORCODE_WATER=6, 65 | COLORCODE_WHITE=7, 66 | COLORCODE_256=8, 67 | COLORS 68 | } colorCode; 69 | 70 | // 8 color support 71 | #define VT100CHARATTR_FG_BASE 30 72 | #define VT100CHARATTR_BG_BASE 40 73 | 74 | #define VT100CHARATTR_FG_BLACK (VT100CHARATTR_FG_BASE + COLORCODE_BLACK) 75 | #define VT100CHARATTR_FG_RED (VT100CHARATTR_FG_BASE + COLORCODE_RED) 76 | #define VT100CHARATTR_FG_GREEN (VT100CHARATTR_FG_BASE + COLORCODE_GREEN) 77 | #define VT100CHARATTR_FG_YELLOW (VT100CHARATTR_FG_BASE + COLORCODE_YELLOW) 78 | #define VT100CHARATTR_FG_BLUE (VT100CHARATTR_FG_BASE + COLORCODE_BLUE) 79 | #define VT100CHARATTR_FG_PURPLE (VT100CHARATTR_FG_BASE + COLORCODE_PURPLE) 80 | #define VT100CHARATTR_FG_WATER (VT100CHARATTR_FG_BASE + COLORCODE_WATER) 81 | #define VT100CHARATTR_FG_WHITE (VT100CHARATTR_FG_BASE + COLORCODE_WHITE) 82 | #define VT100CHARATTR_FG_256 (VT100CHARATTR_FG_BASE + COLORCODE_256) 83 | #define VT100CHARATTR_FG_DEFAULT (VT100CHARATTR_FG_BASE + 9) 84 | 85 | #define VT100CHARATTR_BG_BLACK (VT100CHARATTR_BG_BASE + COLORCODE_BLACK) 86 | #define VT100CHARATTR_BG_RED (VT100CHARATTR_BG_BASE + COLORCODE_RED) 87 | #define VT100CHARATTR_BG_GREEN (VT100CHARATTR_BG_BASE + COLORCODE_GREEN) 88 | #define VT100CHARATTR_BG_YELLOW (VT100CHARATTR_BG_BASE + COLORCODE_YELLOW) 89 | #define VT100CHARATTR_BG_BLUE (VT100CHARATTR_BG_BASE + COLORCODE_BLUE) 90 | #define VT100CHARATTR_BG_PURPLE (VT100CHARATTR_BG_BASE + COLORCODE_PURPLE) 91 | #define VT100CHARATTR_BG_WATER (VT100CHARATTR_BG_BASE + COLORCODE_WATER) 92 | #define VT100CHARATTR_BG_WHITE (VT100CHARATTR_BG_BASE + COLORCODE_WHITE) 93 | #define VT100CHARATTR_BG_256 (VT100CHARATTR_BG_BASE + COLORCODE_256) 94 | #define VT100CHARATTR_BG_DEFAULT (VT100CHARATTR_BG_BASE + 9) 95 | 96 | // 16 color support 97 | #define VT100CHARATTR_FG_HI_BASE 90 98 | #define VT100CHARATTR_BG_HI_BASE 100 99 | 100 | #define VT100CHARATTR_FG_HI_BLACK (VT100CHARATTR_FG_HI_BASE + COLORCODE_BLACK) 101 | #define VT100CHARATTR_FG_HI_RED (VT100CHARATTR_FG_HI_BASE + COLORCODE_RED) 102 | #define VT100CHARATTR_FG_HI_GREEN (VT100CHARATTR_FG_HI_BASE + COLORCODE_GREEN) 103 | #define VT100CHARATTR_FG_HI_YELLOW (VT100CHARATTR_FG_HI_BASE + COLORCODE_YELLOW) 104 | #define VT100CHARATTR_FG_HI_BLUE (VT100CHARATTR_FG_HI_BASE + COLORCODE_BLUE) 105 | #define VT100CHARATTR_FG_HI_PURPLE (VT100CHARATTR_FG_HI_BASE + COLORCODE_PURPLE) 106 | #define VT100CHARATTR_FG_HI_WATER (VT100CHARATTR_FG_HI_BASE + COLORCODE_WATER) 107 | #define VT100CHARATTR_FG_HI_WHITE (VT100CHARATTR_FG_HI_BASE + COLORCODE_WHITE) 108 | 109 | #define VT100CHARATTR_BG_HI_BLACK (VT100CHARATTR_BG_HI_BASE + COLORCODE_BLACK) 110 | #define VT100CHARATTR_BG_HI_RED (VT100CHARATTR_BG_HI_BASE + COLORCODE_RED) 111 | #define VT100CHARATTR_BG_HI_GREEN (VT100CHARATTR_BG_HI_BASE + COLORCODE_GREEN) 112 | #define VT100CHARATTR_BG_HI_YELLOW (VT100CHARATTR_BG_HI_BASE + COLORCODE_YELLOW) 113 | #define VT100CHARATTR_BG_HI_BLUE (VT100CHARATTR_BG_HI_BASE + COLORCODE_BLUE) 114 | #define VT100CHARATTR_BG_HI_PURPLE (VT100CHARATTR_BG_HI_BASE + COLORCODE_PURPLE) 115 | #define VT100CHARATTR_BG_HI_WATER (VT100CHARATTR_BG_HI_BASE + COLORCODE_WATER) 116 | #define VT100CHARATTR_BG_HI_WHITE (VT100CHARATTR_BG_HI_BASE + COLORCODE_WHITE) 117 | 118 | // color codes & masks 119 | #define FG_COLOR_CODE 0x100 120 | #define BG_COLOR_CODE 0x101 121 | #define SELECTED_TEXT 0x102 122 | #define CURSOR_TEXT 0x103 123 | #define CURSOR_BG 0x104 124 | 125 | #define COLOR_CODE_MASK 0x100 126 | #define SELECTION_MASK 0x200 127 | #define BOLD_MASK 0x200 128 | #define BLINK_MASK 0x400 129 | #define UNDER_MASK 0x800 130 | 131 | // terminfo stuff 132 | enum { 133 | TERMINFO_KEY_LEFT, TERMINFO_KEY_RIGHT, TERMINFO_KEY_UP, TERMINFO_KEY_DOWN, 134 | TERMINFO_KEY_HOME, TERMINFO_KEY_END, TERMINFO_KEY_PAGEDOWN, TERMINFO_KEY_PAGEUP, 135 | TERMINFO_KEY_F0, TERMINFO_KEY_F1, TERMINFO_KEY_F2, TERMINFO_KEY_F3, TERMINFO_KEY_F4, 136 | TERMINFO_KEY_F5, TERMINFO_KEY_F6, TERMINFO_KEY_F7, TERMINFO_KEY_F8, TERMINFO_KEY_F9, 137 | TERMINFO_KEY_F10, TERMINFO_KEY_F11, TERMINFO_KEY_F12, TERMINFO_KEY_F13, TERMINFO_KEY_F14, 138 | TERMINFO_KEY_F15, TERMINFO_KEY_F16, TERMINFO_KEY_F17, TERMINFO_KEY_F18, TERMINFO_KEY_F19, 139 | TERMINFO_KEY_F20, TERMINFO_KEY_F21, TERMINFO_KEY_F22, TERMINFO_KEY_F23, TERMINFO_KEY_F24, 140 | TERMINFO_KEY_F25, TERMINFO_KEY_F26, TERMINFO_KEY_F27, TERMINFO_KEY_F28, TERMINFO_KEY_F29, 141 | TERMINFO_KEY_F30, TERMINFO_KEY_F31, TERMINFO_KEY_F32, TERMINFO_KEY_F33, TERMINFO_KEY_F34, 142 | TERMINFO_KEY_F35, 143 | TERMINFO_KEY_BACKSPACE, TERMINFO_KEY_BACK_TAB, 144 | TERMINFO_KEY_TAB, 145 | TERMINFO_KEY_DEL, TERMINFO_KEY_INS, 146 | TERMINFO_KEY_HELP, 147 | TERMINFO_KEYS 148 | }; 149 | 150 | typedef enum { 151 | MOUSE_REPORTING_NONE = -1, 152 | MOUSE_REPORTING_NORMAL = 0, 153 | MOUSE_REPORTING_HILITE, 154 | MOUSE_REPORTING_BUTTON_MOTION, 155 | MOUSE_REPORTING_ALL_MOTION, 156 | } mouseMode; 157 | 158 | @interface VT100Terminal : NSObject 159 | 160 | - (instancetype)init; 161 | 162 | @property (nonatomic, strong) NSString *termType; 163 | @property (nonatomic) BOOL trace; 164 | @property (nonatomic) BOOL strictAnsiMode; 165 | @property (nonatomic) BOOL allowColumnMode; 166 | @property (nonatomic) NSStringEncoding encoding; 167 | 168 | @property (nonatomic, weak) VT100Screen *currentScreen; 169 | @property (nonatomic, strong) VT100Screen *primaryScreen; 170 | @property (nonatomic, strong) VT100Screen *alternateScreen; 171 | 172 | - (void)setMode:(VT100Token *)token; 173 | - (void)setCharAttr:(VT100Token *)token; 174 | 175 | - (void)cleanStream; 176 | - (void)putStreamData:(NSData *)data; 177 | - (VT100Token *)getNextToken; 178 | 179 | - (void)reset; 180 | 181 | - (NSData *)keyArrowUp:(unsigned int)modflag; 182 | - (NSData *)keyArrowDown:(unsigned int)modflag; 183 | - (NSData *)keyArrowLeft:(unsigned int)modflag; 184 | - (NSData *)keyArrowRight:(unsigned int)modflag; 185 | - (NSData *)keyHome:(unsigned int)modflag; 186 | - (NSData *)keyEnd:(unsigned int)modflag; 187 | - (NSData *)keyInsert; 188 | - (NSData *)keyDelete; 189 | - (NSData *)keyBackspace; 190 | - (NSData *)keyPageUp; 191 | - (NSData *)keyPageDown; 192 | - (NSData *)keyFunction:(int)no; 193 | - (NSData *)keyPFn:(int)n; 194 | - (NSData *)keypadData:(unichar)unicode keystr:(NSString *)keystr; 195 | 196 | @property (nonatomic, readonly) BOOL lineMode; // YES=Newline, NO=Line feed 197 | @property (nonatomic, readonly) BOOL cursorMode; // YES=Application, NO=Cursor 198 | @property (nonatomic, readonly) BOOL ansiMode; // YES=ANSI, NO=VT52 199 | @property (nonatomic, readonly) BOOL columnMode; // YES=132 Column, NO=80 Column 200 | @property (nonatomic, readonly) BOOL scrollMode; // YES=Smooth, NO=Jump 201 | @property (nonatomic, readonly) BOOL screenMode; // YES=Reverse, NO=Normal 202 | @property (nonatomic, readonly) BOOL originMode; // YES=Relative, NO=Absolute 203 | @property (nonatomic, readonly) BOOL wraparoundMode; // YES=On, NO=Off 204 | @property (nonatomic, readonly) BOOL autorepeatMode; // YES=On, NO=Off 205 | @property (nonatomic, readonly) BOOL interlaceMode; // YES=On, NO=Off 206 | @property (nonatomic, readonly) BOOL keypadMode; // YES=Application, NO=Numeric 207 | @property (nonatomic, readonly) BOOL insertMode; // YES=Insert, NO=Replace 208 | @property (nonatomic, readonly) int charset; // G0...G3 209 | @property (nonatomic, readonly) BOOL xon; // YES=_xon, NO=XOFF 210 | @property (nonatomic, readonly) mouseMode mouseMode; 211 | 212 | - (int)foregroundColorCode; 213 | - (int)backgroundColorCode; 214 | 215 | - (NSData *)reportActivePositionWithX:(int)x Y:(int)y; 216 | - (NSData *)reportStatus; 217 | - (NSData *)reportDeviceAttribute; 218 | - (NSData *)reportSecondaryDeviceAttribute; 219 | 220 | @end 221 | 222 | /* vim: set syntax=objc sw=4 ts=4 sts=4 expandtab textwidth=80 ff=unix: */ 223 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100Token.h: -------------------------------------------------------------------------------- 1 | #import "ScreenChar.h" 2 | #import "iTermParser.h" 3 | 4 | typedef enum { 5 | // Any control character between 0-0x1f inclusive can by a token type. For these, the value 6 | // matters. Make sure to update the -codeName method when changing this enum. 7 | VT100CC_NULL = 0, 8 | VT100CC_SOH = 1, // Not used 9 | VT100CC_STX = 2, // Not used 10 | VT100CC_ETX = 3, // Not used 11 | VT100CC_EOT = 4, // Not used 12 | VT100CC_ENQ = 5, // Transmit ANSWERBACK message 13 | VT100CC_ACK = 6, // Not used 14 | VT100CC_BEL = 7, // Sound bell 15 | VT100CC_BS = 8, // Move cursor to the left 16 | VT100CC_HT = 9, // Move cursor to the next tab stop 17 | VT100CC_LF = 10, // line feed or new line operation 18 | VT100CC_VT = 11, // Same as . 19 | VT100CC_FF = 12, // Same as . 20 | VT100CC_CR = 13, // Move the cursor to the left margin 21 | VT100CC_SO = 14, // Invoke the G1 character set 22 | VT100CC_SI = 15, // Invoke the G0 character set 23 | VT100CC_DLE = 16, // Not used 24 | VT100CC_DC1 = 17, // Causes terminal to resume transmission (XON). 25 | VT100CC_DC2 = 18, // Not used 26 | VT100CC_DC3 = 19, // Causes terminal to stop transmitting all codes except XOFF and XON (XOFF). 27 | VT100CC_DC4 = 20, // Not used 28 | VT100CC_NAK = 21, // Not used 29 | VT100CC_SYN = 22, // Not used 30 | VT100CC_ETB = 23, // Not used 31 | VT100CC_CAN = 24, // Cancel a control sequence 32 | VT100CC_EM = 25, // Not used 33 | VT100CC_SUB = 26, // Same as . 34 | VT100CC_ESC = 27, // Introduces a control sequence. 35 | VT100CC_FS = 28, // Not used 36 | VT100CC_GS = 29, // Not used 37 | VT100CC_RS = 30, // Not used 38 | VT100CC_US = 31, // Not used 39 | VT100CC_DEL = 127, // Backspaces 40 | 41 | VT100_WAIT = 1000, 42 | VT100_NOTSUPPORT, 43 | VT100_SKIP, 44 | VT100_STRING, 45 | VT100_ASCIISTRING, 46 | VT100_UNKNOWNCHAR, 47 | VT100_INVALID_SEQUENCE, 48 | VT100_BINARY_GARBAGE, 49 | 50 | VT100CSI_CPR, // Cursor Position Report 51 | VT100CSI_CUB, // Cursor Backward 52 | VT100CSI_CUD, // Cursor Down 53 | VT100CSI_CUF, // Cursor Forward 54 | VT100CSI_CUP, // Cursor Position 55 | VT100CSI_CUU, // Cursor Up 56 | VT100CSI_CNL, // Cursor Next Line 57 | VT100CSI_CPL, // Cursor Preceding Line 58 | VT100CSI_DA, // Device Attributes 59 | VT100CSI_DA2, // Secondary Device Attributes 60 | VT100CSI_DECALN, // Screen Alignment Display 61 | VT100CSI_DECDHL, // Double Height Line 62 | VT100CSI_DECDWL, // Double Width Line 63 | VT100CSI_DECID, // Identify Terminal 64 | VT100CSI_DECKPAM, // Keypad Application Mode 65 | VT100CSI_DECKPNM, // Keypad Numeric Mode 66 | VT100CSI_DECRC, // Restore Cursor 67 | VT100CSI_DECRST, 68 | VT100CSI_DECSC, // Save Cursor 69 | VT100CSI_DECSET, 70 | VT100CSI_DECSTBM, // Set Top and Bottom Margins 71 | VT100CSI_DSR, // Device Status Report 72 | VT100CSI_ED, // Erase In Display 73 | VT100CSI_EL, // Erase In Line 74 | VT100CSI_HTS, // Horizontal Tabulation Set 75 | VT100CSI_HVP, // Horizontal and Vertical Position 76 | VT100CSI_IND, // Index 77 | VT100CSI_NEL, // Next Line 78 | VT100CSI_RI, // Reverse Index 79 | VT100CSI_RIS, // Reset To Initial State 80 | VT100CSI_RM, // Reset Mode 81 | VT100CSI_SCS, 82 | VT100CSI_SCS0, // Select Character Set 0 83 | VT100CSI_SCS1, // Select Character Set 1 84 | VT100CSI_SCS2, // Select Character Set 2 85 | VT100CSI_SCS3, // Select Character Set 3 86 | VT100CSI_SGR, // Select Graphic Rendition 87 | VT100CSI_SM, // Set Mode 88 | VT100CSI_TBC, // Tabulation Clear 89 | VT100CSI_DECSCUSR, // Select the Style of the Cursor 90 | VT100CSI_DECSTR, // Soft reset 91 | VT100CSI_DECDSR, // Device Status Report (DEC specific) 92 | VT100CSI_SET_MODIFIERS, // CSI > Ps; Pm m (Whether to set modifiers for different kinds of key presses; no official name) 93 | VT100CSI_RESET_MODIFIERS, // CSI > Ps n (Set all modifiers values to -1, disabled) 94 | VT100CSI_DECSLRM, // Set left-right margin 95 | VT100CSI_DECRQCRA, // Request Checksum of Rectangular Area 96 | VT100CSI_REP, // Repeat 97 | 98 | // some xterm extensions 99 | XTERMCC_WIN_TITLE, // Set window title 100 | XTERMCC_ICON_TITLE, 101 | XTERMCC_WINICON_TITLE, 102 | VT100CSI_ICH, // Insert blank 103 | XTERMCC_INSLN, // Insert lines 104 | XTERMCC_DELCH, // delete blank 105 | XTERMCC_DELLN, // delete lines 106 | XTERMCC_WINDOWSIZE, // (8,H,W) NK: added for Vim resizing window 107 | XTERMCC_WINDOWSIZE_PIXEL, // (8,H,W) NK: added for Vim resizing window 108 | XTERMCC_WINDOWPOS, // (3,Y,X) NK: added for Vim positioning window 109 | XTERMCC_ICONIFY, 110 | XTERMCC_DEICONIFY, 111 | XTERMCC_RAISE, 112 | XTERMCC_LOWER, 113 | XTERMCC_SU, // scroll up 114 | XTERMCC_SD, // scroll down 115 | XTERMCC_REPORT_WIN_STATE, 116 | XTERMCC_REPORT_WIN_POS, 117 | XTERMCC_REPORT_WIN_PIX_SIZE, 118 | XTERMCC_REPORT_WIN_SIZE, 119 | XTERMCC_REPORT_SCREEN_SIZE, 120 | XTERMCC_REPORT_ICON_TITLE, 121 | XTERMCC_REPORT_WIN_TITLE, 122 | XTERMCC_PUSH_TITLE, 123 | XTERMCC_POP_TITLE, 124 | XTERMCC_SET_RGB, 125 | // This is not a real xterm code. It is from eTerm, which extended the xterm 126 | // protocol for its own purposes. We don't follow the eTerm protocol, 127 | // but we follow the template it set. 128 | // http://www.eterm.org/docs/view.php?doc=ref#escape 129 | XTERMCC_PROPRIETARY_ETERM_EXT, 130 | XTERMCC_PWD_URL, 131 | XTERMCC_LINK, 132 | XTERMCC_SET_PALETTE, 133 | XTERMCC_SET_KVP, 134 | // OSC 1337;File=(args):(data) gets changed by the parser from XTERMCC_SET_KVP to a 135 | // series of incidental tokens beginning with XTERMCC_MULTITOKEN_HEADER_SET_KVP. 136 | // See comment above XTERMCC_MULTITOKEN_BODY for details. 137 | XTERMCC_MULTITOKEN_HEADER_SET_KVP, 138 | XTERMCC_PASTE64, 139 | XTERMCC_FINAL_TERM, 140 | 141 | // If a sequence is split into mutiple tokens, the first will be one of the above whose name 142 | // includes MULTITOKEN_HEADER, then zero or more of these, and then XTERMCC_MULTITOKEN_END. 143 | XTERMCC_MULTITOKEN_BODY, 144 | XTERMCC_MULTITOKEN_END, 145 | 146 | // Some ansi stuff 147 | ANSICSI_CHA, // Cursor Horizontal Absolute 148 | ANSICSI_VPA, // Vert Position Absolute 149 | ANSICSI_VPR, // Vert Position Relative 150 | ANSICSI_ECH, // Erase Character 151 | ANSICSI_PRINT, // Print to Ansi 152 | ANSICSI_SCP, // Save cursor position 153 | ANSICSI_RCP, // Restore cursor position 154 | ANSICSI_CBT, // Back tab 155 | 156 | ANSI_RIS, // Reset to initial state (there's also a CSI version) 157 | 158 | // DCS 159 | DCS_REQUEST_TERMCAP_TERMINFO, // Request Termcap/Terminfo String 160 | 161 | // Toggle between ansi/vt52 162 | STRICT_ANSI_MODE, 163 | 164 | // iTerm extension 165 | ITERM_GROWL, 166 | DCS_TMUX_HOOK, // Enter tmux mode 167 | 168 | // Wraps an escape code. The escape code is in csi.string. 169 | DCS_TMUX_CODE_WRAP, 170 | 171 | TMUX_LINE, // A line of input from tmux 172 | TMUX_EXIT, // Exit tmux mode 173 | 174 | // Ambiguous codes - disambiguated at execution time. 175 | VT100CSI_DECSLRM_OR_ANSICSI_SCP, 176 | 177 | // ISO-2022 codes for choosing character encoding. There are a bunch of other encodings that 178 | // there are escape codes for but they're really old-fashioned, so only these two are supported 179 | // so far. 180 | ISO2022_SELECT_LATIN_1, 181 | ISO2022_SELECT_UTF_8 182 | } VT100TerminalTokenType; 183 | 184 | // A preinitialized array of screen_char_t. When ASCII data is present, it will have the codes 185 | // populated and all other fields zeroed out. 186 | #define kStaticScreenCharsCount 16 187 | typedef struct { 188 | screen_char_t *buffer; 189 | int length; 190 | screen_char_t staticBuffer[kStaticScreenCharsCount]; 191 | } ScreenChars; 192 | 193 | // Tokens with type VT100_ASCIISTRING are stored in |asciiData| with this type. 194 | // |buffer| will point at |staticBuffer| or a malloc()ed buffer, depending on 195 | // |length|. 196 | typedef struct { 197 | char *buffer; 198 | int length; 199 | char staticBuffer[128]; 200 | ScreenChars *screenChars; 201 | } AsciiData; 202 | 203 | @interface VT100Token : NSObject { 204 | @public 205 | // data is populated because the current mode uses the raw input. data is 206 | // always set for ascii strings regardless of mode. 207 | BOOL savingData; 208 | } 209 | 210 | @property (nonatomic) VT100TerminalTokenType type; 211 | 212 | @property (nonatomic) unsigned char *position; 213 | @property (nonatomic) int length; 214 | 215 | // For VT100_UNKNOWNCHAR and VT100CSI_SCS0...SCS3. 216 | @property (nonatomic) unsigned char code; 217 | 218 | // For VT100_STRING 219 | @property (nonatomic, retain) NSString *string; 220 | 221 | // For saved data (when copying to clipboard) 222 | @property (nonatomic, retain) NSData *savedData; 223 | 224 | // For XTERMCC_SET_KVP. 225 | @property (nonatomic, retain) NSString *kvpKey; 226 | @property (nonatomic, retain) NSString *kvpValue; 227 | 228 | // For VT100CSI_ codes that take paramters. 229 | @property (nonatomic, readonly) CSIParam *csi; 230 | 231 | // Is this an ascii string? 232 | @property (nonatomic, readonly) BOOL isAscii; 233 | 234 | // Is this a string or ascii string? 235 | @property (nonatomic, readonly) BOOL isStringType; 236 | 237 | // For ascii strings (type==VT100_ASCIISTRING). 238 | @property (nonatomic, readonly) AsciiData *asciiData; 239 | 240 | + (instancetype)token; 241 | + (instancetype)tokenForControlCharacter:(unsigned char)controlCharacter; 242 | 243 | - (void)setAsciiBytes:(char *)bytes length:(int)length; 244 | 245 | // Returns a string for |asciiData|, for convenience (this is slow). 246 | - (NSString *)stringForAsciiData; 247 | 248 | @end 249 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100Token.m: -------------------------------------------------------------------------------- 1 | // 2 | // VT100Token.m 3 | // iTerm 4 | // 5 | // Created by George Nachman on 3/3/14. 6 | // 7 | // 8 | 9 | #import "VT100Token.h" 10 | #include 11 | 12 | @interface VT100Token () 13 | @property(nonatomic, readwrite) CSIParam *csi; 14 | @end 15 | 16 | @implementation VT100Token { 17 | AsciiData _asciiData; 18 | ScreenChars _screenChars; 19 | } 20 | 21 | + (instancetype)token { 22 | return [[self alloc] init]; 23 | } 24 | 25 | + (instancetype)tokenForControlCharacter:(unsigned char)controlCharacter { 26 | VT100Token *token = [[self alloc] init]; 27 | token.type = controlCharacter; 28 | return token; 29 | } 30 | 31 | - (void)dealloc { 32 | if (_csi) { 33 | free(_csi); 34 | _csi = NULL; 35 | } 36 | 37 | if (_asciiData.buffer != _asciiData.staticBuffer) { 38 | free(_asciiData.buffer); 39 | } 40 | if (_asciiData.screenChars && _asciiData.screenChars->buffer != _asciiData.screenChars->staticBuffer) { 41 | free(_asciiData.screenChars->buffer); 42 | } 43 | _asciiData.buffer = NULL; 44 | _asciiData.length = 0; 45 | _asciiData.screenChars = NULL; 46 | 47 | _type = 0; 48 | _code = 0; 49 | } 50 | 51 | - (NSString *)codeName { 52 | NSDictionary *map = @{@(VT100CC_NULL): @"VT100CC_NULL", 53 | @(VT100CC_SOH): @"VT100CC_SOH", 54 | @(VT100CC_STX): @"VT100CC_STX", 55 | @(VT100CC_ETX): @"VT100CC_ETX", 56 | @(VT100CC_EOT): @"VT100CC_EOT", 57 | @(VT100CC_ENQ): @"VT100CC_ENQ", 58 | @(VT100CC_ACK): @"VT100CC_ACK", 59 | @(VT100CC_BEL): @"VT100CC_BEL", 60 | @(VT100CC_BS): @"VT100CC_BS", 61 | @(VT100CC_HT): @"VT100CC_HT", 62 | @(VT100CC_LF): @"VT100CC_LF", 63 | @(VT100CC_VT): @"VT100CC_VT", 64 | @(VT100CC_FF): @"VT100CC_FF", 65 | @(VT100CC_CR): @"VT100CC_CR", 66 | @(VT100CC_SO): @"VT100CC_SO", 67 | @(VT100CC_SI): @"VT100CC_SI", 68 | @(VT100CC_DLE): @"VT100CC_DLE", 69 | @(VT100CC_DC1): @"VT100CC_DC1", 70 | @(VT100CC_DC2): @"VT100CC_DC2", 71 | @(VT100CC_DC3): @"VT100CC_DC3", 72 | @(VT100CC_DC4): @"VT100CC_DC4", 73 | @(VT100CC_NAK): @"VT100CC_NAK", 74 | @(VT100CC_SYN): @"VT100CC_SYN", 75 | @(VT100CC_ETB): @"VT100CC_ETB", 76 | @(VT100CC_CAN): @"VT100CC_CAN", 77 | @(VT100CC_EM): @"VT100CC_EM", 78 | @(VT100CC_SUB): @"VT100CC_SUB", 79 | @(VT100CC_ESC): @"VT100CC_ESC", 80 | @(VT100CC_FS): @"VT100CC_FS", 81 | @(VT100CC_GS): @"VT100CC_GS", 82 | @(VT100CC_RS): @"VT100CC_RS", 83 | @(VT100CC_US): @"VT100CC_US", 84 | @(VT100CC_DEL): @"VT100CC_DEL", 85 | @(VT100_WAIT): @"VT100_WAIT", 86 | @(VT100_NOTSUPPORT): @"VT100_NOTSUPPORT", 87 | @(VT100_SKIP): @"VT100_SKIP", 88 | @(VT100_STRING): @"VT100_STRING", 89 | @(VT100_ASCIISTRING): @"VT100_ASCIISTRING", 90 | @(VT100_UNKNOWNCHAR): @"VT100_UNKNOWNCHAR", 91 | @(VT100_INVALID_SEQUENCE): @"VT100_INVALID_SEQUENCE", 92 | @(VT100_BINARY_GARBAGE): @"VT100_BINARY_GARBAGE", 93 | @(VT100CSI_CPR): @"VT100CSI_CPR", 94 | @(VT100CSI_CUB): @"VT100CSI_CUB", 95 | @(VT100CSI_CUD): @"VT100CSI_CUD", 96 | @(VT100CSI_CUF): @"VT100CSI_CUF", 97 | @(VT100CSI_CUP): @"VT100CSI_CUP", 98 | @(VT100CSI_CUU): @"VT100CSI_CUU", 99 | @(VT100CSI_DA): @"VT100CSI_DA", 100 | @(VT100CSI_DA2): @"VT100CSI_DA2", 101 | @(VT100CSI_DECALN): @"VT100CSI_DECALN", 102 | @(VT100CSI_DECDHL): @"VT100CSI_DECDHL", 103 | @(VT100CSI_DECDWL): @"VT100CSI_DECDWL", 104 | @(VT100CSI_DECID): @"VT100CSI_DECID", 105 | @(VT100CSI_DECKPAM): @"VT100CSI_DECKPAM", 106 | @(VT100CSI_DECKPNM): @"VT100CSI_DECKPNM", 107 | @(VT100CSI_DECRC): @"VT100CSI_DECRC", 108 | @(VT100CSI_DECRQCRA): @"VT100CSI_DECRQCRA", 109 | @(VT100CSI_DECRST): @"VT100CSI_DECRST", 110 | @(VT100CSI_DECSC): @"VT100CSI_DECSC", 111 | @(VT100CSI_DECSET): @"VT100CSI_DECSET", 112 | @(VT100CSI_DECSTBM): @"VT100CSI_DECSTBM", 113 | @(VT100CSI_DSR): @"VT100CSI_DSR", 114 | @(VT100CSI_ED): @"VT100CSI_ED", 115 | @(VT100CSI_EL): @"VT100CSI_EL", 116 | @(VT100CSI_HTS): @"VT100CSI_HTS", 117 | @(VT100CSI_HVP): @"VT100CSI_HVP", 118 | @(VT100CSI_IND): @"VT100CSI_IND", 119 | @(VT100CSI_NEL): @"VT100CSI_NEL", 120 | @(VT100CSI_RI): @"VT100CSI_RI", 121 | @(VT100CSI_RIS): @"VT100CSI_RIS", 122 | @(VT100CSI_RM): @"VT100CSI_RM", 123 | @(VT100CSI_SCS): @"VT100CSI_SCS", 124 | @(VT100CSI_SCS0): @"VT100CSI_SCS0", 125 | @(VT100CSI_SCS1): @"VT100CSI_SCS1", 126 | @(VT100CSI_SCS2): @"VT100CSI_SCS2", 127 | @(VT100CSI_SCS3): @"VT100CSI_SCS3", 128 | @(VT100CSI_SGR): @"VT100CSI_SGR", 129 | @(VT100CSI_SM): @"VT100CSI_SM", 130 | @(VT100CSI_TBC): @"VT100CSI_TBC", 131 | @(VT100CSI_DECSCUSR): @"VT100CSI_DECSCUSR", 132 | @(VT100CSI_DECSTR): @"VT100CSI_DECSTR", 133 | @(VT100CSI_DECDSR): @"VT100CSI_DECDSR", 134 | @(VT100CSI_SET_MODIFIERS): @"VT100CSI_SET_MODIFIERS", 135 | @(VT100CSI_RESET_MODIFIERS): @"VT100CSI_RESET_MODIFIERS", 136 | @(VT100CSI_REP): @"VT100CSI_REP", 137 | @(VT100CSI_DECSLRM): @"VT100CSI_DECSLRM", 138 | @(XTERMCC_WIN_TITLE): @"XTERMCC_WIN_TITLE", 139 | @(XTERMCC_ICON_TITLE): @"XTERMCC_ICON_TITLE", 140 | @(XTERMCC_WINICON_TITLE): @"XTERMCC_WINICON_TITLE", 141 | @(VT100CSI_ICH): @"VT100CSI_ICH", 142 | @(XTERMCC_INSLN): @"XTERMCC_INSLN", 143 | @(XTERMCC_DELCH): @"XTERMCC_DELCH", 144 | @(XTERMCC_DELLN): @"XTERMCC_DELLN", 145 | @(XTERMCC_WINDOWSIZE): @"XTERMCC_WINDOWSIZE", 146 | @(XTERMCC_WINDOWSIZE_PIXEL): @"XTERMCC_WINDOWSIZE_PIXEL", 147 | @(XTERMCC_WINDOWPOS): @"XTERMCC_WINDOWPOS", 148 | @(XTERMCC_ICONIFY): @"XTERMCC_ICONIFY", 149 | @(XTERMCC_DEICONIFY): @"XTERMCC_DEICONIFY", 150 | @(XTERMCC_RAISE): @"XTERMCC_RAISE", 151 | @(XTERMCC_LOWER): @"XTERMCC_LOWER", 152 | @(XTERMCC_SU): @"XTERMCC_SU", 153 | @(XTERMCC_SD): @"XTERMCC_SD", 154 | @(XTERMCC_REPORT_WIN_STATE): @"XTERMCC_REPORT_WIN_STATE", 155 | @(XTERMCC_REPORT_WIN_POS): @"XTERMCC_REPORT_WIN_POS", 156 | @(XTERMCC_REPORT_WIN_PIX_SIZE): @"XTERMCC_REPORT_WIN_PIX_SIZE", 157 | @(XTERMCC_REPORT_WIN_SIZE): @"XTERMCC_REPORT_WIN_SIZE", 158 | @(XTERMCC_REPORT_SCREEN_SIZE): @"XTERMCC_REPORT_SCREEN_SIZE", 159 | @(XTERMCC_REPORT_ICON_TITLE): @"XTERMCC_REPORT_ICON_TITLE", 160 | @(XTERMCC_REPORT_WIN_TITLE): @"XTERMCC_REPORT_WIN_TITLE", 161 | @(XTERMCC_PUSH_TITLE): @"XTERMCC_PUSH_TITLE", 162 | @(XTERMCC_POP_TITLE): @"XTERMCC_POP_TITLE", 163 | @(XTERMCC_SET_RGB): @"XTERMCC_SET_RGB", 164 | @(XTERMCC_PROPRIETARY_ETERM_EXT): @"XTERMCC_PROPRIETARY_ETERM_EXT", 165 | @(XTERMCC_PWD_URL): @"XTERMCC_PWD_URL", 166 | @(XTERMCC_SET_PALETTE): @"XTERMCC_SET_PALETTE", 167 | @(XTERMCC_SET_KVP): @"XTERMCC_SET_KVP", 168 | @(XTERMCC_MULTITOKEN_HEADER_SET_KVP): @"XTERMCC_MULTITOKEN_HEADER_SET_KVP", 169 | @(XTERMCC_MULTITOKEN_BODY): @"XTERMCC_MULTITOKEN_BODY", 170 | @(XTERMCC_MULTITOKEN_END): @"XTERMCC_MULTITOKEN_END", 171 | @(XTERMCC_PASTE64): @"XTERMCC_PASTE64", 172 | @(XTERMCC_FINAL_TERM): @"XTERMCC_FINAL_TERM", 173 | @(XTERMCC_LINK): @"XTERMCC_LINK", 174 | @(ANSICSI_CHA): @"ANSICSI_CHA", 175 | @(ANSICSI_VPA): @"ANSICSI_VPA", 176 | @(ANSICSI_VPR): @"ANSICSI_VPR", 177 | @(ANSICSI_ECH): @"ANSICSI_ECH", 178 | @(ANSICSI_PRINT): @"ANSICSI_PRINT", 179 | @(ANSICSI_SCP): @"ANSICSI_SCP", 180 | @(ANSICSI_RCP): @"ANSICSI_RCP", 181 | @(ANSICSI_CBT): @"ANSICSI_CBT", 182 | @(ANSI_RIS): @"ANSI_RIS", 183 | @(STRICT_ANSI_MODE): @"STRICT_ANSI_MODE", 184 | @(ITERM_GROWL): @"ITERM_GROWL", 185 | @(DCS_TMUX_HOOK): @"DCS_TMUX_HOOK", 186 | @(TMUX_LINE): @"TMUX_LINE", 187 | @(TMUX_EXIT): @"TMUX_EXIT", 188 | @(DCS_TMUX_CODE_WRAP): @"DCS_TMUX_CODE_WRAP", 189 | @(VT100CSI_DECSLRM_OR_ANSICSI_SCP): @"VT100CSI_DECSLRM_OR_ANSICSI_SCP", 190 | @(DCS_REQUEST_TERMCAP_TERMINFO): @"DCS_REQUEST_TERMCAP_TERMINFO", }; 191 | NSString *name = map[@(_type)]; 192 | if (name) { 193 | return name; 194 | } else { 195 | return [NSString stringWithFormat:@"%d", _type]; 196 | } 197 | } 198 | 199 | - (NSString *)description { 200 | NSMutableString *params = [NSMutableString string]; 201 | if (_csi && _csi->count > 0) { 202 | [params appendString:@" params="]; 203 | for (int i = 0; i < _csi->count; i++) { 204 | if (_csi->p[i] < 0) { 205 | [params appendFormat:@"[default];"]; 206 | } else { 207 | [params appendFormat:@"%d;", _csi->p[i]]; 208 | } 209 | } 210 | } 211 | if (_string) { 212 | [params appendFormat:@" string=“%@”", _string]; 213 | } 214 | if (_asciiData.length) { 215 | [params appendFormat:@" asciiData=“%.*s”", _asciiData.length, _asciiData.buffer]; 216 | } 217 | return [NSString stringWithFormat:@"<%@: %p type=%@%@>", self.class, self, [self codeName], params]; 218 | } 219 | 220 | - (CSIParam *)csi { 221 | if (!_csi) { 222 | _csi = calloc(sizeof(*_csi), 1); 223 | } 224 | return _csi; 225 | } 226 | 227 | - (BOOL)isAscii { 228 | return _type == VT100_ASCIISTRING; 229 | } 230 | 231 | - (BOOL)isStringType { 232 | return (_type == VT100_STRING || _type == VT100_ASCIISTRING); 233 | } 234 | 235 | - (void)setAsciiBytes:(char *)bytes length:(int)length { 236 | assert(_asciiData.buffer == NULL); 237 | 238 | _asciiData.length = length; 239 | if (length > sizeof(_asciiData.staticBuffer)) { 240 | _asciiData.buffer = malloc(length); 241 | } else { 242 | _asciiData.buffer = _asciiData.staticBuffer; 243 | } 244 | memcpy(_asciiData.buffer, bytes, length); 245 | 246 | [self preInitializeScreenChars]; 247 | } 248 | 249 | - (AsciiData *)asciiData { 250 | return &_asciiData; 251 | } 252 | 253 | - (NSString *)stringForAsciiData { 254 | return [[NSString alloc] initWithBytes:_asciiData.buffer length:_asciiData.length encoding:NSASCIIStringEncoding]; 255 | } 256 | 257 | - (ScreenChars *)screenChars { 258 | return &_screenChars; 259 | } 260 | 261 | - (void)preInitializeScreenChars { 262 | // TODO: Expand this beyond just ascii characters. 263 | if (_asciiData.length > kStaticScreenCharsCount) { 264 | _screenChars.buffer = calloc(_asciiData.length, sizeof(screen_char_t)); 265 | } else { 266 | _screenChars.buffer = _screenChars.staticBuffer; 267 | memset(_screenChars.buffer, 0, _asciiData.length * sizeof(screen_char_t)); 268 | } 269 | for (int i = 0; i < _asciiData.length; i++) { 270 | _screenChars.buffer[i].code = _asciiData.buffer[i]; 271 | } 272 | _screenChars.length = _asciiData.length; 273 | _asciiData.screenChars = &_screenChars; 274 | } 275 | 276 | @end 277 | -------------------------------------------------------------------------------- /NewTermMarzipan/VT100Types.h: -------------------------------------------------------------------------------- 1 | // VT100Types.h 2 | // MobileTerminal 3 | // 4 | // This header file contains types that are used by both the low level VT100 5 | // components and the higher level text view components so they both do not 6 | // have to depend on each other. 7 | 8 | #import "ScreenChar.h" 9 | 10 | @class FontMetrics; 11 | 12 | // Buffer space used to draw any particular row. 13 | static const int kMaxRowBufferSize = 200; 14 | 15 | typedef struct { 16 | int width; 17 | int height; 18 | } ScreenSize; 19 | 20 | typedef struct { 21 | int x; 22 | int y; 23 | } ScreenPosition; 24 | 25 | // The protocol for reading and writing data to the terminal screen 26 | @protocol ScreenBuffer 27 | @required 28 | 29 | // Return the current size of the screen 30 | - (ScreenSize)screenSize; 31 | 32 | // Resize the screen to the specified size. This is a no-op of the new size 33 | // is the same as the existing size. 34 | - (void)setScreenSize:(ScreenSize)size; 35 | 36 | // Return the position of the cursor on the screen 37 | - (ScreenPosition)cursorPosition; 38 | 39 | // Row indexes include scrollback 40 | - (screen_char_t*)bufferForRow:(int)row; 41 | - (int)numberOfRows; 42 | 43 | - (void)readInputStream:(NSData *)data; 44 | 45 | - (void)clearScreen; 46 | 47 | @end 48 | 49 | // A thin protocol for implementing a delegate interface with a single method 50 | // that is invoked when the screen needs to be refreshed because at least some 51 | // portion has become invalidated. 52 | @protocol ScreenBufferRefreshDelegate 53 | @required 54 | - (void)refresh; 55 | - (void)activateBell; 56 | @end 57 | 58 | // Supplies an attributed string for a row of text. In practice this is just 59 | // a string of screen_char_t converted to an attributed string (text color, 60 | // etc). The row index includes space in the scrollback buffer. 61 | @protocol AttributedStringSupplier 62 | - (int)rowCount; 63 | - (NSString *)stringForLine:(int)rowIndex; 64 | - (NSAttributedString *)attributedString; 65 | @end 66 | -------------------------------------------------------------------------------- /NewTermMarzipan/bell-hud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/bell-hud.png -------------------------------------------------------------------------------- /NewTermMarzipan/bell-hud@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/bell-hud@2x.png -------------------------------------------------------------------------------- /NewTermMarzipan/bell-hud@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/bell-hud@3x.png -------------------------------------------------------------------------------- /NewTermMarzipan/charmaps.h: -------------------------------------------------------------------------------- 1 | /* Derived from linux/drivers/char/consolemap.c, GNU GPL:ed */ 2 | 3 | /* 4 | 0 1 2 3 4 5 6 7 8 9 A B C D E F 5 | U+250x ─ ━ │ ┃ ┄ ┅ ┆ ┇ ┈ ┉ ┊ ┋ ┌ ┍ ┎ ┏ 6 | U+251x ┐ ┑ ┒ ┓ └ ┕ ┖ ┗ ┘ ┙ ┚ ┛ ├ ┝ ┞ ┟ 7 | U+252x ┠ ┡ ┢ ┣ ┤ ┥ ┦ ┧ ┨ ┩ ┪ ┫ ┬ ┭ ┮ ┯ 8 | U+253x ┰ ┱ ┲ ┳ ┴ ┵ ┶ ┷ ┸ ┹ ┺ ┻ ┼ ┽ ┾ ┿ 9 | U+254x ╀ ╁ ╂ ╃ ╄ ╅ ╆ ╇ ╈ ╉ ╊ ╋ ╌ ╍ ╎ ╏ 10 | U+255x ═ ║ ╒ ╓ ╔ ╕ ╖ ╗ ╘ ╙ ╚ ╛ ╜ ╝ ╞ ╟ 11 | U+256x ╠ ╡ ╢ ╣ ╤ ╥ ╦ ╧ ╨ ╩ ╪ ╫ ╬ ╭ ╮ ╯ 12 | U+257x ╰ ╱ ╲ ╳ ╴ ╵ ╶ ╷ ╸ ╹ ╺ ╻ ╼ ╽ ╾ ╿ 13 | */ 14 | 15 | #define iTermBoxDrawingCodeMin 0x2500 16 | #define iTermBoxDrawingCodeMax 0x257f 17 | 18 | typedef NS_ENUM(unichar, iTermBoxDrawingCode) { 19 | iTermBoxDrawingCodeLightHorizontal = 0x2500, // ─ 20 | iTermBoxDrawingCodeHeavyHorizontal = 0x2501, // ━ 21 | 22 | iTermBoxDrawingCodeLightVertical = 0x2502, // │ 23 | iTermBoxDrawingCodeHeavyVertical = 0x2503, // ┃ 24 | 25 | iTermBoxDrawingCodeLightTripleDashHorizontal = 0x2504, // ┄ 26 | iTermBoxDrawingCodeHeavyTripleDashHorizontal = 0x2505, // ┅ 27 | 28 | iTermBoxDrawingCodeLightTripleDashVertical = 0x2506, // ┆ 29 | iTermBoxDrawingCodeHeavyTripleDashVertical = 0x2507, // ┇ 30 | 31 | iTermBoxDrawingCodeLightQuadrupleDashHorizontal = 0x2508, // ┈ 32 | iTermBoxDrawingCodeHeavyQuadrupleDashHorizontal = 0x2509, // ┉ 33 | 34 | iTermBoxDrawingCodeLightQuadrupleDashVertical = 0x250A, // ┊ 35 | iTermBoxDrawingCodeHeavyQuadrupleDashVertical = 0x250B, // ┋ 36 | 37 | iTermBoxDrawingCodeLightDownAndRight = 0x250C, // ┌ 38 | iTermBoxDrawingCodeDownLightAndRightHeavy = 0x250D, // ┍ 39 | iTermBoxDrawingCodeDownHeavyAndRightLight = 0x250E, // ┎ 40 | iTermBoxDrawingCodeHeavyDownAndRight = 0x250F, // ┏ 41 | 42 | iTermBoxDrawingCodeLightDownAndLeft = 0x2510, // ┐ 43 | iTermBoxDrawingCodeDownLightAndLeftHeavy = 0x2511, // ┑ 44 | iTermBoxDrawingCodeDownHeavyAndLeftLight = 0x2512, // ┒ 45 | iTermBoxDrawingCodeHeavyDownAndLeft = 0x2513, // ┓ 46 | 47 | iTermBoxDrawingCodeLightUpAndRight = 0x2514, // └ 48 | iTermBoxDrawingCodeUpLightAndRightHeavy = 0x2515, // ┕ 49 | iTermBoxDrawingCodeUpHeavyAndRightLight = 0x2516, // ┖ 50 | iTermBoxDrawingCodeHeavyUpAndRight = 0x2517, // ┗ 51 | 52 | iTermBoxDrawingCodeLightUpAndLeft = 0x2518, // ┘ 53 | iTermBoxDrawingCodeUpLightAndLeftHeavy = 0x2519, // ┙ 54 | iTermBoxDrawingCodeUpHeavyAndLeftLight = 0x251A, // ┚ 55 | iTermBoxDrawingCodeHeavyUpAndLeft = 0x251B, // ┛ 56 | 57 | iTermBoxDrawingCodeLightVerticalAndRight = 0x251C, // ├ 58 | iTermBoxDrawingCodeVerticalLightAndRightHeavy = 0x251D, // ┝ 59 | iTermBoxDrawingCodeUpHeavyAndRightDownLight = 0x251E, // ┞ 60 | iTermBoxDrawingCodeDownHeavyAndRightUpLight = 0x251F, // ┟ 61 | iTermBoxDrawingCodeVerticalHeavyAndRightLight = 0x2520, // ┠ 62 | iTermBoxDrawingCodeDownLightAndRightUpHeavy = 0x2521, // ┡ 63 | iTermBoxDrawingCodeUpLightAndRightDownHeavy = 0x2522, // ┢ 64 | iTermBoxDrawingCodeHeavyVerticalAndRight = 0x2523, // ┣ 65 | 66 | iTermBoxDrawingCodeLightVerticalAndLeft = 0x2524, // ┤ 67 | iTermBoxDrawingCodeVerticalLightAndLeftHeavy = 0x2525, // ┥ 68 | iTermBoxDrawingCodeUpHeavyAndLeftDownLight = 0x2526, // ┦ 69 | iTermBoxDrawingCodeDownHeavyAndLeftUpLight = 0x2527, // ┧ 70 | iTermBoxDrawingCodeVerticalHeavyAndLeftLight = 0x2528, // ┨ 71 | iTermBoxDrawingCodeDownLightAndLeftUpHeavy = 0x2529, // ┩ 72 | iTermBoxDrawingCodeUpLightAndLeftDownHeavy = 0x252A, // ┪ 73 | iTermBoxDrawingCodeHeavyVerticalAndLeft = 0x252B, // ┫ 74 | 75 | iTermBoxDrawingCodeLightDownAndHorizontal = 0x252C, // ┬ 76 | iTermBoxDrawingCodeLeftHeavyAndRightDownLight = 0x252D, // ┭ 77 | iTermBoxDrawingCodeRightHeavyAndLeftDownLight = 0x252E, // ┮ 78 | iTermBoxDrawingCodeDownLightAndHorizontalHeavy = 0x252F, // ┯ 79 | iTermBoxDrawingCodeDownHeavyAndHorizontalLight = 0x2530, // ┰ 80 | iTermBoxDrawingCodeRightLightAndLeftDownHeavy = 0x2531, // ┱ 81 | iTermBoxDrawingCodeLeftLightAndRightDownHeavy = 0x2532, // ┲ 82 | iTermBoxDrawingCodeHeavyDownAndHorizontal = 0x2533, // ┳ 83 | 84 | iTermBoxDrawingCodeLightUpAndHorizontal = 0x2534, // ┴ 85 | iTermBoxDrawingCodeLeftHeavyAndRightUpLight = 0x2535, // ┵ 86 | iTermBoxDrawingCodeRightHeavyAndLeftUpLight = 0x2536, // ┶ 87 | iTermBoxDrawingCodeUpLightAndHorizontalHeavy = 0x2537, // ┷ 88 | iTermBoxDrawingCodeUpHeavyAndHorizontalLight = 0x2538, // ┸ 89 | iTermBoxDrawingCodeRightLightAndLeftUpHeavy = 0x2539, // ┹ 90 | iTermBoxDrawingCodeLeftLightAndRightUpHeavy = 0x253A, // ┺ 91 | iTermBoxDrawingCodeHeavyUpAndHorizontal = 0x253B, // ┻ 92 | 93 | iTermBoxDrawingCodeLightVerticalAndHorizontal = 0x253C, // ┼ 94 | iTermBoxDrawingCodeLeftHeavyAndRightVerticalLight = 0x253D, // ┽ 95 | iTermBoxDrawingCodeRightHeavyAndLeftVerticalLight = 0x253E, // ┾ 96 | iTermBoxDrawingCodeVerticalLightAndHorizontalHeavy = 0x253F, // ┿ 97 | iTermBoxDrawingCodeUpHeavyAndDownHorizontalLight = 0x2540, // ╀ 98 | iTermBoxDrawingCodeDownHeavyAndUpHorizontalLight = 0x2541, // ╁ 99 | iTermBoxDrawingCodeVerticalHeavyAndHorizontalLight = 0x2542, // ╂ 100 | iTermBoxDrawingCodeLeftUpHeavyAndRightDownLight = 0x2543, // ╃ 101 | iTermBoxDrawingCodeRightUpHeavyAndLeftDownLight = 0x2544, // ╄ 102 | iTermBoxDrawingCodeLeftDownHeavyAndRightUpLight = 0x2545, // ╅ 103 | iTermBoxDrawingCodeRightDownHeavyAndLeftUpLight = 0x2546, // ╆ 104 | iTermBoxDrawingCodeDownLightAndUpHorizontalHeavy = 0x2547, // ╇ 105 | iTermBoxDrawingCodeUpLightAndDownHorizontalHeavy = 0x2548, // ╈ 106 | iTermBoxDrawingCodeRightLightAndLeftVerticalHeavy = 0x2549, // ╉ 107 | iTermBoxDrawingCodeLeftLightAndRightVerticalHeavy = 0x254A, // ╊ 108 | iTermBoxDrawingCodeHeavyVerticalAndHorizontal = 0x254B, // ╋ 109 | 110 | iTermBoxDrawingCodeLightDoubleDashHorizontal = 0x254C, // ╌ 111 | iTermBoxDrawingCodeHeavyDoubleDashHorizontal = 0x254D, // ╍ 112 | 113 | iTermBoxDrawingCodeLightDoubleDashVertical = 0x254E, // ╎ 114 | iTermBoxDrawingCodeHeavyDoubleDashVertical = 0x254F, // ╏ 115 | 116 | iTermBoxDrawingCodeDoubleHorizontal = 0x2550, // ═ 117 | 118 | iTermBoxDrawingCodeDoubleVertical = 0x2551, // ║ 119 | 120 | iTermBoxDrawingCodeDownSingleAndRightDouble = 0x2552, // ╒ 121 | iTermBoxDrawingCodeDownDoubleAndRightSingle = 0x2553, // ╓ 122 | iTermBoxDrawingCodeDoubleDownAndRight = 0x2554, // ╔ 123 | 124 | iTermBoxDrawingCodeDownSingleAndLeftDouble = 0x2555, // ╕ 125 | iTermBoxDrawingCodeDownDoubleAndLeftSingle = 0x2556, // ╖ 126 | iTermBoxDrawingCodeDoubleDownAndLeft = 0x2557, // ╗ 127 | 128 | iTermBoxDrawingCodeUpSingleAndRightDouble = 0x2558, // ╘ 129 | iTermBoxDrawingCodeUpDoubleAndRightSingle = 0x2559, // ╙ 130 | iTermBoxDrawingCodeDoubleUpAndRight = 0x255A, // ╚ 131 | 132 | iTermBoxDrawingCodeUpSingleAndLeftDouble = 0x255B, // ╛ 133 | iTermBoxDrawingCodeUpDoubleAndLeftSingle = 0x255C, // ╜ 134 | iTermBoxDrawingCodeDoubleUpAndLeft = 0x255D, // ╝ 135 | 136 | iTermBoxDrawingCodeVerticalSingleAndRightDouble = 0x255E, // ╞ 137 | iTermBoxDrawingCodeVerticalDoubleAndRightSingle = 0x255F, // ╟ 138 | iTermBoxDrawingCodeDoubleVerticalAndRight = 0x2560, // ╠ 139 | 140 | iTermBoxDrawingCodeVerticalSingleAndLeftDouble = 0x2561, // ╡ 141 | iTermBoxDrawingCodeVerticalDoubleAndLeftSingle = 0x2562, // ╢ 142 | iTermBoxDrawingCodeDoubleVerticalAndLeft = 0x2563, // ╣ 143 | 144 | iTermBoxDrawingCodeDownSingleAndHorizontalDouble = 0x2564, // ╤ 145 | iTermBoxDrawingCodeDownDoubleAndHorizontalSingle = 0x2565, // ╥ 146 | iTermBoxDrawingCodeDoubleDownAndHorizontal = 0x2566, // ╦ 147 | 148 | iTermBoxDrawingCodeUpSingleAndHorizontalDouble = 0x2567, // ╧ 149 | iTermBoxDrawingCodeUpDoubleAndHorizontalSingle = 0x2568, // ╨ 150 | iTermBoxDrawingCodeDoubleUpAndHorizontal = 0x2569, // ╩ 151 | 152 | iTermBoxDrawingCodeVerticalSingleAndHorizontalDouble = 0x256A, // ╪ 153 | iTermBoxDrawingCodeVerticalDoubleAndHorizontalSingle = 0x256B, // ╫ 154 | iTermBoxDrawingCodeDoubleVerticalAndHorizontal = 0x256C, // ╬ 155 | 156 | iTermBoxDrawingCodeLightArcDownAndRight = 0x256D, // ╭ 157 | iTermBoxDrawingCodeLightArcDownAndLeft = 0x256E, // ╮ 158 | iTermBoxDrawingCodeLightArcUpAndLeft = 0x256F, // ╯ 159 | iTermBoxDrawingCodeLightArcUpAndRight = 0x2570, // ╰ 160 | 161 | iTermBoxDrawingCodeLightDiagonalUpperRightToLowerLeft = 0x2571, // ╱ 162 | 163 | iTermBoxDrawingCodeLightDiagonalUpperLeftToLowerRight = 0x2572, // ╲ 164 | 165 | iTermBoxDrawingCodeLightDiagonalCross = 0x2573, // ╳ 166 | 167 | iTermBoxDrawingCodeLightLeft = 0x2574, // ╴ 168 | 169 | iTermBoxDrawingCodeLightUp = 0x2575, // ╵ 170 | 171 | iTermBoxDrawingCodeLightRight = 0x2576, // ╶ 172 | 173 | iTermBoxDrawingCodeLightDown = 0x2577, // ╷ 174 | 175 | iTermBoxDrawingCodeHeavyLeft = 0x2578, // ╸ 176 | 177 | iTermBoxDrawingCodeHeavyUp = 0x2579, // ╹ 178 | 179 | iTermBoxDrawingCodeHeavyRight = 0x257A, // ╺ 180 | 181 | iTermBoxDrawingCodeHeavyDown = 0x257B, // ╻ 182 | 183 | iTermBoxDrawingCodeLightLeftAndHeavyRight = 0x257C, // ╼ 184 | 185 | iTermBoxDrawingCodeLightUpAndHeavyDown = 0x257D, // ╽ 186 | 187 | iTermBoxDrawingCodeHeavyLeftAndLightRight = 0x257E, // ╾ 188 | 189 | iTermBoxDrawingCodeHeavyUpAndLightDown = 0x257F, // ╿ 190 | }; 191 | 192 | // Defines a mapping from ascii characters to their Unicode graphical equivalent. Used in line- 193 | // drawing mode. 194 | static const unichar charmap[256]={ 195 | 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 196 | 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 197 | 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 198 | 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 199 | 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 200 | 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 201 | 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 202 | 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 203 | 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 204 | 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 205 | 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 206 | 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x00a0, 207 | 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 208 | 0x2424, 0x240b, 209 | iTermBoxDrawingCodeLightUpAndLeft, 210 | iTermBoxDrawingCodeLightDownAndLeft, 211 | iTermBoxDrawingCodeLightDownAndRight, 212 | iTermBoxDrawingCodeLightUpAndRight, 213 | iTermBoxDrawingCodeLightVerticalAndHorizontal, 214 | 0x23ba, 0x23bb, 215 | iTermBoxDrawingCodeLightHorizontal, 216 | 0x23bc, 0x23bd, 217 | iTermBoxDrawingCodeLightVerticalAndRight, 218 | iTermBoxDrawingCodeLightVerticalAndLeft, 219 | iTermBoxDrawingCodeLightUpAndHorizontal, 220 | iTermBoxDrawingCodeLightDownAndHorizontal, 221 | iTermBoxDrawingCodeLightVertical, 222 | 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x007f, 223 | 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 224 | 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 225 | 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 226 | 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 227 | 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 228 | 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 229 | 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 230 | 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 231 | 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 232 | 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 233 | 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 234 | 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 235 | 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 236 | 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 237 | 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 238 | 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff 239 | }; 240 | 241 | -------------------------------------------------------------------------------- /NewTermMarzipan/colortest.txt: -------------------------------------------------------------------------------- 1 | iPhone:/System/Library mobile$ ls --color CoreServices | head -5 2 | AppleIDAuthAgent* 3 | AssistiveTouch.app/ 4 | AuthBrokerAgent@ 5 | CFNetworkAgent@ 6 | CacheDeleteAppContainerCaches* 7 | 8 | System colors: 9 |          10 |          11 | 12 | 6x6x6 color cube: 13 |                      14 |                      15 |                      16 |                      17 |                      18 |                      19 |                      20 |                      21 |                      22 |                      23 |                      24 |                      25 | 26 | Greyscale ramp: 27 |              28 |              29 | -------------------------------------------------------------------------------- /NewTermMarzipan/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /NewTermMarzipan/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | TERMINAL 7 | Terminal 8 | 9 | 10 | SETTINGS 11 | Settings 12 | 13 | 14 | TERMINAL_LAUNCH_FAILED 15 | Couldn’t start the terminal subprocess because an error occurred. 16 | 17 | 18 | PROCESS_COMPLETED_TITLE 19 | Process completed 20 | 21 | 22 | PROCESS_COMPLETED_MESSAGE 23 | Press any key to close the tab. 24 | 25 | 26 | NEW_TAB 27 | New Tab 28 | 29 | 30 | CLOSE_TAB 31 | Close Tab 32 | 33 | 34 | -------------------------------------------------------------------------------- /NewTermMarzipan/iTermParser.h: -------------------------------------------------------------------------------- 1 | // 2 | // iTermParser.h 3 | // iTerm2 4 | // 5 | // Created by George Nachman on 1/5/15. 6 | // 7 | // Utilities for parsing escape codes. 8 | 9 | typedef struct { 10 | // Pointer to next character to read. 11 | unsigned char *datap; 12 | // Number of valid bytes starting at datap. 13 | int datalen; 14 | // Number of bytes already consumed. Subtract this from datap to get the original value of datap. 15 | int rmlen; 16 | } iTermParserContext; 17 | 18 | NS_INLINE iTermParserContext iTermParserContextMake(unsigned char *datap, int length) { 19 | iTermParserContext context = { 20 | .datap = datap, 21 | .datalen = length, 22 | .rmlen = 0 23 | }; 24 | return context; 25 | } 26 | 27 | NS_INLINE BOOL iTermParserCanAdvance(iTermParserContext *context) { 28 | return context->datalen > 0; 29 | } 30 | 31 | NS_INLINE unsigned char iTermParserPeek(iTermParserContext *context) { 32 | return context->datap[0]; 33 | } 34 | 35 | NS_INLINE BOOL iTermParserTryPeek(iTermParserContext *context, unsigned char *c) { 36 | if (iTermParserCanAdvance(context)) { 37 | *c = iTermParserPeek(context); 38 | return YES; 39 | } else { 40 | return NO; 41 | } 42 | } 43 | 44 | NS_INLINE void iTermParserAdvance(iTermParserContext *context) { 45 | context->datap++; 46 | context->datalen--; 47 | context->rmlen++; 48 | } 49 | 50 | NS_INLINE void iTermParserAdvanceMultiple(iTermParserContext *context, int n) { 51 | assert(context->datalen >= n); 52 | context->datap += n; 53 | context->datalen -= n; 54 | context->rmlen += n; 55 | } 56 | 57 | NS_INLINE BOOL iTermParserTryAdvance(iTermParserContext *context) { 58 | if (!iTermParserCanAdvance(context)) { 59 | return NO; 60 | } 61 | iTermParserAdvance(context); 62 | return YES; 63 | } 64 | 65 | NS_INLINE NSInteger iTermParserNumberOfBytesConsumed(iTermParserContext *context) { 66 | return context->rmlen; 67 | } 68 | 69 | // Only safe to call if iTermParserCanAdvance returns YES. 70 | NS_INLINE unsigned char iTermParserConsume(iTermParserContext *context) { 71 | unsigned char c = context->datap[0]; 72 | iTermParserAdvance(context); 73 | return c; 74 | } 75 | 76 | NS_INLINE BOOL iTermParserTryConsume(iTermParserContext *context, unsigned char *c) { 77 | if (!iTermParserCanAdvance(context)) { 78 | return NO; 79 | } 80 | *c = iTermParserConsume(context); 81 | return YES; 82 | } 83 | 84 | NS_INLINE void iTermParserConsumeOrDie(iTermParserContext *context, unsigned char expected) { 85 | unsigned char actual; 86 | BOOL consumedOk = iTermParserTryConsume(context, &actual); 87 | 88 | assert(consumedOk); 89 | assert(actual == expected); 90 | } 91 | 92 | NS_INLINE void iTermParserBacktrackBy(iTermParserContext *context, int n) { 93 | context->datap -= n; 94 | context->datalen += n; 95 | context->rmlen -= n; 96 | } 97 | 98 | NS_INLINE void iTermParserBacktrack(iTermParserContext *context) { 99 | iTermParserBacktrackBy(context, context->rmlen); 100 | } 101 | 102 | NS_INLINE int iTermParserNumberOfBytesUntilCharacter(iTermParserContext *context, char c) { 103 | unsigned char *pointer = memchr(context->datap, '\n', context->datalen); 104 | if (!pointer) { 105 | return -1; 106 | } else { 107 | return (int)(pointer - context->datap); 108 | } 109 | } 110 | 111 | NS_INLINE int iTermParserLength(iTermParserContext *context) { 112 | return context->datalen; 113 | } 114 | 115 | NS_INLINE unsigned char *iTermParserPeekRawBytes(iTermParserContext *context, int length) { 116 | if (context->datalen < length) { 117 | return NULL; 118 | } else { 119 | return context->datap; 120 | } 121 | } 122 | 123 | // Returns YES if any digits were found, NO if the first character was not a digit. |n| must be a 124 | // valid pointer. It will be filled in with the integer at the start of the context and the context 125 | // will be advanced to the end of the integer. 126 | NS_INLINE BOOL iTermParserConsumeInteger(iTermParserContext *context, int *n) { 127 | int numDigits = 0; 128 | *n = 0; 129 | unsigned char c; 130 | while (iTermParserCanAdvance(context) && 131 | isdigit((c = iTermParserPeek(context)))) { 132 | ++numDigits; 133 | *n *= 10; 134 | *n += (c - '0'); 135 | iTermParserAdvance(context); 136 | } 137 | 138 | return numDigits > 0; 139 | } 140 | 141 | #pragma mark - CSI 142 | 143 | #define VT100CSIPARAM_MAX 16 // Maximum number of CSI parameters in VT100Token.csi->p. 144 | #define VT100CSISUBPARAM_MAX 16 // Maximum number of CSI sub-parameters in VT100Token.csi->p. 145 | 146 | typedef struct { 147 | // Integer parameters. The first |count| elements are valid. -1 means the value is unset; set 148 | // values are always nonnegative. 149 | int p[VT100CSIPARAM_MAX]; 150 | 151 | // Number of defined values in |p|. 152 | int count; 153 | 154 | // An integer that holds a packed representation of the prefix byte, intermediate byte, and 155 | // final byte. 156 | int32_t cmd; 157 | 158 | BOOL question; 159 | int modifier; 160 | 161 | // Sub-parameters. 162 | int sub[VT100CSIPARAM_MAX][VT100CSISUBPARAM_MAX]; 163 | 164 | // Number of subparameters for each parameter. 165 | int subCount[VT100CSIPARAM_MAX]; 166 | } CSIParam; 167 | 168 | // If the n'th parameter has a negative (default) value, replace it with |value|. 169 | // CSI parameter values are all initialized to -1 before parsing, so this has the effect of setting 170 | // a value iff it hasn't already been set. 171 | // If there aren't yet n+1 parameters, increase the count to n+1. 172 | NS_INLINE void iTermParserSetCSIParameterIfDefault(CSIParam *csiParam, int n, int value) { 173 | csiParam->p[n] = csiParam->p[n] < 0 ? value : csiParam->p[n]; 174 | csiParam->count = MAX(csiParam->count, n + 1); 175 | } 176 | -------------------------------------------------------------------------------- /NewTermMarzipan/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // NewTerm 4 | // 5 | // Created by Adam D on 20/07/2014. 6 | // Copyright (c) 2014 HASHBANG Productions. All rights reserved. 7 | // 8 | 9 | #import "HBNTAppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass(HBNTAppDelegate.class)); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /NewTermMarzipan/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/settings.png -------------------------------------------------------------------------------- /NewTermMarzipan/settings@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/settings@2x.png -------------------------------------------------------------------------------- /NewTermMarzipan/settings@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/NewTermMarzipan/settings@3x.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Experimenting with iOSMac (Marzipan) 2 | This repo accompanies my blog post [**A quick look at UIKit on macOS**](https://kirb.me/2018/06/07/iosmac-research.html), where I investigate the iOSMac (aka UIKit on Mac, aka Marzipan) system included with macOS Mojave. This is a proof-of-concept of my iOS app [NewTerm](https://github.com/hbang/NewTerm) (somewhat) running on macOS using iOSMac. This repo is very messy and iOSMac is very crashy, but it serves as a proof-of-concept that an app really can be ported from iOS to macOS. 3 | 4 | ## Building 5 | Most importantly, you need to be on macOS Mojave (the current developer beta release) with Xcode 10 (also beta), disable System Integrity Protection (Google for a guide), as well as AppleMobileFileIntegrity (AMFI) using `sudo nvram boot-args='amfi_get_out_of_my_way=0x1'`. This is of course disabling a lot of macOS’s security functionality and you should only do this on a test installation of macOS (you’re not running a beta OS as your daily driver, right‽), and re-enable it all once you’re done. Keep in mind NVRAM is global and affects all installations of macOS on the computer, and SIP configuration is kept in NVRAM. 6 | 7 | I haven’t included UIKit headers, so you’ll need to take them from the iOS SDK and patch them to shut up availability warnings: 8 | 9 | ```shell 10 | cp -r /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework/Headers/*.h ~/Downloads/iOSMac-Demo/UIKit/ 11 | cd ~/Downloads/iOSMac-Demo/UIKit/ 12 | 13 | for i in *.h; do 14 | sed -Ei s/'NS_(|CLASS_|ENUM_)AVAILABLE_IOS\([0-9]+_[0-9]\)'//g "$i" 15 | done 16 | ``` 17 | 18 | Open NewTerm.xcodeproj, select the NewTerm project file at the top of the file list, and ensure the signing team is set to yourself (a free developer account works). Then click Run and have fun! 19 | 20 | **Keep in mind** iOSMac is very likely to crash or hang the window server. This project currently causes a hang immediately after. You should enable SSH in the macOS Sharing Preferences so you can SSH in from another machine and run `killall UIKitSystemApp` if you’re in trouble. If you forget or can’t, just hold the power button. There’s no permanent damage — iOSMac is just very unstable right now. 21 | 22 | ## License 23 | This is based on the [NewTerm](https://github.com/hbang/NewTerm) project, licensed under the GNU General Public License, version 2.0. Refer to [LICENSE.md](LICENSE.md). 24 | -------------------------------------------------------------------------------- /UIKit/PUT_PATCHED_HEADERS_HERE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kirb/iOSMac-Demo/742c528fb3280dcdb01b1e71ffe2aceb09245468/UIKit/PUT_PATCHED_HEADERS_HERE --------------------------------------------------------------------------------