ede/ede-desktop/Wallpaper.cpp
Sanel Zukan bee5fd8fb9 Removing old ede-desktop.h. Adding support for editing existing icons.
Editing will be done simply by creating first .desktop file and letting ede-desktop to read it, performing some
checks to determin if only content was changed or file was renamed. Using this scheme, beside it simplify the code,
it addresses the cases when icon was modified outside the desktop or removed.

These things could be solved by using DirWatch, but I'm trying to rely less on OS notification system...
2013-06-24 14:40:28 +00:00

492 lines
12 KiB
C++

/*
* $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 <string.h>
#include <stdlib.h>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_RGB_Image.H>
#include <FL/fl_draw.H>
#include <edelib/Debug.h>
#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);
}
}