/* 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 #include #include #include #ifdef SHAPE # include #endif // SHAPE #ifdef XINERAMA # include #endif // XINERAMA #ifdef RANDR # include #endif // RANDR #ifdef RENDER # include #endif // RENDER #ifdef HAVE_STDIO_H # include #endif // HAVE_STDIO_H #ifdef STDC_HEADERS # include #endif // STDC_HEADERS #ifdef HAVE_SYS_TYPES_H # include #endif // HAVE_SYS_TYPES_H #ifdef HAVE_SYS_WAIT_H # include #endif // HAVE_SYS_WAIT_H #ifdef HAVE_UNISTD_H # include #endif // HAVE_UNISTD_H #ifdef HAVE_ERRNO_H # include #endif // HAVE_ERRNO_H #ifdef HAVE_CODESET # include #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 __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::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(modmap)); } } WindowObject *Waimea::findWin(Window id, long int mask) { map::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::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::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::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::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::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::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