root/src/libs/glut/glutEvent.cpp
/***********************************************************
 *      Copyright (C) 1997, Be Inc.  Copyright (C) 1999, Jake Hamby.
 *
 * This program is freely distributable without licensing fees
 * and is provided without guarantee or warrantee expressed or
 * implied. This program is -not- in the public domain.
 *
 *
 *  FILE:       glutEvent.cpp
 *
 *      DESCRIPTION:    here it is, the BeOS GLUT event loop
 ***********************************************************/

/***********************************************************
 *      Headers
 ***********************************************************/
#include <GL/glut.h>
#include "glutint.h"
#include "glutState.h"
#include "glutBlocker.h"
#include <stdio.h>
#include <stdlib.h>

#define MOUSE_WHEEL_UP   3
#define MOUSE_WHEEL_DOWN 4

/***********************************************************
 *      CLASS:  GLUTtimer
 *
 *      DESCRIPTION:    list of timer callbacks
 ***********************************************************/
struct GLUTtimer {
        GLUTtimer *next;        // list of timers
        bigtime_t timeout;      // time to be called
        GLUTtimerCB func;       // function to call
        int value;                      // value
};

/***********************************************************
 *      Private variables
 ***********************************************************/
static GLUTtimer *__glutTimerList = 0;                  // list of timer callbacks
static GLUTtimer *freeTimerList = 0;

/***********************************************************
 *      FUNCTION:       glutTimerFunc (7.19)
 *
 *      DESCRIPTION:  register a new timer callback
 ***********************************************************/
void APIENTRY
glutTimerFunc(unsigned int interval, GLUTtimerCB timerFunc, int value)
{
  GLUTtimer *timer, *other;
  GLUTtimer **prevptr;

  if (!timerFunc)
    return;

  if (freeTimerList) {
    timer = freeTimerList;
    freeTimerList = timer->next;
  } else {
    timer = new GLUTtimer();
    if (!timer)
      __glutFatalError("out of memory.");
  }

  timer->func = timerFunc;
  timer->value = value;
  timer->next = NULL;
  timer->timeout = system_time() + (interval*1000);     // 1000 ticks in a millisecond
  prevptr = &__glutTimerList;
  other = *prevptr;
  while (other && (other->timeout < timer->timeout)) {
    prevptr = &other->next;
    other = *prevptr;
  }
  timer->next = other;
  *prevptr = timer;
}

/***********************************************************
 *      FUNCTION:       handleTimeouts
 *
 *      DESCRIPTION:  private function to handle outstanding timeouts
 ***********************************************************/
static void
handleTimeouts(void)
{
  bigtime_t now;
  GLUTtimer *timer;

  /* Assumption is that __glutTimerList is already determined
     to be non-NULL. */
  now = system_time();
  while (__glutTimerList->timeout <= now) {
    timer = __glutTimerList;
    if(gState.currentWindow)
            gState.currentWindow->LockGL();
    timer->func(timer->value);
    if(gState.currentWindow)
            gState.currentWindow->UnlockGL();
    __glutTimerList = timer->next;
    timer->next = freeTimerList;
    freeTimerList = timer;
    if (!__glutTimerList)
      break;
  }
}


/***********************************************************
 *      FUNCTION:       processEventsAndTimeouts
 *
 *      DESCRIPTION:  clear gBlock, then check all windows for events
 ***********************************************************/
static void
processEventsAndTimeouts(void)
{
        gBlock.WaitEvent();             // if there is already an event, returns
                                                        // immediately, otherwise wait forever
        gBlock.ClearEvents();

        if(gState.quitAll)
                exit(0);                // exit handler cleans up windows and quits nicely

        if (gState.currentWindow)
                gState.currentWindow->LockGL();
        for(int i=0; i<gState.windowListSize; i++) {
                if (gState.windowList[i]) {
                        GlutWindow *win = gState.windowList[i];
                        // NOTE: we can use win as a shortcut for gState.windowList[i]
                        // in callbacks, EXCEPT we need to check the original variable
                        // after each callback to make sure the window hasn't been destroyed
                        if (win->anyevents) {
                                win->anyevents = false;
                                if (win->reshapeEvent) {
                                        win->reshapeEvent = false;
                                        __glutSetWindow(win);
                                        win->reshape(win->m_width, win->m_height);
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->displayEvent) {
                                        win->displayEvent = false;
                                        __glutSetWindow(win);
                                        win->display();
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->mouseEvent) {
                                        win->mouseEvent = false;
                                        __glutSetWindow(win);
                                        if (win->mouse) {
                                                gState.modifierKeys = win->modifierKeys;
                                                win->mouse(win->button, win->mouseState, win->mouseX, win->mouseY);
                                                gState.modifierKeys = ~0;
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->menuEvent) {
                                        win->menuEvent = false;
                                        __glutSetWindow(win);
                                        GlutMenu *menu = __glutGetMenuByNum(win->menuNumber);
                                        if (menu) {
                                                gState.currentMenu = menu;
                                                menu->select(win->menuValue);
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->statusEvent) {
                                        win->statusEvent = false;
                                        __glutSetWindow(win);
                                        if (gState.menuStatus) {
                                                gState.currentMenu = __glutGetMenuByNum(win->menuNumber);
                                                gState.menuStatus(win->menuStatus, win->statusX, win->statusY);
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->motionEvent) {
                                        win->motionEvent = false;
                                        __glutSetWindow(win);
                                        if (win->motion)
                                                win->motion(win->motionX, win->motionY);
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->passiveEvent) {
                                        win->passiveEvent = false;
                                        __glutSetWindow(win);
                                        if (win->passive)
                                                win->passive(win->passiveX, win->passiveY);
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->keybEvent) {
                                        win->keybEvent = false;
                                        __glutSetWindow(win);
                                        if (win->keyboard) {
                                                gState.modifierKeys = win->modifierKeys;
                                                win->keyboard(win->key, win->keyX, win->keyY);
                                                gState.modifierKeys = ~0;
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->specialEvent) {
                                        win->specialEvent = false;
                                        __glutSetWindow(win);
                                        if (win->special) {
                                                gState.modifierKeys = win->modifierKeys;
                                                win->special(win->specialKey, win->specialX, win->specialY);
                                                gState.modifierKeys = ~0;
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->keybUpEvent) {
                                        win->keybUpEvent = false;
                                        __glutSetWindow(win);
                                        if (win->keyboardUp) {
                                                gState.modifierKeys = win->modifierKeys;
                                                win->keyboardUp(win->key, win->keyX, win->keyY);
                                                gState.modifierKeys = ~0;
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->specialUpEvent) {
                                        win->specialUpEvent = false;
                                        __glutSetWindow(win);
                                        if (win->specialUp) {
                                                gState.modifierKeys = win->modifierKeys;
                                                win->specialUp(win->specialKey, win->specialX, win->specialY);
                                                gState.modifierKeys = ~0;
                                        }
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!


                                if (win->entryEvent) {
                                        win->entryEvent = false;
                                        __glutSetWindow(win);
                                        if (win->entry)
                                                win->entry(win->entryState);
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!

                                if (win->windowStatusEvent) {
                                        win->windowStatusEvent = false;
                                        __glutSetWindow(win);
                                        if (win->windowStatus)
                                                win->windowStatus(win->visState);
                                }
                                if (!gState.windowList[i])
                                        continue;       // window was destroyed by callback!
                        }
                }
        }
        if (gState.currentWindow)
                gState.currentWindow->UnlockGL();

        // This code isn't necessary since BGLView automatically traps errors
#if 0
        if(gState.debug) {
                for(int i=0; i<gState.windowListSize; i++) {
                        if (gState.windowList[i]) {
                                gState.windowList[i]->LockGL();
                                glutReportErrors();
                                gState.windowList[i]->UnlockGL();
                        }
                }
        }
#endif
        if (__glutTimerList) {
      handleTimeouts();
    }
}

/***********************************************************
 *      FUNCTION:       waitForSomething
 *
 *      DESCRIPTION:  use gBlock to wait for a new event or timeout
 ***********************************************************/
static void
waitForSomething(void)
{
        bigtime_t timeout = __glutTimerList->timeout;
        bigtime_t now = system_time();

        if (gBlock.PendingEvent())
                goto immediatelyHandleEvent;

        if(timeout>now)
                gBlock.WaitEvent(timeout-now);
        if (gBlock.PendingEvent()) {
        immediatelyHandleEvent:
                processEventsAndTimeouts();
        } else {
                if (__glutTimerList)
                        handleTimeouts();
        }
}

/***********************************************************
 *      FUNCTION:       idleWait
 *
 *      DESCRIPTION:  check for events, then call idle function
 ***********************************************************/
static void
idleWait(void)
{
  if (gBlock.PendingEvent()) {
    processEventsAndTimeouts();
  } else {
    if (__glutTimerList)
      handleTimeouts();
  }
  /* Make sure idle func still exists! */
  if(gState.currentWindow)
          gState.currentWindow->LockGL();
  if (gState.idle) {
    gState.idle();
  }
  if(gState.currentWindow)
          gState.currentWindow->UnlockGL();
}

/***********************************************************
 *      FUNCTION:       glutMainLoop (3.1)
 *
 *      DESCRIPTION:  enter the event processing loop
 ***********************************************************/
void glutMainLoop()
{
  if (!gState.windowListSize)
    __glutFatalUsage("main loop entered with no windows created.");

  if(gState.currentWindow)
          gState.currentWindow->UnlockGL();

  for (;;) {
    if (gState.idle) {
      idleWait();
    } else {
      if (__glutTimerList) {
        waitForSomething();
      } else {
        processEventsAndTimeouts();
      }
    }
  }
}


void glutSetKeyRepeat(int repeatMode)
{
        switch(repeatMode) {
                case GLUT_KEY_REPEAT_DEFAULT:
                        gState.keyRepeatMode = GLUT_KEY_REPEAT_ON;
                        break;

                case GLUT_KEY_REPEAT_ON:
                case GLUT_KEY_REPEAT_OFF:
                        gState.keyRepeatMode = repeatMode;
                        break;

                default:
                        __glutWarning("invalid glutSetKeyRepeat mode: %d", repeatMode);
        }
}


void glutIgnoreKeyRepeat(int ignore)
{
        if (gState.currentWindow)
                gState.currentWindow->ignoreKeyRepeat = (ignore != 0);
}


/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       KeyDown
 *
 *      DESCRIPTION:  handles keyboard and special events
 ***********************************************************/
void GlutWindow::KeyDown(const char *s, int32 slen)
{
  ulong aChar = s[0];
  BGLView::KeyDown(s,slen);

  BPoint p;

        if (ignoreKeyRepeat
                && Window()->CurrentMessage()->FindInt32("be:key_repeat") > 0)
                return;

        switch (aChar) {
                case B_FUNCTION_KEY:
                switch(Window()->CurrentMessage()->FindInt32("key")) {
                        case B_F1_KEY:
                                aChar = GLUT_KEY_F1;
                                goto specialLabel;
                        case B_F2_KEY:
                                aChar = GLUT_KEY_F2;
                                goto specialLabel;
                        case B_F3_KEY:
                                aChar = GLUT_KEY_F3;
                                goto specialLabel;
                        case B_F4_KEY:
                                aChar = GLUT_KEY_F4;
                                goto specialLabel;
                        case B_F5_KEY:
                                aChar = GLUT_KEY_F5;
                                goto specialLabel;
                        case B_F6_KEY:
                                aChar = GLUT_KEY_F6;
                                goto specialLabel;
                        case B_F7_KEY:
                                aChar = GLUT_KEY_F7;
                                goto specialLabel;
                        case B_F8_KEY:
                                aChar = GLUT_KEY_F8;
                                goto specialLabel;
                        case B_F9_KEY:
                                aChar = GLUT_KEY_F9;
                                goto specialLabel;
                        case B_F10_KEY:
                                aChar = GLUT_KEY_F10;
                                goto specialLabel;
                        case B_F11_KEY:
                                aChar = GLUT_KEY_F11;
                                goto specialLabel;
                        case B_F12_KEY:
                                aChar = GLUT_KEY_F12;
                                goto specialLabel;
                        default:
                                return;
                }
                case B_LEFT_ARROW:
                        aChar = GLUT_KEY_LEFT;
                        goto specialLabel;
                case B_UP_ARROW:
                        aChar = GLUT_KEY_UP;
                        goto specialLabel;
                case B_RIGHT_ARROW:
                        aChar = GLUT_KEY_RIGHT;
                        goto specialLabel;
                case B_DOWN_ARROW:
                        aChar = GLUT_KEY_DOWN;
                        goto specialLabel;
                case B_PAGE_UP:
                        aChar = GLUT_KEY_PAGE_UP;
                        goto specialLabel;
                case B_PAGE_DOWN:
                        aChar = GLUT_KEY_PAGE_DOWN;
                        goto specialLabel;
                case B_HOME:
                        aChar = GLUT_KEY_HOME;
                        goto specialLabel;
                case B_END:
                        aChar = GLUT_KEY_END;
                        goto specialLabel;
                case B_INSERT:
            aChar = GLUT_KEY_INSERT;
specialLabel:
                        if (special) {
                                anyevents = specialEvent = true;
                                GetMouse(&p,&m_buttons);
                                specialKey = aChar;
                                specialX = (int)p.x;
                                specialY = (int)p.y;
                                goto setModifiers;      // set the modifier variable
                        }
                        return;

                default:
                        break;
        }

        if (keyboard) {
                anyevents = keybEvent = true;
                GetMouse(&p,&m_buttons);
                key = aChar;
                keyX = (int)p.x;
                keyY = (int)p.y;
setModifiers:
                modifierKeys = 0;
                uint32 beMod = Window()->CurrentMessage()->FindInt32("modifiers");
                if(beMod & B_SHIFT_KEY)
                        modifierKeys |= GLUT_ACTIVE_SHIFT;
                if(beMod & B_CONTROL_KEY)
                        modifierKeys |= GLUT_ACTIVE_CTRL;
                if(beMod & B_OPTION_KEY) {
                        // since the window traps B_COMMAND_KEY, we'll have to settle
                        // for the option key.. but we need to get the raw character,
                        // not the Unicode-enhanced version
                        key = Window()->CurrentMessage()->FindInt32("raw_char");
                        modifierKeys |= GLUT_ACTIVE_ALT;
                }
                gBlock.NewEvent();
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       KeyUp
 *
 *      DESCRIPTION:  handles keyboard and special events
 ***********************************************************/
void GlutWindow::KeyUp(const char *s, int32 slen)
{
  ulong aChar = s[0];
  BGLView::KeyUp(s,slen);

  BPoint p;

        switch (aChar) {
                case B_FUNCTION_KEY:
                switch(Window()->CurrentMessage()->FindInt32("key")) {
                        case B_F1_KEY:
                                aChar = GLUT_KEY_F1;
                                goto specialLabel;
                        case B_F2_KEY:
                                aChar = GLUT_KEY_F2;
                                goto specialLabel;
                        case B_F3_KEY:
                                aChar = GLUT_KEY_F3;
                                goto specialLabel;
                        case B_F4_KEY:
                                aChar = GLUT_KEY_F4;
                                goto specialLabel;
                        case B_F5_KEY:
                                aChar = GLUT_KEY_F5;
                                goto specialLabel;
                        case B_F6_KEY:
                                aChar = GLUT_KEY_F6;
                                goto specialLabel;
                        case B_F7_KEY:
                                aChar = GLUT_KEY_F7;
                                goto specialLabel;
                        case B_F8_KEY:
                                aChar = GLUT_KEY_F8;
                                goto specialLabel;
                        case B_F9_KEY:
                                aChar = GLUT_KEY_F9;
                                goto specialLabel;
                        case B_F10_KEY:
                                aChar = GLUT_KEY_F10;
                                goto specialLabel;
                        case B_F11_KEY:
                                aChar = GLUT_KEY_F11;
                                goto specialLabel;
                        case B_F12_KEY:
                                aChar = GLUT_KEY_F12;
                                goto specialLabel;
                        default:
                                return;
                }
                case B_LEFT_ARROW:
                        aChar = GLUT_KEY_LEFT;
                        goto specialLabel;
                case B_UP_ARROW:
                        aChar = GLUT_KEY_UP;
                        goto specialLabel;
                case B_RIGHT_ARROW:
                        aChar = GLUT_KEY_RIGHT;
                        goto specialLabel;
                case B_DOWN_ARROW:
                        aChar = GLUT_KEY_DOWN;
                        goto specialLabel;
                case B_PAGE_UP:
                        aChar = GLUT_KEY_PAGE_UP;
                        goto specialLabel;
                case B_PAGE_DOWN:
                        aChar = GLUT_KEY_PAGE_DOWN;
                        goto specialLabel;
                case B_HOME:
                        aChar = GLUT_KEY_HOME;
                        goto specialLabel;
                case B_END:
                        aChar = GLUT_KEY_END;
                        goto specialLabel;
                case B_INSERT:
            aChar = GLUT_KEY_INSERT;
specialLabel:
                        if (specialUp!=0) {
                                anyevents = specialUpEvent = true;
                                GetMouse(&p,&m_buttons);
                                specialKey = aChar;
                                specialX = (int)p.x;
                                specialY = (int)p.y;
                                goto setModifiers;      // set the modifier variable
                        }
                        return;

                default:
                        break;
        }

        if (keyboardUp!=0) {
                anyevents = keybUpEvent = true;
                GetMouse(&p,&m_buttons);
                key = aChar;
                keyX = (int)p.x;
                keyY = (int)p.y;
setModifiers:
                modifierKeys = 0;
                uint32 beMod = Window()->CurrentMessage()->FindInt32("modifiers");
                if(beMod & B_SHIFT_KEY)
                        modifierKeys |= GLUT_ACTIVE_SHIFT;
                if(beMod & B_CONTROL_KEY)
                        modifierKeys |= GLUT_ACTIVE_CTRL;
                if(beMod & B_OPTION_KEY) {
                        // since the window traps B_COMMAND_KEY, we'll have to settle
                        // for the option key.. but we need to get the raw character,
                        // not the Unicode-enhanced version
                        key = Window()->CurrentMessage()->FindInt32("raw_char");
                        modifierKeys |= GLUT_ACTIVE_ALT;
                }
                gBlock.NewEvent();
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       MouseDown
 *
 *      DESCRIPTION:  handles mouse and menustatus events
 ***********************************************************/
void GlutWindow::MouseDown(BPoint point)
{
        BGLView::MouseDown(point);
        MouseCheck();
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       MouseCheck
 *
 *      DESCRIPTION:  checks for button state changes
 ***********************************************************/
void GlutWindow::MouseCheck()
{
        if (mouseEvent)
                return;         // we already have an outstanding mouse event

        BPoint point;
        uint32 newButtons;
        GetMouse(&point, &newButtons);
        if (m_buttons != newButtons) {
                if (newButtons&B_PRIMARY_MOUSE_BUTTON && !(m_buttons&B_PRIMARY_MOUSE_BUTTON)) {
                        button = GLUT_LEFT_BUTTON;
                        mouseState = GLUT_DOWN;
                } else if (m_buttons&B_PRIMARY_MOUSE_BUTTON && !(newButtons&B_PRIMARY_MOUSE_BUTTON)) {
                        button = GLUT_LEFT_BUTTON;
                        mouseState = GLUT_UP;
                } else if (newButtons&B_SECONDARY_MOUSE_BUTTON && !(m_buttons&B_SECONDARY_MOUSE_BUTTON)) {
                        button = GLUT_RIGHT_BUTTON;
                        mouseState = GLUT_DOWN;
                } else if (m_buttons&B_SECONDARY_MOUSE_BUTTON && !(newButtons&B_SECONDARY_MOUSE_BUTTON)) {
                        button = GLUT_RIGHT_BUTTON;
                        mouseState = GLUT_UP;
                } else if (newButtons&B_TERTIARY_MOUSE_BUTTON && !(m_buttons&B_TERTIARY_MOUSE_BUTTON)) {
                        button = GLUT_MIDDLE_BUTTON;
                        mouseState = GLUT_DOWN;
                } else if (m_buttons&B_TERTIARY_MOUSE_BUTTON && !(newButtons&B_TERTIARY_MOUSE_BUTTON)) {
                        button = GLUT_MIDDLE_BUTTON;
                        mouseState = GLUT_UP;
                }
        } else {
                return;         // no change, return
        }
        m_buttons = newButtons;

        if (mouseState == GLUT_DOWN) {
                BWindow *w = Window();
                GlutMenu *m = __glutGetMenuByNum(menu[button]);
                if (m) {
                        if (gState.menuStatus) {
                                anyevents = statusEvent = true;
                                menuNumber = menu[button];
                                menuStatus = GLUT_MENU_IN_USE;
                                statusX = (int)point.x;
                                statusY = (int)point.y;
                                gBlock.NewEvent();
                        }
                        BRect bounds = w->Frame();
                        point.x += bounds.left;
                        point.y += bounds.top;
                        GlutPopUp *bmenu = static_cast<GlutPopUp*>(m->CreateBMenu());   // start menu
                        bmenu->point = point;
                        bmenu->win = this;
                        thread_id menu_thread = spawn_thread(MenuThread, "menu thread", B_NORMAL_PRIORITY, bmenu);
                        resume_thread(menu_thread);
                        return;
                }
        }

        if (mouse) {
                anyevents = mouseEvent = true;
                mouseX = (int)point.x;
                mouseY = (int)point.y;
                modifierKeys = 0;
                uint32 beMod = modifiers();
                if(beMod & B_SHIFT_KEY)
                        modifierKeys |= GLUT_ACTIVE_SHIFT;
                if(beMod & B_CONTROL_KEY)
                        modifierKeys |= GLUT_ACTIVE_CTRL;
                if(beMod & B_OPTION_KEY) {
                        modifierKeys |= GLUT_ACTIVE_ALT;
                }
                gBlock.NewEvent();
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       MouseMoved
 *
 *      DESCRIPTION:  handles entry, motion, and passive events
 ***********************************************************/
void GlutWindow::MouseMoved(BPoint point,
                                                uint32 transit, const BMessage *msg)
{
        BGLView::MouseMoved(point,transit,msg);

        if(transit != B_INSIDE_VIEW) {
                if (entry) {
                        anyevents = entryEvent = true;
                        gBlock.NewEvent();
                }
                if (transit == B_ENTERED_VIEW) {
                        entryState = GLUT_ENTERED;
                        MakeFocus();    // make me the current focus
                        __glutSetCursor(cursor);
                } else
                        entryState = GLUT_LEFT;
        }

        MouseCheck();
        if(m_buttons) {
                if(motion) {
                        anyevents = motionEvent = true;
                        motionX = (int)point.x;
                        motionY = (int)point.y;
                        gBlock.NewEvent();
                }
        } else {
                if(passive) {
                        anyevents = passiveEvent = true;
                        passiveX = (int)point.x;
                        passiveY = (int)point.y;
                        gBlock.NewEvent();
                }
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       MessageReceived
 *
 *      DESCRIPTION:  handles mouse wheel events
 ***********************************************************/

void GlutWindow::MessageReceived(BMessage *message)
{
        switch(message->what){
        case B_MOUSE_WHEEL_CHANGED:
        {
                float shift=0;
                if(message->FindFloat("be:wheel_delta_y",&shift)==B_OK) {
                if(shift>0)button = MOUSE_WHEEL_UP;
                if(shift<0)button = MOUSE_WHEEL_DOWN;
                if(shift!=0) {
                  anyevents = mouseEvent = true;
                  gBlock.NewEvent();
                 }
                }
                break;
        }
        default:
                BGLView::MessageReceived(message);
                break;
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       FrameResized
 *
 *      DESCRIPTION:  handles reshape event
 ***********************************************************/
void GlutWindow::FrameResized(float width, float height)
{
        BGLView::FrameResized(width, height);
        if (visible) {
                anyevents = reshapeEvent = true;
                m_width = (int)(width)+1;
                m_height = (int)(height)+1;
                gBlock.NewEvent();
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       Draw
 *
 *      DESCRIPTION:  handles reshape and display events
 ***********************************************************/
void GlutWindow::Draw(BRect updateRect)
{
        BGLView::Draw(updateRect);
        BRect frame = Frame();
        if (m_width != (frame.Width()+1) || m_height != (frame.Height()+1)) {
                FrameResized(frame.Width(), frame.Height());
        }
        Window()->Lock();
        if (visible) {
                anyevents = displayEvent = true;
                gBlock.NewEvent();
        }
        Window()->Unlock();
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       Pulse
 *
 *      DESCRIPTION:  handles mouse up event (MouseUp is broken)
 ***********************************************************/
void GlutWindow::Pulse()
{
        BGLView::Pulse();
        if (m_buttons) {        // if there are buttons pressed
                MouseCheck();
        }
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       ErrorCallback
 *
 *      DESCRIPTION:  handles GL error messages
 ***********************************************************/
void GlutWindow::ErrorCallback(unsigned long errorCode) {
        __glutWarning("GL error: %s", gluErrorString(errorCode));
}

/***********************************************************
 *      CLASS:          GlutWindow
 *
 *      FUNCTION:       MenuThread
 *
 *      DESCRIPTION:  a new thread to launch popup menu, wait
 *                      wait for response, then clean up afterwards and
 *                      send appropriate messages
 ***********************************************************/
status_t GlutWindow::MenuThread(void *m) {
        GlutPopUp *bmenu = static_cast<GlutPopUp*>(m);
        GlutWindow *win = bmenu->win;   // my window
        GlutBMenuItem *result = (GlutBMenuItem*)bmenu->Go(bmenu->point);
        win->Window()->Lock();
        win->anyevents = win->statusEvent = true;
        win->menuStatus = GLUT_MENU_NOT_IN_USE;
        win->menuNumber = bmenu->menu;
        BPoint cursor;
        uint32 buttons;
        win->GetMouse(&cursor, &buttons);
        win->statusX = (int)cursor.x;
        win->statusY = (int)cursor.y;
        if(result && result->menu) {
                win->menuEvent = true;
                win->menuNumber = result->menu;  // in case it was a submenu
                win->menuValue = result->value;
        }
        win->Window()->Unlock();
        gBlock.NewEvent();
        delete bmenu;
        return B_OK;
}