Skip to content
Snippets Groups Projects
Commit acf3dc5f authored by Armin Co's avatar Armin Co
Browse files

Removed GTK dependecies from the qpong_lib.

Moved gtk dependcies into another dir.
parent 6fd67bf9
No related branches found
No related tags found
No related merge requests found
# get all GTKMM dependencies and configuration
pkg_check_modules(GTKMM gtkmm-3.0)
link_directories(${GTKMM_LIBRARY_DIRS})
include_directories(${GTKMM_INCLUDE_DIRS})
## Create the application based on GTK
##
add_executable(qpong.app
main.cpp
QWindow.cpp
QDrawingArea.cpp
ParticleImage.cpp
)
# we do need some threads
find_package(Threads)
# link all necessary libs to the target
target_link_libraries(qpong.app
qpong
${CMAKE_THREAD_LIBS_INIT}
${GTKMM_LIBRARIES}
fftw3
fftw3_omp
m # math
)
///
/// @file ParticleImage.cpp
/// @author Armin Co
///
#include "ParticleImage.hpp"
#include "Common.hpp"
#include "Profiler.hpp"
constexpr int bytesPerPixel = 3;
constexpr int bitsPerSample = 8;
ParticleImage::ParticleImage()
: ImageBuffer()
{
m_pixbuf = new uChar[QPong::arraySize * bytesPerPixel];
}
Glib::RefPtr<Gdk::Pixbuf> ParticleImage::getPixbuf()
{
for (unsigned i = 0; i < QPong::arraySize * bytesPerPixel;)
{
QColor c = getValue(i/bytesPerPixel);
m_pixbuf[i++] = static_cast<uChar>(c.r * 255);
m_pixbuf[i++] = static_cast<uChar>(c.g * 255);
m_pixbuf[i++] = static_cast<uChar>(c.b * 255);
}
Glib::RefPtr<Gdk::Pixbuf> pb = Gdk::Pixbuf::create_from_data(m_pixbuf, Gdk::Colorspace::COLORSPACE_RGB, false, bitsPerSample, QPong::arrayWidth, QPong::arrayHeight, QPong::arrayWidth * bytesPerPixel);
pb = pb->scale_simple(768, 768, Gdk::InterpType::INTERP_NEAREST);
return pb;
}
\ No newline at end of file
///
/// @file ParticleImage.hpp
/// @author Armin Co
///
/// @brief Class to wrap one dimensional array.
///
#ifndef QPONG_PARTICLE_IMAGE_HPP
#define QPONG_PARTICLE_IMAGE_HPP
#include <gtkmm.h>
#include "ImageBuffer.hpp"
/// @brief Wraps an one dimensional array.
/// Values can be accessed as in 2D.
///
class ParticleImage : public ImageBuffer
{
public:
ParticleImage();
Glib::RefPtr<Gdk::Pixbuf> getPixbuf();
private:
using uChar = unsigned char;
uChar* m_pixbuf;
};
#endif
\ No newline at end of file
///
/// @file QDrawingArea.cpp
/// @author Armin Co
///
#include "QDrawingArea.hpp"
QDrawingArea::QDrawingArea()
{
m_dispatcher.connect(sigc::mem_fun(*this, &QDrawingArea::queue_draw));
}
void QDrawingArea::redraw()
{
m_dispatcher.emit();
}
void QDrawingArea::setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf)
{
m_image = pixbuf;
}
bool QDrawingArea::on_draw(const Cairo::RefPtr<Cairo::Context> &cr)
{
cr->save();
if (!m_image)
{
return true;
}
Gdk::Cairo::set_source_pixbuf(cr, m_image, 0, 0);
cr->paint();
cr->restore();
return true;
}
///
/// @file QDrawingArea.hpp
/// @author Armin Co
///
/// @brief QDrawingArea is a class which can show a Gdk::Pixbuf.
///
#ifndef QPONG_DRAWING_AREA_HPP
#define QPONG_DRAWING_AREA_HPP
#include <gtkmm.h>
#include <gdkmm.h>
/// @brief QDrawingArea is derived from Gtk::DrawingArea.
/// An object of this class stores a reference to Gdk::Pixbuf which can be shown and updated.
///
class QDrawingArea : public Gtk::DrawingArea
{
public:
QDrawingArea();
virtual ~QDrawingArea(){};
/// @brief Request a redraw of the drawing area.
///
void redraw();
/// @brief Set a new pixbuf reference.
///
void setPixbuf(Glib::RefPtr<Gdk::Pixbuf> pixbuf);
protected:
/// @brief Signal handler which is called when a redraw of the drawing area is requested.
///
bool on_draw(const Cairo::RefPtr<Cairo::Context> &cr) override;
private:
Glib::RefPtr<Gdk::Pixbuf> m_image; ///< Reference a pixbuf.
Glib::Dispatcher m_dispatcher; ///< Dispatcher is used to trigger thread safe redraws.
};
#endif
\ No newline at end of file
#include <iostream>
#include <iomanip>
#include <chrono>
#include "QWindow.hpp"
#include "Profiler.hpp"
bool propagating = true;
int propRate = 0;
int renderRate = 0;
bool qPressed = false;
bool aPressed = false;
bool upPressed = false;
bool downPressed = false;
/// @brief This functino has to run in its own thread and propagates the particle as fast as possible.
///
void QWindow::simulation()
{
do
{
auto start = std::chrono::system_clock::now();
m_particle->propagate();
propRate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start).count();
if (qPressed)
m_players[Player::One]->move(Direction::Up);
if (aPressed)
m_players[Player::One]->move(Direction::Down);
if (upPressed)
m_players[Player::Two]->move(Direction::Up);
if (downPressed)
m_players[Player::Two]->move(Direction::Down);
// Limit the speed of the simulation.
auto dt = std::chrono::system_clock::now() - start;
auto dtMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dt).count();
constexpr int propTime = 6;
if (dtMillis < propTime)
{
std::this_thread::sleep_for(std::chrono::milliseconds(propTime - dtMillis));
}
}
while (propagating);
}
/// @brief This function has to run in its own thread an will redraw the canvas every ~25ms.
///
void render(QWindow *w)
{
do
{
using namespace std::chrono_literals;
auto start = std::chrono::system_clock::now();
std::this_thread::sleep_for(20ms);
w->updateView();
renderRate = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - start).count();
} while (true);
}
QWindow::QWindow()
: m_button{"Initialise Particle"}
, m_label("Hi, you are playing QPong and this is a label!")
, m_scorePlayerOne("0")
, m_scorePlayerTwo("0")
{
set_border_width(5);
constexpr double playerOneStartX = -0.7;
constexpr double playerTwoStartX = 0.7;
m_players.insert(std::make_pair<Player, std::shared_ptr<QPlayer>>(Player::One, std::make_shared<QPlayer>(Player::One, Position{-0.7, 0})));
m_players.insert(std::make_pair<Player, std::shared_ptr<QPlayer>>(Player::Two, std::make_shared<QPlayer>(Player::Two, Position{ 0.7, 0.4})));
m_momentum = new ParticleImage;
m_position = new ParticleImage;
m_particle = new Particle(m_momentum, m_position, m_players);
add(m_box);
m_box.pack_start(m_vBox);
m_vBox.pack_start(m_button, Gtk::PACK_SHRINK);
m_vBox.pack_start(m_label, Gtk::PACK_SHRINK);
m_fpsBox.pack_start(m_renderRate, Gtk::PACK_EXPAND_WIDGET);
m_fpsBox.pack_start(m_proppergatingRate, Gtk::PACK_EXPAND_WIDGET);
m_vBox.pack_start(m_fpsBox, Gtk::PACK_SHRINK);
m_scoreBox.pack_start(m_scorePlayerOne, Gtk::PACK_EXPAND_WIDGET);
m_scoreBox.pack_start(m_scorePlayerTwo, Gtk::PACK_EXPAND_WIDGET);
m_vBox.pack_start(m_scoreBox, Gtk::PACK_SHRINK);
m_hBox.pack_start(m_positionArea, Gtk::PACK_EXPAND_WIDGET);
m_hBox.pack_start(m_momentumArea, Gtk::PACK_EXPAND_WIDGET);
m_vBox.pack_start(m_hBox);
m_label.set_margin_top(5);
m_label.set_margin_bottom(5);
m_proppergatingRate.set_text("0");
m_renderRate.set_text("0");
m_renderRate.set_margin_top(5);
m_renderRate.set_margin_bottom(5);
m_proppergatingRate.set_margin_top(5);
m_proppergatingRate.set_margin_bottom(5);
m_button.signal_clicked().connect(sigc::mem_fun(*this, &QWindow::onButtonPress));
this->signal_key_press_event().connect( sigc::mem_fun( *this, &QWindow::onKeyPress));
this->signal_key_release_event().connect(sigc::mem_fun(*this, &QWindow::onKeyPress));
m_guiDispatcher.connect(sigc::mem_fun(*this, &QWindow::updateGui));
m_areaDispatcher.connect(sigc::mem_fun(*this, &QWindow::updateImage));
while (m_particle->notReady())
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
m_propagatingThread = std::thread(&QWindow::simulation, this);
m_propagatingThread.detach();
std::thread rendering(render, this);
rendering.detach();
m_box.show_all();
}
unsigned guiUpdates = 0;
void QWindow::updateGui()
{
guiUpdates++;
if (guiUpdates % 10 == 0)
{
std::stringstream ss;
ss << "image update rate: ";
ss << 1000 / renderRate;
m_renderRate.set_text(ss.str());
ss = std::stringstream();
ss << "simulation rate: ";
ss << 1000 / propRate;
m_proppergatingRate.set_text(ss.str());
ss = std::stringstream();
ss << "Score Player 1: ";
auto score = m_players[Player::One]->getScore();
ss << std::setprecision(2) << std::setw(7) << score << "%";
if (score > 50.0)
{
ss << std::endl << "GEWONNEN!";
ss << std::endl << "Zum Neustarten Partikel neu initialisieren";
}
m_scorePlayerOne.set_text(ss.str());
ss = std::stringstream();
ss << "Score Player 2: ";
score = m_players[Player::Two]->getScore();
ss << std::setprecision(2) << std::setw(7) << score << "%";
if (score > 50.0)
{
ss << std::endl << "GEWONNEN!";
ss << std::endl << "Zum Neustarten Partikel neu initialisieren";
}
m_scorePlayerTwo.set_text(ss.str());
}
}
bool QWindow::onKeyPress(GdkEventKey* event)
{
constexpr int q = 113;
constexpr int a = 97;
constexpr int ue = 252;
constexpr int oe = 246;
if (event->type == GdkEventType::GDK_KEY_PRESS)
{
if (event->keyval == q)
qPressed = true;
if (event->keyval == a)
aPressed = true;
if (event->keyval == ue)
upPressed = true;
if (event->keyval == oe)
downPressed = true;
}
else if (event->type == GdkEventType::GDK_KEY_RELEASE)
{
if (event->keyval == q)
qPressed = false;
if (event->keyval == a)
aPressed = false;
if (event->keyval == ue)
upPressed = false;
if (event->keyval == oe)
downPressed = false;
}
return true;
}
void QWindow::updateView()
{
m_areaDispatcher.emit();
m_guiDispatcher.emit();
}
void QWindow::updateImage()
{
m_positionArea.setPixbuf(m_position->getPixbuf());
m_momentumArea.setPixbuf(m_momentum->getPixbuf());
m_positionArea.redraw();
m_momentumArea.redraw();
}
void QWindow::onButtonPress()
{
propagating = false;
std::this_thread::sleep_for((std::chrono::seconds)1);
m_particle->initMomentum();
for (auto player : m_players)
{
player.second->reset();
}
m_propagatingThread = std::thread(&QWindow::simulation, this);
propagating = true;
m_propagatingThread.detach();
}
\ No newline at end of file
///
/// @file QWindow.hpp
/// @author Armin Co
///
#ifndef Q_WINDOW_HPP
#define Q_WINDOW_HPP
#include <thread>
#include <map>
#include <memory>
#include <gtkmm.h>
#include "Particle.hpp"
#include "ParticleImage.hpp"
#include "QDrawingArea.hpp"
/// @brief Definition of the QPong window
///
class QWindow : public Gtk::Window
{
public:
/// @brief Constructor of the window
///
QWindow();
/// @brief Virtual destructor of the window.
///
virtual ~QWindow(){}
/// @brief Sends a signal to GTK to update drawing areas.
void updateView();
protected:
/// @brief signal handlers
///
void onButtonPress();
bool onKeyPress(GdkEventKey* event);
private:
/// @brief Updates the QDrawingArea for position and momentum.
///
void updateImage();
/// @brief Update all labels from the gui.
///
void updateGui();
/// @brief Loop throug iteratins of the simulation
///
void simulation();
Gtk::Box m_box;
Gtk::VBox m_vBox;
Gtk::HBox m_hBox;
Gtk::Box m_fpsBox;
Gtk::Label m_label;
Gtk::Button m_button;
Gtk::Label m_renderRate;
Gtk::Label m_proppergatingRate;
Gtk::Box m_scoreBox;
Gtk::Label m_scorePlayerOne;
Gtk::Label m_scorePlayerTwo;
Glib::Dispatcher m_guiDispatcher;
Glib::Dispatcher m_areaDispatcher;
QDrawingArea m_positionArea;
QDrawingArea m_momentumArea;
Particle *m_particle;
std::thread m_propagatingThread;
ParticleImage *m_momentum;
ParticleImage *m_position;
ParticleImage *m_obstacle;
std::map<Player, std::shared_ptr<QPlayer>> m_players;
};
#endif
\ No newline at end of file
///
/// @file main.cpp
/// @author Armin Co
///
/// @brief The QPong game.
///
#include <iostream>
#include <gtkmm.h>
#include "QWindow.hpp"
/// @brief MAIN
///
int main(int argc, char* argv[])
{
std::cout << "QPong -- starting" << std::endl;
auto app = Gtk::Application::create(argc, argv, "de.armin-co.qpong");
QWindow window;
window.set_default_size(1920, 1080);
return app->run(window);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment