added gtk+ client which listens for UDP packets from frameserver to display
colourful animationsmaster
parent
c286b459ba
commit
4be489ca09
|
@ -51,7 +51,7 @@ while (1):
|
|||
data += z_buffer
|
||||
for i in range(0,width):
|
||||
for j in range(0,height):
|
||||
pixel = fabs(sin(2*pi*(float(i)/width)+t*frequency)*sin(2*pi*(float(j)/height)+t*frequency))
|
||||
pixel = fabs(sin(2*pi*(float(i+1)/width)+t*frequency)*sin(2*pi*(float(j+1)/height)+t*frequency))
|
||||
data = data + chr(int(255*pixel)) + alpha
|
||||
data = data + "\n"
|
||||
for i in range(0,segwidth):
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
#include "AppWindow.h"
|
||||
|
||||
AppWindow::AppWindow() :
|
||||
hbox(),
|
||||
canvas()
|
||||
{
|
||||
hbox.pack_start(canvas);
|
||||
add(hbox);
|
||||
show_all();
|
||||
|
||||
canvas.signal_size_allocate().connect(
|
||||
sigc::mem_fun(canvas,&Renderer::on_resize_event));
|
||||
|
||||
resize(420,800);
|
||||
}
|
||||
|
||||
AppWindow::~AppWindow()
|
||||
{
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef APPWINDOW_H
|
||||
#define APPWINDOW_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
|
||||
#include "Renderer.h"
|
||||
|
||||
class AppWindow : public Gtk::Window
|
||||
{
|
||||
public:
|
||||
AppWindow();
|
||||
virtual ~AppWindow();
|
||||
|
||||
protected:
|
||||
Gtk::HBox hbox;
|
||||
|
||||
Renderer canvas;
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,249 @@
|
|||
|
||||
#include "Renderer.h"
|
||||
|
||||
|
||||
#include <iostream>
|
||||
|
||||
Renderer::Renderer()
|
||||
{
|
||||
Glib::signal_timeout().connect( sigc::mem_fun(*this, &Renderer::on_timeout), 34 );
|
||||
|
||||
#ifndef GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
|
||||
//Connect the signal handler if it isn't already a virtual method override:
|
||||
signal_expose_event().connect(sigc::mem_fun(*this, &Renderer::on_expose_event), false);
|
||||
#endif //GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED
|
||||
|
||||
thickness = (int)((double)get_width()*0.05);
|
||||
|
||||
fullscreen = true;
|
||||
invalid.xmin = get_width();
|
||||
invalid.ymin = get_height();
|
||||
invalid.width = invalid.height = 0;
|
||||
|
||||
Glib::Thread::create( sigc::mem_fun(this, &Renderer::listen), false);
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
{}
|
||||
|
||||
void Renderer::on_resize_event(Gtk::Allocation &allocation)
|
||||
{
|
||||
// thickness controls the area that is redrawn around a new point
|
||||
thickness = (int)((double)get_width()*0.05);
|
||||
|
||||
// if fullscreen redraw is enabled set redraw limits to drawing area size
|
||||
if( fullscreen )
|
||||
{
|
||||
invalid.xmin = 0;
|
||||
invalid.width = get_width();
|
||||
invalid.ymin = 0;
|
||||
invalid.height = get_height();
|
||||
}
|
||||
}
|
||||
|
||||
bool Renderer::on_expose_event(GdkEventExpose *event)
|
||||
{
|
||||
frame_t f;
|
||||
// read the frame in a thread-safe manner
|
||||
{
|
||||
Glib::Mutex::Lock lock(mutex_);
|
||||
f = frame;
|
||||
}
|
||||
Glib::RefPtr<Gdk::Window> window = get_window();
|
||||
|
||||
if(window)
|
||||
{
|
||||
//Gtk::Allocation allocation = get_allocation();
|
||||
width = get_width();
|
||||
height = get_height();
|
||||
Cairo::RefPtr<Cairo::Context> cr = window->create_cairo_context();
|
||||
cr->set_line_cap(Cairo::LINE_CAP_ROUND);
|
||||
cr->set_line_join(Cairo::LINE_JOIN_ROUND);
|
||||
|
||||
|
||||
if(event)
|
||||
{
|
||||
cr->rectangle(event->area.x, event->area.y,
|
||||
event->area.width, event->area.height);
|
||||
cr->clip();
|
||||
}
|
||||
|
||||
cr->scale(width,height);
|
||||
|
||||
cr->set_source_rgb(0.3,0.3,0.3);
|
||||
cr->paint();
|
||||
|
||||
cr->set_line_width(0.05);
|
||||
|
||||
double window_width = 0.04;
|
||||
double window_height = 0.08;
|
||||
double window_hsep = 0.03;
|
||||
double window_vsep = 0.04;
|
||||
|
||||
for(int i = 0; i < HEIGHT; i++)
|
||||
{
|
||||
for(int j = 0; j < WIDTH; j++)
|
||||
{
|
||||
for(int a = 0; a < CHANNELS; a++)
|
||||
{
|
||||
cr->set_source_rgb( (double)f.windows[i][j][0]/255, (double)f.windows[i][j][0]/255, (double)f.windows[i][j][0]/255 );
|
||||
cr->rectangle( window_hsep + j*(window_hsep+window_width) , window_vsep + i*(window_height+window_vsep), window_width, window_height );
|
||||
cr->fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int w = 0; w < SEGWIDTH; w++ )
|
||||
{
|
||||
for(int n = 0;n < SEGNUM; n++)
|
||||
{
|
||||
double r = (double)f.segments[w][n][0]/255;
|
||||
double b = (double)f.segments[w][n][1]/255;
|
||||
double g = (double)f.segments[w][n][2]/255;
|
||||
|
||||
switch(n)
|
||||
{
|
||||
case 0:
|
||||
draw_hsegment( window_hsep + w*(window_width+window_hsep), 0.88 , 0.01, window_width, r, g, b, cr);
|
||||
break;
|
||||
case 3:
|
||||
draw_hsegment( window_hsep + w*(window_width+window_hsep), 0.88 + window_height , 0.01, window_width, r, g, b, cr);
|
||||
break;
|
||||
case 6:
|
||||
draw_hsegment( window_hsep + w*(window_width+window_hsep), 0.88 + window_height/2, 0.01, window_width, r, g, b, cr);
|
||||
break;
|
||||
case 1:
|
||||
draw_vsegment( window_hsep + w*(window_width+window_hsep) + window_width, 0.88, 0.01, window_width, r,g,b,cr);
|
||||
break;
|
||||
case 2:
|
||||
draw_vsegment( window_hsep + w*(window_width+window_hsep) + window_width, 0.88 + window_width, 0.01, window_width, r,g,b,cr);
|
||||
break;
|
||||
case 4:
|
||||
draw_vsegment( window_hsep + w*(window_width+window_hsep), 0.88 + window_width, 0.01, window_width, r,g,b,cr);
|
||||
break;
|
||||
case 5:
|
||||
draw_vsegment( window_hsep + w*(window_width+window_hsep), 0.88, 0.01, window_width, r,g,b,cr);
|
||||
break;
|
||||
case 7:
|
||||
cr->arc( window_hsep + w*(window_width+window_hsep) + window_width + 0.005, 0.88 + window_height + 0.002, 0.003, 0 , 2*PI );
|
||||
cr->set_source_rgb(r,g,b);
|
||||
cr->fill();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// x, y, thickness, length, colours
|
||||
void Renderer::draw_hsegment(double x, double y, double t, double l, double r, double g, double b, Cairo::RefPtr<Cairo::Context> cr)
|
||||
{
|
||||
cr->move_to(x,y);
|
||||
cr->line_to(x+0.1*l,y+0.5*t);
|
||||
cr->line_to(x+0.9*l,y+0.5*t);
|
||||
cr->line_to(x+l,y);
|
||||
cr->line_to(x+l-0.1*l,y-0.5*t);
|
||||
cr->line_to(x+l-0.9*l,y-0.5*t);
|
||||
cr->close_path();
|
||||
cr->set_source_rgb(r,g,b);
|
||||
cr->fill();
|
||||
}
|
||||
|
||||
// x, y, thickness, length, colours
|
||||
void Renderer::draw_vsegment(double x, double y, double t, double l, double r, double g, double b, Cairo::RefPtr<Cairo::Context> cr)
|
||||
{
|
||||
cr->move_to(x,y);
|
||||
cr->line_to(x+0.5*t,y+0.1*l);
|
||||
cr->line_to(x+0.5*t,y+0.9*l);
|
||||
cr->line_to(x,y+l);
|
||||
cr->line_to(x-0.5*t,y+l-0.1*l);
|
||||
cr->line_to(x-0.5*t,y+l-0.9*l);
|
||||
cr->close_path();
|
||||
cr->set_source_rgb(r,g,b);
|
||||
cr->fill();
|
||||
}
|
||||
|
||||
bool Renderer::on_timeout()
|
||||
{
|
||||
/*static*/ Glib::RefPtr<Gdk::Window> win;
|
||||
win = get_window();
|
||||
if(win)
|
||||
{
|
||||
Gdk::Rectangle r(invalid.xmin,invalid.ymin,invalid.width,
|
||||
invalid.height);
|
||||
win->invalidate_rect(r,false);
|
||||
}
|
||||
|
||||
// if fullscreen redraw is DISABLED, reset redraw rectangle
|
||||
if( !fullscreen )
|
||||
{
|
||||
invalid.xmin = get_width();
|
||||
invalid.ymin = get_height();
|
||||
invalid.width = invalid.height = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// listen to udp packets on port 1234 which contain information about the frame
|
||||
void Renderer::listen()
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::asio::io_service io_service;
|
||||
udp::socket socket(io_service, udp::endpoint(udp::v4(), 1234));
|
||||
|
||||
while (1)
|
||||
{
|
||||
// creating the buffer each time is faster than zeroing it out
|
||||
boost::array<unsigned char, WIDTH*HEIGHT*CHANNELS + SEGWIDTH*SEGNUM*SEGCHANNELS> recv_buf;
|
||||
udp::endpoint remote_endpoint;
|
||||
boost::system::error_code error;
|
||||
|
||||
socket.receive_from(boost::asio::buffer(recv_buf),
|
||||
remote_endpoint, 0, error);
|
||||
|
||||
{
|
||||
Glib::Mutex::Lock lock(mutex_);
|
||||
for(int i = 0; i < HEIGHT; i++)
|
||||
{
|
||||
for(int j = 0; j < WIDTH; j++)
|
||||
{
|
||||
for(int a = 0; a < CHANNELS; a++)
|
||||
{
|
||||
frame.windows[i][j][a] = recv_buf[i*(CHANNELS*WIDTH) + j*CHANNELS + a];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int w = 0; w < SEGWIDTH; w++ )
|
||||
{
|
||||
for(int n = 0;n < SEGNUM; n++)
|
||||
{
|
||||
for(int a = 0; a < SEGCHANNELS; a++)
|
||||
{
|
||||
frame.segments[w][n][a] = recv_buf[WIDTH*HEIGHT*CHANNELS + w*(SEGCHANNELS*SEGNUM) + n*SEGCHANNELS + a];
|
||||
}
|
||||
}
|
||||
}
|
||||
} // lock is released here because the block ends
|
||||
|
||||
if (error && error != boost::asio::error::message_size)
|
||||
throw boost::system::system_error(error);
|
||||
|
||||
std::string message = "received";
|
||||
|
||||
boost::system::error_code ignored_error;
|
||||
// we can provide feedback to clients
|
||||
//socket.send_to(boost::asio::buffer(message),
|
||||
// remote_endpoint, 0, ignored_error);
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include <gtkmm.h>
|
||||
#include <cairomm/cairomm.h>
|
||||
#include <glibmm.h>
|
||||
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
using boost::asio::ip::udp;
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace std;
|
||||
|
||||
class Renderer : public Gtk::DrawingArea
|
||||
{
|
||||
public:
|
||||
Renderer();
|
||||
virtual ~Renderer();
|
||||
|
||||
void on_resize_event(Gtk::Allocation &allocation);
|
||||
|
||||
protected:
|
||||
|
||||
virtual bool on_expose_event(GdkEventExpose *event);
|
||||
bool on_timeout();
|
||||
|
||||
|
||||
private:
|
||||
void listen();
|
||||
|
||||
void draw_hsegment(double,double,double,double,double,double,double,Cairo::RefPtr<Cairo::Context>);
|
||||
void draw_vsegment(double,double,double,double,double,double,double,Cairo::RefPtr<Cairo::Context>);
|
||||
|
||||
Glib::Mutex mutex_;
|
||||
frame_t frame;
|
||||
|
||||
std::list<position> points;
|
||||
std::list<position>::iterator it_points_end;
|
||||
|
||||
bool fullscreen;
|
||||
int width,height;
|
||||
|
||||
area invalid;
|
||||
int thickness;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef DEFINES_H
|
||||
#define DEFINES_H
|
||||
|
||||
#include "../frameserver/defines.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define PI 3.1415
|
||||
|
||||
|
||||
|
||||
struct area
|
||||
{
|
||||
int xmin, width, ymin, height;
|
||||
};
|
||||
|
||||
enum operation
|
||||
{
|
||||
move,
|
||||
begin,
|
||||
end
|
||||
};
|
||||
|
||||
class position
|
||||
{
|
||||
public:
|
||||
position(void) {}
|
||||
~position(void) {}
|
||||
|
||||
position(double x_in, double y_in)
|
||||
{
|
||||
x = x_in;
|
||||
y = y_in;
|
||||
}
|
||||
|
||||
bool operator==(position pos)
|
||||
{
|
||||
if( fabs(pos.x - (*this).x) <= 0.01 && fabs(pos.y - (*this).y) <= 0.01 )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
bool operator!=(position pos)
|
||||
{
|
||||
if( fabs(pos.x - (*this).x) > 0.01 && fabs(pos.y - (*this).y) > 0.01 )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
position operator=(position pos)
|
||||
{
|
||||
(*this).x=pos.x;
|
||||
(*this).y=pos.y;
|
||||
(*this).op=pos.op;
|
||||
}
|
||||
|
||||
double x;
|
||||
double y;
|
||||
|
||||
operation op;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#include <gtkmm.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "defines.h"
|
||||
|
||||
#include "AppWindow.h"
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Glib::thread_init();
|
||||
std::cout << "init\n";
|
||||
Gtk::Main kit(&argc,&argv);
|
||||
std::cout << "kit\n";
|
||||
AppWindow myWindow;
|
||||
std::cout << "window\n";
|
||||
|
||||
// launch the udp listener thread in the window in an idle moment
|
||||
std::cout << "launch\n";
|
||||
Gtk::Main::run(myWindow);
|
||||
std::cout << "run\n";
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
g++ -O2 -march=pentium-m -pipe -fomit-frame-pointer -lboost_system `pkg-config --libs --cflags gdkmm-2.4` `pkg-config --libs --cflags gtkmm-2.4` `pkg-config --libs --cflags gthread-2.0` -o displayclient Renderer.cpp AppWindow.cpp main.cpp
|
|
@ -27,6 +27,7 @@ Server::~Server()
|
|||
void Server::launch_threads()
|
||||
{
|
||||
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::listen), false ) );
|
||||
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::send), false ) );
|
||||
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::mix), false ) );
|
||||
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::console), false) );
|
||||
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::expire), false) );
|
||||
|
@ -166,19 +167,25 @@ void Server::listen()
|
|||
}
|
||||
|
||||
/* this sends the frame content to a host expecting udp packets */
|
||||
void Server::send(frame_t _frame, udp::endpoint destination)
|
||||
void Server::send()
|
||||
{
|
||||
// write the frame into a char buffer, make it static so we don't waste time creating
|
||||
// it at every function call
|
||||
|
||||
const int length = WIDTH*HEIGHT*CHANNELS + SEGWIDTH*SEGNUM*SEGCHANNELS;
|
||||
static char data[length];
|
||||
|
||||
while(1)
|
||||
{
|
||||
{
|
||||
// lock the object for this operation
|
||||
Glib::Mutex::Lock lock(mutex_);
|
||||
// write the frame into a char buffer
|
||||
for(int i = 0; i < HEIGHT; i++)
|
||||
{
|
||||
for(int j = 0; j < WIDTH; j++)
|
||||
{
|
||||
for(int k = 0; k < CHANNELS; k++)
|
||||
{
|
||||
data[i*WIDTH*CHANNELS + CHANNELS*j + k] = _frame.windows[i][j][k];
|
||||
data[i*WIDTH*CHANNELS + CHANNELS*j + k] = frame.windows[i][j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +197,8 @@ void Server::send(frame_t _frame, udp::endpoint destination)
|
|||
for(int k = 0; k < SEGCHANNELS; k++)
|
||||
{
|
||||
data[WIDTH*HEIGHT*CHANNELS +
|
||||
i*SEGNUM*SEGCHANNELS + SEGCHANNELS*j + k] = _frame.segments[i][j][k];
|
||||
i*SEGNUM*SEGCHANNELS + SEGCHANNELS*j + k] = frame.segments[i][j][k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -199,14 +207,24 @@ void Server::send(frame_t _frame, udp::endpoint destination)
|
|||
{
|
||||
boost::asio::io_service io_service;
|
||||
|
||||
udp::socket socket(io_service, udp::endpoint(udp::v4(),0));
|
||||
/*UDP */
|
||||
boost::asio::ip::udp::endpoint u_remote_endpoint( boost::asio::ip::address_v4::from_string((char*)REMOTE_IP) , (short)REMOTE_PORT );
|
||||
udp::socket u_socket(io_service, udp::endpoint(udp::v4(),0));
|
||||
u_socket.send_to(boost::asio::buffer(data,length), u_remote_endpoint);
|
||||
|
||||
/* TCP */
|
||||
boost::asio::ip::tcp::endpoint t_remote_endpoint( boost::asio::ip::address_v4::from_string((char*)REMOTE_IP) , (short)REMOTE_PORT );
|
||||
tcp::socket t_socket(io_service);
|
||||
t_socket.connect(t_remote_endpoint);
|
||||
boost::asio::write(t_socket, boost::asio::buffer(data, length));
|
||||
|
||||
// std::cerr << destination << std::endl;
|
||||
socket.send_to(boost::asio::buffer(data,length), destination);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << e.what() << std::endl;
|
||||
//std::cerr << e.what() << std::endl;
|
||||
}
|
||||
// sleep until next update cycle
|
||||
usleep(25000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -309,18 +327,13 @@ void Server::mix()
|
|||
* (note, temp_frame is passed by value and will live only in the
|
||||
* argument of the "output" function and does not need to be locked) */
|
||||
output(temp_frame);
|
||||
usleep( 25000 );
|
||||
}
|
||||
}
|
||||
|
||||
// output to hardware using OLA
|
||||
void Server::output(frame_t _frame)
|
||||
{
|
||||
boost::asio::ip::udp::endpoint remote_endpoint( boost::asio::ip::address_v4::from_string(REMOTE_IP) , REMOTE_PORT );
|
||||
// send frame to a network host
|
||||
send(_frame,remote_endpoint);
|
||||
|
||||
// pretend we're doing something
|
||||
usleep( 25000 );
|
||||
}
|
||||
|
||||
void Server::console()
|
||||
|
|
|
@ -25,6 +25,7 @@ enum modes
|
|||
};
|
||||
|
||||
using boost::asio::ip::udp;
|
||||
using boost::asio::ip::tcp;
|
||||
using namespace std;
|
||||
|
||||
class Server : public sigc::trackable
|
||||
|
@ -51,7 +52,7 @@ private:
|
|||
void console_printclients();
|
||||
|
||||
void listen();
|
||||
void send(frame_t,udp::endpoint);
|
||||
void send();
|
||||
void mix();
|
||||
void output(frame_t);
|
||||
int get_size();
|
||||
|
|
|
@ -6,7 +6,7 @@ import time
|
|||
import random
|
||||
|
||||
# Set the socket parameters
|
||||
local_port = 5000 + int( sys.argv[1] )
|
||||
local_port = 6000 + int( sys.argv[1] )
|
||||
remote_port = 4321
|
||||
|
||||
# TODO: autodetect interface address for remote application
|
||||
|
@ -22,9 +22,11 @@ UDPSock.bind((outgoing_if, local_port))
|
|||
#UDPSock.connect((remote_host, remote_port))
|
||||
|
||||
display = open('display', 'r')
|
||||
|
||||
hash = "abcdefghij"
|
||||
z_buffer = chr(1) + "\n"
|
||||
|
||||
data = z_buffer + display.read()
|
||||
data = hash + z_buffer + display.read()
|
||||
|
||||
random.seed()
|
||||
|
||||
|
|
Loading…
Reference in New Issue