window.c

/* 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);
}
* * o * *