Import Upstream version 1.9.0
This commit is contained in:
commit
a60b7c6315
79 files changed
+51673
No files matched your search
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
This file is part of g15daemon.
|
||||
|
||||
g15daemon is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
g15daemon is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with g15daemon; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
(c) 2006 Mike Lampard, Philip Lawatsch, and others
|
||||
|
||||
$Revision: 244 $ - $Date: 2007-01-05 08:04:05 +1030 (Fri, 05 Jan 2007) $ $Author: mlampard $
|
||||
|
||||
This daemon listens on localhost port 15550 for client connections,
|
||||
and arbitrates LCD display. Allows for multiple simultaneous clients.
|
||||
Client screens can be cycled through by pressing the 'L1' key.
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <poll.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <libg15.h>
|
||||
#include <g15daemon.h>
|
||||
|
||||
static int leaving;
|
||||
int server_events(plugin_event_t *myevent);
|
||||
|
||||
/* tcp server defines */
|
||||
#define LISTEN_PORT 15550
|
||||
#define LISTEN_ADDR "127.0.0.1"
|
||||
|
||||
/* any more than this number of simultaneous clients will be rejected. */
|
||||
#define MAX_CLIENTS 10
|
||||
|
||||
/* custom plugininfo for clients... */
|
||||
plugin_info_t lcdclient_info[] = {
|
||||
/* TYPE, name, initfunc, updatefreq, exitfunc, eventhandler, initfunc */
|
||||
{G15_PLUGIN_LCD_SERVER, "LCDclient" , NULL, 500, NULL, (void*)server_events, NULL},
|
||||
{G15_PLUGIN_NONE, "" , NULL, 0, NULL, NULL, NULL}
|
||||
};
|
||||
|
||||
static void process_client_cmds(lcdnode_t *lcdnode, int sock, unsigned int *msgbuf, unsigned int len)
|
||||
{
|
||||
|
||||
switch(msgbuf[0]){
|
||||
case CLIENT_CMD_SWITCH_PRIORITIES: {
|
||||
g15daemon_send_event(lcdnode,G15_EVENT_REQ_PRIORITY,1);
|
||||
break;
|
||||
}
|
||||
case CLIENT_CMD_IS_FOREGROUND: { /* client wants to know if it's currently viewable */
|
||||
pthread_mutex_lock(&lcdlist_mutex);
|
||||
memset(msgbuf,0,2);
|
||||
if(lcdnode->list->current == lcdnode){
|
||||
msgbuf[0] = '1';
|
||||
}else{
|
||||
msgbuf[0] = '0';
|
||||
}
|
||||
pthread_mutex_unlock(&lcdlist_mutex);
|
||||
send(sock,msgbuf,1,MSG_OOB);
|
||||
break;
|
||||
}
|
||||
case CLIENT_CMD_IS_USER_SELECTED: { /* client wants to know if it was set to foreground by the user */
|
||||
pthread_mutex_lock(&lcdlist_mutex);
|
||||
if(lcdnode->lcd->usr_foreground) /* user manually selected this lcd */
|
||||
msgbuf[0] = '1';
|
||||
else
|
||||
msgbuf[0] = '0';
|
||||
pthread_mutex_unlock(&lcdlist_mutex);
|
||||
send(sock,msgbuf,1,0);
|
||||
break;
|
||||
}
|
||||
case CLIENT_CMD_BACKLIGHT: { /* client wants to change the LCD backlight */
|
||||
/* return current state to the client then implement the new state */
|
||||
send(sock,&lcdnode->lcd->backlight_state,1,MSG_OOB);
|
||||
lcdnode->lcd->backlight_state = msgbuf[0]-0x80;
|
||||
lcdnode->lcd->state_changed = 1;
|
||||
break;
|
||||
}
|
||||
case CLIENT_CMD_KB_BACKLIGHT: { /* client wants to change the KB backlight */
|
||||
setKBBrightness((unsigned int)msgbuf[0]-0x80);
|
||||
break;
|
||||
}
|
||||
case CLIENT_CMD_CONTRAST: { /* client wants to change the LCD contrast */
|
||||
/* send current state to the client */
|
||||
send(sock,&lcdnode->lcd->contrast_state,1,MSG_OOB);
|
||||
lcdnode->lcd->contrast_state = msgbuf[0]-0x40;
|
||||
lcdnode->lcd->state_changed = 1;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
if(msgbuf[0] & CLIENT_CMD_MKEY_LIGHTS)
|
||||
{ /* client wants to change the M-key backlights */
|
||||
lcdnode->lcd->mkey_state = msgbuf[0]-0x20;
|
||||
lcdnode->lcd->state_changed = 1;
|
||||
//if the client is the keyhandler, allow full, direct control over the mled status
|
||||
if(lcdnode->lcd->masterlist->remote_keyhandler_sock==lcdnode->lcd->connection)
|
||||
setLEDs(msgbuf[0]-0x20);
|
||||
} else if (msgbuf[0] & CLIENT_CMD_KEY_HANDLER)
|
||||
{
|
||||
g15daemon_log(LOG_WARNING, "Client is taking over keystate");
|
||||
|
||||
lcdnode->list->remote_keyhandler_sock = sock;
|
||||
g15daemon_log(LOG_WARNING, "Client has taken over keystate");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* create and open a socket for listening */
|
||||
int init_sockserver(){
|
||||
int listening_socket;
|
||||
int yes=1;
|
||||
int tos = 0x18;
|
||||
|
||||
struct sockaddr_in servaddr;
|
||||
|
||||
if ((listening_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {
|
||||
g15daemon_log(LOG_WARNING, "Unable to create socket.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsockopt(listening_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
|
||||
setsockopt(listening_socket, SOL_SOCKET, SO_PRIORITY, &tos, sizeof(tos));
|
||||
|
||||
memset(&servaddr, 0, sizeof(servaddr));
|
||||
servaddr.sin_family = AF_INET;
|
||||
inet_aton (LISTEN_ADDR, &servaddr.sin_addr);
|
||||
servaddr.sin_port = htons(LISTEN_PORT);
|
||||
|
||||
if (bind(listening_socket, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
|
||||
g15daemon_log(LOG_WARNING, "error calling bind()\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(listening_socket, MAX_CLIENTS) < 0 ) {
|
||||
g15daemon_log(LOG_WARNING, "error calling listen()\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return listening_socket;
|
||||
}
|
||||
|
||||
|
||||
int g15_send(int sock, char *buf, unsigned int len)
|
||||
{
|
||||
int total = 0;
|
||||
int retval = 0;
|
||||
int bytesleft = len;
|
||||
struct pollfd pfd[1];
|
||||
|
||||
while(total < len && !leaving) {
|
||||
memset(pfd,0,sizeof(pfd));
|
||||
pfd[0].fd = sock;
|
||||
pfd[0].events = POLLOUT | POLLERR | POLLHUP | POLLNVAL;
|
||||
if(poll(pfd,1,500)>0) {
|
||||
if(pfd[0].revents & POLLOUT && !(pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP || pfd[0].revents & POLLNVAL)) {
|
||||
retval = send(sock, buf+total, bytesleft, 0);
|
||||
if (retval == -1) {
|
||||
break;
|
||||
}
|
||||
bytesleft -= retval;
|
||||
total += retval;
|
||||
}
|
||||
if((pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP || pfd[0].revents & POLLNVAL)){
|
||||
retval=-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval==-1?-1:0;
|
||||
}
|
||||
|
||||
|
||||
int g15_recv(lcdnode_t *lcdnode, int sock, char *buf, unsigned int len)
|
||||
{
|
||||
int total = 0;
|
||||
int retval = 0;
|
||||
int msgret = 0;
|
||||
int bytesleft = len;
|
||||
struct pollfd pfd[1];
|
||||
unsigned int msgbuf[20];
|
||||
|
||||
while(total < len && !leaving) {
|
||||
memset(pfd,0,sizeof(pfd));
|
||||
pfd[0].fd = sock;
|
||||
pfd[0].events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
|
||||
if(poll(pfd,1,500)>0){
|
||||
if(pfd[0].revents & POLLPRI && !(pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP || pfd[0].revents & POLLNVAL)) { /* receive out-of-band request from client and deal with it */
|
||||
memset(msgbuf,0,20);
|
||||
msgret = recv(sock, msgbuf, 10 , MSG_OOB);
|
||||
if (msgret < 1) {
|
||||
break;
|
||||
}
|
||||
process_client_cmds(lcdnode, sock, msgbuf,len);
|
||||
}
|
||||
else if(pfd[0].revents & POLLIN && !(pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP || pfd[0].revents & POLLNVAL)) {
|
||||
|
||||
retval = recv(sock, buf+total, bytesleft, 0);
|
||||
if (retval < 1) {
|
||||
break;
|
||||
}
|
||||
total += retval;
|
||||
bytesleft -= retval;
|
||||
}
|
||||
if((pfd[0].revents & POLLERR || pfd[0].revents & POLLHUP || pfd[0].revents & POLLNVAL)){
|
||||
retval=-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
|
||||
/* the client must send 6880 bytes for each lcd screen. This thread will continue to copy data
|
||||
* into the clients LCD buffer for as long as the connection remains open.
|
||||
* so, the client should open a socket, check to ensure that the server is a g15daemon,
|
||||
* and send multiple 6880 byte packets (1 for each screen update)
|
||||
* once the client disconnects by closing the socket, the LCD buffer is
|
||||
* removed and will no longer be displayed.
|
||||
*/
|
||||
void *lcd_client_thread(void *display) {
|
||||
|
||||
lcdnode_t *g15node = display;
|
||||
lcd_t *client_lcd = g15node->lcd;
|
||||
int retval;
|
||||
unsigned int width, height, buflen,header=4;
|
||||
|
||||
int client_sock = client_lcd->connection;
|
||||
char helo[]=SERV_HELO;
|
||||
unsigned char *tmpbuf=g15daemon_xmalloc(6880);
|
||||
|
||||
if(g15_send(client_sock, (char*)helo, strlen(SERV_HELO))<0){
|
||||
goto exitthread;
|
||||
}
|
||||
/* check for requested buffer type.. we only handle pixel buffers atm */
|
||||
if(g15_recv(g15node, client_sock,(char*)tmpbuf,4)<4)
|
||||
goto exitthread;
|
||||
|
||||
/* we will in the future handle txt buffers gracefully but for now we just hangup */
|
||||
if(tmpbuf[0]=='G') {
|
||||
while(!leaving) {
|
||||
retval = g15_recv(g15node, client_sock,(char *)tmpbuf,6880);
|
||||
if(retval!=6880){
|
||||
break;
|
||||
}
|
||||
pthread_mutex_lock(&lcdlist_mutex);
|
||||
memset(client_lcd->buf,0,1024);
|
||||
g15daemon_convert_buf(client_lcd,tmpbuf);
|
||||
client_lcd->ident = random();
|
||||
pthread_mutex_unlock(&lcdlist_mutex);
|
||||
}
|
||||
}
|
||||
else if (tmpbuf[0]=='R') { /* libg15render buffer */
|
||||
while(!leaving) {
|
||||
retval = g15_recv(g15node, client_sock, (char *)tmpbuf, 1048);
|
||||
if(retval != 1048) {
|
||||
break;
|
||||
}
|
||||
pthread_mutex_lock(&lcdlist_mutex);
|
||||
memcpy(client_lcd->buf,tmpbuf,sizeof(client_lcd->buf));
|
||||
client_lcd->ident = random();
|
||||
pthread_mutex_unlock(&lcdlist_mutex);
|
||||
}
|
||||
}
|
||||
else if (tmpbuf[0]=='W'){ /* wbmp buffer - we assume (stupidly) that it's 160 pixels wide */
|
||||
while(!leaving) {
|
||||
retval = g15_recv(g15node, client_sock,(char*)tmpbuf, 865);
|
||||
if(!retval)
|
||||
break;
|
||||
|
||||
if (tmpbuf[2] & 1) {
|
||||
width = ((unsigned char)tmpbuf[2] ^ 1) | (unsigned char)tmpbuf[3];
|
||||
height = tmpbuf[4];
|
||||
header = 5;
|
||||
} else {
|
||||
width = tmpbuf[2];
|
||||
height = tmpbuf[3];
|
||||
header = 4;
|
||||
}
|
||||
|
||||
buflen = (width/8)*height;
|
||||
|
||||
if(buflen>860){ /* grab the remainder of the image and discard excess bytes */
|
||||
/* retval=g15_recv(client_sock,(char*)tmpbuf+865,buflen-860); */
|
||||
retval=g15_recv(g15node, client_sock,NULL,buflen-860);
|
||||
buflen = 860;
|
||||
}
|
||||
|
||||
if(width!=160) /* FIXME - we ought to scale images I suppose */
|
||||
goto exitthread;
|
||||
|
||||
pthread_mutex_lock(&lcdlist_mutex);
|
||||
memcpy(client_lcd->buf,tmpbuf+header,buflen+header);
|
||||
client_lcd->ident = random();
|
||||
pthread_mutex_unlock(&lcdlist_mutex);
|
||||
}
|
||||
}
|
||||
exitthread:
|
||||
if(client_lcd->masterlist->remote_keyhandler_sock==client_sock)
|
||||
client_lcd->masterlist->remote_keyhandler_sock=0;
|
||||
close(client_sock);
|
||||
free(tmpbuf);
|
||||
g15daemon_lcdnode_remove(display);
|
||||
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
/* poll the listening socket for connections, spawning new threads as needed to handle clients */
|
||||
int g15_clientconnect (g15daemon_t **g15daemon, int listening_socket) {
|
||||
|
||||
int conn_s;
|
||||
struct pollfd pfd[1];
|
||||
pthread_t client_connection;
|
||||
pthread_attr_t attr;
|
||||
lcdnode_t *clientnode;
|
||||
|
||||
memset(pfd,0,sizeof(pfd));
|
||||
pfd[0].fd = listening_socket;
|
||||
pfd[0].events = POLLIN;
|
||||
|
||||
if (poll(pfd,1,500)>0){
|
||||
if (!(pfd[0].revents & POLLIN)){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ( (conn_s = accept(listening_socket, NULL, NULL) ) < 0 ) {
|
||||
if(errno==EWOULDBLOCK || errno==EAGAIN){
|
||||
}else{
|
||||
g15daemon_log(LOG_WARNING, "error calling accept()\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
clientnode = g15daemon_lcdnode_add(g15daemon);
|
||||
clientnode->lcd->connection = conn_s;
|
||||
/* override the default (generic handler and use our own for our clients */
|
||||
clientnode->lcd->g15plugin->info=(void*)(&lcdclient_info);
|
||||
|
||||
memset(&attr,0,sizeof(pthread_attr_t));
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setstacksize(&attr,256*1024); /* set stack to 768k - dont need 8Mb - this is probably rather excessive also */
|
||||
if (pthread_create(&client_connection, &attr, lcd_client_thread, clientnode) != 0) {
|
||||
g15daemon_log(LOG_WARNING,"Unable to create client thread.");
|
||||
if (close(conn_s) < 0 ) {
|
||||
g15daemon_log(LOG_WARNING, "error calling close()\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
pthread_detach(client_connection);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this thread only listens for new connections.
|
||||
* sockserver_accept will spawn a new thread for each connected client
|
||||
*/
|
||||
static void lcdserver_thread(void *lcdlist){
|
||||
|
||||
g15daemon_t *masterlist = (g15daemon_t*) lcdlist ;
|
||||
int g15_socket=-1;
|
||||
|
||||
if((g15_socket = init_sockserver())<0){
|
||||
g15daemon_log(LOG_ERR,"Unable to initialise the server at port %i",LISTEN_PORT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fcntl(g15_socket, F_SETFL, O_NONBLOCK) <0 ) {
|
||||
g15daemon_log(LOG_ERR,"Unable to set socket to nonblocking");
|
||||
}
|
||||
|
||||
while ( !leaving ) {
|
||||
g15_clientconnect(&masterlist,g15_socket);
|
||||
}
|
||||
|
||||
close(g15_socket);
|
||||
return;
|
||||
}
|
||||
|
||||
/* incoming events */
|
||||
int server_events(plugin_event_t *event) {
|
||||
lcd_t *lcd = (lcd_t*) event->lcd;
|
||||
|
||||
switch (event->event)
|
||||
{
|
||||
case G15_EVENT_KEYPRESS:{
|
||||
if(lcd->connection && lcd->masterlist->remote_keyhandler_sock!=lcd->connection) { /* server client */
|
||||
if((send(lcd->connection,(void *)&event->value,sizeof(event->value),0))<0)
|
||||
g15daemon_log(LOG_WARNING,"Error in send: %s\n",strerror(errno));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case G15_EVENT_VISIBILITY_CHANGED:
|
||||
break;
|
||||
case G15_EVENT_USER_FOREGROUND:
|
||||
lcd->usr_foreground = event->value;
|
||||
break;
|
||||
case G15_EVENT_MLED:
|
||||
case G15_EVENT_BACKLIGHT:
|
||||
case G15_EVENT_CONTRAST:
|
||||
/* should never receive these */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return G15_PLUGIN_OK;
|
||||
}
|
||||
|
||||
|
||||
/* if no exitfunc or eventhandler, member should be NULL */
|
||||
plugin_info_t g15plugin_info[] = {
|
||||
/* TYPE, name, initfunc, updatefreq, exitfunc, eventhandler, initfunc */
|
||||
{G15_PLUGIN_LCD_SERVER, "LCDServer" , (void*)lcdserver_thread, 500, NULL, (void*)server_events, NULL},
|
||||
{G15_PLUGIN_NONE, "" , NULL, 0, NULL, NULL, NULL}
|
||||
};
|
||||
Reference in new issue
Block a user