From 4be489ca09073370ead2d6c4479512b4b3f6c2e2 Mon Sep 17 00:00:00 2001 From: Bartek Kostrzewa Date: Thu, 12 May 2011 16:10:27 +0200 Subject: [PATCH] added gtk+ client which listens for UDP packets from frameserver to display colourful animations --- clients/plasma.py | 2 +- displayclient/AppWindow.cpp | 19 +++ displayclient/AppWindow.h | 23 +++ displayclient/Renderer.cpp | 249 +++++++++++++++++++++++++++++++ displayclient/Renderer.h | 50 +++++++ displayclient/defines.h | 65 ++++++++ displayclient/main.cpp | 23 +++ displayclient/make_displayclient | 2 + frameserver/Server.cpp | 83 ++++++----- frameserver/Server.h | 3 +- frameserver/servertest.py | 6 +- 11 files changed, 486 insertions(+), 39 deletions(-) create mode 100644 displayclient/AppWindow.cpp create mode 100644 displayclient/AppWindow.h create mode 100644 displayclient/Renderer.cpp create mode 100644 displayclient/Renderer.h create mode 100644 displayclient/defines.h create mode 100644 displayclient/main.cpp create mode 100755 displayclient/make_displayclient diff --git a/clients/plasma.py b/clients/plasma.py index 8bd9b25..f6747c2 100644 --- a/clients/plasma.py +++ b/clients/plasma.py @@ -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): diff --git a/displayclient/AppWindow.cpp b/displayclient/AppWindow.cpp new file mode 100644 index 0000000..964b0ad --- /dev/null +++ b/displayclient/AppWindow.cpp @@ -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() +{ +} diff --git a/displayclient/AppWindow.h b/displayclient/AppWindow.h new file mode 100644 index 0000000..55904d1 --- /dev/null +++ b/displayclient/AppWindow.h @@ -0,0 +1,23 @@ +#ifndef APPWINDOW_H +#define APPWINDOW_H + +#include + +#include "Renderer.h" + +class AppWindow : public Gtk::Window +{ + public: + AppWindow(); + virtual ~AppWindow(); + + protected: + Gtk::HBox hbox; + + Renderer canvas; + + private: + +}; + +#endif diff --git a/displayclient/Renderer.cpp b/displayclient/Renderer.cpp new file mode 100644 index 0000000..b4414d7 --- /dev/null +++ b/displayclient/Renderer.cpp @@ -0,0 +1,249 @@ + +#include "Renderer.h" + + +#include + +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 window = get_window(); + + if(window) + { + //Gtk::Allocation allocation = get_allocation(); + width = get_width(); + height = get_height(); + Cairo::RefPtr 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 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 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 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 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; + } +} diff --git a/displayclient/Renderer.h b/displayclient/Renderer.h new file mode 100644 index 0000000..13d8a17 --- /dev/null +++ b/displayclient/Renderer.h @@ -0,0 +1,50 @@ +#ifndef RENDERER_H +#define RENDERER_H + +#include +#include +#include + +#include +#include + +#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); + void draw_vsegment(double,double,double,double,double,double,double,Cairo::RefPtr); + + Glib::Mutex mutex_; + frame_t frame; + + std::list points; + std::list::iterator it_points_end; + + bool fullscreen; + int width,height; + + area invalid; + int thickness; +}; + +#endif diff --git a/displayclient/defines.h b/displayclient/defines.h new file mode 100644 index 0000000..07f5677 --- /dev/null +++ b/displayclient/defines.h @@ -0,0 +1,65 @@ +#ifndef DEFINES_H +#define DEFINES_H + +#include "../frameserver/defines.h" + +#include + +#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 diff --git a/displayclient/main.cpp b/displayclient/main.cpp new file mode 100644 index 0000000..f5b0c75 --- /dev/null +++ b/displayclient/main.cpp @@ -0,0 +1,23 @@ +#include +#include + +#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; +} diff --git a/displayclient/make_displayclient b/displayclient/make_displayclient new file mode 100755 index 0000000..f92c265 --- /dev/null +++ b/displayclient/make_displayclient @@ -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 diff --git a/frameserver/Server.cpp b/frameserver/Server.cpp index 1b098dc..ed5dcb9 100644 --- a/frameserver/Server.cpp +++ b/frameserver/Server.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,47 +167,64 @@ 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]; - 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 + - i*SEGNUM*SEGCHANNELS + SEGCHANNELS*j + k] = _frame.segments[i][j][k]; + 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++) + { + 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 - { - boost::asio::io_service io_service; + try + { + 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; - socket.send_to(boost::asio::buffer(data,length), destination); - } - catch (std::exception& e) - { - std::cerr << e.what() << std::endl; + /* 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)); + + } + 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 * 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() diff --git a/frameserver/Server.h b/frameserver/Server.h index 5e2aaec..4dbeb06 100644 --- a/frameserver/Server.h +++ b/frameserver/Server.h @@ -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(); diff --git a/frameserver/servertest.py b/frameserver/servertest.py index e6d869b..802a8ca 100644 --- a/frameserver/servertest.py +++ b/frameserver/servertest.py @@ -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()