/* * $Id$ * * Eiconman, desktop and icon manager * Part of Equinox Desktop Environment (EDE). * Copyright (c) 2000-2007 EDE Authors. * * This program is licensed under terms of the * GNU General Public License version 2 or newer. * See COPYING for details. */ #include "Wallpaper.h" #include "Utils.h" #include #include #include #include #include #include // memcpy #include // malloc #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); 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 { EWARNING(ESTRLOC ": 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)) bool create_tile(Fl_Image* orig, Fl_RGB_Image*& copied, int X, int Y, int W, int H) { if(orig->w() >= W && orig->h() >= H) return false; 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; return true; } Wallpaper::Wallpaper(int X, int Y, int W, int H) : Fl_Box(X, Y, W, H, 0), rootpmap_pixmap(0), tiled(false) { } Wallpaper::~Wallpaper() { if(rootpmap_pixmap) XFreePixmap(fl_display, rootpmap_pixmap); } bool Wallpaper::set(const char* path) { EASSERT(path != NULL); tiled = false; Fl_Image* i = Fl_Shared_Image::get(path); if(!i) return false; image(i); set_rootpmap(); return true; } bool Wallpaper::set_tiled(const char* path) { EASSERT(path != NULL); Fl_Image* i = Fl_Shared_Image::get(path); if(!i) return false; Fl_RGB_Image* res = 0; if(create_tile(i, res, x(), y(), w(), h())) { image(res); tiled = true; set_rootpmap(); return true; } else { EWARNING(ESTRLOC ": Unable to create tiles for %s\n", path); tiled = false; } return false; } 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_image) { /* * XDestroyImage function calls frees both the image structure and the * data pointed to by the image structure. */ XDestroyImage(rootpmap_image); } if(!rootpmap_pixmap) return; XChangeProperty(fl_display, RootWindow(fl_display, fl_screen), _XA_XROOTPMAP_ID, XA_PIXMAP, 32, PropModeReplace, (unsigned char *)&rootpmap_pixmap, 1); #if 0 XGCValues gcv; gcv.graphics_exposures = False; GC dgc = XCreateGC(fl_display, pix, GCGraphicsExposures, &gcv); XImage img; img.byte_order = LSBFirst; // TODO: check img.format = ZPixmap; img.depth = fl_visual->depth; // depth of screen or depth of image() ? // find out bits_per_pixel field int num_pfv; XPixmapFormatValues* pfv = 0; XPixmapFormatValues* pfvlst = 0; pfvlst = XListPixmapFormats(fl_display, &num_pfv); for(pfv = pfvlst; pfv < pfvlst + num_pfv; pfv++) { if(pfv->depth == fl_visual->depth) break; } img.bits_per_pixel = pfv->bits_per_pixel; if(img.bits_per_pixel & 7) { EWARNING("Can't work with %i bpp !!!\n", img.bits_per_pixel); return; } #endif } 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(!tiled) { // center image in the box ix = (w()/2) - (iw/2); iy = (h()/2) - (ih/2); ix += x(); iy += y(); } else { ix = x(); iy = y(); } ix = x(); iy = y(); im->draw(ix, iy); /* * For debugging purposes :) * Uncommenting this (and removing GC/Window creation in create_xpixmap * will draw _XA_XROOTPMAP_ID Pixmap directly in Wallpaper widget. * It is used to check Fl_Image->Image conversion. */ #if 0 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; }