/* * $Id$ * * ede-desktop, desktop and icon manager * Part of Equinox Desktop Environment (EDE). * Copyright (c) 2006-2009 EDE Authors. * * This program is licensed under terms of the * GNU General Public License version 2 or newer. * See COPYING for details. */ #include // memcpy #include // malloc #include #include #include #include #include #include "Wallpaper.h" #include "Utils.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 = false; if(ImageByteOrder(fl_display) == MSBFirst) msb = true; else msb = false; unsigned int r, g, b, tmp; unsigned char* dest = (unsigned char*)malloc(sizeof(unsigned char) * iw * ih * id); 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) { // big endian *destptr++ = (tmp & 0xff000000) >> 24; *destptr++ = (tmp & 0xff0000) >> 16; *destptr++ = (tmp & 0xff00) >> 8; // FIXME: check this somehow ! if(id == 4) *destptr++ = (tmp & 0xff); } else { // little endian if(id == 4) *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) { // big endian *destptr++ = (tmp & 0xff0000) >> 16; *destptr++ = (tmp & 0xff00) >> 8; *destptr++ = (tmp & 0xff); } else { // little endian *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) { // big endian *destptr++ = b; *destptr++ = g; *destptr++ = r; } else { // little endian *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) { // big endian *destptr++ = (tmp >> 8) & 0xff; *destptr++ = (tmp & 0xff); } else { // little endian *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) { if(!image()) return; XImage* rootpmap_image = 0; rootpmap_pixmap = create_xpixmap(image(), &rootpmap_image, rootpmap_pixmap, w(), h()); if(!rootpmap_pixmap) return; /* XDestroyImage function calls frees both the image structure and the data pointed to by the image structure */ if(rootpmap_image) XDestroyImage(rootpmap_image); 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, WallpaperState s) { E_ASSERT(path != NULL); Fl_Shared_Image* i = Fl_Shared_Image::get(path); if(!i) return false; if(s == WALLPAPER_TILE) { Fl_RGB_Image* tiled; create_tile((Fl_Image*)i, &tiled, x(), y(), w(), h()); image(tiled); } else if(s == WALLPAPER_STRETCH) { Fl_Image* stretched = NULL; if(i->w() == w() && i->h() == h()) stretched = i; else { /* valgrind reports it as possible lost, but FLTK should free it */ delete stretched_alloc; stretched = i->copy(w(), h()); i->release(); stretched_alloc = stretched; } image(stretched); } else { image(i); } state = s; /* set root pixmap for pseudo transparency */ set_rootpmap(); return true; } void Wallpaper::draw(void) { if(!image()) return; int ix, iy, iw, ih; Fl_Image* im = image(); iw = im->w(); ih = im->h(); if(iw == 0 || ih == 0) return; 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; }