/* This is free software. See LICENSE for terms.
* Copyright 2004 - 2015, Patricia Kirk.
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/mount.h>
#include <netdb.h>
#include <sys/stat.h>
#include <math.h>
#if defined (__linux__)
#include <sys/time.h>
#include <signal.h>
#endif
#include "proto.h"
extern struct global g;
extern struct pixstack *iconpix, *mainpix;
extern char **args;
Pixmap icondraw, iconmask, maindraw, mainmask;
Display *maindisplay=0;
Window rootwindow, iconwindow, mainwindow;
Atom wmdeletewindow, wmprotocols;
GC icongc, maingc;
int mainscreen, colordepth;
#if WITH_REMEXEC || WITH_TIMESYNC
/* Check the execute and timesync sockets for
* pending requests for service. execute is
* a call to launch a salmon process, timesync
* is a request for the current time if this
* machine is the server.
*/
void checkSocket() {
struct sockaddr_in sockaddr;
socklen_t socklen=sizeof(struct sockaddr);
int sock=-1;
char buff[BUFPLUS], buffer[BUFPLUS];
#if WITH_TIMESYNC
struct timeval tv;
fd_set ready;
const char *request="syncreq";
if (g.timeserver && g.timesock > 0) {
/* If this host is the time server, hostname == timehost,
* respond to requests for the time with the current time
* of day as "tv_sec tv_usec".
*/
FD_ZERO(&ready);
FD_SET(g.timesock, &ready);
tv.tv_sec = tv.tv_usec = 0;
select(g.timesock + 1, &ready, 0, 0, &tv);
if (FD_ISSET(g.timesock, &ready) &&
(sock = accept(g.timesock, (struct sockaddr *)&sockaddr, &socklen)) > 0) {
getnameinfo((struct sockaddr *)&sockaddr, socklen, buff, sizeof(buff), NULL, 0, 0);
if (g.timelog) {
snprintf(buffer, sizeof(buffer), "time sync request from %s", buff);
logMessage(buffer);
}
if (strstr(buff, g.network)) {
bzero(&buffer, sizeof(buffer));
read(sock, buffer, sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
if (strlen(buffer) && strstr(buffer, request) == buffer) {
if (!(gettimeofday(&tv, NULL)))
snprintf(buffer, sizeof(buffer), "%d %06d",
(int)tv.tv_sec, (int)tv.tv_usec);
else
bzero(&buffer, sizeof(buffer));
send(sock, buffer, strlen(buffer), 256);
}
}
shutdown(sock, SHUT_RDWR);
close(sock);
}
}
#endif /* WITH_TIMESYNC */
#if WITH_REMEXEC
if (g.execsock > 0) {
/* Respond to requests to launch new salmon processes
* on localhost. These may be displayed locally or
* on a remote host. Restrict responses to the LAN.
*/
if ((sock = accept(g.execsock, (struct sockaddr *)&sockaddr, &socklen)) > 0) {
getnameinfo((struct sockaddr *)&sockaddr, socklen, buff, sizeof(buff), NULL, 0, 0);
if (g.execlog) {
snprintf(buffer, sizeof(buffer), "remote execute request from %s", buff);
logMessage(buffer);
}
bzero(&buffer, sizeof(buffer));
if (strstr(buff, g.network)) {
read(sock, buffer, sizeof(buffer));
buffer[sizeof(buffer) - 1] = '\0';
}
shutdown(sock, SHUT_RDWR);
close(sock);
if (strlen(buffer) && !strncmp(buffer, "salmon ", 7)) {
execComm(buffer);
if (g.execstop)
gdayMate(NULL, NULL);
}
}
sock = -1;
listen(g.execsock, 1);
}
#endif /* WITH_REMEXEC */
#if WITH_CONTROL
if (g.ctlport)
checkCtl();
#endif /* WITH_CONTROL */
}
#endif /* WITH_REMEXEC || WITH_TIMESYNC */
/* This is only called when salmon
* is sent to an inactive display.
*/
void checkFunc() {
static int count=0;
if (g.daemonize) {
/* In some OS/X11 cases this routine is a memory leak that
* can become serious after a few hours. If the process
* is a child, exiting after five minutes launches a new
* child and keeps the leak to a minimum.
*/
count++;
if (count >= 300)
gdayMate(NULL, NULL);
}
#if WITH_REMEXEC || WITH_TIMESYNC
/* Check for execute and syncreq calls. */
checkSocket();
if (g.timeport && !g.timeserver)
checkSync();
#endif /* WITH_REMEXEC || WITH_TIMESYNC */
#if WITH_CONTROL
if (g.ctlport)
checkCtl();
#endif /* WITH_CONTROL */
}
void initWindow(int argc, char **argv) {
XGCValues icongcv, maingcv;
XSizeHints sizehints;
XTextProperty title;
XClassHint classhint;
XWMHints wmhints;
Pixel backpix, forepix;
int position, x, y;
#if defined(__OpenBSD__)
/* What? */
int w, z;
#else
size_t w, z;
#endif /* __OpenBSD__ */
char *smalbuf=NULL, *name=NULL, *cp;
struct itimerval itv;
bzero(&itv, sizeof(itv));
itv.it_interval.tv_sec = itv.it_value.tv_sec = 1;
setitimer(ITIMER_REAL, &itv, NULL);
signal(SIGALRM, checkFunc);
while (!(maindisplay = XOpenDisplay(g.display)))
sleep(2);
bzero(&itv, sizeof(itv));
setitimer(ITIMER_REAL, &itv, NULL);
mainscreen = DefaultScreen(maindisplay);
backpix = BlackPixel(maindisplay, mainscreen);
forepix = WhitePixel(maindisplay, mainscreen);
colordepth = DefaultDepth(maindisplay, mainscreen);
rootwindow = RootWindow(maindisplay, mainscreen);
x = DisplayHeight(maindisplay, mainscreen);
g.isize = ISIZE;
for (;;) {
g.msize = g.isize * g.xmag;
if (g.panel)
g.msize *= 2;
if (g.msize > x)
g.xmag--;
else
break;
}
g.psize = g.msize;
if (g.panel)
g.psize /= 2;
if (g.pixmap)
scaleGraphic(g.pixmap, TRUE);
bzero(&sizehints, sizeof(sizehints));
sizehints.flags = USSize;
CALLOC(smalbuf, SMALBUF, sizeof(char), "initWindow");
snprintf(smalbuf, SMALBUF, "%dx%d", g.isize, g.isize);
XWMGeometry(maindisplay, mainscreen, smalbuf, NULL, 0, &sizehints, &sizehints.x,
&sizehints.y, &sizehints.width, &sizehints.height, &sizehints.win_gravity);
sizehints.min_width = sizehints.max_width = sizehints.width;
sizehints.min_height = sizehints.max_height = sizehints.height;
sizehints.flags |= PMinSize | PMaxSize | PWinGravity;
iconmask = XCreatePixmap(maindisplay, rootwindow, sizehints.width, sizehints.height, colordepth);
icondraw = XCreatePixmap(maindisplay, rootwindow, sizehints.width, sizehints.height, colordepth);
iconwindow = XCreateSimpleWindow(maindisplay, rootwindow, sizehints.x, sizehints.y,
sizehints.width, sizehints.height, 0, forepix, backpix);
XSetWMNormalHints(maindisplay, iconwindow, &sizehints);
wmhints.icon_window = iconwindow;
if (g.withdrawn) {
wmhints.window_group = iconwindow;
wmhints.flags |= WindowGroupHint;
}
if (g.iconic || g.withdrawn) {
wmhints.icon_x = sizehints.x;
wmhints.icon_y = sizehints.y;
wmhints.flags |= IconPositionHint;
}
bzero(&sizehints, sizeof(sizehints));
sizehints.flags = USSize;
position = XParseGeometry(g.position, &x, &y, &w, &z);
if (position & XValue && position & YValue)
sizehints.flags |= USPosition;
w = z = g.msize;
if (position & XNegative && position & YNegative)
snprintf(smalbuf, SMALBUF, "%dx%d-%d-%d", w, z, -x, -y);
else if (position & XNegative)
snprintf(smalbuf, SMALBUF, "%dx%d-%d+%d", w, z, -x, y);
else if (position & YNegative)
snprintf(smalbuf, SMALBUF, "%dx%d+%d-%d", w, z, x, -y);
else
snprintf(smalbuf, SMALBUF, "%dx%d+%d+%d", w, z, x, y);
XWMGeometry(maindisplay, mainscreen, smalbuf, NULL, 0, &sizehints, &sizehints.x,
&sizehints.y, &sizehints.width, &sizehints.height, &sizehints.win_gravity);
FREE(smalbuf);
sizehints.min_width = sizehints.max_width = sizehints.width;
sizehints.min_height = sizehints.max_height = sizehints.height;
sizehints.flags |= PMinSize | PMaxSize | PWinGravity;
mainmask = XCreatePixmap(maindisplay, rootwindow, sizehints.width, sizehints.height, colordepth);
maindraw = XCreatePixmap(maindisplay, rootwindow, sizehints.width, sizehints.height, colordepth);
mainwindow = XCreateSimpleWindow(maindisplay, rootwindow, sizehints.x, sizehints.y,
sizehints.width, sizehints.height, 0, forepix, backpix);
XSetWMNormalHints(maindisplay, mainwindow, &sizehints);
if (g.pixmap == 1) {
XSetWindowBackgroundPixmap(maindisplay, iconwindow, ParentRelative);
XSetWindowBackgroundPixmap(maindisplay, mainwindow, ParentRelative);
} else {
XSetWindowBackgroundPixmap(maindisplay, iconwindow, iconpix->pixmap);
XSetWindowBackgroundPixmap(maindisplay, mainwindow, mainpix->pixmap);
}
if (g.appname) {
XStringListToTextProperty(&g.appname, 1, &title);
} else {
/* Set the name only if not specified and only for X. */
cp = (strrchr(argv[0], SLASH)) ? strrchr(argv[0], SLASH) + 1 : argv[0];
STRDUP(name, cp);
name[0] = toupper((int)name[0]);
XStringListToTextProperty(&name, 1, &title);
FREE(name);
}
XSetWMName(maindisplay, iconwindow, &title);
XSetWMName(maindisplay, mainwindow, &title);
classhint.res_name = (char *)title.value;
classhint.res_class = (char *)title.value;
XSetClassHint(maindisplay, mainwindow, &classhint);
XStoreName(maindisplay, mainwindow, (char *)title.value);
XSetIconName(maindisplay, mainwindow, (char *)title.value);
XSelectInput(maindisplay, iconwindow, ExposureMask);
XSelectInput(maindisplay, mainwindow, ExposureMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
icongcv.background = backpix;
icongcv.foreground = forepix;
icongcv.graphics_exposures = False;
icongc = XCreateGC(maindisplay, iconwindow, GCForeground | GCBackground, &icongcv);
maingcv.background = backpix;
maingcv.foreground = forepix;
maingcv.graphics_exposures = False;
maingc = XCreateGC(maindisplay, mainwindow, GCForeground | GCBackground, &maingcv);
XSetCommand(maindisplay, mainwindow, argv, argc);
wmdeletewindow = XInternAtom(maindisplay, "WM_DELETE_WINDOW", False);
wmprotocols = XInternAtom(maindisplay, "WM_PROTOCOLS", False);
XSetWMProtocols(maindisplay, iconwindow, &wmdeletewindow, 1);
XSetWMProtocols(maindisplay, mainwindow, &wmdeletewindow, 1);
wmhints.flags = StateHint | IconWindowHint;
wmhints.initial_state = g.withdrawn ? WithdrawnState : g.iconic ? IconicState : NormalState;
XSetWMHints(maindisplay, mainwindow, &wmhints);
XMapWindow(maindisplay, mainwindow);
}