checkevents.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 <errno.h>
#include <sys/stat.h>

#if defined (__linux__)
#define __USE_XOPEN
#include <time.h>
#undef __USE_XOPEN
#endif

#include "proto.h"

extern struct global g;
extern struct data fresh;
extern struct lineopts *linelist;

extern Display *maindisplay;
extern Atom wmdeletewindow, wmprotocols;

char smalbuf[SMALBUF];

struct keyval {
    int	key;
    int	value;
};

int keyCheck(int key) {
    struct keyval keys[] = {
    	/* Esc */
	{9,	 99},
	/* Top row */
	{10,	  1},	{11,	  2},	{12,	  3},
	{13,	  4},	{14,	  5},	{15,	  6},
	{16,	  7},	{17,	  8},	{18,	  9},
	{19,	  0},
	/* Back space, Enter, Space */
	{22,	 22},	{36,	 36},	{65,	 65},
	/* Key pad */
	{79,	  7},	{80,	  8},	{81,	  9},
	{83,	  4},	{84,	  5},	{85,	  6},
	{87,	  1},	{88,	  2},	{89,	  3},
	{90,	  0},
	/* Up, Left, Right */
	{98,	 98},	{100,	100},	{102,	102},
	/* Return */
	{108,	 36},
	{0,	 -1}
    };
    int i=0;

    for (;; i++) {
	if (!keys[i].key || (keys[i].key == key))
	    break;
    }
    return keys[i].value;
}

void runLines(int key, int button, int x, int y) {
    struct lineopts *line;

    for (line = linelist; line; line = line->next) {
	if (line->left <= x && line->right >= x &&
		line->upper <= y && line->lower >= y) {
#if WITH_DETACH
	    if (disMount(line)) {
		break;
	    } else
#endif	/* WITH_DETACH */
	    if (itsaTalker(line)) {
		break;
	    } else if (itsCalendar(line)) {
		break;
	    } else if (itsaTimer(line, key, button, x)) {
		break;
	    } else if (itsElapsed(line, key, button, x)) {
		break;
	    } else if (itsVolume(line, key, button, x, y)) {
		break;
	    }
#if WITH_COMMANDS || WITH_STEPEXEC
	    else if (line->command) {	/* Any key or button */
		execComm(line->command);
		break;
	    }
#endif	/* WITH_COMMANDS || WITH_STEPEXEC */
	    break;	/* from linelist */
	}
    }
}

void reFresh(int function) {
    int refresh;

    refresh = g.refresh;
    g.refresh = 0;
    if (function) {
	checkLines();
	g.updaterequest = 1;
    } else {
	getVolume();
    }
    g.refresh = refresh;
}

XEvent event;

void checkEvents() {
    int key;
#if WITH_SUID
    static int shutdown=0;
#endif	/* WITH_SUID */

    while (XPending(maindisplay)) {
	XNextEvent(maindisplay, &event);
	switch (event.type) {
	    case KeyPress:
		key = event.xkey.keycode;
		if ((key == 31 || key == 53 || key == 59) &&
			(event.xkey.state & ControlMask)) {
		    /* ^q(uit) || ^w(indow) */
		    gdayMate(NULL, NULL);
		} else if (event.xkey.state & ShiftMask) {
		    smalbuf[0] = '\0';
		    if (key == 10 || key == 87) {		/* 1 */
			key = (g.powerlog = !g.powerlog);
			snprintf(smalbuf, sizeof(smalbuf), "power state");
		    } else if (key == 11 || key == 88) {	/* 2 */
			key = (g.timelog = !g.timelog);
			snprintf(smalbuf, sizeof(smalbuf), "time sync");
		    } else if (key == 12 || key == 89) {	/* 3 */
			key = (g.thermlog = !g.thermlog);
			snprintf(smalbuf, sizeof(smalbuf), "thermal zone");
		    } else if (key == 13 || key == 83) {	/* 4 */
			key = (g.execlog = !g.execlog);
			snprintf(smalbuf, sizeof(smalbuf), "remote execute");
		    }
		    if (key == 1)
			stringcat(smalbuf, " logging is on", sizeof(smalbuf));
		    else if (!key)
			stringcat(smalbuf, " logging is off", sizeof(smalbuf));
		    if (strlen(smalbuf))
			logMessage(smalbuf);
#if WITH_SUID
		} else if (g.clickoff) {
		    /* Don`t respond to other keys. */
		    break;
#endif	/* WITH_SUID */
		} else if (key == 65 && g.refresh > 1) {
		    /* Space in top. */
		    reFresh(1);
		    break;
		} else if ((key = keyCheck(key)) >= 0) {
		    /* Key entry for line manipulation. */
		    if (!calcPhase(key, -1, event.xkey.x, event.xkey.y))
			runLines(key, -1, event.xkey.x, event.xkey.y);
		}
		break;	/* from key press */
	    case ButtonPress:
#if WITH_SUID
		if (g.clickoff) {
		    /* Click anywhere for shutdown.
		     * Click again to kill shutdown.
		     */
		    if (!shutdown) {
			shutdown = execShutdown(g.clicktime);
			makeNoise(g.clicksound);
		    } else {
			shutdown = execShutdown(-1);
		    }
		    break;
		}
#endif	/* WITH_SUID */
		if (!calcPhase(-1, event.xbutton.button, event.xbutton.x, event.xbutton.y))
		    runLines(-1, event.xbutton.button, event.xbutton.x, event.xbutton.y);
		break;	/* from button press */
	    case Expose:
		if (event.xexpose.count == 0)
		    g.updaterequest = 1;
		break;
	    case ClientMessage:
		/* Respond to click on close window button. */
		if ((event.xclient.message_type == wmprotocols) &&
			(event.xclient.data.l[0] == wmdeletewindow))
		    gdayMate(NULL, NULL);
		break;
	}
    }
}

#if WITH_DETACH
int disMount(struct lineopts *line) {
    char *buffer, *ptr;
    FILE *fd;
    int yes=0;

    if (line->command && !strcmp(line->command, "detach")) {
	yes = 1;
	if (!g.localhost || !(buffer = calloc(BUFSIZE, sizeof(char))))
	    return yes;
	buffer[0] = '\0';
	if (g.effectiveuid) {
	    snprintf(buffer, BUFSIZE,
		     "echo Salmon must be run SUID to implement the detach function");
	} else if (!strcmp(line->type, "mount")) {
	    /* This is an absolute address to an alleged file system. */
	    if (!strcmp(line->argument, "/")) {
		snprintf(buffer, BUFSIZE,
			"echo Salmon will not un/mount root");
	    } else if (!strcmp(line->status, "0")) {
		snprintf(buffer, BUFSIZE,
			"echo File system %s is not in /etc/fstab",
			line->argument);
	    } else if (!strcmp(line->status, "1")) {
		/* The file system is found but not mounted.
		 * line->value has been set with fs_mntops etc.
		 * by mntPoint() in refresh.c
		 */
		snprintf(buffer, BUFSIZE, "/sbin/mount %s",
			line->value);
	    } else if (!strcmp(line->status, "2")) {
		snprintf(buffer, BUFSIZE, "/sbin/umount -f %s",
			line->argument);
	    }
	} else if (!strcmp(line->type, "interface")) {
	    /* This is a network interface. */
	    if (!strncmp(line->argument, "wlan", 4)) {
		snprintf(buffer, BUFSIZE,
			"echo Salmon does not support detaching %s",
			line->argument);
	    } else if (!strcmp(line->status, "0")) {
		snprintf(buffer, BUFSIZE, "/sbin/ifconfig %s up",
			line->argument);
	    } else {
		snprintf(buffer, BUFSIZE, "/sbin/ifconfig %s down",
			line->argument);
	    }
	}
	if (strlen(buffer)) {
	    if (line->value && strstr(line->value, "nfs")) {
		/* line->next was spliced into the linelist at the
		 * first pass through mntPoint() and set to type
		 * remoteHost.  The status of the host is kept
		 * updated by checkLines() and the icmp echo
		 * reply code.  Don`t attempt to toggle the
		 * state if the host is not responding.
		 */
		if (parseStrtol(line->next->value, TRUE, "disMount") >= 5)
		    snprintf(buffer, BUFSIZE,
			    "echo Host %s for file system %s is not responding",
			    line->next->argument, line->argument);
	    }
	    seteuid(g.effectiveuid);
	    if ((fd = popen(buffer, "r"))) {
		if (!strncmp(buffer, "echo ", 5)) {
		    buffer[fread(buffer, sizeof(char), BUFSIZE, fd)] = '\0';
		    if ((ptr = strchr(buffer, '\n')))
			*ptr = '\0';
		    logMessage(buffer);
		}
		pclose(fd);
	    }
	    seteuid(g.realuid);
	}
	FREE(buffer);
    }
    return yes;
}
#endif	/* WITH_DETACH */

#if defined(__NetBSD__) || defined(__OpenBSD__)
int pointless[]={0, 0, 7, 7, 7,
		15, 15, 15, 23, 23, 23, 31, 31, 31,
		39, 39, 39, 47, 47, 47, 55, 55, 55,
		63, 63, 63, 71, 71, 71, 79, 79, 79,
		87, 87, 87, 95, 95, 95, 103, 103, 103,
		111, 111, 111, 119, 119, 119, 127, 127, 127,
		135, 135, 135, 143, 143, 143, 151, 151, 151,
		159, 159, 159, 167, 167, 167, 175, 175, 175,
		183, 183, 183, 191, 191, 191, 199, 199, 199,
		207, 207, 207, 215, 215, 215, 223, 223, 223,
		231, 231, 231, 239, 239, 239, 247, 247, 247,
		247, 255, 255, 255, 255, 255
		};
#endif

int itsVolume(struct lineopts *line, int key, int button, int x, int y) {
    static time_t lasttime=0;
    time_t timenow=time(NULL);
    static int step=0, sleep=0;
    int yes=0, channel, volume[2];
    FILE *fd;

    if ((yes = (line->index == getIndex("volume")))) {
	if (!step) {
#if defined(__NetBSD__) || defined(__OpenBSD__)
	    step = 3;
#else
	    step = 1;
#endif
	}
	if (!strcmp(line->status, "connect")) {
#if !defined(__linux__)
	    g.volconn = !g.volconn;
#endif
	    return yes;
	}
	if (timenow >= (lasttime + 1))
	    sleep = 0;
	if (sleep)
	    /* Update the current values.
	     * This is necessary for the
	     * left & right press & hold
	     * action to work.
	     */
	    reFresh(0);
	volume[0] = fresh.volume[0];
	volume[1] = fresh.volume[1];
	channel = parseStrtol(line->value, TRUE, "itsVolume");
	if (x <= line->left + (6 * g.xmag)) {
#if defined(__NetBSD__) || defined(__OpenBSD__)
	    if (volume[channel] > 95)
		volume[channel] = 95;
#endif
	    volume[channel] -= step;
	    goto sleep;
	} else if (x >= line->left + (58 * g.xmag)) {
#if defined(__NetBSD__) || defined(__OpenBSD__)
	    if (volume[channel] < 3)
		volume[channel] = 3;
#endif
	    volume[channel] += step;
#if defined(__NetBSD__) || defined(__OpenBSD__)
	    if (volume[channel] > 95)
		volume[channel] = 100;
#endif
sleep:
	    if (!sleep)
		sleep = 100000;
	    else
		sleep = 20000;
	} else {
	    volume[channel] = (((x - line->left) / g.xmag) - 6) * 2;
	    sleep = 0;
	}
	if (volume[channel] < 0)
	    volume[channel] = 0;
	else if (volume[channel] > 100)
	    volume[channel] = 100;
#if defined(__NetBSD__) || defined(__OpenBSD__)
	volume[channel] = pointless[volume[channel] +
		((sleep != 0) && (volume[channel] != 100))];
#endif
	if (g.volconn) {
	    if (channel)
		volume[0] = volume[1];
	    else
		volume[1] = volume[0];
	}
#if defined(__FreeBSD__)
	snprintf(smalbuf, SMALBUF, "%s vol %d:%d",
		g.mixer, volume[0], volume[1]);
#elif defined(__linux__)
	snprintf(smalbuf, SMALBUF, "%s -v %d",
		g.mixer, volume[0]);
#else
	snprintf(smalbuf, SMALBUF, "%s -w outputs.master=%d,%d",
		g.mixer, volume[0], volume[1]);
#endif
	if (strlen(smalbuf) && (fd = popen(smalbuf, "r")))
	    /* execComm() prints scruff to stdout. */
	    pclose(fd);
	if (XPending(maindisplay)) {
	    sleep = 0;
	} else if (sleep) {
	    usleep(sleep);
	    if (!XPending(maindisplay))
		XPutBackEvent(maindisplay, &event);
	}
    }
    lasttime = timenow;
    return yes;
}

/* Toggle talking clock on and off.
 * Don`t set yes unless line->setting
 * is defined.
 */
int itsaTalker(struct lineopts *line) {
    int yes=0;

    if (!strcmp(line->type, "format")) {
	if (line->setting) {
	    STRDUP(line->buffer, line->setting);
	    FREE(line->setting);
	    yes = 1;
	} else if (line->buffer) {
	    STRDUP(line->setting, line->buffer);
	    FREE(line->buffer);
	    yes = 1;
	}
    }
    return yes;
}

/* Move current date to line
 * on key/pointer event.
 */
int itsCalendar(struct lineopts *line) {
    int yes=0, color=0, setting=1, dir=-1;
    struct lineopts *l1, *ln;

    if ((yes = (line->index == getIndex("calendar")))) {
	for (l1 = linelist; l1; l1 = l1->next) {
	    if (l1->index == getIndex("calendar"))
		break;
	}
	for (ln = l1; ln; ln = ln->next) {
	    if (ln->index != getIndex("calendar"))
		break;
	    setting += dir;
	    if (ln->color)
		color = ln->color;
	    if (ln == line)
		dir = 0;
	}
	while (l1 && l1->index == getIndex("calendar")) {
	    snprintf(smalbuf, SMALBUF, "%d", setting);
	    STRDUP(l1->setting, smalbuf);
	    if (setting && l1->color)
		l1->color = 0;
	    else if (!setting)
		l1->color = color;
	    l1 = l1->next;
	    setting++;
	}
	reFresh(1);
    }
    return yes;
}

int itsaTimer(struct lineopts *line, int key, int button, int x) {
    char showbuf[SHOWBUF];
    int yes=0;

    if ((yes = (line->index >= getIndex("timer") && line->index <= getIndex("reset")))) {
	if (!line->status) {
	    if (key == 102 || button == 3) {			/* Right arrow or right click, start count */
		if (line->value) {				/* argument value */
		    STRDUP(line->buffer, line->value);
		} else if (line->setting) {			/* setting value */
		    STRDUP(line->buffer, line->setting);
		} else {
		    return yes;
		}
		if (!strcmp(line->type, "timer")) {
		    snprintf(showbuf, sizeof(showbuf), "%d",
			    (int)(parseStrtol(line->buffer, TRUE, "itsaTimer") + time(NULL)));
		    STRDUP(line->buffer, showbuf);
		}
		STRDUP(line->status, "running");
	    } else if (key == 99 || key == 98 || button == 2) {		/* ESC, up arrow, middle click, edit setting */
		if (!line->setting) {
		    STRDUP(line->setting, "0");
		}
		STRDUP(line->buffer, line->setting);
		STRDUP(line->status, "setting");
		if (!strcmp(line->type, "timer"))
		    adjTimer(line, -1, 0, x);
		else
		    adjAlarm(line, -1, 0, x);
	    } else if (key == 22 || key == 100 || button == 1) {	/* Backspace, left arrow, left click, edit value */
		if (!line->value) {
		    STRDUP(line->value, "0");
		}
		STRDUP(line->buffer, line->value);
		STRDUP(line->status, "value");
		if (!strcmp(line->type, "timer"))
		    adjTimer(line, -1, 0, x);
		else
		    adjAlarm(line, -1, 0, x);
	    }
	} else if (!strcmp(line->status, "value") ||
		!strcmp(line->status, "setting")) {		/* In edit mode. */
	    if (key == 98 || button == 2) {			/* Up arrow, start count. */
		if (!strcmp(line->type, "timer")) {
		    snprintf(showbuf, sizeof(showbuf), "%d",
			    (int)(parseStrtol(line->buffer, TRUE, "itsaTimer") + time(NULL)));
		    STRDUP(line->buffer, showbuf);
		}
		if (!strcmp(line->type, "reset") &&
			!strcmp(line->status, "setting")) {
		    FREE(line->status);
		} else {
		    STRDUP(line->status, "running");
		}
	    } else {
		if (!strcmp(line->type, "timer"))
		    adjTimer(line, key, button, x);
		else
		    adjAlarm(line, key, button, x);
	    }
	} else if (!strcmp(line->status, "running") &&
		(key == 100 || button == 1)) {			/* Left arrow or left click, stop count. */
	    FREE(line->status);
	}
    }
    return yes;
}

int itsElapsed(struct lineopts *line, int key, int button, int x) {
    time_t timeval=0, timenow=time(NULL);
    char showbuf[SHOWBUF];
    int yes=0;

    if ((yes = (!strcmp(line->type, "elapsed")))) {
	if (!line->status) {			/* Any key or button starts. */
	    snprintf(showbuf, sizeof(showbuf), "%d", (int)timenow);
	    STRDUP(line->value, showbuf);
	    STRDUP(line->status, "running");
	} else if (!strcmp(line->status, "running")) {
	     if (key == 98 || button == 2) {	/* Up */
		STRDUP(line->status, "stopped");
		snprintf(showbuf, sizeof(showbuf), "%d", (int)timenow);
		STRDUP(line->buffer, showbuf);
	     }
	} else if (!strcmp(line->status, "stopped") || !strcmp(line->status, "clear")) {
	    if (key == 100 || button == 1) {	/* Left */
		/* Run time. */
		timeval = timenow - parseStrtol(line->buffer, TRUE, "itsElapsed");
		snprintf(showbuf, sizeof(showbuf), "%d",
			parseStrtol(line->value, TRUE, "itsElapsed") + (int)timeval);
		STRDUP(line->value, showbuf);
		STRDUP(line->status, "running");
	    } else if (key == 102 || button == 3) {	/* Right */
		/* Lap time. */
		STRDUP(line->status, "running");
		FREE(line->buffer);
	    } else if (key == 98 || button == 2) {	/* Up */
		if (!strcmp(line->status, "stopped")) {
		    STRDUP(line->status, "clear");
		} else if (!strcmp(line->status, "clear")) {
		    FREE(line->status);
		}
	    }
	}
    }
    return yes;
}

/* This only responds to up, left or right arrow
 * keys or button clicks.  Click on any part of
 * the window or panel for function.
 */
int calcPhase(int key, int button, int x, int y) {
    time_t timenow=time(NULL);
    struct lineopts *line;
    int yes=0;

    if (g.phasecalc) {
	for (line = linelist; line; line = line->next) {
	    if (line->status && !strcmp(line->type, "display") &&
		    x >= line->left && x <= line->right &&
		    y >= ((line->panel > 2) * g.psize) &&
		    y <= ((line->panel > 2) * g.psize) + g.psize &&
		    (key >= 98 || button > 0)) {
		yes = 1;
		if (!g.phasetime)
		    g.phasetime = timenow;
		if (key == 100 || button == 1)
		    /* Previous quarter, static display. */
		    getNext(g.phasetime, -1);
		else if (key == 102 || button == 3)
		    /* Next quarter, static display. */
		    getNext(g.phasetime, 1);
		else if (key == 98 || button == 2)
		    /* Current status, running display. */
		    g.phasetime = 0;
		checkLines();	/* Update the data. */
		g.updaterequest = 1;	/* Update the display. */
	    }
	}
    }
    return yes;
}

/* Convert user date and time into seconds for alarms.
 * Enter with line->buffer as HH:MM [yy/mm/dd].
 * Returns expiration time in seconds.
 */
time_t getExpiration(struct lineopts *line) {
    struct tm *timeptr;
    time_t timenow=time(NULL);
    int hour=0, minute=0, year=0, month=0, day=0, count;

    timeptr = localtime(&timenow);
    timeptr->tm_sec = 0;
    count = sscanf(line->buffer, "%d:%d %d/%d/%d", &hour, &minute, &year, &month, &day);
    if (count == 5) {
	timeptr->tm_hour = hour;
	timeptr->tm_min = minute;
	timeptr->tm_year = year + 100;
	timeptr->tm_mon = month - 1;
	timeptr->tm_mday = day;
    } else if (count == 2) {
	timeptr->tm_hour = hour;
	timeptr->tm_min = minute;
    } else {
	gdayMate("getExpiration() alarm/reset setting", strerror(EINVAL));
    }
    return mktime(timeptr);
}

/* Convert user time into seconds for timers.
 * Enter with line->buffer as ddd HH:MM:SS.
 * Returns duration in seconds.
 */
time_t getDuration(struct lineopts *line) {
    int days=0, hours, minutes, seconds;
    
    if ((sscanf(line->buffer, "%d:%d:%d",
	    &hours, &minutes, &seconds) == 3) ||
	    (sscanf(line->buffer, "%d %d:%d:%d",
	    &days, &hours, &minutes, &seconds) == 4) ||
	    (sscanf(line->buffer, "T||%d||%d:%d:%d",
	    &days, &hours, &minutes, &seconds) == 4)) {
	if (days > 999)
	    days = 999;
	if (hours > 23)
	    hours = 23;
	if (minutes > 59)
	    minutes = 59;
	if (seconds > 59)
	    seconds = 59;
    } else {
	gdayMate("getDuration() timer preset", strerror(EINVAL));
    }
    return ((days * 86400) + (hours * 3600) + (minutes * 60) + seconds);
}

/* Translate key and button events
 * for adjTimer() and adjAlarm().
 */
int transKey(int *key, int *button, int *iskey) {
    int yes=0;

    /* Use left and right arrow keys as button events. */
    if (*key == 100) {
	*key = -1;
	*button = 1;
    } else if (*key == 102) {
	*key = -1;
	*button = 3;
    }
    if (*key >= 0 && *key <= 9) {
	*key += AZERO;
	*iskey = 1;
	yes = 1;
    } else if (*button > 0) {
	if (*button == 1)
	    *button = -1;
	else if (*button == 3)
	    *button = 1;
	yes = 1;
    } else if (*key == 99) {
	yes = 1;
    }
    return yes;
}

/* Used for key or button setting timers.
 */
void adjTimer(struct lineopts *line, int key, int button, int x) {
    int days, hours, minutes, seconds, mx, space=0, iskey=0;
    char showbuf[SHOWBUF];

    mx = (x - line->left) / g.xmag;
    splitSeconds(&days, &hours, &minutes, &seconds, parseStrtol(line->buffer, TRUE, "edjTimer"));
    snprintf(showbuf, sizeof(showbuf), "T||%03d||%02d:%02d:%02d",
	    days, hours, minutes, seconds);
    if (transKey(&key, &button, &iskey)) {
	if (mx >= 55) {
	    /* Seconds. */
	    space = 15;
	} else if (mx >= 50 && mx < 55) {
	    /* Ten seconds. */
	    space = 14;
	} else if (mx >= 43 && mx < 48) {
	    /* Minutes. */
	    space = 12;
	} else if (mx >= 38 && mx < 43) {
	    /* Ten minutes. */
	    space = 11;
	} else if (mx >= 32 && mx < 37) {
	    /* Hours. */
	    space = 9;
	} else if (mx >= 26 && mx < 31) {
	    /* Ten hours. */
	    space = 8;
	} else if (mx >= 19 && mx < 24) {
	    /* Days. */
	    space = 5;
	} else if (mx >= 14 && mx < 19) {
	    /* Ten days. */
	    space = 4;
	} else if (mx >= 9 && mx < 14) {
	    /* One hundred days. */
	    space = 3;
	} else if (mx < 8 && button == -1) {
	    FREE(line->status);
	}
	if (space) {
	    if (iskey) {
		showbuf[space] = key;
	    } else {
		showbuf[space] += button;
		if (showbuf[space] > ANINE)
		    showbuf[space] = ANINE;
		else if (showbuf[space] < AZERO)
		    showbuf[space] = AZERO;
	    }
	}
    }
    STRDUP(line->buffer, showbuf);
    snprintf(showbuf, sizeof(showbuf), "%d", (int)getDuration(line));
    STRDUP(line->buffer, showbuf);
    if (key == 36 && mx < 8) {
	if (!strcmp(line->status, "setting")) {
	    STRDUP(line->setting, showbuf);
	} else {
	    STRDUP(line->value, showbuf);
	}
	snprintf(showbuf, sizeof(showbuf), "%d",
		(int)(parseStrtol(line->buffer, TRUE, "adjTimer") + time(NULL)));
	STRDUP(line->buffer, showbuf);
	STRDUP(line->status, "running");
    }
}

/* Change the reset interval from the default 24
 * hours or user preset on line->type "reset".
 */
void adjInterval(struct lineopts *line, int key, int button, int x) {
    char showbuf[SHOWBUF];
    time_t timeval=0;
    int mx, space=0, top=ANINE, iskey=0;
    int days, hours, minutes;

    mx = (x - line->left) / g.xmag;
    timeval = parseStrtol(line->buffer, TRUE, "adjInterval");
    splitSeconds(&days, &hours, &minutes, NULL, timeval);
    snprintf(showbuf, sizeof(showbuf), "Interval|%02d:%02d",
	    hours + (days * 24), minutes);
    if (transKey(&key, &button, &iskey)) {
	if (mx >= 58) {
	    /* Minutes */
	    space = 13;
	} else if (mx >= 53 && mx < 58) {
	    /* Ten minutes */
	    space = 12;
	    top = 53;		/* 5 */
	} else if (mx >= 46 && mx < 51) {
	    /* Hours */
	    space = 10;
	} else if (mx >= 41 && mx < 46) {
	    /* Ten hours */
	    space = 9;
	} else if (mx < 8) {
	    if (button == -1) {
		FREE(line->status);
	    }
	}
	if (space) {
	    if (iskey)
		showbuf[space] = key;
	    else
		showbuf[space] += button;
	    if (showbuf[space] > top)
		showbuf[space] = top;
	    else if (showbuf[space] < AZERO)
		showbuf[space] = AZERO;
	}
    }
    sscanf(showbuf, "Interval|%d:%d", &hours, &minutes);
    timeval = (hours * 3600) + (minutes * 60);
    snprintf(showbuf, sizeof(showbuf), "%d", (int)timeval);
    STRDUP(line->buffer, showbuf);
    if (key == 36 && mx < 8) {		/* Enter, set new interval */
	if (!timeval) {
	    FREE(line->setting);	/* Restore default 24 hours */
	} else {
	    STRDUP(line->setting, showbuf);
	}
	FREE(line->status);
    }
}

void adjAlarm(struct lineopts *line, int key, int button, int x) {
    struct tm *timeptr=NULL;
    time_t timeval=0, timenow=time(NULL);
    char showbuf[SHOWBUF], *ptr;
    int mx, space=0, top=ANINE, iskey=0;

    if (!strcmp(line->type, "reset") && !strcmp(line->status, "setting")) {
	adjInterval(line, key, button, x);
	return;
    }
    mx = (x - line->left) / g.xmag;
    timeval = parseStrtol(line->buffer, TRUE, "adjAlarm");
    if (!timeval)
	timeval = timenow;
    timeptr = localtime(&timeval);
    timeptr->tm_sec = 0;
    strftime(showbuf, sizeof(showbuf), "A||%m/%d||%H:%M", timeptr);
    if (!strcmp(line->type, "reset"))
	showbuf[0] = 'R';
    if (key == 99)	/* ESC, set to now plus one hour. */
	button = 0;	/* So it won`t change values. */
    if (transKey(&key, &button, &iskey)) {
	if (mx >= 55) {
	    /* Minutes */
	    space = 14;
	} else if (mx >= 50 && mx < 55) {
	    /* Ten minutes */
	    space = 13;
	    top = 53;		/* 5 */
	} else if (mx >= 43 && mx < 48) {
	    /* Hours */
	    space = 11;
	} else if (mx >= 38 && mx < 43) {
	    /* Ten hours */
	    space = 10;
	    top = 50;		/* 2 */
	} else if (mx >= 31 && mx < 36) {
	    /* Days */
	    space = 7;
	} else if (mx >= 26 && mx < 31) {
	    /* Ten days */
	    space = 6;
	    top = 51;		/* 3 */
	} else if (mx >= 15 && mx < 20) {
	    /* Months */
	    space = 4;
	} else if (mx >= 10 && mx < 15) {
	    /* Ten months */
	    space = 3;
	    top = 49;		/* 1 */
	} else if (mx < 8) {
	    if (key == 99) {		/* ESC */
		timeval = timenow + 3600;
		timeptr = localtime(&timeval);
		strftime(showbuf, sizeof(showbuf), " ||%m/%d||%H:%M", timeptr);
	    } else if (button == -1) {
		FREE(line->status);
	    }
	}
	if (space) {
	    if (iskey)
		showbuf[space] = key;
	    else
		showbuf[space] += button;
	    if (showbuf[space] > top)
		showbuf[space] = top;
	    else if (showbuf[space] < AZERO)
		showbuf[space] = AZERO;
	}
    }
    timeptr = localtime(&timenow);
    timeptr->tm_sec = 0;
    ptr = showbuf;
    ptr++;	/* Past the A or D */
    strptime(ptr, "||%m/%d||%H:%M", timeptr);
    timeval = mktime(timeptr);
    snprintf(showbuf, sizeof(showbuf), "%d", (int)timeval);
    STRDUP(line->buffer, showbuf);
    if (key == 36 && mx < 8) {
	if (!strcmp(line->status, "setting")) {
	    STRDUP(line->setting, showbuf);
	} else {
	    STRDUP(line->value, showbuf);
	}
	STRDUP(line->status, "running");
    }
}
* * o * *