/** * @brief Set basic properties on a window. */ void wm_wid_set_info(session_t *ps, Window wid, const char *name, Atom window_type) { // Set window name { char *textcpy = mstrjoin("skippy-xd ", name); { XTextProperty text_prop = { }; if (Success == XmbTextListToTextProperty(ps->dpy, &textcpy, 1, XStdICCTextStyle, &text_prop)) XSetWMName(ps->dpy, wid, &text_prop); sxfree(text_prop.value); } wm_wid_set_prop_utf8(ps, wid, _NET_WM_NAME, textcpy); free(textcpy); } // Set window class { XClassHint *classh = allocchk(XAllocClassHint()); classh->res_name = "skippy-xd"; classh->res_class = "skippy-xd"; XSetClassHint(ps->dpy, wid, classh); XFree(classh); } // Set window type { if (!window_type) window_type = _NET_WM_WINDOW_TYPE_NORMAL; long val = window_type; XChangeProperty(ps->dpy, wid, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *) &val, 1); } }
pictw_t * sjpg_read(session_t *ps, const char *path) { pictw_t *pictw = NULL; JSAMPLE *data = NULL; bool need_abort = false; struct jpeg_error_mgr jerr; struct jpeg_decompress_struct cinfo = { .err = jpeg_std_error(&jerr), }; jpeg_create_decompress(&cinfo); FILE *fp = fopen(path, "rb"); if (unlikely(!fp)) { printfef("(\"%s\"): Failed to open file.", path); goto sjpg_read_end; } jpeg_stdio_src(&cinfo, fp); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); need_abort = true; int width = 0, height = 0, depth = 24; { const int comps = 4; data = allocchk(malloc(cinfo.output_width * cinfo.output_height * comps * sizeof(JSAMPLE))); JSAMPROW rowptrs[cinfo.output_height]; for (int i = 0; i < cinfo.output_height; ++i) rowptrs[i] = data + i * cinfo.output_width * comps; width = cinfo.output_width; height = cinfo.output_height; // libjpeg enforces a loop to read scanlines while (cinfo.output_scanline < cinfo.output_height) { if (unlikely(!jpeg_read_scanlines(&cinfo, &rowptrs[cinfo.output_scanline], cinfo.output_height - cinfo.output_scanline))) { printfef("(\"%s\"): Failed to read scanline %d.", path, cinfo.output_scanline); goto sjpg_read_end; } } // Expand greyscale value to RGBA if (1 == cinfo.output_components) { for (int i = 0; i < height; ++i) for (int j = width - 1; j >= 0; --j) { unsigned char a = rowptrs[i][j]; rowptrs[i][j * comps + 0] = a; rowptrs[i][j * comps + 1] = a; rowptrs[i][j * comps + 2] = a; rowptrs[i][j * comps + 3] = 0xff; } } else if (3 == cinfo.output_components) { for (int i = 0; i < height; ++i) { simg_data24_fillalpha(&data[i * 4 * width], width); simg_data24_tobgr(&data[i * 4 * width], width); } } } if (unlikely(!jpeg_finish_decompress(&cinfo))) { printfef("(\"%s\"): Failed to finish decompression.", path); goto sjpg_read_end; } need_abort = false; pictw = simg_data_to_pictw(ps, width, height, depth, data, 0); free(data); if (unlikely(!pictw)) { printfef("(\"%s\"): Failed to create Picture.", path); goto sjpg_read_end; } sjpg_read_end: /* if (unlikely(data && !pictw)) free(data); */ if (unlikely(need_abort)) jpeg_abort_decompress(&cinfo); if (likely(fp)) fclose(fp); jpeg_destroy_decompress(&cinfo); return pictw; }
ClientWin * clientwin_create(MainWin *mw, Window client) { session_t *ps = mw->ps; ClientWin *cw = allocchk(malloc(sizeof(ClientWin))); { static const ClientWin CLIENTWT_DEF = CLIENTWT_INIT; memcpy(cw, &CLIENTWT_DEF, sizeof(ClientWin)); } XWindowAttributes attr; cw->mainwin = mw; cw->wid_client = client; if (ps->o.includeFrame) cw->src.window = wm_find_frame(ps, client); if (!cw->src.window) cw->src.window = client; cw->mini.format = mw->format; { XSetWindowAttributes sattr = { .border_pixel = 0, .background_pixel = 0, .colormap = mw->colormap, .event_mask = ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | EnterWindowMask | LeaveWindowMask | PointerMotionMask | ExposureMask | FocusChangeMask, .override_redirect = ps->o.lazyTrans, }; cw->mini.window = XCreateWindow(ps->dpy, (ps->o.lazyTrans ? ps->root : mw->window), 0, 0, 1, 1, 0, mw->depth, InputOutput, mw->visual, CWColormap | CWBackPixel | CWBorderPixel | CWEventMask | CWOverrideRedirect, &sattr); } if (!cw->mini.window) goto clientwin_create_err; { static const char *PREFIX = "mini window of "; const int len = strlen(PREFIX) + 20; char *str = allocchk(malloc(len)); snprintf(str, len, "%s%#010lx", PREFIX, cw->src.window); wm_wid_set_info(cw->mainwin->ps, cw->mini.window, str, None); free(str); } // Listen to events on the window. We don't want to miss any changes so // this is to be done as early as possible XSelectInput(cw->mainwin->ps->dpy, cw->src.window, SubstructureNotifyMask | StructureNotifyMask); XGetWindowAttributes(ps->dpy, client, &attr); if (IsViewable != attr.map_state) goto clientwin_create_err; clientwin_update(cw); // Get window pixmap if (ps->o.useNameWindowPixmap) { XCompositeRedirectWindow(ps->dpy, cw->src.window, CompositeRedirectAutomatic); cw->redirected = true; cw->cpixmap = XCompositeNameWindowPixmap(ps->dpy, cw->src.window); } // Create window picture { Drawable draw = cw->cpixmap; if (!draw) draw = cw->src.window; XRenderPictureAttributes pa = { .subwindow_mode = IncludeInferiors }; cw->origin = XRenderCreatePicture(cw->mainwin->ps->dpy, draw, cw->src.format, CPSubwindowMode, &pa); } if (!cw->origin) goto clientwin_create_err; XRenderSetPictureFilter(cw->mainwin->ps->dpy, cw->origin, FilterBest, 0, 0); return cw; clientwin_create_err: if (cw) clientwin_destroy(cw, False); return NULL; } void clientwin_update(ClientWin *cw) { Window tmpwin; XWindowAttributes wattr; XGetWindowAttributes(cw->mainwin->ps->dpy, cw->src.window, &wattr); XTranslateCoordinates(cw->mainwin->ps->dpy, cw->src.window, wattr.root, -wattr.border_width, -wattr.border_width, &cw->src.x, &cw->src.y, &tmpwin); cw->src.width = wattr.width; cw->src.height = wattr.height; cw->src.format = XRenderFindVisualFormat(cw->mainwin->ps->dpy, wattr.visual); cw->mini.x = cw->mini.y = 0; cw->mini.width = cw->mini.height = 1; }
pictw_t * sgif_read(session_t *ps, const char *path) { assert(path); pictw_t *pictw = NULL; GifPixelType *data = NULL; unsigned char *tdata = NULL; GifRecordType rectype; int ret = 0, err = 0; const char *errstr = NULL; #ifdef SGIF_THREADSAFE GifFileType *f = DGifOpenFileName(path, &err); #else GifFileType *f = DGifOpenFileName(path); #endif if (unlikely(!f)) { #ifdef SGIF_HAS_ERROR err = GifError(); errstr = GifErrorString(); #endif #ifdef SGIF_THREADSAFE errstr = GifErrorString(err); #endif printfef("(\"%s\"): Failed to open file: %d (%s)", path, err, errstr); goto sgif_read_end; } int width = 0, height = 0, transp = -1; { int i = 0; while (GIF_OK == (ret = DGifGetRecordType(f, &rectype)) && TERMINATE_RECORD_TYPE != rectype) { ++i; switch (rectype) { case UNDEFINED_RECORD_TYPE: printfef("(\"%s\"): %d: Encountered a record of unknown type.", path, i); break; case SCREEN_DESC_RECORD_TYPE: printfef("(\"%s\"): %d: Encountered a record of " "ScreenDescRecordType. This shouldn't happen!", path, i); break; case IMAGE_DESC_RECORD_TYPE: if (data) { printfef("(\"%s\"): %d: Extra image section ignored.", path, i); break; } if (GIF_ERROR == DGifGetImageDesc(f)) { printfef("(\"%s\"): %d: Failed to read GIF image info.", path, i); break; } width = f->Image.Width; height = f->Image.Height; if (width <= 0 || height <= 0) { printfef("(\"%s\"): %d: Width/height invalid.", path, i); break; } assert(!data); data = allocchk(malloc(width * height * sizeof(GifPixelType))); // FIXME: Interlace images may need special treatments for (int j = 0; j < height; ++j) if (GIF_OK != DGifGetLine(f, &data[j * width], width)) { printfef("(\"%s\"): %d: Failed to read line %d.", path, i, j); goto sgif_read_end; } break; case EXTENSION_RECORD_TYPE: { int code = 0; GifByteType *pbytes = NULL; if (GIF_OK != DGifGetExtension(f, &code, &pbytes) || !pbytes) { printfef("(\"%s\"): %d: Failed to read extension block.", path, i); break; } do { // Transparency if (0xf9 == code && (pbytes[1] & 1)) transp = pbytes[4]; } while (GIF_OK == DGifGetExtensionNext(f, &pbytes) && pbytes); } break; case TERMINATE_RECORD_TYPE: assert(0); break; } } if (unlikely(!data)) { printfef("(\"%s\"): No valid data found.", path); goto sgif_read_end; } } // Colormap translation int depth = 32; { ColorMapObject *cmap = f->Image.ColorMap; if (!cmap) cmap = f->SColorMap; if (unlikely(!cmap)) { printfef("(\"%s\"): No colormap found.", path); goto sgif_read_end; } tdata = allocchk(malloc(width * height * depth / 8)); { GifPixelType *pd = data; unsigned char *end = tdata + width * height * depth / 8; for (unsigned char *p = tdata; p < end; p += depth / 8, ++pd) { // When the alpha is 0, X seemingly wants all color channels // to be 0 as well. if (transp >= 0 && transp == *pd) { p[0] = p[1] = p[2] = 0; if (32 == depth) p[3] = 0; continue; } p[0] = cmap->Colors[*pd].Blue; p[1] = cmap->Colors[*pd].Green; p[2] = cmap->Colors[*pd].Red; p[3] = 0xff; } } } if (unlikely(!(pictw = simg_data_to_pictw(ps, width, height, depth, tdata, 0)))) { printfef("(\"%s\"): Failed to create Picture.", path); goto sgif_read_end; } sgif_read_end: if (data) free(data); if (likely(f)) DGifCloseFile(f); return pictw; }
pictw_t * spng_read(session_t *ps, const char *path) { assert(path); char sig[SPNG_SIGBYTES] = ""; pictw_t *pictw = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; FILE *fp = fopen(path, "rb"); bool need_premultiply = false; if (unlikely(!fp)) { printfef("(\"%s\"): Failed to open file.", path); goto spng_read_end; } if (unlikely(SPNG_SIGBYTES != fread(&sig, 1, SPNG_SIGBYTES, fp))) { printfef("(\"%s\"): Failed to read %d-byte signature.", path, SPNG_SIGBYTES); goto spng_read_end; } if (unlikely(png_sig_cmp((png_bytep) sig, 0, SPNG_SIGBYTES))) { printfef("(\"%s\"): PNG signature invalid.", path); goto spng_read_end; } png_ptr = allocchk(png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)); info_ptr = allocchk(png_create_info_struct(png_ptr)); if (setjmp(png_jmpbuf(png_ptr))) goto spng_read_end; png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, SPNG_SIGBYTES); png_read_info(png_ptr, info_ptr); png_uint_32 width = 0, height = 0; // Set transformations int bit_depth = 0, color_type = 0; { int interlace_type = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL); // Scale or strip 16-bit colors if (bit_depth == 16) { printfdf("(\"%s\"): Scaling 16-bit colors.", path); #if PNG_LIBPNG_VER >= 10504 png_set_scale_16(png_ptr); #else png_set_strip_16(png_ptr); #endif bit_depth = 8; } /* if (bit_depth < 8) png_set_packing(png_ptr); */ // No idea why this is needed... png_set_bgr(png_ptr); // Convert palette to RGB if (color_type == PNG_COLOR_TYPE_PALETTE) { printfdf("(\"%s\"): Converting palette PNG to RGB.", path); png_set_palette_to_rgb(png_ptr); color_type = PNG_COLOR_TYPE_RGB; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { printfdf("(\"%s\"): Converting rDNS to full alpha.", path); png_set_tRNS_to_alpha(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY || PNG_COLOR_TYPE_GRAY_ALPHA == color_type) { printfdf("(\"%s\"): Converting gray (+ alpha) PNG to RGB.", path); png_set_gray_to_rgb(png_ptr); if (PNG_COLOR_TYPE_GRAY == color_type) color_type = PNG_COLOR_TYPE_RGB; else color_type = PNG_COLOR_TYPE_RGB_ALPHA; } /* if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { printfdf("(\"%s\"): Converting 1/2/4 bit gray PNG to 8-bit.", path); #if PNG_LIBPNG_VER >= 10209 png_set_expand_gray_1_2_4_to_8(png_ptr); #else png_set_gray_1_2_4_to_8(png_ptr); #endif bit_depth = 8; } */ // Somehow XImage requires 24-bit visual to use 32 bits per pixel if (color_type == PNG_COLOR_TYPE_RGB) { printfdf("(\"%s\"): Appending filler alpha values.", path); png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER); } // Premultiply alpha if (PNG_COLOR_TYPE_RGB_ALPHA == color_type) { #if PNG_LIBPNG_VER >= 10504 png_set_alpha_mode(png_ptr, PNG_ALPHA_STANDARD, 1.0); #else need_premultiply = true; #endif } /* int number_passes = 1; #ifdef PNG_READ_INTERLACING_SUPPORTED number_passes = png_set_interlace_handling(png_ptr); #endif */ if (PNG_INTERLACE_NONE != interlace_type) png_set_interlace_handling(png_ptr); } png_read_update_info(png_ptr, info_ptr); int depth = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); switch (color_type) { case PNG_COLOR_TYPE_GRAY: depth = 1 * bit_depth; break; case PNG_COLOR_TYPE_RGB: depth = 3 * bit_depth; break; case PNG_COLOR_TYPE_RGB_ALPHA: depth = 4 * bit_depth; break; default: assert(0); break; } // Read data and fill to Picture { int rowbytes = png_get_rowbytes(png_ptr, info_ptr); png_bytep row_pointers[height]; memset(row_pointers, 0, sizeof(row_pointers)); row_pointers[0] = png_malloc(png_ptr, rowbytes * height); for (int row = 1; row < height; row++) row_pointers[row] = row_pointers[row - 1] + rowbytes; png_read_image(png_ptr, row_pointers); if (need_premultiply) for (int row = 0; row < height; row++) simg_data32_premultiply(row_pointers[row], width); if (unlikely(!(pictw = simg_data_to_pictw(ps, width, height, depth, row_pointers[0], rowbytes)))) { printfef("(\"%s\"): Failed to create Picture.", path); goto spng_read_end; } } spng_read_end: if (png_ptr) png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (fp) fclose(fp); return pictw; }