/* * $Id$ * * Copyright (C) 2006-2013 Sanel Zukan * * This program 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 * of the License, or (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "Wallpaper.h" #define CALC_PIXEL(tmp, rshift, rmask, gshift, gmask, bshift, bmask) \ tmp = 0; \ if(rshift >= 0) \ tmp |= (((int)r << rshift) & rmask); \ else \ tmp |= (((int)r >> (-rshift)) & rmask); \ \ if(gshift >= 0) \ tmp |= (((int)g << gshift) & gmask); \ else \ tmp |= (((int)g >> (-gshift)) & gmask); \ \ if(bshift >= 0) \ tmp |= (((int)b << bshift) & bmask); \ else \ tmp |= (((int)b >> (-bshift)) & bmask) static Pixmap create_xpixmap(Fl_Image* img, XImage** xim, Pixmap pix, int wp_w, int wp_h) { if(!img) return 0; if(pix) XFreePixmap(fl_display, pix); unsigned long rmask = fl_visual->visual->red_mask; unsigned long gmask = fl_visual->visual->green_mask; unsigned long bmask = fl_visual->visual->blue_mask; unsigned long start_mask; int start_shift; int rshift = 0; int gshift = 0; int bshift = 0; if(fl_visual->depth == 24 || fl_visual->depth == 16) { unsigned long n; if(fl_visual->depth == 24) { start_shift = 24; start_mask = 0x80000000; } else { start_shift = 8; start_mask = 0x8000; } rshift = start_shift; n = start_mask; while(!(n & rmask)) { n >>= 1; rshift--; } gshift = start_shift; n = start_mask; while(!(n & gmask)) { n >>= 1; gshift--; } bshift = start_shift; n = start_mask; while(!(n & bmask)) { n >>= 1; bshift--; } } /* * Figure out bitmap_pad and create image coresponding to the current * display depth except for 8 bpp display */ int bitmap_pad = 0; if(fl_visual->depth > 16) bitmap_pad = 32; else if(fl_visual->depth > 8) bitmap_pad = 16; else { E_WARNING(E_STRLOC ": Visual %i not supported\n", fl_visual->depth); return 0; } *xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth, ZPixmap, 0, 0, img->w(), img->h(), bitmap_pad, 0); int iw = img->w(); int ih = img->h(); int id = img->d(); bool msb = (ImageByteOrder(fl_display) == MSBFirst) ? true : false; unsigned int r, g, b, tmp; unsigned char* dest = (unsigned char*)malloc(ih * (*xim)->bytes_per_line); unsigned char* destptr = dest; unsigned char* src = (unsigned char*)img->data()[0]; if((*xim)->bits_per_pixel == 32) { if(id == 3 || id == 4) { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src++; g = *src++; b = *src++; if(id == 4) src++; CALC_PIXEL(tmp, rshift, rmask, gshift, gmask, bshift, bmask); if(msb) { *destptr++ = (tmp & 0xff000000) >> 24; *destptr++ = (tmp & 0xff0000) >> 16; *destptr++ = (tmp & 0xff00) >> 8; *destptr++ = (tmp & 0xff); } else { *destptr++ = (tmp & 0xff); *destptr++ = (tmp & 0xff00) >> 8; *destptr++ = (tmp & 0xff0000) >> 16; *destptr++ = (tmp & 0xff000000) >> 24; } } } } else { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src++; g = *src++; b = *src++; if(msb) { *destptr++ = 0; *destptr++ = b; *destptr++ = g; *destptr++ = r; } else { *destptr++ = r; *destptr++ = g; *destptr++ = b; *destptr++ = 0; } } } } } else if((*xim)->bits_per_pixel == 24) { if(id == 3 || id == 4) { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src++; g = *src++; b = *src++; if(id == 4) src++; CALC_PIXEL(tmp, rshift, rmask, gshift, gmask, bshift, bmask); if(msb) { *destptr++ = (tmp & 0xff0000) >> 16; *destptr++ = (tmp & 0xff00) >> 8; *destptr++ = (tmp & 0xff); } else { *destptr++ = (tmp & 0xff); *destptr++ = (tmp & 0xff00) >> 8; *destptr++ = (tmp & 0xff0000) >> 16; } } } } else { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src++; g = *src++; b = *src++; if(msb) { *destptr++ = b; *destptr++ = g; *destptr++ = r; } else { *destptr++ = r; *destptr++ = g; *destptr++ = b; } } } } } else if((*xim)->bits_per_pixel == 16) { if(id == 3 || id == 4) { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src++; g = *src++; b = *src++; if(id == 4) src++; CALC_PIXEL(tmp, rshift, rmask, gshift, gmask, bshift, bmask); if(msb) { *destptr++ = (tmp >> 8) & 0xff; *destptr++ = (tmp & 0xff); } else { *destptr++ = (tmp & 0xff); *destptr++ = (tmp >> 8) & 0xff; } } } } else { for(int j = 0; j < ih; j++) { for(int i = 0; i < iw; i++) { r = *src >> 3; src++; g = *src >> 2; src++; b = *src >> 3; src++; *destptr++ = r << 11 | g << 5 | b; } } } } (*xim)->data = (char*)dest; /* * Creating another window as drawable is needed since fl_window (as drawable) can't be * used here (valid only in draw()). Drawable must be size as wallpaper area or clients who * query _XA_XROOTPMAP_ID will get BadWindow when goes out of drawable area (but not out of * wallpaper area). * * FIXME: drawable background should be the same color as wallpaper background */ Window drawable = XCreateSimpleWindow(fl_display, RootWindow(fl_display, fl_screen), 0, 0, wp_w, wp_h, 0, 0, BlackPixel(fl_display, fl_screen)); pix = XCreatePixmap(fl_display, drawable, wp_w, wp_h, fl_visual->depth); /* The same applies as above; fl_gc can't be used here */ XGCValues gcv; gcv.graphics_exposures = False; GC dgc = XCreateGC(fl_display, pix, GCGraphicsExposures, &gcv); XPutImage(fl_display, pix, dgc, *xim, 0, 0, 0, 0, iw, ih); XDestroyWindow(fl_display, drawable); XFreeGC(fl_display, dgc); return pix; } #define PIXEL_POS(x, y, w, d) ((((y) * (w)) + (x)) * (d)) static void create_tile(Fl_Image* orig, Fl_RGB_Image** copied, int X, int Y, int W, int H) { /* don't tile large image */ if(orig->w() >= W && orig->h() >= H) { *copied = (Fl_RGB_Image*) orig; return; } int iw = orig->w(); int ih = orig->h(); int idepth = orig->d(); int tx = X - (X % iw); int ty = Y - (Y % ih); int tw = W + tx; int th = H + ty; unsigned char* dest = new unsigned char[tw * th * orig->d()]; unsigned char* destptr = dest; unsigned char* src = (unsigned char*)orig->data()[0]; int ppos = 0; /* for bounds checks */ int imax = iw * ih * idepth; if(idepth == 3 || idepth == 4) { for(int j = 0, cj = 0; j < th; j++, cj++) { if(cj > ih) cj = 0; for(int i = 0, ci = 0; i < tw; i++, ci++) { if(ci > iw) ci = 0; ppos = PIXEL_POS(ci, cj, iw, idepth); if(ppos > imax) ppos = imax; *destptr++ = src[ppos]; *destptr++ = src[ppos + 1]; *destptr++ = src[ppos + 2]; if(idepth == 4) *destptr++ = src[ppos + 3]; } } } else if(idepth == 2) { for(int j = 0, cj = 0; j < th; j++, cj++) { if(cj > ih) cj = 0; for(int i = 0, ci = 0; i < tw; i++, ci++) { if(ci > iw) ci = 0; ppos = PIXEL_POS(ci, cj, iw, idepth); if(ppos > imax) ppos = imax; *destptr++ = src[ppos]; *destptr++ = src[ppos + 1]; } } } else { for(int j = 0, cj = 0; j < th; j++, cj++) { if(cj > ih) cj = 0; for(int i = 0, ci = 0; i < tw; i++, ci++) { if(ci > iw) ci = 0; ppos = PIXEL_POS(ci, cj, iw, idepth); if(ppos > imax) ppos = imax; *destptr++ = src[ppos]; } } } Fl_RGB_Image* c = new Fl_RGB_Image(dest, tw, th, idepth, orig->ld()); c->alloc_array = 1; *copied = c; } Wallpaper::~Wallpaper() { if(rootpmap_pixmap) XFreePixmap(fl_display, rootpmap_pixmap); delete stretched_alloc; } void Wallpaper::set_rootpmap(void) { E_RETURN_IF_FAIL(image() != NULL); XImage *rootpmap_image = 0; Atom _XA_XROOTPMAP_ID; rootpmap_pixmap = create_xpixmap(image(), &rootpmap_image, rootpmap_pixmap, w(), h()); E_RETURN_IF_FAIL(rootpmap_pixmap != 0); /* XDestroyImage function calls frees both the image structure and the data pointed to by the image structure */ if(rootpmap_image) XDestroyImage(rootpmap_image); _XA_XROOTPMAP_ID = XInternAtom(fl_display, "_XROOTPMAP_ID", False); XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), _XA_XROOTPMAP_ID, XA_PIXMAP, 32, PropModeReplace, (unsigned char *)&rootpmap_pixmap, 1); } bool Wallpaper::load(const char *path, int s, bool rootpmap) { E_ASSERT(path != NULL); /* in case this function gets multiple calls */ if(wpath == path && state == s && rootpmap == use_rootpmap) return true; Fl_Shared_Image *img = Fl_Shared_Image::get(path); E_RETURN_VAL_IF_FAIL(img != NULL, false); if(s != WALLPAPER_CENTER && s != WALLPAPER_TILE && s != WALLPAPER_STRETCH) s = WALLPAPER_CENTER; if(s == WALLPAPER_TILE) { Fl_RGB_Image *tiled; create_tile((Fl_Image*)img, &tiled, x(), y(), w(), h()); image(tiled); } else if(s == WALLPAPER_STRETCH) { Fl_Image *stretched = NULL; if(img->w() == w() && img->h() == h()) stretched = img; else { /* valgrind reports it as possible lost, but FLTK should free it */ delete stretched_alloc; stretched = img->copy(w(), h()); img->release(); stretched_alloc = stretched; } image(stretched); } else { image(img); } /* set root pixmap for pseudo transparency */ if(rootpmap) set_rootpmap(); state = s; use_rootpmap = rootpmap; /* prevent self assignment or bad things will happen */ if(wpath != path) wpath = path; return true; } void Wallpaper::draw(void) { E_RETURN_IF_FAIL(image() != NULL); int ix, iy, iw, ih; Fl_Image* im = image(); iw = im->w(); ih = im->h(); E_RETURN_IF_FAIL(iw > 0 && ih > 0); if(state == WALLPAPER_CENTER) { ix = (w()/2) - (iw/2); iy = (h()/2) - (ih/2); ix += x(); iy += y(); } else { ix = x(); iy = y(); } im->draw(ix, iy); #if 0 /* * For debugging purposes :) * Uncommenting this (and removing GC/Window creation in create_xpixmap will draw _XA_XROOTPMAP_ID * Pixmap directly in Wallpaper widget. Used to check Fl_Image->Image conversion. */ if(global_xim) { Pixmap pix = fl_create_offscreen(image()->w(), image()->h()); fl_begin_offscreen(pix); XPutImage(fl_display, pix, fl_gc, global_xim, 0, 0, 0, 0, image()->w(), image()->h()); fl_end_offscreen(); fl_copy_offscreen(ix, iy, image()->w(), image()->h(), pix, 0, 0); XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), _XA_XROOTPMAP_ID, XA_PIXMAP, 32, PropModeReplace, (unsigned char *)&pix, 1); } #endif } int Wallpaper::handle(int event) { switch(event) { /* Route all DND events to parent (desktop), otherwise desktop will not get them if Wallpaper is visible */ case FL_DND_ENTER: case FL_DND_DRAG: case FL_DND_LEAVE: case FL_DND_RELEASE: case FL_PASTE: return parent()->handle(event); } return 0; } void Wallpaper::resize(int X, int Y, int W, int H) { if(X == x() && Y == y() && W == w() && H == h()) return; Fl_Box::resize(X, Y, W, H); if(image()) { /* * It is safe to call 'load()' again, as Fl_Shared_Image::get() will cache successfully loaded image. * Also benefit is that image transormations (scaling, tiling) will be done again on original image, so * there will be no lost data. * * TODO: Fl_Shared_Image will eat a memory; this needs some investigation. */ load(wpath.c_str(), state, use_rootpmap); } }