ギャップバッファ

ギャップバッファで単純なテキストエディタをつくります。
単純なのでC言語だけで書きます。
ただし、プログラムのコンパイルと実行にはOpenGLとGLUTが必要です。

buffer.h

大きな文字列を割り当ててギャップバッファとして使います。
更に、ポインタ配列を割り当て、それもギャップバッファとして、 行の先頭へのポインタを格納します。

typedef struct {
  int length;
  unsigned char **buffer;
  unsigned char *left, *right;
  int line, top, next;
  int col;
} buffer;
extern buffer b;

void b_init(void);
int b_prevchar(void);
int b_nextchar(void);
int b_remove(void);
int b_newline(void);
int b_nextline(void);
int b_prevline(void);
int b_insert(int c);

構造体の説明

length
ポインタ配列の長さ。行数 + 1. 最後の要素は番人です。
buffer
ポインタ配列のアドレス。 配列の要素は各行の先頭を指すポインタです。
left, right
文字列バッファのギャップの両端を参照するのに使います。
line
現在の行。ポインタ配列のギャップの左端を参照するのにも使います。
top
現在の画面の一番上に表示される行。
next
次の行 (または番人)。 ポインタ配列のギャップの右端を参照するのにも使います。
col
例えばカーソルが行の先頭からk文字目を指していて
次の行が空行だとします。
次の行に移動してから前の行に戻ったとき、 カーソルの位置が (行の先頭ではなく) k文字目に戻る
という機能を実現するため、kを一時的に記憶します。

buffer.c

pixels.h は BOTTOM の定義のためだけに include します。
画面に表示される行は top から top + BOTTOM - 1 までです。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pixels.h"
#include "buffer.h"

static void error(const char *file, int line)
{
  fprintf(stderr, "%s %d\n", file, line);
  exit(EXIT_FAILURE);
}
#define error_if(x) if (x) error(__FILE__, __LINE__)

buffer b;

/* つづく */

b_init

void b_init(void)
{
  size_t size = 65536;

  b.length = 1000;
  b.buffer = malloc((b.length + 1) * sizeof *b.buffer);
  error_if(!b.buffer);
  b.buffer[0] = malloc(size);
  error_if(!b.buffer[0]);

  b.buffer[b.length] = b.buffer[0] + size; /* sentinel */
  b.left = b.buffer[0];
  b.right = b.buffer[b.length];
  b.line = b.top = 0;
  b.next = b.length;

  b.col = -1;
}

初期化。
b.colを使わないときは-1を入れておきます。

b_prevchar

int b_prevchar(void)
{
  if (b.left == b.buffer[b.line]) {
    if (b.line == 0) { return -1; }
    b.buffer[--b.next] = b.right;
    if (b.line-- == b.top) { b.top--; }
  } else {
    *--b.right = *--b.left;
  }
  b.col = -1;
  return 0;
}

カーソルが行の先頭にある場合と、それ以外の場合に分けます。
カーソルが行の先頭にある場合、前の行の末尾に移動します。
このとき、文字列バッファはそのままでポインタ配列だけ変更します。
(文字列バッファには改行文字が入っていないので)

b_nextchar

int b_nextchar(void)
{
  if (b.right == b.buffer[b.next]) {
    if (b.next == b.length) { return -1; }
    b.buffer[b.line + 1] = b.left;
    if (++b.line == b.top + BOTTOM) { b.top++; }
    b.next++;
  } else {
    *b.left++ = *b.right++;
  }
  b.col = -1;
  return 0;
}

b_prevcharと同様。
インクリメント・デクリメントは前置と後置とでは意味が違います。

b_remove

int b_remove(void)
{
  if (b.left == b.buffer[b.line]) {
    if (b.line == 0) { return -1; }
    if (b.line-- == b.top) { b.top--; }
  } else {
    b.left--;
  }
  b.col = -1;
  return 0;
}

これもb_prevcharとよく似ています。

b_newline

int b_newline(void)
{
  if (b.line == b.next - 1) {
    int i;
    unsigned char **p;

    /* realloc */
    if (b.length > 0x1000000) { return -1; }
    p = b.buffer;
    b.buffer = realloc(p, (b.length * 2 + 1) * sizeof *b.buffer);
    if (!b.buffer) { b.buffer = p; return -1; }

    /* move pointers */
    for (i = b.length; i >= b.next; --i) {
      b.buffer[i + b.length] = b.buffer[i];
    }
    b.next += b.length;
    b.length += b.length;
  }
  b.buffer[b.line + 1] = b.left;
  if (++b.line == b.top + BOTTOM) { b.top++; }
  b.col = -1;
  return 0;
}

改行文字を挿入します。
実際には内部の文字列バッファには改行文字を書き込みません。
ポインタ配列と文字列バッファの両方が溢れる場合を考えなくて済む、 というメリットがあります。
行数が0x1000000を越えたらエラーとしますが この数字に深い意味はありません。

b_nextline

int b_nextline(void)
{
  int gap, off, len, nlen;
  int move;

  if (b.next == b.length) { return -1; }
  gap = b.right - b.left;
  off = b.left - b.buffer[b.line];
  len = b.buffer[b.next] - b.right;
  nlen = b.buffer[b.next + 1] - b.buffer[b.next];
  if (b.col == -1) {
    if (nlen < off) {
      b.col = off;
      move = len + nlen;
    } else {
      move = len + off;
    }
  } else if (nlen < b.col) {
    move = len + nlen;
  } else {
    move = len + b.col;
    b.col = -1;
  }
  memmove(b.left, b.right, move);
  b.left += move;
  b.right += move;
  b.buffer[b.line + 1] = b.buffer[b.next] - gap;
  if (++b.line == b.top + BOTTOM) { b.top++; }
  b.next++;
  return 0;
}
  1. 文字列バッファのギャップを移動する量 move を計算
  2. memmove
  3. ポインタの更新

という流れ。

memmoveはコピー元とコピー先の一部が重なっている場合にも問題なく使えます。

b_prevline

int b_prevline(void)
{
  int gap, off, plen;
  int move;

  if (b.line == 0) { return -1; }
  gap = b.right - b.left;
  off = b.left - b.buffer[b.line];
  plen = b.buffer[b.line] - b.buffer[b.line - 1];
  if (b.col == -1) {
    if (plen < off) {
      b.col = off;
      move = off;
    } else {
      move = plen;
    }
  } else if (plen < b.col) {
    move = off;
  } else {
    move = off + plen - b.col;
    b.col = -1;
  }
  b.left -= move;
  b.right -= move;
  memmove(b.right, b.left, move);
  b.buffer[--b.next] = b.buffer[b.line] + gap;
  if (b.line-- == b.top) { b.top--; }
  return 0;
}

b_insert

int b_insert(int c)
{
  if (b.left == b.right) {
    unsigned char *p;
    size_t size;
    int i;

    /* realloc */
    size = b.buffer[b.length] - b.buffer[0];
    if (size > 0x1000000) { return -1; }
    p = b.buffer[0];
    b.buffer[0] = realloc(p, size * 2);
    if (!b.buffer[0]) { b.buffer[0] = p; return -1; }

    /* update pointers */
    for (i = 1; i <= b.length; ++i) {
      b.buffer[i] = b.buffer[0] + (b.buffer[i] - p);
    }
    b.left = b.buffer[0] + (b.left - p);
    b.right = b.buffer[0] + (b.right - p);

    /* memmove */
    memmove(b.right + size, b.right, b.buffer[b.length] - b.right);

    /* update pointers */
    b.right += size;
    for (i = b.next; i <= b.length; ++i) {
      b.buffer[i] += size;
    }
  }

  *b.left++ = c;
  b.col = -1;
  return 0;
}

pixels.h

#define WIDTH 640
#define HEIGHT 480
enum { red, green, blue };
extern unsigned char pixels[HEIGHT][WIDTH][3];
#define pixels_(x, y) pixels[HEIGHT - 1 - (y)][x]

#define BOTTOM 24

void draw_string(int x, int y, const unsigned char *p, int len);
void cursor(int x, int y);
void clear(void);

自前のフレームバッファのようなものに書き込んで表示します。

pixels.c

フォントデータは JISX0213(所謂第3,4水準漢字)用bdfフォントのページ にあるものを元にしています。

#include "pixels.h"

static unsigned char ascii[][14] = {
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x18,0x18,0x18,0x18,0x10,0x10,0x10,0x00,0x00,0x18,0x18,0x00},
  {0x00,0x6c,0x6c,0x24,0x48,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x24,0x24,0x24,0x7e,0x24,0x24,0x48,0x7e,0x48,0x48,0x48,0x00},
  {0x00,0x00,0x10,0x3c,0x52,0x52,0x50,0x38,0x14,0x52,0x52,0x3c,0x10,0x00},
  {0x00,0x00,0x22,0x52,0x54,0x54,0x28,0x18,0x14,0x2a,0x2a,0x4a,0x44,0x00},
  {0x00,0x00,0x10,0x28,0x28,0x10,0x26,0x52,0x52,0x4c,0x44,0x2a,0x10,0x00},
  {0x00,0x30,0x30,0x10,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x04,0x08,0x10,0x10,0x20,0x20,0x20,0x20,0x20,0x10,0x10,0x08,0x04},
  {0x00,0x20,0x10,0x08,0x08,0x04,0x04,0x04,0x04,0x04,0x08,0x08,0x10,0x20},
  {0x00,0x00,0x00,0x10,0x10,0xd6,0x54,0x38,0x54,0xd6,0x10,0x10,0x00,0x00},
  {0x00,0x00,0x00,0x10,0x10,0x10,0x10,0x7e,0x10,0x10,0x10,0x10,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x10,0x20},
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00},
  {0x02,0x02,0x04,0x04,0x08,0x08,0x10,0x10,0x20,0x20,0x40,0x40,0x80,0x80},
  {0x00,0x00,0x18,0x24,0x24,0x42,0x42,0x42,0x42,0x24,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x10,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x22,0x04,0x08,0x10,0x20,0x22,0x7e,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x44,0x18,0x04,0x42,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x04,0x0c,0x14,0x14,0x24,0x24,0x7e,0x04,0x04,0x0e,0x00,0x00},
  {0x00,0x00,0x7c,0x40,0x40,0x58,0x64,0x42,0x02,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x1c,0x22,0x42,0x58,0x64,0x42,0x42,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x3e,0x22,0x42,0x04,0x04,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x24,0x18,0x24,0x42,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x42,0x26,0x1a,0x02,0x44,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18,0x08,0x10,0x00},
  {0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x02,0x00},
  {0x00,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x40,0x20,0x10,0x08,0x04,0x02,0x04,0x08,0x10,0x20,0x40,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x42,0x04,0x08,0x10,0x10,0x00,0x18,0x18,0x00},
  {0x00,0x00,0x18,0x24,0x22,0x4a,0x56,0x56,0x56,0x4a,0x20,0x22,0x1c,0x00},
  {0x00,0x00,0x10,0x10,0x28,0x28,0x28,0x24,0x7c,0x44,0x44,0xee,0x00,0x00},
  {0x00,0x00,0x78,0x24,0x24,0x28,0x3c,0x22,0x22,0x22,0x24,0x78,0x00,0x00},
  {0x00,0x00,0x1a,0x26,0x22,0x42,0x40,0x40,0x40,0x22,0x26,0x18,0x00,0x00},
  {0x00,0x00,0x78,0x24,0x24,0x22,0x22,0x22,0x22,0x24,0x24,0x78,0x00,0x00},
  {0x00,0x00,0x7c,0x22,0x20,0x24,0x3c,0x24,0x20,0x20,0x22,0x7e,0x00,0x00},
  {0x00,0x00,0x7c,0x22,0x20,0x24,0x3c,0x24,0x20,0x20,0x20,0x78,0x00,0x00},
  {0x00,0x00,0x1a,0x26,0x22,0x42,0x40,0x4e,0x42,0x22,0x26,0x1a,0x00,0x00},
  {0x00,0x00,0xee,0x44,0x44,0x44,0x7c,0x44,0x44,0x44,0x44,0xee,0x00,0x00},
  {0x00,0x00,0x7c,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x7c,0x00,0x00},
  {0x00,0x00,0x1e,0x04,0x04,0x04,0x04,0x04,0x44,0x44,0x48,0x30,0x00,0x00},
  {0x00,0x00,0x6e,0x24,0x28,0x28,0x30,0x28,0x24,0x24,0x22,0x76,0x00,0x00},
  {0x00,0x00,0x70,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x22,0x7c,0x00,0x00},
  {0x00,0x00,0x42,0x66,0x66,0x6a,0x5a,0x52,0x52,0x42,0x42,0x66,0x00,0x00},
  {0x00,0x00,0x46,0x62,0x62,0x52,0x52,0x4a,0x4a,0x46,0x46,0x62,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x78,0x24,0x22,0x22,0x24,0x38,0x20,0x20,0x20,0x70,0x00,0x00},
  {0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x72,0x4e,0x24,0x18,0x06,0x00},
  {0x00,0x00,0x78,0x24,0x22,0x22,0x24,0x38,0x28,0x24,0x24,0x72,0x00,0x00},
  {0x00,0x00,0x1a,0x26,0x42,0x20,0x18,0x04,0x02,0x42,0x64,0x58,0x00,0x00},
  {0x00,0x00,0x7e,0x52,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00},
  {0x00,0x00,0x76,0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x14,0x08,0x00,0x00},
  {0x00,0x00,0x66,0x42,0x24,0x24,0x24,0x28,0x18,0x10,0x10,0x10,0x00,0x00},
  {0x00,0x00,0x92,0x92,0x92,0x52,0x5a,0x6a,0x6c,0x24,0x24,0x24,0x00,0x00},
  {0x00,0x00,0x62,0x44,0x24,0x28,0x10,0x18,0x28,0x24,0x44,0x4e,0x00,0x00},
  {0x00,0x00,0xe6,0x42,0x24,0x28,0x18,0x10,0x10,0x10,0x10,0x38,0x00,0x00},
  {0x00,0x00,0x3e,0x44,0x04,0x08,0x08,0x10,0x20,0x20,0x42,0x7c,0x00,0x00},
  {0x00,0x3c,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x3c},
  {0x80,0x80,0x40,0x40,0x20,0x20,0x10,0x10,0x08,0x08,0x04,0x04,0x02,0x02},
  {0x00,0x3c,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x3c},
  {0x00,0x10,0x28,0x44,0x82,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xfe},
  {0x00,0x18,0x18,0x10,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x38,0x44,0x44,0x1c,0x24,0x44,0x44,0x3a,0x00,0x00},
  {0x00,0x00,0x60,0x20,0x28,0x34,0x22,0x22,0x22,0x22,0x24,0x38,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x1a,0x26,0x42,0x40,0x40,0x42,0x26,0x18,0x00,0x00},
  {0x00,0x00,0x0c,0x04,0x14,0x2c,0x44,0x44,0x44,0x44,0x24,0x1e,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x18,0x24,0x42,0x7e,0x40,0x42,0x22,0x1c,0x00,0x00},
  {0x00,0x00,0x0c,0x12,0x12,0x7c,0x10,0x10,0x10,0x10,0x10,0x38,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x1a,0x24,0x24,0x24,0x18,0x20,0x5c,0x42,0x42,0x3c},
  {0x00,0x00,0xc0,0x40,0x50,0x68,0x44,0x44,0x44,0x44,0x44,0xc6,0x00,0x00},
  {0x00,0x00,0x18,0x18,0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x3c,0x00,0x00},
  {0x00,0x00,0x0c,0x0c,0x00,0x1c,0x04,0x04,0x04,0x04,0x04,0x44,0x48,0x30},
  {0x00,0x00,0xc0,0x40,0x46,0x44,0x48,0x58,0x68,0x44,0x44,0xce,0x00,0x00},
  {0x00,0x00,0x38,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00},
  {0x00,0x00,0x00,0x00,0xac,0xd2,0x92,0x92,0x92,0x92,0x92,0x92,0x00,0x00},
  {0x00,0x00,0x00,0x00,0xd8,0x64,0x44,0x44,0x44,0x44,0x44,0xc6,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x18,0x24,0x42,0x42,0x42,0x42,0x24,0x18,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x58,0x24,0x22,0x22,0x22,0x22,0x24,0x38,0x20,0x70},
  {0x00,0x00,0x00,0x00,0x1a,0x24,0x44,0x44,0x44,0x44,0x24,0x1c,0x04,0x0e},
  {0x00,0x00,0x00,0x00,0x5c,0x22,0x22,0x20,0x20,0x20,0x20,0x70,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x3c,0x44,0x40,0x30,0x0c,0x42,0x62,0x5c,0x00,0x00},
  {0x00,0x00,0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x10,0x12,0x0c,0x00,0x00},
  {0x00,0x00,0x00,0x00,0xcc,0x44,0x44,0x44,0x44,0x44,0x4c,0x32,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x66,0x42,0x44,0x24,0x28,0x18,0x10,0x10,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x92,0x92,0x92,0x92,0x5a,0x6c,0x24,0x24,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x66,0x24,0x28,0x18,0x18,0x14,0x24,0x66,0x00,0x00},
  {0x00,0x00,0x00,0x00,0x66,0x22,0x22,0x14,0x14,0x08,0x08,0x48,0x50,0x20},
  {0x00,0x00,0x00,0x00,0x3e,0x44,0x08,0x08,0x10,0x10,0x22,0x7e,0x00,0x00},
  {0x00,0x06,0x08,0x10,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x10,0x08,0x06},
  {0x00,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10},
  {0x00,0x60,0x10,0x08,0x08,0x08,0x08,0x04,0x08,0x08,0x08,0x08,0x10,0x60},
  {0x00,0x32,0x4c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
  {0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe}};

unsigned char pixels[HEIGHT][WIDTH][3];

/* つづく */

draw_string

void draw_char(int x, int y, int c)
{
  int i, j, k;

  if (!(0 <= x && x < WIDTH / 8 && 0 <= y && y < HEIGHT / 16)) {
    return;
  }
  if (c < ' ' || '~' < c) {
    c = '\x7f';
  }
  c -= ' ';
  x *= 8;
  y *= 16;
  for (j = 0; j < 14; ++j) {
    for (i = 0; i < 7; ++i) {
      k = 1 & (ascii[c][j] >> (7 - i));
      pixels_(x + i + 1, y + j + 1)[red] = 0xff * k;
      pixels_(x + i + 1, y + j + 1)[green] = 0xff * k;
      pixels_(x + i + 1, y + j + 1)[blue] = 0xff * k;
    }
  }
}

void draw_string(int x, int y, const unsigned char *p, int len)
{
  if (!(0 <= x && x < WIDTH / 8)) { return; }
  if (x + len > WIDTH / 8) { len = WIDTH / 8 - x; }
  while (len-- > 0) {
    draw_char(x++, y, *p++);
  }
}

cursor

void cursor(int x, int y)
{
  int i;

  if (0 <= x && x < WIDTH / 8) {
    x *= 8;
    for (i = 0; i < BOTTOM * 16; ++i) {
      pixels_(x, i)[red] = 100;
      pixels_(x, i)[green] = 100;
      pixels_(x, i)[blue] = 100;
    }
  }
  if (0 <= y && y < BOTTOM) {
    y = y * 16 + 15;
    for (i = 0; i < WIDTH; ++i) {
      pixels_(i, y)[red] = 100;
      pixels_(i, y)[green] = 100;
      pixels_(i, y)[blue] = 100;
    }
  }
}

カーソルを描きます。

clear

void clear(void)
{
  int i, j;

  for (j = 0; j < BOTTOM * 16; ++j) {
    for (i = 0; i < WIDTH; ++i) {
      pixels_(i, j)[red] = 0;
      pixels_(i, j)[green] = 0;
      pixels_(i, j)[blue] = 0;
    }
  }
}

main.c

OpenGLとGLUTを使っていますが、高度なことは何もしていません。

glRasterPosでラスタ位置を指定するとき、
誤差により位置が画面の外にずれると、何も表示されなくなるらしいので注意。

#include <GL/glut.h>
#include "pixels.h"
#include "buffer.h"

static void reshape(int w, int h)
{
  (void)w;
  glViewport(0, h - HEIGHT, WIDTH, HEIGHT);
}

static void display(void)
{
  int x, y, i;
  unsigned char **p;

  glClear(GL_COLOR_BUFFER_BIT);

  clear();
  x = b.left - b.buffer[b.line];
  y = b.line - b.top;
  cursor(x, y);
  for (i = 0, p = &b.buffer[b.top]; i < y; ++i, ++p) {
    draw_string(0, i, p[0], p[1] - p[0]);
  }
  draw_string(0, y, *p, x);
  draw_string(x, y, b.right, b.buffer[b.next] - b.right);
  for (i = y + 1, p = &b.buffer[b.next];
      i < BOTTOM && p < &b.buffer[b.length]; ++i, ++p) {
    draw_string(0, i, p[0], p[1] - p[0]);
  }

  glRasterPos2i(-1, -1);
  glDrawPixels(WIDTH, HEIGHT, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  glFlush();
}

static void keyboard(unsigned char key, int x, int y)
{
  (void)x;
  (void)y;
  switch (key) {
  case 2: /* C-b */
    b_prevchar();
    break;
  case 6: /* C-f */
    b_nextchar();
    break;
  case '\b':
    b_remove();
    break;
  case '\n':
  case '\r':
    b_newline();
    break;
  case 14: /* C-n */
    b_nextline();
    break;
  case 16: /* C-p */
    b_prevline();
    break;
  default:
    b_insert(key);
    break;
  }
  glutPostRedisplay();
}

int main(int argc, char *argv[])
{
  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
  glutInitWindowSize(WIDTH, HEIGHT);
  glutInitWindowPosition(0, 0);
  glutCreateWindow(argv[0]);

  glClearColor(0.0, 0.0, 0.5, 1.0);
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  b_init();

  glutReshapeFunc(reshape);
  glutDisplayFunc(display);
  glutKeyboardFunc(keyboard);
  glutMainLoop();
  return 0;
}
inserted by FC2 system