
/* 
   A* -------------------------------------------------------------------
   B* This file contains source code for the PyMOL computer program
   C* Copyright (c) Schrodinger, LLC. 
   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"


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
#include <signal.h>
#include <tchar.h>
#endif

/* END PROPRIETARY CODE SEGMENT */

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

#ifdef _DRI_WORKAROUND
#include <dlfcn.h>
#endif

#include "PyMOLGlobals.h"
#include "PyMOL.h"
#include "PyMOLOptions.h"

#include "MemoryDebug.h"
#include "Base.h"
#include "main.h"

#include "P.h"
#include "PConv.h"
#include "Ortho.h"
#include "Scene.h"
#include "Seq.h"
#include "Util.h"
#include "Control.h"
#include "Movie.h"
#include "Executive.h"
#include "Feedback.h"

int _gScaleFactor = 1;

#ifdef _PYMOL_NO_MAIN

int MainSavingUnderWhileIdle(void)
{
  return 0;
}

#else


void MainFree(void);
static void MainReshape(int width, int height);
static void MainDrawLocked(void);
static void MainDrag(int x, int y);

static CPyMOL *PyMOLInstance = nullptr;    /* eliminate */

struct _CMain {
  int IdleMode;
  double IdleTime;
  int IdleCount;
  int Modifiers;
  int FinalInitCounter, FinalInitTrigger, FinalInitDone;
  int TheWindow;
  int WindowIsVisible;
  double ReshapeTime;
  double DrawAfter, DrawDelay;
  int DrawGovernorActive, DrawDeferred, DrawSignalled;
  int DrawnFlag, DeferReshapeDeferral;
  int MaximizeCheck;
  CPyMOLOptions *OwnedOptions;
  /* if Main owns a reference to a copy of the startup options that
     needs to be freed upon shutdown to fully scrub the heap */
};


/* global options */

static
void MainOnExit(void);

static void MainPushValidContext(PyMOLGlobals * G)
{
  PLockStatus(G);
  PyMOL_PushValidContext(G->PyMOL);
  PUnlockStatus(G);
}

static void MainPopValidContext(PyMOLGlobals * G)
{
  PLockStatus(G);
  PyMOL_PopValidContext(G->PyMOL);
  PUnlockStatus(G);
}

static void DrawBlueLine(PyMOLGlobals * G)
{
  if(G->Option->blue_line) {
    GLint i;
    unsigned long buffer;
    GLint window_width, window_height;

    window_width = G->Option->winX;
    window_height = G->Option->winY;

    glPushAttrib(GL_ALL_ATTRIB_BITS);

    glDisable(GL_ALPHA_TEST);
    glDisable(GL_BLEND);
    for(i = 0; i < 6; i++)
      glDisable(GL_CLIP_PLANE0 + i);
    glDisable(GL_COLOR_LOGIC_OP);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_DITHER);
    glDisable(GL_FOG);
    glDisable(GL_LIGHTING);
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_LINE_STIPPLE);
    glDisable(GL_SCISSOR_TEST);
    glDisable(GL_STENCIL_TEST);


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef _PYMOL_OSX
    /*glDisable(GL_SHARED_TEXTURE_PALETTE_EXT); */
    glDisable(GL_TEXTURE_1D);
    glDisable(GL_TEXTURE_2D);
    glDisable(GL_TEXTURE_3D);
    glDisable(GL_TEXTURE_CUBE_MAP);
    glDisable(GL_TEXTURE_RECTANGLE_EXT);
    glDisable(GL_VERTEX_PROGRAM_ARB);
#endif

/* END PROPRIETARY CODE SEGMENT */

    for(buffer = GL_BACK_LEFT; buffer <= GL_BACK_RIGHT; buffer++) {
      GLint matrixMode;
      GLint vp[4];

      OrthoDrawBuffer(G, buffer);

      glGetIntegerv(GL_VIEWPORT, vp);
      glViewport(0, 0, window_width, window_height);

      glGetIntegerv(GL_MATRIX_MODE, &matrixMode);
      glMatrixMode(GL_PROJECTION);
      glPushMatrix();
      glLoadIdentity();

      glMatrixMode(GL_MODELVIEW);
      glPushMatrix();
      glLoadIdentity();
      glScalef(2.0f / window_width, -2.0f / window_height, 1.0f);
      glTranslatef(-window_width / 2.0f, -window_height / 2.0f, 0.0f);

      /* draw sync lines */
      glColor3d(0.0f, 0.0f, 0.0f);
      glBegin(GL_LINES);        /* Draw a background line */
      glVertex3f(0.0F, window_height - 0.5F, 0.0F);
      glVertex3f((float) window_width, window_height - 0.5F, 0.0F);
      glEnd();
      glColor3d(0.0f, 0.0f, 1.0f);
      glBegin(GL_LINES);        /* Draw a line of the correct length (the cross
                                   over is about 40% across the screen from the left */
      glVertex3f(0.0f, window_height - 0.5f, 0.0f);
      if(buffer == GL_BACK_LEFT)
        glVertex3f(window_width * 0.30f, window_height - 0.5f, 0.0f);
      else
        glVertex3f(window_width * 0.80f, window_height - 0.5f, 0.0f);
      glEnd();

      glPopMatrix();
      glMatrixMode(GL_PROJECTION);
      glPopMatrix();
      glMatrixMode(matrixMode);

      glViewport(vp[0], vp[1], vp[2], vp[3]);
    }
    glPopAttrib();
  }
}

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

void MainOnExit(void)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  /* 
     here we enter not knowing anything about the current state...
     so, no graceful exit is possible -- in fact under Window's we'll
     crash unless we take the drastic way out 
   */
  if(G && !G->Terminating) {
    G->Terminating = true;
    printf(" PyMOL: abrupt program termination.\n");

/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
    TerminateProcess(GetCurrentProcess(), 0);   /* only way to avoid a crash */
#endif

/* END PROPRIETARY CODE SEGMENT */
    exit(EXIT_SUCCESS);
  }
}


/*========================================================================*/
int MainSavingUnderWhileIdle(void)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  CMain *I = G->Main;
  return (I && (I->IdleMode > 3));
}


/*========================================================================*/
void MainSetWindowVisibility(int mode)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  G->Option->window_visible = mode;

}


/*========================================================================*/
static void MainDrag(int x, int y)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  CMain *I = G->Main;

  if(PLockAPIAsGlut(G, false)) {

    y = G->Option->winY - y;

    PyMOL_Drag(PyMOLInstance, x, y, I->Modifiers);

    if(PyMOL_GetRedisplay(PyMOLInstance, true)) {
      if(G->HaveGUI) {
        p_glutPostRedisplay();
      }
    }
    I->IdleMode = 0;

    PUnlockAPIAsGlut(G);
  }
}


/*========================================================================*/
static void MainButton(int button, int state, int x, int y)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  int glMod;

  CMain *I = G->Main;

#ifdef WIN32
  // fix wheel x/y on Windows (PYMOL-2173)
  switch(button) {
  case P_GLUT_BUTTON_SCROLL_FORWARD:
  case P_GLUT_BUTTON_SCROLL_BACKWARD:
    x -= p_glutGet(P_GLUT_WINDOW_X);
    y -= p_glutGet(P_GLUT_WINDOW_Y);
  }
#endif

  glMod = p_glutGetModifiers();

  if(PLockAPIAsGlut(G, false)) {

    I->IdleMode = 0;            /* restore responsiveness */

    if(PyMOL_GetPassive(PyMOLInstance, (button < 3) ? true : false)) {
      MainDrag(x, y);
    } else {
      /* stay blocked here because Clicks->SexFrame->PParse */

      y = G->Option->winY - y;

      I->Modifiers = ((glMod & P_GLUT_ACTIVE_SHIFT) ? cOrthoSHIFT : 0) |
        ((glMod & P_GLUT_ACTIVE_CTRL) ? cOrthoCTRL : 0) |
        ((glMod & P_GLUT_ACTIVE_ALT) ? cOrthoALT : 0);

      PyMOL_Button(PyMOLInstance, button, state, x, y, I->Modifiers);
    }

    PUnlockAPIAsGlut(G);
  }
}


/*========================================================================*/
static void MainPassive(int x, int y)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  CMain *I = G->Main;

#define PASSIVE_EDGE 20

  if(PyMOL_GetPassive(G->PyMOL, false)) {       /* a harmless race condition -- we don't want
                                                   to slow Python down buy locking on passive
                                                   mouse motion */

    if(PLockAPIAsGlut(G, false)) {
      if((y < -PASSIVE_EDGE) || (x < -PASSIVE_EDGE) ||
         (x > (G->Option->winX + PASSIVE_EDGE)) ||
         (y > (G->Option->winY + PASSIVE_EDGE))) {

        /* release passive drag if mouse leaves window... 
           [IF we continue to receive mouse events...] */

        y = G->Option->winY - y;

        PyMOL_Button(PyMOLInstance, P_GLUT_LEFT_BUTTON, P_GLUT_UP, x, y, I->Modifiers);

        PyMOL_GetPassive(G->PyMOL, true);       /* reset the flag */

      } else {

        y = G->Option->winY - y;

        PyMOL_Drag(PyMOLInstance, x, y, I->Modifiers);

      }

      if(PyMOL_GetRedisplay(PyMOLInstance, true)) {
        if(G->HaveGUI) {
          p_glutPostRedisplay();
        }
        I->IdleMode = 0;
      }

      PUnlockAPIAsGlut(G);
    }
  }

}


/*========================================================================*/
static void MainDrawLocked(void)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  CMain *I = G->Main;
  if(I->FinalInitTrigger) {

    I->FinalInitTrigger = false;

#ifndef _PYMOL_NOPY

    /* okay, on the way in, the API is locked but the interpreter is
       running async, so we need to block it */

    PBlock(G);

    if(PyErr_Occurred())
      PyErr_Print();

    /* next, we need to let PyMOL know we have a valid context,
       because some initializations, involve GL calls (such as going
       into full-screen or stereo mode) */

    if(G->HaveGUI)
      MainPushValidContext(G);

    /* restore working directory if asked to */
    PRunStringModule(G,
                     "if 'PYMOL_WD' in os.environ: os.chdir(os.environ['PYMOL_WD'])");

    if(PyErr_Occurred())
      PyErr_Print();


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef _PYMOL_OSX
    PRunStringModule(G,
                     "if os.getcwd()[-23:]=='.app/Contents/Resources': os.chdir('../../..')");
#endif

/* END PROPRIETARY CODE SEGMENT */

    if(PyErr_Occurred())
      PyErr_Print();

    PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->obj, "launch_gui", "O", G->P_inst->obj));

    if(PyErr_Occurred())
      PyErr_Print();

    PXDecRef(PYOBJECT_CALLMETHOD
             (G->P_inst->obj, "adapt_to_hardware", "O", G->P_inst->obj));

    if(PyErr_Occurred())
      PyErr_Print();

    if(G->Option->incentive_product) {  /* perform incentive product initialization (if any) */
      PyRun_SimpleString("try:\n   import ipymol\nexcept:\n   pass\n");
      if(PyErr_Occurred())
        PyErr_Print();
    }

    PXDecRef(PYOBJECT_CALLMETHOD(G->P_inst->obj, "exec_deferred", "O", G->P_inst->obj));

    if(PyErr_Occurred())
      PyErr_Print();

    if(G->HaveGUI)
      MainPopValidContext(G);

    PUnblock(G);

#endif
    I->FinalInitDone = true;
  }

  PyMOL_Draw(PyMOLInstance);

  if(G->HaveGUI) {
    if(Feedback(G, FB_OpenGL, FB_Debugging)) {
      PyMOLCheckOpenGLErr("During Rendering");
    }
  }

  if(PyMOL_GetSwap(G->PyMOL, true)) {
    if(!SettingGetGlobal_b(G, cSetting_suspend_updates))
      if(G->HaveGUI) {
        DrawBlueLine(G);
        p_glutSwapBuffers();
      }
  }
}

static void MainDrawProgress(PyMOLGlobals * G)
{
  int progress[PYMOL_PROGRESS_SIZE];
  int update = false;
  PBlock(G);
  PLockStatus(G);
  update = PyMOL_GetProgress(G->PyMOL, progress, true);
  PUnlockStatus(G);
  PUnblock(G);

  /*
     printf("show progress %d %d %d %d %d %d\n",
     progress[0],progress[1],progress[2],
     progress[3],progress[4],progress[5]); */

  if(update &&
     (progress[PYMOL_PROGRESS_SLOW] ||
      progress[PYMOL_PROGRESS_MED] || progress[PYMOL_PROGRESS_FAST])) {

    int offset;
    int x = 0, y;
    float black[3] = { 0, 0, 0 };
    float white[3] = { 1, 1, 1 };
    GLint ViewPort[4];

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

    glGetIntegerv(GL_VIEWPORT, ViewPort);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    glOrtho(0, ViewPort[2], 0, ViewPort[3], -100, 100);
    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(0.33F, 0.33F, 0.0F);   /* this generates better 
                                           rasterization on macs */

    glDisable(GL_ALPHA_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_FOG);
    glDisable(GL_NORMALIZE);
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_COLOR_MATERIAL);
    glDisable(GL_LINE_SMOOTH);
    glDisable(GL_DITHER);
    glDisable(GL_BLEND);

    {
      int pass = 0;

      int draw_both = SceneMustDrawBoth(G);

      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 */
        }

        glColor3fv(black);
        glBegin(GL_POLYGON);
        glVertex2i(0, ViewPort[3]);
        glVertex2i(cBusyWidth, ViewPort[3]);
        glVertex2i(cBusyWidth, ViewPort[3] - cBusyHeight);
        glVertex2i(0, ViewPort[3] - cBusyHeight);
        glVertex2i(0, ViewPort[3]);     /* needed on old buggy Mesa */
        glEnd();
        y = ViewPort[3] - cBusyMargin;

        glColor3fv(white);

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

        for(offset = 0; offset < PYMOL_PROGRESS_SIZE; offset += 2) {

          if(progress[offset + 1]) {
            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();
            glColor3fv(white);
            x =
              (progress[offset] * (cBusyWidth - 2 * cBusyMargin) / progress[offset + 1]) +
              cBusyMargin;
            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();
            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);
    }

    glPopMatrix();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);

  }
}


/*========================================================================*/
static void MainDraw(void)
{
  int usec_sleep_after_draw = 0;
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  PRINTFD(G, FB_Main)
    " MainDraw: called.\n" ENDFD;
  if(PLockAPIAsGlut(G, false)) {
    CMain *I = G->Main;
    if(I->DrawGovernorActive &&
       (!I->DrawSignalled) && (UtilGetSeconds(G) < I->DrawAfter) && I->FinalInitDone) {
      I->DrawDeferred = true;
    } else {
      int skip = false;
      if(I->MaximizeCheck) {
        {
          /* is the window manager screwing us over??? */

          int height = p_glutGet(P_GLUT_SCREEN_HEIGHT);
          int width = p_glutGet(P_GLUT_SCREEN_WIDTH);
          int actual_x = p_glutGet(P_GLUT_WINDOW_X);
          int actual_y = p_glutGet(P_GLUT_WINDOW_Y);

          I->MaximizeCheck = false;

          if(actual_x != 0) {
            width -= 2 * actual_x;
            height -= actual_x;
          }
          if(actual_y != 0) {
            height -= actual_y;
          }
          p_glutPositionWindow(0, 0);
          p_glutReshapeWindow(width, height);
          skip = true;
        }
      }
      if((!skip) && (!I->DrawnFlag) && I->FinalInitDone) {
        if(I->DeferReshapeDeferral > 0)
          I->DeferReshapeDeferral--;
        else {
          double time_since_reshape = UtilGetSeconds(G) - I->ReshapeTime;
          if(time_since_reshape < 0.05) {
            /* defer screen updates while it's being actively resized */
            skip = true;
          }
        }
      }
      if(!skip) {
        MainDrawLocked();
        I->DrawnFlag = true;
        if(PyMOL_GetModalDraw(PyMOLInstance)) {
          usec_sleep_after_draw = 10000;        /* give other threads a chance to run */
        }
      } else {
        PyMOL_NeedRedisplay(PyMOLInstance);
      }

      I->DrawAfter = UtilGetSeconds(G) + I->DrawDelay;
      I->DrawSignalled = false;
      I->DrawDeferred = false;
    }
    PUnlockAPIAsGlut(G);
  } else {                      /* we're busy -- so try to display a progress indicator */
    MainDrawProgress(G);
  }
  PRINTFD(G, FB_Main)
    " MainDraw: completed.\n" ENDFD;
  if(usec_sleep_after_draw && G) {
    PSleepUnlocked(G, usec_sleep_after_draw);
  }
}


/*========================================================================*/
static void MainKey(unsigned char k, int x, int y)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  CMain *I = G->Main;
  int glMod;

  glMod = p_glutGetModifiers();

#if defined(_PYMOL_OSX) && !defined(_PYMOL_LIB)
  // MacOS GLUT.framework: swap delete and backspace
  switch (k) {
    case 0x08: k = 0x7f; break;
    case 0x7f: k = 0x08; break;
  }
#endif

  PRINTFB(G, FB_Main, FB_Blather)
    " MainKey: code:%d modifiers:0x%02x x:%d y:%d\n", k, glMod, x, y ENDFB(G);
  if(PLockAPIAsGlut(G, false)) {

    I->IdleMode = 0;            /* restore responsiveness */

    I->Modifiers = ((glMod & P_GLUT_ACTIVE_SHIFT) ? cOrthoSHIFT : 0) |
      ((glMod & P_GLUT_ACTIVE_CTRL) ? cOrthoCTRL : 0) |
      ((glMod & P_GLUT_ACTIVE_ALT) ? cOrthoALT : 0);

    PyMOL_Key(PyMOLInstance, k, x, y, I->Modifiers);

    PUnlockAPIAsGlut(G);
  } else {
    if((k == 8) || (k == 127)) {        /* interrupt busy state (if possibele) */
      PBlock(G);
      PLockStatus(G);
      PyMOL_SetInterrupt(G->PyMOL, true);
      PUnlockStatus(G);
      PUnblock(G);
    }
  }
}


/*========================================================================*/
static void MainSpecial(int k, int x, int y)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  CMain *I = G->Main;
  int glMod;

  PRINTFB(G, FB_Main, FB_Blather)
    " MainSpecial: %d %d %d\n", k, x, y ENDFB(G);
  glMod = p_glutGetModifiers();
  if(PLockAPIAsGlut(G, false)) {

    I->Modifiers = ((glMod & P_GLUT_ACTIVE_SHIFT) ? cOrthoSHIFT : 0) |
      ((glMod & P_GLUT_ACTIVE_CTRL) ? cOrthoCTRL : 0) |
      ((glMod & P_GLUT_ACTIVE_ALT) ? cOrthoALT : 0);

    PyMOL_Special(PyMOLInstance, k, x, y, I->Modifiers);

    PUnlockAPIAsGlut(G);
  }
}


/* new window size or exposure */

/*========================================================================*/
void MainReshape(int width, int height)
{                               /* called by Glut */
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  if(G) {
    CMain *I = G->Main;

    I->ReshapeTime = (double) UtilGetSeconds(G);
    I->DrawnFlag = false;

    if (width==0 || height==0)
      return;
    if(PLockAPIAsGlut(G, true)) {
      if(G->HaveGUI) {
        glViewport(0, 0, (GLint) width, (GLint) height);
        if((!PyMOLInstance) ||
           (width != OrthoGetWidth(G)) || (height != OrthoGetHeight(G))) {
          /* wipe the screen ASAP to prevent display of garbage... */

          int draw_both = G->StereoCapable &&
            ((SceneGetStereo(G) == 1) ||
             SettingGetGlobal_b(G, cSetting_stereo_double_pump_mono));

          SceneGLClearColor(0.0, 0.0, 0.0, 1.0);
          if(draw_both) {
            OrthoDrawBuffer(G, GL_FRONT_LEFT);
            glClear(GL_COLOR_BUFFER_BIT);
            OrthoDrawBuffer(G, GL_FRONT_RIGHT);
            glClear(GL_COLOR_BUFFER_BIT);
            OrthoDrawBuffer(G, GL_BACK_LEFT);
            glClear(GL_COLOR_BUFFER_BIT);
            OrthoDrawBuffer(G, GL_BACK_RIGHT);
            glClear(GL_COLOR_BUFFER_BIT);
          } else {
            OrthoDrawBuffer(G, GL_FRONT);
            glClear(GL_COLOR_BUFFER_BIT);
            OrthoDrawBuffer(G, GL_BACK);
            glClear(GL_COLOR_BUFFER_BIT);
          }
        }
        PyMOL_SwapBuffers(PyMOLInstance);
      }
    }
    if(PyMOLInstance)
      PyMOL_Reshape(PyMOLInstance, width, height, false);
    PUnlockAPIAsGlut(G);
  }
}


/*========================================================================*/
/**
 * only called from CmdViewport(), and only with !_PYMOL_NO_MAIN
 */
void MainDoReshape(int width, int height)
{                               /* called internally */
  int internal_feedback;
  int force = false;
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  if(G) {
    /* if width and height are negative and we are in full screen, don't reshape window */
    bool keep_fullscreen = (width < 0 && height < 0 && ExecutiveIsFullScreen(G));

    /* if width is negative, force a reshape based on the current width */

    if(width < 0) {
      width = SceneGetBlock(G)->getWidth();
      if(SettingGetGlobal_b(G, cSetting_internal_gui))
        width += SettingGetGlobal_i(G, cSetting_internal_gui_width);
      force = true;
    } 

    /* if height is negative, force a reshape based on the current height */

    if(height < 0) {
      height = SceneGetBlock(G)->getHeight();
      internal_feedback = SettingGetGlobal_i(G, cSetting_internal_feedback);
      if(internal_feedback)
        height += (internal_feedback - 1) * cOrthoLineHeight + cOrthoBottomSceneMargin;
      if(SettingGetGlobal_b(G, cSetting_seq_view)
         && !SettingGetGlobal_b(G, cSetting_seq_view_overlay))
        height += SeqGetHeight(G);
      height += MovieGetPanelHeight(G);
      force = true;
    } 

    /* if we have a GUI, for a reshape event */

    if(G->HaveGUI && G->ValidContext && width > 0 && height > 0) {
      p_glutReshapeWindow(width, height);
      glViewport(0, 0, (GLint) width, (GLint) height);
    }

    if((!width)||(!height)) {
      int actual_width = width ? width : G->Option->winX;
      int actual_height = height ? height : G->Option->winY;

      PyMOL_Reshape(G->PyMOL, actual_width, actual_height, true);

    } else {
      PyMOL_Reshape(G->PyMOL, width, height, force);
      if(G->Main) {
        G->Main->DeferReshapeDeferral = 1;
      }

      /* do we need to become full-screen? */
      if(keep_fullscreen) {
        p_glutFullScreen();
      }
    }
  }

}


/*========================================================================*/
static void MainInit(PyMOLGlobals * G)
{

  CMain *I = (G->Main = pymol::calloc<CMain>(1));
  /* Data structure is zeroed on start...no need for explicit zero inits */

  I->DeferReshapeDeferral = 1;

  PyMOL_Start(PyMOLInstance);

  // moved to PyMOL_DrawWithoutLock
  // PyMOL_ConfigureShadersGL(PyMOLInstance);

  PyMOL_SetSwapBuffersFn(PyMOLInstance, (PyMOLSwapBuffersFn *) p_glutSwapBuffers);
  I->ReshapeTime = (I->IdleTime = UtilGetSeconds(G));

  I->DrawGovernorActive = false;
  I->DrawSignalled = true;
  I->DrawDelay = 0.01;          /* 100 FPS max */
  I->DrawAfter = 0.0;
  I->DrawDeferred = false;
}


/*========================================================================*/
void MainFree(void)
{
  PyMOLGlobals *G = PyMOL_GetGlobals(PyMOLInstance);    /* temporary -- will change */

  CPyMOLOptions *owned_options = G->Main->OwnedOptions;

  int show_message = G->Option->show_splash && !G->Option->quiet;


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
  int haveGUI = G->HaveGUI;
  int theWindow = G->Main->TheWindow;
#endif
#ifdef _PYMOL_OSX
  int game_mode = G->Option->game_mode;
  int haveGUI = G->HaveGUI;
  int theWindow = G->Main->TheWindow;
#endif

/* END PROPRIETARY CODE SEGMENT */

  FreeP(G->Main);

  if(owned_options)
    PyMOLOptions_Free(owned_options);   /* clean up launch options if we're supposed to */

  if(show_message) {
    printf(" PyMOL: normal program termination.\n");
  }

/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
  if(haveGUI)
    p_glutDestroyWindow(theWindow);
  TerminateProcess(GetCurrentProcess(), 0);     /* only way to avoid a crash */
#endif
#ifdef _PYMOL_OSX
  if(haveGUI) {
    if(game_mode) {
      p_glutLeaveGameMode();
      /* force a full-screen refresh to eliminate garbage on screen 
       * NOTE that we currently have to patch Apple's GLUT to make this work */
      p_glutInitWindowPosition(0, 0);
      p_glutInitWindowSize(640, 480);
      p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE);
      if(p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE)) {
        theWindow = p_glutCreateWindow("PyMOL Viewer");
        p_glutFullScreen();
        p_glutDestroyWindow(theWindow);
      }
    } else
      p_glutDestroyWindow(theWindow);
  }
#endif

/* END PROPRIETARY CODE SEGMENT */

}


/*========================================================================*/
void MainRefreshNow(void)
{                               /* should only be called by the master thread, with a locked API */
  PyMOLGlobals *G = SingletonPyMOLGlobals;

  CMain *I = G->Main;
  if(PyMOL_GetSwap(G->PyMOL, true)) {
    if(G->HaveGUI) {
      DrawBlueLine(G);
      p_glutSwapBuffers();
    }
  }
  if(PyMOL_GetRedisplay(PyMOLInstance, true)) {
    if(G->HaveGUI)
      p_glutPostRedisplay();
    else
      MainDrawLocked();
    I->IdleMode = 0;
  }
}


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

static void MainBusyIdle(void)
{
  PyMOLGlobals *G = SingletonPyMOLGlobals;
  /* This is one of the few places in the program where we can be sure 
   * that we have the "glut" thread...glut doesn't seem to be completely
   * thread safe or rather thread consistent
   */

  CMain *I = G->Main;

  PRINTFD(G, FB_Main)
    " MainBusyIdle: called.\n" ENDFD;

  /* flush command and output queues */

  /*  PRINTFD(G,FB_Main)
     " MainBusyIdle: entered, IdleMode %d\n",
     I->IdleMode
     ENDFD; */

  if(PLockAPIAsGlut(G, false)) {

    PRINTFD(G, FB_Main)
      " MainBusyIdle: got lock.\n" ENDFD;

    /* change window visibility & refresh, if necessary */

    if(G->HaveGUI) {
      if(I->WindowIsVisible != G->Option->window_visible) {
        I->WindowIsVisible = G->Option->window_visible;
        if(I->WindowIsVisible) {
          p_glutShowWindow();
          OrthoDirty(G);
        } else {
          p_glutHideWindow();
        }
      }
    }

    PRINTFD(G, FB_Main)
      " MainBusyIdle: calling idle function.\n" ENDFD;

    if(PyMOL_Idle(PyMOLInstance)) {
      /* we did some work, so keep PyMOL responsive */
      I->IdleMode = 1;
    } else if(!I->IdleMode) {
      I->IdleMode = 1;
    } else if(I->IdleMode == 1) {
      I->IdleMode = 2;
      I->IdleTime = UtilGetSeconds(G);
    }
    PRINTFD(G, FB_Main)
      " MainBusyIdle: swap check.\n" ENDFD;

    if(PyMOL_GetSwap(G->PyMOL, true)) {
      if(G->HaveGUI) {
        DrawBlueLine(G);
        p_glutSwapBuffers();
      }
    }

    /* if the screen has become dirty, post a redisplay event, or if
       we're running without a GUI, then call the draw routine (if we */

    PRINTFD(G, FB_Main)
      " MainBusyIdle: redisplay.\n" ENDFD;

    if(PyMOL_GetRedisplay(PyMOLInstance, true)) {
      if(G->HaveGUI)
        p_glutPostRedisplay();
      else
        MainDrawLocked();
      if(I->IdleMode > 1) {
        I->IdleMode = 1;
      }
    }

    PRINTFD(G, FB_Main)
      " MainBusyIdle: redisplay.\n" ENDFD;

    /* the following code enables PyMOL to avoid busy-idling 
     * even though we're using GLUT! */

    switch (I->IdleMode) {
    case 2:                    /* avoid racing the CPU */
      if((UtilGetSeconds(G) - I->IdleTime) > (SettingGetGlobal_f(G, cSetting_idle_delay) / 5.0)) {
        I->IdleMode = 3;
        I->IdleTime = UtilGetSeconds(G);
      }
      break;
    case 3:
      if((UtilGetSeconds(G) - I->IdleTime) > (SettingGetGlobal_f(G, cSetting_idle_delay))) {
        I->IdleMode = 4;
        if(G->HaveGUI)
          if(SettingGetGlobal_b(G, cSetting_cache_display)) {
            p_glutPostRedisplay();      /* trigger caching of the current scene */
          }
        break;
      }
    }

    PRINTFD(G, FB_Main)
      " MainBusyIdle: unlocking.\n" ENDFD;

    {
      int control_idling = false;
      if(I->IdleMode == 1) {
        control_idling = ControlIdling(G);
      }
      PUnlockAPIAsGlut(G);

      switch (I->IdleMode) {
      case 4:
        PSleepUnlocked(G, SettingGetGlobal_i(G, cSetting_slow_idle));   /* slow idle - save CPU cycles */
        break;
      case 3:
        PSleepUnlocked(G, SettingGetGlobal_i(G, cSetting_fast_idle));   /* fast idle - more responsive */
        break;
      case 2:
        PSleepUnlocked(G, SettingGetGlobal_i(G, cSetting_no_idle));     /* give Tcl/Tk a chance to run */
        break;
      case 1:
        if(control_idling) {
          PSleepUnlocked(G, SettingGetGlobal_i(G, cSetting_no_idle));   /* give Tcl/Tk a chance to run */
        }
        break;
      default:
        break;
      }
    }

    /* run final initilization code for Python-based PyMOL implementations. */

#define FINAL_INIT_AT 10
    if(I->FinalInitCounter < FINAL_INIT_AT) {
      I->FinalInitCounter = I->FinalInitCounter + 1;
      if(I->FinalInitCounter == FINAL_INIT_AT) {
        I->FinalInitTrigger = true;
        PyMOL_NeedRedisplay(PyMOLInstance);
      }
    }

    /* when running in command-line mode, if we're not reading from
     * standard input and if we're not keeping the thread alive, then 
     * we can have no further input.  Therefore die. */

    if(!G->HaveGUI) {
      if(!(OrthoCommandWaiting(G) ||
           PyMOL_GetModalDraw(G->PyMOL) ||
           OrthoDeferredWaiting(G) || SettingGetGlobal_b(G, cSetting_keep_alive))) {
        if((!G->Option->keep_thread_alive) &&
           (!G->Option->read_stdin) && (I->FinalInitCounter >= FINAL_INIT_AT)) {
          I->IdleCount++;
          if(I->IdleCount == 10) {
            if(PLockAPIAsGlut(G, true)) {
              PParse(G, "_quit");
              PFlush(G);
              PUnlockAPIAsGlut(G);
            }
          }
        }
      } else {
        I->IdleCount = 0;
      }
    }

    {
      int max_ups = SettingGetGlobal_i(G, cSetting_max_ups);
      if(max_ups < 1) {
        I->DrawGovernorActive = false;
        if(I->DrawDeferred) {
          p_glutPostRedisplay();
        }
      } else {
        I->DrawDelay = 1.0 / max_ups;
        I->DrawGovernorActive = true;
        if(I->DrawDeferred) {
          if(UtilGetSeconds(G) > I->DrawAfter) {
            I->DrawSignalled = true;
          }
          if(I->DrawSignalled) {
            I->DrawDeferred = false;
            p_glutPostRedisplay();
          }
        }
      }
    }
  } else {
    PRINTFD(G, FB_Main)
      " MainBusyIdle: lock not obtained...\n" ENDFD;

    PSleepWhileBusy(G, 100000); /* 10 per second */
    if(G->HaveGUI) {
      PBlock(G);
      PLockStatus(G);
      if(PyMOL_GetProgressChanged(G->PyMOL, false))
        p_glutPostRedisplay();
      PUnlockStatus(G);
      PUnblock(G);
    }
  }

  PRINTFD(G, FB_Main)
    " MainBusyIdle: leaving... IdleMode %d\n", I->IdleMode ENDFD;

}

void MainSetWindowPosition(PyMOLGlobals * G, int x, int y)
{
#ifdef _PYMOL_FINK
  y -= 22;                      /* something is wrong with FinkGlut's window positioning... */
#endif
  p_glutPositionWindow(x, y);
}

void MainSetWindowSize(PyMOLGlobals * G, int w, int h)
{
  G->Main->DeferReshapeDeferral = 1;
  p_glutReshapeWindow(w, h);
}

void MainMaximizeWindow(PyMOLGlobals * G)
{
  int height = p_glutGet(P_GLUT_SCREEN_HEIGHT);
  int width = p_glutGet(P_GLUT_SCREEN_WIDTH);
  G->Main->DeferReshapeDeferral = 1;
  G->Main->MaximizeCheck = true;
  p_glutPositionWindow(0, 0);
  p_glutReshapeWindow(width, height);

}

void MainCheckWindowFit(PyMOLGlobals * G)
{
  CMain *I = G->Main;
  if(G && G->Main) {
    int height = p_glutGet(P_GLUT_SCREEN_HEIGHT);
    int width = p_glutGet(P_GLUT_SCREEN_WIDTH);
    int actual_x = p_glutGet(P_GLUT_WINDOW_X);
    int actual_y = p_glutGet(P_GLUT_WINDOW_Y);
    int actual_width = p_glutGet(P_GLUT_WINDOW_WIDTH);
    int actual_height = p_glutGet(P_GLUT_WINDOW_HEIGHT);
    int new_width = -1;
    int new_height = -1;

    I->DeferReshapeDeferral = 1;

    if((actual_x + actual_width) > width)
      new_width = (width - actual_x) - 5;       /* allow room for decoration */
    if((actual_y + actual_height) > height)
      new_height = (height - actual_y) - 5;     /* allow room for decoration */
    if((new_width > 0) || (new_height > 0)) {
      if(new_width < 0)
        new_width = actual_width;
      if(new_height < 0)
        new_height = actual_height;
      MainSetWindowSize(G, new_width, new_height);
    }
  }

}


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


/* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType     /*  control signal type */
  )
{
  switch (dwCtrlType) {
  case CTRL_CLOSE_EVENT:
  case CTRL_BREAK_EVENT:
  case CTRL_C_EVENT:
    TerminateProcess(GetCurrentProcess(), 0);   /* only way to avoid a crash */

    break;
  }
  return 1;
}
#endif

static void launch(CPyMOLOptions * options, int own_the_options)
{

  int multisample_mask = 0;
  int theWindow = 0;
  PyMOLGlobals *G = nullptr;

  PyMOLInstance = PyMOL_NewWithOptions(options);
  G = PyMOL_GetGlobals(PyMOLInstance);

  G->HaveGUI = options->pmgui;

  assert(!SingletonPyMOLGlobals);
  SingletonPyMOLGlobals = G;

  if(G->Option->multisample)
    multisample_mask = P_GLUT_MULTISAMPLE;

  /* if were running GLUT, then we have the ability to increase the
   * size of the window in order to accomodate the context */

  if(G->Option->internal_gui && (!G->Option->game_mode))
    G->Option->winX += cOrthoRightSceneMargin;

  if(G->Option->internal_feedback && (!G->Option->game_mode))
    G->Option->winY +=
      (G->Option->internal_feedback - 1) * cOrthoLineHeight + cOrthoBottomSceneMargin;

  if(G->HaveGUI) {
#ifndef _PYMOL_OSX
    atexit(MainOnExit);         /* register callback to help prevent crashes
                                   when GLUT spontaneously kills us */
#endif

    /* BEGIN PROPRIETARY CODE SEGMENT (see disclaimer in "os_proprietary.h") */
#ifdef WIN32
    SetConsoleCtrlHandler(HandlerRoutine,       // address of handler function
                          true  // handler to add or remove
      );
    SetConsoleTitle(_T("PyMOL"));
#endif

    int myArgc = 0;
    char* myArgv[8] = {strdup("pymol")};
    p_glutInit(&myArgc, myArgv);

    {
      int display_mode_possible = false;
      if(G->Option->stereo_mode > 1)
        G->Option->force_stereo = 0;

      switch (G->Option->force_stereo) {
      case -1:                 /* force mono */
        G->StereoCapable = 0;
        break;
      case 1:                  /* force quad buffer stereo (if possible) */
        G->Option->stereo_mode = cStereo_quadbuffer;
      case 0:                  /* default/autodetect */
        switch (G->Option->stereo_mode) {
        case cStereo_default:
        case cStereo_quadbuffer:
          p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | multisample_mask |
                                P_GLUT_DOUBLE | P_GLUT_STEREO);
          display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
          if(multisample_mask && (!display_mode_possible)) {
            G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_MultisampleFailed;
            p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE |
                                  P_GLUT_STEREO);
            display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
          }
          if(display_mode_possible) {
            G->StereoCapable = 1;
          } else if(G->Option->stereo_mode == cStereo_quadbuffer) {
            G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_StereoFailed;
          }
          break;
        case cStereo_clone_dynamic:
          p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE | P_GLUT_ACCUM
                                | P_GLUT_STEREO);
          display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
          if(!display_mode_possible) {
            G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_StereoFailed;
            G->Option->stereo_mode = 0;
          } else {
            G->StereoCapable = 1;
          }
          break;
        case cStereo_dynamic:
          p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE |
                                P_GLUT_ACCUM);
          display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
          if(!display_mode_possible) {
            G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_StereoFailed;
            G->Option->stereo_mode = 0;
          }
          break;
        case cStereo_stencil_by_row:
        case cStereo_stencil_by_column:
        case cStereo_stencil_checkerboard:
        case cStereo_stencil_custom:
          p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE |
                                P_GLUT_STENCIL);
          display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
          if(!display_mode_possible) {
            G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_StereoFailed;
            G->Option->stereo_mode = 0;
          }
          break;
        case cStereo_anaglyph: /* nothing special required for anaglyph */
          G->StereoCapable = 1;
          break;
        default:               /* fall through */
          break;
        }
        break;
      }
      /* fallback behavior */

      if(!display_mode_possible) {
        G->LaunchStatus &= ~cPyMOLGlobals_LaunchStatus_MultisampleFailed;
        p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | multisample_mask |
                              P_GLUT_DOUBLE);
        display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
        G->StereoCapable = 0;
      }

      if(multisample_mask && (!display_mode_possible)) {
        G->LaunchStatus |= cPyMOLGlobals_LaunchStatus_MultisampleFailed;
        p_glutInitDisplayMode(P_GLUT_RGBA | P_GLUT_DEPTH | P_GLUT_DOUBLE);
        display_mode_possible = p_glutGet(P_GLUT_DISPLAY_MODE_POSSIBLE);
        G->StereoCapable = 0;
      }
    }

    if(!G->Option->game_mode) {
      if((G->Option->winPX > -10000) && (G->Option->winPY > -10000)) {
#ifndef _PYMOL_FINK
        p_glutInitWindowPosition(G->Option->winPX, G->Option->winPY);
#else
        p_glutInitWindowPosition(G->Option->winPX, G->Option->winPY - 22);      /* somethings wrong with FinkGlut's window positioning... */
#endif
      }

      p_glutInitWindowSize(G->Option->winX, G->Option->winY);

      theWindow = p_glutCreateWindow("PyMOL Viewer");

      if(G->Option->full_screen) {
        p_glutFullScreen();
      }

      if(G->Option->window_visible) {
        p_glutShowWindow();
      } else {
        p_glutHideWindow();
      }

    } else {
      char str[255];
      sprintf(str, "%dx%d:32@120", G->Option->winX, G->Option->winY);
      p_glutGameModeString(str);
      p_glutEnterGameMode();
    }
  }
  MainInit(G);
  if(own_the_options)
    G->Main->OwnedOptions = options;
  /* make sure we can clean up these options later if we've been asked to do so */
  {
    CMain *I = G->Main;

    I->TheWindow = theWindow;

    PInit(G, true);

    if(G->HaveGUI) {
      p_glutDisplayFunc(MainDraw);
      p_glutReshapeFunc(MainReshape);
      p_glutKeyboardFunc(MainKey);
      p_glutMouseFunc(MainButton);
      p_glutMotionFunc(MainDrag);
      p_glutPassiveMotionFunc(MainPassive);
      p_glutSpecialFunc(MainSpecial);
      p_glutIdleFunc(MainBusyIdle);
    }

    PUnblock(G);

    if(G->HaveGUI) {
      if(!I->WindowIsVisible)
        MainReshape(G->Option->winX, G->Option->winY);
      I->IdleMode = 3;
      p_glutMainLoop();         /* never returns with traditional GLUT implementation */
      PBlock(G);                /* if we've gotten here, then we're heading back to Python... */
    } else {
      SceneSetCardInfo(G, "none", "ray trace only", "none");
      if(G->Option->show_splash && !G->Option->quiet)
        printf(" Command mode. No graphics front end.\n");
      MainReshape(G->Option->winX, G->Option->winY);
      MainDraw();               /* for command line processing */
      while(1) {
        MainBusyIdle();
        MainDraw();
      }
    }
  }
}


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

static int decoy_input_hook(void) { return 0; }

int main_shared(int block_input_hook)
{
  if(block_input_hook)
    PyOS_InputHook = decoy_input_hook;

#ifdef _DRI_WORKAROUND
  dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL);
#endif

  {                             /* no matter how PyMOL was built, we always come through here... */

    CPyMOLOptions *options = PyMOLOptions_New();

    if(options) {

      PGetOptions(options);

      launch(options, true);
      /* this only returns when PyMOL is not running under GLUT */

    }
  }
  return 0;
}

#endif


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

PyObject *MainAsPyList(PyMOLGlobals *G)
{
#ifdef _PYMOL_NOPY
  return nullptr;
#else
  PyObject *result = nullptr;
  int width, height;
  result = PyList_New(2);
  width = SceneGetBlock(G)->getWidth();
  height = SceneGetBlock(G)->getHeight();
  if(SettingGetGlobal_b(G, cSetting_seq_view)
     && !SettingGetGlobal_b(G, cSetting_seq_view_overlay))
    height += SeqGetHeight(G);
  PyList_SetItem(result, 0, PyInt_FromLong(width));
  PyList_SetItem(result, 1, PyInt_FromLong(height));
  return (PConvAutoNone(result));
#endif
}

int MainFromPyList(PyMOLGlobals *G, PyObject * list)
{
#ifdef _PYMOL_NOPY
  return 0;
#else

  int ok = true;
  int win_x, win_y;
  int ll = 0;
  OrthoLineType buffer;

  if(ok)
    ok = (list != nullptr);
  if(ok)
    ok = PyList_Check(list);
  if(ok)
    ll = PyList_Size(list);
  if(ok && (ll >= 2)) {
    if(!G->Option->presentation &&
        !G->Option->full_screen &&
        !ExecutiveIsFullScreen(G)) {
      if(ok)
        ok = PConvPyIntToInt(PyList_GetItem(list, 0), &win_x);
      if(ok)
        ok = PConvPyIntToInt(PyList_GetItem(list, 1), &win_y);
      /* BlockGetSize(SceneGetBlock(G),&win_x,&win_y); * so how did this get into 0.98beta29?  */
      if(ok) {

        sprintf(buffer, "viewport %d, %d", win_x, win_y);
        PParse(G, buffer);
      }
    }
  }
  return (ok);
#endif
}
