/////////////////////////////////////////////////////////////////////////
// $Id$
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2001-2021  The Bochs Project
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
/////////////////////////////////////////////////////////////////////////

// macintosh.cc -- bochs GUI file for the Macintosh
// written by David Batterham <drbatter@progsoc.uts.edu.au>
// with contributions from Tim Senecal

// Define BX_PLUGGABLE in files that can be compiled into plugins.  For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE

// BOCHS INCLUDES
#include <MacTypes.h>

#include "param_names.h"
#include "bochs.h"
#include "iodev.h"

// decide whether to enable this file or not
#if BX_WITH_MACOS

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

// MAC OS INCLUDES
#undef ACCESSOR_CALLS_ARE_FUNCTIONS
#define ACCESSOR_CALLS_ARE_FUNCTIONS 1
#include <Quickdraw.h>
#include <QuickdrawText.h>
#include <QDOffscreen.h>
#include <Icons.h>
#include <ImageCompression.h>
#include <Palettes.h>
#include <Windows.h>
#include <Memory.h>
#include <Events.h>
#include <TextUtils.h>
#include <ToolUtils.h>
#include <Dialogs.h>
#include <LowMem.h>
#include <Disks.h>
#include <CursorDevices.h>
#include <Menus.h>
#include <Sound.h>
#include <SIOUX.h>
#include <Devices.h>

// CONSTANTS

#define rMBarID    128
#define mApple     128
#define iAbout       1
#define mFile      129
#define iQuit        1
#define mEdit      130
#define mBochs     131
#define iFloppy      1
#define iCursor      3
#define iTool        4
#define iMenuBar     5
#define iFullScreen  6
#define iConsole     7
#define iSnapshot    9
#define iReset      10

#define SLEEP_TIME  0 // Number of ticks to surrender the processor during a WaitNextEvent()
// Change this to 15 or higher if you don't want Bochs to hog the processor!

#define FONT_WIDTH    8
#define FONT_HEIGHT  16

#define WINBITMAP(w)  (((GrafPtr)(w))->portBits)

#define ASCII_1_MASK  0x00FF0000
#define ASCII_2_MASK  0x000000FF

const RGBColor  black   = {0, 0, 0};
const RGBColor  white   = {0xFFFF, 0xFFFF, 0xFFFF};
const RGBColor  medGrey = {0xCCCC, 0xCCCC, 0xCCCC};
const RGBColor  ltGrey  = {0xEEEE, 0xEEEE, 0xEEEE};

class bx_macintosh_gui_c : public bx_gui_c {
public:
  bx_macintosh_gui_c (void) {}
  DECLARE_GUI_VIRTUAL_METHODS()
};

// declare one instance of the gui object and call macro to insert the
// plugin code
static bx_macintosh_gui_c *theGui = NULL;
IMPLEMENT_GUI_PLUGIN_CODE(macintosh)

#define LOG_THIS theGui->

// GLOBALS

WindowPtr            win, toolwin, fullwin, backdrop, hidden, SouixWin;
SInt16               gOldMBarHeight;
bool                 menubarVisible = true, cursorVisible = true;
RgnHandle            mBarRgn, cnrRgn;
unsigned             mouse_button_state = 0;
CTabHandle           gCTable;
PixMapHandle         gTile;
BitMap               *vgafont[256];
Rect                 srcTextRect, srcTileRect;
Point                scrCenter = {320, 240};
Ptr                  KCHR;
short                gheaderbar_y;
Point                prevPt;
unsigned             width, height, gMinTop, gMaxTop, gLeft;
GWorldPtr            gOffWorld;
Ptr                  gMyBuffer;
static unsigned      disp_bpp=8;
static EventModifiers oldMods = 0;
static unsigned int text_rows=25, text_cols=80;

// HEADERBAR STUFF
int             numPixMaps = 0, toolPixMaps = 0;
unsigned        bx_bitmap_left_xorigin  = 2; // pixels from left
unsigned        bx_bitmap_right_xorigin = 2; // pixels from right
//PixMapHandle  bx_pixmap[BX_MAX_PIXMAPS];
CIconHandle     bx_cicn[BX_MAX_PIXMAPS];

struct {
        CIconHandle cicn;
//      PixMapHandle pm;
        unsigned xdim;
        unsigned ydim;
        unsigned xorigin;
        unsigned yorigin;
        unsigned alignment;
        void (*f)(void);
} bx_tool_pixmap[BX_MAX_PIXMAPS];

// Event handlers
BX_CPP_INLINE void HandleKey(EventRecord *event, Bit32u keyState);
BX_CPP_INLINE void HandleToolClick(Point where);
void HandleMenuChoice(long menuChoice);
BX_CPP_INLINE void HandleClick(EventRecord *event);

// Update routines
void UpdateWindow(WindowPtr window);
void UpdateRgn(RgnHandle rgn);

// Show/hide UI elements
void HidePointer(void);
void ShowPointer(void);
void HideTools(void);
void ShowTools(void);
void HideMenubar(void);
void ShowMenubar(void);
void HideConsole(void);
void ShowConsole(void);

// Initialisation
void FixWindow(void);
void MacPanic(void);
void InitToolbox(void);
void CreateTile(void);
void CreateMenus(void);
void CreateWindows(void);
void CreateKeyMap(void);
void CreateVGAFont(void);
BitMap *CreateBitMap(unsigned width,  unsigned height);
PixMapHandle CreatePixMap(unsigned left, unsigned top, unsigned width,
        unsigned height, unsigned depth, CTabHandle clut);

//this routine moves the initial window position so that it is entirely onscreen
//it is needed for os 8.x with appearance managaer
void FixWindow(void)
{
  RgnHandle wStruct;
  Region *wRgn;
  CWindowRecord *thing;
  Rect wRect;
  RgnHandle tStruct;
  Region *tRgn;
  Rect tRect;
  short MinVal;

  thing = (CWindowRecord *)win;
  wStruct = thing->strucRgn;
  wRgn = (Region *)*wStruct;
  wRect = wRgn->rgnBBox;

  thing = (CWindowRecord *)toolwin;
  tStruct = thing->strucRgn;
  tRgn = (Region *)*tStruct;
  tRect = tRgn->rgnBBox;

  if (wRect.left < 2)
  {
    gLeft = gLeft + (2 - wRect.left);
  }

  MinVal = tRect.bottom+2;
//MinVal = MinVal + GetMBarHeight();

  if (wRect.top < MinVal)
  {
//  gMinTop = gMinTop + (MinVal - wRect.top);
    gMaxTop = gMaxTop + (MinVal - wRect.top);
  }

  MoveWindow(win, gLeft, gMaxTop, false);
}

void MacPanic(void)
{
  StopAlert(200, NULL);
}

void InitToolbox(void)
{
  InitGraf(&qd.thePort);
  InitWindows();
  InitMenus();
  InitDialogs(nil);
  InitCursor();
  MaxApplZone();
  // Initialise the toolbox
}

void CreateTile(void)
{
  GDHandle  saveDevice;
  CGrafPtr  savePort;
  OSErr     err;
  unsigned  long p_f;
  long      theRowBytes = ((((long) (disp_bpp==24?32:(((disp_bpp+1)>>1)<<1)) * ((long) (srcTileRect.right-srcTileRect.left)) + 31) >> 5) << 2);

//if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get())
//{

  GetGWorld(&savePort, &saveDevice);
  switch (disp_bpp) {
      case 1:
        p_f = k1MonochromePixelFormat;
        break;
      case 2:
        p_f = k2IndexedPixelFormat;
        break;
      case 4:
        p_f = k4IndexedPixelFormat;
        break;
      case 8:
        p_f = k8IndexedPixelFormat;
        break;
      case 15:
        p_f = k16LE555PixelFormat;
        break;
      case 16:
        p_f = k16LE565PixelFormat;
        break;
      case 24:
        //p_f = k24BGRPixelFormat;
        //break;
      case 32:
        p_f = k32ARGBPixelFormat;//k32BGRAPixelFormat;
       break;
  }

  BX_ASSERT((gMyBuffer = (Ptr)malloc(theRowBytes * (srcTileRect.bottom - srcTileRect.top))) != NULL);
  err = QTNewGWorldFromPtr(&gOffWorld, p_f,
      &srcTileRect, disp_bpp>8 ? NULL : gCTable, NULL, keepLocal, gMyBuffer, theRowBytes);
  if (err != noErr || gOffWorld == NULL)
    BX_PANIC(("mac: can't create gOffWorld; err=%hd", err));

  SetGWorld(gOffWorld, NULL);
  RGBForeColor(&black);
  RGBBackColor(&white);

  gTile = GetGWorldPixMap(gOffWorld);

  if (gTile != NULL)
  {
    NoPurgePixels(gTile);
    if (!LockPixels(gTile))
      BX_ERROR(("mac: can't LockPixels gTile"));
    if ((**gTile).pixelType != RGBDirect && (**gTile).pmTable != gCTable)
    {
      DisposeCTable(gCTable);
      gCTable = (**gTile).pmTable;
    }

    (**gCTable).ctFlags |= 0x4000;   //use palette manager indexes
    CTabChanged(gCTable);
  }
  else
    BX_PANIC(("mac: can't create gTile"));

  SetGWorld(savePort, saveDevice);

/*}
  else
  {
    gOffWorld = NULL;
    gTile = CreatePixMap(0, 0, srcTileRect.right, srcTileRect.bottom, 8, gCTable);
    if (gTile == NULL)
      BX_PANIC(("mac: can't create gTile"));
}*/
}

void CreateMenus(void)
{
  Handle menu;

  menu = GetNewMBar(rMBarID);   // get our menus from resource
  if (menu != nil)
  {
    SetMenuBar(menu);
    DisposeHandle(menu);
    AppendResMenu(GetMenuHandle(mApple), 'DRVR');          // add apple menu items
    DrawMenuBar();
  }
  else
    BX_PANIC(("can't create menu"));
}

void CreateWindows(void)
{
  int l, t, r, b;
  Rect winRect;

  SetRect(&winRect, 0, 0, qd.screenBits.bounds.right, qd.screenBits.bounds.bottom);
  backdrop = NewWindow(NULL, &winRect, "\p", false, plainDBox, (WindowPtr)-1, false, 0);

  width = 640;
  height = 480;
  gLeft = 4;
  gMinTop = 44;
  gMaxTop = 44 + gheaderbar_y;

  l = (qd.screenBits.bounds.right - width)/2;
  r = l + width;
  t = (qd.screenBits.bounds.bottom - height)/2;
  b = t + height;

  SetRect(&winRect, 0, 20, qd.screenBits.bounds.right, 22+gheaderbar_y);
  toolwin = NewCWindow(NULL, &winRect, "\pMacBochs 586", true, floatProc,
    (WindowPtr)-1, false, 0);
  if (toolwin == NULL)
    BX_PANIC(("mac: can't create tool window"));
  // Create a moveable tool window for the "headerbar"

  SetRect(&winRect, l, t, r, b);
  fullwin = NewCWindow(NULL, &winRect, "\p", false, plainDBox, (WindowPtr)-1, false, 1);

  SetRect(&winRect, gLeft, gMaxTop, gLeft+width, gMaxTop+height);
  win = NewCWindow(NULL, &winRect, "\pMacBochs 586", true, documentProc,
    (WindowPtr)-1, true, 1);
  if (win == NULL)
    BX_PANIC(("mac: can't create emulator window"));

  FixWindow();

  hidden = fullwin;

  HiliteWindow(win, true);

  SetPort(win);
}

// MACINTOSH implementation of the bx_gui_c methods (see nogui.cc for details)

void bx_macintosh_gui_c::specific_init(int argc, char **argv, unsigned headerbar_y)
{
  put("MACGUI");
  InitToolbox();

  //SouixWin = FrontWindow();

  atexit(MacPanic);

  gheaderbar_y = headerbar_y;

  CreateKeyMap();

  gCTable = GetCTable(128);
  BX_ASSERT (gCTable != NULL);
  CTabChanged(gCTable); //(*gCTable)->ctSeed = GetCTSeed();
  SetRect(&srcTextRect, 0, 0, FONT_WIDTH, FONT_HEIGHT);
  SetRect(&srcTileRect, 0, 0, x_tilesize, y_tilesize);

  CreateMenus();
  CreateVGAFont();
  CreateTile();
  CreateWindows();

  GetMouse(&prevPt);
  SetEventMask(everyEvent);

  SIOUXSettings.setupmenus = false;
  SIOUXSettings.autocloseonquit = true;
  SIOUXSettings.asktosaveonclose = false;
  SIOUXSettings.standalone = false;

  UNUSED(argc);
  UNUSED(argv);

  //HideWindow(SouixWin);
}

// HandleKey()
//
// Handles keyboard-related events.

BX_CPP_INLINE void HandleKey(EventRecord *event, Bit32u keyState)
{
  UInt32    key;
  UInt32    trans;
  static UInt32 transState = 0;

  key = event->message & charCodeMask;

  if (event->modifiers & cmdKey)
  {
    HandleMenuChoice(MenuKey(key));
  }
//else if (FrontWindow() == SouixWin)
//{
//  SIOUXHandleOneEvent(event);
//}
  else
  {
/*  if (event->modifiers & shiftKey)
      DEV_kbd_gen_scancode(BX_KEY_SHIFT_L | keyState);
    if (event->modifiers & controlKey)
      DEV_kbd_gen_scancode(BX_KEY_CTRL_L | keyState);
    if (event->modifiers & optionKey)
      DEV_kbd_gen_scancode(BX_KEY_ALT_L | keyState);*/

    key = (event->message & keyCodeMask) >> 8;

    trans = KeyTranslate(KCHR, key, &transState);
    if ((trans == BX_KEY_PRINT) && ((oldMods & optionKey) || (oldMods & rightOptionKey)))
      trans = BX_KEY_ALT_SYSREQ;
    if ((trans == BX_KEY_PAUSE) && ((oldMods & controlKey) || (oldMods & rightControlKey)))
      trans = BX_KEY_CTRL_BREAK;

    // KeyTranslate maps Mac virtual key codes to any type of character code
    // you like (in this case, Bochs key codes). Much nicer than a huge switch
    // statement!

    if (trans > 0)
      DEV_kbd_gen_scancode(trans | keyState);

/*  if (event->modifiers & shiftKey)
      DEV_kbd_gen_scancode(BX_KEY_SHIFT_L | BX_KEY_RELEASED);
    if (event->modifiers & controlKey)
      DEV_kbd_gen_scancode(BX_KEY_CTRL_L | BX_KEY_RELEASED);
    if (event->modifiers & optionKey)
      DEV_kbd_gen_scancode(BX_KEY_ALT_L | BX_KEY_RELEASED);*/
  }
}

// HandleToolClick()
//
// Handles mouse clicks in the Bochs tool window

BX_CPP_INLINE void HandleToolClick(Point where)
{
  unsigned i;
  int xorigin;
  Rect bounds;

  SetPort(toolwin);
  GlobalToLocal(&where);
  for (i=0; i<toolPixMaps; i++)
  {
    if (bx_tool_pixmap[i].alignment == BX_GRAVITY_LEFT)
      xorigin = bx_tool_pixmap[i].xorigin;
    else
      xorigin = toolwin->portRect.right - bx_tool_pixmap[i].xorigin;
    SetRect(&bounds, xorigin, 0, xorigin+32, 32);
    if (PtInRect(where, &bounds))
      bx_tool_pixmap[i].f();
  }
  theGui->show_headerbar();
}

BX_CPP_INLINE void ResetPointer(void)
{
#if 0
  CursorDevice *theMouse;
  if (true)
  {
    theMouse = NULL;
    CrsrDevNextDevice(&theMouse);
    CrsrDevMoveTo(theMouse, (long)scrCenter.h, (long)scrCenter.v);
  }
#endif

#define MouseCur 0x082C
#define MouseTemp 0x0828
#define MouseNew 0x08CE
#define MouseAttached 0x08CF

  *(Point *)MouseCur = scrCenter;
  *(Point *)MouseTemp = scrCenter;
  *(Ptr)MouseNew = *(Ptr)MouseAttached;
  //*(char *)CrsrNew = 0xFF;
}

// HandleClick()
//
// Handles mouse click events.


void HandleMenuChoice(long menuChoice)
{
  OSErr           err = noErr;
  short           item, menu, i;
  short           daRefNum;
  Rect            bounds;
  Str255          daName;
  WindowRef       newWindow;
  DialogPtr       theDlog;

  item = LoWord(menuChoice);
  menu = HiWord(menuChoice);

  switch(menu) {
  case mApple:
    switch(item) {
    case iAbout:
      theDlog = GetNewDialog(128, NULL, (WindowPtr)-1);
      ModalDialog(NULL, &i);
      DisposeDialog(theDlog);
      break;

    default:
      GetMenuItemText(GetMenuHandle(mApple), item, daName);
      daRefNum = OpenDeskAcc(daName);
      break;
    }
    break;

  case mFile:
    switch(item) {
    case iQuit:
      BX_PANIC(("User terminated"));
      break;

    default:
      break;
    }
    break;

  case mBochs:
    switch(item) {
    case iFloppy:
      DiskEject(1);
      break;

    case iCursor:
      if (cursorVisible)
        HidePointer();
      else
        ShowPointer();
      break;

    case iTool:
      if (IsWindowVisible(toolwin))
        HideTools();
      else
        ShowTools();
      break;

    case iMenuBar:
      if (menubarVisible)
        HideMenubar();
      else
        ShowMenubar();
      break;

    case iFullScreen:
      if (cursorVisible || IsWindowVisible(toolwin) || menubarVisible)
      {
        if (cursorVisible)
          HidePointer();
        if (menubarVisible)
          HideMenubar();
        if (IsWindowVisible(toolwin))
          HideTools();
      }
      else
      {
        if (!cursorVisible)
          ShowPointer();
        if (!menubarVisible)
          ShowMenubar();
        if (!IsWindowVisible(toolwin))
          ShowTools();
      }
      break;

    case iConsole:
      if (IsWindowVisible(SouixWin))
        HideConsole();
      else
        ShowConsole();
      break;

    case iSnapshot:
      // the following will break if snapshot is not last bitmap button instantiated
      bx_tool_pixmap[toolPixMaps-1].f();
      break;
    }

  default:
    break;
  }

  HiliteMenu(0);
}

BX_CPP_INLINE void HandleClick(EventRecord *event)
{
  short part;
  WindowPtr whichWindow;
  Rect dRect;

  part = FindWindow(event->where, &whichWindow);

  switch(part)
  {
    case inContent:
      if (whichWindow == win)
      {
        if (win != FrontWindow())
          SelectWindow(win);
        if (event->modifiers & cmdKey)
          mouse_button_state |= 0x02;
        else
          mouse_button_state |= 0x01;
      }
      else if (whichWindow == toolwin)
      {
        HiliteWindow(win, true);
        HandleToolClick(event->where);
      }
      else if (whichWindow == backdrop)
      {
        SelectWindow(win);
      }
      else if (whichWindow == SouixWin)
      {
        SelectWindow(SouixWin);
      }
      break;

    case inDrag:
      dRect = qd.screenBits.bounds;
      if (IsWindowVisible(toolwin))
        dRect.top = gMaxTop;
      DragWindow(whichWindow, event->where, &dRect);
      break;

    case inMenuBar:
      HandleMenuChoice(MenuSelect(event->where));
      break;
  }
}

void UpdateWindow(WindowPtr window)
{
  GrafPtr oldPort;
  Rect    box;

  GetPort(&oldPort);

  SetPort(window);
  BeginUpdate(window);

  if (window == win)
  {
    box = window->portRect;
    DEV_vga_redraw_area(box.left, box.top, box.right, box.bottom);
  }
  else if (window == backdrop)
  {
    box = window->portRect;
    FillRect(&box, &qd.black);
  }
  else if (window == toolwin)
  {
    theGui->show_headerbar();
  }

  EndUpdate(window);
  SetPort(oldPort);
}

void bx_macintosh_gui_c::handle_events(void)
{
  EventRecord event;
  Point mousePt;
  int dx, dy;
  int oldMods1=0;
  EventModifiers newMods;
  unsigned curstate;
  GrafPtr oldport;

  curstate = mouse_button_state;      //so we can compare the old and the new mouse state

  if (WaitNextEvent(everyEvent, &event, SLEEP_TIME, NULL))
  {
    switch(event.what)
    {
      case nullEvent:
        break;

      case mouseDown:
        HandleClick(&event);
        break;

      case mouseUp:
        if (event.modifiers & cmdKey)
          mouse_button_state &= ~0x02;
        else
          mouse_button_state &= ~0x01;
        break;

      case keyDown:
      case autoKey:
        oldMods1 = event.modifiers;
        HandleKey(&event, BX_KEY_PRESSED);
        break;

      case keyUp:
        event.modifiers = oldMods1;
        HandleKey(&event, BX_KEY_RELEASED);
        break;

      case updateEvt:
        if ((WindowPtr)event.message == SouixWin)
          SIOUXHandleOneEvent(&event);
        else
          UpdateWindow((WindowPtr)event.message);
        break;

      case diskEvt:
        floppyA_handler();

      default:
        break;
    }
  }
  else if (oldMods != (newMods = (event.modifiers & 0xfe00)))
  {
    if ((newMods ^ oldMods) & shiftKey)
      DEV_kbd_gen_scancode(BX_KEY_SHIFT_L | ((newMods & shiftKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & alphaLock)
      DEV_kbd_gen_scancode(BX_KEY_CAPS_LOCK | ((newMods & alphaLock)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & optionKey)
      DEV_kbd_gen_scancode(BX_KEY_ALT_L | ((newMods & optionKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & controlKey)
      DEV_kbd_gen_scancode(BX_KEY_CTRL_L | ((newMods & controlKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & rightShiftKey)
      DEV_kbd_gen_scancode(BX_KEY_SHIFT_R | ((newMods & rightShiftKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & rightOptionKey)
      DEV_kbd_gen_scancode(BX_KEY_ALT_R | ((newMods & rightOptionKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    if ((newMods ^ oldMods) & rightControlKey)
      DEV_kbd_gen_scancode(BX_KEY_CTRL_R | ((newMods & rightControlKey)?BX_KEY_PRESSED:BX_KEY_RELEASED));
    oldMods = newMods;
  }

  GetPort(&oldport);
  SetPort(win);

  GetMouse(&mousePt);

//if mouse has moved, or button has changed state
  if ((!EqualPt(mousePt, prevPt)) || (curstate != mouse_button_state))
  {
    dx = mousePt.h - prevPt.h;
    dy = prevPt.v - mousePt.v;

    DEV_mouse_motion(dx, dy, 0, mouse_button_state, 0);

    if (!cursorVisible)
    {
      SetPt(&scrCenter, 320, 240);
      LocalToGlobal(&scrCenter);
      ResetPointer();         //next getmouse should be 320, 240
      SetPt(&mousePt, 320, 240);
    }
  }

  prevPt = mousePt;

  SetPort(oldport);
}

void bx_macintosh_gui_c::flush(void)
{
  // an opportunity to make the Window Manager happy.
  // not needed on the macintosh....
}

void bx_macintosh_gui_c::clear_screen(void)
{
  SetPort(win);

  RGBForeColor(&black);
  RGBBackColor(&white);

  FillRect(&win->portRect, &qd.black);
}

void bx_macintosh_gui_c::text_update(Bit8u *old_text, Bit8u *new_text,
                 unsigned long cursor_x, unsigned long cursor_y,
                 bx_vga_tminfo_t *tm_info)
{
  int           i;
  unsigned char achar;
  int           x, y;
  static int    previ;
  int           cursori;
  Rect          destRect;
  RGBColor      fgColor, bgColor;
  GrafPtr       oldPort;
  unsigned      nchars;
  OSErr         theError;

  GetPort(&oldPort);

  SetPort(win);

  //current cursor position
  cursori = (cursor_y * text_cols + cursor_x) * 2;

  // Number of characters on screen, variable number of rows
  nchars = text_cols * text_rows;

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

//    fgColor = (**gCTable).ctTable[new_text[i+1] & 0x0F].rgb;
//    bgColor = (**gCTable).ctTable[(new_text[i+1] & 0xF0) >> 4].rgb;

//    RGBForeColor(&fgColor);
//    RGBBackColor(&bgColor);

      if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get())
      {
        PmForeColor(new_text[i+1] & 0x0F);
        PmBackColor((new_text[i+1] & 0xF0) >> 4);
      }
      else
      {
        fgColor = (**gCTable).ctTable[new_text[i+1] & 0x0F].rgb;
        bgColor = (**gCTable).ctTable[(new_text[i+1] & 0xF0) >> 4].rgb;

        RGBForeColor(&fgColor);
        RGBBackColor(&bgColor);
      }

      x = ((i/2) % text_cols)*FONT_WIDTH;
      y = ((i/2) / text_cols)*FONT_HEIGHT;

      SetRect(&destRect, x, y, x+FONT_WIDTH, y+FONT_HEIGHT);

      CopyBits(vgafont[achar], &WINBITMAP(win),
                                &srcTextRect, &destRect, srcCopy, NULL);
      if ((theError = QDError()) != noErr)
        BX_ERROR(("mac: CopyBits returned %hd", theError));

      if (i == cursori)   //invert the current cursor block
      {
        InvertRect(&destRect);
      }
    }
  }

  //previous cursor position
  previ = cursori;

  SetPort(oldPort);
}

int bx_macintosh_gui_c::get_clipboard_text(Bit8u **bytes, Bit32s *nbytes)
{
  return 0;
}

int bx_macintosh_gui_c::set_clipboard_text(char *text_snapshot, Bit32u len)
{
  return 0;
}

bool bx_macintosh_gui_c::palette_change(Bit8u index, Bit8u red, Bit8u green, Bit8u blue)
{
  PaletteHandle thePal, oldpal;
  GDHandle  saveDevice;
  CGrafPtr  savePort;
  GrafPtr   oldPort;

/*if (gOffWorld != NULL) //(SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get())
  {
    GetGWorld(&savePort, &saveDevice);

    SetGWorld(gOffWorld, NULL);
  }*/

  if ((**gTile).pixelType != RGBDirect)
  {
    GetPort(&oldPort);
    SetPort(win);

    (**gCTable).ctTable[index].value = index;
    (**gCTable).ctTable[index].rgb.red = (red << 8);
    (**gCTable).ctTable[index].rgb.green = (green << 8);
    (**gCTable).ctTable[index].rgb.blue = (blue << 8);

    SetEntries(index, index, (**gCTable).ctTable);

    CTabChanged(gCTable);

    SetPort(oldPort);
  }

/*if (gOffWorld != NULL) //(SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get())
    SetGWorld(savePort, saveDevice);*/

  if (SIM->get_param_bool(BXPN_PRIVATE_COLORMAP)->get())
  {
    thePal = NewPalette(index, gCTable, pmTolerant, 0x5000);
    oldpal = GetPalette(win);

    SetPalette(win, thePal, false);
    SetPalette(fullwin, thePal, false);
    SetPalette(hidden, thePal, false);

    return(1);
  }

  return((**gTile).pixelType != RGBDirect);
}

void bx_macintosh_gui_c::graphics_tile_update(Bit8u *tile, unsigned x0, unsigned y0)
{
  Rect      destRect;
  OSErr     theError;
  Ptr       theBaseAddr;
  GrafPtr   oldPort;
/*GDHandle  saveDevice;
  CGrafPtr  savePort;

  GetGWorld(&savePort, &saveDevice);

  SetGWorld(gOffWorld, NULL); */
  GetPort(&oldPort);
  SetPort(win);
  destRect = srcTileRect;
  OffsetRect(&destRect, x0, y0);

  //(**gTile).baseAddr = (Ptr)tile;
  if ((theBaseAddr = GetPixBaseAddr(gTile)) == NULL)
    BX_PANIC(("mac: gTile has NULL baseAddr (offscreen buffer purged)"));
  else if (disp_bpp == 24 || disp_bpp == 32) {
    for (unsigned iY = 0; iY < (srcTileRect.bottom-srcTileRect.top); iY++) {
      Bit8u *iA = ((Bit8u *)theBaseAddr) + iY * GetPixRowBytes(gTile);
      for (unsigned iX = 0; iX < (srcTileRect.right-srcTileRect.left); iX++) {
        iA[iX*4 + 3] = tile[((srcTileRect.right-srcTileRect.left)*iY+iX)*(disp_bpp>>3)];
        iA[iX*4 + 2] = tile[((srcTileRect.right-srcTileRect.left)*iY+iX)*(disp_bpp>>3) + 1];
        iA[iX*4 + 1] = tile[((srcTileRect.right-srcTileRect.left)*iY+iX)*(disp_bpp>>3) + 2];
        iA[iX*4] = disp_bpp == 24 ? 0 : tile[((srcTileRect.right-srcTileRect.left)*iY+iX)*4 + 3];
      }
    }
  } else {
    BlockMoveData(tile, theBaseAddr, (srcTileRect.bottom-srcTileRect.top) * GetPixRowBytes(gTile));
  }
  RGBForeColor(&black);
  RGBBackColor(&white);
  CopyBits(GetPortBitMapForCopyBits(gOffWorld), &WINBITMAP(win),
                                                &srcTileRect, &destRect, srcCopy, NULL);
  if ((theError = QDError()) != noErr)
    BX_ERROR(("mac: CopyBits returned %hd", theError));
  SetPort(oldPort);

//SetGWorld(savePort, saveDevice);
}

void bx_macintosh_gui_c::dimension_update(unsigned x, unsigned y, unsigned fheight, unsigned fwidth, unsigned bpp)
{
  if ((bpp != 1) && (bpp != 2) && (bpp != 4) && (bpp != 8) && (bpp != 15) && (bpp != 16) && (bpp != 24) && (bpp != 32)) {
    BX_PANIC(("%d bpp graphics mode not supported yet", bpp));
  }
  guest_textmode = (fheight > 0);
  guest_xres = x;
  guest_yres = y;
  guest_bpp = bpp;
  if (bpp != disp_bpp) {
    free(gMyBuffer);
    if ((**gTile).pixelType == RGBDirect)
      gCTable = GetCTable(128);
    DisposeGWorld(gOffWorld);
    disp_bpp = bpp;
    CreateTile();
  }
  if (guest_textmode) {
    text_cols = x / fwidth;
    text_rows = y / fheight;
    if (fwidth != 8) {
      x = x * 8 / fwidth;
    }
    if (fheight != 16) {
      y = y * 16 / fheight;
    }
  }

  if (x != width || y != height)
  {
    SizeWindow(win, x, y, false);
    SizeWindow(fullwin, x, y, false);
    SizeWindow(hidden, x, y, false);
    width = x;
    height = y;
  }

  host_xres = x;
  host_yres = y;
  host_bpp = bpp;
}

// ::CREATE_BITMAP()
//
// rewritten by tim senecal to use the cicn (color icon) resources instead

unsigned bx_macintosh_gui_c::create_bitmap(const unsigned char *bmap, unsigned xdim, unsigned ydim)
{
  unsigned i;
  unsigned char *data;
  long row_bytes, bytecount;

  bx_cicn[numPixMaps] = GetCIcon(numPixMaps+128);
  BX_ASSERT(bx_cicn[numPixMaps]);

  numPixMaps++;

  return(numPixMaps-1);
}

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

  toolPixMaps++;
  hb_index = toolPixMaps-1;
//bx_tool_pixmap[hb_index].pm = bx_pixmap[bmap_id];
  bx_tool_pixmap[hb_index].cicn = bx_cicn[bmap_id];
  bx_tool_pixmap[hb_index].alignment = alignment;
  bx_tool_pixmap[hb_index].f = f;

  if (alignment == BX_GRAVITY_LEFT)
  {
    bx_tool_pixmap[hb_index].xorigin = bx_bitmap_left_xorigin;
    bx_tool_pixmap[hb_index].yorigin = 0;
//  bx_bitmap_left_xorigin += (**bx_pixmap[bmap_id]).bounds.right;
    bx_bitmap_left_xorigin += 34;
  }
  else
  {
//  bx_bitmap_right_xorigin += (**bx_pixmap[bmap_id]).bounds.right;
    bx_bitmap_right_xorigin += 34;
    bx_tool_pixmap[hb_index].xorigin = bx_bitmap_right_xorigin;
    bx_tool_pixmap[hb_index].yorigin = 0;
  }
  return(hb_index);
}

void bx_macintosh_gui_c::show_headerbar(void)
{
  Rect destRect;
  int  i, xorigin;

  SetPort(toolwin);
  RGBForeColor(&medGrey);
  FillRect(&toolwin->portRect, &qd.black);
  for (i=0; i<toolPixMaps; i++)
  {
    if (bx_tool_pixmap[i].alignment == BX_GRAVITY_LEFT)
      xorigin = bx_tool_pixmap[i].xorigin;
    else
      xorigin = toolwin->portRect.right - bx_tool_pixmap[i].xorigin;

    SetRect(&destRect, xorigin, 0, xorigin+32, 32);

    //changed, simply plot the cicn for that button, in the prescribed rectangle
    PlotCIcon(&destRect, bx_tool_pixmap[i].cicn);
  }
  RGBForeColor(&black);

}

void bx_macintosh_gui_c::replace_bitmap(unsigned hbar_id, unsigned bmap_id)
{
//bx_tool_pixmap[hbar_id].pm = bx_pixmap[bmap_id];
  bx_tool_pixmap[hbar_id].cicn = bx_cicn[bmap_id];
  show_headerbar();
}

void bx_macintosh_gui_c::exit(void)
{
  if (!menubarVisible)
    ShowMenubar(); // Make the menubar visible again
  InitCursor();
}

#if 0
void bx_macintosh_gui_c::snapshot_handler(void)
{
  PicHandle ScreenShot;
  long val;

  SetPort(win);

  ScreenShot = OpenPicture(&win->portRect);

  CopyBits(&win->portBits, &win->portBits, &win->portRect, &win->portRect, srcCopy, NULL);

  ClosePicture();

  val = ZeroScrap();

  HLock((Handle)ScreenShot);
  PutScrap(GetHandleSize((Handle)ScreenShot), 'PICT', *ScreenShot);
  HUnlock((Handle)ScreenShot);

  KillPicture(ScreenShot);
}
#endif

// UpdateRgn()
//
// Updates the screen after the menubar and round corners have been hidden

void UpdateRgn(RgnHandle rgn)
{
  WindowPtr window;

  window = FrontWindow();
  PaintBehind(window, rgn);
  CalcVisBehind(window, rgn);
}

// HidePointer()
//
// Hides the Mac mouse pointer

void HidePointer()
{
  HiliteMenu(0);
  HideCursor();
  SetPort(win);
  SetPt(&scrCenter, 320, 240);
  LocalToGlobal(&scrCenter);
  ResetPointer();
  GetMouse(&prevPt);
  cursorVisible = false;
  CheckItem(GetMenuHandle(mBochs), iCursor, false);
}

// ShowPointer()
//
// Shows the Mac mouse pointer

void ShowPointer()
{
  InitCursor();
  cursorVisible = true;
  CheckItem(GetMenuHandle(mBochs), iCursor, true);
}

// HideTools()
//
// Hides the Bochs toolbar

void HideTools()
{
  HideWindow(toolwin);
  if (menubarVisible)
  {
    MoveWindow(win, gLeft, gMinTop, false);
  }
  else
  {
    MoveWindow(hidden, gLeft, gMinTop, false);
  }
  CheckItem(GetMenuHandle(mBochs), iTool, false);
  HiliteWindow(win, true);
}

// ShowTools()
//
// Shows the Bochs toolbar

void ShowTools()
{
  if (menubarVisible)
  {
    MoveWindow(win, gLeft, gMaxTop, false);
  }
  else
  {
    MoveWindow(hidden, gLeft, gMaxTop, false);
  }
  ShowWindow(toolwin);
  BringToFront(toolwin);
  SelectWindow(toolwin);
  HiliteWindow(win, true);
//theGui->show_headerbar();
  CheckItem(GetMenuHandle(mBochs), iTool, true);
  HiliteWindow(win, true);
}

// HideMenubar()
//
// Hides the menubar (obviously)

void HideMenubar()
{
  Rect      mBarRect;
  RgnHandle grayRgn;

  grayRgn = LMGetGrayRgn();
  gOldMBarHeight = GetMBarHeight();
  LMSetMBarHeight(0);
  mBarRgn = NewRgn();
  mBarRect = qd.screenBits.bounds;
  mBarRect.bottom = mBarRect.top + gOldMBarHeight;
  RectRgn(mBarRgn, &mBarRect);
  UnionRgn(grayRgn, mBarRgn, grayRgn);
  UpdateRgn(mBarRgn);

  cnrRgn = NewRgn();
  RectRgn(cnrRgn, &qd.screenBits.bounds);
  DiffRgn(cnrRgn, grayRgn, cnrRgn);
  UnionRgn(grayRgn, cnrRgn, grayRgn);
  UpdateRgn(mBarRgn);

  HideWindow(win);
  ShowWindow(backdrop);
  SelectWindow(backdrop);
  hidden = win;
  win = fullwin;
  ShowWindow(win);

  SelectWindow(win);
  menubarVisible = false;
  CheckItem(GetMenuHandle(mBochs), iMenuBar, false);
}

// ShowMenubar()
//
// Makes the menubar visible again so other programs will display correctly.

void ShowMenubar()
{
  RgnHandle grayRgn;
  GrafPtr   savePort;

  grayRgn = LMGetGrayRgn();
  LMSetMBarHeight(gOldMBarHeight);
  DiffRgn(grayRgn, mBarRgn, grayRgn);
  DisposeRgn(mBarRgn);
  DrawMenuBar();

  GetPort(&savePort);
  SetPort(LMGetWMgrPort());
  SetClip(cnrRgn);
  FillRgn(cnrRgn, &qd.black);
  SetPort(savePort);
  DiffRgn(grayRgn, cnrRgn, grayRgn);
  DisposeRgn(cnrRgn);

  HideWindow(backdrop);
  win = hidden;
  hidden = fullwin;
  HideWindow(hidden);
  ShowWindow(win);
  HiliteWindow(win, true);

  menubarVisible = true;
  CheckItem(GetMenuHandle(mBochs), iMenuBar, true);
}

void HideConsole()
{
  HideWindow(SouixWin);
  CheckItem(GetMenuHandle(mBochs), iConsole, false);
}

void ShowConsole()
{
  ShowWindow(SouixWin);
  SelectWindow(SouixWin);
  CheckItem(GetMenuHandle(mBochs), iConsole, true);
}

// CreateKeyMap()
//
// Create a KCHR data structure to map Mac virtual key codes to Bochs key codes

void CreateKeyMap(void)
{
  const unsigned char KCHRHeader [258] = { 0, 1 };

  const unsigned char KCHRTable [130] = {
    0,
    1,
    BX_KEY_A,
    BX_KEY_S,
    BX_KEY_D,
    BX_KEY_F,
    BX_KEY_H,
    BX_KEY_G,
    BX_KEY_Z,
    BX_KEY_X,
    BX_KEY_C,
    BX_KEY_V,
    BX_KEY_LEFT_BACKSLASH,
    BX_KEY_B,
    BX_KEY_Q,
    BX_KEY_W,
    BX_KEY_E,
    BX_KEY_R,
    BX_KEY_Y,
    BX_KEY_T,
    BX_KEY_1,
    BX_KEY_2,
    BX_KEY_3,
    BX_KEY_4,
    BX_KEY_6,
    BX_KEY_5,
    BX_KEY_EQUALS,
    BX_KEY_9,
    BX_KEY_7,
    BX_KEY_MINUS,
    BX_KEY_8,
    BX_KEY_0,
    BX_KEY_RIGHT_BRACKET,
    BX_KEY_O,
    BX_KEY_U,
    BX_KEY_LEFT_BRACKET,
    BX_KEY_I,
    BX_KEY_P,
    BX_KEY_ENTER,
    BX_KEY_L,
    BX_KEY_J,
    BX_KEY_SINGLE_QUOTE,
    BX_KEY_K,
    BX_KEY_SEMICOLON,
    BX_KEY_BACKSLASH,
    BX_KEY_COMMA,
    BX_KEY_SLASH,
    BX_KEY_N,
    BX_KEY_M,
    BX_KEY_PERIOD,
    BX_KEY_TAB,
    BX_KEY_SPACE,
    BX_KEY_GRAVE,
    BX_KEY_BACKSPACE,
    BX_KEY_KP_ENTER,
    BX_KEY_ESC,
    0, // 0x36 (record button)
    0, // 0x37 (cmd key)
    0, // 0x38 (left shift)
    0, // 0x39 (caps lock)
    0, // 0x3A (left option/alt)
    0, // 0x3B (left ctrl)
    0, // 0x3C (right shift)
    0, // 0x3D (right option/alt)
    0, // 0x3E (right ctrl)
    0, // 0x3F (fn key -- laptops)
    0, // 0x40
    BX_KEY_KP_DELETE, // KP_PERIOD
    0, // 0x42 (move right/multiply)
    BX_KEY_KP_MULTIPLY,
    0, // 0x44
    BX_KEY_KP_ADD,
    0, // 0x46 (move left/add)
    BX_KEY_NUM_LOCK,
    0, // 0x48 (move down/equals)
    0, // 0x49
    0, // 0x4A
    BX_KEY_KP_DIVIDE,
    BX_KEY_KP_ENTER,
    0, // 0x4D (move up/divide)
    BX_KEY_KP_SUBTRACT,
    0, // 0x4F
    0, // 0x50
    BX_KEY_EQUALS, // 0x51 (kp equals)
    BX_KEY_KP_INSERT, // 0x52 (kp 0)
    BX_KEY_KP_END, // 0x53 (kp 1)
    BX_KEY_KP_DOWN, // 0x54 (kp 2)
    BX_KEY_KP_PAGE_DOWN, // 0x55 (kp 3)
    BX_KEY_KP_LEFT, // 0x56 (kp 4)
    BX_KEY_KP_5,
    BX_KEY_KP_RIGHT, // 0x58 (kp 6)
    BX_KEY_KP_HOME, // 0x59 (kp 7)
    0, // 0x5A
    BX_KEY_KP_UP, // 0x5B (kp 8)
    BX_KEY_KP_PAGE_UP, // 0x5C (kp 9)
    0, // 0x5D
    0, // 0x5E
    0, // 0x5F
    BX_KEY_F5,
    BX_KEY_F6,
    BX_KEY_F7,
    BX_KEY_F3,
    BX_KEY_F8,
    BX_KEY_F9,
    0, // 0x66
    BX_KEY_F11,
    0, // 0x68
    BX_KEY_PRINT, // 0x69 (print screen)
    0, // 0x6A
    BX_KEY_SCRL_LOCK, // 0x6B (scroll lock)
    0, // 0x6C
    BX_KEY_F10,
    BX_KEY_MENU, // 0x6E
    BX_KEY_F12,
    0, // 0x70
    BX_KEY_PAUSE, // 0x71 (pause)
    BX_KEY_INSERT,
    BX_KEY_HOME,
    BX_KEY_PAGE_UP,
    BX_KEY_DELETE,
    BX_KEY_F4,
    BX_KEY_END,
    BX_KEY_F2,
    BX_KEY_PAGE_DOWN,
    BX_KEY_F1,
    BX_KEY_LEFT,
    BX_KEY_RIGHT,
    BX_KEY_DOWN,
    BX_KEY_UP
  };

  KCHR = NewPtrClear(390);
  if (KCHR == NULL)
    BX_PANIC(("mac: can't allocate memory for key map"));

  BlockMove(KCHRHeader, KCHR, sizeof(KCHRHeader));
  BlockMove(KCHRTable, Ptr(KCHR + sizeof(KCHRHeader)), sizeof(KCHRTable));
}

// CreateVGAFont()
//
// Create an array of PixMaps for the PC screen font

void CreateVGAFont(void)
{
  int i, x;
  unsigned char *fontData, curPixel;
  long row_bytes, bytecount;

  for (i=0; i<256; i++)
  {
    vgafont[i] = CreateBitMap(FONT_WIDTH, FONT_HEIGHT);
    row_bytes = (*(vgafont[i])).rowBytes;
    bytecount = row_bytes * FONT_HEIGHT;
    fontData = (unsigned char *)NewPtrClear(bytecount);

    for (x=0; x<16; x++)
    {
      //curPixel = ~(bx_vgafont[i].data[x]);
      curPixel = (bx_vgafont[i].data[x]);
      fontData[x*row_bytes] = reverse_bitorder(curPixel);
    }

    vgafont[i]->baseAddr = Ptr(fontData);
  }
}

// CreateBitMap()
// Allocate a new bitmap and fill in the fields with appropriate
// values.

BitMap *CreateBitMap(unsigned width,  unsigned height)
{
  BitMap *bm;
  long   row_bytes;

  row_bytes = ((width + 31) >> 5) << 2;
  bm = (BitMap *)calloc(1, sizeof(BitMap));
  if (bm == NULL)
    BX_PANIC(("mac: can't allocate memory for pixmap"));
  SetRect(&bm->bounds, 0, 0, width, height);
  bm->rowBytes = row_bytes;
  // Quickdraw allocates a new color table by default, but we want to
  // use one we created earlier.

  return bm;
}

// CreatePixMap()
// Allocate a new pixmap handle and fill in the fields with appropriate
// values.

PixMapHandle CreatePixMap(unsigned left, unsigned top, unsigned width,
        unsigned height, unsigned depth, CTabHandle clut)
{
  PixMapHandle pm;
  long         row_bytes;

  row_bytes = (((long) depth * ((long) width) + 31) >> 5) << 2;
  pm = NewPixMap();
  if (pm == NULL)
    BX_PANIC(("mac: can't allocate memory for pixmap"));
  (**pm).bounds.left = left;
  (**pm).bounds.top = top;
  (**pm).bounds.right = left+width;
  (**pm).bounds.bottom = top+height;
  (**pm).pixelSize = depth;
  (**pm).rowBytes = row_bytes | 0x8000;

  DisposeCTable((**pm).pmTable);
  (**pm).pmTable = clut;
  // Quickdraw allocates a new color table by default, but we want to
  // use one we created earlier.

  return pm;
}

void bx_macintosh_gui_c::mouse_enabled_changed_specific(bool val)
{
}

#endif /* if BX_WITH_MACOS */
