speed-dreams/src/libs/tgfclient/guieventloop.cpp

357 lines
9.2 KiB
C++

/***************************************************************************
guieventloop.cpp -- Event loop for GfuiApplications
-------------------
created : Thu Mar 8 10:00:00 CEST 2006
copyright : (C) 2006 by Brian Gavin ; 2008, 2010 Jean-Philippe Meuret
web : http://www.speed-dreams.org
version : $Id$
***************************************************************************/
/***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include <SDL.h>
#include "tgfclient.h"
#include <chrono>
#include "tgf.h"
#ifdef WEBSERVER
#include "webserver.h"
#endif //WEBSERVER
// Private data (pimp pattern) =============================================
class GfuiEventLoop::Private
{
public:
//! Constructor.
Private();
public: // Public data members.
// Callback function pointers.
void (*cbMouseButton)(int button, int state, int x, int y);
void (*cbMouseMotion)(int x, int y);
void (*cbMousePassiveMotion)(int x, int y);
void (*cbMouseWheel)(int x, int y, unsigned int direction);
void (*cbJoystickAxis)(int joy, int axis, float value);
void (*cbJoystickButton)(int joy, int button, int value);
void (*cbDisplay)(void);
void (*cbReshape)(int width, int height);
// Variables.
bool bRedisplay; // Flag to say if a redisplay is necessary.
double max_refresh; // Max refresh rate.
};
GfuiEventLoop::Private::Private()
: cbMouseButton(0), cbMouseMotion(0), cbMousePassiveMotion(0), cbMouseWheel(0),
cbJoystickAxis(0), cbJoystickButton(0),
cbDisplay(0), cbReshape(0), bRedisplay(false),
max_refresh(50.0)
{
void *hparmScrConf = GfParmReadFileLocal(GFSCR_CONF_FILE, GFPARM_RMODE_STD);
if (hparmScrConf)
{
max_refresh = GfParmGetNum(hparmScrConf, GFSCR_SECT_VALIDPROPS,
GFSCR_ATT_MAXREFRESH, NULL, 50.0);
GfParmReleaseHandle(hparmScrConf);
}
}
// GfuiEventLoop class ============================================================
GfuiEventLoop::GfuiEventLoop()
: GfEventLoop()
{
_pPrivate = new Private;
}
GfuiEventLoop::~GfuiEventLoop()
{
delete _pPrivate;
}
void GfuiEventLoop::injectKeyboardEvent(int code, int modifier, int state,
int unicode, int x, int y)
{
if(GfScrUsingResizableWindow())
{
SDL_GetMouseState(&x, &y);
GfEventLoop::injectKeyboardEvent(code, modifier, state, unicode, x, y);
}
else
{
#ifndef WIN32
// Hard-coded Alt+Enter shortcut, to enable the user to quit/re-enter
// the full-screen mode ; as in SDL's full screen mode, events never reach
// the Window Manager, we need this trick for the user to enjoy
// its WM keyboard shortcuts (didn't find any other way yet).
if (code == SDLK_RETURN && (modifier & KMOD_ALT) && state == 0)
{
if (GfScrToggleFullScreen())
GfLogDebug("Toggle full-screen mode ON \n");
else
GfLogDebug("Toggle full-screen mode OFF \n");
}
else
#endif
{
SDL_GetMouseState(&x, &y);
GfEventLoop::injectKeyboardEvent(code, modifier, state, unicode, x, y);
}
//printf("Key %x State %x mod %x\n", code, state, modifier);
}
}
void GfuiEventLoop::injectMouseMotionEvent(int state, int x, int y)
{
if (state == 0)
{
if (_pPrivate->cbMousePassiveMotion)
_pPrivate->cbMousePassiveMotion(x, y);
}
else
{
if (_pPrivate->cbMouseMotion)
_pPrivate->cbMouseMotion(x, y);
}
}
void GfuiEventLoop::injectMouseButtonEvent(int button, int state, int x, int y)
{
if (_pPrivate->cbMouseButton)
_pPrivate->cbMouseButton(button, state, x, y);
}
void GfuiEventLoop::injectMouseWheelEvent(int x, int y, unsigned int direction)
{
if (_pPrivate->cbMouseWheel)
_pPrivate->cbMouseWheel(x, y, direction);
}
void GfuiEventLoop::injectJoystickAxisEvent(int joy, int axis, float value)
{
if (_pPrivate->cbJoystickAxis)
_pPrivate->cbJoystickAxis(joy, axis, value);
}
void GfuiEventLoop::injectJoystickButtonEvent(int joy, int button, int value)
{
if (_pPrivate->cbJoystickButton)
_pPrivate->cbJoystickButton(joy, button, value);
}
// The event loop itself.
void GfuiEventLoop::operator()()
{
SDL_Event event; // Event structure
static int unicode = 0;
static SDL_Keymod modifier = KMOD_NONE;
static SDL_Keycode keysym = SDLK_UNKNOWN;
// Check for events.
while (!quitRequested())
{
// Loop until there are no events left in the queue.
while (!quitRequested() && SDL_PollEvent(&event))
{
// Process events we care about, and ignore the others.
switch(event.type)
{
case SDL_KEYDOWN:
if((event.key.keysym.sym & SDLK_SCANCODE_MASK) == SDLK_SCANCODE_MASK)
{
injectKeyboardEvent(event.key.keysym.sym, event.key.keysym.mod, 0,0);
}
else if(false == isprint(event.key.keysym.sym))
{
injectKeyboardEvent(event.key.keysym.sym, event.key.keysym.mod, 0,0);
}
else if((event.key.keysym.mod & KMOD_CTRL)
||(event.key.keysym.mod & KMOD_ALT)
||(event.key.keysym.mod & KMOD_GUI))
{
injectKeyboardEvent(event.key.keysym.sym, event.key.keysym.mod, 0,0);
}
else
{
//GfLogDebug("SDL_KEYDOWN: %c\r\n",(char)event.key.keysym.sym);
keysym = event.key.keysym.sym;
}
break;
case SDL_TEXTINPUT:
unicode = (int)(event.text.text[0]);
modifier = SDL_GetModState();
injectKeyboardEvent(keysym, modifier, 0, unicode);
//GfLogDebug("SDL_TEXTINPUT: %c %X\r\n",(char)unicode,modifier);
break;
case SDL_KEYUP:
injectKeyboardEvent(event.key.keysym.sym, event.key.keysym.mod, 1,0);
//GfLogDebug("SDL_KEYUP: %c\r\n",(char)event.key.keysym.sym);
break;
case SDL_MOUSEMOTION:
injectMouseMotionEvent(event.motion.state, event.motion.x, event.motion.y);
break;
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
injectMouseButtonEvent(event.button.button, event.button.state,
event.button.x, event.button.y);
break;
case SDL_MOUSEWHEEL:
injectMouseWheelEvent(event.wheel.x, event.wheel.y, event.wheel.direction);
break;
case SDL_QUIT:
postQuit();
break;
case SDL_JOYAXISMOTION:
injectJoystickAxisEvent(event.jaxis.which, event.jaxis.axis, (float) event.jaxis.value / 32768);
break;
case SDL_JOYBUTTONDOWN:
injectJoystickButtonEvent(event.jbutton.which, event.jbutton.button, SDL_PRESSED);
break;
case SDL_JOYBUTTONUP:
injectJoystickButtonEvent(event.jbutton.which, event.jbutton.button, 0);
break;
case SDL_WINDOWEVENT:
switch(event.window.event)
{
case SDL_WINDOWEVENT_SIZE_CHANGED:
if(_pPrivate->cbReshape)
{
_pPrivate->cbReshape(event.window.data1,event.window.data2);
}
break;
}
break;
}
}
if (!quitRequested())
{
const auto now = std::chrono::system_clock::now();
// Recompute if anything to.
recompute();
// Redisplay if anything to.
redisplay();
const std::chrono::duration<double> elapsed = std::chrono::system_clock::now() - now;
double rate;
if (_pPrivate->max_refresh)
{
rate = 1.0 / _pPrivate->max_refresh;
if (elapsed.count() < rate)
GfSleep(rate - elapsed.count());
}
}
}
GfLogTrace("Quitting GFUI event loop.\n");
}
void GfuiEventLoop::setMouseButtonCB(void (*func)(int button, int state, int x, int y))
{
_pPrivate->cbMouseButton = func;
}
void GfuiEventLoop::setMouseMotionCB(void (*func)(int x, int y))
{
_pPrivate->cbMouseMotion = func;
}
void GfuiEventLoop::setMouseWheelCB(void (*func)(int x, int y, unsigned int direction))
{
_pPrivate->cbMouseWheel = func;
}
void GfuiEventLoop::setMousePassiveMotionCB(void (*func)(int x, int y))
{
_pPrivate->cbMousePassiveMotion = func;
}
void GfuiEventLoop::setRedisplayCB(void (*func)(void))
{
_pPrivate->cbDisplay = func;
}
void GfuiEventLoop::setJoystickAxisCB(void (*func)(int joy, int axis, float value))
{
_pPrivate->cbJoystickAxis = func;
}
void GfuiEventLoop::setJoystickButtonCB(void (*func)(int joy, int button, int value))
{
_pPrivate->cbJoystickButton = func;
}
void GfuiEventLoop::setReshapeCB(void (*func)(int width, int height))
{
_pPrivate->cbReshape = func;
}
void GfuiEventLoop::postRedisplay(void)
{
_pPrivate->bRedisplay = true;
}
void GfuiEventLoop::setMaxRefreshRate(double rate)
{
_pPrivate->max_refresh = rate;
}
void GfuiEventLoop::forceRedisplay()
{
#ifdef WEBSERVER
webServer().updateStatus();
#endif //WEBSERVER
if (_pPrivate->cbDisplay)
_pPrivate->cbDisplay();
}
void GfuiEventLoop::redisplay()
{
#ifdef WEBSERVER
//temp
_pPrivate->bRedisplay=true;
#endif //WEBSERVER
// Refresh display if requested and if any redisplay CB.
if (_pPrivate->bRedisplay)
{
// Acknowledge the request
// (Note: do it before forceRedisplay(), in case it calls postRedisplay() ;-).
_pPrivate->bRedisplay = false;
// Really call the redisplay call-back if any.
forceRedisplay();
}
}