/******************************************************************************!
* \file x11scope.cpp
* \author Sebastien Beaugrand
* \sa http://beaugrand.chez.com/
* \copyright CeCILL 2.1 Free Software license
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "x11/display.h"
#include "x11/window.h"
#include "x11/event.h"
#include "x11Scope.h"
#define DIV_COUNT 10
#define DIV_SIZE 60
#define MENU_BORDER 10
namespace X11 {
/******************************************************************************!
* \fn Scope
******************************************************************************/
Scope::Scope()
: mEvent(NULL)
{
mDisplay = NULL;
mWindow = NULL;
mVal1Offset = 0;
mVal2Offset = 0;
mDivScaleX = 1.0;
mDivScaleY = 1.0;
mMenuSelect = MENU_DIV_SCALE_X;
}
/******************************************************************************!
* \fn ~Scope
******************************************************************************/
Scope::~Scope()
{
if (mEvent != NULL) {
delete mEvent;
}
if (mWindow != NULL) {
delete mWindow;
}
if (mDisplay != NULL) {
delete mDisplay;
}
}
/******************************************************************************!
* \fn drawMenu
******************************************************************************/
void
Scope::drawMenu() const
{
static char str[32];
int p = mDisplay->getFontAscent() + mDisplay->getFontDescent();
int y = mWindow->getHeight() - p;
for (int i = MENU_SIZE - 1; i >= 0; --i) {
if (i == mMenuSelect) {
mDisplay->setColor(1);
} else {
mDisplay->setColor(2);
}
switch (i) {
case MENU_DIV_SCALE_X:
if (mDivScaleX < 1.0) {
sprintf(str, "1.0 div = %.1f s", 1 / mDivScaleX);
} else {
sprintf(str, "%.1f div = 1.0 s", mDivScaleX);
}
mWindow->drawString(p, y, str, strlen(str));
break;
case MENU_DIV_SCALE_Y:
if (mDivScaleY < 1.0) {
sprintf(str, "1.0 div = %.1f V", 1 / mDivScaleY);
} else {
sprintf(str, "%.1f div = 1.0 V", mDivScaleY);
}
mWindow->drawString(p, y, str, strlen(str));
break;
case MENU_POS_V1:
sprintf(str, "offset 1 = %dV", mVal1Offset);
mWindow->drawString(p, y, str, strlen(str));
break;
case MENU_POS_V2:
sprintf(str, "offset 2 = %dV", mVal2Offset);
mWindow->drawString(p, y, str, strlen(str));
break;
default:
fprintf(stderr, "error: menu select\n");
}
y -= p;
}
}
/******************************************************************************!
* \fn drawGrid
******************************************************************************/
void
Scope::drawGrid() const
{
int w = mWindow->getWidth();
int h = mWindow->getHeight();
int x;
int y;
int p;
int c;
mDisplay->setColor(mDisplay->white());
mWindow->clear(mDisplay->black());
y = h >> 1;
p = w / (DIV_COUNT * 10);
c = 0;
for (x = 0; x < w; x += p) {
if (c == 0) {
mDisplay->setColor(2);
mWindow->drawLine(x, 0, x, h);
mDisplay->setColor(mDisplay->white());
mWindow->drawLine(x, y - 3, x, y + 3);
} else {
mWindow->drawLine(x, y - 1, x, y + 1);
}
++c;
if (c == 10) {
c = 0;
}
}
x = w >> 1;
p = h / (DIV_COUNT * 10);
c = 0;
for (y = 0; y < h; y += p) {
if (c == 0) {
mDisplay->setColor(2);
mWindow->drawLine(0, y, w, y);
mDisplay->setColor(mDisplay->white());
mWindow->drawLine(x - 3, y, x + 3, y);
} else {
mWindow->drawLine(x - 1, y, x + 1, y);
}
++c;
if (c == 10) {
c = 0;
}
}
mWindow->drawLine(0, h >> 1, w, h >> 1);
mWindow->drawLine(w >> 1, 0, w >> 1, h);
drawMenu();
mWindow->refresh();
}
/******************************************************************************!
* \fn drawPoints
******************************************************************************/
void
Scope::drawPoints(double time, double val1, double val2)
{
static double x1v1 = 0.0;
static double y1v1 = 0.0;
static double y1v2 = 0.0;
static double timeOffset = -DIV_COUNT;
if (time - timeOffset >= DIV_COUNT / mDivScaleX) {
this->drawGrid();
timeOffset += DIV_COUNT / mDivScaleX;
x1v1 = time - timeOffset;
y1v1 = val1;
y1v2 = val2;
return;
}
double x2v1 = time - timeOffset;
double y2v1 = val1;
int x1 = x1v1 * DIV_SIZE * mDivScaleX;
int y1 = ((DIV_COUNT >> 1) - (mVal1Offset + y1v1) * mDivScaleY) * DIV_SIZE;
int x2 = x2v1 * DIV_SIZE * mDivScaleX;
int y2 = ((DIV_COUNT >> 1) - (mVal1Offset + y2v1) * mDivScaleY) * DIV_SIZE;
mDisplay->setColor(0);
mWindow->drawLine(x1, y1, x2, y2);
mWindow->refresh(x1, y1, x2, y2);
mWindow->flush();
x1v1 = x2v1;
y1v1 = y2v1;
if (val2 > -88.0) {
double y2v2 = val2;
y1 = ((DIV_COUNT >> 1) - (mVal2Offset + y1v2) * mDivScaleY) * DIV_SIZE;
y2 = ((DIV_COUNT >> 1) - (mVal2Offset + y2v2) * mDivScaleY) * DIV_SIZE;
mDisplay->setColor(1);
mWindow->drawLine(x1, y1, x2, y2);
mWindow->refresh(x1, y1, x2, y2);
mWindow->flush();
y1v2 = y2v2;
}
}
/******************************************************************************!
* \fn keyPress
******************************************************************************/
int
Scope::keyPress(const Event* event)
{
KeySym ks = event->getNextKey();
double inc = (event->getNextState() & ShiftMask) ? 0.1 : 1.0;
switch (ks) {
case XK_Escape: return EXIT;
case XK_Up:
mMenuSelect = (mMenuSelect > 0) ? mMenuSelect - 1 : MENU_SIZE - 1;
drawMenu();
mWindow->refresh();
return 0;
case XK_Down:
mMenuSelect = (mMenuSelect + 1) % MENU_SIZE;
drawMenu();
mWindow->refresh();
return 0;
}
switch (mMenuSelect) {
case MENU_DIV_SCALE_X:
if (ks == XK_Left) {
if (mDivScaleX >= 1.0) {
mDivScaleX += inc;
} else {
mDivScaleX = 1 / (1 / mDivScaleX - inc);
}
drawGrid();
} else if (ks == XK_Right) {
if (mDivScaleX > 1.0) {
mDivScaleX -= inc;
} else {
mDivScaleX = 1 / (1 / mDivScaleX + inc);
}
drawGrid();
}
break;
case MENU_DIV_SCALE_Y:
if (ks == XK_Left) {
if (mDivScaleY >= 1.0) {
mDivScaleY += inc;
} else {
mDivScaleY = 1 / (1 / mDivScaleY - inc);
}
drawGrid();
} else if (ks == XK_Right) {
if (mDivScaleY > 1.0) {
mDivScaleY -= inc;
} else {
mDivScaleY = 1 / (1 / mDivScaleY + inc);
}
drawGrid();
}
break;
case MENU_POS_V1:
if (ks == XK_Left) {
--mVal1Offset;
drawGrid();
} else if (ks == XK_Right) {
++mVal1Offset;
drawGrid();
}
break;
case MENU_POS_V2:
if (ks == XK_Left) {
--mVal2Offset;
drawGrid();
} else if (ks == XK_Right) {
++mVal2Offset;
drawGrid();
}
break;
}
return 0;
}
/******************************************************************************!
* \fn run
******************************************************************************/
void
Scope::run(Callback* loopObj,
int (Callback::* loopFunc)(const Event*))
{
try {
mDisplay = new Display(3);
} catch (const Exception& e) {
return;
}
mDisplay->addColor(0, 0, 255, 0);
mDisplay->addColor(1, 255, 0, 0);
mDisplay->addColor(2, 15, 15, 15);
mWindow = new Window(mDisplay,
DIV_SIZE * DIV_COUNT + 1,
DIV_SIZE * DIV_COUNT + 1);
mWindow->setTitle("Oscilloscope");
mWindow->raise(mDisplay->black());
mEvent = new Event(mDisplay);
mEvent->addWindowWithExposeCB(mWindow);
mEvent->addInput(mWindow, KeyPressMask, KeyPress, this,
(int (Callback::*)(const Event*)) & Scope::keyPress);
mEvent->addInput(mWindow, 0, 0, loopObj, loopFunc);
mEvent->run();
}
} // namespace X11