mirror of
https://github.com/edeproject/ede.git
synced 2023-08-10 21:13:03 +03:00
529 lines
11 KiB
C++
529 lines
11 KiB
C++
/*
|
|
* $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 <edelib/Debug.h>
|
|
|
|
#include <FL/Fl_Shared_Image.H>
|
|
#include <FL/Fl_RGB_Image.H>
|
|
#include <FL/fl_draw.H>
|
|
#include <FL/x.H>
|
|
|
|
#include <string.h> // memcpy
|
|
#include <stdlib.h> // 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;
|
|
}
|