/** Get the width of a string. */ int GetStringWidth(FontType ft, const char *str) { #ifdef USE_XFT XGlyphInfo extents; #endif #ifdef USE_FRIBIDI FriBidiChar *temp_i; FriBidiChar *temp_o; FriBidiParType type = FRIBIDI_PAR_ON; int unicodeLength; #endif int len; char *output; int result; char *utf8String; /* Convert to UTF-8 if necessary. */ utf8String = GetUTF8String(str); /* Length of the UTF-8 string. */ len = strlen(utf8String); /* Apply the bidi algorithm if requested. */ #ifdef USE_FRIBIDI temp_i = AllocateStack((len + 1) * sizeof(FriBidiChar)); temp_o = AllocateStack((len + 1) * sizeof(FriBidiChar)); unicodeLength = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, utf8String, len, temp_i); fribidi_log2vis(temp_i, unicodeLength, &type, temp_o, NULL, NULL, NULL); output = AllocateStack(4 * len + 1); fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, temp_o, unicodeLength, (char*)output); len = strlen(output); #else output = utf8String; #endif /* Get the width of the string. */ #ifdef USE_XFT JXftTextExtentsUtf8(display, fonts[ft], (const unsigned char*)output, len, &extents); result = extents.xOff; #else result = XTextWidth(fonts[ft], output, len); #endif /* Clean up. */ #ifdef USE_FRIBIDI ReleaseStack(temp_i); ReleaseStack(temp_o); ReleaseStack(output); #endif ReleaseUTF8String(utf8String); return result; }
/** Load a gradient background. */ void LoadGradientBackground(BackgroundNode *bp) { XColor color1; XColor color2; char *temp; char *sep; int len; sep = strchr(bp->value, ':'); if(!sep) { bp->pixmap = None; bp->window = None; return; } /* Get the first color. */ len = (int)(sep - bp->value); temp = AllocateStack(len + 1); memcpy(temp, bp->value, len); temp[len] = 0; ParseColor(temp, &color1); ReleaseStack(temp); /* Get the second color. */ len = strlen(sep + 1); temp = AllocateStack(len + 1); memcpy(temp, sep + 1, len); temp[len] = 0; ParseColor(temp, &color2); ReleaseStack(temp); /* Create the window. */ bp->window = JXCreateSimpleWindow(display, rootWindow, 0, 0, rootWidth, rootHeight, 0, 0, 0); bp->pixmap = JXCreatePixmap(display, bp->window, rootWidth, rootHeight, rootDepth); if(color1.pixel == color2.pixel) { JXSetForeground(display, rootGC, color1.pixel); JXFillRectangle(display, bp->pixmap, rootGC, 0, 0, rootWidth, rootHeight); } else { DrawHorizontalGradient(bp->pixmap, rootGC, color1.pixel, color2.pixel, 0, 0, rootWidth, rootHeight); } }
/** Set _NET_WORKAREA. */ void SetWorkarea(void) { BoundingBox box; unsigned long *array; unsigned int count; int x; count = 4 * settings.desktopCount * sizeof(unsigned long); array = (unsigned long*)AllocateStack(count); box.x = 0; box.y = 0; box.width = rootWidth; box.height = rootHeight; SubtractTrayBounds(GetTrays(), &box, LAYER_NORMAL); SubtractStrutBounds(&box, NULL); for(x = 0; x < settings.desktopCount; x++) { array[x * 4 + 0] = box.x; array[x * 4 + 1] = box.y; array[x * 4 + 2] = box.width; array[x * 4 + 3] = box.height; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_WORKAREA], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, settings.desktopCount * 4); ReleaseStack(array); }
/** Parse an entity reference. * The entity value is returned in ch and the length of the entity * is returned as the value of the function. */ int ParseEntity(const char *entity, char *ch, const char *file, int line) { char *temp; int x; if(!strncmp(""", entity, 6)) { *ch = '\"'; return 6; } else if(!strncmp("<", entity, 4)) { *ch = '<'; return 4; } else if(!strncmp(">", entity, 4)) { *ch = '>'; return 4; } else if(!strncmp("&", entity, 5)) { *ch = '&'; return 5; } else if(!strncmp("'", entity, 6)) { *ch = '\''; return 6; } else { for(x = 0; entity[x]; x++) { if(entity[x] == ';') { break; } } temp = AllocateStack(x + 2); strncpy(temp, entity, x + 1); temp[x + 1] = 0; Warning("%s[%d]: invalid entity: \"%.8s\"", file, line, temp); ReleaseStack(temp); *ch = '&'; return 1; } }
/** Parse a color which may be a gradient. */ void ParseGradient(const char *value, ColorType a, ColorType b) { const char *sep; char *temp; int len; /* Find the separator. */ sep = strchr(value, ':'); if(!sep) { /* Only one color given - use the same color for both. */ SetColor(a, value); SetColor(b, value); } else { /* Two colors. */ /* Get the first color. */ len = (int)(sep - value); temp = AllocateStack(len + 1); memcpy(temp, value, len); temp[len] = 0; SetColor(a, temp); ReleaseStack(temp); /* Get the second color. */ len = strlen(sep + 1); temp = AllocateStack(len + 1); memcpy(temp, sep + 1, len); temp[len] = 0; SetColor(b, temp); ReleaseStack(temp); } }
/** Helper for loading icons by name. */ IconNode *LoadNamedIconHelper(const char *name, const char *path, char save) { IconNode *result; char *temp; temp = AllocateStack(strlen(name) + strlen(path) + 1); strcpy(temp, path); strcat(temp, name); result = CreateIconFromFile(temp, save); ReleaseStack(temp); return result; }
/** Maintain the _NET_CLIENT_LIST[_STACKING] properties on the root. */ void UpdateNetClientList(void) { TaskEntry *tp; ClientNode *client; Window *windows; unsigned int count; int layer; /* Determine how much we need to allocate. */ if(clientCount == 0) { windows = NULL; } else { windows = AllocateStack(clientCount * sizeof(Window)); } /* Set _NET_CLIENT_LIST */ count = 0; for(tp = taskEntries; tp; tp = tp->next) { ClientEntry *cp; for(cp = tp->clients; cp; cp = cp->next) { windows[count] = cp->client->window; count += 1; } } Assert(count <= clientCount); JXChangeProperty(display, rootWindow, atoms[ATOM_NET_CLIENT_LIST], XA_WINDOW, 32, PropModeReplace, (unsigned char*)windows, count); /* Set _NET_CLIENT_LIST_STACKING */ count = 0; for(layer = FIRST_LAYER; layer <= LAST_LAYER; layer++) { for(client = nodes[layer]; client; client = client->next) { windows[count] = client->window; count += 1; } } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_CLIENT_LIST_STACKING], XA_WINDOW, 32, PropModeReplace, (unsigned char*)windows, count); if(windows != NULL) { ReleaseStack(windows); } }
/** Helper for loading icons by name. */ IconNode *LoadNamedIconHelper(const char *name, const char *path, char save, char preserveAspect) { IconNode *result; char *temp; const unsigned nameLength = strlen(name); const unsigned pathLength = strlen(path); unsigned i, has_extension = 0; ImageNode *image; temp = AllocateStack(nameLength + pathLength + MAX_EXTENSION_LENGTH + 1); memcpy(&temp[0], path, pathLength); memcpy(&temp[pathLength], name, nameLength+1); result = NULL; size_t templen = pathLength + nameLength; for(i = 1; i < EXTENSION_COUNT; i++){ if (!strcmp(ICON_EXTENSIONS[i],temp+templen-strlen(ICON_EXTENSIONS[i]))){ has_extension = 1; break; } } if (has_extension){ image = LoadImage(temp, 0, 0, 1); }else for(i = 0; i < EXTENSION_COUNT; i++) { const unsigned len = strlen(ICON_EXTENSIONS[i]); memcpy(&temp[templen], ICON_EXTENSIONS[i], len + 1); image = LoadImage(temp, 0, 0, 1); if (image) break; } if (image) { result = CreateIcon(image); result->preserveAspect = preserveAspect; result->name = CopyString(temp); if (save) InsertIcon(result); DestroyImage(image); } ReleaseStack(temp); return result; }
/** Startup the dock. */ void StartupDock(void) { char *selectionName; if(!dock) { /* No dock has been requested. */ return; } if(dock->window == None) { /* No dock yet. */ /* Get the selection atom. */ selectionName = AllocateStack(sizeof(BASE_SELECTION_NAME)); snprintf(selectionName, sizeof(BASE_SELECTION_NAME), BASE_SELECTION_NAME, rootScreen); dockAtom = JXInternAtom(display, selectionName, False); ReleaseStack(selectionName); /* The location and size of the window doesn't matter here. */ dock->window = JXCreateSimpleWindow(display, rootWindow, /* x, y, width, height */ 0, 0, 1, 1, /* border_size, border_color */ 0, 0, /* background */ colors[COLOR_TRAY_BG2]); JXSelectInput(display, dock->window, SubstructureNotifyMask | SubstructureRedirectMask | EnterWindowMask | PointerMotionMask | PointerMotionHintMask); } dock->cp->window = dock->window; }
/** Set root hints and intern atoms. */ void StartupHints(void) { unsigned long *array; char *data; Atom *supported; Window win; unsigned int x; unsigned int count; /* Determine how much space we will need on the stack and allocate it. */ count = 0; for(x = 0; x < settings.desktopCount; x++) { count += strlen(GetDesktopName(x)) + 1; } if(count < 2 * sizeof(unsigned long)) { count = 2 * sizeof(unsigned long); } if(count < ATOM_COUNT * sizeof(Atom)) { count = ATOM_COUNT * sizeof(Atom); } data = AllocateStack(count); array = (unsigned long*)data; supported = (Atom*)data; /* Intern the atoms */ for(x = 0; x < ATOM_COUNT; x++) { *atomList[x].atom = JXInternAtom(display, atomList[x].name, False); } /* _NET_SUPPORTED */ for(x = FIRST_NET_ATOM; x <= LAST_NET_ATOM; x++) { supported[x - FIRST_NET_ATOM] = atoms[x]; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_SUPPORTED], XA_ATOM, 32, PropModeReplace, (unsigned char*)supported, LAST_NET_ATOM - FIRST_NET_ATOM + 1); /* _NET_NUMBER_OF_DESKTOPS */ SetCardinalAtom(rootWindow, ATOM_NET_NUMBER_OF_DESKTOPS, settings.desktopCount); /* _NET_DESKTOP_NAMES */ count = 0; for(x = 0; x < settings.desktopCount; x++) { const char *name = GetDesktopName(x); const unsigned len = strlen(name); memcpy(&data[count], name, len + 1); count += len + 1; } JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_NAMES], atoms[ATOM_UTF8_STRING], 8, PropModeReplace, (unsigned char*)data, count); /* _NET_DESKTOP_GEOMETRY */ array[0] = rootWidth; array[1] = rootHeight; JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_GEOMETRY], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 2); /* _NET_DESKTOP_VIEWPORT */ array[0] = 0; array[1] = 0; JXChangeProperty(display, rootWindow, atoms[ATOM_NET_DESKTOP_VIEWPORT], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 2); /* _NET_WM_NAME */ win = supportingWindow; JXChangeProperty(display, win, atoms[ATOM_NET_WM_NAME], atoms[ATOM_UTF8_STRING], 8, PropModeReplace, (unsigned char*)"JWM", 3); /* _NET_WM_PID */ array[0] = getpid(); JXChangeProperty(display, win, atoms[ATOM_NET_WM_PID], XA_CARDINAL, 32, PropModeReplace, (unsigned char*)array, 1); /* _NET_SUPPORTING_WM_CHECK */ SetWindowAtom(rootWindow, ATOM_NET_SUPPORTING_WM_CHECK, win); SetWindowAtom(win, ATOM_NET_SUPPORTING_WM_CHECK, win); ReleaseStack(data); }
/** Raise all clients in a group and focus the top-most. */ void FocusGroup(const TaskEntry *tp) { const char *className = tp->clients->client->className; ClientNode **toRestore; const ClientEntry *cp; unsigned restoreCount; int i; char shouldSwitch; /* If there is no class name, then there will only be one client. */ if(!className || !settings.groupTasks) { if(!(tp->clients->client->state.status & STAT_STICKY)) { ChangeDesktop(tp->clients->client->state.desktop); } RestoreClient(tp->clients->client, 1); FocusClient(tp->clients->client); return; } /* If there is a client in the group on this desktop, * then we remain on the same desktop. */ shouldSwitch = 1; for(cp = tp->clients; cp; cp = cp->next) { if(IsClientOnCurrentDesktop(cp->client)) { shouldSwitch = 0; break; } } /* Switch to the desktop of the top-most client in the group. */ if(shouldSwitch) { for(i = 0; i < LAYER_COUNT; i++) { ClientNode *np; for(np = nodes[i]; np; np = np->next) { if(np->className && !strcmp(np->className, className)) { if(ShouldFocus(np, 0)) { if(!(np->state.status & STAT_STICKY)) { ChangeDesktop(np->state.desktop); } break; } } } } } /* Build up the list of clients to restore in correct order. */ toRestore = AllocateStack(sizeof(ClientNode*) * clientCount); restoreCount = 0; for(i = 0; i < LAYER_COUNT; i++) { ClientNode *np; for(np = nodes[i]; np; np = np->next) { if(!ShouldFocus(np, 1)) { continue; } if(np->className && !strcmp(np->className, className)) { toRestore[restoreCount] = np; restoreCount += 1; } } } Assert(restoreCount <= clientCount); for(i = restoreCount - 1; i >= 0; i--) { RestoreClient(toRestore[i], 1); } for(i = 0; i < restoreCount; i++) { if(toRestore[i]->state.status & (STAT_CANFOCUS | STAT_TAKEFOCUS)) { FocusClient(toRestore[i]); break; } } ReleaseStack(toRestore); }
/** Display a string. */ void RenderString(Drawable d, FontType font, ColorType color, int x, int y, int width, const char *str) { #ifdef USE_ICONV static char isUTF8 = -1; #endif XRectangle rect; Region renderRegion; int len; char *output; #ifdef USE_FRIBIDI FriBidiChar *temp_i; FriBidiChar *temp_o; FriBidiParType type = FRIBIDI_PAR_ON; int unicodeLength; #endif #ifdef USE_XFT XGlyphInfo extents; #endif char *utf8String; /* Early return for empty strings. */ if(!str || !str[0]) { return; } /* Convert to UTF-8 if necessary. */ utf8String = GetUTF8String(str); /* Get the length of the UTF-8 string. */ len = strlen(utf8String); /* Apply the bidi algorithm if requested. */ #ifdef USE_FRIBIDI temp_i = AllocateStack((len + 1) * sizeof(FriBidiChar)); temp_o = AllocateStack((len + 1) * sizeof(FriBidiChar)); unicodeLength = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, utf8String, len, temp_i); fribidi_log2vis(temp_i, unicodeLength, &type, temp_o, NULL, NULL, NULL); output = AllocateStack(4 * len + 1); fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, temp_o, unicodeLength, (char*)output); len = strlen(output); #else output = utf8String; #endif /* Get the bounds for the string based on the specified width. */ rect.x = x; rect.y = y; rect.height = GetStringHeight(font); #ifdef USE_XFT JXftTextExtentsUtf8(display, fonts[font], (const unsigned char*)output, len, &extents); rect.width = extents.xOff; #else rect.width = XTextWidth(fonts[font], output, len); #endif rect.width = Min(rect.width, width) + 2; /* Combine the width bounds with the region to use. */ renderRegion = XCreateRegion(); XUnionRectWithRegion(&rect, renderRegion, renderRegion); /* Display the string. */ #ifdef USE_XFT JXftDrawChange(xd, d); JXftDrawSetClip(xd, renderRegion); JXftDrawStringUtf8(xd, GetXftColor(color), fonts[font], x, y + fonts[font]->ascent, (const unsigned char*)output, len); JXftDrawChange(xd, rootWindow); #else JXSetForeground(display, fontGC, colors[color]); JXSetRegion(display, fontGC, renderRegion); JXSetFont(display, fontGC, fonts[font]->fid); JXDrawString(display, d, fontGC, x, y + fonts[font]->ascent, output, len); #endif /* Free any memory used for UTF conversion. */ #ifdef USE_FRIBIDI ReleaseStack(temp_i); ReleaseStack(temp_o); ReleaseStack(output); #endif ReleaseUTF8String(utf8String); XDestroyRegion(renderRegion); }
ImageNode *LoadPNGImage(const char *fileName, int rwidth, int rheight, char preserveAspect) { static ImageNode *result; static FILE *fd; static unsigned char **rows; static png_structp pngData; static png_infop pngInfo; static png_infop pngEndInfo; unsigned char header[8]; unsigned long rowBytes; int bitDepth, colorType; unsigned int x, y; png_uint_32 width; png_uint_32 height; Assert(fileName); result = NULL; fd = NULL; rows = NULL; pngData = NULL; pngInfo = NULL; pngEndInfo = NULL; fd = fopen(fileName, "rb"); if(!fd) { return NULL; } x = fread(header, 1, sizeof(header), fd); if(x != sizeof(header) || png_sig_cmp(header, 0, sizeof(header))) { fclose(fd); return NULL; } pngData = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(JUNLIKELY(!pngData)) { fclose(fd); Warning(_("could not create read struct for PNG image: %s"), fileName); return NULL; } if(JUNLIKELY(setjmp(png_jmpbuf(pngData)))) { png_destroy_read_struct(&pngData, &pngInfo, &pngEndInfo); if(fd) { fclose(fd); } if(rows) { ReleaseStack(rows); } DestroyImage(result); Warning(_("error reading PNG image: %s"), fileName); return NULL; } pngInfo = png_create_info_struct(pngData); if(JUNLIKELY(!pngInfo)) { png_destroy_read_struct(&pngData, NULL, NULL); fclose(fd); Warning(_("could not create info struct for PNG image: %s"), fileName); return NULL; } pngEndInfo = png_create_info_struct(pngData); if(JUNLIKELY(!pngEndInfo)) { png_destroy_read_struct(&pngData, &pngInfo, NULL); fclose(fd); Warning("could not create end info struct for PNG image: %s", fileName); return NULL; } png_init_io(pngData, fd); png_set_sig_bytes(pngData, sizeof(header)); png_read_info(pngData, pngInfo); png_get_IHDR(pngData, pngInfo, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL); result = CreateImage(width, height, 0); png_set_expand(pngData); if(bitDepth == 16) { png_set_strip_16(pngData); } else if(bitDepth < 8) { png_set_packing(pngData); } png_set_swap_alpha(pngData); png_set_filler(pngData, 0xFF, PNG_FILLER_BEFORE); if(colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(pngData); } png_read_update_info(pngData, pngInfo); rowBytes = png_get_rowbytes(pngData, pngInfo); rows = AllocateStack(result->height * sizeof(result->data)); y = 0; for(x = 0; x < result->height; x++) { rows[x] = &result->data[y]; y += rowBytes; } png_read_image(pngData, rows); png_read_end(pngData, pngInfo); png_destroy_read_struct(&pngData, &pngInfo, &pngEndInfo); fclose(fd); ReleaseStack(rows); rows = NULL; return result; }
/** Restack the clients according the way we want them. */ void RestackClients(void) { TrayType *tp; ClientNode *np; unsigned int layer, index; int trayCount; Window *stack; Window fw; if(JUNLIKELY(shouldExit)) { return; } /* Allocate memory for restacking. */ trayCount = GetTrayCount(); stack = AllocateStack((clientCount + trayCount) * sizeof(Window)); /* Prepare the stacking array. */ fw = None; index = 0; if(activeClient && (activeClient->state.status & STAT_FULLSCREEN)) { fw = activeClient->window; for(np = nodes[activeClient->state.layer]; np; np = np->next) { if(np->owner == fw) { if(np->parent != None) { stack[index] = np->parent; } else { stack[index] = np->window; } index += 1; } } if(activeClient->parent != None) { stack[index] = activeClient->parent; } else { stack[index] = activeClient->window; } index += 1; } layer = LAST_LAYER; for(;;) { for(np = nodes[layer]; np; np = np->next) { if( (np->state.status & (STAT_MAPPED | STAT_SHADED)) && !(np->state.status & STAT_HIDDEN)) { if(fw != None && (np->window == fw || np->owner == fw)) { continue; } if(np->parent != None) { stack[index] = np->parent; } else { stack[index] = np->window; } index += 1; } } for(tp = GetTrays(); tp; tp = tp->next) { if(layer == tp->layer) { stack[index] = tp->window; index += 1; } } if(layer == FIRST_LAYER) { break; } layer -= 1; } JXRestackWindows(display, stack, index); ReleaseStack(stack); UpdateNetClientList(); RequirePagerUpdate(); }
/** Tiled placement. */ char TileClient(const BoundingBox *box, ClientNode *np) { const ClientNode *tp; int layer; int north, south, east, west; int i, j; int count; int *xs; int *ys; /* Determine how much space to allocate. */ count = 1; for(layer = np->state.layer; layer < LAYER_COUNT; layer++) { for(tp = nodes[layer]; tp; tp = tp->next) { if(tp->state.desktop != currentDesktop) { if(!(tp->state.status & STAT_STICKY)) { continue; } } if(!(tp->state.status & STAT_MAPPED)) { continue; } if(tp == np) { continue; } count += 2; } } /* Allocate space for the points. */ xs = AllocateStack(sizeof(int) * count); ys = AllocateStack(sizeof(int) * count); /* Insert points. */ xs[0] = box->x; ys[0] = box->y; count = 1; for(layer = np->state.layer; layer < LAYER_COUNT; layer++) { for(tp = nodes[layer]; tp; tp = tp->next) { if(tp->state.desktop != currentDesktop) { if(!(tp->state.status & STAT_STICKY)) { continue; } } if(!(tp->state.status & STAT_MAPPED)) { continue; } if(tp == np) { continue; } GetBorderSize(&tp->state, &north, &south, &east, &west); xs[count + 0] = tp->x - west; xs[count + 1] = tp->x + tp->width + east; ys[count + 0] = tp->y - north; ys[count + 1] = tp->y + tp->height + south; count += 2; } } /* Sort the points. */ qsort(xs, count, sizeof(int), IntComparator); qsort(ys, count, sizeof(int), IntComparator); /* Try all possible positions. */ for(i = 0; i < count; i++) { for(j = 0; j < count; j++) { if(TryTileClient(box, np, xs[i], ys[j])) { ReleaseStack(xs); ReleaseStack(ys); return 1; } } } ReleaseStack(xs); ReleaseStack(ys); /* Tiled placement failed. */ return 0; }