blob: a166aaa18c21d361660edb44bb7be98bdfe872ba [file] [log] [blame]
#include "ball.h"
#include <guacamole/client.h>
#include <guacamole/layer.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/timestamp.h>
#include <guacamole/user.h>
#include <pthread.h>
#include <stdlib.h>
/* Client plugin arguments (empty) */
const char* TUTORIAL_ARGS[] = { NULL };
void* ball_render_thread(void* arg) {
/* Get data */
guac_client* client = (guac_client*) arg;
ball_client_data* data = (ball_client_data*) client->data;
/* Init time of last frame to current time */
guac_timestamp last_frame = guac_timestamp_current();
/* Update ball position as long as client is running */
while (client->state == GUAC_CLIENT_RUNNING) {
/* Default to 30ms frames */
int frame_duration = 30;
/* Lengthen frame duration if client is lagging */
int processing_lag = guac_client_get_processing_lag(client);
if (processing_lag > frame_duration)
frame_duration = processing_lag;
/* Sleep for duration of frame, then get timestamp */
usleep(frame_duration * 1000);
guac_timestamp current = guac_timestamp_current();
/* Calculate change in time */
int delta_t = current - last_frame;
/* Update position */
data->ball_x += data->ball_velocity_x * delta_t / 1000;
data->ball_y += data->ball_velocity_y * delta_t / 1000;
/* Bounce if necessary */
if (data->ball_x < 0) {
data->ball_x = -data->ball_x;
data->ball_velocity_x = -data->ball_velocity_x;
}
else if (data->ball_x >= 1024 - 128) {
data->ball_x = (2 * (1024 - 128)) - data->ball_x;
data->ball_velocity_x = -data->ball_velocity_x;
}
if (data->ball_y < 0) {
data->ball_y = -data->ball_y;
data->ball_velocity_y = -data->ball_velocity_y;
}
else if (data->ball_y >= 768 - 128) {
data->ball_y = (2 * (768 - 128)) - data->ball_y;
data->ball_velocity_y = -data->ball_velocity_y;
}
guac_protocol_send_move(client->socket, data->ball,
GUAC_DEFAULT_LAYER, data->ball_x, data->ball_y, 0);
/* End frame and flush socket */
guac_client_end_frame(client);
guac_socket_flush(client->socket);
/* Update timestamp */
last_frame = current;
}
return NULL;
}
int ball_join_handler(guac_user* user, int argc, char** argv) {
/* Get client associated with user */
guac_client* client = user->client;
/* Get ball layer from client data */
ball_client_data* data = (ball_client_data*) client->data;
guac_layer* ball = data->ball;
/* Get user-specific socket */
guac_socket* socket = user->socket;
/* Send the display size */
guac_protocol_send_size(socket, GUAC_DEFAULT_LAYER, 1024, 768);
/* Create background tile */
guac_layer* texture = guac_client_alloc_buffer(client);
guac_protocol_send_rect(socket, texture, 0, 0, 64, 64);
guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
0x88, 0x88, 0x88, 0xFF);
guac_protocol_send_rect(socket, texture, 0, 0, 32, 32);
guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
0xDD, 0xDD, 0xDD, 0xFF);
guac_protocol_send_rect(socket, texture, 32, 32, 32, 32);
guac_protocol_send_cfill(socket, GUAC_COMP_OVER, texture,
0xDD, 0xDD, 0xDD, 0xFF);
/* Prepare a curve which covers the entire layer */
guac_protocol_send_rect(socket, GUAC_DEFAULT_LAYER,
0, 0, 1024, 768);
/* Fill curve with texture */
guac_protocol_send_lfill(socket,
GUAC_COMP_OVER, GUAC_DEFAULT_LAYER,
texture);
/* Set up ball layer */
guac_protocol_send_size(socket, ball, 128, 128);
/* Prepare a circular curve */
guac_protocol_send_arc(socket, data->ball,
64, 64, 62, 0, 6.28, 0);
guac_protocol_send_close(socket, data->ball);
/* Draw a 4-pixel black border */
guac_protocol_send_cstroke(socket,
GUAC_COMP_OVER, data->ball,
GUAC_LINE_CAP_ROUND, GUAC_LINE_JOIN_ROUND, 4,
0x00, 0x00, 0x00, 0xFF);
/* Fill the circle with color */
guac_protocol_send_cfill(socket,
GUAC_COMP_OVER, data->ball,
0x00, 0x80, 0x80, 0x80);
/* Free texture (no longer needed) */
guac_client_free_buffer(client, texture);
/* Mark end-of-frame */
guac_protocol_send_sync(socket, client->last_sent_timestamp);
/* Flush buffer */
guac_socket_flush(socket);
/* User successfully initialized */
return 0;
}
int ball_free_handler(guac_client* client) {
ball_client_data* data = (ball_client_data*) client->data;
/* Wait for render thread to terminate */
pthread_join(data->render_thread, NULL);
/* Free client-level ball layer */
guac_client_free_layer(client, data->ball);
/* Free client-specific data */
free(data);
/* Data successfully freed */
return 0;
}
int guac_client_init(guac_client* client) {
/* Allocate storage for client-specific data */
ball_client_data* data = malloc(sizeof(ball_client_data));
/* Set up client data and handlers */
client->data = data;
/* Allocate layer at the client level */
data->ball = guac_client_alloc_layer(client);
/* Start ball at upper left */
data->ball_x = 0;
data->ball_y = 0;
/* Move at a reasonable pace to the lower right */
data->ball_velocity_x = 200; /* pixels per second */
data->ball_velocity_y = 200; /* pixels per second */
/* Start render thread */
pthread_create(&data->render_thread, NULL, ball_render_thread, client);
/* This example does not implement any arguments */
client->args = TUTORIAL_ARGS;
/* Client-level handlers */
client->join_handler = ball_join_handler;
client->free_handler = ball_free_handler;
return 0;
}