/** Parse an include. */ void ParseInclude(const TokenNode *tp, int depth) { char *temp; Assert(tp); if(JUNLIKELY(!tp->value)) { ParseError(tp, "no include file specified"); } else { temp = CopyString(tp->value); ExpandPath(&temp); if(JUNLIKELY(!ParseFile(temp, depth))) { ParseError(tp, "could not open included file %s", temp); } Release(temp); } }
/** Read a file. */ char *ReadFile(FILE *fd) { const int BLOCK_SIZE = 1024; // Start at 1k. char *buffer; int len, max; len = 0; max = BLOCK_SIZE; buffer = Allocate(max + 1); for(;;) { const size_t count = fread(&buffer[len], 1, max - len, fd); len += count; if(len < max) { break; } max *= 2; if(JUNLIKELY(max < 0)) { /* File is too big. */ break; } buffer = Reallocate(buffer, max + 1); if(JUNLIKELY(buffer == NULL)) { FatalError(_("out of memory")); } } buffer[len] = 0; return buffer; }
/** Read a file. */ char *ReadFile(FILE *fd) { const int BLOCK_SIZE = 1 << 14; // Start at 16k. char *buffer; int len, max; int ch; len = 0; max = BLOCK_SIZE; buffer = Allocate(max + 1); for(;;) { ch = fgetc(fd); if(JUNLIKELY(ch == EOF || ch == 0)) { break; } buffer[len++] = ch; if(JUNLIKELY(len >= max)) { max *= 2; if(JUNLIKELY(max < 0)) { /* File is too big. */ break; } buffer = Reallocate(buffer, max + 1); } } buffer[len] = 0; return buffer; }
/** Startup font support. */ void StartupFonts(void) { unsigned int x; /* Inherit unset fonts from the tray for tray items. */ if(!fontNames[FONT_TASK]) { fontNames[FONT_TASK] = CopyString(fontNames[FONT_TRAY]); } if(!fontNames[FONT_TRAYBUTTON]) { fontNames[FONT_TRAYBUTTON] = CopyString(fontNames[FONT_TRAY]); } if(!fontNames[FONT_CLOCK]) { fontNames[FONT_CLOCK] = CopyString(fontNames[FONT_TRAY]); } if(!fontNames[FONT_PAGER]) { fontNames[FONT_PAGER] = CopyString(fontNames[FONT_TRAY]); } #ifdef USE_XFT for(x = 0; x < FONT_COUNT; x++) { if(fontNames[x]) { fonts[x] = JXftFontOpenName(display, rootScreen, fontNames[x]); if(!fonts[x]) { fonts[x] = JXftFontOpenXlfd(display, rootScreen, fontNames[x]); } if(JUNLIKELY(!fonts[x])) { Warning(_("could not load font: %s"), fontNames[x]); } } if(!fonts[x]) { fonts[x] = JXftFontOpenName(display, rootScreen, DEFAULT_FONT); } if(JUNLIKELY(!fonts[x])) { FatalError(_("could not load the default font: %s"), DEFAULT_FONT); } } #else /* USE_XFT */ for(x = 0; x < FONT_COUNT; x++) { if(fontNames[x]) { fonts[x] = JXLoadQueryFont(display, fontNames[x]); if(JUNLIKELY(!fonts[x] && fontNames[x])) { Warning(_("could not load font: %s"), fontNames[x]); } } if(!fonts[x]) { fonts[x] = JXLoadQueryFont(display, DEFAULT_FONT); } if(JUNLIKELY(!fonts[x])) { FatalError(_("could not load the default font: %s"), DEFAULT_FONT); } } #endif /* USE_XFT */ }
/** Create a button tray component. */ TrayComponentType *CreateTrayButton(const char *iconName, const char *label, const char *action, const char *popup, int width, int height) { TrayButtonType *bp; TrayComponentType *cp; if(JUNLIKELY((label == NULL || strlen(label) == 0) && (iconName == NULL || strlen(iconName) == 0))) { Warning(_("no icon or label for TrayButton")); return NULL; } if(JUNLIKELY(width < 0)) { Warning(_("invalid TrayButton width: %d"), width); width = 0; } if(JUNLIKELY(height < 0)) { Warning(_("invalid TrayButton height: %d"), height); height = 0; } bp = Allocate(sizeof(TrayButtonType)); bp->next = buttons; buttons = bp; bp->icon = NULL; bp->iconName = CopyString(iconName); bp->label = CopyString(label); bp->action = CopyString(action); bp->popup = CopyString(popup); cp = CreateTrayComponent(); cp->object = bp; bp->cp = cp; cp->requestedWidth = width; cp->requestedHeight = height; bp->mousex = -POPUP_DELTA; bp->mousey = -POPUP_DELTA; cp->Create = Create; cp->Destroy = Destroy; cp->SetSize = SetSize; cp->Resize = Resize; cp->ProcessButtonPress = ProcessButtonPress; cp->ProcessButtonRelease = ProcessButtonRelease; if(popup || label) { cp->ProcessMotionEvent = ProcessMotionEvent; } return cp; }
/** Get tokens from a menu include (either dynamic or static). */ TokenNode *ParseMenuIncludeHelper(const TokenNode *tp, const char *command) { FILE *fd; char *path; char *buffer; TokenNode *start; buffer = NULL; if(!strncmp(command, "exec:", 5)) { path = Allocate(strlen(command) - 5 + 1); strcpy(path, command + 5); ExpandPath(&path); fd = popen(path, "r"); if(JLIKELY(fd)) { buffer = ReadFile(fd); pclose(fd); } else { ParseError(tp, "could not execute included program: %s", path); } } else { path = CopyString(command); ExpandPath(&path); fd = fopen(path, "r"); if(JLIKELY(fd)) { buffer = ReadFile(fd); fclose(fd); } else { ParseError(NULL, "could not open include: %s", path); } } if(JUNLIKELY(!buffer)) { Release(path); return NULL; } start = Tokenize(buffer, path); Release(buffer); Release(path); if(JUNLIKELY(!start || start->type != TOK_JWM)) { ParseError(tp, "invalid include: %s", command); ReleaseTokens(start); return NULL; } return start; }
/** Startup font support. */ void StartupFonts(void) { unsigned int x; /* Inherit unset fonts from the tray for tray items. */ for(x = 0; x < ARRAY_LENGTH(INHERITED_FONTS); x++) { const FontType dest = INHERITED_FONTS[x].dest; if(!fontNames[dest]) { const FontType src = INHERITED_FONTS[x].src; fontNames[dest] = CopyString(fontNames[src]); } } #ifdef USE_XFT for(x = 0; x < FONT_COUNT; x++) { if(fontNames[x]) { fonts[x] = JXftFontOpenName(display, rootScreen, fontNames[x]); if(!fonts[x]) { fonts[x] = JXftFontOpenXlfd(display, rootScreen, fontNames[x]); } if(JUNLIKELY(!fonts[x])) { Warning(_("could not load font: %s"), fontNames[x]); } } if(!fonts[x]) { fonts[x] = JXftFontOpenName(display, rootScreen, DEFAULT_FONT); } if(JUNLIKELY(!fonts[x])) { FatalError(_("could not load the default font: %s"), DEFAULT_FONT); } } #else /* USE_XFT */ for(x = 0; x < FONT_COUNT; x++) { if(fontNames[x]) { fonts[x] = JXLoadQueryFont(display, fontNames[x]); if(JUNLIKELY(!fonts[x] && fontNames[x])) { Warning(_("could not load font: %s"), fontNames[x]); } } if(!fonts[x]) { fonts[x] = JXLoadQueryFont(display, DEFAULT_FONT); } if(JUNLIKELY(!fonts[x])) { FatalError(_("could not load the default font: %s"), DEFAULT_FONT); } } #endif /* USE_XFT */ }
/** Parse a key binding. */ void ParseKey(const TokenNode *tp) { const char *key; const char *code; const char *mask; const char *action; const char *command; KeyType k; int x; Assert(tp); mask = FindAttribute(tp->attributes, "mask"); key = FindAttribute(tp->attributes, "key"); code = FindAttribute(tp->attributes, "keycode"); action = tp->value; if(JUNLIKELY(action == NULL)) { ParseError(tp, "no action specified for Key"); return; } command = NULL; k = KEY_NONE; if(!strncmp(action, "exec:", 5)) { k = KEY_EXEC; command = action + 5; } else if(!strncmp(action, "root:", 5)) { k = KEY_ROOT; command = action + 5; } else { for(x = 0; KEY_MAP[x].name; x++) { if(!strcmp(action, KEY_MAP[x].name)) { k = KEY_MAP[x].key; break; } } } /* Insert the binding if it's valid. */ if(JUNLIKELY(k == KEY_NONE)) { ParseError(tp, "invalid Key action: \"%s\"", action); } else { InsertBinding(k, mask, key, code, command); } }
/** Create a swallowed application tray component. */ TrayComponentType *CreateSwallow(const char *name, const char *command, int width, int height) { TrayComponentType *cp; SwallowNode *np; if(JUNLIKELY(!name)) { Warning(_("cannot swallow a client with no name")); return NULL; } /* Make sure this name isn't already used. */ for(np = swallowNodes; np; np = np->next) { if(JUNLIKELY(!strcmp(np->name, name))) { Warning(_("cannot swallow the same client multiple times")); return NULL; } } np = Allocate(sizeof(SwallowNode)); np->name = CopyString(name); np->command = CopyString(command); np->next = swallowNodes; swallowNodes = np; cp = CreateTrayComponent(); np->cp = cp; cp->object = np; cp->Destroy = Destroy; cp->Resize = Resize; if(width) { cp->requestedWidth = width; np->userWidth = 1; } else { cp->requestedWidth = 1; np->userWidth = 0; } if(height) { cp->requestedHeight = height; np->userHeight = 1; } else { cp->requestedHeight = 1; np->userHeight = 0; } return cp; }
/** Parse a key binding. */ void ParseKey(const TokenNode *tp) { const char *key; const char *code; const char *mask; const char *action; const char *command; KeyType k; Assert(tp); mask = FindAttribute(tp->attributes, "mask"); key = FindAttribute(tp->attributes, "key"); code = FindAttribute(tp->attributes, "keycode"); action = tp->value; if(JUNLIKELY(action == NULL)) { ParseError(tp, "no action specified for Key"); return; } command = NULL; k = KEY_NONE; if(!strncmp(action, "exec:", 5)) { k = KEY_EXEC; command = action + 5; } else if(!strncmp(action, "root:", 5)) { k = KEY_ROOT; command = action + 5; } else { /* Look up the option in the key map using binary search. */ const int x = FindValue(KEY_MAP, KEY_MAP_COUNT, action); if(x >= 0) { k = (KeyType)x; } } /* Insert the binding if it's valid. */ if(JUNLIKELY(k == KEY_NONE)) { ParseError(tp, "invalid Key action: \"%s\"", action); } else { InsertBinding(k, mask, key, code, command); } }
/** Open a connection to the X server. */ void OpenConnection(void) { display = JXOpenDisplay(displayString); if(JUNLIKELY(!display)) { if(displayString) { printf("error: could not open display %s\n", displayString); } else { printf("error: could not open display\n"); } DoExit(1); } rootScreen = DefaultScreen(display); rootWindow = RootWindow(display, rootScreen); rootWidth = DisplayWidth(display, rootScreen); rootHeight = DisplayHeight(display, rootScreen); rootDepth = DefaultDepth(display, rootScreen); rootVisual = DefaultVisual(display, rootScreen); rootColormap = DefaultColormap(display, rootScreen); rootGC = DefaultGC(display, rootScreen); colormapCount = MaxCmapsOfScreen(ScreenOfDisplay(display, rootScreen)); XSetGraphicsExposures(display, rootGC, False); }
/** Update all task bars. */ void UpdateTaskBar(void) { TaskBarType *bp; int lastHeight = -1; if(JUNLIKELY(shouldExit)) { return; } for(bp = bars; bp; bp = bp->next) { if(bp->layout == LAYOUT_VERTICAL) { TaskEntry *tp; lastHeight = bp->cp->requestedHeight; if(bp->userHeight > 0) { bp->itemHeight = bp->userHeight; } else { bp->itemHeight = GetStringHeight(FONT_TASKLIST) + 12; } bp->cp->requestedHeight = 0; for(tp = taskEntries; tp; tp = tp->next) { if(ShouldShowEntry(tp)) { bp->cp->requestedHeight += bp->itemHeight; } } bp->cp->requestedHeight = Max(1, bp->cp->requestedHeight); if(lastHeight != bp->cp->requestedHeight) { ResizeTray(bp->cp->tray); } } ComputeItemSize(bp); Render(bp); } }
/** Parse a modifier mask string. */ unsigned int ParseModifierString(const char *str) { unsigned int mask; unsigned int x, y; char found; if(!str) { return MASK_NONE; } mask = MASK_NONE; for(x = 0; str[x]; x++) { found = 0; for(y = 0; modifiers[y].name; y++) { if(modifiers[y].name == str[x]) { mask |= modifiers[y].mask; found = 1; break; } } if(JUNLIKELY(!found)) { Warning(_("invalid modifier: \"%c\""), str[x]); } } return mask; }
/** Set a client's desktop. This will update transients. */ void SetClientDesktop(ClientNode *np, unsigned int desktop) { ClientNode *tp; Assert(np); if(JUNLIKELY(desktop >= settings.desktopCount)) { return; } if(!(np->state.status & STAT_STICKY)) { int x; for(x = 0; x < LAYER_COUNT; x++) { for(tp = nodes[x]; tp; tp = tp->next) { if(tp == np || tp->owner == np->window) { tp->state.desktop = desktop; if(desktop == currentDesktop) { ShowClient(tp); } else { HideClient(tp); } SetCardinalAtom(tp->window, ATOM_NET_WM_DESKTOP, tp->state.desktop); } } } RequirePagerUpdate(); RequireTaskUpdate(); } }
/** Create a dock component. */ TrayComponentType *CreateDock(int width) { TrayComponentType *cp; if(JUNLIKELY(dock != NULL && dock->cp != NULL)) { Warning(_("only one Dock allowed")); return NULL; } else if(dock == NULL) { dock = Allocate(sizeof(DockType)); dock->nodes = NULL; dock->window = None; } cp = CreateTrayComponent(); cp->object = dock; cp->requestedWidth = 1; cp->requestedHeight = 1; dock->cp = cp; dock->itemSize = width; cp->SetSize = SetSize; cp->Create = Create; cp->Resize = Resize; return cp; }
/** Execute an external program. */ void RunCommand(const char *command) { const char *displayString; char *str; if(JUNLIKELY(!command)) { return; } displayString = DisplayString(display); if(!fork()) { close(ConnectionNumber(display)); if(displayString && displayString[0]) { str = malloc(strlen(displayString) + 9); sprintf(str, "DISPLAY=%s", displayString); putenv(str); } setsid(); execl(SHELL_NAME, SHELL_NAME, "-c", command, NULL); Warning(_("exec failed: (%s) %s"), SHELL_NAME, command); exit(EXIT_SUCCESS); } }
/** * Parse a specific file. * @return 1 on success and 0 on failure. */ char ParseFile(const char *fileName, int depth) { TokenNode *tokens; FILE *fd; char *buffer; depth += 1; if(JUNLIKELY(depth > MAX_INCLUDE_DEPTH)) { ParseError(NULL, "include depth (%d) exceeded", MAX_INCLUDE_DEPTH); return 0; } fd = fopen(fileName, "r"); if(!fd) { return 0; } buffer = ReadFile(fd); fclose(fd); tokens = Tokenize(buffer, fileName); Release(buffer); Parse(tokens, depth); ReleaseTokens(tokens); return 1; }
/** Parse a key string. */ KeySym ParseKeyString(const char *str) { KeySym symbol; symbol = JXStringToKeysym(str); if(JUNLIKELY(symbol == NoSymbol)) { Warning(_("invalid key symbol: \"%s\""), str); } return symbol; }
/** Parse the JWM configuration. */ void ParseConfig(const char *fileName) { if(!ParseFile(fileName, 0)) { if(JUNLIKELY(!ParseFile(SYSTEM_CONFIG, 0))) { ParseError(NULL, "could not open %s or %s", fileName, SYSTEM_CONFIG); } } ValidateTrayButtons(); ValidateKeys(); }
/** Parse an unsigned integer. */ unsigned int ParseUnsigned(const TokenNode *tp, const char *str) { const long value = strtol(str, NULL, 0); if(JUNLIKELY(value < 0 || value > UINT_MAX)) { ParseError(tp, _("invalid setting: %s"), str); return 0; } else { return (unsigned int)value; } }
/** Load an image from the specified file. */ ImageNode *LoadImage(const char *fileName, int rwidth, int rheight, char preserveAspect) { unsigned i; unsigned name_length; ImageNode *result = NULL; /* Make sure we have a reasonable file name. */ if(!fileName) { return result; } name_length = strlen(fileName); if(JUNLIKELY(name_length == 0)) { return result; } /* Make sure the file exists. */ if(access(fileName, R_OK) < 0) { return result; } /* First we attempt to use the extension to determine the type * to avoid trying all loaders. */ for(i = 0; i < IMAGE_LOADER_COUNT; i++) { const char *ext = IMAGE_LOADERS[i].extension; const unsigned ext_length = strlen(ext); if(JLIKELY(name_length >= ext_length)) { const unsigned offset = name_length - ext_length; if(!StrCmpNoCase(&fileName[offset], ext)) { const ImageLoader loader = IMAGE_LOADERS[i].loader; result = (loader)(fileName, rwidth, rheight, preserveAspect); if(JLIKELY(result)) { return result; } break; } } } /* We were unable to load by extension, so try everything. */ for(i = 0; i < IMAGE_LOADER_COUNT; i++) { const ImageLoader loader = IMAGE_LOADERS[i].loader; result = (loader)(fileName, rwidth, rheight, preserveAspect); if(result) { /* We were able to load the image, so it must have either the * wrong extension or an extension we don't recognize. */ Warning(_("unrecognized extension for \"%s\", expected \"%s\""), fileName, IMAGE_LOADERS[i].extension); return result; } } /* No image could be loaded. */ return result; }
/** Set the font to use for a component. */ void SetFont(FontType type, const char *value) { if(JUNLIKELY(!value)) { Warning(_("empty Font tag")); return; } if(fontNames[type]) { Release(fontNames[type]); } fontNames[type] = CopyString(value); }
/** Set the preferred height of the specified task bar. */ void SetTaskBarHeight(TrayComponentType *cp, const char *value) { TaskBarType *bp = (TaskBarType*)cp->object; int temp; temp = atoi(value); if(JUNLIKELY(temp < 0)) { Warning(_("invalid height for TaskList: %s"), value); return; } bp->userHeight = temp; }
/** Parse opacity (a float between 0.0 and 1.0). */ unsigned int ParseOpacity(const TokenNode *tp, const char *str) { const float value = ParseFloat(str); if(JUNLIKELY(value <= 0.0 || value > 1.0)) { ParseError(tp, _("invalid opacity: %s"), str); return UINT_MAX; } else if(value == 1.0) { return UINT_MAX; } else { return (unsigned int)(value * UINT_MAX); } }
/** Change to the specified desktop. */ void ChangeDesktop(unsigned int desktop) { ClientNode *np; unsigned int x; if(JUNLIKELY(desktop >= desktopCount)) { return; } if(currentDesktop == desktop && !initializing) { return; } /* Hide clients from the old desktop. * Note that we show clients in a separate loop to prevent an issue * with clients losing focus. */ for(x = 0; x < LAYER_COUNT; x++) { for(np = nodes[x]; np; np = np->next) { if(np->state.status & STAT_STICKY) { continue; } if(np->state.desktop == currentDesktop) { HideClient(np); } } } /* Show clients on the new desktop. */ for(x = 0; x < LAYER_COUNT; x++) { for(np = nodes[x]; np; np = np->next) { if(np->state.status & STAT_STICKY) { continue; } if(np->state.desktop == desktop) { ShowClient(np); } } } currentDesktop = desktop; SetCardinalAtom(rootWindow, ATOM_NET_CURRENT_DESKTOP, currentDesktop); RestackClients(); UpdatePager(); UpdateTaskBar(); LoadBackground(desktop); }
/** Create a button tray component. */ TrayComponentType *CreateTrayButton(const char *iconName, const char *label, const char *popup, unsigned int width, unsigned int height) { TrayButtonType *bp; TrayComponentType *cp; if(JUNLIKELY((label == NULL || strlen(label) == 0) && (iconName == NULL || strlen(iconName) == 0))) { Warning(_("no icon or label for TrayButton")); return NULL; } bp = Allocate(sizeof(TrayButtonType)); bp->next = buttons; buttons = bp; bp->icon = NULL; bp->iconName = CopyString(iconName); bp->label = CopyString(label); bp->actions = NULL; bp->popup = CopyString(popup); cp = CreateTrayComponent(); cp->object = bp; bp->cp = cp; cp->requestedWidth = width; cp->requestedHeight = height; bp->mousex = -settings.doubleClickDelta; bp->mousey = -settings.doubleClickDelta; cp->Create = Create; cp->Destroy = Destroy; cp->SetSize = SetSize; cp->Resize = Resize; cp->Redraw = Draw; cp->ProcessButtonPress = ProcessButtonPress; cp->ProcessButtonRelease = ProcessButtonRelease; if(popup || label) { cp->ProcessMotionEvent = ProcessMotionEvent; } RegisterCallback(settings.popupDelay / 2, SignalTrayButton, bp); return cp; }
/** Validate actions. */ void ValidateActions(const ActionType *actions) { const ActionType *ap; for(ap = actions; ap; ap = ap->next) { if(ap->action && !strncmp(ap->action, "root:", 5)) { const int bindex = GetRootMenuIndexFromString(&ap->action[5]); if(JUNLIKELY(!IsRootMenuDefined(bindex))) { Warning(_("action: root menu \"%s\" not defined"), &ap->action[5]); } } } }
/** Validate key bindings. */ void ValidateKeys(void) { KeyNode *kp; for(kp = bindings; kp; kp = kp->next) { if((kp->key & 0xFF) == KEY_ROOT && kp->command) { const int bindex = GetRootMenuIndexFromString(kp->command); if(JUNLIKELY(!IsRootMenuDefined(bindex))) { Warning(_("key binding: root menu \"%s\" not defined"), kp->command); } } } }
/** Initialize a dock component. */ void Create(TrayComponentType *cp) { XEvent event; Assert(cp); /* Map the dock window. */ if(cp->window != None) { JXResizeWindow(display, cp->window, cp->width, cp->height); JXMapRaised(display, cp->window); } /* Set the orientation atom. */ SetCardinalAtom(dock->cp->window, ATOM_NET_SYSTEM_TRAY_ORIENTATION, orientation); /* Get the selection if we don't already own it. * If we did already own it, getting it again would cause problems * with some clients due to the way restarts are handled. */ if(!owner) { owner = 1; JXSetSelectionOwner(display, dockAtom, dock->cp->window, CurrentTime); if(JUNLIKELY(JXGetSelectionOwner(display, dockAtom) != dock->cp->window)) { owner = 0; Warning(_("could not acquire system tray selection")); } else { memset(&event, 0, sizeof(event)); event.xclient.type = ClientMessage; event.xclient.window = rootWindow; event.xclient.message_type = atoms[ATOM_MANAGER]; event.xclient.format = 32; event.xclient.data.l[0] = CurrentTime; event.xclient.data.l[1] = dockAtom; event.xclient.data.l[2] = dock->cp->window; event.xclient.data.l[3] = 0; event.xclient.data.l[4] = 0; JXSendEvent(display, rootWindow, False, StructureNotifyMask, &event); } } }
/** Update a specific component on a tray. */ void UpdateSpecificTray(const TrayType *tp, const TrayComponentType *cp) { if(JUNLIKELY(shouldExit)) { return; } /* If the tray is hidden, draw only the background. */ if(!tp->hidden && cp->pixmap != None) { JXCopyArea(display, cp->pixmap, tp->window, rootGC, 0, 0, cp->width, cp->height, cp->x, cp->y); } }