/* Waimea.cc
Copyright © 2003 David Reveman.
This file is part of Waimea.
Waimea 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, or (at your option) any later
version.
Waimea 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 Waimea; see the file COPYING. If not, write to the Free
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif // HAVE_CONFIG_H
extern "C" {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xlocale.h>
#include <cairo.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 RENDER
# include <X11/extensions/Xrender.h>
#endif // RENDER
#ifdef HAVE_STDIO_H
# include <stdio.h>
#endif // HAVE_STDIO_H
#ifdef STDC_HEADERS
# include <stdlib.h>
#endif // STDC_HEADERS
#ifdef HAVE_SYS_TYPES_H
# include <sys/types.h>
#endif // HAVE_SYS_TYPES_H
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif // HAVE_SYS_WAIT_H
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif // HAVE_UNISTD_H
#ifdef HAVE_ERRNO_H
# include <errno.h>
#endif // HAVE_ERRNO_H
#ifdef HAVE_CODESET
# include <langinfo.h>
#endif // HAVE_CODESET
}
#include "Waimea.hh"
#include "Window.hh"
Waimea *waimea = NULL;
char *waimea_pathenv;
int grab_count = 0;
char **argv;
bool hush;
int errors;
XClientMessageEvent cme;
list<DWindowObject *> __render_list;
#ifdef THREAD
unsigned int __render_thread_count;
pthread_mutex_t __render_mutex;
pthread_cond_t __render_cond;
unsigned int __render_count;
pthread_mutex_t __render_list_mutex;
pthread_cond_t __render_list_cond;
#endif // THREAD
Waimea::Waimea(char **av, char **_options) {
struct sigaction action;
int dummy;
EventDetail ed;
char render_info[256];
render_info[0] = '\0';
config_info[0] = '\0';
dummy = 0;
argv = av;
options = _options;
running = false;
#ifdef THREAD
__render_thread_count = 0;
if (! XInitThreads()) {
cerr << "error: xlib is not thread-safe" << endl;
exit(1);
}
#endif // THREAD
XSetErrorHandler((XErrorHandler) xerrorhandler);
if (! (display = XOpenDisplay(options[ARG_DISPLAY]))) {
cerr << "error: can't open display: " << options[ARG_DISPLAY] << endl;
exit(1);
}
#ifdef DEBUG
XSynchronize(display, true);
#endif // DEBUG
waimea = this;
hush = wmerr = false;
errors = 0;
eh = NULL;
timer = NULL;
quit_signal = restart_signal = unknown_signal = false;
prefocus = (Window) 0;
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);
action_create_tsts();
render_create_tsts();
parser_create_tsts();
setlocale(LC_ALL, "");
#ifdef HAVE_ICONV
#ifdef HAVE_CODESET
char *fromcode = nl_langinfo(CODESET);
#else // !HAVE_CODESET
char *fromcode = "ASCII";
#endif // HAVE_CODESET
utf8conv = iconv_open(INTERNALCODE, fromcode);
if (utf8conv == (iconv_t) -1) {
utf8conv = (iconv_t) 0;
WARNING; perror(NULL);
}
#endif // HAVE_ICONV
cursor = new WaCursor(display);
#ifdef SHAPE
shape_event = 0;
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_event = 0;
randr = XRRQueryExtension(display, &randr_event, &dummy);
#endif // RANDR
rh = new ResourceHandler(this, options);
net = new NetHandler(this);
rh->loadConfig(this);
if (! client_side_rendering) {
if (XRenderQueryExtension(display, &dummy, &dummy)) {
int major, minor;
XRenderQueryVersion(display, &major, &minor);
if (major == 0 && minor < 6) {
snprintf(render_info, 256,
"RENDER extension version on display %s "
"is %d.%d. Version 0.6 is required for server-side "
"rendering. Client-side rendering forced.",
DisplayString(display), major, minor);
client_side_rendering = true;
}
} else {
snprintf(render_info, 256, "RENDER extension missing on display"
"%s. Client-side rendering forced.",
DisplayString(display));
client_side_rendering = true;
}
}
eh = new EventHandler(this);
timer = new Timer(this);
#ifdef THREAD
__render_count = 0;
if (__render_thread_count) {
pthread_mutex_init(&__render_mutex, NULL);
pthread_cond_init(&__render_cond, NULL);
pthread_mutex_init(&__render_list_mutex, NULL);
pthread_cond_init(&__render_list_cond, NULL);
}
#endif // THREAD
XDisplayKeycodes(display, &min_key, &max_key);
getModifierMappings();
int i, screens = 0;
for (i = 0; i < ScreenCount(display); ++i) {
if (screenmask & (1L << i)) {
WaScreen *ws = new WaScreen(display, i, this);
if (! wmerr) {
ws->commonStyleUpdate();
wascreen_list.push_back(ws);
screens++;
} else {
delete ws;
wmerr = false;
}
}
}
if (! screens) {
ERROR << "no managable screens found on display " <<
DisplayString(display) << endl;
exit(1);
}
if (*render_info != '\0')
wascreen_list.front()->showInfoMessage(__FUNCTION__, render_info);
if (*config_info != '\0')
wascreen_list.front()->showInfoMessage(__FUNCTION__, config_info);
XrmDestroyDatabase(rh->database);
running = true;
ed.type = MapRequest;
ed.detail = 0;
ed.x11mod = ed.wamod = 0;
list<WaScreen *>::iterator it = wascreen_list.begin();
for (; it != wascreen_list.end(); it++)
eh->evAct(NULL, (*it)->id, &ed);
#ifdef THREAD
if (__render_thread_count) {
pthread_t t;
pthread_attr_t attr;
struct sched_param param;
param.sched_priority = render_thread_prio;
pthread_attr_init(&attr);
pthread_attr_setschedparam(&attr, ¶m);
for (unsigned int i = 0; i < __render_thread_count; i++)
pthread_create(&t, &attr, render_thread_func, NULL);
pthread_attr_destroy(&attr);
}
#endif // THREAD
}
Waimea::~Waimea(void) {
running = false;
delete timer;
RENDER_GET;
LISTDEL(wascreen_list);
XSync(display, false);
delete rh;
delete cursor;
#ifndef RANDR
XCloseDisplay(display); /* XXX: crashes if linked to librandr */
#endif // RANDR
}
void Waimea::getModifierMappings(void) {
while (! modmaps.empty()) {
delete [] modmaps.back()->name;
delete modmaps.back();
modmaps.pop_back();
}
const XModifierKeymap* const modmap = XGetModifierMapping(display);
if (modmap && modmap->max_keypermod > 0) {
const int mask_table[] = {
ShiftMask, LockMask, ControlMask, Mod1Mask,
Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask
};
const size_t size = (sizeof(mask_table) / sizeof(mask_table[0])) *
modmap->max_keypermod;
for (size_t i = 0; i < size; ++i) {
if (! modmap->modifiermap[i]) continue;
KeySym ksym = XKeycodeToKeysym(display, modmap->modifiermap[i], 0);
if (ksym) {
char *kstring = XKeysymToString(ksym);
if (kstring) {
int modmask = mask_table[i / modmap->max_keypermod];
ModifierMap *mm = new ModifierMap;
mm->name = WA_STRDUP(kstring);
mm->modifier = modmask;
modmaps.push_back(mm);
}
}
}
if (modmap) XFreeModifiermap(const_cast<XModifierKeymap*>(modmap));
}
}
WindowObject *Waimea::findWin(Window id, long 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;
}
bool Waimea::focusNew(Window win, bool make_vis) {
bool status = false;
map<Window, WindowObject *>::iterator it;
if ((it = window_table.find(win)) != window_table.end()) {
switch (((*it).second)->type) {
case RootType: {
WaScreen *ws = (WaScreen *) (*it).second;
if (ws->style && (! ws->style->focusable)) return status;
ws->focused = true;
prefocus = win;
XInstallColormap(display, ws->colormap);
XSetInputFocus(display, win, RevertToPointerRoot, CurrentTime);
status = true;
} break;
case WindowType: {
WaWindow *ww = (WaWindow *) (*it).second;
int newvx, newvy, x, y;
XEvent e;
if (ww->master) ww->hidden = ww->master->hidden;
if ((! (ww->wstate & StateFocusableMask)) ||
((! make_vis) && (ww->hidden ||
(ww->wstate & StateMinimizedMask))))
return status;
if (ww->mapped) {
if (make_vis) {
if (ww->wstate & StateMinimizedMask) ww->unMinimize();
if (! (ww->desktop_mask &
(1L << ww->ws->current_desktop->number))) {
list<Desktop *>::iterator dit =
ww->ws->desktop_list.begin();
for (; dit != ww->ws->desktop_list.end(); dit++)
if (ww->desktop_mask &
(1L << (*dit)->number)) {
ww->ws->goToDesktop((*dit)->number);
break;
}
}
bool x_toolarge = false, y_toolarge = false;
if ((ww->ws->v_x + ww->attrib.x) >=
(ww->ws->v_xmax + (int) ww->ws->width))
x_toolarge = true;
if ((ww->ws->v_y + ww->attrib.y) >=
(ww->ws->v_ymax + (int) ww->ws->height))
y_toolarge = true;
if (x_toolarge || y_toolarge) {
int th = ww->attrib.height + ww->bottom_spacing;
int tw = ww->attrib.width + ww->right_spacing;
if (x_toolarge)
ww->attrib.x =
(ww->ws->v_xmax + ww->ws->width -
ww->ws->v_x) - tw;
if (y_toolarge)
ww->attrib.y =
(ww->ws->v_ymax + ww->ws->height -
ww->ws->v_y) - th;
ww->redrawWindow();
}
if (ww->attrib.x >= (int) ww->ws->width ||
ww->attrib.y >= (int) ww->ws->height ||
(ww->attrib.x + (int) ww->attrib.width) <= 0 ||
(ww->attrib.y + (int) ww->attrib.height) <= 0) {
x = ww->ws->v_x + ww->attrib.x;
y = ww->ws->v_y + ww->attrib.y;
newvx = (x / ww->ws->width) * ww->ws->width;
newvy = (y / ww->ws->height) * ww->ws->height;
ww->ws->moveViewportTo(newvx, newvy);
XSync(display, false);
while (XCheckTypedEvent(display, EnterNotify, &e));
}
if (ww->mergedback) ww->toFront();
} else if (ww->mergedback) return status;
XInstallColormap(display, ww->attrib.colormap);
if (ww->input_field) {
prefocus = ww->id;
XSetInputFocus(display, ww->id,
RevertToPointerRoot,
CurrentTime);
status = true;
} else if (ww->protocol_mask & TakeFocusProtocalMask) {
prefocus = ww->id;
e.type = ClientMessage;
e.xclient.window = ww->id;
e.xclient.message_type = net->wm_protocols;
e.xclient.format = 32;
e.xclient.data.l[0] = net->wm_take_focus;
/* XXX: we shouldn't use CurrentTime here */
e.xclient.data.l[1] = CurrentTime;
XSendEvent(display, ww->id, false, NoEventMask, &e);
status = true;
} else {
/* XXX: no input window shouldn't get input focus
according to ICCCM 2.0. We give it input focus
anyway, is this bad? */
prefocus = ww->id;
XSetInputFocus(display, ww->id,
RevertToPointerRoot,
CurrentTime);
status = true;
}
} else
ww->want_focus = true;
} break;
case MenuItemType: {
MenuItem *mi = (MenuItem *) (*it).second;
if (mi->style && (! mi->style->focusable)) return status;
if (mi->menu->mapped) {
mi->focused = true;
mi->menu->focus = prefocus = mi->id;
XSetInputFocus(display, mi->id, RevertToPointerRoot,
CurrentTime);
status = true;
} break;
}
}
}
return status;
}
void Waimea::addToFocusHistory(Window win) {
if (win == None) return;
focus_history.remove(win);
focus_history.push_front(win);
}
void Waimea::removeFromFocusHistory(Window win) {
if (win == None) return;
focus_history.remove(win);
}
void Waimea::focusRevertFrom(WaScreen *ws, Window win) {
list<Window>::iterator it = focus_history.begin();
for (;it != focus_history.end(); it++) {
if (ws->config.revert_to_window && *it == ws->id)
continue;
if (*it != win) {
if (focusNew(*it)) break;
}
}
if (it == focus_history.end()) {
list<WaWindow *>::iterator wit = ws->wawindow_list.begin();
for (; wit != ws->wawindow_list.end(); wit++)
if (! (*wit)->hidden &&
(! ((*wit)->wstate & StateMinimizedMask))) {
if (focusNew((*wit)->id))
break;
}
if (wit == ws->wawindow_list.end())
focusNew(ws->id);
}
}
void wa_grab_server(void) {
if (grab_count == 0) {
grab_count++;
XGrabServer(waimea->display);
}
#ifdef DEBUG
else {
WARNING << "server allready grabbed" << endl;
abort();
}
#endif // DEBUG
}
void wa_ungrab_server(void) {
#ifdef DEBUG
if (grab_count == 0) {
WARNING << "server not grabbed" << endl;
abort();
} else if (grab_count != 1) {
WARNING << "server grabbed more than once" << endl;
abort();
}
#endif // DEBUG
if (grab_count > 0) {
XUngrabServer(waimea->display);
grab_count = 0;
}
}
bool validate_drawable(Drawable d, unsigned int *w, unsigned int *h) {
int ret, _d;
unsigned int _ud;
Window _wd;
XSync(waimea->display, false);
XEvent e;
if (XCheckTypedWindowEvent(waimea->display, d, DestroyNotify, &e)) {
XPutBackEvent(waimea->display, &e);
return false;
}
errors = 0;
hush = 1;
if (w && h)
XGetGeometry(waimea->display, d, &_wd, &_d, &_d, w, h, &_ud, &_ud);
else
XGetGeometry(waimea->display, d, &_wd, &_d, &_d, &_ud, &_ud, &_ud,
&_ud);
XSync(waimea->display, false);
hush = 0;
ret = ( errors == 0 );
errors = 0;
return ret;
}
const bool validate_window_mapped(Window id) {
XSync(waimea->display, false);
XEvent e;
if (XCheckTypedWindowEvent(waimea->display, id, DestroyNotify, &e) ||
XCheckTypedWindowEvent(waimea->display, id, UnmapNotify, &e)) {
XPutBackEvent(waimea->display, &e);
return false;
}
return true;
}
int xerrorhandler(Display *d, XErrorEvent *e) {
char buff[128];
map<Window, WindowObject *>::iterator it;
errors++;
if (! hush) {
bool error_output;
switch (e->request_code) {
case X_CreateWindow:
case X_DestroyWindow:
case X_CreatePixmap:
case X_FreePixmap:
error_output = true;
break;
default:
error_output = false;
break;
}
#ifdef DEBUG
bool error_abort = error_output;
error_output = true;
#endif // DEBUG
if (error_output) {
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()) {
if (((*it).second)->type == WindowType) {
if (error_output) {
cerr << " (" << ((WaWindow *) (*it).second)->name << ")";
}
((WaWindow *) (*it).second)->deleted = true;
} else if (((*it).second)->type == DockAppType) {
if (error_output) {
cerr << " (" << ((Dockapp *) (*it).second)->name << ")";
}
((Dockapp *) (*it).second)->deleted = true;
}
}
if (error_output) cerr << endl;
#ifdef DEBUG
if (error_abort) abort();
#endif // DEBUG
}
return 0;
}
int wmrunningerror(Display *, XErrorEvent *) {
waimea->wmerr = true;
return 0;
}
void signalhandler(int sig) {
int status;
switch (sig) {
case SIGCHLD:
waitpid(-1, &status, WNOHANG | WUNTRACED);
break;
case SIGINT:
waimea->quit_signal = true;
break;
case SIGHUP:
waimea->restart_signal = true;
break;
default:
waimea->unknown_signal = true;
break;
}
}
void restart(char *command) {
char *tmp_argv[128];
cme.type = ClientMessage;
cme.display = waimea->display;
cme.format = 32;
cme.window = waimea->wascreen_list.front()->id;
cme.data.l[0] = waimea->wascreen_list.front()->id;
cme.data.l[1] = RestartNotify;
cme.data.l[2] = cme.data.l[3] = cme.data.l[4] = 0l;
waimea->eh->handleEvent((XEvent *) &cme);
if (command) {
commandline_to_argv(WA_STRDUP(command), tmp_argv, 128);
waimea->running = false;
delete waimea;
execvp(*tmp_argv, tmp_argv);
WARNING;
perror(*tmp_argv);
exit(EXIT_FAILURE);
} else
delete waimea;
execvp(argv[0], argv);
perror(argv[0]);
WARNING;
exit(EXIT_FAILURE);
}
void quit(int status) {
cme.type = ClientMessage;
cme.display = waimea->display;
cme.format = 32;
cme.window = waimea->wascreen_list.front()->id;
cme.data.l[0] = waimea->wascreen_list.front()->id;
cme.data.l[1] = RestartNotify;
cme.data.l[2] = cme.data.l[3] = cme.data.l[4] = 0l;
waimea->eh->handleEvent((XEvent *) &cme);
waimea->running = false;
delete waimea;
exit(status);
}
char *expand(char *org, WaWindow *w, MenuItem *m,
const char *warning_function, char *warning_message,
MenuItem *m2) {
int i;
char *insert, *expanded, *tmp;
bool cont, found = false;
char insert_buf[1024];
int offset;
if (! org) return NULL;
expanded = org;
for (i = 0; expanded[i] != '\0';) {
for (; expanded[i] != '\0' && expanded[i] != '%'; i++);
if (expanded[i] == '\0') break;
insert = "";
offset = 2;
cont = true;
if (w) {
cont = false;
switch (expanded[i + 1]) {
case 'r': {
insert = w->rid;
} break;
case 't':
insert = w->name;
break;
case 'P':
insert = w->pid;
break;
case 's':
insert = w->host;
break;
case 'c':
insert = w->wclass;
break;
case 'C':
insert = w->wclassname;
break;
case 'x':
snprintf(insert_buf, 1024, "%d", w->frame->attrib.x);
insert = insert_buf;
break;
case 'y':
snprintf(insert_buf, 1024, "%d", w->frame->attrib.y);
insert = insert_buf;
break;
case 'X':
snprintf(insert_buf, 1024, "%d",
w->ws->v_x + w->frame->attrib.x);
insert = insert_buf;
break;
case 'Y':
snprintf(insert_buf, 1024, "%d",
w->ws->v_y + w->frame->attrib.y);
insert = insert_buf;
break;
case 'w':
snprintf(insert_buf, 1024, "%d", w->attrib.width /
w->size.width_inc);
insert = insert_buf;
break;
case 'h':
snprintf(insert_buf, 1024, "%d", w->attrib.height /
w->size.height_inc);
insert = insert_buf;
break;
case 'W':
snprintf(insert_buf, 1024, "%d", w->attrib.width);
insert = insert_buf;
break;
case 'H':
snprintf(insert_buf, 1024, "%d", w->attrib.height);
insert = insert_buf;
break;
default:
cont = true;
}
}
if (m && cont) {
cont = false;
char *exp2;
switch (expanded[i + 1]) {
case 'm':
if (m->str_dynamic) {
exp2 = expand(m->str, w, NULL, NULL, NULL, m);
strncpy(insert_buf, exp2, 1023);
insert_buf[1023] = '\0';
delete [] exp2;
insert = insert_buf;
} else
insert = m->str;
break;
case 'M':
if (m->str2_dynamic) {
exp2 = expand(m->str2, w, NULL, NULL, NULL, m);
strncpy(insert_buf, exp2, 1023);
insert_buf[1023] = '\0';
delete [] exp2;
insert = insert_buf;
} else
insert = m->str2;
break;
default:
cont = true;
}
}
if (m2 && cont) {
cont = false;
switch (expanded[i + 1]) {
case 'S':
if (m2->monitor_state) {
if (m2->mstate)
insert = "true";
else
insert = "false";
}
break;
default:
cont = true;
}
}
if (cont) {
cont = false;
switch (expanded[i + 1]) {
case 'd': {
char *format = NULL;
char tmp_char = expanded[i + 2];
if (expanded[i + 2] == '(') {
format = &expanded[i + 3];
for (offset = 3; expanded[i + offset] != '\0' &&
expanded[i + offset] != ')'; offset++);
tmp_char = expanded[i + offset];
if (tmp_char == ')')
expanded[i + offset] = '\0';
}
#ifdef HAVE_STRFTIME
time_t ttmp = time(NULL);
if (ttmp != -1) {
#ifdef THREAD
# ifdef HAVE_LOCALTIME_R
struct tm tt_r;
localtime_r(&ttmp, &tt_r);
struct tm *tt = &tt_r;
# else // !HAVE_LOCALTIME_R
struct tm *tt = localtime(&ttmp);
# endif // HAVE_LOCALTIME_R
#else // !THREAD
struct tm *tt = localtime(&ttmp);
#endif // THREAD
if (tt) {
int len = 0;
if (format)
len = strftime(insert_buf, 1024, format, tt);
if (len == 0) {
char def[3];
def[0] = '%';
def[1] = 'c';
def[2] = '\0';
strftime(insert_buf, 1024, def, tt);
}
insert = insert_buf;
}
}
#else // !HAVE_STRFTIME
insert = "00:00";
#endif // HAVE_STRFTIME
expanded[i + offset] = tmp_char;
if (tmp_char != '\0') offset += 1;
} break;
case 'f':
if (warning_function)
insert = (char *) warning_function;
break;
case 'm':
if (warning_message)
insert = warning_message;
break;
default:
cont = true;
}
}
int ilen = strlen(insert);
tmp = new char[strlen(expanded) + ilen + 1];
expanded[i] = '\0';
sprintf(tmp, "%s%s%s", expanded, insert, &expanded[i + offset]);
if (found) delete [] expanded;
else expanded[i] = '%';
expanded = tmp;
found = true;
i += ilen;
}
if (found) return expanded;
else return WA_STRDUP(org);
}
char *preexpand(char *org, bool *dynamic) {
int i;
char *insert, *expanded, *tmp;
bool cont, found = false;
int offset;
char insert_buf[1024];
*dynamic = 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;
if (expanded[i + 1] == '\0') break;
insert = "";
offset = 2;
switch (expanded[i + 1]) {
case 'v':
insert = VERSION;
break;
case 'p':
insert = PACKAGE;
break;
case 'D':
insert = __DATE__;
break;
case 'T':
insert = __TIME__;
break;
case 'E': {
char *extstr = insert_buf;
extstr[0] = '\0';
if (! waimea->client_side_rendering)
extstr += sprintf(extstr, "RENDER ");
#ifdef SHAPE
if (waimea->shape)
extstr += sprintf(extstr, "SHAPE ");
#endif // SHAPE
#ifdef XINERAMA
if (waimea->xinerama)
extstr += sprintf(extstr, "XINERAMA ");
#endif // XINERAMA
#ifdef RANDR
if (waimea->randr)
extstr += sprintf(extstr, "RANDR ");
#endif // RANDR
insert = insert_buf;
} break;
case 'F':
insert = ""
#ifdef THREAD
"thread "
#endif // THREAD
#ifdef SHAPE
"shape "
#endif // SHAPE
#ifdef XINERAMA
"xinerama "
#endif // XINERAMA
#ifdef RANDR
"randr "
#endif // RANDR
#ifdef COMPOSITE
"composite "
#endif // COMPOSITE
#ifdef PNG
"png "
#endif // PNG
#ifdef SVG
"svg "
#endif // SVG
#ifdef XCURSOR
"xcursor "
#endif // XCURSOR
;
break;
case 'e': {
char *env_var = NULL;
char tmp_char = expanded[i + 2];
if (expanded[i + 2] == '(') {
env_var = &expanded[i + 3];
for (offset = 3; expanded[i + offset] != '\0' &&
expanded[i + offset] != ')'; offset++);
tmp_char = expanded[i + offset];
if (tmp_char == ')')
expanded[i + offset] = '\0';
}
insert = NULL;
if (env_var) insert = getenv(env_var);
if (! insert) insert = "";
expanded[i + offset] = tmp_char;
if (tmp_char != '\0') offset += 1;
} break;
case 'u':
insert = getenv("USER");
if (! insert) insert = "";
break;
case 'n':
insert = "\n";
break;
case 'd':
if (expanded[i + 2] == '(') {
for (offset = 3; expanded[i + offset] != '\0' &&
expanded[i + offset] != ')'; offset++);
i += offset;
} else
i += 2;
*dynamic = true;
cont = true;
break;
case '%':
insert = "%";
break;
default:
if (expanded[i + 1] == 'r' || expanded[i + 1] == 't' ||
expanded[i + 1] == 'P' || expanded[i + 1] == 's' ||
expanded[i + 1] == 'T' || expanded[i + 1] == 'c' ||
expanded[i + 1] == 'C' || expanded[i + 1] == 'x' ||
expanded[i + 1] == 'y' || expanded[i + 1] == 'X' ||
expanded[i + 1] == 'Y' || expanded[i + 1] == 'w' ||
expanded[i + 1] == 'h' || expanded[i + 1] == 'W' ||
expanded[i + 1] == 'H' || expanded[i + 1] == 'm' ||
expanded[i + 1] == 'M' || expanded[i + 1] == 'S' ||
expanded[i + 1] == 'f') {
*dynamic = true;
i += 2;
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 + offset]);
if (found) delete [] expanded;
else expanded[i] = '%';
expanded = tmp;
found = true;
i += ilen;
}
if (found) return expanded;
else return WA_STRDUP(org);
}
#ifdef HAVE_ICONV
#ifndef ICONV_CONST
# define ICONV_CONST
#endif // !ICONV_CONST
char *wa_locale_to_utf8(const char *str) {
char *dest;
char *outp;
const char *p;
size_t len;
size_t inbytes_remaining;
size_t outbytes_remaining;
size_t err;
size_t outbuf_size;
bool have_error = false;
len = strlen(str);
p = str;
inbytes_remaining = len;
outbuf_size = len + 1;
outbytes_remaining = outbuf_size - 1;
outp = dest = new char[outbuf_size];
again:
err = iconv(waimea->utf8conv, (ICONV_CONST char **) &p,
&inbytes_remaining, &outp, &outbytes_remaining);
if (err == (size_t) -1) {
switch (errno) {
case EINVAL:
break;
case E2BIG: {
size_t used = outp - dest;
size_t newsize = outbuf_size * 2;
char *newdest = new char[newsize];
memcpy(newdest, dest, outbuf_size);
delete [] dest;
outbuf_size = newsize;
dest = newdest;
outp = dest + used;
outbytes_remaining = outbuf_size - used - 1;
goto again;
}
case EILSEQ:
WARNING_FUNC <<
"invalid byte sequence in conversion input" << endl;
have_error = true;
break;
default:
WARNING_FUNC << "error during conversion: ";
perror(NULL);
have_error = true;
break;
}
}
*outp = '\0';
if (have_error) {
delete [] dest;
return NULL;
} else
return dest;
}
#else
char *wa_locale_to_utf8(const char *str) {
return WA_STRDUP((char *) str);
}
#endif // HAVE_ICONV
#ifdef THREAD
void *render_thread_func(void *) {
sigset_t sigset;
cairo_t *cr = cairo_create();
/* XXX: cairo need a call to this function for text support to be
initialized, will probably dissapear soon. */
cairo_set_target_drawable(cr, waimea->display,
DefaultRootWindow(waimea->display));
sigemptyset(&sigset);
sigaddset(&sigset, SIGALRM);
pthread_sigmask(SIG_BLOCK, &sigset, NULL);
for (;;) {
RENDER_LIST_GET;
DWindowObject *dw = __render_list.front();
if (! dw) {
RENDER_LIST_RELEASE;
continue;
}
__render_list.pop_front();
list<DWindowObject *>::iterator it = __render_list.begin();
while (it != __render_list.end()) {
if (*it == dw) it = __render_list.erase(it);
else it++;
}
RENDER_LIST_RELEASE;
RENDER_LOCK;
__render_count++;
RENDER_RELEASE;
DWIN_RENDER_LOCK(dw);
dw->decor_root->__win__render_count++;
DWIN_RENDER_RELEASE_BY_THREAD(dw);
dw->renderWindow(cr);
XSync(waimea->display, false);
DWIN_RENDER_LOCK(dw);
dw->decor_root->__win__render_count--;
if (dw->decor_root->__win__render_count == 0) {
DWIN_RENDER_BROADCAST(dw);
} else
DWIN_RENDER_RELEASE_BY_THREAD(dw);
RENDER_LOCK;
__render_count--;
if (__render_count == 0) { RENDER_BROADCAST; }
else RENDER_RELEASE;
}
}
#endif // THREAD
syntax highlighted by Code2HTML, v. 0.9.1