/** Startup tray buttons. */ void StartupTrayButtons() { TrayButtonType *bp; for(bp = buttons; bp; bp = bp->next) { if(bp->label) { bp->cp->requestedWidth = GetStringWidth(FONT_TRAYBUTTON, bp->label) + 4; bp->cp->requestedHeight = GetStringHeight(FONT_TRAYBUTTON); } else { bp->cp->requestedWidth = 0; bp->cp->requestedHeight = 0; } if(bp->iconName) { bp->icon = LoadNamedIcon(bp->iconName); if(JLIKELY(bp->icon)) { bp->cp->requestedWidth += bp->icon->image->width; bp->cp->requestedHeight += bp->icon->image->height; } else { Warning(_("could not load tray icon: \"%s\""), bp->iconName); } } bp->cp->requestedWidth += 2 * BUTTON_SIZE; bp->cp->requestedHeight += 2 * BUTTON_SIZE; } }
/** Read the WM state for a window. */ void ReadWMState(Window win, ClientState *state) { Status status; unsigned long count; unsigned long extra; Atom realType; int realFormat; unsigned long *temp; status = JXGetWindowProperty(display, win, atoms[ATOM_WM_STATE], 0, 2, False, atoms[ATOM_WM_STATE], &realType, &realFormat, &count, &extra, (unsigned char**)&temp); if(JLIKELY(status == Success && realFormat == 32 && count == 2)) { switch(temp[0]) { case IconicState: state->status |= STAT_MINIMIZED; break; case WithdrawnState: state->status &= ~STAT_MAPPED; break; default: break; } JXFree(temp); } }
/** Parse move mode. */ void ParseMoveMode(const TokenNode *tp) { const char *str; str = FindAttribute(tp->attributes, COORDINATES_ATTRIBUTE); if(str) { settings.moveStatusType = ParseStatusWindowType(tp, str); } str = FindAttribute(tp->attributes, DELAY_ATTRIBUTE); if(str) { settings.desktopDelay = ParseUnsigned(tp, str); } if(JLIKELY(tp->value)) { if(!strcmp(tp->value, OUTLINE_VALUE)) { settings.moveMode = MOVE_OUTLINE; } else if(!strcmp(tp->value, OPAQUE_VALUE)) { settings.moveMode = MOVE_OPAQUE; } else { ParseError(tp, "invalid move mode: %s", tp->value); } } else { ParseError(tp, "move mode not specified"); } }
/** Parse a clock tray component. */ void ParseClock(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; const char *format; const char *zone; const char *temp; int width, height; Assert(tp); Assert(tray); format = FindAttribute(tp->attributes, "format"); zone = FindAttribute(tp->attributes, "zone"); temp = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(temp) { width = ParseUnsigned(tp, temp); } else { width = 0; } temp = FindAttribute(tp->attributes, HEIGHT_ATTRIBUTE); if(temp) { height = ParseUnsigned(tp, temp); } else { height = 0; } cp = CreateClock(format, zone, width, height); if(JLIKELY(cp)) { ParseTrayComponentActions(tp, cp, AddClockAction); AddTrayComponent(tray, cp); } }
/** Parse a spacer tray component. */ void ParseSpacer(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; int width; int height; char *str; Assert(tp); Assert(tray); /* Get the width. */ str = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(str) { width = ParseUnsigned(tp, str); } else { width = 0; } /* Get the height. */ str = FindAttribute(tp->attributes, HEIGHT_ATTRIBUTE); if(str) { height = ParseUnsigned(tp, str); } else { height = 0; } /* Create the spacer. */ cp = CreateSpacer(width, height); if(JLIKELY(cp)) { AddTrayComponent(tray, cp); } }
/** Startup tray buttons. */ void StartupTrayButtons(void) { TrayButtonType *bp; for(bp = buttons; bp; bp = bp->next) { if(bp->label) { bp->cp->requestedWidth = GetStringWidth(FONT_TRAY, bp->label) + 4; bp->cp->requestedHeight = GetStringHeight(FONT_TRAY); } else { bp->cp->requestedWidth = 0; bp->cp->requestedHeight = 0; } if(bp->iconName) { bp->icon = LoadNamedIcon(bp->iconName, 1, 1); if(JLIKELY(bp->icon)) { bp->cp->requestedWidth += bp->icon->images->width + 4; if(bp->label) { bp->cp->requestedWidth -= 2; } bp->cp->requestedHeight = Max(bp->icon->images->height + 4, bp->cp->requestedHeight); } else { Warning(_("could not load tray icon: \"%s\""), bp->iconName); } } } }
/** Add a window class to a group. */ void AddGroupClass(GroupType *gp, const char *pattern) { Assert(gp); if(JLIKELY(pattern)) { AddPattern(&gp->patterns, pattern, MATCH_CLASS); } else { Warning(_("invalid group class")); } }
/** Add a window name to a group. */ void AddGroupName(GroupType *gp, const char *pattern) { Assert(gp); if(JLIKELY(pattern)) { AddPattern(&gp->patterns, pattern, MATCH_NAME); } else { Warning(_("invalid group name")); } }
/** Parse popup style. */ void ParsePopupStyle(const TokenNode *tp) { static const StringMappingType enable_mapping[] = { { "button", POPUP_BUTTON }, { "clock", POPUP_CLOCK }, { "false", POPUP_NONE }, { "pager", POPUP_PAGER }, { "task", POPUP_TASK }, { "true", POPUP_ALL } }; const TokenNode *np; const char *str; char *tok; tok = FindAttribute(tp->attributes, "enabled"); if(tok) { settings.popupMask = POPUP_NONE; tok = strtok(tok, ","); while(tok) { const int x = FindValue(enable_mapping, ARRAY_LENGTH(enable_mapping), tok); if(JLIKELY(x >= 0)) { settings.popupMask |= x; } else { ParseError(tp, "invalid value for 'enabled': \"%s\"", tok); } tok = strtok(NULL, ","); } } str = FindAttribute(tp->attributes, "delay"); if(str) { settings.popupDelay = ParseUnsigned(tp, str); } for(np = tp->subnodeHead; np; np = np->next) { switch(np->type) { case TOK_FONT: SetFont(FONT_POPUP, np->value); break; case TOK_FOREGROUND: SetColor(COLOR_POPUP_FG, np->value); break; case TOK_BACKGROUND: SetColor(COLOR_POPUP_BG, np->value); break; case TOK_OUTLINE: SetColor(COLOR_POPUP_OUTLINE, np->value); break; default: InvalidTag(np, TOK_POPUPSTYLE); break; } } }
/** Insert a bounding box to the list of struts. */ void InsertStrut(const BoundingBox *box, ClientNode *np) { if(JLIKELY(box->width > 0 && box->height > 0)) { Strut *sp = Allocate(sizeof(Strut)); sp->client = np; sp->box = *box; sp->next = struts; struts = sp; } }
/** Parse a menu include. */ MenuItem *ParseMenuInclude(const TokenNode *tp, Menu *menu, MenuItem *last) { TokenNode *start = ParseMenuIncludeHelper(tp, tp->value); if(JLIKELY(start)) { last = ParseMenuItem(start->subnodeHead, menu, last); ReleaseTokens(start); } return last; }
/** Parse a dynamic menu (called from menu code). */ Menu *ParseDynamicMenu(const char *command) { Menu *menu = NULL; TokenNode *start = ParseMenuIncludeHelper(NULL, command); if(JLIKELY(start)) { menu = ParseMenu(start); ReleaseTokens(start); } return menu; }
/** Parse focus model. */ void ParseFocusModel(const TokenNode *tp) { if(JLIKELY(tp->value)) { if(!strcmp(tp->value, "sloppy")) { settings.focusModel = FOCUS_SLOPPY; } else if(!strcmp(tp->value, "click")) { settings.focusModel = FOCUS_CLICK; } else { ParseError(tp, "invalid focus model: \"%s\"", tp->value); } } else { ParseError(tp, "focus model not specified"); } }
/** Grab the mouse for choosing a window. */ char GrabMouseForChoose() { int result; unsigned int mask; mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask; result = JXGrabPointer(display, rootWindow, False, mask, GrabModeAsync, GrabModeAsync, None, cursors[CURSOR_CHOOSE], CurrentTime); if(JLIKELY(result == GrabSuccess)) { mousew = rootWindow; return 1; } else { return 0; } }
/** Grab the mouse for choosing a window. */ int GrabMouseForChoose() { int result; result = JXGrabPointer(display, rootWindow, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, chooseCursor, CurrentTime); if(JLIKELY(result == GrabSuccess)) { return 1; } else { return 0; } }
/** Parse layer. */ WinLayerType ParseLayer(const TokenNode *tp, const char *str) { static const StringMappingType mapping[] = { { "above", LAYER_ABOVE }, { "below", LAYER_BELOW }, { "normal", LAYER_NORMAL } }; const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str); if(JLIKELY(x >= 0)) { return x; } else { ParseError(tp, "invalid layer: %s", str); return LAYER_NORMAL; } }
/** Parse a string using a string mapping. */ int ParseAttribute(const StringMappingType *mapping, int count, const TokenNode *tp, const char *attr, int def) { const char *str = FindAttribute(tp->attributes, attr); if(str == NULL) { return def; } else { const int x = FindValue(mapping, count, str); if(JLIKELY(x >= 0)) { return x; } else { ParseError(tp, "invalid value for %s: \"%s\"", attr, str); return def; } } }
/** Parse a token value using a string mapping. */ int ParseTokenValue(const StringMappingType *mapping, int count, const TokenNode *tp, int def) { if(JUNLIKELY(tp->value == NULL)) { ParseError(tp, "%s is empty", GetTokenName(tp)); return def; } else { const int x = FindValue(mapping, count, tp->value); if(JLIKELY(x >= 0)) { return x; } else { ParseError(tp, "invalid %s: \"%s\"", GetTokenName(tp), tp->value); return def; } } }
/** Grab the mouse for resizing a window. */ int GrabMouseForResize(BorderActionType action) { Cursor cur; int result; cur = GetFrameCursor(action); result = JXGrabPointer(display, rootWindow, False, ButtonPressMask | ButtonReleaseMask | PointerMotionMask, GrabModeAsync, GrabModeAsync, None, cur, CurrentTime); if(JLIKELY(result == GrabSuccess)) { return 1; } else { return 0; } }
/** Parse a button tray component. */ void ParseTrayButton(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; const char *icon; const char *label; const char *popup; const char *temp; unsigned int width, height; char border; Assert(tp); Assert(tray); icon = FindAttribute(tp->attributes, ICON_ATTRIBUTE); label = FindAttribute(tp->attributes, LABEL_ATTRIBUTE); popup = FindAttribute(tp->attributes, POPUP_ATTRIBUTE); temp = FindAttribute(tp->attributes, BORDER_ATTRIBUTE); if(temp && !strcmp(temp, FALSE_VALUE)) { border = 0; } else { border = 1; } temp = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(temp) { width = ParseUnsigned(tp, temp); } else { width = 0; } temp = FindAttribute(tp->attributes, HEIGHT_ATTRIBUTE); if(temp) { height = ParseUnsigned(tp, temp); } else { height = 0; } cp = CreateTrayButton(icon, label, tp->value, popup, width, height, border); if(JLIKELY(cp)) { AddTrayComponent(tray, cp); } }
/** Parse a clock tray component. */ void ParseClock(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; const char *format; const char *zone; const char *command; const char *temp; int width, height; Assert(tp); Assert(tray); format = FindAttribute(tp->attributes, FORMAT_ATTRIBUTE); zone = FindAttribute(tp->attributes, ZONE_ATTRIBUTE); if(tp->value && strlen(tp->value) > 0) { command = tp->value; } else { command = NULL; } temp = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(temp) { width = ParseUnsigned(tp, temp); } else { width = 0; } temp = FindAttribute(tp->attributes, HEIGHT_ATTRIBUTE); if(temp) { height = ParseUnsigned(tp, temp); } else { height = 0; } cp = CreateClock(format, zone, command, width, height); if(JLIKELY(cp)) { AddTrayComponent(tray, cp); } }
/** Show a root menu caused by a key binding. */ void ShowKeyMenu(const XKeyEvent *event) { KeyNode *np; unsigned int state; /* Remove the lock key modifiers. */ state = event->state & ~lockMask; for(np = bindings; np; np = np->next) { if(np->state == state && np->code == event->keycode) { const int button = GetRootMenuIndexFromString(np->command); if(JLIKELY(button >= 0)) { ShowRootMenu(button, -1, -1, 1); } return; } } }
/** Set the vertical tray alignment. */ void SetTrayVerticalAlignment(TrayType *tp, const char *str) { static const StringMappingType mapping[] = { { "bottom", TALIGN_BOTTOM }, { "center", TALIGN_CENTER }, { "fixed", TALIGN_FIXED }, { "top", TALIGN_TOP } }; if(!str) { tp->valign = TALIGN_FIXED; } else { const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str); if(JLIKELY(x >= 0)) { tp->valign = x; } else { Warning(_("invalid tray vertical alignment: \"%s\""), str); tp->valign = TALIGN_FIXED; } } }
/** Set the horizontal tray alignment. */ void SetTrayHorizontalAlignment(TrayType *tp, const char *str) { static const StringMappingType mapping[] = { { "center", TALIGN_CENTER }, { "fixed", TALIGN_FIXED }, { "left", TALIGN_LEFT }, { "right", TALIGN_RIGHT } }; if(!str) { tp->halign = TALIGN_FIXED; } else { const int x = FindValue(mapping, ARRAY_LENGTH(mapping), str); if(JLIKELY(x >= 0)) { tp->halign = x; } else { Warning(_("invalid tray horizontal alignment: \"%s\""), str); tp->halign = TALIGN_FIXED; } } }
/** Parse resize mode. */ void ParseResizeMode(const TokenNode *tp) { const char *str; str = FindAttribute(tp->attributes, COORDINATES_ATTRIBUTE); if(str) { settings.resizeStatusType = ParseStatusWindowType(tp, str); } if(JLIKELY(tp->value)) { if(!strcmp(tp->value, OUTLINE_VALUE)) { settings.resizeMode = RESIZE_OUTLINE; } else if(!strcmp(tp->value, OPAQUE_VALUE)) { settings.resizeMode = RESIZE_OPAQUE; } else { ParseError(tp, "invalid resize mode: %s", tp->value); } } else { ParseError(tp, "resize mode not specified"); } }
/** Parse a dock tray component. */ void ParseDock(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; int width; char *str; Assert(tp); Assert(tray); str = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(str) { width = ParseUnsigned(tp, str); } else { width = 0; } cp = CreateDock(width); if(JLIKELY(cp)) { AddTrayComponent(tray, cp); } }
/** Parse a button tray component. */ void ParseTrayButton(const TokenNode *tp, TrayType *tray) { TrayComponentType *cp; const char *icon; const char *label; const char *popup; const char *temp; unsigned int width, height; Assert(tp); Assert(tray); icon = FindAttribute(tp->attributes, ICON_ATTRIBUTE); label = FindAttribute(tp->attributes, LABEL_ATTRIBUTE); popup = FindAttribute(tp->attributes, "popup"); temp = FindAttribute(tp->attributes, WIDTH_ATTRIBUTE); if(temp) { width = ParseUnsigned(tp, temp); } else { width = 0; } temp = FindAttribute(tp->attributes, HEIGHT_ATTRIBUTE); if(temp) { height = ParseUnsigned(tp, temp); } else { height = 0; } cp = CreateTrayButton(icon, label, popup, width, height); if(JLIKELY(cp)) { AddTrayComponent(tray, cp); ParseTrayComponentActions(tp, cp, AddTrayButtonAction); } }
/** Parse snap mode for moving windows. */ void ParseSnapMode(const TokenNode *tp) { const char *distance; distance = FindAttribute(tp->attributes, DISTANCE_ATTRIBUTE); if(distance) { settings.snapDistance = ParseUnsigned(tp, distance); } if(JLIKELY(tp->value)) { if(!strcmp(tp->value, "none")) { settings.snapMode = SNAP_NONE; } else if(!strcmp(tp->value, "screen")) { settings.snapMode = SNAP_SCREEN; } else if(!strcmp(tp->value, "border")) { settings.snapMode = SNAP_BORDER; } else { ParseError(tp, "invalid snap mode: %s", tp->value); } } else { ParseError(tp, "snap mode not specified"); } }
/** Apply a group to a client. */ void ApplyGroup(const GroupType *gp, ClientNode *np) { OptionListType *lp; Assert(gp); Assert(np); for(lp = gp->options; lp; lp = lp->next) { switch(lp->option) { case OPTION_STICKY: np->state.status |= STAT_STICKY; break; case OPTION_NOLIST: np->state.status |= STAT_NOLIST; break; case OPTION_NOPAGER: np->state.status |= STAT_NOPAGER; break; case OPTION_BORDER: np->state.border |= BORDER_OUTLINE; break; case OPTION_NOBORDER: np->state.border &= ~BORDER_OUTLINE; break; case OPTION_TITLE: np->state.border |= BORDER_TITLE; break; case OPTION_NOTITLE: np->state.border &= ~BORDER_TITLE; np->state.border &= ~BORDER_SHADE; break; case OPTION_LAYER: np->state.layer = lp->uvalue; break; case OPTION_DESKTOP: if(JLIKELY(lp->uvalue >= 1 && lp->uvalue <= settings.desktopCount)) { np->state.desktop = lp->uvalue - 1; } else { Warning(_("invalid group desktop: %d"), lp->uvalue); } break; case OPTION_ICON: DestroyIcon(np->icon); np->icon = LoadNamedIcon(lp->svalue, 1); break; case OPTION_PIGNORE: np->state.status |= STAT_PIGNORE; break; case OPTION_IIGNORE: np->state.status |= STAT_IIGNORE; break; case OPTION_MAXIMIZED: np->state.status |= STAT_HMAX | STAT_VMAX; break; case OPTION_MINIMIZED: np->state.status |= STAT_MINIMIZED; break; case OPTION_SHADED: np->state.status |= STAT_SHADED; break; case OPTION_OPACITY: np->state.opacity = lp->uvalue; np->state.status |= STAT_OPACITY; break; case OPTION_MAX_V: np->state.border &= ~BORDER_MAX_H; break; case OPTION_MAX_H: np->state.border &= ~BORDER_MAX_V; break; case OPTION_NOFOCUS: np->state.status |= STAT_NOFOCUS; break; case OPTION_NOSHADE: np->state.border &= ~BORDER_SHADE; break; case OPTION_CENTERED: np->state.status |= STAT_CENTERED; break; case OPTION_TILED: np->state.status |= STAT_TILED; break; case OPTION_NOTURGENT: np->state.status |= STAT_NOTURGENT; break; case OPTION_CONSTRAIN: np->state.border |= BORDER_CONSTRAIN; break; default: Debug("invalid option: %d", lp->option); break; } } }
/** Tokenize data. */ TokenNode *Tokenize(const char *line, const char *fileName) { AttributeNode *ap; TokenNode *current; char *temp; unsigned int x; unsigned int offset; unsigned int lineNumber; char inElement; char found; head = NULL; current = NULL; inElement = 0; lineNumber = 1; x = 0; /* Skip any initial white space. */ while(IsSpace(line[x], &lineNumber)) { x += 1; } /* Skip any XML stuff. */ if(!strncmp(line + x, "<?", 2)) { while(line[x]) { if(line[x] == '\n') { lineNumber += 1; } if(!strncmp(line + x, "?>", 2)) { x += 2; break; } x += 1; } } /* Process the XML data. */ while(line[x]) { /* Skip comments and white space. */ do { /* Skip white space. */ while(IsSpace(line[x], &lineNumber)) { x += 1; } /* Skip comments */ found = 0; if(!strncmp(line + x, "<!--", 4)) { while(line[x]) { if(line[x] == '\n') { lineNumber += 1; } if(!strncmp(line + x, "-->", 3)) { x += 3; found = 1; break; } x += 1; } } } while(found); switch(line[x]) { case '<': x += 1; if(line[x] == '/') { /* Close tag. */ x += 1; temp = ReadElementName(line + x); if(current) { if(JLIKELY(temp)) { if(JUNLIKELY(current->type != LookupType(temp, NULL))) { Warning(_("%s[%u]: close tag \"%s\" does not " "match open tag \"%s\""), fileName, lineNumber, temp, GetTokenName(current)); } } else { Warning(_("%s[%u]: unexpected and invalid close tag"), fileName, lineNumber); } current = current->parent; } else { if(temp) { Warning(_("%s[%u]: close tag \"%s\" without open tag"), fileName, lineNumber, temp); } else { Warning(_("%s[%u]: invalid close tag"), fileName, lineNumber); } } if(temp) { x += strlen(temp); Release(temp); } } else { /* Open tag. */ current = CreateNode(current, fileName, lineNumber); temp = ReadElementName(line + x); if(JLIKELY(temp)) { x += strlen(temp); LookupType(temp, current); Release(temp); } else { Warning(_("%s[%u]: invalid open tag"), fileName, lineNumber); } } inElement = 1; break; case '/': /* End of open/close tag. */ if(inElement) { x += 1; if(JLIKELY(line[x] == '>' && current)) { x += 1; current = current->parent; inElement = 0; } else { Warning(_("%s[%u]: invalid tag"), fileName, lineNumber); } } else { goto ReadDefault; } break; case '>': /* End of open tag. */ x += 1; inElement = 0; break; default: ReadDefault: if(inElement) { /* In the open tag; read attributes. */ ap = CreateAttribute(current); ap->name = ReadElementName(line + x); if(ap->name) { x += strlen(ap->name); if(line[x] == '=') { x += 1; } if(line[x] == '\"') { x += 1; } ap->value = ReadAttributeValue(line + x, fileName, &offset, &lineNumber); x += offset; if(line[x] == '\"') { x += 1; } } } else { /* In tag body; read text. */ temp = ReadElementValue(line + x, fileName, &offset, &lineNumber); x += offset; if(temp) { if(current) { if(current->value) { current->value = Reallocate(current->value, strlen(current->value) + strlen(temp) + 1); strcat(current->value, temp); Release(temp); } else { current->value = temp; } } else { if(JUNLIKELY(temp[0])) { Warning(_("%s[%u]: unexpected text: \"%s\""), fileName, lineNumber, temp); } Release(temp); } } } break; } } return head; }