#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 2002, 2003\
The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD: wsmoused.c,v 1.29 2022/05/20 21:31:24 andvar Exp $");
#endif
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/tty.h>
#include <dev/wscons/wsconsio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <util.h>
#include "pathnames.h"
#include "wsmoused.h"
static struct mouse Mouse;
static char *Pid_File = NULL;
static int Foreground = 1;
static int X_Console = -1;
static int X_Console_Delay = 5;
#ifdef WSMOUSED_ACTION_MODE
extern struct mode_bootstrap Action_Mode;
#endif
#ifdef WSMOUSED_SELECTION_MODE
extern struct mode_bootstrap Selection_Mode;
#endif
#define MAX_MODES 2
static struct mode_bootstrap *Modes[MAX_MODES];
static struct mode_bootstrap *Avail_Modes[] = {
#ifdef WSMOUSED_ACTION_MODE
&Action_Mode,
#endif
#ifdef WSMOUSED_SELECTION_MODE
&Selection_Mode,
#endif
};
static void usage(void) __attribute__((__noreturn__));
static void open_device(unsigned int);
static void init_mouse(void);
static void event_loop(void);
static void generic_wscons_event(struct wscons_event);
static int attach_mode(const char *);
static void attach_modes(char *);
static void detach_mode(const char *);
static void detach_modes(void);
static void signal_terminate(int);
static int debug;
static void
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-d device] [-f config_file] [-m modes] [-n]\n",
getprogname());
exit(EXIT_FAILURE);
}
void
log_err(int e, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Foreground)
verr(e, fmt, ap);
else {
int olderrno = errno;
vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap);
errno = olderrno;
syslog(LOG_DAEMON | LOG_ERR, "%m");
exit(e);
}
va_end(ap);
}
void
log_errx(int e, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Foreground)
verrx(e, fmt, ap);
else {
vsyslog(LOG_DAEMON | LOG_ERR, fmt, ap);
exit(e);
}
va_end(ap);
}
void
log_info(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Foreground) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
} else
vsyslog(LOG_DAEMON | LOG_INFO, fmt, ap);
va_end(ap);
}
void
log_warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Foreground)
vwarn(fmt, ap);
else {
int olderrno = errno;
vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap);
errno = olderrno;
syslog(LOG_DAEMON | LOG_WARNING, "%m");
}
va_end(ap);
}
void
log_warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (Foreground)
vwarnx(fmt, ap);
else
vsyslog(LOG_DAEMON | LOG_WARNING, fmt, ap);
va_end(ap);
}
static void
init_mouse(void)
{
Mouse.m_devfd = -1;
open_device(0);
Mouse.m_fifofd = -1;
if (Mouse.m_fifoname != NULL) {
Mouse.m_fifofd = open(Mouse.m_fifoname,
O_RDWR | O_NONBLOCK, 0);
if (Mouse.m_fifofd == -1)
log_err(EXIT_FAILURE, "cannot open %s",
Mouse.m_fifoname);
}
}
static void
open_device(unsigned int secs)
{
int status;
if (Mouse.m_devfd != -1)
return;
sleep(secs);
Mouse.m_devfd = open(Mouse.m_devname, O_RDONLY | O_NONBLOCK, 0);
if (Mouse.m_devfd == -1)
log_err(EXIT_FAILURE, "cannot open %s", Mouse.m_devname);
const int version = WSMOUSE_EVENT_VERSION;
status = ioctl(Mouse.m_devfd, WSMOUSEIO_SETVERSION, &version);
if (status == -1)
log_err(EXIT_FAILURE, "cannot set version %s", Mouse.m_devname);
Mouse.m_doabs = 0;
unsigned int mouse_type = 0;
status = ioctl(Mouse.m_devfd, WSMOUSEIO_GTYPE, &mouse_type);
if (status == -1) {
log_warn("WSMOUSEIO_GTYPE");
return;
}
if (mouse_type != WSMOUSE_TYPE_TPANEL)
return;
status = ioctl(Mouse.m_devfd, WSMOUSEIO_GCALIBCOORDS, &Mouse.m_calib);
if (status == -1) {
log_warn("WSMOUSEIO_GCALIBCOORDS");
return;
}
Mouse.m_doabs = 1;
}
static void
event_loop(void)
{
int i, res;
struct pollfd fds[2];
struct wscons_event event;
fds[0].fd = Mouse.m_statfd;
fds[0].events = POLLIN;
for (;;) {
fds[1].fd = Mouse.m_devfd;
fds[1].events = POLLIN;
if (Mouse.m_disabled)
res = poll(fds, 1, INFTIM);
else
res = poll(fds, 2, 300);
if (res < 0)
log_warn("failed to read from devices");
if (fds[0].revents & POLLIN) {
res = read(Mouse.m_statfd, &event, sizeof(event));
if (debug)
(void)fprintf(stderr, "event [type=%u,value=%d,"
"time=[%lld,%ld]\n", event.type,
event.value, (long long)event.time.tv_sec,
(long)event.time.tv_nsec);
if (res != sizeof(event))
log_warn("failed to read from mouse stat");
for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
if (Modes[i]->mb_wscons_event != NULL)
Modes[i]->mb_wscons_event(event, 1);
generic_wscons_event(event);
for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
if (Modes[i]->mb_wscons_event != NULL)
Modes[i]->mb_wscons_event(event, 0);
} else if (fds[1].revents & POLLIN) {
res = read(Mouse.m_devfd, &event, sizeof(event));
if (res != sizeof(event))
log_warn("failed to read from mouse");
if (debug)
(void)fprintf(stderr, "event [type=%u,value=%d,"
"time=[%lld,%ld]\n", event.type,
event.value, (long long)event.time.tv_sec,
(long)event.time.tv_nsec);
if (Mouse.m_fifofd >= 0) {
res = write(Mouse.m_fifofd, &event,
sizeof(event));
if (res != sizeof(event))
log_warn("failed to write to fifo");
}
for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
if (Modes[i]->mb_wsmouse_event != NULL)
Modes[i]->mb_wsmouse_event(event);
} else {
for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
if (Modes[i]->mb_poll_timeout != NULL)
Modes[i]->mb_poll_timeout();
}
}
}
static void
generic_wscons_event(struct wscons_event evt)
{
switch (evt.type) {
case WSCONS_EVENT_SCREEN_SWITCH:
if (evt.value == X_Console) {
Mouse.m_disabled = 1;
(void)close(Mouse.m_devfd);
Mouse.m_devfd = -1;
} else {
if (Mouse.m_disabled) {
open_device(X_Console_Delay);
Mouse.m_disabled = 0;
} else {
(void)close(Mouse.m_devfd);
Mouse.m_devfd = -1;
open_device(0);
}
}
break;
}
}
static int
attach_mode(const char *name)
{
int i, pos;
struct mode_bootstrap *mb;
for (i = 0, pos = -1; i < MAX_MODES; i++)
if (Modes[i] == NULL) {
pos = i;
break;
}
if (pos == -1) {
log_warnx("modes table full; cannot register `%s'", name);
return 0;
}
for (i = 0; i < MAX_MODES; i++) {
mb = Avail_Modes[i];
if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
int res;
res = mb->mb_startup(&Mouse);
if (res == 0) {
log_warnx("startup failed for `%s' mode",
mb->mb_name);
return 0;
} else {
Modes[pos] = mb;
return 1;
}
}
}
log_warnx("unknown mode `%s' (see the `modes' directive)", name);
return 0;
}
static void
attach_modes(char *list)
{
char *last, *p;
int count;
(void)memset(&Modes, 0, sizeof(struct mode_bootstrap *) * MAX_MODES);
for (count = 0, (p = strtok_r(list, " ", &last)); p;
(p = strtok_r(NULL, " ", &last))) {
if (attach_mode(p))
count++;
}
if (count == 0)
log_errx(EXIT_FAILURE, "no active modes found; exiting...");
}
static void
detach_mode(const char *name)
{
int i;
struct mode_bootstrap *mb;
for (i = 0; i < MAX_MODES; i++) {
mb = Modes[i];
if (mb != NULL && strcmp(name, mb->mb_name) == 0) {
int res;
res = mb->mb_cleanup();
if (res == 0) {
log_warnx("cleanup failed for `%s' mode",
mb->mb_name);
return;
} else {
Modes[i] = NULL;
return;
}
}
}
log_warnx("unknown mode `%s' (see the `modes' directive)", name);
}
static void
detach_modes(void)
{
int i;
for (i = 0; i < MAX_MODES && Modes[i] != NULL; i++)
detach_mode(Modes[i]->mb_name);
}
static void
signal_terminate(int sig)
{
detach_modes();
config_free();
exit(EXIT_SUCCESS);
}
int
main(int argc, char **argv)
{
char *conffile, *modelist, *tstat;
int needconf, nodaemon, opt;
struct block *conf;
setprogname(argv[0]);
(void)memset(&Mouse, 0, sizeof(struct mouse));
conffile = _PATH_CONF;
modelist = NULL;
needconf = 0;
nodaemon = -1;
while ((opt = getopt(argc, argv, "Dd:f:m:n")) != -1) {
switch (opt) {
case 'D':
debug++;
break;
case 'd':
Mouse.m_devname = optarg;
break;
case 'f':
needconf = 1;
conffile = optarg;
break;
case 'm':
modelist = optarg;
break;
case 'n':
nodaemon = 1;
break;
default:
usage();
}
}
config_read(conffile, needconf);
conf = config_get_mode("Global");
if (nodaemon == -1)
nodaemon = block_get_propval_int(conf, "nodaemon", 0);
X_Console = block_get_propval_int(conf, "xconsole", -1);
X_Console_Delay = block_get_propval_int(conf, "xconsole_delay",
X_Console_Delay);
tstat = block_get_propval(conf, "ttystat", _PATH_TTYSTAT);
Mouse.m_statfd = open(tstat, O_RDONLY | O_NONBLOCK, 0);
if (Mouse.m_statfd == -1)
log_err(EXIT_FAILURE, "cannot open %s", tstat);
if (Mouse.m_devname == NULL)
Mouse.m_devname = block_get_propval(conf, "device",
_PATH_DEFAULT_MOUSE);
Mouse.m_fifoname = block_get_propval(conf, "fifo", NULL);
init_mouse();
if (modelist != NULL)
attach_modes(modelist);
else
attach_modes(block_get_propval(conf, "modes", "selection"));
(void)signal(SIGINT, signal_terminate);
(void)signal(SIGKILL, signal_terminate);
(void)signal(SIGQUIT, signal_terminate);
(void)signal(SIGTERM, signal_terminate);
if (!nodaemon) {
if (daemon(0, 0) == -1)
log_err(EXIT_FAILURE, "failed to become a daemon");
Pid_File = block_get_propval(conf, "pidfile", NULL);
if (pidfile(Pid_File) == -1)
log_warn("pidfile %s", Pid_File);
Foreground = 0;
}
event_loop();
return EXIT_SUCCESS;
}