Port the editor to non-sortix operating systems.

This commit is contained in:
Jonas 'Sortie' Termansen 2014-07-28 01:30:50 +02:00
parent 63146072a4
commit e5ed21520c
8 changed files with 347 additions and 60 deletions

View File

@ -1,4 +1,3 @@
SOFTWARE_MEANT_FOR_SORTIX=1
include ../build-aux/platform.mak
include ../build-aux/compiler.mak
include ../build-aux/version.mak
@ -10,6 +9,10 @@ CFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
CFLAGS:=$(CFLAGS) -Wall -Wextra
ifeq ($(HOST_IS_SORTIX),0)
CPPFLAGS+=-D_GNU_SOURCE
endif
BINARY=editor
OBJS=\

View File

@ -814,6 +814,11 @@ void editor_type_paste(struct editor* editor)
}
}
void editor_type_suspend(struct editor* editor)
{
editor->suspend_requested = true;
}
void editor_type_character(struct editor* editor, wchar_t c)
{
if ( editor->control )
@ -832,6 +837,7 @@ void editor_type_character(struct editor* editor, wchar_t c)
editor_type_save(editor); break;
case L'v': editor_type_paste(editor); break;
case L'x': editor_type_cut(editor); break;
case L'z': editor_type_suspend(editor); break;
}
return;
}

View File

@ -72,6 +72,7 @@ void editor_type_raw_character(struct editor* editor, wchar_t c);
void editor_type_copy(struct editor* editor);
void editor_type_cut(struct editor* editor);
void editor_type_paste(struct editor* editor);
void editor_type_suspend(struct editor* editor);
void editor_type_character(struct editor* editor, wchar_t c);
#endif

View File

@ -21,7 +21,7 @@
#include <assert.h>
#include <errno.h>
#include <error.h>
#include <err.h>
#include <limits.h>
#include <locale.h>
#include <pwd.h>
@ -91,7 +91,7 @@ void editor_load_config_path(struct editor* editor, const char* path)
if ( !fp )
{
if ( errno != ENOENT )
error(0, errno, "%s", path);
warn("%s", path);
return;
}
char* line = NULL;
@ -106,7 +106,7 @@ void editor_load_config_path(struct editor* editor, const char* path)
editor_modal_command_config(editor, line);
}
if ( ferror(fp) )
error(0, errno, "getline: %s", path);
warn("getline: %s", path);
fclose(fp);
}
@ -119,7 +119,7 @@ void editor_load_config(struct editor* editor)
char* path;
if ( asprintf(&path, "%s/.editor", home) < 0 )
{
error(0, errno, "malloc");
warn("malloc");
return;
}
editor_load_config_path(editor, path);
@ -275,9 +275,9 @@ int main(int argc, char* argv[])
setlocale(LC_ALL, "");
if ( !isatty(0) )
error(1, errno, "standard input");
err(1, "standard input");
if ( !isatty(1) )
error(1, errno, "standard output");
err(1, "standard output");
struct editor editor;
initialize_editor(&editor);
@ -285,7 +285,7 @@ int main(int argc, char* argv[])
editor_load_config(&editor);
if ( 2 <= argc && !editor_load_file(&editor, argv[1]) )
error(1, errno, "`%s'", argv[1]);
err(1, "`%s'", argv[1]);
struct editor_input editor_input;
editor_input_begin(&editor_input);
@ -297,6 +297,14 @@ int main(int argc, char* argv[])
while ( editor.mode != MODE_QUIT )
{
if ( editor.suspend_requested )
{
editor_input_suspend(&editor_input);
editor.suspend_requested = false;
reset_terminal_state(stdout, &stdout_state);
fflush(stdout);
}
struct terminal_state output_state;
make_terminal_state(stdout, &output_state);
editor_colorize(&editor);

View File

@ -86,6 +86,7 @@ struct editor
bool modal_error;
enum language highlight_source;
bool line_numbering;
bool suspend_requested;
};
__attribute__((unused))

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,19 +17,88 @@
#if defined(__sortix__)
#include <sys/keycodes.h>
#include <sys/termmode.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <wchar.h>
#include "command.h"
#include "editor.h"
#include "input.h"
#include "modal.h"
struct terminal_sequence
{
const char* sequence;
int kbkey;
bool control;
bool shift;
};
// TODO: The terminal doesn't deliver shift-pageup and shift-pagedown events.
// TODO: The terminal doesn't deliver shift-home and shift-end events.
struct terminal_sequence terminal_sequences[] =
{
{ "\e[1;2A", KBKEY_UP, false, true },
{ "\e[1;2B", KBKEY_DOWN, false, true },
{ "\e[1;2C", KBKEY_RIGHT, false, true },
{ "\e[1;2D", KBKEY_LEFT, false, true },
{ "\e[1;2F", KBKEY_END, false, true },
{ "\e[1;2H", KBKEY_HOME, false, true },
{ "\e[1;2~", KBKEY_HOME, false, true },
{ "\e[1;5A", KBKEY_UP, true, false },
{ "\e[1;5B", KBKEY_DOWN, true, false },
{ "\e[1;5C", KBKEY_RIGHT, true, false },
{ "\e[1;5D", KBKEY_LEFT, true, false },
{ "\e[1;5F", KBKEY_END, true, false },
{ "\e[1;5H", KBKEY_HOME, true, false },
{ "\e[1;5~", KBKEY_HOME, true, false },
{ "\e[1;6A", KBKEY_UP, true, true },
{ "\e[1;6B", KBKEY_DOWN, true, true },
{ "\e[1;6C", KBKEY_RIGHT, true, true },
{ "\e[1;6D", KBKEY_LEFT, true, true },
{ "\e[1;6F", KBKEY_END, true, true },
{ "\e[1;6H", KBKEY_HOME, true, true },
{ "\e[1;6~", KBKEY_HOME, true, true },
{ "\e[1~", KBKEY_HOME, false, false },
{ "\e[3;2~", KBKEY_DELETE, false, true },
{ "\e[3;5~", KBKEY_DELETE, true, false },
{ "\e[3;6~", KBKEY_DELETE, true, true },
{ "\e[3~", KBKEY_DELETE, false, false },
{ "\e[4;2~", KBKEY_END, false, true },
{ "\e[4;5~", KBKEY_END, true, false },
{ "\e[4;6~", KBKEY_END, true, true },
{ "\e[4~", KBKEY_END, false, false },
{ "\e[5;2~", KBKEY_PGUP, false, true },
{ "\e[5;5~", KBKEY_PGUP, true, false },
{ "\e[5;6~", KBKEY_PGUP, true, true },
{ "\e[5~", KBKEY_PGUP, false, false },
{ "\e[6;2~", KBKEY_PGDOWN, false, true },
{ "\e[6;5~", KBKEY_PGDOWN, true, false },
{ "\e[6;6~", KBKEY_PGDOWN, true, true },
{ "\e[6~", KBKEY_PGDOWN, false, false },
{ "\e[A", KBKEY_UP, false, false },
{ "\e[B", KBKEY_DOWN, false, false },
{ "\e[C", KBKEY_RIGHT, false, false },
{ "\e[D", KBKEY_LEFT, false, false },
{ "\e[F", KBKEY_END, false, false },
{ "\e[H", KBKEY_HOME, false, false },
{ "\e:", KBKEY_ESC, false, false },
{ "\eOF", KBKEY_END, false, false },
{ "\eOH", KBKEY_HOME, false, false },
{ "\x7F", KBKEY_BKSPC, false, false },
};
void editor_codepoint(struct editor* editor, uint32_t codepoint)
{
wchar_t c = (wchar_t) codepoint;
@ -43,7 +112,6 @@ void editor_codepoint(struct editor* editor, uint32_t codepoint)
editor_modal_character(editor, c);
}
#if defined(__sortix__)
void editor_type_kbkey(struct editor* editor, int kbkey)
{
if ( kbkey < 0 )
@ -131,67 +199,232 @@ void editor_modal_kbkey(struct editor* editor, int kbkey)
void editor_kbkey(struct editor* editor, int kbkey)
{
int abskbkey = kbkey < 0 ? -kbkey : kbkey;
if ( abskbkey == KBKEY_LCTRL )
{
editor->control = 0 <= kbkey;
return;
}
if ( abskbkey == KBKEY_LSHIFT )
{
editor->lshift = 0 <= kbkey;
editor->shift = editor->lshift || editor->rshift;
return;
}
if ( abskbkey == KBKEY_RSHIFT )
{
editor->rshift = 0 <= kbkey;
editor->shift = editor->lshift || editor->rshift;
return;
}
if ( editor->mode == MODE_EDIT )
editor_type_kbkey(editor, kbkey);
else
editor_modal_kbkey(editor, kbkey);
}
void editor_emulate_kbkey(struct editor* editor,
int kbkey,
bool control,
bool shift)
{
editor->control = control;
editor->lshift = shift;
editor->rshift = false;
editor->shift = shift;
editor_kbkey(editor, kbkey);
editor_kbkey(editor, -kbkey);
editor->control = false;
editor->lshift = false;
editor->rshift = false;
editor->shift = false;
}
void editor_emulate_control_letter(struct editor* editor, uint32_t c)
{
#if !defined(__sortix__)
if ( c == 'Z' )
{
raise(SIGSTOP);
}
#endif
editor->control = true;
editor_codepoint(editor, c);
editor->control = false;
}
void editor_input_begin(struct editor_input* editor_input)
{
#if defined(__sortix__)
gettermmode(0, &editor_input->saved_termmode);
settermmode(0, TERMMODE_KBKEY | TERMMODE_UNICODE);
#else
(void) editor_input;
#endif
memset(editor_input, 0, sizeof(*editor_input));
tcgetattr(0, &editor_input->saved_termios);
struct termios tcattr;
memcpy(&tcattr, &editor_input->saved_termios, sizeof(struct termios));
tcattr.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN);
tcattr.c_iflag |= ICRNL;
tcattr.c_cc[VMIN] = 1;
tcattr.c_cc[VTIME] = 0;
tcsetattr(0, TCSADRAIN, &tcattr);
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049h");
fflush(stdout);
}
}
void editor_input_process(struct editor_input* editor_input,
struct editor* editor)
{
#if defined(__sortix__)
(void) editor_input;
uint32_t input;
if ( read(0, &input, sizeof(input)) != sizeof(input) )
return;
int kbkey;
if ( (kbkey = KBKEY_DECODE(input)) )
editor_kbkey(editor, kbkey);
bool was_ambiguous_escape = editor_input->ambiguous_escape;
editor_input->ambiguous_escape = false;
if ( was_ambiguous_escape )
fcntl(0, F_SETFL, fcntl(0, F_GETFL, (void*) NULL) | O_NONBLOCK);
unsigned char uc;
ssize_t amount_read = read(0, &uc, sizeof(uc));
if ( was_ambiguous_escape )
fcntl(0, F_SETFL, fcntl(0, F_GETFL, (void*) NULL) &~ O_NONBLOCK);
if ( amount_read != sizeof(uc) )
{
if ( was_ambiguous_escape &&
(errno == EWOULDBLOCK || errno == EAGAIN) )
uc = ':';
else
return;
}
#if 0
if ( 1 <= uc && uc <= 26 )
fprintf(stderr, "Input: ^%c\n", 'A' + uc - 1);
else if ( uc == '\e' )
fprintf(stderr, "Input: ^[\n");
else
editor_codepoint(editor, input);
#else
(void) editor_input;
(void) editor;
fprintf(stderr, "Input: '%c'\n", uc);
#endif
#if 0
fputc(uc, stderr);
#endif
if ( editor_input->termseq_used < MAX_TERMSEQ_SIZE )
editor_input->termseq[editor_input->termseq_used++] = (char) uc;
size_t num_seqs = sizeof(terminal_sequences) / sizeof(terminal_sequences[0]);
while ( editor_input->termseq_seen < editor_input->termseq_used )
{
size_t match = 0;
size_t match_size = 0;
bool full_match = false;
bool partial_match = false;
for ( size_t i = 0; i < num_seqs; i++ )
{
struct terminal_sequence* terminal_sequence = &terminal_sequences[i];
const char* sequence = terminal_sequence->sequence;
bool potential_partial_match = false;
for ( size_t n = 0; n < editor_input->termseq_used; n++ )
{
if ( sequence[n] != editor_input->termseq[n] )
{
potential_partial_match = false;
break;
}
if ( sequence[n+1] == '\0' )
{
potential_partial_match = false;
full_match = true;
match = i;
match_size = n + 1;
break;
}
potential_partial_match = true;
}
if ( potential_partial_match )
partial_match = true;
}
if ( full_match )
{
editor_emulate_kbkey(editor,
terminal_sequences[match].kbkey,
terminal_sequences[match].control,
terminal_sequences[match].shift);
memmove(editor_input->termseq,
editor_input->termseq + match_size,
editor_input->termseq_used - match_size);
editor_input->termseq_used -= match_size;
editor_input->termseq_seen = 0;
continue;
}
if ( partial_match )
{
editor_input->termseq_seen = editor_input->termseq_used;
// HACK: We can't reliably tell an actual escape press apart from
// the beginning of an escape sequence. However, we could use
// timing to get close to the truth, through the assumption
// that a following non-blocking read will fail only if this
// was a single escape press.
if ( editor_input->termseq_used == 1 &&
editor_input->termseq[0] == '\e' )
{
editor_input->ambiguous_escape = true;
return editor_input_process(editor_input, editor);
}
continue;
}
char input = editor_input->termseq[0];
if ( 1 <= input && input <= 26 && input != '\t' && input != '\n' )
{
editor_emulate_control_letter(editor, L'A' + input - 1);
}
else
{
wchar_t wc;
size_t amount = mbrtowc(&wc, &input, 1, &editor_input->ps);
if ( amount == (size_t) -1 )
memset(&editor_input->ps, 0, sizeof(editor_input->ps));
if ( amount == (size_t) 1 )
editor_codepoint(editor, (uint32_t) wc);
}
memmove(editor_input->termseq,
editor_input->termseq + 1,
editor_input->termseq_used - 1);
editor_input->termseq_used--;
editor_input->termseq_seen = 0;
}
}
void editor_input_end(struct editor_input* editor_input)
{
#if defined(__sortix__)
settermmode(0, editor_input->saved_termmode);
#else
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049l");
fflush(stdout);
}
tcsetattr(0, TCSADRAIN, &editor_input->saved_termios);
}
void editor_input_suspend(struct editor_input* editor_input)
{
(void) editor_input;
#if !defined(__sortix__)
struct termios current_termios;
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049l");
fflush(stdout);
}
tcgetattr(0, &current_termios);
raise(SIGSTOP);
tcsetattr(0, TCSADRAIN, &current_termios);
if ( getenv("TERM") && strcmp(getenv("TERM"), "sortix") != 0 )
{
printf("\e[?1049h");
fflush(stdout);
}
#endif
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -20,18 +20,41 @@
#ifndef EDITOR_INPUT_H
#define EDITOR_INPUT_H
#include <termios.h>
#include <wchar.h>
#if !defined(__sortix__)
#define KBKEY_ESC 0x01
#define KBKEY_BKSPC 0x0E
#define KBKEY_HOME (0x80 + 0x47)
#define KBKEY_UP (0x80 + 0x48)
#define KBKEY_PGUP (0x80 + 0x49)
#define KBKEY_LEFT (0x80 + 0x4B)
#define KBKEY_RIGHT (0x80 + 0x4D)
#define KBKEY_END (0x80 + 0x4F)
#define KBKEY_DOWN (0x80 + 0x50)
#define KBKEY_PGDOWN (0x80 + 0x51)
#define KBKEY_DELETE (0x80 + 0x53)
#endif
struct editor;
#define MAX_TERMSEQ_SIZE 16
struct editor_input
{
#if defined(__sortix__)
unsigned int saved_termmode;
#endif
struct termios saved_termios;
mbstate_t ps;
char termseq[MAX_TERMSEQ_SIZE];
size_t termseq_used;
size_t termseq_seen;
bool ambiguous_escape;
};
void editor_input_begin(struct editor_input* editor_input);
void editor_input_process(struct editor_input* editor_input,
struct editor* editor);
void editor_input_end(struct editor_input* editor_input);
void editor_input_suspend(struct editor_input* editor_input);
#endif

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2013, 2014, 2016 Jonas 'Sortie' Termansen.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -17,6 +17,8 @@
* Terminal handling.
*/
#include <sys/ioctl.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
@ -37,9 +39,19 @@ void update_terminal_color(FILE* fp, uint8_t desired_color,
uint8_t current_fg = (current->color >> 0) % 16;
uint8_t current_bg = (current->color >> 4) % 16;
if ( desired_fg != current_fg )
fprintf(fp, "\e[%im", desired_fg + (desired_fg < 8 ? 30 : 90-8) );
{
if ( desired_fg == 7 )
fprintf(fp, "\e[39m");
else
fprintf(fp, "\e[%im", desired_fg + (desired_fg < 8 ? 30 : 90-8));
}
if ( desired_bg != current_bg )
fprintf(fp, "\e[%im", desired_bg + (desired_bg < 8 ? 40 : 100-8) );
{
if ( desired_bg == 0 ) // Transparent background colors and such.
fprintf(fp, "\e[49m");
else
fprintf(fp, "\e[%im", desired_bg + (desired_bg < 8 ? 40 : 100-8));
}
current->color = desired_color;
}
@ -109,7 +121,7 @@ void make_terminal_state(FILE* fp, struct terminal_state* state)
memset(state, 0, sizeof(*state));
struct winsize terminal_size;
tcgetwinsize(fileno(fp), &terminal_size);
ioctl(fileno(fp), TIOCGWINSZ, &terminal_size);
state->width = (int) terminal_size.ws_col;
state->height = (int) terminal_size.ws_row;
size_t data_length = state->width * state->height;