12 Commits
r0 ... r2

Author SHA1 Message Date
Kelvin Sherlock
8d282293f0 version bump. 2018-01-29 21:35:26 -05:00
Kelvin Sherlock
c41a520ef8 Merge branch 'master' of https://github.com/ksherlock/TwoTerm 2018-01-29 21:24:51 -05:00
Kelvin Sherlock
42b9206abe replace grand-central io/pid monitoring with a dedicated thread and kevents.
based on testing, the pty EOF is never flagged. Instead, exit the thread when the child pid dies.
2018-01-29 21:24:15 -05:00
Kelvin Sherlock
3542ed50ce fix scroll-right bug (PTSE) 2018-01-29 21:22:08 -05:00
ksherlock
b25935338e Create README.md 2018-01-27 14:43:44 -05:00
Kelvin Sherlock
6299c68117 pics 2018-01-27 14:42:24 -05:00
Kelvin Sherlock
0044693288 pics 2018-01-27 14:34:02 -05:00
Kelvin Sherlock
4f1e79178f use ragel version of gno console emulator 2018-01-27 13:26:15 -05:00
Kelvin Sherlock
38dad13969 dispatch_io_create() is supposed to be the correct way to read from a stream. However, it doesn't seem to work and I believe it's due to poll() not working with pseudo terminals in OS X.
Leaving the code in just for fun.

Underlying issue - when the pty closes, the block doesn't always get notified so the fd doesn't close until the window closes.
2018-01-27 08:59:40 -05:00
Kelvin Sherlock
a6408fc242 in high sierra, estimated read size always seems to be 0. handle that situation. 2018-01-18 15:43:13 -05:00
Kelvin Sherlock
de8810b8e2 name edits. 2017-11-30 14:43:04 -05:00
Kelvin Sherlock
57021b2cba project update. 2017-11-30 14:42:48 -05:00
12 changed files with 141 additions and 132 deletions

View File

@@ -357,7 +357,7 @@
screen->eraseRect(tmp);
}
// hmmm ... embedded control characters seem to be processed as control characters...
# hmmm ... embedded control characters seem to be processed as control characters...
| 0x1e arg1 arg2 ${
// CTRL('^'):
// goto x y
@@ -390,9 +390,6 @@
@implementation GNOConsole
+(void)load
{
[EmulatorManager registerClass: self];

View File

@@ -178,7 +178,7 @@ static void advance(Screen *screen, gsos_context &ctx) {
*/
if (cursor.y < window.maxY()-1) cursor.y++;
else if (_context.consScroll)
screen->scrollUp(window);
screen->scrollUp(window);
}
| 0x0b ${

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="11762" systemVersion="16C67" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="13529" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="11762"/>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="13529"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -10,13 +11,13 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="2Term" id="56">
<menu key="submenu" title="2Term" systemMenu="apple" id="57">
<menuItem title="TwoTerm" id="56" userLabel="TwoTerm">
<menu key="submenu" title="TwoTerm" systemMenu="apple" id="57">
<items>
<menuItem title="About 2Term" id="58">
<menuItem title="About TwoTerm" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
@@ -35,7 +36,7 @@
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide 2Term" keyEquivalent="h" id="134">
<menuItem title="Hide TwoTerm" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
@@ -54,7 +55,7 @@
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit 2Term" keyEquivalent="q" id="136">
<menuItem title="Quit TwoTerm" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
@@ -648,7 +649,7 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="491">
<items>
<menuItem title="2Term Help" keyEquivalent="?" id="492">
<menuItem title="TwoTerm Help" keyEquivalent="?" id="492">
<connections>
<action selector="showHelp:" target="-1" id="493"/>
</connections>

View File

@@ -21,7 +21,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<string>2</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>

12
README.md Normal file
View File

@@ -0,0 +1,12 @@
# TwoTerm
Apple-II inspired terminal emulator for OS X
Uses the Apple II character generator font but also emulates the GS/OS Console, GNO/ME Console, etc.
You'll probably need a [termcap](https://github.com/ksherlock/a2-terminfo) entry.
![](screenshots/vim.png?raw=true)
![](screenshots/cal.png?raw=true)

View File

@@ -7,7 +7,7 @@
//
#import <Cocoa/Cocoa.h>
#include <atomic>
@class EmulatorView;
@class ColorView;
@@ -25,11 +25,9 @@
NSObject <Emulator> *_emulator;
NSThread * _thread;
int _fd;
pid_t _pid;
dispatch_source_t _read_source;
dispatch_source_t _wait_source;
std::atomic<pid_t> _pid;
}

View File

@@ -11,8 +11,11 @@
#import "CurveView.h"
#import "EmulatorWindow.h"
//#import "VT52.h"
//#import "PTSE.h"
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <atomic>
#import "Defaults.h"
@@ -48,7 +51,7 @@
[_colorView release];
[_parameters release];
[_thread release];
[super dealloc];
}
@@ -107,7 +110,7 @@
std::string s;
s.append("TERM_PROGRAM=2Term");
s.append("TERM_PROGRAM=TwoTerm");
s.append(1, (char)0);
s.append("LANG=C");
@@ -174,115 +177,120 @@
[_emulatorView setFd: _fd];
[self monitor];
/*
if (!_childMonitor)
{
_childMonitor = [ChildMonitor new];
[_childMonitor setDelegate: _emulatorView];
}
-(BOOL)read: (int)fd {
BOOL rv = NO;
for(;;) {
uint8_t buffer[1024];
ssize_t size = read(fd, buffer, sizeof(buffer));
if (size < 0 && errno == EINTR) continue;
if (size <= 0) break;
[_emulatorView processData: buffer size: size];
rv = YES;
}
*/
return rv;
}
-(int)wait: (pid_t)pid {
std::atomic_exchange(&_pid, -1);
int status = 0;
for(;;) {
int ok = waitpid(pid, &status, WNOHANG);
if (ok >= 0) break;
if (errno == EINTR) continue;
NSLog(@"waitpid(%d): %s", pid, strerror(errno));
break;
}
return status;
}
-(void)monitor {
int fd = _fd;
int pid = _pid;
pid_t pid = _pid;
int q = kqueue();
struct kevent events[2] = {};
EV_SET(&events[0], pid, EVFILT_PROC, EV_ADD | EV_RECEIPT, NOTE_EXIT | NOTE_EXITSTATUS, 0, NULL);
EV_SET(&events[1], fd, EVFILT_READ, EV_ADD | EV_RECEIPT, 0, 0, NULL);
int flags;
// non-blocking io.
if (fcntl(_fd, F_GETFL, &flags) < 0) flags = 0;
fcntl(_fd, F_SETFL, flags | O_NONBLOCK);
kevent(q, events, 2, NULL, 0, NULL);
[_emulatorView childBegan];
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
_thread = [[NSThread alloc] initWithBlock: ^(){
struct kevent events[2] = {};
_wait_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,
pid, DISPATCH_PROC_EXIT, queue);
if (_wait_source)
{
bool stop = false;
int status = 0;
while (!stop) {
dispatch_source_set_event_handler(_wait_source, ^{
int status = 0;
int ok;
for(;;) {
ok = waitpid(pid, &status, WNOHANG);
if (ok >= 0) break;
if (errno == EINTR) continue;
int n = kevent(q, NULL, 0, events, 2, NULL);
if (n <= 0) {
NSLog(@"kevent");
break;
}
_pid = 0;
//dispatch_async(dispatch_get_main_queue(), ^(){
[_emulatorView childFinished: status];
//});
dispatch_source_cancel(_wait_source);
dispatch_release(_wait_source);
_wait_source = nullptr;
});
dispatch_resume(_wait_source);
}
_read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
fd, 0, queue);
if (_read_source)
{
// Install the event handler
dispatch_source_set_event_handler(_read_source, ^{
static uint8_t sbuffer[1024];
size_t estimated = dispatch_source_get_data(_read_source);
uint8_t *buffer = estimated > sizeof(sbuffer) ? (uint8_t *)malloc(estimated) : sbuffer;
if (buffer)
{
ssize_t actual;
for (;;) {
actual = read(fd, buffer, (estimated));
if (actual < 0) {
if (errno == EINTR) continue;
if (errno == EAGAIN) {
if (buffer != sbuffer) free(buffer);
return;
}
NSLog(@"read: %s", strerror(errno));
dispatch_source_cancel(_read_source);
dispatch_release(_read_source);
_read_source = nullptr;
for (unsigned i = 0; i < n; ++i) {
const auto &e = events[i];
unsigned flags = e.flags;
if (e.filter == EVFILT_READ) {
int fd = (int)e.ident;
if (flags & EV_EOF) {
NSLog(@"EV_EOF");
}
break;
if (flags & EV_ERROR) {
NSLog(@"EV_ERROR");
}
[self read: fd];
continue;
}
if (actual > 0) [_emulatorView processData: buffer size: actual];
if (e.filter == EVFILT_PROC) {
if (buffer != sbuffer) free(buffer);
if (actual == 0) {
NSLog(@"closing fd");
dispatch_source_cancel(_read_source);
dispatch_release(_read_source);
_read_source = nullptr;
pid_t pid = (pid_t)e.ident;
NSLog(@"Child finished");
status = [self wait: pid];
stop = true;
}
}
});
}
if (![_thread isCancelled]) {
dispatch_source_set_cancel_handler(_read_source, ^{
_fd = -1;
[_emulatorView setFd: -1];
close(fd);
});
dispatch_resume(_read_source);
}
// read any lingering io...
[self read: fd];
[_emulatorView childFinished: status];
}
close(q);
close(fd);
_fd = -1;
//NSLog(@"Closing fd");
}];
[_thread start];
}
#pragma mark -
@@ -352,28 +360,11 @@
-(void)windowWillClose:(NSNotification *)notification
{
pid_t pid = std::atomic_exchange(&_pid, -1);
[_thread cancel];
if (_wait_source) {
dispatch_source_cancel(_wait_source);
dispatch_release(_wait_source);
}
if (_read_source) {
dispatch_source_cancel(_read_source);
dispatch_release(_read_source);
}
int status;
int ok;
if (_pid) {
kill(_pid, 9);
for(;;) {
ok = waitpid(_pid, &status, 0);
if (ok >= 0) break;
if (errno == EINTR) continue;
perror("waitpid: ");
break;
}
if (pid > 0) {
kill(pid, 9);
}
[self autorelease];

View File

@@ -33,6 +33,8 @@
B61EF7D81482FB6D008C1891 /* titlebar-left.png in Resources */ = {isa = PBXBuildFile; fileRef = B61EF7D51482FB6D008C1891 /* titlebar-left.png */; };
B61EF7D91482FB6D008C1891 /* titlebar-right.png in Resources */ = {isa = PBXBuildFile; fileRef = B61EF7D61482FB6D008C1891 /* titlebar-right.png */; };
B638188214A179D60027D007 /* ColorView.m in Sources */ = {isa = PBXBuildFile; fileRef = B638188114A179D60027D007 /* ColorView.m */; };
B6407804201CE8BD00D3F2D1 /* GNOConsole.mm.ragel in Resources */ = {isa = PBXBuildFile; fileRef = B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */; };
B6407805201CE93500D3F2D1 /* GNOConsole.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B612F45B12DD5DF1005D1B77 /* GNOConsole.mm.ragel */; };
B66412391480A070003BC8D3 /* EmulatorWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = B66412381480A070003BC8D3 /* EmulatorWindow.m */; };
B675F4A81E540394004B0D9C /* Screen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B612F44D12DD5DAD005D1B77 /* Screen.cpp */; };
B675F4A91E561D20004B0D9C /* Apple80.mm.ragel in Sources */ = {isa = PBXBuildFile; fileRef = B612F45712DD5DF1005D1B77 /* Apple80.mm.ragel */; };
@@ -43,7 +45,6 @@
B67B3CE612B6FA040033AE07 /* a2-charset-80.png in Resources */ = {isa = PBXBuildFile; fileRef = B67B3CE412B6FA040033AE07 /* a2-charset-80.png */; };
B6801BD912EB549300B22E9E /* vt100-charset.png in Resources */ = {isa = PBXBuildFile; fileRef = B6801BD812EB549300B22E9E /* vt100-charset.png */; };
B68E632A12FF909D00EAFF5F /* ExampleView.m in Sources */ = {isa = PBXBuildFile; fileRef = B68E632912FF909C00EAFF5F /* ExampleView.m */; };
B6ACA2AC1E5C8BEC000E774B /* GNOConsole.mm in Sources */ = {isa = PBXBuildFile; fileRef = B6ACA2AB1E5C8BEC000E774B /* GNOConsole.mm */; };
B6ACA2AD1E614E38000E774B /* VT52.mm in Sources */ = {isa = PBXBuildFile; fileRef = B612F46312DD5DF1005D1B77 /* VT52.mm */; };
B6ACA2AF1E635CEC000E774B /* vt52-charset.png in Resources */ = {isa = PBXBuildFile; fileRef = B6ACA2AE1E635CEC000E774B /* vt52-charset.png */; };
B6C704EF15CCC64100CC0401 /* titlebar-center@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B6C704EC15CCC64100CC0401 /* titlebar-center@2x.png */; };
@@ -62,6 +63,7 @@
isEditable = 1;
outputFiles = (
);
script = "";
};
B6C173931D32A8840024E360 /* PBXBuildRule */ = {
isa = PBXBuildRule;
@@ -423,6 +425,7 @@
B676065111DEBAE900D6B66C /* TermWindow.xib in Resources */,
B67B3CE512B6FA040033AE07 /* a2-charset-40.png in Resources */,
B67B3CE612B6FA040033AE07 /* a2-charset-80.png in Resources */,
B6407804201CE8BD00D3F2D1 /* GNOConsole.mm.ragel in Resources */,
B6801BD912EB549300B22E9E /* vt100-charset.png in Resources */,
B61EF7C51481561E008C1891 /* titlebar-corner.png in Resources */,
B61EF7C61481561E008C1891 /* titlebar-middle.png in Resources */,
@@ -448,6 +451,7 @@
B675F4A91E561D20004B0D9C /* Apple80.mm.ragel in Sources */,
B675F4AC1E56A7F2004B0D9C /* GSOSConsole.mm.ragel in Sources */,
B6D1CD071E577E7D00C4A6BC /* PTSE.mm.ragel in Sources */,
B6407805201CE93500D3F2D1 /* GNOConsole.mm.ragel in Sources */,
8D11072D0486CEB800E47090 /* main.m in Sources */,
256AC3DA0F4B6AC300CF3369 /* TwoTermAppDelegate.mm in Sources */,
B676063B11DEAD3500D6B66C /* TermWindowController.mm in Sources */,
@@ -456,7 +460,6 @@
B61D0D60125B7ACA001C713B /* NewTerminalWindowController.mm in Sources */,
B61D0D69125B8E06001C713B /* Defaults.m in Sources */,
B6ECFF271D2EEA2B00871A81 /* TextLabel.m in Sources */,
B6ACA2AC1E5C8BEC000E774B /* GNOConsole.mm in Sources */,
B612F45012DD5DAD005D1B77 /* iGeometry.cpp in Sources */,
B612F45112DD5DAD005D1B77 /* Lock.cpp in Sources */,
B612F45212DD5DAD005D1B77 /* OutputChannel.cpp in Sources */,

View File

@@ -31,6 +31,13 @@
NSDictionary *parameters;
CIFilter *filter;
#if 0
struct sigaction sa = {};
sa.sa_handler = SIG_IGN;
sa.sa_flags = SA_RESTART;
sigaction(SIGCHLD, &sa, NULL);
#endif
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver: self selector: @selector(newTerminal:) name: kNotificationNewTerminal object: nil];

View File

@@ -321,7 +321,7 @@ void Screen::scrollRight(iRect rect, int n) {
auto xIter = line.begin() + rect.minX();
auto xEnd = line.begin() + rect.maxX();
auto iter = std::copy(xIter, xEnd-n, xEnd);
auto iter = std::copy_backward(xIter, xEnd-n, xEnd);
while (n--) { --iter; *iter = char_info(); }
}

BIN
screenshots/cal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

BIN
screenshots/vim.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB