diff --git a/CMakeLists.txt b/CMakeLists.txt index 1f25432..bade6af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,7 @@ SET (cubicsdr_sources src/audio/AudioThread.cpp src/util/Gradient.cpp src/util/Timer.cpp + src/util/MouseTracker.cpp src/visual/PrimaryGLContext.cpp src/visual/ScopeCanvas.cpp src/visual/ScopeContext.cpp @@ -115,6 +116,7 @@ SET (cubicsdr_headers src/util/Gradient.h src/util/Timer.h src/util/ThreadQueue.h + src/util/MouseTracker.h src/visual/PrimaryGLContext.h src/visual/ScopeCanvas.h src/visual/ScopeContext.h diff --git a/src/CubicSDR.h b/src/CubicSDR.h index 74cf702..e767c7f 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -36,6 +36,10 @@ public: return iqVisualQueue; } + DemodulatorInstance *getDemodTest() { + return demodulatorTest; + } + private: PrimaryGLContext *m_glContext; diff --git a/src/util/MouseTracker.cpp b/src/util/MouseTracker.cpp new file mode 100644 index 0000000..3c4a7a7 --- /dev/null +++ b/src/util/MouseTracker.cpp @@ -0,0 +1,118 @@ +#include "MouseTracker.h" +#include + +void MouseTracker::OnMouseMoved(wxMouseEvent& event) { + if (target == NULL) { + return; + } + + const wxSize ClientSize = target->GetClientSize(); + + mouseX = (float) event.m_x / (float) ClientSize.x; + mouseY = (float) event.m_y / (float) ClientSize.y; + + deltaMouseX = mouseX - lastMouseX; + deltaMouseY = mouseY - lastMouseY; + + if (isMouseDown) { + lastMouseX = mouseX; + + if (vertDragLock && mouseY != lastMouseY) { + target->WarpPointer(event.m_x, lastMouseY * ClientSize.y); + } else { + lastMouseY = mouseY; + } + } else { + lastMouseY = mouseY; + lastMouseX = mouseX; + } +} + +void MouseTracker::OnMouseDown(wxMouseEvent& event) { + if (target == NULL) { + return; + } + + const wxSize ClientSize = target->GetClientSize(); + + mouseX = lastMouseX = (float) event.m_x / (float) ClientSize.x; + mouseY = lastMouseY = (float) event.m_y / (float) ClientSize.y; + + originMouseX = mouseX; + originMouseY = mouseY; + + isMouseDown = true; +} + +void MouseTracker::OnMouseWheelMoved(wxMouseEvent& event) { +// std::cout << "wheel?" << std::endl; +} + +void MouseTracker::OnMouseReleased(wxMouseEvent& event) { + isMouseDown = false; +} + +void MouseTracker::OnMouseEnterWindow(wxMouseEvent& event) { + isMouseInView = true; +} + +void MouseTracker::OnMouseLeftWindow(wxMouseEvent& event) { + isMouseDown = false; + isMouseInView = false; +} + +float MouseTracker::getOriginMouseX() { + return originMouseX; +} + +float MouseTracker::getOriginMouseY() { + return originMouseY; +} + +float MouseTracker::getOriginDeltaMouseX() { + return mouseX - originMouseX; +} + +float MouseTracker::getOriginDeltaMouseY() { + return mouseY - originMouseY; +} + +float MouseTracker::getDeltaMouseX() { + return deltaMouseX; +} + +float MouseTracker::getDeltaMouseY() { + return deltaMouseY; +} + +float MouseTracker::getLastMouseX() { + return lastMouseX; +} + +float MouseTracker::getLastMouseY() { + return lastMouseY; +} + +float MouseTracker::getMouseX() { + return mouseX; +} + +float MouseTracker::getMouseY() { + return mouseY; +} + +void MouseTracker::setVertDragLock(bool dragLock) { + vertDragLock = dragLock; +} + +bool MouseTracker::mouseDown() { + return isMouseDown; +} + +bool MouseTracker::mouseInView() { + return isMouseInView; +} + +void MouseTracker::setTarget(wxWindow *target_in) { + target = target_in; +} diff --git a/src/util/MouseTracker.h b/src/util/MouseTracker.h new file mode 100644 index 0000000..9e00b41 --- /dev/null +++ b/src/util/MouseTracker.h @@ -0,0 +1,51 @@ +#pragma once + +#include "wx/window.h" + +class MouseTracker { +public: + MouseTracker(wxWindow *target) : + target(target), mouseX(0), mouseY(0), lastMouseX(0), lastMouseY(0), originMouseX(0), originMouseY(0), deltaMouseX(0), deltaMouseY(0), isMouseDown( + false), vertDragLock(false), isMouseInView(false) { + + } + + MouseTracker() : + target(NULL), mouseX(0), mouseY(0), lastMouseX(0), lastMouseY(0), originMouseX(0), originMouseY(0), deltaMouseX(0), deltaMouseY(0), isMouseDown( + false), vertDragLock(false), isMouseInView(false) { + + } + + void OnMouseMoved(wxMouseEvent& event); + void OnMouseDown(wxMouseEvent& event); + void OnMouseWheelMoved(wxMouseEvent& event); + void OnMouseReleased(wxMouseEvent& event); + void OnMouseEnterWindow(wxMouseEvent& event); + void OnMouseLeftWindow(wxMouseEvent& event); + + float getOriginMouseX(); + float getOriginMouseY(); + float getOriginDeltaMouseX(); + float getOriginDeltaMouseY(); + float getDeltaMouseX(); + float getDeltaMouseY(); + float getLastMouseX(); + float getLastMouseY(); + float getMouseX(); + float getMouseY(); + + void setVertDragLock(bool dragLock); + bool mouseDown(); + bool mouseInView(); + void setTarget(wxWindow *target_in); + +private: + float mouseX, mouseY; + float lastMouseX, lastMouseY; + float deltaMouseX, deltaMouseY; + float originMouseX, originMouseY; + + bool isMouseDown, isMouseInView; + bool vertDragLock; + wxWindow *target; +}; diff --git a/src/visual/ScopeCanvas.cpp b/src/visual/ScopeCanvas.cpp index 36a11ae..a7d7ce9 100644 --- a/src/visual/ScopeCanvas.cpp +++ b/src/visual/ScopeCanvas.cpp @@ -57,13 +57,13 @@ void ScopeCanvas::OnKeyDown(wxKeyEvent& event) { freq = wxGetApp().getFrequency(); freq += 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); - break; + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); + break; case WXK_LEFT: freq = wxGetApp().getFrequency(); freq -= 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; case WXK_DOWN: break; @@ -81,7 +81,7 @@ void ScopeCanvas::OnIdle(wxIdleEvent &event) { // timer.update(); // frameTimer += timer.lastUpdateSeconds(); // if (frameTimer > 1.0/30.0) { - Refresh(false); + Refresh(false); // frameTimer = 0; // } } diff --git a/src/visual/ScopeCanvas.h b/src/visual/ScopeCanvas.h index 0e3145d..bc12903 100644 --- a/src/visual/ScopeCanvas.h +++ b/src/visual/ScopeCanvas.h @@ -29,7 +29,7 @@ private: ScopeContext *glContext; Timer timer; - float frameTimer; + float frameTimer; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index 28354d5..d949b18 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -29,7 +29,7 @@ wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0), isMouseDown(false) { + wxFULL_REPAINT_ON_RESIZE), parent(parent), frameTimer(0) { int in_block_size = BUF_SIZE / 2; int out_block_size = FFT_SIZE; @@ -44,6 +44,9 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : glContext = new SpectrumContext(this, &wxGetApp().GetContext(this)); timer.start(); + mTracker.setTarget(this); + mTracker.setVertDragLock(true); + SetCursor(wxCURSOR_SIZEWE); } @@ -72,13 +75,13 @@ void SpectrumCanvas::OnKeyDown(wxKeyEvent& event) { freq = wxGetApp().getFrequency(); freq += 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; case WXK_LEFT: freq = wxGetApp().getFrequency(); freq -= 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; case WXK_DOWN: break; @@ -167,62 +170,44 @@ void SpectrumCanvas::OnIdle(wxIdleEvent &event) { // timer.update(); // frameTimer += timer.lastUpdateSeconds(); // if (frameTimer > 1.0/30.0) { - Refresh(false); + Refresh(false); // frameTimer = 0; // } } - - - - void SpectrumCanvas::mouseMoved(wxMouseEvent& event) { - if (isMouseDown) { - const wxSize ClientSize = GetClientSize(); - float mouseX = (float)event.m_x/(float)ClientSize.x; - float mouseY = (float)event.m_y/(float)ClientSize.y; + mTracker.OnMouseMoved(event); + if (mTracker.mouseDown()) { + int freqChange = mTracker.getDeltaMouseX() * SRATE; + if (freqChange != 0) { + int freq = wxGetApp().getFrequency(); + freq -= freqChange; + wxGetApp().setFrequency(freq); - deltaMouseX = mouseX-lastMouseX; - deltaMouseY = mouseY-lastMouseY; - - lastMouseX = mouseX; -// lastMouseY = mouseY; - - int freqChange = deltaMouseX*SRATE; - - int freq = wxGetApp().getFrequency(); - freq -= freqChange; - wxGetApp().setFrequency(freq); - - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %s"),wxNumberFormatter::ToString((long)freq,wxNumberFormatter::Style_WithThousandsSep))); - - if (mouseY != lastMouseY) { - WarpPointer(event.m_x,lastMouseY*ClientSize.y); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxString::Format(wxT("Set center frequency: %s"), + wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); } } } void SpectrumCanvas::mouseDown(wxMouseEvent& event) { - const wxSize ClientSize = GetClientSize(); - lastMouseX = (float)event.m_x/(float)ClientSize.x; - lastMouseY = (float)event.m_y/(float)ClientSize.y; - - isMouseDown = true; + mTracker.OnMouseDown(event); SetCursor(wxCURSOR_CROSS); } void SpectrumCanvas::mouseWheelMoved(wxMouseEvent& event) { - std::cout << "wheel?" << std::endl; + mTracker.OnMouseWheelMoved(event); } void SpectrumCanvas::mouseReleased(wxMouseEvent& event) { - isMouseDown = false; + mTracker.OnMouseReleased(event); SetCursor(wxCURSOR_SIZEWE); } void SpectrumCanvas::mouseLeftWindow(wxMouseEvent& event) { - isMouseDown = false; + mTracker.OnMouseLeftWindow(event); SetCursor(wxCURSOR_SIZEWE); } diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 7c5089c..46d68ef 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -10,6 +10,7 @@ #include "fftw3.h" #include "Timer.h" +#include "MouseTracker.h" class SpectrumCanvas: public wxGLCanvas { public: @@ -48,9 +49,7 @@ private: Timer timer; float frameTimer; - float lastMouseX, lastMouseY; - float deltaMouseX, deltaMouseY; - bool isMouseDown; + MouseTracker mTracker; // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index f45a942..2b26167 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -15,9 +15,16 @@ #include "AppFrame.h" #include +#include + wxBEGIN_EVENT_TABLE(WaterfallCanvas, wxGLCanvas) EVT_PAINT(WaterfallCanvas::OnPaint) EVT_KEY_DOWN(WaterfallCanvas::OnKeyDown) EVT_IDLE(WaterfallCanvas::OnIdle) +EVT_MOTION(WaterfallCanvas::mouseMoved) +EVT_LEFT_DOWN(WaterfallCanvas::mouseDown) +EVT_LEFT_UP(WaterfallCanvas::mouseReleased) +EVT_LEAVE_WINDOW(WaterfallCanvas::mouseLeftWindow) +EVT_ENTER_WINDOW(WaterfallCanvas::mouseEnterWindow) wxEND_EVENT_TABLE() WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : @@ -37,6 +44,8 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : glContext = new WaterfallContext(this, &wxGetApp().GetContext(this)); timer.start(); + mTracker.setTarget(this); + SetCursor(wxCURSOR_CROSS); } @@ -51,8 +60,14 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->SetCurrent(*this); glViewport(0, 0, ClientSize.x, ClientSize.y); + glContext->BeginDraw(); glContext->Draw(spectrum_points); + if (mTracker.mouseInView()) { + glContext->DrawFreqSelector(mTracker.getMouseX()); + } + glContext->EndDraw(); + SwapBuffers(); } @@ -65,13 +80,13 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { freq = wxGetApp().getFrequency(); freq += 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; case WXK_LEFT: freq = wxGetApp().getFrequency(); freq -= 100000; wxGetApp().setFrequency(freq); - ((wxFrame*)parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"),freq)); + ((wxFrame*) parent)->GetStatusBar()->SetStatusText(wxString::Format(wxT("Set center frequency: %i"), freq)); break; case WXK_DOWN: break; @@ -158,7 +173,64 @@ void WaterfallCanvas::OnIdle(wxIdleEvent &event) { // timer.update(); // frameTimer += timer.lastUpdateSeconds(); // if (frameTimer > 1.0/30.0) { - Refresh(false); + Refresh(false); // frameTimer = 0; // } } + +void WaterfallCanvas::mouseMoved(wxMouseEvent& event) { + mTracker.OnMouseMoved(event); + if (mTracker.mouseDown()) { + int freqChange = mTracker.getDeltaMouseX() * SRATE; + + if (freqChange != 0) { + int freq = wxGetApp().getFrequency(); + freq -= freqChange; + wxGetApp().setFrequency(freq); + + ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxString::Format(wxT("Set center frequency: %s"), + wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + } + } +} + +void WaterfallCanvas::mouseDown(wxMouseEvent& event) { + mTracker.OnMouseDown(event); + SetCursor(wxCURSOR_CROSS); +} + +void WaterfallCanvas::mouseWheelMoved(wxMouseEvent& event) { + mTracker.OnMouseWheelMoved(event); +} + +void WaterfallCanvas::mouseReleased(wxMouseEvent& event) { + mTracker.OnMouseReleased(event); + + if (mTracker.getOriginDeltaMouseX() == 0 && mTracker.getOriginDeltaMouseX() == 0) { + + float pos = mTracker.getMouseX(); + + int freq = wxGetApp().getFrequency(); + + freq += (pos - 0.5) * SRATE; + + wxGetApp().setFrequency(freq); + + ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxString::Format(wxT("Set center frequency: %s"), + wxNumberFormatter::ToString((long) freq, wxNumberFormatter::Style_WithThousandsSep))); + } + + SetCursor(wxCURSOR_SIZEWE); +} + +void WaterfallCanvas::mouseLeftWindow(wxMouseEvent& event) { + mTracker.OnMouseLeftWindow(event); + SetCursor(wxCURSOR_SIZEWE); +} + +void WaterfallCanvas::mouseEnterWindow(wxMouseEvent& event) { + mTracker.OnMouseEnterWindow(event); + SetCursor(wxCURSOR_SIZEWE); +} diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 207c288..7447be4 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -7,6 +7,7 @@ #include #include "WaterfallContext.h" +#include "MouseTracker.h" #include "fftw3.h" #include "Timer.h" @@ -23,6 +24,13 @@ private: void OnIdle(wxIdleEvent &event); + void mouseMoved(wxMouseEvent& event); + void mouseDown(wxMouseEvent& event); + void mouseWheelMoved(wxMouseEvent& event); + void mouseReleased(wxMouseEvent& event); + void mouseEnterWindow(wxMouseEvent& event); + void mouseLeftWindow(wxMouseEvent& event); + wxWindow *parent; std::vector spectrum_points; @@ -39,6 +47,8 @@ private: WaterfallContext *glContext; Timer timer; float frameTimer; + MouseTracker mTracker; + // event table wxDECLARE_EVENT_TABLE(); }; diff --git a/src/visual/WaterfallContext.cpp b/src/visual/WaterfallContext.cpp index dddb87c..df10b1b 100644 --- a/src/visual/WaterfallContext.cpp +++ b/src/visual/WaterfallContext.cpp @@ -1,5 +1,6 @@ #include "WaterfallContext.h" #include "WaterfallCanvas.h" +#include "CubicSDR.h" WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext) : PrimaryGLContext(canvas, sharedContext) { @@ -36,11 +37,14 @@ WaterfallContext::WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedC glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, &(grad.getBlue())[0]); } -void WaterfallContext::Draw(std::vector &points) { +void WaterfallContext::BeginDraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); +} + +void WaterfallContext::Draw(std::vector &points) { if (points.size()) { memmove(waterfall_tex + FFT_SIZE, waterfall_tex, (NUM_WATERFALL_LINES - 1) * FFT_SIZE); @@ -57,15 +61,13 @@ void WaterfallContext::Draw(std::vector &points) { } } + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, waterfall); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, FFT_SIZE, NUM_WATERFALL_LINES, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); - glDisable(GL_TEXTURE_2D); - glColor3f(1.0, 1.0, 1.0); - glEnable(GL_TEXTURE_2D); - // glEnable(GL_COLOR_TABLE); glBindTexture(GL_TEXTURE_2D, waterfall); glBegin(GL_QUADS); glTexCoord2f(0.0, 1.0); @@ -78,6 +80,37 @@ void WaterfallContext::Draw(std::vector &points) { glVertex3f(-1.0, 1.0, 0.0); glEnd(); +} + +void WaterfallContext::DrawFreqSelector(float uxPos) { + DemodulatorInstance *demod = wxGetApp().getDemodTest(); + + if (!demod) { + return; + } + + glClear(GL_DEPTH_BUFFER_BIT); + glDisable(GL_TEXTURE_2D); + + glColor3f(1.0, 1.0, 1.0); + + glBegin(GL_LINES); + glVertex3f((uxPos - 0.5) * 2.0, 1.0, 0.0); + glVertex3f((uxPos - 0.5) * 2.0, -1.0, 0.0); + + float ofs = ((float) demod->params.inputResampleRate) / (float) SRATE; + + glVertex3f((uxPos - 0.5) * 2.0 - ofs, 1.0, 0.0); + glVertex3f((uxPos - 0.5) * 2.0 - ofs, -1.0, 0.0); + + glVertex3f((uxPos - 0.5) * 2.0 + ofs, 1.0, 0.0); + glVertex3f((uxPos - 0.5) * 2.0 + ofs, -1.0, 0.0); + + glEnd(); + +} + +void WaterfallContext::EndDraw() { glFlush(); CheckGLError(); diff --git a/src/visual/WaterfallContext.h b/src/visual/WaterfallContext.h index a488f6d..0b8d0bb 100644 --- a/src/visual/WaterfallContext.h +++ b/src/visual/WaterfallContext.h @@ -11,7 +11,10 @@ class WaterfallContext: public PrimaryGLContext { public: WaterfallContext(WaterfallCanvas *canvas, wxGLContext *sharedContext); + void BeginDraw(); void Draw(std::vector &points); + void DrawFreqSelector(float uxPos); + void EndDraw(); private: Gradient grad;