/*
* Copyright 2001 by Marco d'Itri <md@linux.it>
*
* Original version by Mike Baker, then maintained by pcg@goof.com.
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#if HAS_REGEX
#include <regex.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
/* data structures */
struct logfile_entry {
char *fname; /* name of file */
char *desc; /* alternative description */
FILE *fp; /* FILE struct associated with file */
ino_t inode; /* inode of the file opened */
off_t last_size; /* file size at the last check */
unsigned long color; /* color to be used for printing */
struct logfile_entry *next;
};
struct linematrix {
char *line;
unsigned long color;
};
/* global variables */
unsigned int width = STD_WIDTH, listlen = STD_HEIGHT;
int win_x = LOC_X, win_y = LOC_Y;
int w = -1, h = -1, font_width, font_height, font_descent;
int do_reopen;
struct timeval interval = { 2, 400000 }; /* see Knuth */
/* command line options */
int opt_noinitial, opt_shade, opt_frame, opt_reverse, opt_nofilename,
geom_mask, reload = 3600;
const char *command = NULL,
*fontname = USE_FONT, *dispname = NULL, *def_color = DEF_COLOR;
struct logfile_entry *loglist = NULL, *loglist_tail = NULL;
Display *disp;
Window new_root, real_root;
GC WinGC;
#if HAS_REGEX
struct re_list {
regex_t from;
const char *to;
struct re_list *next;
};
struct re_list *re_head, *re_tail;
#endif
/* prototypes */
void list_files(int);
void force_reopen(int);
void force_refresh(int);
void blank_window(int);
Window ToonGetRootWindow(Display *, int, Window *);
void InitWindow(void);
unsigned long GetColor(const char *);
void redraw(void);
void refresh(struct linematrix *, int, int);
void transform_line(char *s);
int lineinput(char *, int, FILE *);
void reopen(void);
void check_open_files(void);
FILE *openlog(struct logfile_entry *);
void main_loop(void);
void display_version(void);
void display_help(char *);
void install_signal(int, void (*)(int));
void *xstrdup(const char *);
void *xmalloc(size_t);
int daemonize(void);
/* signal handlers */
void list_files(int dummy)
{
struct logfile_entry *e;
fprintf(stderr, "Files opened:\n");
for (e = loglist; e; e = e->next)
fprintf(stderr, "\t%s (%s)\n", e->fname, e->desc);
}
void force_reopen(int dummy)
{
do_reopen = 1;
}
void force_refresh(int dummy)
{
XClearWindow(disp, new_root);
redraw();
}
void blank_window(int dummy)
{
XClearWindow(disp, new_root);
XFlush(disp);
exit(0);
}
/* X related functions */
unsigned long GetColor(const char *ColorName)
{
XColor Color;
XWindowAttributes Attributes;
XGetWindowAttributes(disp, real_root, &Attributes);
Color.pixel = 0;
if (!XParseColor(disp, Attributes.colormap, ColorName, &Color))
fprintf(stderr, "can't parse %s\n", ColorName);
else if (!XAllocColor(disp, Attributes.colormap, &Color))
fprintf(stderr, "can't allocate %s\n", ColorName);
return Color.pixel;
}
void InitWindow(void)
{
XGCValues gcv;
Font font;
unsigned long gcm;
XFontStruct *info;
int screen, ScreenWidth, ScreenHeight;
if (!(disp = XOpenDisplay(dispname))) {
fprintf(stderr, "Can't open display %s.\n", dispname);
exit(1);
}
screen = DefaultScreen(disp);
ScreenHeight = DisplayHeight(disp, screen);
ScreenWidth = DisplayWidth(disp, screen);
real_root = RootWindow(disp, screen);
new_root = ToonGetRootWindow( disp, screen, &real_root );
gcm = GCBackground;
gcv.graphics_exposures = True;
WinGC = XCreateGC(disp, new_root, gcm, &gcv);
XMapWindow(disp, new_root);
XSetForeground(disp, WinGC, GetColor(DEF_COLOR));
font = XLoadFont(disp, fontname);
XSetFont(disp, WinGC, font);
info = XQueryFont(disp, font);
font_width = info->max_bounds.width;
font_descent = info->max_bounds.descent;
font_height = info->max_bounds.ascent + font_descent;
w = width * font_width;
h = listlen * font_height;
if (geom_mask & XNegative)
win_x = win_x + ScreenWidth - w;
if (geom_mask & YNegative)
win_y = win_y + ScreenHeight - h;
XSelectInput(disp, new_root, ExposureMask|FocusChangeMask);
}
char *
detabificate (char *s)
{
char * out;
int i, j;
out = malloc (8 * strlen (s) + 1);
for(i = 0, j = 0; s[i]; i++)
{
if (s[i] == '\t')
do
out[j++] = ' ';
while (j % 8);
else
out[j++] = s[i];
}
out[j] = '\0';
return out;
}
/*
* redraw does a complete redraw, rather than an update (i.e. the area
* gets cleared first)
* the rest is handled by regular refresh()'es
*/
void redraw(void)
{
XClearArea(disp, new_root, win_x, win_y, w, h + font_descent + 2, True);
}
/* Just redraw everything without clearing (i.e. after an EXPOSE event) */
void refresh(struct linematrix *lines, int miny, int maxy)
{
int lin;
int offset = (listlen + 1) * font_height;
unsigned long black_color = GetColor("black");
miny -= win_y + font_height;
maxy -= win_y - font_height;
for (lin = listlen; lin--;)
{
char *temp;
offset -= font_height;
if (offset < miny || offset > maxy)
continue;
#define LIN ((opt_reverse)?(listlen-lin-1):(lin))
temp = detabificate (lines[LIN].line);
if (opt_shade)
{
XSetForeground (disp, WinGC, black_color);
XDrawString (disp, new_root, WinGC, win_x + 2, win_y + offset + 2,
temp, strlen (temp));
}
XSetForeground (disp, WinGC, lines[LIN].color);
XDrawString (disp, new_root, WinGC, win_x, win_y + offset,
temp, strlen (temp));
free (temp);
}
if (opt_frame) {
int bot_y = win_y + h + font_descent + 2;
XDrawLine(disp, new_root, WinGC, win_x, win_y, win_x + w, win_y);
XDrawLine(disp, new_root, WinGC, win_x + w, win_y, win_x + w, bot_y);
XDrawLine(disp, new_root, WinGC, win_x + w, bot_y, win_x, bot_y);
XDrawLine(disp, new_root, WinGC, win_x, bot_y, win_x, win_y);
}
}
#if HAS_REGEX
void transform_line(char *s)
{
#ifdef I_AM_Md
int i;
if (1) {
for (i = 16; s[i]; i++)
s[i] = s[i + 11];
}
s[i + 1] = '\0';
#endif
if (transformre) {
int i;
regmatch_t matched[16];
i = regexec(&transformre, string, 16, matched, 0);
if (i == 0) { /* matched */
}
}
}
#endif
/*
* This routine should read 'width' characters and not more. However,
* we really want to read width + 1 charachters if the last char is a '\n',
* which we should remove afterwards. So, read width+1 chars and ungetc
* the last character if it's not a newline. This means 'string' must be
* width + 2 wide!
*/
int lineinput(char *string, int slen, FILE *f)
{
int len;
do {
if (fgets(string, slen, f) == NULL) /* EOF or Error */
return 0;
len = strlen(string);
} while (len == 0);
if (string[len - 1] == '\n')
string[len - 1] = '\0'; /* erase newline */
else if (len >= slen - 1) {
ungetc(string[len - 1], f);
string[len - 1] = '\0';
}
#if HAS_REGEX
transform_line(string);
#endif
return len;
}
/* input: reads file->fname
* output: fills file->fp, file->inode
* returns file->fp
* in case of error, file->fp is NULL
*/
FILE *openlog(struct logfile_entry *file)
{
struct stat stats;
if ((file->fp = fopen(file->fname, "r")) == NULL) {
file->fp = NULL;
return NULL;
}
fstat(fileno(file->fp), &stats);
if (S_ISFIFO(stats.st_mode)) {
if (fcntl(fileno(file->fp), F_SETFL, O_NONBLOCK) < 0)
perror("fcntl"), exit(1);
file->inode = 0;
} else
file->inode = stats.st_ino;
if (opt_noinitial)
fseek (file->fp, 0, SEEK_END);
else if (stats.st_size > (listlen + 1) * width)
{
char dummy[255];
fseek(file->fp, -((listlen + 2) * width), SEEK_END);
/* the pointer might point halfway some line. Let's
be nice and skip this damaged line */
lineinput(dummy, sizeof(dummy), file->fp);
}
file->last_size = stats.st_size;
return file->fp;
}
void reopen(void)
{
struct logfile_entry *e;
for (e = loglist; e; e = e->next) {
if (!e->inode)
continue; /* skip stdin */
if (e->fp)
fclose(e->fp);
/* if fp is NULL we will try again later */
openlog(e);
}
do_reopen = 0;
}
void check_open_files(void)
{
struct logfile_entry *e;
struct stat stats;
for (e = loglist; e; e = e->next) {
if (!e->inode)
continue; /* skip stdin */
if (stat(e->fname, &stats) < 0) { /* file missing? */
sleep(1);
if (e->fp)
fclose(e->fp);
if (openlog(e) == NULL)
break;
}
if (stats.st_ino != e->inode) { /* file renamed? */
if (e->fp)
fclose(e->fp);
if (openlog(e) == NULL)
break;
}
if (stats.st_size < e->last_size) { /* file truncated? */
fseek(e->fp, 0, SEEK_SET);
e->last_size = stats.st_size;
}
}
}
#define SCROLL_UP(lines, listlen) \
{ \
int cur_line; \
for (cur_line = 0; cur_line < (listlen - 1); cur_line++) { \
strcpy(lines[cur_line].line, lines[cur_line + 1].line); \
lines[cur_line].color = lines[cur_line + 1].color; \
} \
}
void main_loop(void)
{
struct linematrix *lines = xmalloc(sizeof(struct linematrix) * listlen);
int lin, miny, maxy, buflen;
char *buf;
time_t lastreload;
Region region = XCreateRegion();
XEvent xev;
maxy = 0;
miny = win_y + h;
buflen = width + 2;
buf = xmalloc(buflen);
lastreload = time(NULL);
/* Initialize linematrix */
for (lin = 0; lin < listlen; lin++) {
lines[lin].line = xmalloc(buflen);
strcpy(lines[lin].line, "~");
lines[lin].color = GetColor(def_color);
}
if (!opt_noinitial)
while (lineinput(buf, buflen, loglist->fp) != 0) {
SCROLL_UP(lines, listlen);
/* print the next line */
strcpy(lines[listlen - 1].line, buf);
}
for (;;) {
int need_update = 0;
struct logfile_entry *current;
static struct logfile_entry *lastprinted = NULL;
/* read logs */
for (current = loglist; current; current = current->next) {
if (!current->fp)
continue; /* skip missing files */
clearerr(current->fp);
while (lineinput(buf, buflen, current->fp) != 0) {
/* print filename if any, and if last line was from
different file */
if (!opt_nofilename &&
!(lastprinted && lastprinted == current) &&
current->desc[0]) {
SCROLL_UP(lines, listlen);
sprintf(lines[listlen - 1].line, "[%s]", current->desc);
lines[listlen - 1].color = current->color;
}
SCROLL_UP(lines, listlen);
strcpy(lines[listlen - 1].line, buf);
lines[listlen - 1].color = current->color;
lastprinted = current;
need_update = 1;
}
}
if (need_update)
redraw();
else {
XFlush(disp);
if (!XPending(disp)) {
fd_set fdr;
struct timeval to = interval;
FD_ZERO(&fdr);
FD_SET(ConnectionNumber(disp), &fdr);
select(ConnectionNumber(disp) + 1, &fdr, 0, 0, &to);
}
}
check_open_files();
if (do_reopen)
reopen();
/* we ignore possible errors due to window resizing &c */
while (XPending(disp)) {
XNextEvent(disp, &xev);
switch (xev.type) {
case Expose:
{
XRectangle r;
r.x = xev.xexpose.x;
r.y = xev.xexpose.y;
r.width = xev.xexpose.width;
r.height = xev.xexpose.height;
XUnionRectWithRegion(&r, region, region);
if (miny > r.y)
miny = r.y;
if (maxy < r.y + r.height)
maxy = r.y + r.height;
}
break;
default:
#ifdef DEBUGMODE
fprintf(stderr, "PANIC! Unknown event %d\n", xev.type);
#endif
break;
}
}
/* reload if requested */
if (reload && lastreload + reload < time(NULL)) {
if (command)
system(command);
reopen();
lastreload = time(NULL);
}
if (!XEmptyRegion(region)) {
XSetRegion(disp, WinGC, region);
refresh(lines, miny, maxy);
XDestroyRegion(region);
region = XCreateRegion();
maxy = 0;
miny = win_y + h;
}
}
}
int main(int argc, char *argv[])
{
int i;
int opt_daemonize = 0;
#if HAS_REGEX
char *transform = NULL;
#endif
/* window needs to be initialized before colorlookups can be done */
/* just a dummy to get the color lookups right */
geom_mask = NoValue;
InitWindow();
for (i = 1; i < argc; i++) {
if (argv[i][0] == '-' && argv[i][1] != '\0' && argv[i][1] != ',') {
if (!strcmp(argv[i], "--?") ||
!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h"))
display_help(argv[0]);
else if (!strcmp(argv[i], "-V"))
display_version();
else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "-geometry"))
geom_mask = XParseGeometry(argv[++i],
&win_x, &win_y, &width, &listlen);
else if (!strcmp(argv[i], "-display"))
dispname = argv[++i];
else if (!strcmp(argv[i], "-font") || !strcmp(argv[i], "-fn"))
fontname = argv[++i];
#if HAS_REGEX
else if (!strcmp(argv[i], "-t"))
transform = argv[++i];
#endif
else if (!strcmp(argv[i], "-fork") || !strcmp(argv[i], "-f"))
opt_daemonize = 1;
else if (!strcmp(argv[i], "-reload")) {
reload = atoi(argv[++i]);
command = argv[++i];
}
else if (!strcmp(argv[i], "-shade"))
opt_shade = 1;
else if (!strcmp(argv[i], "-frame"))
opt_frame = 1;
else if (!strcmp(argv[i], "-no-filename"))
opt_nofilename = 1;
else if (!strcmp(argv[i], "-reverse"))
opt_reverse = 1;
else if (!strcmp(argv[i], "-color"))
def_color = argv[++i];
else if (!strcmp(argv[i], "-noinitial"))
opt_noinitial = 1;
else if (!strcmp(argv[i], "-interval") || !strcmp(argv[i], "-i")) {
double iv = atof(argv[++i]);
interval.tv_sec = (int) iv;
interval.tv_usec = (iv - interval.tv_sec) * 1e6;
} else {
fprintf(stderr, "Unknown option '%s'.\n"
"Try --help for more information.\n", argv[i]);
exit(1);
}
} else { /* it must be a filename */
struct logfile_entry *e;
const char *fname, *desc, *fcolor = def_color;
char *p;
/* this is not foolproof yet (',' in filenames are not allowed) */
fname = desc = argv[i];
if ((p = strchr(argv[i], ','))) {
*p = '\0';
fcolor = p + 1;
if ((p = strchr(fcolor, ','))) {
*p = '\0';
desc = p + 1;
}
}
e = xmalloc(sizeof(struct logfile_entry));
if (argv[i][0] == '-' && argv[i][1] == '\0') {
if ((e->fp = fdopen(0, "r")) == NULL)
perror("fdopen"), exit(1);
if (fcntl(0, F_SETFL, O_NONBLOCK) < 0)
perror("fcntl"), exit(1);
e->fname = NULL;
e->inode = 0;
e->desc = xstrdup("stdin");
} else {
int l;
e->fname = xstrdup(fname);
if (openlog(e) == NULL)
perror(fname), exit(1);
l = strlen(desc);
if (l > width - 2) /* must account for [ ] */
l = width - 2;
e->desc = xmalloc(l + 1);
memcpy(e->desc, desc, l);
*(e->desc + l) = '\0';
}
e->color = GetColor(fcolor);
e->next = NULL;
if (!loglist)
loglist = e;
if (loglist_tail)
loglist_tail->next = e;
loglist_tail = e;
}
}
if (!loglist) {
fprintf(stderr, "You did not specify any files to tail\n"
"use %s --help for help\n", argv[0]);
exit(1);
}
#if HAS_REGEX
if (transform) {
int i;
transformre = xmalloc(sizeof(transformre));
i = regcomp(&transformre, transform, REG_EXTENDED);
if (i != 0) {
char buf[512];
regerror(i, &transformre, buf, sizeof(buf));
fprintf(stderr, "Cannot compile regular expression: %s\n", buf);
}
}
#endif
InitWindow();
install_signal(SIGINT, blank_window);
install_signal(SIGQUIT, blank_window);
install_signal(SIGTERM, blank_window);
install_signal(SIGHUP, force_reopen);
install_signal(SIGUSR1, list_files);
install_signal(SIGUSR2, force_refresh);
if (opt_daemonize)
daemonize();
main_loop();
exit(1); /* to make gcc -Wall stop complaining */
}
void install_signal(int sig, void (*handler)(int))
{
struct sigaction action;
action.sa_handler = handler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART;
if (sigaction(sig, &action, NULL) < 0)
fprintf(stderr, "sigaction(%d): %s\n", sig, strerror(errno)), exit(1);
}
void *xstrdup(const char *string)
{
void *p;
while ((p = strdup(string)) == NULL) {
fprintf(stderr, "Memory exausted.");
sleep(10);
}
return p;
}
void *xmalloc(size_t size)
{
void *p;
while ((p = malloc(size)) == NULL) {
fprintf(stderr, "Memory exausted.");
sleep(10);
}
return p;
}
void display_help(char *myname)
{
printf("Usage: %s [options] file1[,color[,desc]] "
"[file2[,color[,desc]] ...]\n", myname);
printf(" -g | -geometry geometry -g WIDTHxHEIGHT+X+Y\n"
" -color color use color $color as default\n"
" -reload sec command reload after $sec and run command\n"
" by default -- 3 mins\n"
" -font FONTSPEC (-fn) font to use\n"
" -f | -fork fork into background\n"
" -reverse print new lines at the top\n"
" -shade add shading to font\n"
" -noinitial don't display the last file lines on\n"
" startup\n"
" -i | -interval seconds interval between checks (fractional\n"
" values o.k.). Default 3\n"
" -V display version information and exit\n"
"\n");
printf("Example:\n%s -g 80x25+100+50 -font fixed /var/log/messages,green "
"/var/log/secure,red,'ALERT'\n", myname);
exit(0);
}
void display_version(void) {
printf("root-tail version " VERSION "\n");
exit(0);
}
int daemonize(void) {
switch (fork()) {
case -1:
return -1;
case 0:
break;
default:
_exit(0);
}
if (setsid() == -1)
return -1;
return 0;
}
/* toon_root.c - finding the correct background window / virtual root
* Copyright (C) 1999-2001 Robin Hogan
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/* Since xpenguins version 2.1, the ToonGetRootWindow() function
* attempts to find the window IDs of
*
* 1) The background window that is behind the toplevel client
* windows; this is the window that we draw the toons on.
*
* 2) The parent window of the toplevel client windows; this is used
* by ToonLocateWindows() to build up a map of the space that the
* toons can occupy.
*
* In simple (sensible?) window managers (e.g. blackbox, sawfish, fvwm
* and countless others), both of these are the root window. The other
* more complex scenarios that ToonGetRootWindow() attempts to cope
* with are:
*
* Some `virtual' window managers (e.g. amiwm, swm and tvtwm) that
* reparent all client windows to a desktop window that sits on top of
* the root window. This desktop window is easy to find - we just look
* for a property __SWM_VROOT in the immediate children of the root
* window that contains the window ID of this desktop window. The
* desktop plays both roles (1 and 2 above). This functionality was
* detected in xpenguins 1.x with the vroot.h header file.
*
* Enlightenment (0.16) can have a number of desktops with different
* backgrounds; client windows on these are reparented, except for
* Desktop 0 which is the root window. Therefore versions less than
* 2.1 of xpenguins worked on Desktop 0 but not on any others. To fix
* this we look for a root-window property _WIN_WORKSPACE which
* contains the numerical index of the currently active desktop. The
* active desktop is then simply the immediate child of the root
* window that has a property ENLIGHTENMENT_DESKTOP set to this value.
*
* KDE 2.0: Oh dear. The kdesktop is a program separate from the
* window manager that launches a window which sits behind all the
* other client windows and has all the icons on it. Thus the other
* client windows are still children of the root window, but we want
* to draw to the uppermost window of the kdesktop. This is difficult
* to find - it is the great-great-grandchild of the root window and
* in KDE 2.0 has nothing to identify it from its siblings other than
* its size. KDE 2.1+ usefully implements the __SWM_VROOT property in
* a child of the root window, but the client windows are still
* children of the root window. A problem is that the penguins erase
* the desktop icons when they walk which is a bit messy. The icons
* are not lost - they reappear when the desktop window gets an expose
* event (i.e. move some windows over where they were and back again).
*
* Nautilus (GNOME 1.4+): Creates a background window to draw icons
* on, but does not reparent the client windows. The toplevel window
* of the desktop is indicated by the root window property
* NAUTILUS_DESKTOP_WINDOW_ID, but then we must descend down the tree
* from this toplevel window looking for subwindows that are the same
* size as the screen. The bottom one is the one to draw to. Hopefully
* one day Nautilus will implement __SWM_VROOT in exactly the same way
* as KDE 2.1+.
*
* Other cases: CDE, the common desktop environment. This is a
* commercial product that has been packaged with Sun (and other)
* workstations. It typically implements four virtual desktops but
* provides NO properties at all for apps such as xpenguins to use to
* work out where to draw to. Seeing as Sun are moving over to GNOME,
* CDE use is on the decline so I don't have any current plans to try
* and get xpenguins to work with it.
*
* As a note to developers of window managers and big screen hoggers
* like kdesktop, please visit www.freedesktop.org and implement their
* Extended Window Manager Hints spec that help pagers and apps like
* xpenguins and xearth to find their way around. In particular,
* please use the _NET_CURRENT_DESKTOP and _NET_VIRTUAL_ROOTS
* properties if you reparent any windows (e.g. Enlightenment). Since
* no window managers that I know yet use these particular hints, I
* haven't yet added any code to parse them. */
// #include "toon.h"
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xproto.h>
#include <stdio.h>
#include <string.h>
/* Time to throw up. Here is a kludgey function that recursively calls
* itself (up to a limit) to find the window ID of the KDE desktop to
* draw on. It works with KDE 2.0, but since KDE 2.0 is less stable
* than Windows 95, I don't expect many people to remain using it now
* that 2.1 is available, which implements __SWM_VROOT and makes this
* function redundant. This is the hierarchy we're trying to traverse:
*
* -> The root window
* 0 -> window with name="KDE Desktop"
* 1 -> window with no name
* 2 -> window with name="KDE Desktop" & _NET_WM_WINDOW_TYPE_DESKTOP
* 3 -> window with no name and width >= width of screen
*
* The last window in the hierarchy is the one to draw to. The
* numbers show the value of the `depth' argument. */
static
Window
__ToonGetKDEDesktop(Display *display, int screen, Window window,
Atom atom, char *atomname, int depth)
{
char *name = NULL;
Atom *wintype = NULL;
Window winreturn = 0;
unsigned long nitems, bytesafter;
Atom actual_type;
int actual_format;
Window rootReturn, parentReturn, *children;
unsigned int nChildren;
char go_deeper = 0;
if (XFetchName(display, window, &name)) {
if (strcasecmp(name, "KDE Desktop") == 0) {
/* Presumably either at depth 0 or 2 */
if (XGetWindowProperty(display, window, atom, 0, 1,
False, XA_ATOM,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &wintype) == Success
&& wintype) {
char *tmpatomname = XGetAtomName(display, *wintype);
if (tmpatomname) {
if (strcmp(atomname, tmpatomname) == 0 && depth == 2) {
/* OK, at depth 2 */
go_deeper = 1;
}
XFree((char *) tmpatomname);
}
}
else if (depth < 2) {
go_deeper = 1;
}
}
else if (depth == 1) {
go_deeper = 1;
}
XFree((char *) name);
}
else if (depth == 1) {
go_deeper = 1;
}
/* If go_deeper is 1 then there is a possibility that the background
* window is a descendant of the current window; otherwise we're
* barking up the wrong tree. */
if (go_deeper && XQueryTree(display, window, &rootReturn,
&parentReturn, &children,
&nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
/* children[i] is now at depth 3 */
if (depth == 2) {
XWindowAttributes attributes;
if (XGetWindowAttributes(display, children[i], &attributes)) {
if (attributes.width >= DisplayWidth(display, screen)/2
&& attributes.height > 0) {
/* Found it! */
winreturn = children[i];
break;
}
}
}
else if ((winreturn = __ToonGetKDEDesktop(display, screen,
children[i],
atom, atomname,
depth+1))) {
break;
}
}
XFree((char *) children);
}
return winreturn;
}
/* Look for the Nautilus desktop window to draw to, given the toplevel
* window of the Nautilus desktop. Basically recursively calls itself
* looking for subwindows the same size as the root window. */
static
Window
__ToonGetNautilusDesktop(Display *display, int screen, Window window,
int depth)
{
Window rootReturn, parentReturn, *children;
Window winreturn = window;
unsigned int nChildren;
if (depth > 5) {
return ((Window) 0);
}
else if (XQueryTree(display, window, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
XWindowAttributes attributes;
if (XGetWindowAttributes(display, children[i], &attributes)) {
if (attributes.width == DisplayWidth(display, screen)
&& attributes.height == DisplayHeight(display, screen)) {
/* Found a possible desktop window */
winreturn = __ToonGetNautilusDesktop(display, screen,
children[i], depth+1);
}
}
}
XFree((char *) children);
}
return winreturn;
}
/*
* Returns the window ID of the `background' window on to which the
* toons should be drawn. Also returned (in clientparent) is the ID of
* the parent of all the client windows, since this may not be the
* same as the background window. If no recognised virtual window
* manager or desktop environment is found then the root window is
* returned in both cases. The string toon_message contains
* information about the window manager that was found.
*/
Window
ToonGetRootWindow(Display *display, int screen, Window *clientparent)
{
Window background = 0; /* The return value */
Window root = RootWindow(display, screen);
Window rootReturn, parentReturn, *children;
Window *toplevel = (Window *) 0;
unsigned int nChildren;
unsigned long nitems, bytesafter;
Atom actual_type;
int actual_format;
unsigned long *workspace = NULL;
unsigned long *desktop = NULL;
Atom NAUTILUS_DESKTOP_WINDOW_ID = XInternAtom(display,
"NAUTILUS_DESKTOP_WINDOW_ID",
False);
*clientparent = root;
if (XGetWindowProperty(display, root,
NAUTILUS_DESKTOP_WINDOW_ID,
0, 1, False, XA_WINDOW,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &toplevel) == Success
&& toplevel) {
/* Nautilus is running */
background = __ToonGetNautilusDesktop(display, screen,
*toplevel, 0);
XFree((char *) toplevel);
if (background) {
printf("Drawing to Nautilus Desktop");
}
}
/* Next look for a virtual root or a KDE Desktop */
if (!background
&& XQueryTree(display, root, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
Atom _NET_WM_WINDOW_TYPE = XInternAtom(display,
"_NET_WM_WINDOW_TYPE",
False);
Atom __SWM_VROOT = XInternAtom(display, "__SWM_VROOT", False);
for (i = 0; i < nChildren && !background; ++i) {
Window *newroot = (Window *) 0;
if (XGetWindowProperty(display, children[i],
__SWM_VROOT, 0, 1, False, XA_WINDOW,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &newroot) == Success
&& newroot) {
/* Found a window with a __SWM_VROOT property that contains
* the window ID of the virtual root. Now we must check
* whether it is KDE (2.1+) or not. If it is KDE then it does
* not reparent the clients. If the root window has the
* _NET_SUPPORTED property but not the _NET_VIRTUAL_ROOTS
* property then we assume it is KDE. */
Atom _NET_SUPPORTED = XInternAtom(display,
"_NET_SUPPORTED",
False);
Atom *tmpatom;
if (XGetWindowProperty(display, root,
_NET_SUPPORTED, 0, 1, False,
XA_ATOM, &actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &tmpatom) == Success
&& tmpatom) {
Window *tmpwindow = (Window *) 0;
Atom _NET_VIRTUAL_ROOTS = XInternAtom(display,
"_NET_VIRTUAL_ROOTS",
False);
XFree((char *) tmpatom);
if (XGetWindowProperty(display, root,
_NET_VIRTUAL_ROOTS, 0, 1, False,
XA_WINDOW, &actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &tmpwindow) != Success
|| !tmpwindow) {
/* Must be KDE 2.1+ */
printf("Drawing to KDE Desktop");
background = *newroot;
}
else if (tmpwindow) {
XFree((char *) tmpwindow);
}
}
if (!background) {
/* Not KDE: assume windows are reparented */
printf("Drawing to virtual root window");
background = *clientparent = *newroot;
}
XFree((char *) newroot);
}
else if ((background = __ToonGetKDEDesktop(display, screen, children[i],
_NET_WM_WINDOW_TYPE,
"_NET_WM_WINDOW_TYPE_DESKTOP",
0))) {
/* Found a KDE 2.0 desktop and located the background window */
/* Note that the clientparent is still the root window */
printf( "Drawing to KDE desktop");
}
}
XFree((char *) children);
}
if (!background) {
/* Look for a _WIN_WORKSPACE property, used by Enlightenment */
Atom _WIN_WORKSPACE = XInternAtom(display, "_WIN_WORKSPACE", False);
if (XGetWindowProperty(display, root, _WIN_WORKSPACE,
0, 1, False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &workspace) == Success
&& workspace) {
/* Found a _WIN_WORKSPACE property - this is the desktop to look for.
* For now assume that this is Enlightenment.
* We're looking for a child of the root window that has an
* ENLIGHTENMENT_DESKTOP atom with a value equal to the root window's
* _WIN_WORKSPACE atom. */
Atom ENLIGHTENMENT_DESKTOP = XInternAtom(display,
"ENLIGHTENMENT_DESKTOP",
False);
/* First check to see if the root window is the current desktop... */
if (XGetWindowProperty(display, root,
ENLIGHTENMENT_DESKTOP, 0, 1,
False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &desktop) == Success
&& desktop && *desktop == *workspace) {
/* The root window is the current Enlightenment desktop */
printf( "Drawing to Enlightenment Desktop %lu (the root window)");
background = root;
XFree((char *) desktop);
}
/* Now look at each immediate child window of root to see if it is
* the current desktop */
else if (XQueryTree(display, root, &rootReturn, &parentReturn,
&children, &nChildren)) {
int i;
for (i = 0; i < nChildren; ++i) {
if (XGetWindowProperty(display, children[i],
ENLIGHTENMENT_DESKTOP, 0, 1,
False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytesafter,
(unsigned char **) &desktop) == Success
&& desktop && *desktop == *workspace) {
/* Found current Enlightenment desktop */
printf("Drawing to Enlightenment Desktop %lu");
background = *clientparent = children[i];
XFree((char *) desktop);
}
}
XFree((char *) children);
}
XFree((char *) workspace);
}
}
if (background) {
return background;
}
else {
return root;
}
}