added gtk+ client which listens for UDP packets from frameserver to display

colourful animations
master
Bartek Kostrzewa 2011-05-12 16:10:27 +02:00
parent c286b459ba
commit 4be489ca09
11 changed files with 486 additions and 39 deletions

View File

@ -51,7 +51,7 @@ while (1):
data += z_buffer data += z_buffer
for i in range(0,width): for i in range(0,width):
for j in range(0,height): 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 + chr(int(255*pixel)) + alpha
data = data + "\n" data = data + "\n"
for i in range(0,segwidth): for i in range(0,segwidth):

View File

@ -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()
{
}

23
displayclient/AppWindow.h Normal file
View File

@ -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

249
displayclient/Renderer.cpp Normal file
View File

@ -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;
}
}

50
displayclient/Renderer.h Normal file
View File

@ -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

65
displayclient/defines.h Normal file
View File

@ -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

23
displayclient/main.cpp Normal file
View File

@ -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;
}

View File

@ -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

View File

@ -27,6 +27,7 @@ Server::~Server()
void Server::launch_threads() 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::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::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::console), false) );
threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::expire), false) ); threads.push_back( Glib::Thread::create( sigc::mem_fun(this, &Server::expire), false) );
@ -166,47 +167,64 @@ void Server::listen()
} }
/* this sends the frame content to a host expecting udp packets */ /* 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; const int length = WIDTH*HEIGHT*CHANNELS + SEGWIDTH*SEGNUM*SEGCHANNELS;
static char data[length]; static char data[length];
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];
}
}
}
for(int i = 0; i < SEGWIDTH; i++) while(1)
{ {
for(int j = 0; j < SEGNUM; j++)
{ {
for(int k = 0; k < SEGCHANNELS; k++) // 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++)
{ {
data[WIDTH*HEIGHT*CHANNELS + for(int j = 0; j < WIDTH; j++)
i*SEGNUM*SEGCHANNELS + SEGCHANNELS*j + k] = _frame.segments[i][j][k]; {
for(int k = 0; k < CHANNELS; k++)
{
data[i*WIDTH*CHANNELS + CHANNELS*j + k] = frame.windows[i][j][k];
}
}
}
for(int i = 0; i < SEGWIDTH; i++)
{
for(int j = 0; j < SEGNUM; j++)
{
for(int k = 0; k < SEGCHANNELS; k++)
{
data[WIDTH*HEIGHT*CHANNELS +
i*SEGNUM*SEGCHANNELS + SEGCHANNELS*j + k] = frame.segments[i][j][k];
}
}
} }
} }
}
try try
{ {
boost::asio::io_service io_service; 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);
// std::cerr << destination << std::endl; /* TCP */
socket.send_to(boost::asio::buffer(data,length), destination); 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);
catch (std::exception& e) t_socket.connect(t_remote_endpoint);
{ boost::asio::write(t_socket, boost::asio::buffer(data, length));
std::cerr << e.what() << std::endl;
}
catch (std::exception& e)
{
//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 * (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) */ * argument of the "output" function and does not need to be locked) */
output(temp_frame); output(temp_frame);
usleep( 25000 );
} }
} }
// output to hardware using OLA // output to hardware using OLA
void Server::output(frame_t _frame) 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() void Server::console()

View File

@ -25,6 +25,7 @@ enum modes
}; };
using boost::asio::ip::udp; using boost::asio::ip::udp;
using boost::asio::ip::tcp;
using namespace std; using namespace std;
class Server : public sigc::trackable class Server : public sigc::trackable
@ -51,7 +52,7 @@ private:
void console_printclients(); void console_printclients();
void listen(); void listen();
void send(frame_t,udp::endpoint); void send();
void mix(); void mix();
void output(frame_t); void output(frame_t);
int get_size(); int get_size();

View File

@ -6,7 +6,7 @@ import time
import random import random
# Set the socket parameters # Set the socket parameters
local_port = 5000 + int( sys.argv[1] ) local_port = 6000 + int( sys.argv[1] )
remote_port = 4321 remote_port = 4321
# TODO: autodetect interface address for remote application # TODO: autodetect interface address for remote application
@ -22,9 +22,11 @@ UDPSock.bind((outgoing_if, local_port))
#UDPSock.connect((remote_host, remote_port)) #UDPSock.connect((remote_host, remote_port))
display = open('display', 'r') display = open('display', 'r')
hash = "abcdefghij"
z_buffer = chr(1) + "\n" z_buffer = chr(1) + "\n"
data = z_buffer + display.read() data = hash + z_buffer + display.read()
random.seed() random.seed()