
/* 
A* -------------------------------------------------------------------
B* This file contains source code for the PyMOL computer program
C* copyright 1998-2000 by Warren Lyford Delano of DeLano Scientific. 
D* -------------------------------------------------------------------
E* It is unlawful to modify or remove this copyright notice.
F* -------------------------------------------------------------------
G* Please see the accompanying LICENSE file for further information. 
H* -------------------------------------------------------------------
I* Additional authors of this source file include:
-* 
-* 
-*
Z* -------------------------------------------------------------------
*/
#include"os_python.h"

#include"os_predef.h"
#include"os_std.h"
#include"os_gl.h"

#include"main.h"
#include"Version.h"
#include"MemoryDebug.h"
#include"Err.h"
#include"Util.h"
#include"ListMacros.h"
#include"Ortho.h"
#include"P.h"
#include"Scene.h"
#include"Executive.h"
#include"ButMode.h"
#include "Seq.h"
#include"Control.h"
#include"Setting.h"
#include"Wizard.h"
#include"Queue.h"
#include"Pop.h"
#include"Seq.h"
#include"Text.h"
#include"PyMOLOptions.h"
#include"PyMOL.h"
#include"Movie.h"
#include "ShaderMgr.h"

#ifndef true
#define true 1
#endif

#ifndef false
#define false 0
#endif

#define OrthoSaveLines 0xFF
#define OrthoHistoryLines 0xFF

#define cOrthoCharWidth 8
#define cOrthoLeftMargin 3
#define cOrthoBottomMargin 5

#define CMD_QUEUE_MASK 0x3

struct _COrtho {
  Block *Blocks;
  Block *GrabbedBy, *ClickedIn;
  int X, Y, Height, Width;
  int LastX, LastY, LastModifiers;
  int ActiveButton;
  int DrawText;
  int InputFlag;                /* whether or not we have active input on the line */

  OrthoLineType Line[OrthoSaveLines + 1];
  OrthoLineType History[OrthoHistoryLines + 1];
  int HistoryLine, HistoryView;
  int CurLine, CurChar, PromptChar, CursorChar;
  int AutoOverlayStopLine;
  FILE *Pipe;
  char Prompt[255];
  int ShowLines;
  char Saved[OrthoLineLength];
  int SavedPC, SavedCC;
  float TextColor[3], OverlayColor[3], WizardBackColor[3], WizardTextColor[3];
  int DirtyFlag;
  double BusyLast, BusyLastUpdate;
  int BusyStatus[4];
  char BusyMessage[255];
  char *WizardPromptVLA;
  int SplashFlag;
  int HaveSeqViewer;
  BlockRect LoopRect;
  int LoopFlag;
  int cmdNestLevel;
  CQueue *cmdQueue[CMD_QUEUE_MASK + 1], *cmdActiveQueue;
  CQueue *feedback;
  int Pushed;
  CDeferred *deferred;
  int RenderMode;
  GLint ViewPort[4];
  int WrapXFlag;
  GLenum ActiveGLBuffer;
  double DrawTime, LastDraw;
  int WrapClickSide;            /* ugly kludge for finding click side in geowall stereo mode */

  /* packing information */
  int WizardHeight;
  int TextBottom;

  int IssueViewportWhenReleased;
};

void OrthoParseCurrentLine(PyMOLGlobals * G);

Block *OrthoFindBlock(PyMOLGlobals * G, int x, int y);
void OrthoKeyControl(PyMOLGlobals * G, unsigned char k);
void OrthoKeyAlt(PyMOLGlobals * G, unsigned char k);
void OrthoKeyCtSh(PyMOLGlobals * G, unsigned char k);
void OrthoKeyCmmd(PyMOLGlobals * G, unsigned char k);



#define cBusyWidth 240
#define cBusyHeight 60
#define cBusyMargin 10
#define cBusyBar 10
#define cBusySpacing 15

#define cBusyUpdate 0.2

#define cWizardTopMargin 15
#define cWizardLeftMargin 15
#define cWizardBorder 7

static int get_wrap_x(int x, int *last_x, int width, int *click_side)
{
  int width_2 = width / 2;
  int width_3 = width / 3;
  if(!last_x) {
    if(x > width_2) {
      x -= width_2;
      if(click_side)
        *click_side = 1;
    } else {
      if(click_side)
        *click_side = -1;
    }
  } else {
    if((x - (*last_x)) > width_3) {
      x -= width_2;
      if(click_side)
        *click_side = 1;
    } else if(((*last_x) - x) > width_3) {
      x += width_2;
      if(click_side)
        *click_side = 1;
    } else {
      if(click_side)
        *click_side = -1;
    }
  }
  return x;
}

void OrthoDrawBuffer(PyMOLGlobals * G, GLenum mode)
{
  register COrtho *I = G->Ortho;
  if((mode != I->ActiveGLBuffer) && G->HaveGUI && G->ValidContext) {
#ifndef _PYMOL_GL_DRAWARRAYS
	      /* NEED TODO FOR _PYMOL_GL_DRAWARRAYS */
    glDrawBuffer(mode);
#endif
    I->ActiveGLBuffer = mode;
  }
}

int OrthoGetDirty(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return I->DirtyFlag;
}

int OrthoGetRenderMode(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return I->RenderMode;
}

void OrthoSetLoopRect(PyMOLGlobals * G, int flag, BlockRect * rect)
{
  register COrtho *I = G->Ortho;
  I->LoopRect = (*rect);
  I->LoopFlag = flag;
  OrthoDirty(G);
}

int OrthoDeferredWaiting(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return (I->deferred != NULL);
}

void OrthoExecDeferred(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  CDeferred *deferred = I->deferred;

  I->deferred = NULL;
  /* execute all deferred actions that happened to require a
   * valid OpenGL context (such as atom picks, etc.) */

  DeferredExec(deferred);
}

void OrthoDefer(PyMOLGlobals * G, CDeferred * D)
{
  register COrtho *I = G->Ortho;
  register CDeferred *d = I->deferred;
  if(d) {
    while(d->next)
      d = d->next;
    d->next = D;
  } else {
    I->deferred = D;
  }
  OrthoDirty(G);
}

int OrthoGetWidth(PyMOLGlobals * G)
{
  if(G) {
    register COrtho *I = G->Ortho;
    return (I->Width);
  }
  return 0;
}

int OrthoGetHeight(PyMOLGlobals * G)
{
  if(G) {
    register COrtho *I = G->Ortho;
    return (I->Height);
  }
  return 0;
}


/*========================================================================*/
void OrthoFakeDrag(PyMOLGlobals * G)
{                               /* for timing-based events, such as pop-ups */
  register COrtho *I = G->Ortho;
  if(I->GrabbedBy)
    OrthoDrag(G, I->LastX, I->LastY, I->LastModifiers);
}


/*========================================================================*/

void OrthoSetWizardPrompt(PyMOLGlobals * G, char *vla)
{
  register COrtho *I = G->Ortho;
  VLAFreeP(I->WizardPromptVLA);
  I->WizardPromptVLA = vla;
}


/*========================================================================*/
void OrthoSpecial(PyMOLGlobals * G, int k, int x, int y, int mod)
{
  register COrtho *I = G->Ortho;
  int curLine = I->CurLine & OrthoSaveLines;

  PRINTFB(G, FB_Ortho, FB_Blather)
    " OrthoSpecial: %c (%d), x %d y %d, mod %d\n", k, k, x, y, mod ENDFB(G);

  switch (k) {
  case P_GLUT_KEY_DOWN:
    if(I->CurChar && (I->HistoryView == I->HistoryLine)) {
      strcpy(I->History[I->HistoryLine], I->Line[curLine] + I->PromptChar);
    }
    I->HistoryView = (I->HistoryView + 1) & OrthoHistoryLines;
    strcpy(I->Line[curLine], I->Prompt);
    I->PromptChar = strlen(I->Prompt);
    if(I->History[I->HistoryView][0]) {
      strcat(I->Line[curLine], I->History[I->HistoryView]);
      I->CurChar = strlen(I->Line[curLine]);
    } else {
      I->CurChar = I->PromptChar;
    }
    I->InputFlag = 1;
    I->CursorChar = -1;
    break;
  case P_GLUT_KEY_UP:
    if(I->CurChar && (I->HistoryView == I->HistoryLine)) {
      strcpy(I->History[I->HistoryLine], I->Line[curLine] + I->PromptChar);
    }
    I->HistoryView = (I->HistoryView - 1) & OrthoHistoryLines;
    strcpy(I->Line[curLine], I->Prompt);
    I->PromptChar = strlen(I->Prompt);
    if(I->History[I->HistoryView][0]) {
      strcat(I->Line[curLine], I->History[I->HistoryView]);
      I->CurChar = strlen(I->Line[curLine]);
    } else {
      I->CurChar = I->PromptChar;
    }
    I->CursorChar = -1;
    I->InputFlag = 1;
    break;
  case P_GLUT_KEY_LEFT:
    if(I->CursorChar >= 0) {
      I->CursorChar--;
    } else {
      I->CursorChar = I->CurChar - 1;
    }
    if(I->CursorChar < I->PromptChar)
      I->CursorChar = I->PromptChar;
    break;
  case P_GLUT_KEY_RIGHT:
    if(I->CursorChar >= 0) {
      I->CursorChar++;
    } else {
      I->CursorChar = I->CurChar - 1;
    }
    if((unsigned) I->CursorChar > strlen(I->Line[curLine]))
      I->CursorChar = strlen(I->Line[curLine]);
    break;
  }
  OrthoDirty(G);
}


/*========================================================================*/
int OrthoTextVisible(PyMOLGlobals * G)
{
  return (SettingGet(G, cSetting_internal_feedback) ||
          SettingGet(G, cSetting_text) || SettingGet(G, cSetting_overlay));
}


/*========================================================================*/

int OrthoArrowsGrabbed(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return ((I->CurChar > I->PromptChar) && OrthoTextVisible(G));
  /* arrows can't be grabbed if text isn't visible */
}

/*========================================================================*/
int OrthoGetOverlayStatus(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  int overlay = SettingGetGlobal_i(G, cSetting_overlay);
  if(!overlay) {
    if(SettingGetGlobal_i(G, cSetting_auto_overlay) > 0) {
      if(I->CurLine != I->AutoOverlayStopLine) {
        overlay = -1;           /* signal auto overlay */
      }
    }
  }
  return overlay;
}


/*========================================================================*/
void OrthoRemoveAutoOverlay(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  I->AutoOverlayStopLine = I->CurLine;
}


/*========================================================================*/
void OrthoRemoveSplash(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  I->SplashFlag = false;
}


/*========================================================================*/
void OrthoCommandNest(PyMOLGlobals * G, int dir)
{
  register COrtho *I = G->Ortho;
  I->cmdNestLevel += dir;
  {
    int level = I->cmdNestLevel;
    if(level < 0)
      level = 0;
    if(level > CMD_QUEUE_MASK)
      level = CMD_QUEUE_MASK;
    I->cmdActiveQueue = I->cmdQueue[level];
  }
}


/*========================================================================*/
int OrthoCommandOut(PyMOLGlobals * G, char *buffer)
{
  if(G && buffer) {
    register COrtho *I = G->Ortho;

    if(I && I->cmdActiveQueue) {
      int result;
      result = QueueStrOut(I->cmdActiveQueue, buffer);
      return (result);
    } else
      return 0;
  } else
    return 0;
}


/*========================================================================*/
int OrthoCommandWaiting(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return (QueueStrCheck(I->cmdActiveQueue));
}


/*========================================================================*/
void OrthoClear(PyMOLGlobals * G)
{
  int a;
  register COrtho *I = G->Ortho;
  for(a = 0; a <= OrthoSaveLines; a++)
    I->Line[a][0] = 0;
  OrthoNewLine(G, NULL, true);
  OrthoRestorePrompt(G);
  OrthoDirty(G);
}


/*========================================================================*/
void OrthoFeedbackIn(PyMOLGlobals * G, char *buffer)
{
  register COrtho *I = G->Ortho;
  if(G->HaveGUI) {
    if(I->feedback)
      QueueStrIn(I->feedback, buffer);
  }
}


/*========================================================================*/
int OrthoFeedbackOut(PyMOLGlobals * G, char *buffer)
{
  register COrtho *I = G->Ortho;
  if(I->feedback)
    return (QueueStrOut(I->feedback, buffer));
  else
    return (0);
}


/*========================================================================*/
void OrthoDirty(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  PRINTFD(G, FB_Ortho)
    " OrthoDirty: called.\n" ENDFD;
  if(!I->DirtyFlag) {
    I->DirtyFlag = true;
  }

  PyMOL_NeedRedisplay(G->PyMOL);
}


/*========================================================================*/
void OrthoBusyMessage(PyMOLGlobals * G, char *message)
{
  register COrtho *I = G->Ortho;
  if(strlen(message) < 255)
    strcpy(I->BusyMessage, message);
}


/*========================================================================*/
void OrthoBusySlow(PyMOLGlobals * G, int progress, int total)
{
  register COrtho *I = G->Ortho;
  double time_yet = (-I->BusyLastUpdate) + UtilGetSeconds(G);

  PRINTFD(G, FB_Ortho)
    " OrthoBusySlow-DEBUG: progress %d total %d\n", progress, total ENDFD;
  I->BusyStatus[0] = progress;
  I->BusyStatus[1] = total;
  if(SettingGetGlobal_b(G, cSetting_show_progress) && (time_yet > 0.15F)) {
    if(PyMOL_GetBusy(G->PyMOL, false)) {        /* harmless race condition */
#ifndef _PYMOL_NOPY
      int blocked = PAutoBlock(G);
      if(PLockStatusAttempt(G)) {
#endif
        PyMOL_SetProgress(G->PyMOL, PYMOL_PROGRESS_SLOW, progress, total);
        I->BusyLastUpdate = UtilGetSeconds(G);

#ifndef _PYMOL_NOPY
        PUnlockStatus(G);
      }
      PAutoUnblock(G, blocked);
#endif
    }
    OrthoBusyDraw(G, false);
  }
}


/*========================================================================*/
void OrthoBusyFast(PyMOLGlobals * G, int progress, int total)
{
  register COrtho *I = G->Ortho;
  double time_yet = (-I->BusyLastUpdate) + UtilGetSeconds(G);
  PRINTFD(G, FB_Ortho)
    " OrthoBusyFast-DEBUG: progress %d total %d\n", progress, total ENDFD;
  I->BusyStatus[2] = progress;
  I->BusyStatus[3] = total;
  if(SettingGetGlobal_b(G, cSetting_show_progress) && (time_yet > 0.15F)) {
    if(PyMOL_GetBusy(G->PyMOL, false)) {        /* harmless race condition */
#ifndef _PYMOL_NOPY
      int blocked = PAutoBlock(G);
      if(PLockStatusAttempt(G)) {
#endif
        PyMOL_SetProgress(G->PyMOL, PYMOL_PROGRESS_FAST, progress, total);
        I->BusyLastUpdate = UtilGetSeconds(G);
#ifndef _PYMOL_NOPY
        PUnlockStatus(G);
      }
      PAutoUnblock(G, blocked);
#endif
    }
    OrthoBusyDraw(G, false);
  }
}


/*========================================================================*/
void OrthoBusyPrime(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  int a;
  for(a = 0; a < 4; a++)
    I->BusyStatus[a] = 0;
  I->BusyMessage[0] = 0;
  I->BusyLast = UtilGetSeconds(G);
  I->BusyLastUpdate = UtilGetSeconds(G);
}


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef _MACPYMOL_XCODE
void MacPyMOLSetProgress(float value);
#endif

/* END PROPRIETARY CODE SEGMENT */

/*========================================================================*/
void OrthoBusyDraw(PyMOLGlobals * G, int force)
{
  register COrtho *I = G->Ortho;
  double now;
  double busyTime;

  PRINTFD(G, FB_Ortho)
    " OrthoBusyDraw: entered.\n" ENDFD;
  now = UtilGetSeconds(G);
  busyTime = (-I->BusyLast) + now;
  if(SettingGet(G, cSetting_show_progress) && (force || (busyTime > cBusyUpdate))) {

    I->BusyLast = now;
    if(PIsGlutThread()) {

#ifdef _MACPYMOL_XCODE
      /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
      float busyValue = 0.0F;
      if(I->BusyStatus[1]) {
        busyValue = (I->BusyStatus[0] * 1.0F / I->BusyStatus[1]);
      }
      if(I->BusyStatus[3]) {
        busyValue = (I->BusyStatus[2] * 1.0F / I->BusyStatus[3]);
      }
      MacPyMOLSetProgress(busyValue);
      /* END PROPRIETARY CODE SEGMENT */
#else
      if(G->HaveGUI && G->ValidContext) {
        char *c;
        int x, y;
        float black[3] = { 0, 0, 0 };
        float white[3] = { 1, 1, 1 };
        int draw_both = SceneMustDrawBoth(G);
        OrthoPushMatrix(G);
        {
          int pass = 0;
          glClear(GL_DEPTH_BUFFER_BIT);
          while(1) {
            if(draw_both) {
              if(!pass)
                OrthoDrawBuffer(G, GL_FRONT_LEFT);
              else
                OrthoDrawBuffer(G, GL_FRONT_RIGHT);
            } else {
              OrthoDrawBuffer(G, GL_FRONT);     /* draw into the front buffer */
            }

#ifdef PURE_OPENGL_ES_2
	    {
	      GLfloat polyVerts[] = {
                0, I->Height, 0,
                cBusyWidth, I->Height, 0, 
                0, I->Height - cBusyHeight, 0, 
                cBusyWidth, I->Height - cBusyHeight, 0
	      }; 
	      GLenum err ;
	      glVertexAttrib3fv(VERTEX_COLOR, black);
	      glVertexAttribPointer(VERTEX_POS, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, polyVerts);
	      glEnableVertexAttribArray(VERTEX_POS);
	      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	      glDisableVertexAttribArray(VERTEX_POS);
	    }
	    glVertexAttrib3fv(VERTEX_COLOR, white);
#else
            glColor3fv(black);
#ifdef _PYMOL_GL_DRAWARRAYS
	    {
	      const GLint polyVerts[] = {
                0, I->Height,
                cBusyWidth, I->Height,
                0, I->Height - cBusyHeight,
                cBusyWidth, I->Height - cBusyHeight
	      };
	      glEnableClientState(GL_VERTEX_ARRAY);
	      glEnableClientState(GL_COLOR_ARRAY);
	      glVertexPointer(2, GL_INT, 0, polyVerts);
	      glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	      glDisableClientState(GL_VERTEX_ARRAY);
	    }
#else
            glBegin(GL_POLYGON);
            glVertex2i(0, I->Height);
            glVertex2i(cBusyWidth, I->Height);
            glVertex2i(cBusyWidth, I->Height - cBusyHeight);
            glVertex2i(0, I->Height - cBusyHeight);
            glVertex2i(0, I->Height);   /* needed on old buggy Mesa */
            glEnd();
#endif
            glColor3fv(white);
#endif

            y = I->Height - cBusyMargin;
            c = I->BusyMessage;
            if(*c) {
              TextSetColor(G, white);
              TextSetPos2i(G, cBusyMargin, y - (cBusySpacing / 2));
              TextDrawStr(G, c);
              y -= cBusySpacing;
            }

            if(I->BusyStatus[1]) {
#if defined(_PYMOL_GL_DRAWARRAYS) || defined(PURE_OPENGL_ES_2)
	      {
		const GLint lineVerts[] = {
		  cBusyMargin, y,
		  cBusyWidth - cBusyMargin, y,
		  cBusyWidth - cBusyMargin, y - cBusyBar,
		  cBusyMargin, y - cBusyBar,
		  cBusyMargin, y       /* needed on old buggy Mesa */
		};
#endif
      
#ifdef PURE_OPENGL_ES_2
		glEnableVertexAttribArray(VERTEX_POS);
		glVertexAttribPointer(VERTEX_POS, 2, GL_INT, GL_FALSE, 0, lineVerts);	
		glDrawArrays(GL_LINE_LOOP, 0, 5);
		glDisableVertexAttribArray(VERTEX_POS);
	      }
#else		
#ifdef _PYMOL_GL_DRAWARRAYS
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_INT, 0, lineVerts);
		glDrawArrays(GL_LINE_LOOP, 0, 5);
		glDisableClientState(GL_VERTEX_ARRAY);
	      }
#else
              glBegin(GL_LINE_LOOP);
              glVertex2i(cBusyMargin, y);
              glVertex2i(cBusyWidth - cBusyMargin, y);
              glVertex2i(cBusyWidth - cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y);       /* needed on old buggy Mesa */
              glEnd();
#endif
	      glColor3fv(white);
#endif
              x =
                (I->BusyStatus[0] * (cBusyWidth - 2 * cBusyMargin) / I->BusyStatus[1]) +
                cBusyMargin;
#if defined(_PYMOL_GL_DRAWARRAYS) || defined(PURE_OPENGL_ES_2)
	      {
		const GLint polyVerts[] = {
		  cBusyMargin, y,
		  x, y,
		  cBusyMargin, y - cBusyBar,
		  x, y - cBusyBar,
		};	      
#endif
#ifdef PURE_OPENGL_ES_2
		glEnableVertexAttribArray(VERTEX_POS);
		glVertexAttribPointer(VERTEX_POS, 2, GL_INT, GL_FALSE, 0, polyVerts);	      
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		glDisableVertexAttribArray(VERTEX_POS);
	      }
#else
#ifdef _PYMOL_GL_DRAWARRAYS
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_INT, 0, polyVerts);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		glDisableClientState(GL_VERTEX_ARRAY);
	      }
#else
              glBegin(GL_POLYGON);
              glVertex2i(cBusyMargin, y);
              glVertex2i(x, y);
              glVertex2i(x, y - cBusyBar);
              glVertex2i(cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y);       /* needed on old buggy Mesa */
              glEnd();
#endif
#endif
              y -= cBusySpacing;
            }

            if(I->BusyStatus[3]) {
#if defined(_PYMOL_GL_DRAWARRAYS) || defined(PURE_OPENGL_ES_2)
	      {
		const GLint lineVerts[] = {
		  cBusyMargin, y,
		  cBusyWidth - cBusyMargin, y,
		  cBusyWidth - cBusyMargin, y - cBusyBar,
		  cBusyMargin, y - cBusyBar,
		  cBusyMargin, y       /* needed on old buggy Mesa */
		};
#endif
#ifdef PURE_OPENGL_ES_2
		glVertexAttrib3fv(VERTEX_COLOR, white);
		glEnableVertexAttribArray(VERTEX_POS);
		glVertexAttribPointer(VERTEX_POS, 2, GL_INT, GL_FALSE, 0, lineVerts);	      
		glDrawArrays(GL_LINE_LOOP, 0, 5);
		glDisableVertexAttribArray(VERTEX_POS);
	      }
#else
              glColor3fv(white);
#ifdef _PYMOL_GL_DRAWARRAYS
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_INT, 0, lineVerts);
		glDrawArrays(GL_LINE_LOOP, 0, 5);
		glDisableClientState(GL_VERTEX_ARRAY);
	      }
#else
              glBegin(GL_LINE_LOOP);
              glVertex2i(cBusyMargin, y);
              glVertex2i(cBusyWidth - cBusyMargin, y);
              glVertex2i(cBusyWidth - cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y);       /* needed on old buggy Mesa */
              glEnd();
#endif
#endif
              x =
                (I->BusyStatus[2] * (cBusyWidth - 2 * cBusyMargin) / I->BusyStatus[3]) +
                cBusyMargin;
#if defined(_PYMOL_GL_DRAWARRAYS) || defined(PURE_OPENGL_ES_2)
	      {
		const GLint polyVerts[] = {
		  cBusyMargin, y,
		  x, y,
		  cBusyMargin, y - cBusyBar,
		  x, y - cBusyBar
		};
#endif
#ifdef PURE_OPENGL_ES_2
		glVertexAttrib3fv(VERTEX_COLOR, white);
		glEnableVertexAttribArray(VERTEX_POS);
		glVertexAttribPointer(VERTEX_POS, 2, GL_INT, GL_FALSE, 0, polyVerts);	      
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		glDisableVertexAttribArray(VERTEX_POS);
	      }
#else
              glColor3fv(white);
#ifdef _PYMOL_GL_DRAWARRAYS
		glEnableClientState(GL_VERTEX_ARRAY);
		glVertexPointer(2, GL_INT, 0, polyVerts);
		glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
		glDisableClientState(GL_VERTEX_ARRAY);
	      }
#else
              glBegin(GL_POLYGON);
              glVertex2i(cBusyMargin, y);
              glVertex2i(x, y);
              glVertex2i(x, y - cBusyBar);
              glVertex2i(cBusyMargin, y - cBusyBar);
              glVertex2i(cBusyMargin, y);       /* needed on old buggy Mesa */
              glEnd();
#endif
#endif
              y -= cBusySpacing;
            }

            if(!draw_both)
              break;
            if(pass > 1)
              break;
            pass++;
          }

          glFlush();
          glFinish();

          if(draw_both)
            OrthoDrawBuffer(G, GL_BACK_LEFT);
          else
            OrthoDrawBuffer(G, GL_BACK);
        }
        OrthoPopMatrix(G);
        OrthoDirty(G);
      }
#endif

    }
  }

  PRINTFD(G, FB_Ortho)
    " OrthoBusyDraw: leaving...\n" ENDFD;

}


/*========================================================================*/
void OrthoRestorePrompt(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  int curLine;
  if(!I->InputFlag) {
    if(I->Saved[0]) {
      if(I->CurChar) {
        OrthoNewLine(G, NULL, true);
      }
      curLine = I->CurLine & OrthoSaveLines;
      strcpy(I->Line[curLine], I->Saved);
      I->Saved[0] = 0;
      I->CurChar = I->SavedCC;
      I->PromptChar = I->SavedPC;
    } else {
      if(I->CurChar)
        OrthoNewLine(G, I->Prompt, true);
      else {
        curLine = I->CurLine & OrthoSaveLines;
        strcpy(I->Line[curLine], I->Prompt);
        I->CurChar = (I->PromptChar = strlen(I->Prompt));
      }
    }
    I->InputFlag = 1;
  }
}


/*========================================================================*/
void OrthoKeyControl(PyMOLGlobals * G, unsigned char k)
{
  char buffer[OrthoLineLength];

  /* safer... */

  sprintf(buffer, "cmd._ctrl(chr(%d))", k);
  /* sprintf(buffer,"_ctrl %c",k); */
  PLog(G, buffer, cPLog_pym);
  PParse(G, buffer);
  PFlush(G);

}

/*========================================================================*/
void OrthoKeyCmmd(PyMOLGlobals * G, unsigned char k)
{
  char buffer[OrthoLineLength];

  /* safer... */

  sprintf(buffer, "cmd._cmmd(chr(%d))", k);
  /* sprintf(buffer,"_ctrl %c",k); */
  PLog(G, buffer, cPLog_pym);
  PParse(G, buffer);
  PFlush(G);

}

/*========================================================================*/
void OrthoKeyCtSh(PyMOLGlobals * G, unsigned char k)
{
  char buffer[OrthoLineLength];

  /* safer... */

  sprintf(buffer, "cmd._ctsh(chr(%d))", k);
  /* sprintf(buffer,"_ctrl %c",k); */
  PLog(G, buffer, cPLog_pym);
  PParse(G, buffer);
  PFlush(G);

}


/*========================================================================*/
void OrthoKeyAlt(PyMOLGlobals * G, unsigned char k)
{
  char buffer[OrthoLineLength];

  /* safer... */

  if(k == '@') {
    /* option G produces '@' on some non-US keyboards, so simply
       ignore the modifier */
    OrthoKey(G, k, 0, 0, 0);
  } else {
    sprintf(buffer, "cmd._alt(chr(%d))", k);
    /* sprintf(buffer,"_alt %c",k); */
    PLog(G, buffer, cPLog_pym);
    PParse(G, buffer);
    PFlush(G);
  }

}

static int add_normal_char(COrtho * I, unsigned char k)
{
  char buffer[OrthoLineLength];
  int curLine = I->CurLine & OrthoSaveLines;
  if(I->CursorChar >= 0) {
    strcpy(buffer, I->Line[curLine] + I->CursorChar);
    I->Line[curLine][I->CursorChar] = k;
    I->CursorChar++;
    I->CurChar++;
    strcpy(I->Line[curLine] + I->CursorChar, buffer);
  } else {
    I->Line[curLine][I->CurChar] = k;
    I->CurChar++;
    I->Line[curLine][I->CurChar] = 0;
  }
  return curLine;
}


/*========================================================================*/
void OrthoKey(PyMOLGlobals * G, unsigned char k, int x, int y, int mod)
{
  register COrtho *I = G->Ortho;
  char buffer[OrthoLineLength];
  int curLine;

  PRINTFB(G, FB_Ortho, FB_Blather)
    " OrthoKey: %c (%d), x %d y %d, mod %d\n", k, k, x, y, mod ENDFB(G);

  if(!I->InputFlag) {
    if(I->Saved[0]) {
      if(I->CurChar) {
        OrthoNewLine(G, NULL, true);
      }
      curLine = I->CurLine & OrthoSaveLines;
      strcpy(I->Line[curLine], I->Saved);
      I->Saved[0] = 0;
      I->CurChar = I->SavedCC;
      I->PromptChar = I->SavedPC;
    } else {
      if(I->CurChar) {
        OrthoNewLine(G, I->Prompt, true);
      } else {
        curLine = I->CurLine & OrthoSaveLines;
        strcpy(I->Line[curLine], I->Prompt);
        I->CurChar = (I->PromptChar = strlen(I->Prompt));
      }
    }
    I->InputFlag = 1;
  }
  if(mod == 4) {                /* alt */
    OrthoKeyAlt(G, k);
  } else if  (mod == 3) {       /* chsh */
    OrthoKeyCtSh(G,(unsigned int) (k+64));
  } else if((k > 32) && (k != 127)) {
    curLine = add_normal_char(I, k);
  } else
    switch (k) {
    case 32:                   /* spacebar */
      if((!OrthoArrowsGrabbed(G)) &&
	 (I->CurChar == I->PromptChar)) { /* no text entered yet... */
        if(SettingGetGlobal_b(G, cSetting_presentation)) {
          if(mod & cOrthoSHIFT) {
            OrthoCommandIn(G,"rewind;mplay");
          } else {
            PParse(G, "cmd.scene('','next')");
          }
        } else {
          if(mod & cOrthoSHIFT) {
            OrthoCommandIn(G,"rewind;mplay");
          } else {
            OrthoCommandIn(G,"mtoggle");
          }
        }
      } else {
        curLine = add_normal_char(I, k);
      }
      break;
    case 127:                  /* delete */
#if !defined(_PYMOL_OSX) || defined(_PYMOL_LIB)
      /* this defined(_PYMOL_LIB) should really be for JyMOL, not all _PYMOL_LIB, AX? */
      if((!I->CurChar) || (I->CurChar == I->PromptChar) || !OrthoTextVisible(G)) {
        OrthoKeyControl(G, 4 + 64);
      } else {
        if(I->CursorChar >= 0) {
          if(I->CursorChar < I->CurChar)
            I->CursorChar++;
          if(I->CursorChar == I->CurChar)
            I->CursorChar = -1;
        }
        if(I->CurChar > I->PromptChar) {
          curLine = I->CurLine & OrthoSaveLines;
          if(I->CursorChar >= 0) {
            if(I->CursorChar > I->PromptChar) {
              strcpy(buffer, I->Line[curLine] + I->CursorChar);
              I->CursorChar--;
              I->CurChar--;
              strcpy(I->Line[curLine] + I->CursorChar, buffer);
            }
          } else {
            I->CurChar--;
            I->Line[curLine][I->CurChar] = 0;
          }
        }
      }
      break;
    case 8:                    /* backspace */
#endif
      if(I->CurChar > I->PromptChar) {
        curLine = I->CurLine & OrthoSaveLines;
        if(I->CursorChar >= 0) {
          if(I->CursorChar > I->PromptChar) {
            strcpy(buffer, I->Line[curLine] + I->CursorChar);
            I->Line[curLine][I->CursorChar] = k;
            I->CursorChar--;
            I->CurChar--;
            strcpy(I->Line[curLine] + I->CursorChar, buffer);
          }
        } else {
          I->CurChar--;
          I->Line[curLine][I->CurChar] = 0;
        }
      }
      break;
    case 5:                    /* CTRL E -- ending */
      if(OrthoArrowsGrabbed(G)) {
        I->CursorChar = -1;
      } else
        OrthoKeyControl(G, (unsigned char) (k + 64));
      break;
    case 1:                    /* CTRL A -- beginning */
      if(OrthoArrowsGrabbed(G)) {
        if(I->CurChar)
          I->CursorChar = I->PromptChar;
      } else
        OrthoKeyControl(G, (unsigned char) (k + 64));
      break;
    case 4:                    /* CTRL D */
      if((!I->CurChar) || (I->CurChar == I->PromptChar) || !OrthoTextVisible(G)) {
        OrthoKeyControl(G, (unsigned char) (4 + 64));
      } else if((I->CurChar > I->PromptChar) && (I->CursorChar >= 0) && (I->CursorChar < I->CurChar)) { /* deleting */
        curLine = I->CurLine & OrthoSaveLines;
        strcpy(buffer, I->Line[curLine] + I->CursorChar + 1);
        I->CurChar--;
        strcpy(I->Line[curLine] + I->CursorChar, buffer);
      } else {                  /* filename completion query */
        curLine = I->CurLine & OrthoSaveLines;
        if(I->PromptChar) {
          strcpy(buffer, I->Line[curLine]);
          if(PComplete(G, buffer + I->PromptChar, sizeof(OrthoLineType) - I->PromptChar));      /* just print, don't complete */
        }
      }
      break;
    case 9:                    /* CTRL I -- tab */
      if(mod & cOrthoCTRL) {
        OrthoKeyControl(G, (unsigned char) (k + 64));
      } else {   
        curLine = I->CurLine & OrthoSaveLines;
        if(I->PromptChar) {
          strcpy(buffer, I->Line[curLine]);

          if(PComplete(G, buffer + I->PromptChar, sizeof(OrthoLineType) - I->PromptChar)) {
            OrthoRestorePrompt(G);
            curLine = I->CurLine & OrthoSaveLines;
            strcpy(I->Line[curLine], buffer);
            I->CurChar = strlen(I->Line[curLine]);
          }
        }
      }
      break;
    case 27:                   /* ESCAPE */
      if(SettingGetGlobal_b(G, cSetting_presentation)
         && !(mod & (cOrthoCTRL || cOrthoSHIFT))) {
        PParse(G, "_quit");
      } else {
        if(I->SplashFlag) {
          OrthoRemoveSplash(G);
        } else {
          if(mod & cOrthoSHIFT)
            SettingSet(G, cSetting_overlay,
                       (float) (!((int) SettingGet(G, cSetting_overlay))));
          else
            SettingSet(G, cSetting_text, (float) (!((int) SettingGet(G, cSetting_text))));
        }
      }
      break;
    case 13:                   /* CTRL M -- carriage return */
      if(I->CurChar > I->PromptChar)
        OrthoParseCurrentLine(G);
      else if(((SettingGetGlobal_b(G, cSetting_movie_panel) ||
                SettingGetGlobal_b(G, cSetting_presentation)) 
               && MovieGetLength(G))) {
        if(mod & cOrthoSHIFT) {
          if(mod & cOrthoCTRL) 
            OrthoCommandIn(G,"mview toggle_interp,quiet=1,object=same");	    
          else
            OrthoCommandIn(G,"mview toggle_interp,quiet=1");	    
        } else if(mod & cOrthoCTRL) {
          OrthoCommandIn(G,"mview toggle,freeze=1,quiet=1");
        } else {
          if(SettingGetGlobal_b(G, cSetting_presentation)) {
            OrthoCommandIn(G,"mtoggle");  
          } else {
            OrthoCommandIn(G,"mview toggle,quiet=1");
          }
        }
      }
      break;
    case 11:                   /* CTRL K -- truncate */
      if(OrthoArrowsGrabbed(G)) {
        if(I->CursorChar >= 0) {
          I->Line[I->CurLine & OrthoSaveLines][I->CursorChar] = 0;
          I->CurChar = I->CursorChar;
          I->CursorChar = -1;
        }
      } else {
        if(mod & cOrthoCTRL) {
          OrthoKeyControl(G, (unsigned char) (k + 64));
        }
      }
      break;
    case 22:                   /* CTRL V -- paste */
#ifndef _PYMOL_NOPY
      if (I->CurChar != I->PromptChar) { /* no text entered yet... */
	PBlockAndUnlockAPI(G);
	PRunStringInstance(G, "cmd.paste()");
	PLockAPIAndUnblock(G);
      } else {
	OrthoKeyControl(G, (unsigned char) (k + 64));
      }
#endif
      break;
    default:
      OrthoKeyControl(G, (unsigned char) (k + 64));
      break;
    }
  PyMOL_NeedRedisplay(G->PyMOL);
}


/*========================================================================*/
void OrthoParseCurrentLine(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  char buffer[OrthoLineLength];
  int curLine;

  OrthoRemoveAutoOverlay(G);
  curLine = I->CurLine & OrthoSaveLines;
  I->Line[curLine][I->CurChar] = 0;
  strcpy(buffer, I->Line[curLine] + I->PromptChar);
#ifndef _PYMOL_NOPY
  if(buffer[0]) {
    strcpy(I->History[I->HistoryLine], buffer);
    I->HistoryLine = (I->HistoryLine + 1) & OrthoHistoryLines;
    I->History[I->HistoryLine][0] = 0;
    I->HistoryView = I->HistoryLine;
    OrthoNewLine(G, NULL, true);
    if(WordMatch(G, buffer, "quit", true) == 0) /* don't log quit */
      PLog(G, buffer, cPLog_pml);
    OrthoDirty(G);              /* this will force a redraw, if necessary */
    PParse(G, buffer);
    OrthoRestorePrompt(G);
  }
#endif
  I->CursorChar = -1;
}


/*========================================================================*/
void OrthoAddOutput(PyMOLGlobals * G, char *str)
{
  register COrtho *I = G->Ortho;
  int curLine;
  char *p, *q;
  int cc;
  int wrap;
  curLine = I->CurLine & OrthoSaveLines;
  if(I->InputFlag) {
    strcpy(I->Saved, I->Line[curLine]);
    I->SavedPC = I->PromptChar;
    I->SavedCC = I->CurChar;
    I->PromptChar = 0;
    I->CurChar = 0;
    I->Line[curLine][0] = 0;
    I->InputFlag = 0;
  }
  curLine = I->CurLine & OrthoSaveLines;
  p = str;
  q = I->Line[curLine] + I->CurChar;
  cc = I->CurChar;
  while(*p) {
    if(*p >= 32) {
      cc++;
      wrap = (int) SettingGet(G, cSetting_wrap_output);

      if(wrap > 0) {
        if(cc > wrap) {
          *q = 0;
          I->CurChar = cc;
          OrthoNewLine(G, NULL, true);
          cc = 0;
          q = I->Line[I->CurLine & OrthoSaveLines];
          curLine = I->CurLine & OrthoSaveLines;
        }
      }
      if(cc >= OrthoLineLength - 6) {   /* fail safe */
        *q = 0;
        I->CurChar = cc;
        OrthoNewLine(G, NULL, false);
        cc = 0;
        q = I->Line[I->CurLine & OrthoSaveLines];
        curLine = I->CurLine & OrthoSaveLines;
      }
      *q++ = *p++;
    } else if((*p == 13) || (*p == 10)) {
      *q = 0;
      I->CurChar = cc;
      OrthoNewLine(G, NULL, true);
      q = I->Line[I->CurLine & OrthoSaveLines];
      curLine = I->CurLine & OrthoSaveLines;
      p++;
      cc = 0;
    } else
      p++;
  }
  *q = 0;
  I->CurChar = strlen(I->Line[curLine]);
  if((SettingGet(G, cSetting_internal_feedback) > 1) ||
     SettingGet(G, cSetting_overlay) || SettingGet(G, cSetting_auto_overlay))
    OrthoDirty(G);
}


/*========================================================================*/
void OrthoNewLine(PyMOLGlobals * G, char *prompt, int crlf)
{
  int curLine;
  register COrtho *I = G->Ortho;

  /*  printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L %d\n",I->CurChar,I->CurLine,
     I->PromptChar,I->InputFlag); */
  /*  if(I->CurChar)
     { */
  if(I->CurChar)
    OrthoFeedbackIn(G, I->Line[I->CurLine & OrthoSaveLines]);
  else
    OrthoFeedbackIn(G, " ");
  if(Feedback(G, FB_Python, FB_Output)) {
    if(crlf) {
      printf("%s\n", I->Line[I->CurLine & OrthoSaveLines]);
    } else {
      printf("%s", I->Line[I->CurLine & OrthoSaveLines]);
    }
    fflush(stdout);
  }
  /*        } */

  /*  if(I->Line[I->CurLine&OrthoSaveLines][0]) */
  I->CurLine++;
  curLine = I->CurLine & OrthoSaveLines;

  if(prompt) {
    strcpy(I->Line[curLine], prompt);
    I->CurChar = (I->PromptChar = strlen(prompt));
    I->InputFlag = 1;
  } else {
    I->CurChar = 0;
    I->Line[curLine][0] = 0;
    I->PromptChar = 0;
    I->InputFlag = 0;
  }
  /*printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L %d\n",I->CurChar,I->CurLine,
     I->PromptChar,I->InputFlag); */

}


/*========================================================================*/
void OrthoGrab(PyMOLGlobals * G, Block * block)
{
  register COrtho *I = G->Ortho;
  I->GrabbedBy = block;
}

int OrthoGrabbedBy(PyMOLGlobals * G, Block * block)
{
  register COrtho *I = G->Ortho;
  return I->GrabbedBy == block;
}

void OrthoDoViewportWhenReleased(PyMOLGlobals *G)
{
  register COrtho *I = G->Ortho;
  if(!(I->GrabbedBy||I->ClickedIn)) { /* no active UI element? */
    OrthoCommandIn(G, "viewport"); /* then issue viewport refresh */
    OrthoDirty(G);
  } else {
    I->IssueViewportWhenReleased = true; /* otherwise, defer */
  }
}

/*========================================================================*/
void OrthoUngrab(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  I->GrabbedBy = NULL;
}


/*========================================================================*/
Block *OrthoNewBlock(PyMOLGlobals * G, Block * block)
{
  if(!block)
    ListElemAlloc(G, block, Block);
  UtilZeroMem(block, sizeof(Block));
  BlockInit(G, block);
  return (block);
}


/*========================================================================*/
void OrthoFreeBlock(PyMOLGlobals * G, Block * block)
{
  if(block)
    ListElemFree(block);
}


/*========================================================================*/
void OrthoAttach(PyMOLGlobals * G, Block * block, int type)
{
  register COrtho *I = G->Ortho;
  ListInsert(I->Blocks, block, NULL, next, Block);
}


/*========================================================================*/
void OrthoDetach(PyMOLGlobals * G, Block * block)
{
  register COrtho *I = G->Ortho;
  if(I->GrabbedBy == block)
    I->GrabbedBy = NULL;
  ListDetach(I->Blocks, block, next, Block);
}

float *OrthoGetOverlayColor(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  return I->OverlayColor;
}


/*========================================================================*/

/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef PYMOL_EVAL
#include "OrthoEvalMessage.h"
#endif
#ifdef PYMOL_BETA
#include "OrthoBetaMessage.h"
#endif
#ifdef JYMOL_EVAL
#include "OrthoJyMolEvalMessage.h"
#endif
#ifdef PYMOL_EDU
#include "OrthoEduMessage.h"
#endif
#ifdef PYMOL_COLL
#include "OrthoCollMessage.h"
#endif
#ifdef AXPYMOL_EVAL
#include "OrthoAxMessage.h"
#endif

/* END PROPRIETARY CODE SEGMENT */

/* draw background gradient from bg_rgb_top
 * to bg_rgb_bottom is bg_gradient is set
 */

void bg_grad(PyMOLGlobals * G) {
  float * top = SettingGet_3fv(G, NULL, NULL, cSetting_bg_rgb_top);
  float * bottom = SettingGet_3fv(G, NULL, NULL, cSetting_bg_rgb_bottom);
  float alpha =  SettingGet_i(G, NULL, NULL, cSetting_opaque_background) ? 1.0 : 0.0;

  if (! SettingGet_b(G, NULL, NULL, cSetting_bg_gradient))
    return;

#ifdef PURE_OPENGL_ES_2
#else
  glMatrixMode (GL_MODELVIEW);
  glPushMatrix ();
  glLoadIdentity ();
  glMatrixMode (GL_PROJECTION);
  glPushMatrix ();
  glLoadIdentity ();
  glDisable(GL_LIGHTING);
#endif
  glDisable(GL_DEPTH_TEST);

#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
#ifdef _PYMOL_GL_DRAWARRAYS
  {
    const GLfloat colorVals[] = {
      bottom[0], bottom[1], bottom[2], alpha,
      bottom[0], bottom[1], bottom[2], alpha,
      top[0], top[1], top[2], alpha,
      top[0], top[1], top[2], alpha
    };
    const GLfloat polyVerts[] = {
      -1.0f, -1.0f, -1.0f,
      1.0f, -1.0f, -1.0f,
      -1.0f, 1.0f, -1.0f,
      1.0f, 1.0f, -1.0f
    };
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, polyVerts);    
    glColorPointer(4, GL_FLOAT, 0, colorVals);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
  }
#else
  glBegin (GL_QUADS);
  glColor4f(bottom[0], bottom[1], bottom[2], alpha);
  glVertex3f (-1.0f, -1.0f, -1.0f);
  glVertex3f (1.0f, -1.0f, -1.0f);

  glColor4f(top[0], top[1], top[2], alpha);
  glVertex3f (1.0f, 1.0f, -1.0f);
  glVertex3f (-1.0f, 1.0f, -1.0f);
  glEnd ();
#endif
#endif

#ifndef PURE_OPENGL_ES_2
  glEnable(GL_LIGHTING);
  glEnable(GL_DEPTH_TEST);
  glPopMatrix ();
  glMatrixMode (GL_MODELVIEW);
  glPopMatrix ();
#endif
}

void OrthoDoDraw(PyMOLGlobals * G, int render_mode)
{
  register COrtho *I = G->Ortho;

  int x, y;
  int l, lcount;
  char *str;
  float *v;
  int showLines;
  int height;
  int overlay, text;
  int rightSceneMargin;
  int internal_feedback;
  int times = 1;
  int double_pump = false;
  float *bg_color;
  int skip_prompt = 0;
  int render = false;
  int internal_gui_mode = SettingGetGlobal_i(G, cSetting_internal_gui_mode);

  I->RenderMode = render_mode;
  if(SettingGetGlobal_b(G, cSetting_seq_view)) {
    SeqUpdate(G);
    I->HaveSeqViewer = true;
  } else if(I->HaveSeqViewer) {
    SeqUpdate(G);
    I->HaveSeqViewer = false;
  }

  if(SettingGet_i(G, NULL, NULL, cSetting_internal_prompt))
    skip_prompt = 0;
  else
    skip_prompt = 1;

  double_pump = SettingGet_i(G, NULL, NULL, cSetting_stereo_double_pump_mono);
  bg_color = SettingGet_3fv(G, NULL, NULL, cSetting_bg_rgb);

  I->OverlayColor[0] = 1.0F - bg_color[0];
  I->OverlayColor[1] = 1.0F - bg_color[1];
  I->OverlayColor[2] = 1.0F - bg_color[2];
  if(diff3f(I->OverlayColor, bg_color) < 0.25)
    zero3f(I->OverlayColor);

  PRINTFD(G, FB_Ortho)
    " OrthoDoDraw: entered.\n" ENDFD;
  if(G->HaveGUI && G->ValidContext) {

    if(Feedback(G, FB_OpenGL, FB_Debugging))
      PyMOLCheckOpenGLErr("OrthoDoDraw checkpoint 0");

    if(SettingGetGlobal_b(G, cSetting_internal_gui)) {
      switch (SettingGetGlobal_i(G, cSetting_internal_gui_mode)) {
      case 0:
        rightSceneMargin = (int) SettingGet(G, cSetting_internal_gui_width);
        break;
      default:
        rightSceneMargin = 0;
        break;
      }
    } else {
      rightSceneMargin = 0;
    }

    internal_feedback = (int) SettingGet(G, cSetting_internal_feedback);

    v = SettingGetfv(G, cSetting_bg_rgb);
    overlay = OrthoGetOverlayStatus(G);
    switch (overlay) {
    case -1:                   /* auto overlay */
      overlay = I->CurLine - I->AutoOverlayStopLine;
      if(overlay < 0) {
        overlay += (OrthoSaveLines + 1);
      }
      if(internal_feedback > 1) {
        overlay -= (internal_feedback - 1);
      }
      if(overlay < 0)
        overlay = 0;
      break;
    case 1:                    /* default -- user overlay_lines */
      overlay = (int) SettingGet(G, cSetting_overlay_lines);
      break;
    }

    text = (int) SettingGet(G, cSetting_text);
    if(text)
      overlay = 0;

    {
      float alpha = (SettingGetGlobal_b(G, cSetting_opaque_background) ? 1.0F : 0.0F);
      //plain clear color
      glClearColor(v[0], v[1], v[2], alpha);
    }
    if(overlay || (!text))
      if(!SceneRenderCached(G))
        render = true;

    if(render_mode < 2) {
      if(SceneMustDrawBoth(G)) {
        OrthoDrawBuffer(G, GL_BACK_LEFT);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	bg_grad(G);
        OrthoDrawBuffer(G, GL_BACK_RIGHT);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	bg_grad(G);
        times = 2;
        double_pump = true;
      } else {
        OrthoDrawBuffer(G, GL_BACK);
        glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
	bg_grad(G);
        times = 1;
        double_pump = false;
      }
    } else {
      times = 1;
      double_pump = false;
    }

    I->DrawTime = -I->LastDraw;
    I->LastDraw = UtilGetSeconds(G);
    I->DrawTime += I->LastDraw;
    ButModeSetRate(G, (float) I->DrawTime);

    if(render && (render_mode < 2))
      SceneRender(G, NULL, 0, 0, NULL, 0, 0, 0,
                  SettingGetGlobal_b(G, cSetting_image_copy_always));

    glClearColor(0.0, 0.0, 0.0, 1.0);

    while(times--) {

      switch (times) {
      case 1:
        OrthoDrawBuffer(G, GL_BACK_LEFT);

        break;
      case 0:
        if(double_pump) {
          OrthoDrawBuffer(G, GL_BACK_RIGHT);
        } else
          OrthoDrawBuffer(G, GL_BACK);
        break;
      }

      OrthoPushMatrix(G);

      x = I->X;
      y = I->Y;

      if(I->DrawText && internal_feedback) {    /* moved to avoid conflict with menus */
        Block *block = SceneGetBlock(G);
        height = block->rect.bottom;
        switch (internal_gui_mode) {
        case 0:
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
          glColor3f(0.0, 0.0, 0.0);
#ifdef _PYMOL_GL_DRAWARRAYS
	  {
	    const GLint polyVerts[] = {
	      I->Width - rightSceneMargin, height - 1,
	      I->Width - rightSceneMargin, 0,
	      0, height - 1,
	      0, 0
	    };
	    glEnableClientState(GL_VERTEX_ARRAY);
	    glVertexPointer(2, GL_INT, 0, polyVerts);	  
	    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	    glDisableClientState(GL_VERTEX_ARRAY);	  
	  }
#else
          glBegin(GL_POLYGON);
          glVertex2i(I->Width - rightSceneMargin, height - 1);
          glVertex2i(I->Width - rightSceneMargin, 0);
          glVertex2i(0, 0);
          glVertex2i(0, height - 1);
          glEnd();
#endif
#endif
          /* deliberate fall-through */
        case 1:
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
          glColor3f(0.3, 0.3, 0.3);
#ifdef _PYMOL_GL_DRAWARRAYS
	  {
	    const GLint lineVerts[] = {
	      1 + I->Width - rightSceneMargin, height - 1,
	      -1, height - 1
	    };
	    glEnableClientState(GL_VERTEX_ARRAY);
	    glVertexPointer(2, GL_INT, 0, lineVerts);
	    glDrawArrays(GL_LINES, 0, 2);
	    glDisableClientState(GL_VERTEX_ARRAY);
	  }
#else
          glBegin(GL_LINES);
          glVertex2i(1 + I->Width - rightSceneMargin, height - 1);
          glVertex2i(-1, height - 1);
          glEnd();
#endif
#endif
          break;
        }
      }

      PRINTFD(G, FB_Ortho)
        " OrthoDoDraw: drawing blocks...\n" ENDFD;

      if(SettingGet(G, cSetting_internal_gui)) {
        int internal_gui_width = (int) SettingGet(G, cSetting_internal_gui_width);
        if(internal_gui_mode != 2) {
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
          glColor3f(0.3, 0.3, 0.3);
#ifdef _PYMOL_GL_DRAWARRAYS
	  {
	    const GLint lineVerts[] = {
	      I->Width - internal_gui_width, 0,
	      I->Width - internal_gui_width, I->Height
	    };
	    glEnableClientState(GL_VERTEX_ARRAY);
	    glVertexPointer(2, GL_INT, 0, lineVerts);
	    glDrawArrays(GL_LINES, 0, 2);
	    glDisableClientState(GL_VERTEX_ARRAY);
	  }
#else
          glBegin(GL_LINES);
          glVertex2i(I->Width - internal_gui_width, 0);
          glVertex2i(I->Width - internal_gui_width, I->Height);
          glEnd();
#endif
#endif
        }
      }

      OrthoRestorePrompt(G);

      if(I->DrawText) {
        int adjust_at = 0;
        /* now print the text */

        lcount = 0;
        x = cOrthoLeftMargin;
        y = cOrthoBottomMargin + MovieGetPanelHeight(G);

#ifdef _PYMOL_SHARP3D
        if(SceneGetStereo(G) && SettingGetGlobal_b(G, cSetting_overlay)) {
          y += (7 * cOrthoLineHeight) / 10;
        }
#endif
        if((int) SettingGet(G, cSetting_text) || I->SplashFlag)
          showLines = I->ShowLines;
        else {
          showLines = internal_feedback + overlay;
        }
        if(internal_feedback)
          adjust_at = internal_feedback + 1;

        l = (I->CurLine - (lcount + skip_prompt)) & OrthoSaveLines;

#ifdef PURE_OPENGL_ES_2
#else
        glColor3fv(I->TextColor);
#endif
        while(l >= 0) {
          lcount++;
          if(lcount > showLines)
            break;
          if(lcount == adjust_at)
            y += 4;
          str = I->Line[l & OrthoSaveLines];
          if(internal_gui_mode) {
            TextSetColor(G, I->OverlayColor);
          } else if(strncmp(str, I->Prompt, 6) == 0) {
            if(lcount < adjust_at)
              TextSetColor(G, I->TextColor);
            else {
              if(length3f(I->OverlayColor) < 0.5)
                TextSetColor(G, I->OverlayColor);
              else
                TextSetColor(G, I->TextColor);
            }
          } else
            TextSetColor(G, I->OverlayColor);
          TextSetPos2i(G, x, y);
          if(str) {
            TextDrawStr(G, str);
            if((lcount == 1) && (I->InputFlag)) {
              if(!skip_prompt) {
                if(I->CursorChar >= 0) {
                  TextSetPos2i(G, x + 8 * I->CursorChar, y);
                }
                TextDrawChar(G, '_');
              }
            }
          }
          l = (I->CurLine - (lcount + skip_prompt)) & OrthoSaveLines;
          y = y + cOrthoLineHeight;
        }
      }

      OrthoDrawWizardPrompt(G);

      if((int) SettingGet(G, cSetting_text) || I->SplashFlag) {
        Block *block;
        int active_tmp;
        block = SeqGetBlock(G);
        active_tmp = block->active;
        block->active = false;
        BlockRecursiveDraw(I->Blocks);
        block->active = active_tmp;
      } else {
        BlockRecursiveDraw(I->Blocks);
      }

      PRINTFD(G, FB_Ortho)
        " OrthoDoDraw: blocks drawn.\n" ENDFD;

      if(I->LoopFlag) {
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
        glColor3f(1.0, 1.0, 1.0);
#ifdef _PYMOL_GL_DRAWARRAYS
	{
	  const GLint lineVerts[] = {
	    I->LoopRect.left, I->LoopRect.top,
	    I->LoopRect.right, I->LoopRect.top,
	    I->LoopRect.right, I->LoopRect.bottom,
	    I->LoopRect.left, I->LoopRect.bottom,
	    I->LoopRect.left, I->LoopRect.top
	  };
	  glEnableClientState(GL_VERTEX_ARRAY);
	  glVertexPointer(2, GL_INT, 0, lineVerts);
	  glDrawArrays(GL_LINE_LOOP, 0, 5);
	  glDisableClientState(GL_VERTEX_ARRAY);
	}
#else
        glBegin(GL_LINE_LOOP);
        glVertex2i(I->LoopRect.left, I->LoopRect.top);
        glVertex2i(I->LoopRect.right, I->LoopRect.top);
        glVertex2i(I->LoopRect.right, I->LoopRect.bottom);
        glVertex2i(I->LoopRect.left, I->LoopRect.bottom);
        glVertex2i(I->LoopRect.left, I->LoopRect.top);
        glEnd();
#endif
#endif
      }


      /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef PYMOL_EVAL
      OrthoDrawEvalMessage(G);
#endif
#ifdef PYMOL_BETA
      OrthoDrawBetaMessage(G);
#endif
#ifdef JYMOL_EVAL
      OrthoDrawEvalMessage(G);
#endif
#ifdef PYMOL_EDU
      OrthoDrawEduMessage(G);
#endif
#ifdef PYMOL_COLL
      OrthoDrawCollMessage(G);
#endif
#ifdef AXPYMOL_EVAL
      OrthoDrawAxMessage(G);
#endif

      /* END PROPRIETARY CODE SEGMENT */

      OrthoPopMatrix(G);

      if(Feedback(G, FB_OpenGL, FB_Debugging))
        PyMOLCheckOpenGLErr("OrthoDoDraw final checkpoint");

    }                           /* while */

  }

  I->DirtyFlag = false;
  PRINTFD(G, FB_Ortho)
    " OrthoDoDraw: leaving...\n" ENDFD;

}


/*========================================================================*/

void OrthoDrawWizardPrompt(PyMOLGlobals * G)
{
  /* assumes PMGUI */

  register COrtho *I = G->Ortho;

  char *vla, *p;
  int nLine;
  int x, y, xx;
  int nChar, c, ll;
  int maxLen;
  BlockRect rect;
  int prompt_mode = SettingGetGlobal_i(G, cSetting_wizard_prompt_mode);
  int gui_mode = SettingGetGlobal_b(G, cSetting_internal_gui_mode);
  float *text_color = I->WizardTextColor;
  float black[3] = { 0.0F, 0.0F, 0.0F };

  if(I->WizardPromptVLA && prompt_mode) {
    vla = I->WizardPromptVLA;

    if(gui_mode)
      text_color = black;
    nLine = UtilCountStringVLA(vla);
    if(nLine) {
      nChar = VLAGetSize(I->WizardPromptVLA);

      /* count max line length; it's strlen - X, 
       * where X is 4*n, where n is the number
       * of colors in the text label */

      maxLen = 0;
      p = vla;
      ll = 0;
      c = nChar;
      while(c > 0) {
        if(!*p) {
          if(maxLen < ll)
            maxLen = ll;
          ll = 0;
          p++;
          c--;
        } else if(((*p) == '\\') &&     /* color encoded */
                  (p[1] >= '0') && (p[1] <= '9') && (p[2] >= '0') && (p[2] <= '9') && (p[3] >= '0') && (p[3] <= '9')) { /* relying upon short-circuit logic to avoid overrun */
          p += 4;
          c -= 4;
        } else {
          ll++;
          p++;
          c--;
        }
      }

      /* determine the coordinates from which to draw the text;
       * need to make adjustments for the sequence viewer */

      rect.top = I->Height;
      if(I->HaveSeqViewer)
        if(!SettingGetGlobal_b(G, cSetting_seq_view_location)) {
          rect.top -= SeqGetHeight(G);
        }

      if(prompt_mode != 3) {
        rect.top -= cWizardTopMargin;
        rect.left = cWizardLeftMargin;
      } else {
        rect.top -= 1;
        rect.left = 1;
      }

      rect.bottom = rect.top - (nLine * cOrthoLineHeight + 2 * cWizardBorder) - 2;
      rect.right = rect.left + cOrthoCharWidth * maxLen + 2 * cWizardBorder + 1;

      if(prompt_mode == 1) {
#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
        if(SettingGetGlobal_b(G, cSetting_internal_gui_mode)) {
          glColor3f(1.0, 1.0F, 1.0F);
        } else {
          glColor3fv(I->WizardBackColor);
        }
#ifdef _PYMOL_GL_DRAWARRAYS
	{
	  const GLint polyVerts[] = {
	    rect.right, rect.top,
	    rect.right, rect.bottom,
	    rect.left, rect.top,
	    rect.left, rect.bottom
	  };
	  glEnableClientState(GL_VERTEX_ARRAY);
	  glVertexPointer(2, GL_INT, 0, polyVerts);
	  glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
	  glDisableClientState(GL_VERTEX_ARRAY);
	}
#else
        glBegin(GL_POLYGON);
        glVertex2i(rect.right, rect.top);
        glVertex2i(rect.right, rect.bottom);
        glVertex2i(rect.left, rect.bottom);
        glVertex2i(rect.left, rect.top);
        glEnd();
#endif
#endif
      }

#ifdef PURE_OPENGL_ES_2
    /* TODO */
#else
      glColor3fv(text_color);
#endif
      x = rect.left + cWizardBorder;
      y = rect.top - (cWizardBorder + cOrthoLineHeight);

      vla = I->WizardPromptVLA;

      /* count max line length */

      TextSetColor(G, text_color);
      TextSetPos2i(G, x, y);
      xx = x;
      p = vla;
      ll = 0;
      c = nChar;
      /* set the char color, position the characters and draw the text */
      while(c > 0) {
        if(*p) {
          if((*p == '\\') && (*(p + 1)) && (*(p + 2)) && (*(p + 3))) {
            if(*(p + 1) == '-') {
              TextSetColor(G, text_color);
              p += 4;
              c -= 4;
            } else {
              TextSetColor3f(G, (*(p + 1) - '0') / 9.0F, (*(p + 2) - '0') / 9.0F,
                             (*(p + 3) - '0') / 9.0F);
              p += 4;
              c -= 4;
            }
            TextSetPos2i(G, xx, y);
          }
        }
        if(c--) {
          if(*p) {
            TextDrawChar(G, *p);
            xx = xx + 8;
          }
          if(!*(p++)) {
            y = y - cOrthoLineHeight;
            xx = x;
            TextSetPos2i(G, x, y);
          }
        }
      }
    }
  }
}

static void OrthoLayoutPanel(PyMOLGlobals * G,
                             int m_top, int m_left, int m_bottom, int m_right)
{
  register COrtho *I = G->Ortho;
  Block *block = NULL;

  int controlHeight = 20;
  int butModeHeight = ButModeGetHeight(G);
  int wizardHeight = I->WizardHeight;

  int controlBottom = m_bottom;
  int butModeBottom = controlBottom + controlHeight;
  int wizardBottom = butModeBottom + butModeHeight;
  int executiveBottom = wizardBottom + wizardHeight;

  int height = I->Height;

  if(SettingGet(G, cSetting_internal_gui)) {
#ifndef _PYMOL_NOPY
    block = ExecutiveGetBlock(G);
    BlockSetMargin(block, m_top, m_left, executiveBottom, m_right);
    block->active = true;

    block = WizardGetBlock(G);
    BlockSetMargin(block, height - executiveBottom + 1, m_left, wizardBottom, m_right);
    block->active = false;

    block = ButModeGetBlock(G);
    BlockSetMargin(block, height - wizardBottom + 1, m_left, butModeBottom, m_right);
    block->active = true;
#else
    block = ExecutiveGetBlock(G);
    BlockSetMargin(block, m_top, m_left, executiveBottom, m_right);
    block->active = true;

    block = WizardGetBlock(G);
    BlockSetMargin(block, height - executiveBottom + 1, m_left, wizardBottom, m_right);
    block->active = false;

    block = ButModeGetBlock(G);
    BlockSetMargin(block, height - wizardBottom + 1, m_left, butModeBottom, m_right);
    block->active = false;
#endif

    block = ControlGetBlock(G);
    BlockSetMargin(block, height - butModeBottom + 1, m_left, controlBottom, m_right);
    block->active = true;
  } else {
    block = ExecutiveGetBlock(G);
    BlockSetMargin(block, m_right, m_bottom, m_right, m_bottom);
    block->active = false;

    block = WizardGetBlock(G);
    BlockSetMargin(block, m_right, m_bottom, m_right, m_bottom);
    block->active = false;

    block = ButModeGetBlock(G);
    BlockSetMargin(block, m_right, m_bottom, m_right, m_bottom);
    block->active = false;

    block = ControlGetBlock(G);
    BlockSetMargin(block, m_right, m_bottom, m_right, m_bottom);
    block->active = false;
  }
}


/*========================================================================*/
void OrthoReshape(PyMOLGlobals * G, int width, int height, int force)
{
  register COrtho *I = G->Ortho;

  Block *block = NULL;
  int sceneBottom, sceneRight = 0;
  int textBottom = 0;
  int internal_gui_width;
  int internal_feedback;
  int sceneTop = 0;

  PRINTFD(G, FB_Ortho)
    " OrthoReshape-Debug: %d %d\n", width, height ENDFD;

  I->WrapXFlag = false;
  if(width > 0) {
    int stereo = SettingGetGlobal_i(G, cSetting_stereo);
    int stereo_mode = SettingGetGlobal_i(G, cSetting_stereo_mode);
    if (stereo){
      switch (stereo_mode) {
      case cStereo_geowall:
      case cStereo_dynamic:
	width = width / 2;
	I->WrapXFlag = true;
	break;
      }
    }
  }

  if((width != I->Width) || (height != I->Height) || force) {
    if(width < 0)
      width = I->Width;
    if(height < 0)
      height = I->Height;

    I->Height = height;
    I->Width = width;
    I->ShowLines = height / cOrthoLineHeight;

    textBottom += MovieGetPanelHeight(G);
    I->TextBottom = textBottom;

    internal_feedback = (int) SettingGet(G, cSetting_internal_feedback);
    if(internal_feedback)
      sceneBottom =
        textBottom + (internal_feedback - 1) * cOrthoLineHeight + cOrthoBottomSceneMargin;
    else
      sceneBottom = textBottom;

    internal_gui_width = (int) SettingGet(G, cSetting_internal_gui_width);
    if(!SettingGetGlobal_b(G, cSetting_internal_gui)) {
      internal_gui_width = 0;
      sceneRight = 0;
    } else {
      switch (SettingGetGlobal_i(G, cSetting_internal_gui_mode)) {
      case 2:
        sceneRight = 0;
        sceneBottom = 0;
        break;
      default:
        sceneRight = internal_gui_width;
        break;
      }
    }

    {
      int seqHeight;
      block = SeqGetBlock(G);
      block->active = true;

      /* reloate the sequence viewer as necessary */

      if(SettingGetGlobal_b(G, cSetting_seq_view_location)) {

        BlockSetMargin(block, height - sceneBottom - 10, 0, sceneBottom, sceneRight);
        if(block->fReshape)
          block->fReshape(block, width, height);
        seqHeight = SeqGetHeight(G);
        BlockSetMargin(block, height - sceneBottom - seqHeight, 0, sceneBottom,
                       sceneRight);
        if(!SettingGetGlobal_b(G, cSetting_seq_view_overlay)) {
          sceneBottom += seqHeight;
        }

      } else {

        BlockSetMargin(block, 0, 0, height - 10, sceneRight);
        if(block->fReshape)
          block->fReshape(block, width, height);
        seqHeight = SeqGetHeight(G);
        BlockSetMargin(block, 0, 0, height - seqHeight, sceneRight);
        if(!SettingGetGlobal_b(G, cSetting_seq_view_overlay)) {
          sceneTop = seqHeight;
        }
      }
    }

    OrthoLayoutPanel(G, 0, width - internal_gui_width, textBottom, 0);

    block = MovieGetBlock(G);
    BlockSetMargin(block, height - textBottom, 0, 0, 0);
    block->active = (textBottom && true);

    block = SceneGetBlock(G);
    BlockSetMargin(block, sceneTop, 0, sceneBottom, sceneRight);

    block = NULL;
    while(ListIterate(I->Blocks, block, next))
      if(block->fReshape) {
        block->fReshape(block, width, height);
      }

    WizardRefresh(G);           /* safe to call even if no wizard exists */
  }
  SceneInvalidateStencil(G);
  OrthoDirty(G);
}


/*========================================================================*/
void OrthoReshapeWizard(PyMOLGlobals * G, ov_size wizHeight)
{
  register COrtho *I = G->Ortho;
  I->WizardHeight = wizHeight;

  if(SettingGet(G, cSetting_internal_gui) > 0.0) {
    Block *block;
    int internal_gui_width = (int) SettingGet(G, cSetting_internal_gui_width);
#if 1
    OrthoLayoutPanel(G, 0, I->Width - internal_gui_width, I->TextBottom, 0);

    block = ExecutiveGetBlock(G);
    block->fReshape(block, I->Width, I->Height);
    block = WizardGetBlock(G);
    block->fReshape(block, I->Width, I->Height);
    block->active = (wizHeight && true);
#else
    int height, width;
    height = I->Height;
    width = I->Width;

    int WizardMargin = WizardMargin1;

    block = ExecutiveGetBlock(G);

    if(!SettingGet(G, cSetting_mouse_grid)) {
      WizardMargin = WizardMargin2;
    }

    if(height) {
      int wh = wizHeight;
      if(wh)
        wh++;
      BlockSetMargin(block, 0, width - internal_gui_width, WizardMargin + wh, 0);
    } else {
      BlockSetMargin(block, 0, width - internal_gui_width, WizardMargin, 0);
    }
    block->fReshape(block, width, height);

    block = WizardGetBlock(G);

    if(wizHeight) {
      BlockSetMargin(block, height - (WizardMargin + wizHeight),
                     width - internal_gui_width, WizardMargin, 0);
      block->active = true;
    } else {
      BlockSetMargin(block, height - WizardMargin, width - internal_gui_width,
                     WizardMargin, 0);
      block->active = false;
    }
    block->fReshape(block, width, height);
#endif
  }
}


/*========================================================================*/
Block *OrthoFindBlock(PyMOLGlobals * G, int x, int y)
{
  register COrtho *I = G->Ortho;

  return (BlockRecursiveFind(I->Blocks, x, y));
}


/*========================================================================*/
int OrthoGetWrapClickSide(PyMOLGlobals * G)
{
  return G->Ortho->WrapClickSide;
}


/*========================================================================*/
int OrthoButton(PyMOLGlobals * G, int button, int state, int x, int y, int mod)
{
  register COrtho *I = G->Ortho;
  Block *block = NULL;
  int handled = 0;

  PRINTFB(G, FB_Ortho, FB_Blather)
    "OrthoButton: button:%d, state=%d, x=%d, y=%d, mod=%d\n", 
    button,state,x,y,mod
    ENDFB(G);

  switch (button) {
  case 3:
  case 4:
    if((button != I->ActiveButton) && (I->ActiveButton>=0) && (I->ActiveButton<3)) {
      /* suppress wheel events when a button is already pushed */
      return 1;
    }
    block = SceneGetBlock(G);
    break;
  }

  if(I->WrapXFlag) {
    if(state == P_GLUT_DOWN) {
      x = get_wrap_x(x, NULL, G->Option->winX, &I->WrapClickSide);
    } else {
      x = get_wrap_x(x, &I->LastX, G->Option->winX, &I->WrapClickSide);
    }
  } else {
    I->WrapClickSide = 0;
  }

  OrthoRemoveSplash(G);
  OrthoRemoveAutoOverlay(G);
  I->X = x;
  I->Y = y;
  I->LastX = x;
  I->LastY = y;
  I->LastModifiers = mod;

  if(state == P_GLUT_DOWN) {
    I->ActiveButton = button;
    if(I->GrabbedBy) {
      if(I->GrabbedBy->inside)
        block = BlockRecursiveFind(I->GrabbedBy->inside, x, y);
      else
        block = I->GrabbedBy;
    } else if(!block)
      block = OrthoFindBlock(G, x, y);
    if(block) {
      I->ClickedIn = block;
      if(block->fClick) {
        handled = block->fClick(block, button, x, y, mod);
      }
    }
  } else if(state == P_GLUT_UP) {
    if(I->IssueViewportWhenReleased) {
      OrthoCommandIn(G, "viewport");
      I->IssueViewportWhenReleased = false;
    }
    
    if(I->GrabbedBy) {
      block = I->GrabbedBy;
      if(block->fRelease)
        handled = block->fRelease(block, button, x, y, mod);
      I->ClickedIn = NULL;
    }
    if(I->ClickedIn) {
      block = I->ClickedIn;
      if(block->fRelease)
        handled = block->fRelease(block, button, x, y, mod);
      I->ClickedIn = NULL;
    }
    I->ActiveButton = -1;
  }
#if 0
  if(block && !handled) {
    if(SceneGetBlock(G) == block) {
      if(state == P_GLUT_DOWN) {
        I->LoopRect.left = x;
        I->LoopRect.top = y;
        I->LoopRect.right = x;
        I->LoopRect.bottom = y;
        I->LoopFlag = true;
        I->LoopMod = mod;
        I->GrabbedBy = &I->LoopBlock;
        OrthoDirty(G);
      }
    }
  }
#endif
  return (handled);
}


/*========================================================================*/
int OrthoDrag(PyMOLGlobals * G, int x, int y, int mod)
{
  register COrtho *I = G->Ortho;

  Block *block = NULL;
  int handled = 0;

  if(I->WrapXFlag) {
    x = get_wrap_x(x, &I->LastX, G->Option->winX, NULL);
  }

  I->LastX = x;
  I->LastY = y;
  I->LastModifiers = mod;

  I->X = x;
  I->Y = y;
  if(I->GrabbedBy) {
    block = I->GrabbedBy;
    if(block->fDrag)
      handled = block->fDrag(block, x, y, mod);
  } else if(I->ClickedIn) {
    block = I->ClickedIn;
    if(block->fDrag)
      handled = block->fDrag(block, x, y, mod);
  }
  return (handled);
}


/*========================================================================*/
void OrthoSplash(PyMOLGlobals * G)
{

/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef _PYMOL_IP_SPLASH
#include"OrthoIPSplash.h"
#else
  if(G->Option->incentive_product) {
#ifdef AXPYMOL_EVAL
    PRINTF
      " AxPyMOL(TM) Evaluation Product - Copyright (c) Schrodinger, LLC.\n \n"
      ENDF(G);
    PRINTF " This Executable Build integrates and extends Open-Source PyMOL " ENDF(G);
    PRINTF _PyMOL_VERSION ENDF(G);
    PRINTF ".\n" ENDF(G);
#else
    PRINTF " PyMOL(TM) Incentive Product - Copyright (c) Schrodinger, LLC.\n \n"
      ENDF(G);
    PRINTF " This Executable Build integrates and extends Open-Source PyMOL " ENDF(G);
    PRINTF _PyMOL_VERSION ENDF(G);
    PRINTF ".\n" ENDF(G);
#endif
  } else

/* END PROPRIETARY CODE SEGMENT */
  {
    /* Splash message for unrestricted access open-source versions... */
    PRINTF " PyMOL(TM) Molecular Graphics System, Version " ENDF(G);
    PRINTF _PyMOL_VERSION ENDF(G);
    PRINTF ".\n" ENDF(G);
    PRINTF " Copyright (c) Schrodinger, LLC.\n All Rights Reserved.\n \n"
      ENDF(G);

    PRINTF "    Created by Warren L. DeLano, Ph.D. \n \n" ENDF(G);

    /* PRINTF " Other Major Authors and Contributors:\n\n" ENDF(G);
     * PRINTF " Ralf W. Grosse-Kunstleve, Ph.D.\n \n" ENDF(G);
     * 
     * NOTICE: Enduring thanks to Ralf, but in point of fact, his
     * sglite module is no longer used by PyMOL, and thus we should
     * not mislead everyone by asserting otherwise... */

    PRINTF "    PyMOL is user-supported open-source software.  Although some versions\n"
      ENDF(G);
    PRINTF "    are freely available, PyMOL is not in the public domain.\n \n" ENDF(G);

    PRINTF "    If PyMOL is helpful in your work or study, then please volunteer \n"
      ENDF(G);
    PRINTF
      "    support for our ongoing efforts to create open and affordable scientific\n"
      ENDF(G);
    PRINTF
      "    software by purchasing a PyMOL Maintenance and/or Support subscription.\n\n"
      ENDF(G);

    PRINTF "    More information can be found at \"http://www.pymol.org\".\n \n" ENDF(G);

    PRINTF "    Enter \"help\" for a list of commands.\n" ENDF(G);
    PRINTF
      "    Enter \"help <command-name>\" for information on a specific command.\n\n"
      ENDF(G);

    PRINTF " Hit ESC anytime to toggle between text and graphics.\n\n" ENDF(G);
  }
#endif
}


/*========================================================================*/
int OrthoInit(PyMOLGlobals * G, int showSplash)
{
  register COrtho *I = NULL;

  if((I = (G->Ortho = Calloc(COrtho, 1)))) {

    ListInit(I->Blocks);

    I->ActiveButton = -1;
    I->Pushed = 0;
    {
      int a;
      for(a = 0; a <= CMD_QUEUE_MASK; a++)
        I->cmdQueue[a] = QueueNew(G, 0x7FFF);   /* 32K ea. level for commands */
      I->cmdActiveQueue = I->cmdQueue[0];
      I->cmdNestLevel = 0;
    }
    I->feedback = QueueNew(G, 0x3FFFF); /* ~256K for output */
    I->deferred = NULL;
    I->RenderMode = 0;
    I->WrapXFlag = false;

    I->WizardBackColor[0] = 0.2F;
    I->WizardBackColor[1] = 0.2F;
    I->WizardBackColor[2] = 0.2F;
    I->WizardTextColor[0] = 0.2F;
    I->WizardTextColor[1] = 1.0F;
    I->WizardTextColor[2] = 0.2F;

    I->GrabbedBy = NULL;
    I->ClickedIn = NULL;
    I->DrawText = 1;
    I->HaveSeqViewer = false;
    I->TextColor[0] = 0.83F;
    I->TextColor[1] = 0.83F;
    I->TextColor[2] = 1.0;
    I->OverlayColor[0] = 1.0;
    I->OverlayColor[1] = 1.0;
    I->OverlayColor[2] = 1.0;
    I->CurLine = 1000;
    I->PromptChar = 0;
    I->CurChar = 0;
    I->CurLine = 0;
    I->AutoOverlayStopLine = 0;
    I->CursorChar = -1;
    I->HistoryLine = 0;
    I->HistoryView = 0;
    I->Line[I->CurLine & OrthoSaveLines][I->CurChar] = 0;
    I->WizardPromptVLA = NULL;
    I->SplashFlag = false;
    I->ShowLines = 1;
    I->Saved[0] = 0;
    I->DirtyFlag = true;
    I->ActiveGLBuffer = GL_NONE;
    I->LastDraw = UtilGetSeconds(G);
    I->DrawTime = 0.0;
    if(showSplash) {
      OrthoSplash(G);
      I->SplashFlag = true;
    }
    /*  OrthoFeedbackIn(G," "); */
    I->CurLine++;

#ifndef _PYMOL_LIB
    /* prompt (and typing) should only be shown for PyMOL, not libpymol */
    strcpy(I->Prompt, "PyMOL>");
#endif
    strcpy(I->Line[I->CurLine], I->Prompt);
    I->CurChar = (I->PromptChar = strlen(I->Prompt));
    I->InputFlag = 1;

    /*printf("orthoNewLine: CC: %d CL:%d PC: %d IF:L %d\n",I->CurChar,I->CurLine,
       I->PromptChar,I->InputFlag); */

    PopInit(G);
    {
      int a;
      for(a = 0; a <= OrthoHistoryLines; a++)
        I->History[a][0] = 0;
    }

    return 1;
  } else {
    return 0;
  }
}


/*========================================================================*/
void OrthoFree(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;

  VLAFreeP(I->WizardPromptVLA);
  PopFree(G);
  {
    int a;
    I->cmdActiveQueue = NULL;
    for(a = 0; a <= CMD_QUEUE_MASK; a++) {
      QueueFree(I->cmdQueue[a]);
      I->cmdQueue[a] = NULL;
    }
  }
  QueueFree(I->feedback);
  I->feedback = NULL;
  if(I->deferred) {
    DeferredFree(I->deferred);
    I->deferred = NULL;
  }
  FreeP(G->Ortho);
}


/*========================================================================*/
void OrthoPushMatrix(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;

  if(G->HaveGUI && G->ValidContext) {

    if(!I->Pushed) {
      glGetIntegerv(GL_VIEWPORT, I->ViewPort);
    }
    switch (I->RenderMode) {
    case 1:
      glViewport(I->ViewPort[0], I->ViewPort[1], I->ViewPort[2], I->ViewPort[3]);
      break;
    case 2:
      glViewport(I->ViewPort[0] + I->ViewPort[2], I->ViewPort[1],
                 I->ViewPort[2], I->ViewPort[3]);
      break;
    default:
      glViewport(I->ViewPort[0], I->ViewPort[1], I->ViewPort[2], I->ViewPort[3]);
    }

#ifndef PURE_OPENGL_ES_2
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, I->ViewPort[2], 0, I->ViewPort[3], -100, 100);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(0.33F, 0.33F, 0.0F);   /* this generates better 
                                           rasterization on macs */
#endif

#ifdef PURE_OPENGL_ES_2
#else
    if(!SettingGetGlobal_b(G, cSetting_texture_fonts)) {
      glDisable(GL_ALPHA_TEST);
    } else {
      glEnable(GL_ALPHA_TEST);
    }
    glDisable(GL_LIGHTING);
    glDisable(GL_FOG);
    glDisable(GL_NORMALIZE);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_LINE_SMOOTH);
#endif

    glDisable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_DITHER);

#ifndef PURE_OPENGL_ES_2
    glShadeModel(GL_SMOOTH);
#endif
    if(G->Option->multisample)
      glDisable(0x809D);        /* GL_MULTISAMPLE_ARB */

    I->Pushed++;
  }
  /*  glDisable(GL_ALPHA_TEST);
     glDisable(GL_CULL_FACE);
     glDisable(GL_POINT_SMOOTH); */

}


/*========================================================================*/
void OrthoPopMatrix(PyMOLGlobals * G)
{
  register COrtho *I = G->Ortho;
  if(G->HaveGUI && G->ValidContext) {

    if(I->Pushed >= 0) {
      glViewport(I->ViewPort[0], I->ViewPort[1], I->ViewPort[2], I->ViewPort[3]);
#ifndef PURE_OPENGL_ES_2
      glPopMatrix();
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(GL_MODELVIEW);
#endif
      I->Pushed--;
    }
  }
}

int OrthoGetPushed(PyMOLGlobals * G)
{
  return G->Ortho->Pushed;
}


/*========================================================================*/
void OrthoCommandIn(PyMOLGlobals * G, char *buffer)
{
  register COrtho *I = G->Ortho;
  if(I->cmdActiveQueue)
    QueueStrIn(I->cmdActiveQueue, buffer);
}


/*========================================================================*/
void OrthoPasteIn(PyMOLGlobals * G, char *buffer)
{
  register COrtho *I = G->Ortho;
  int curLine = I->CurLine & OrthoSaveLines;
  int execFlag = false;
  OrthoLineType buf2;

  if(I->InputFlag) {
    if(I->CursorChar >= 0) {
      strcpy(buf2, I->Line[curLine] + I->CursorChar);
      strcpy(I->Line[curLine] + I->CursorChar, buffer);
      I->CurChar = strlen(I->Line[curLine]);
      I->CursorChar = I->CurChar;
      while((I->Line[curLine][I->CurChar - 1] == 10)
            || (I->Line[curLine][I->CurChar - 1] == 13)) {
        execFlag = true;
        I->CurChar--;
        I->Line[curLine][I->CurChar] = 0;
        if(I->CurChar <= I->PromptChar)
          break;
      }
      if(!execFlag) {
        strcpy(I->Line[curLine] + I->CursorChar, buf2);
        I->CurChar = strlen(I->Line[curLine]);
      }
    } else {
      strcat(I->Line[curLine], buffer);
      I->CurChar = strlen(I->Line[curLine]);
      while((I->Line[curLine][I->CurChar - 1] == 10)
            || (I->Line[curLine][I->CurChar - 1] == 13)) {
        execFlag = true;
        I->CurChar--;
        I->Line[curLine][I->CurChar] = 0;
        if(I->CurChar <= I->PromptChar)
          break;
      }
    }
  } else {
    OrthoRestorePrompt(G);

    while((I->Line[curLine][I->CurChar - 1] == 10)
          || (I->Line[curLine][I->CurChar - 1] == 13)) {
      execFlag = true;
      I->CurChar--;
      I->Line[curLine][I->CurChar] = 0;
      if(I->CurChar <= I->PromptChar)
        break;
    }
  }
  if(execFlag) {
    printf("[%s]\n", I->Line[curLine]);
    OrthoParseCurrentLine(G);
  } else
    I->InputFlag = true;
}
