/**
* @file Waimea.cc
* @author David Reveman <david@waimea.org>
* @date 02-May-2001 00:48:03
*
* @brief Implementation of Waimea and WindowObject classes
*
* Waimea Window Manager.
*
* Copyright (C) David Reveman. All rights reserved.
*
*/
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif // HAVE_CONFIG_H
extern "C" {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#ifdef SHAPE
# include <X11/extensions/shape.h>
#endif // SHAPE
#ifdef XINERAMA
# include <X11/extensions/Xinerama.h>
#endif // XINERAMA
#ifdef RANDR
# include <X11/extensions/Xrandr.h>
#endif // RANDR
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif // HAVE_STDIO_H
#ifdef STDC_HEADERS
# include <stdlib.h>
#endif // STDC_HEADERS
#ifdef HAVE_UNISTD_H
# include <sys/types.h>
# include <sys/wait.h>
# include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_SIGNAL_H
# include <signal.h>
#endif // HAVE_SIGNAL_H
}
#include <iostream>
using std::cerr;
using std::cout;
using std::endl;
#include "Waimea.hh"
Waimea *waimea;
char **argv;
bool hush;
int errors;
/**
* @fn Waimea(char **av)
* @brief Constructor for Waimea class
*
* Here we open a connection to the display and set signal and xerror
* handler functions. A list for all WaWindow is created and one list for all
* WaMenus. An hash_map is also created for effective window searching. Then
* we load the configuration file, menu file and actions file. We create one
* WaScreen for the displays default screen and an eventhandler for handling
* all events.
*
* @param av Vector of command line arguments
* @param _options Parsed command line options
*/
Waimea::Waimea(char **av, struct waoptions *_options) {
struct sigaction action;
int dummy;
argv = av;
options = _options;
XSetErrorHandler((XErrorHandler) xerrorhandler);
if (! (display = XOpenDisplay(options->display))) {
cerr << "error: can't open display: " << options->display << endl;
exit(1);
}
waimea = this;
hush = wmerr = false;
errors = 0;
eh = NULL;
timer = NULL;
action.sa_handler = signalhandler;
action.sa_mask = sigset_t();
action.sa_flags = SA_NOCLDSTOP | SA_NODEFER;
sigaction(SIGTERM, &action, NULL);
sigaction(SIGINT, &action, NULL);
sigaction(SIGCHLD, &action, NULL);
sigaction(SIGHUP, &action, NULL);
session_cursor = XCreateFontCursor(display, XC_left_ptr);
move_cursor = XCreateFontCursor(display, XC_fleur);
resizeleft_cursor = XCreateFontCursor(display, XC_ll_angle);
resizeright_cursor = XCreateFontCursor(display, XC_lr_angle);
#ifdef SHAPE
shape = XShapeQueryExtension(display, &shape_event, &dummy);
#endif // SHAPE
#ifdef XINERAMA
xinerama = XineramaQueryExtension(display, &dummy, &dummy);
if (xinerama)
xinerama = XineramaIsActive(display);
else
xinerama = false;
if (xinerama) {
xinerama_info = XineramaQueryScreens(display, &xinerama_info_num);
}
#endif // XINERAMA
#ifdef RANDR
randr = XRRQueryExtension(display, &randr_event, &dummy);
#endif // RANDR
rh = new ResourceHandler(this, options);
net = new NetHandler(this);
rh->LoadConfig(this);
int i, screens = 0;
WaScreen *ws;
for (i = 0; i < ScreenCount(display); ++i) {
if (screenmask & (1L << i)) {
ws = new WaScreen(display, i, this);
if (! wmerr) {
wascreen_list.push_back(ws);
screens++;
}
else wmerr = false;
}
}
if (! screens) {
cerr << "waimea: error: no managable screens found on display " <<
DisplayString(display) << endl;
exit(1);
}
eh = new EventHandler(this);
timer = new Timer(this);
}
/**
* @fn ~Waimea(void)
* @brief Destructor for Waimea class
*
* Deletes all WaScreens. Closes the connection to the display.
*/
Waimea::~Waimea(void) {
XSetErrorHandler(NULL);
LISTDEL(wascreen_list);
delete net;
delete rh;
MAPCLEAR(window_table);
if (eh) delete eh;
if (timer) delete timer;
delete [] pathenv;
XSync(display, false);
XCloseDisplay(display);
}
/**
* @fn FindWin(Window id, int mask)
* @brief Find WindowObject
*
* Returns WindowObject matching id and mask. NULL if no match was found.
*
* @param id Window id to use for matching
* @param mask Window type mask to use for matching
*
* @return Matching WindowObject
*/
WindowObject *Waimea::FindWin(Window id, int mask) {
map<Window, WindowObject *>::iterator it;
if ((it = window_table.find(id)) != window_table.end()) {
if (((*it).second)->type & mask)
return (*it).second;
}
return NULL;
}
/**
* @fn validatedrawable(Drawable d, unsigned int *w, unsigned int *h)
* @brief Validates if a drawable exists
*
* Tries to get geometry for the drawable. The drawable is valid
* if no XError is generated.
*
* @param d Resource ID used for drawable validation
* @param w Return the drawable's width
* @param h Return the drawable's height
*
* @return True if drawable is valid, otherwise false
*/
bool validatedrawable(Drawable d, unsigned int *w, unsigned int *h) {
int ret, _d;
unsigned int _ud;
Window _wd;
XSync(waimea->display, false);
errors = 0;
hush = 1;
if (w == NULL)
XGetGeometry(waimea->display, d, &_wd, &_d, &_d, &_ud, &_ud, &_ud,
&_ud);
else
XGetGeometry(waimea->display, d, &_wd, &_d, &_d, w, h, &_ud, &_ud);
XSync(waimea->display, false);
hush = 0;
ret = ( errors == 0 );
errors = 0;
return ret;
}
/**
* @fn validateclient_mapped(Window id)
* @brief Validates if a window exist and is mapped
*
* First we check that the window exists, if it exists we check the event
* queue so that there's no UnmapNotify event from the window in it. If there's
* no UnmapNotify event in the event queue then the window exists and is
* mapped.
*
* @param id Resource ID used for window validation
*
* @return True if window is valid, otherwise false
*/
const bool validateclient_mapped(Window id) {
XFlush(waimea->display);
XEvent e;
if (validatedrawable((Drawable) id)) {
if (XCheckTypedWindowEvent(waimea->display, id, UnmapNotify, &e)) {
XPutBackEvent(waimea->display, &e);
return false;
}
return true;
}
return false;
}
/**
* @fn waexec(const char *command, char *displaystring)
* @brief Executes a command line
*
* Executes a command line in the 'sh' shell.
*
* @param format Command line to execute
* @param displaystring Displaystring to export prior to execution
*/
void waexec(const char *command, char *displaystring) {
if (! fork()) {
setsid();
putenv(displaystring);
execl("/bin/sh", "/bin/sh", "-c", command, NULL);
exit(0);
}
}
/**
* @fn xerrorhandler(Display *d, XErrorEvent *e)
* @brief X error handler function
*
* Prints error message then a X error occurs.
*
* @param d X display
* @param e X error event
*
* @return always 0
*/
int xerrorhandler(Display *d, XErrorEvent *e) {
char buff[128];
map<Window, WindowObject *>::iterator it;
errors++;
if (! hush) {
XGetErrorDatabaseText(d, "XlibMessage", "XError", "", buff, 128);
cerr << buff;
XGetErrorText(d, e->error_code, buff, 128);
cerr << ": " << buff << endl;
XGetErrorDatabaseText(d, "XlibMessage", "MajorCode", "%d", buff, 128);
cerr << " ";
fprintf(stderr, buff, e->request_code);
sprintf(buff, "%d", e->request_code);
XGetErrorDatabaseText(d, "XRequest", buff, "%d", buff, 128);
cerr << " (" << buff << ")" << endl;
XGetErrorDatabaseText(d, "XlibMessage", "MinorCode", "%d", buff, 128);
cerr << " ";
fprintf(stderr, buff, e->minor_code);
cerr << endl;
XGetErrorDatabaseText(d, "XlibMessage", "ResourceID", "%d", buff, 128);
cerr << " ";
fprintf(stderr, buff, e->resourceid);
if (((it = waimea->window_table.find(e->resourceid)) !=
waimea->window_table.end()) &&
(((*it).second)->type == WindowType))
cerr << " (" << ((WaWindow *) (*it).second)->name << ")";
cerr << endl;
}
return 0;
}
/**
* @fn wmrunningerror(Display *d, XErrorEvent *)
* @brief X error handler function
*
* X error handler function used when setting input mask of root window.
* If X error occurs another window manager is running on that screen.
*
* @param d X display
*
* @return 0
*/
int wmrunningerror(Display *d, XErrorEvent *) {
waimea->wmerr = true;
return 0;
}
/**
* @fn signalhandler(int sig)
* @brief Signal handler function
*
* When one of the signals we handle arrives this function is called. Depending
* on what type of signal we received we do something, ex. restart, exit.
*
* @param sig The signal we received
*/
void signalhandler(int sig) {
int status;
switch(sig) {
case SIGINT:
case SIGTERM:
quit(EXIT_SUCCESS);
break;
case SIGHUP:
restart(NULL);
break;
case SIGCHLD:
waitpid(-1, &status, WNOHANG | WUNTRACED);
break;
default:
quit(EXIT_FAILURE);
}
}
/**
* @fn restart(char *command)
* @brief Restarts program
*
* Deletes the waimea object and restarts window manager.
*
* @param command Program name to execute when restarting
*/
void restart(char *command) {
char *tmp_argv[128];
char *__m_wastrdup_tmp;
if (command) {
commandline_to_argv(__m_wastrdup(command), tmp_argv);
delete waimea;
execvp(*tmp_argv, tmp_argv);
perror(*tmp_argv);
exit(EXIT_FAILURE);
} else
delete waimea;
execvp(argv[0], argv);
perror(argv[0]);
exit(EXIT_FAILURE);
}
/**
* @fn quit(void)
* @brief Ends program execution
*
* Deletes the waimea object and then ends program execution.
*
* @param status Return status
*/
void quit(int status) {
delete waimea;
exit(status);
}
/**
* @fn commandline_to_argv(char *s)
* @brief Parse command line to arguments
*
* Parses a command line string into an vector of arguments. Characters
* between '"' are parsed as one argument. Dangerous because it destroys
* command line string given as parameter.
*
* @param s Command line string
* @param tmp_argv Vector that should point to the different parameters
*
* @return argument vector
*/
char **commandline_to_argv(char *s, char **tmp_argv) {
int i;
for (i = 0;;) {
for (; *s == ' ' || *s == '\t'; s++);
if (*s == '"') {
tmp_argv[i++] = ++s;
for (; *s != '"' && *s != '\0'; s++);
if (*s == '\0') break;
*s = '\0';
s++;
if (*s == '\0') break;
}
else {
tmp_argv[i++] = s;
for (; *s != ' ' && *s != '\t' && *s != '\0'; s++);
if (*s == '\0') break;
*s = '\0';
s++;
}
}
tmp_argv[i] = NULL;
return tmp_argv;
}
/**
* @fn expand(char *org, WaWindow *w)
* @brief Window info expansion
*
* Expands a special window info characters in a string. Special info
* characters are:
* '\p' replaced with _NET_WM_PID hint for window
* '\n' replaced with name part of windows WM_CLASS hint
* '\c' replaced with class part of windows WM_CLASS hint
* '\h' replaced with WM_CLIENT_MACHINE hint
*
* @param org Original string used as source for expansion
* @param w WaWindow to get expansion info from
*
* @return Expanded version of original string, or NULL if no expansion have
* been made.
*/
char *expand(char *org, WaWindow *w) {
int i;
char *insert, *expanded, *tmp;
bool cont, found = false;
if (! org) return NULL;
expanded = org;
for (i = 0; expanded[i] != '\0';) {
cont = false;
for (; expanded[i] != '\0' && expanded[i] != '\\'; i++);
if (expanded[i] == '\0') break;
switch (expanded[i + 1]) {
case 'p':
if (w->pid) insert = w->pid;
else insert = "";
break;
case 'h':
if (w->host) insert = w->host;
else insert = "";
break;
case 'n':
if (w->classhint->res_name) insert = w->classhint->res_name;
else insert = "";
break;
case 'c':
if (w->classhint->res_class) insert = w->classhint->res_class;
else insert = "";
break;
default:
cont = true;
}
if (cont) continue;
int ilen = strlen(insert);
tmp = new char[strlen(expanded) + ilen + 1];
expanded[i] = '\0';
sprintf(tmp, "%s%s%s", expanded, insert, &expanded[i + 2]);
if (found) delete [] expanded;
else expanded[i] = '\\';
expanded = tmp;
found = true;
i += ilen;
}
if (found) return expanded;
else return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1