// Copyright Notice
// ================
// BOCHS is Copyright 1994-1999 by Kevin P. Lawton.
//
// BOCHS is commercial software.
//
// For more information, read the file 'LICENSE' included in the bochs
// distribution.  If you don't have access to this file, or have questions
// regarding the licensing policy, the author may be contacted via:
//
//     US Mail:  Kevin Lawton
//               528 Lexington St.
//               Waltham, MA 02452-3028
//
//     EMail:    bochs@world.std.com


#include <app/Application.h>
#include <interface/Window.h>
#include <interface/View.h>
#include <interface/Screen.h>
#include <interface/Bitmap.h>
#include <interface/GraphicsDefs.h>


#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "bochs.h"
// #include "icon_bochs.h"
#include "font/vga.bitmap.h"

#define PAD_NEAREST(n, quantum) (( ((n) + ((quantum) - 1)) / (n) ) * (n))


class BochsApplication : public BApplication {

public:
  BochsApplication();
  };

class BochsWindow : public BWindow {

public:
  BochsWindow(BRect frame);
  virtual  bool  QuitRequested();
  virtual  void  FrameResized(float width, float height);
  };

class BochsView : public BView {
private:
	BBitmap *backing_store;
	BView *backing_view;
	
public:
  BochsView(BRect frame, char *name);
  ~BochsView();
  virtual  void  AttachedToWindow();
  virtual  void  Draw(BRect updateRect);
  virtual  void  KeyDown(const char *bytes, int32 numBytes);
  virtual  void  KeyUp(const char *bytes, int32 numBytes);
  virtual  void  MouseDown(BPoint point);
  virtual  void  MouseUp(BPoint point);
  virtual  void  MouseMoved(BPoint point,
                            uint32 transit, const BMessage *message);
  void DrawBitmap(const BBitmap *aBitmap, BPoint where);							
  void FillRect(BRect r, pattern p = B_SOLID_HIGH);
  void SetHighColor(uchar r, uchar g, uchar b, uchar a = 255);
  void SetLowColor(uchar r, uchar g, uchar b, uchar a = 255);
  void SetFont(const BFont *font, uint32 mask = B_FONT_ALL);
  void set_text_colors(void);
  void set_inv_text_colors(void);
  void set_headerbar_colors(void);
  };


void GUI_end(void);
void   enq_key_event(Bit32u key, Bit32u press_release);
Bit32u deq_key_event(void);
#define SCANCODE_BUFSIZE 20
static Bit32u keyevents[SCANCODE_BUFSIZE];
static unsigned head=0, tail=0;
static bool view_attached = 0;

static BochsApplication *myApplication = NULL;
static BochsWindow      *aWindow = NULL;
static BochsView        *aView = NULL;
static BView            *canvas = NULL;
static uint8 cmap_index[256]; // indeces into system colormap

static BRect rect;
static BPoint image_origin;
static BBitmap *image = NULL;
static BScreen *screen;
static unsigned long rowsize_padded=0;
static uint8 *rawdata = NULL;

//static int rows=25, columns=80;

static unsigned font_width, font_height;
static Bit8u blank_line[80];
static unsigned dimension_x, dimension_y;

//static unsigned imDepth, imWide, imBPP;

// current cursor coordinates
//static int prev_x=-1, prev_y=-1;
//static int current_x=-1, current_y=-1;
//static Bit8u mouse_button_state = 0;

static unsigned prev_block_cursor_x=0;
static unsigned prev_block_cursor_y=0;

static void headerbar_click(int x, int y);

Bit8u ascii_to_scancode[0x7f] = {
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,
  0x39, 0x02, 0x28, 0x04, 0x05, 0x06, 0x08, 0x28, /*  !"#$%&' */
  0x0a, 0x0b, 0x09, 0x0d, 0x33, 0x0c, 0x34, 0x35, /* ()*+,-./ */
  0x0b, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, /* 01234567 */
  0x09, 0x0a, 0x27, 0x27, 0x33, 0x0d, 0x34, 0x35, /* 89:;<=>? */
  0x03, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, /* @ABCDEFG */
  0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, /* HIJKLMNO */
  0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, /* PQRSTUVW */
  0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x07, 0x0c, /* XYZ[\]^_ */
  0x29, 0x1e, 0x30, 0x2e, 0x20, 0x12, 0x21, 0x22, /* `abcdefg */
  0x23, 0x17, 0x24, 0x25, 0x26, 0x32, 0x31, 0x18, /* hijklmno */
  0x19, 0x10, 0x13, 0x1f, 0x14, 0x16, 0x2f, 0x11, /* pqrstuvw */
  0x2d, 0x15, 0x2c, 0x1a, 0x2b, 0x1b, 0x29        /* xyz{|}~  */
  };

extern Bit8u graphics_snapshot[32 * 1024];
//extern pc_keyb_c *pc_keyboard;




unsigned col_vals[256], curr_foreground, curr_background;

static unsigned x_tilesize, y_tilesize;

struct {
  BBitmap *bmap;
  unsigned xdim;
  unsigned ydim;
  } bx_bitmaps[BX_MAX_PIXMAPS];
unsigned bx_bitmap_entries = 0;

static struct {
  BBitmap   *bitmap;
  unsigned xdim;
  unsigned ydim;
  unsigned xorigin;
  unsigned yorigin;
  unsigned alignment;
  void (*f)(void);
  } bx_headerbar_entry[BX_MAX_HEADERBAR_ENTRIES];
static unsigned bx_headerbar_y = 0;
static unsigned bx_headerbar_entries = 0;
static unsigned bx_bitmap_left_xorigin = 0;  // pixels from left
static unsigned bx_bitmap_right_xorigin = 0; // pixels from right

static void headerbar_click(int x, int y);
static unsigned char reverse_bitorder(unsigned char b);
static void create_vga_font(void);
static BBitmap *vgafont[256];

static bx_gui_c *bx_gui_c_ptr;


  void
bx_gui_c::specific_init(bx_gui_c *th, int argc, char **argv,
                        unsigned tilewidth, unsigned tileheight,
                        unsigned header_bar_y)
{
  unsigned i;
#if BX_CPU_LEVEL < 2
  char *window_name = "Bochs 8086 emulator, http://world.std.com/~bochs";
#elif BX_CPU_LEVEL == 2
  char *window_name = "Bochs 80286 emulator, http://world.std.com/~bochs";
#elif BX_CPU_LEVEL == 3
  char *window_name = "Bochs 80386 emulator, http://world.std.com/~bochs";
#else
  char *window_name = "Bochs 80386+ emulator, http://world.std.com/~bochs";
#endif

  bx_gui_c_ptr = th;
UNUSED(argc);
UNUSED(argv);
UNUSED(window_name);

if (bx_options.private_colormap) {
  fprintf(stderr, "#WARNING: BeOS: private_colormap option not handled yet.\n");
  }

  x_tilesize = tilewidth;
  y_tilesize = tileheight;
  bx_headerbar_y = header_bar_y;

  for (i=0; i<80; i++) {
    blank_line[i] = ' ';
    }


  font_width = 8;
  font_height = 16;

  // width = columns * font_width;
  // height = rows * font_height;

bx_printf("font_width = %u\n", (unsigned) font_width);
bx_printf("font_height = %u\n", (unsigned) font_height);

  // Create pixmap of depth 1 (bitmap) for icon
  // icon_pixmap = XCreateBitmapFromData(bx_x_display, win,
  //   (char *) bochs_icon_bits, bochs_icon_width, bochs_icon_height);

  curr_background = 0;
  // XSetBackground(bx_x_display, gc, col_vals[curr_background]);
  curr_foreground = 1;
  // XSetForeground(bx_x_display, gc, col_vals[curr_foreground]);
  //XGrabPointer( bx_x_display, win, True, 0, GrabModeAsync, GrabModeAsync,
  //  win, None, CurrentTime );
  //XFlush(bx_x_display);

  myApplication = new BochsApplication();

  // wait until window on screen
  while (1) {
    aWindow->Lock();
    if (aWindow->IsHidden()) {
     aWindow->Unlock();
     continue;
     }
    aWindow->Unlock();
    break;
    }

  screen = new BScreen;
  // start out with all color map indeces pointing to Black
  cmap_index[0] = screen->IndexForColor(0, 0, 0);
  for (unsigned i=1; i<256; i++) {
    cmap_index[i] = cmap_index[0];
    }

  rect.Set(0,0, x_tilesize-1, y_tilesize-1);
  image = new BBitmap(rect, B_COLOR_8_BIT);
  rowsize_padded = PAD_NEAREST(x_tilesize, sizeof(int32));
  rawdata = new uint8[rowsize_padded * y_tilesize];

  create_vga_font();
}



  void
bx_gui_c::handle_events(void)
{
  Bit32u key;

  while ( head != tail ) {
    key = deq_key_event();
    bx_devices.keyboard->gen_scancode(key);
    }
}

  void
bx_gui_c::flush(void)
{
  if (view_attached)
    aView->Flush();
}


  void
bx_gui_c::clear_screen(void)
{
  aWindow->Lock();
  aView->FillRect(BRect(0, bx_headerbar_y, dimension_x-1, dimension_y-1), B_SOLID_LOW);
  aWindow->Unlock();
}



  void
bx_gui_c::text_update(Bit8u *old_text, Bit8u *new_text,
                      unsigned long cursor_x, unsigned long cursor_y,
                      unsigned nrows)
{
  unsigned i, x, y;
  BPoint point;
  unsigned char achar;
  unsigned nchars;

  aWindow->Lock();

  // Number of characters on screen, variable number of rows
  nchars = 80*nrows;

  // first draw over character at original block cursor location
  if ( (prev_block_cursor_y*80 + prev_block_cursor_x) < nchars ) {
    achar = new_text[(prev_block_cursor_y*80 + prev_block_cursor_x)*2];
    point.Set(prev_block_cursor_x*8, prev_block_cursor_y*16 + bx_headerbar_y);
    aView->DrawBitmap(vgafont[achar], point );
    }

  for (i=0; i<nchars*2; i+=2) {
    if ( (old_text[i]!=new_text[i]) ||
         (old_text[i+1]!=new_text[i+1]) ) {

      achar = new_text[i];

      x = (i/2) % 80;
      y = (i/2) / 80;

      point.Set(x*8, y*16 + bx_headerbar_y);
      aView->DrawBitmap(vgafont[achar], point );
      }
    }

  prev_block_cursor_x = cursor_x;
  prev_block_cursor_y = cursor_y;

  // now draw character at new block cursor location in reverse
  if ( (cursor_y*80 + cursor_x) < nchars ) {
    achar = new_text[(cursor_y*80 + cursor_x)*2];
    point.Set(cursor_x*8, cursor_y*16 + bx_headerbar_y);
    aView->set_inv_text_colors();
    aView->DrawBitmap(vgafont[achar], point );
    aView->set_text_colors();
    }

  aWindow->Unlock();
}


  void
bx_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
{
  for (unsigned y=0; y<y_tilesize; y++) {
    for (unsigned x=0; x<x_tilesize; x++) {
      rawdata[y*rowsize_padded + x] = cmap_index[ tile[y*x_tilesize + x] ];
      }
    }
  aWindow->Lock();
  image->SetBits(rawdata, rowsize_padded * y_tilesize, 0, B_COLOR_8_BIT);
  image_origin.Set(x0, y0 + bx_headerbar_y);

  aView->DrawBitmap( image, image_origin );
  aWindow->Unlock();
}


  Boolean
bx_gui_c::palette_change(unsigned index, unsigned red, unsigned green, unsigned blue)
{
  cmap_index[index] = screen->IndexForColor(
    red, green, blue);
  return(1);
}


  void
bx_gui_c::dimension_update(unsigned x, unsigned y)
{
  aWindow->Lock();
  aWindow->ResizeTo(x, y + bx_headerbar_y);
  aWindow->Unlock();
}


//////////////////////////////////////////////////////


  void
GUI_end()
{
  //myApplication->Run();

  delete(myApplication);
}

BochsApplication::BochsApplication()
            : BApplication("application/x-vnd.Be-Bochs")
{
  BRect      aRect;

  int left, right, top, bottom;
  left = 5;
  top = 80;
  dimension_x = 640;
  dimension_y = 480 + bx_headerbar_y;
  right = left + dimension_x - 1;
  bottom = top + dimension_y - 1;

  // set up a rectangle and instantiate a new window
  aRect.Set(left, top, right, bottom);
  aWindow = new BochsWindow(aRect);

  // set up a rectangle and instantiate a new view
  // view rect should be same size as window rect but with left top at (0, 0)
  aRect.OffsetTo(B_ORIGIN);
  aView = new BochsView(aRect, "BochsView");
  aView->SetViewColor(0, 0, 0);

  aView->set_text_colors();

  // add view to window
  aWindow->AddChild(aView);

  // make window visible
  aWindow->Show();
}


//void set_palette_entry(long i,rgb_color c);

BochsWindow::BochsWindow(BRect frame)
        : BWindow(frame, "Bochs", B_TITLED_WINDOW, B_NOT_RESIZABLE)
{
}

bool BochsWindow::QuitRequested()
{
  be_app->PostMessage(B_QUIT_REQUESTED);
  return(TRUE);
}


  void
BochsWindow::FrameResized(float width, float height)
{
  dimension_x = unsigned(width);
  dimension_y = unsigned(height) + bx_headerbar_y;
  if ( bx_gui_c_ptr )
    bx_gui_c_ptr->show_headerbar();
}


//------------------------------


BochsView::BochsView(BRect rect, char *name)
          : BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW)
{
	backing_store = new BBitmap(rect, B_BITMAP_ACCEPTS_VIEWS, B_COLOR_8_BIT);
	backing_view = new BView(rect,"backing store", B_FOLLOW_ALL, B_WILL_DRAW);
	backing_store->AddChild(backing_view);
}

  void
BochsView::set_text_colors(void)
{
  aView->SetHighColor(255, 255, 255);
  aView->SetLowColor(0, 0, 0);
}

  void
BochsView::set_inv_text_colors(void)
{
  aView->SetHighColor(0, 0, 0);
  aView->SetLowColor(255, 255, 255);
}

  void
BochsView::set_headerbar_colors(void)
{
  aView->SetHighColor(0, 0, 0);
  aView->SetLowColor(255, 255, 255);
}

void BochsView::AttachedToWindow()
{
  BFont font(be_fixed_font);

  aWindow->Lock();
  font.SetSize(16);
  font.SetFlags(B_DISABLE_ANTIALIASING);
  aView->SetFont(&font);

  aView->MakeFocus(true);
  aWindow->Unlock();

  view_attached = 1;
}

void  BochsView::MouseDown(BPoint point)
{
  UNUSED(point);
  if (point.y < BX_HEADER_BAR_Y) {
    headerbar_click(int(point.x), int(point.y));
    return;
    }
  fprintf(stderr, "# mousedown()\n");
}

void  BochsView::MouseUp(BPoint point)
{
  UNUSED(point);
  // currently a place holder function
  fprintf(stderr, "# mouseup()\n");
}

void  BochsView::MouseMoved(BPoint point,
                          uint32 transit, const BMessage *message)
{
  UNUSED(point);
  UNUSED(transit);
  UNUSED(message);
  fprintf(stderr, "# mousemoved()\n");
}

void BochsView::KeyDown(const char *bytes, int32 numBytes)
{
  BMessage *msg;
  //int32 key;
  int32 modifiers;

  uint8 byte;

  msg = Window()->CurrentMessage();
  if ( !msg ) {
    fprintf(stderr, "# keydown() msg NULL\n");
    return;
    }
  modifiers = msg->FindInt32("modifiers");
#if 0
  B_SHIFT_KEY          = 0x00000001,
  B_COMMAND_KEY        = 0x00000002,
  B_CONTROL_KEY        = 0x00000004,
  B_CAPS_LOCK          = 0x00000008,
  B_SCROLL_LOCK        = 0x00000010,
  B_NUM_LOCK           = 0x00000020,
  B_OPTION_KEY         = 0x00000040,
  B_MENU_KEY           = 0x00000080,
  B_LEFT_SHIFT_KEY     = 0x00000100,
  B_RIGHT_SHIFT_KEY    = 0x00000200,
  B_LEFT_COMMAND_KEY   = 0x00000400,
  B_RIGHT_COMMAND_KEY  = 0x00000800,
  B_LEFT_CONTROL_KEY   = 0x00001000,
  B_RIGHT_CONTROL_KEY  = 0x00002000,
  B_LEFT_OPTION_KEY    = 0x00004000,
  B_RIGHT_OPTION_KEY   = 0x00008000
#endif
//if (modifiers) {
//  fprintf(stderr, "# modifiers = %08x\n", (unsigned) modifiers);
//  }

  if (numBytes == 1) {
    //fprintf(stderr, "# down: char %02xh\n", (unsigned) bytes[0]);
    byte = bytes[0];
    if ( byte == 0x00 ) {
      // Ctrl-Space
      enq_key_event(BX_KEY_CTRL_L,  BX_KEY_PRESSED);
      enq_key_event(BX_KEY_SPACE, BX_KEY_PRESSED);
      enq_key_event(BX_KEY_SPACE, BX_KEY_RELEASED);
      enq_key_event(BX_KEY_CTRL_L,  BX_KEY_RELEASED);
      return;
      }
    if ( byte == 0x1b ) {
      // Esc
      if (modifiers & B_CONTROL_KEY)
        enq_key_event(BX_KEY_CTRL_L,  BX_KEY_PRESSED);
      enq_key_event(BX_KEY_ESC, BX_KEY_PRESSED);
      enq_key_event(BX_KEY_ESC, BX_KEY_RELEASED);
      if (modifiers & B_CONTROL_KEY)
        enq_key_event(BX_KEY_CTRL_L,  BX_KEY_RELEASED);
      return;
      }
    if ( (byte >= 0x30) && (byte <= 0x39) ) {
      // 0 .. 9
      byte -= 0x30;
      enq_key_event(BX_KEY_0 + byte, BX_KEY_PRESSED);
      enq_key_event(BX_KEY_0 + byte, BX_KEY_RELEASED);
      return;
      }
    if ( (byte >= 0x41) && (byte <= 0x5A) ) {
      // A .. Z
      byte -= 0x41;
      enq_key_event(BX_KEY_SHIFT_L,    BX_KEY_PRESSED);
      enq_key_event(BX_KEY_A + byte, BX_KEY_PRESSED);
      enq_key_event(BX_KEY_A + byte, BX_KEY_RELEASED);
      enq_key_event(BX_KEY_SHIFT_L,    BX_KEY_RELEASED);
      return;
      }
    if ( (byte >= 0x61) && (byte <= 0x7A) ) {
      // a .. z
      byte -= 0x61;
      enq_key_event(BX_KEY_A + byte, BX_KEY_PRESSED);
      enq_key_event(BX_KEY_A + byte, BX_KEY_RELEASED);
      return;
      }
    switch (byte) {
      case 0x20: // Space
        enq_key_event(BX_KEY_SPACE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SPACE, BX_KEY_RELEASED);
        break;
      case 0x21: // Exclamation Point
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_1,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_1,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x22: // Double Quotes
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SINGLE_QUOTE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SINGLE_QUOTE, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x23: // Pound Sign
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_3,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_3,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x24: // Dollar Sign
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_4,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_4,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x25: // Percent
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_5,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_5,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x26: // Ampersand
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_7,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_7,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x27: // Single Quote
        enq_key_event(BX_KEY_SINGLE_QUOTE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SINGLE_QUOTE, BX_KEY_RELEASED);
        break;
      case 0x28: // Left Paren
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_9,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_9,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x29: // Right Paren
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_0,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_0,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x2a: // Multiply
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_8,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_8,     BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x2b: // Add
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_EQUALS, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_EQUALS, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x2c: // Comma
        enq_key_event(BX_KEY_COMMA, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_COMMA, BX_KEY_RELEASED);
        break;
      case 0x2d: // Minus
        enq_key_event(BX_KEY_MINUS, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_MINUS, BX_KEY_RELEASED);
        break;
      case 0x2e: // Period
        enq_key_event(BX_KEY_PERIOD, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_PERIOD, BX_KEY_RELEASED);
        break;
      case 0x2f: // Slash
        enq_key_event(BX_KEY_SLASH, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SLASH, BX_KEY_RELEASED);
        break;

      case 0x3a: // Colon
        enq_key_event(BX_KEY_SHIFT_L,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SEMICOLON, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SEMICOLON, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,     BX_KEY_RELEASED);
        break;
      case 0x3b: // Semi-Colon
        enq_key_event(BX_KEY_SEMICOLON, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SEMICOLON, BX_KEY_RELEASED);
        break;
      case 0x3c: // Less Than
        enq_key_event(BX_KEY_SHIFT_L,     BX_KEY_PRESSED);
        enq_key_event(BX_KEY_COMMA, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_COMMA, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,     BX_KEY_RELEASED);
        break;
      case 0x3d: // Equals
        enq_key_event(BX_KEY_EQUALS, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_EQUALS, BX_KEY_RELEASED);
        break;
      case 0x3e: // Greater Than
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_PERIOD, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_PERIOD, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x3f: // Question Mark
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SLASH, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_SLASH, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x40: // At Sign
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_2,      BX_KEY_PRESSED);
        enq_key_event(BX_KEY_2,      BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;

      case 0x5b: // Left Bracket
        enq_key_event(BX_KEY_LEFT_BRACKET, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_LEFT_BRACKET, BX_KEY_RELEASED);
        break;
      case 0x5c: // Back Slash
        enq_key_event(BX_KEY_BACKSLASH, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_BACKSLASH, BX_KEY_RELEASED);
        break;
      case 0x5d: // Right Bracket
        enq_key_event(BX_KEY_RIGHT_BRACKET, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_RIGHT_BRACKET, BX_KEY_RELEASED);
        break;
      case 0x5e: // Caret
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_6,      BX_KEY_PRESSED);
        enq_key_event(BX_KEY_6,      BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x5f: // Underscore
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_MINUS, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_MINUS, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;
      case 0x60: // Grave Accent
        enq_key_event(BX_KEY_GRAVE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_GRAVE, BX_KEY_RELEASED);
        break;

      case 0x7b: // Left Brace
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_LEFT_BRACKET, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_LEFT_BRACKET, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x7c: // Verticle Bar
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_PRESSED);
        enq_key_event(BX_KEY_BACKSLASH, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_BACKSLASH, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,  BX_KEY_RELEASED);
        break;
      case 0x7d: // Right Brace
        enq_key_event(BX_KEY_SHIFT_L,         BX_KEY_PRESSED);
        enq_key_event(BX_KEY_RIGHT_BRACKET, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_RIGHT_BRACKET, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L,         BX_KEY_RELEASED);
        break;
      case 0x7e: // Tilde
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_GRAVE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_GRAVE, BX_KEY_RELEASED);
        enq_key_event(BX_KEY_SHIFT_L, BX_KEY_RELEASED);
        break;

      case B_BACKSPACE:
        enq_key_event(BX_KEY_BACKSPACE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_BACKSPACE, BX_KEY_RELEASED);
        break;
      case B_ENTER: //case B_RETURN:
        enq_key_event(BX_KEY_ENTER, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_ENTER, BX_KEY_RELEASED);
        break;
      case B_TAB:
        enq_key_event(BX_KEY_TAB, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_TAB, BX_KEY_RELEASED);
        break;
      case B_LEFT_ARROW:
        enq_key_event(BX_KEY_LEFT, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_LEFT, BX_KEY_RELEASED);
        break;
      case B_RIGHT_ARROW:
        enq_key_event(BX_KEY_RIGHT, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_RIGHT, BX_KEY_RELEASED);
        break;
      case B_UP_ARROW:
        enq_key_event(BX_KEY_UP, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_UP, BX_KEY_RELEASED);
        break;
      case B_DOWN_ARROW:
        enq_key_event(BX_KEY_DOWN, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_DOWN, BX_KEY_RELEASED);
        break;
#if 0
      case B_FUNCTION_KEY: break;
        msg->FindInt32("key", &key);
        switch ( key ) {
          case B_F1_KEY: break;
          case B_F2_KEY: break;
          case B_F3_KEY: break;
          case B_F4_KEY: break;
          case B_F5_KEY: break;
          case B_F6_KEY: break;
          case B_F7_KEY: break;
          case B_F8_KEY: break;
          case B_F9_KEY: break;
          case B_F10_KEY: break;
          case B_F11_KEY: break;
          case B_F12_KEY: break;
          case B_PRINT_KEY: break;
          case B_SCROLL_KEY: break;
          case B_PAUSE_KEY: break;
          default:
            fprintf(stderr, "# keydown() unknown function key %08xh\n",
              (unsigned) key);
          }
#endif
      case B_INSERT: break;
        enq_key_event(BX_KEY_INSERT, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_INSERT, BX_KEY_RELEASED);
        break;
      case B_DELETE: break;
        enq_key_event(BX_KEY_DELETE, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_DELETE, BX_KEY_RELEASED);
        break;
      case B_HOME: break;
        enq_key_event(BX_KEY_HOME, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_HOME, BX_KEY_RELEASED);
        break;
      case B_END: break;
        enq_key_event(BX_KEY_END, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_END, BX_KEY_RELEASED);
        break;
      case B_PAGE_UP: break;
        enq_key_event(BX_KEY_PAGE_UP, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_PAGE_UP, BX_KEY_RELEASED);
        break;
      case B_PAGE_DOWN: break;
        enq_key_event(BX_KEY_PAGE_DOWN, BX_KEY_PRESSED);
        enq_key_event(BX_KEY_PAGE_DOWN, BX_KEY_RELEASED);
        break;

      default:
        if ( (byte >= 0x01) && (byte <= 0x1a) ) {
          // If the above keys dont catch this case, synthesize a
          // Ctrl-A .. Ctrl-Z event
          byte -= 1;
          enq_key_event(BX_KEY_CTRL_L,     BX_KEY_PRESSED);
          enq_key_event(BX_KEY_A + byte, BX_KEY_PRESSED);
          enq_key_event(BX_KEY_A + byte, BX_KEY_RELEASED);
          enq_key_event(BX_KEY_CTRL_L,     BX_KEY_RELEASED);
          return;
          }
        fprintf(stderr, "# keydown: char %02xh unhandled\n",
          (unsigned) bytes[0]);
        return;
      }
    return;
    }
  else {
    // ignore for now
    fprintf(stderr, "# keydown() ignoring multibyte key\n");
    }
}

void BochsView::KeyUp(const char *bytes, int32 numBytes)
{
  UNUSED(bytes);
  UNUSED(numBytes);
}

void BochsView::Draw(BRect rect)
{
	DrawBitmapAsync(backing_store, rect, rect);
}

BochsView::~BochsView()
{
	delete backing_store;
	delete backing_view;
}

void 
BochsView::SetFont(const BFont *font, uint32 mask)
{
	backing_store->Lock();
	backing_view->SetFont(font,mask);
	backing_store->Unlock();
}

void 
BochsView::SetHighColor(uchar r, uchar g, uchar b, uchar a)
{
	backing_store->Lock();
	backing_view->SetHighColor(r,g,b,a);
	backing_store->Unlock();
}

void 
BochsView::SetLowColor(uchar r, uchar g, uchar b, uchar a)
{
	backing_store->Lock();
	backing_view->SetLowColor(r,g,b,a);
	backing_store->Unlock();
}

void 
BochsView::DrawBitmap(const BBitmap *aBitmap, BPoint where)
{
	backing_store->Lock();
	backing_view->DrawBitmap(aBitmap,where);
	backing_store->Unlock();
	BRect r = aBitmap->Bounds();
	r.OffsetBy(where);
	Invalidate(r);
}

void 
BochsView::FillRect(BRect r, pattern p)
{
	backing_store->Lock();
	backing_view->FillRect(r,p);
	backing_store->Unlock();
	Invalidate(r);
}



  void
enq_key_event(Bit32u key, Bit32u press_release)
{
  if ( ((tail+1) % SCANCODE_BUFSIZE) == head ) {
    fprintf(stderr, "# enq_key_event: buffer full\n");
    return;
    }
  keyevents[tail] = key | press_release;
  tail = (tail + 1) % SCANCODE_BUFSIZE;
}

  Bit32u
deq_key_event(void)
{
  Bit32u key;

  if ( head == tail ) {
    fprintf(stderr, "# deq_key_event: buffer empty\n");
    return(0);
    }
  key = keyevents[head];
  head = (head + 1) % SCANCODE_BUFSIZE;
  return(key);
}

  unsigned char
reverse_bitorder(unsigned char b)
{
  unsigned char ret=0;
  for (unsigned i=0; i<8; i++) {
    ret |= (b & 0x01) << (7-i);
    b >>= 1;
    }
  return(ret);
}

  unsigned
bx_gui_c::create_bitmap(const unsigned char *bmap,
                        unsigned xdim, unsigned ydim)
{
  BRect rect(0.0, 0.0, xdim-1, ydim-1);
  int32 bitslength;
  //int32 bytesperrow;
  unsigned char *data;

  if (bx_bitmap_entries >= BX_MAX_PIXMAPS) {
    bx_panic("beos: too many pixmaps, increase BX_MAX_PIXMAPS\n");
    }

  bx_bitmaps[bx_bitmap_entries].bmap =
    new BBitmap(rect, B_MONOCHROME_1_BIT);

  bitslength = bx_bitmaps[bx_bitmap_entries].bmap->BitsLength();
  //bytesperrow = bx_bitmaps[bx_bitmap_entries].bmap->BytesPerRow();
  data = (unsigned char *) bx_bitmaps[bx_bitmap_entries].bmap->Bits();
  for (int32 i=0; i<bitslength; i++) {
    data[i] = reverse_bitorder(bmap[i]);
    }

  bx_bitmaps[bx_bitmap_entries].xdim = xdim;
  bx_bitmaps[bx_bitmap_entries].ydim = ydim;
  if (!bx_bitmaps[bx_bitmap_entries].bmap) {
    bx_panic("beos: could not create bitmap\n");
    }
  bx_bitmap_entries++;
  return(bx_bitmap_entries-1); // return index as handle
}

  unsigned
bx_gui_c::headerbar_bitmap(unsigned bmap_id, unsigned alignment,
                           void (*f)(void))
{
  unsigned hb_index;

  if ( (bx_headerbar_entries+1) > BX_MAX_HEADERBAR_ENTRIES )
    bx_panic("beos: too many headerbar entries, increase BX_MAX_HEADERBAR_ENTRIES\n");

  bx_headerbar_entries++;
  hb_index = bx_headerbar_entries - 1;

  bx_headerbar_entry[hb_index].bitmap = bx_bitmaps[bmap_id].bmap;
  bx_headerbar_entry[hb_index].xdim   = bx_bitmaps[bmap_id].xdim;
  bx_headerbar_entry[hb_index].ydim   = bx_bitmaps[bmap_id].ydim;
  bx_headerbar_entry[hb_index].alignment = alignment;
  bx_headerbar_entry[hb_index].f = f;
  if (alignment == BX_GRAVITY_LEFT) {
    bx_headerbar_entry[hb_index].xorigin = bx_bitmap_left_xorigin;
    bx_headerbar_entry[hb_index].yorigin = 0;
    bx_bitmap_left_xorigin += bx_bitmaps[bmap_id].xdim;
    }
  else { // BX_GRAVITY_RIGHT
    bx_bitmap_right_xorigin += bx_bitmaps[bmap_id].xdim;
    bx_headerbar_entry[hb_index].xorigin = bx_bitmap_right_xorigin;
    bx_headerbar_entry[hb_index].yorigin = 0;
    }
  return(hb_index);
}

  void
bx_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
{
  unsigned xorigin;

  bx_headerbar_entry[hbar_id].bitmap = bx_bitmaps[bmap_id].bmap;

  if (bx_headerbar_entry[hbar_id].alignment == BX_GRAVITY_LEFT)
    xorigin = bx_headerbar_entry[hbar_id].xorigin;
  else
    xorigin = dimension_x - bx_headerbar_entry[hbar_id].xorigin;
  aWindow->Lock();
  aView->set_headerbar_colors();
  aView->DrawBitmap(bx_headerbar_entry[hbar_id].bitmap, BPoint(xorigin, 0));
  aView->set_text_colors();
  aWindow->Unlock();
}

  void
bx_gui_c::show_headerbar(void)
{
  unsigned xorigin;
  BPoint origin;

  // clear header bar area to white

  aWindow->Lock();
  aView->set_headerbar_colors();
  aView->FillRect(BRect(0, 0, dimension_x-1, bx_headerbar_y-1), B_SOLID_LOW);
  for (unsigned i=0; i<bx_headerbar_entries; i++) {
    if (bx_headerbar_entry[i].alignment == BX_GRAVITY_LEFT)
      xorigin = bx_headerbar_entry[i].xorigin;
    else
      xorigin = dimension_x - bx_headerbar_entry[i].xorigin;
    origin.Set(xorigin, 0);
    aView->DrawBitmap( bx_headerbar_entry[i].bitmap, origin );
    }
  aView->set_text_colors();
  aWindow->Unlock();
}


  void
headerbar_click(int x, int y)
{
  int xorigin;

  // assuming y is in bounds
  UNUSED(y);
  for (unsigned i=0; i<bx_headerbar_entries; i++) {
    if (bx_headerbar_entry[i].alignment == BX_GRAVITY_LEFT)
      xorigin = bx_headerbar_entry[i].xorigin;
    else
      xorigin = dimension_x - bx_headerbar_entry[i].xorigin;
    if ( (x>=xorigin) && (x<(xorigin+int(bx_headerbar_entry[i].xdim))) ) {
      bx_headerbar_entry[i].f();
      return;
      }
    }
}


  void
create_vga_font(void)
{
  int32 bitslength;
  int32 bytesperrow;
  unsigned char *data;
  BRect brect(0,0, 7,15);

  fprintf(stderr, "# BeOS: creating VGA font from bitmaps\n");

  // VGA font is 8wide x 16high
  for (unsigned c=0; c<256; c++) {
    vgafont[c] = new BBitmap(brect, B_MONOCHROME_1_BIT);
    if (!vgafont[c]) {
      bx_panic("beos: could not create bitmap\n");
      }

    bitslength = vgafont[c]->BitsLength();
    bytesperrow = vgafont[c]->BytesPerRow();
    data = (unsigned char *) vgafont[c]->Bits();
    memset(data, 0, bitslength);
    for (unsigned i=0; i<16; i++) {
      data[i*bytesperrow] = reverse_bitorder(bx_vgafont[c].data[i]);
      }
    }
}


  void
bx_gui_c::exit(void)
{
  fprintf(stderr, "# WARNING: BEOS: bx_gui_c::exit() not implemented yet.\n");
}
