/*
* secde -- experimental lightweight Wayland/X11 server
* Copyright (C) 2019 Samuel Lidén Borell
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see .
*/
#include
#include
#include "evdev.h"
#include "fbdev.h"
#include "misc.h"
#include "protohnd.h"
struct FdInfo {
struct FdInfo *next;
int fd;
FdFlags flags;
FdProtocol protocol;
/* For readable/writeable fds (flags & FDF_READABLE) */
int write_rdy;
unsigned char *buffer, *largebuffer;
int bufflen, lbufflen;
/* For client sockets (flags & FDF_SOCKET) */
struct FdInfo *server;
};
/*#define INITIAL_FDINFO_HASHBITS 4*/
static struct {
int hashbits; /* num buckets = 1<<(hashbits-1) */
int count;
struct FdInfo **buckets;
} fdinfomap;
struct ProtoInfo {
int bufflen;
int maxlbuff;
};
#define BUFFERMIN
static const struct ProtoInfo protoinfo[] = {
/* UNSPEC */ { 0, 0 },
/* X11 */ { BUFFERMIN+1024, 128*1024 }, /* TODO what is the max packet size? */
/* PULSE */ { 0, 0 }, /* TODO max packet size for the other protocols */
/* WAYLAND */ { 0, 0 },
/* KMS */ { 0, 0 },
/* EVDEV */ { BUFFERMIN+48, 0 }, /* TODO size? seems to be either 32 or 48 bytes per packet... what about 64-bit platforms? */
/* ALSA */ { 0, 0 },
/* V4L2 */ { 0, 0 },
/* INOTIFY */ { BUFFERMIN+512, 0 } /* TODO max size? something+NAME_MAX... what about 64-bit platforms? */
};
void protohnd_initing(void)
{
map_init(fdinfomap);
}
struct FdInfo *protohnd_getfdinfo(int fd)
{
FdInfo *ret;
map_get(fdinfomap, fd, struct FdInfo, ret);
return ret;
}
int protohnd_getfd(struct FdInfo *info) {
return info->fd;
}
FdFlags protohnd_getfdflags(struct FdInfo *info) {
return info->flags;
}
struct FdInfo *protohnd_fdadded(struct FdInfo *server, int fd,
FdFlags flags, FdProtocol protocol
/*, cliantaddr, clientaddrsize*/)
{
struct FdInfo *info;
int serverfd = server ? server->fd : -1;
printf("fd added %d (server: %d)\n", fd, serverfd);
if (protocol == FDP_UNSPEC && server != NULL) {
protocol = server->protocol;
}
map_add(fdinfomap, fd, struct FdInfo, info);
if (!info) {
fprintf(stderr, "out of memory [map_add] (protocol #%d, "
"server %d, client %d)\n", protocol, serverfd, fd);
return NULL;
}
info->flags = flags;
info->protocol = protocol;
info->write_rdy = 0;
info->server = server;
info->buffer = NULL; /* lazy allocated */
info->bufflen = 0;
info->largebuffer = NULL;
info->lbufflen = 0;
switch (protocol) {
case FDP_EVDEV:
if (!input_evdev_init(info)) {
protohnd_closed(info);
return NULL;
}
break;
case FDP_FBDEV:
if (!display_fbdev_init(info)) {
protohnd_closed(info);
return NULL;
}
printf("testing display\n");
display_fbdev_test(info);
break;
case FDP_X11:
case FDP_PULSE:
case FDP_WAYLAND:
case FDP_KMS:
case FDP_ALSA:
case FDP_V4L2:
case FDP_INOTIFY:
case FDP_UNSPEC:
/* Do nothing */
break;
}
return info;
}
#if 0
/* TODO should inotify be implemented here in protohnd instead? */
int protohnd_fdexists(FdProtocol protocol, int devnum)
{
/* TODO */
}
#endif
void protohnd_inited(void)
{
}
void protohnd_willclose(struct FdInfo *info)
{
(void) info;
}
void protohnd_closed(struct FdInfo *info)
{
free(info->buffer);
map_remove(fdinfomap, info->fd, struct FdInfo);
/* TODO do something more? */
}
/**
* Allocates the packet buffer for an FdInfo
* \return 1 if successful, 0 if not.
*/
static int allocate_buffer(struct FdInfo *info)
{
const int protocol = info->protocol;
if (!protoinfo[protocol].bufflen) {
fprintf(stderr, "internal error - protocol #%d is not supposed to use buffering\n", protocol);
return 0;
}
info->buffer = malloc(protoinfo[protocol].bufflen);
if (!info->buffer) {
fprintf(stderr, "out of memory [buffer] (protocol #%d, "
"client %d)\n", protocol, info->fd);
return 0;
}
return 1;
}
static int process_received(struct FdInfo *info,
const unsigned char *buff,
int len)
{
switch (info->protocol) {
case FDP_EVDEV:
return input_evdev_handler(info, buff, len);
case FDP_X11:
/* for X11, /usr/include/X11/Xproto.h contains defs for the different packets.
for example, xConnClientPrefix which is what the client sends upon connection */
case FDP_PULSE:
case FDP_WAYLAND:
case FDP_KMS:
case FDP_ALSA:
case FDP_V4L2:
fprintf(stderr, "received data for unimplemented "
"protocol #%d\n", info->protocol);
return len;
case FDP_INOTIFY:
fprintf(stderr, "received data for protocol #%d was not "
"handled correctly.\n", info->protocol);
return len;
case FDP_UNSPEC:
case FDP_FBDEV: ;
/* Fall through */
}
fprintf(stderr, "Corrupted FdInfo. Unknown protocol #%d\n", info->protocol);
return len;
}
void protohnd_received(struct FdInfo *info, const unsigned char *buff, int numrecv)
{
if (!info->bufflen) {
/* Avoid copying to the buffer if there is no need */
int processed = process_received(info, buff, numrecv);
numrecv -= processed;
if (!numrecv) return;
buff += processed;
if (!info->buffer) {
if (!allocate_buffer(info)) return;
}
memcpy(info->buffer, buff, numrecv);
info->bufflen = numrecv;
} else {
int buffcap = protoinfo[info->protocol].bufflen;
int newlen, processed;
if (!info->buffer) {
if (!allocate_buffer(info)) return;
}
newlen = info->bufflen + numrecv;
if (newlen > buffcap) {
fprintf(stderr, "BUG: protocol #%d buffer would overflow, throwing away data\n", info->protocol);
info->bufflen = 0;
return;
}
memcpy(&info->buffer[info->bufflen], buff, numrecv);
processed = process_received(info, buff, numrecv);
info->bufflen -= processed;
memmove(info->buffer, &info->buffer[processed], processed);
}
}
void protohnd_writeready(struct FdInfo *info, int state)
{
info->write_rdy = state;
}
struct FdInfo *protohnd_getwrite(unsigned char **buff, int *length,
int *clientmore)
{
(void) buff;
(void) length;
(void) clientmore;
return NULL;
}
void protohnd_written(struct FdInfo *info, int numwritten,
int remaining, int result)
{
(void) info;
(void) numwritten;
(void) remaining;
(void) result;
}