1 // FbPixmap.cc for FbTk - Fluxbox ToolKit
2 // Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 // DEALINGS IN THE SOFTWARE.
21
22 #include "FbPixmap.hh"
23 #include "App.hh"
24 #include "GContext.hh"
25 #include "Transparent.hh"
26 #include "FbWindow.hh"
27 #include "TextUtils.hh"
28
29 #include <X11/Xutil.h>
30 #include <X11/Xatom.h>
31 #include <iostream>
32 #include <vector>
33 #ifdef HAVE_CSTRING
34 #include <cstring>
35 #else
36 #include <string.h>
37 #endif
38
39 using std::cerr;
40
41 namespace FbTk {
42
43 namespace {
44
45 std::vector <Pixmap> s_root_pixmaps;
46
47 struct RootProps {
48 const char* name;
49 Atom atom;
50 };
51
52 struct RootProps root_props[] = {
53 { "_XROOTPMAP_ID", None },
54 { "_XSETROOT_ID", None }
55 };
56
checkAtoms()57 void checkAtoms() {
58
59 Display* display = FbTk::App::instance()->display();
60 for (size_t i = 0; i < sizeof(root_props)/sizeof(RootProps); ++i) {
61 if (root_props[i].atom == None) {
62 root_props[i].atom = XInternAtom(display, root_props[i].name, False);
63 }
64 }
65 }
66
67 } // end of anonymous namespace
68
FbPixmap()69 FbPixmap::FbPixmap():m_pm(0),
70 m_width(0), m_height(0),
71 m_depth(0), m_dont_free(false) {
72 }
73
FbPixmap(const FbPixmap & the_copy)74 FbPixmap::FbPixmap(const FbPixmap &the_copy):FbDrawable(), m_pm(0),
75 m_width(0), m_height(0),
76 m_depth(0), m_dont_free(false) {
77 copy(the_copy);
78 }
79
FbPixmap(Pixmap pm)80 FbPixmap::FbPixmap(Pixmap pm):m_pm(0),
81 m_width(0), m_height(0),
82 m_depth(0), m_dont_free(false) {
83 if (pm == 0)
84 return;
85 // assign X pixmap to this
86 (*this) = pm;
87 }
88
FbPixmap(const FbDrawable & src,unsigned int width,unsigned int height,unsigned int depth)89 FbPixmap::FbPixmap(const FbDrawable &src,
90 unsigned int width, unsigned int height,
91 unsigned int depth):m_pm(0),
92 m_width(0), m_height(0),
93 m_depth(0), m_dont_free(false) {
94
95 create(src.drawable(), width, height, depth);
96 }
97
FbPixmap(Drawable src,unsigned int width,unsigned int height,unsigned int depth)98 FbPixmap::FbPixmap(Drawable src,
99 unsigned int width, unsigned int height,
100 unsigned int depth):m_pm(0),
101 m_width(0), m_height(0),
102 m_depth(0), m_dont_free(false) {
103
104 create(src, width, height, depth);
105 }
106
~FbPixmap()107 FbPixmap::~FbPixmap() {
108 free();
109 }
110
operator =(const FbPixmap & the_copy)111 FbPixmap &FbPixmap::operator = (const FbPixmap &the_copy) {
112 copy(the_copy);
113 return *this;
114 }
115
operator =(Pixmap pm)116 FbPixmap &FbPixmap::operator = (Pixmap pm) {
117 // free pixmap before we set new
118 free();
119
120 if (pm == 0)
121 return *this;
122
123 // get width, height and depth for the pixmap
124 Window root;
125 int x, y;
126 unsigned int border_width, bpp;
127 if (!XGetGeometry(display(),
128 pm,
129 &root,
130 &x, &y,
131 &m_width, &m_height,
132 &border_width,
133 &bpp))
134 return *this;
135
136 m_depth = bpp;
137
138 m_pm = pm;
139
140 return *this;
141 }
142
copy(const FbPixmap & the_copy)143 void FbPixmap::copy(const FbPixmap &the_copy) {
144 /* This function previously retained the old pixmap and copied in
145 the new contents if they had the same dimensions.
146 This broke the image cache, so we don't do that now. If you want to
147 do it, then you'll need to invalidate all copies of this pixmap in
148 the cache */
149 free();
150
151 if (the_copy.drawable() != 0) {
152 create(the_copy.drawable(),
153 the_copy.width(), the_copy.height(),
154 the_copy.depth());
155
156 if (drawable()) {
157 GContext gc(drawable());
158
159 copyArea(the_copy.drawable(),
160 gc.gc(),
161 0, 0,
162 0, 0,
163 width(), height());
164 }
165 }
166 }
167
168 // screen doesn't count if depth is "zero"...
copy(Pixmap pm,unsigned int depth,int screen_num)169 void FbPixmap::copy(Pixmap pm, unsigned int depth, int screen_num) {
170 free();
171 if (pm == 0)
172 return;
173
174 // get width, height and depth for the pixmap
175 Window root;
176 int x, y;
177 unsigned int border_width, bpp;
178 unsigned int new_width, new_height;
179
180 if (!XGetGeometry(display(),
181 pm,
182 &root,
183 &x, &y,
184 &new_width, &new_height,
185 &border_width,
186 &bpp))
187 return;
188
189 if (depth == 0)
190 depth = bpp;
191
192 // create new pixmap and copy area
193 create(root, new_width, new_height, depth);
194
195 GC gc = XCreateGC(display(), drawable(), 0, 0);
196
197 if (depth == bpp) {
198 XCopyArea(display(), pm, drawable(), gc,
199 0, 0,
200 width(), height(),
201 0, 0);
202 } else {
203 XSetForeground(display(), gc, Color("black", screen_num).pixel());
204 XSetBackground(display(), gc, Color("white", screen_num).pixel());
205 XCopyPlane(display(), pm, drawable(), gc,
206 0, 0,
207 width(), height(),
208 0, 0, 1);
209 }
210
211 XFreeGC(display(), gc);
212 }
213
rotate(FbTk::Orientation orient)214 void FbPixmap::rotate(FbTk::Orientation orient) {
215 if (orient == ROT0)
216 return;
217
218 unsigned int oldw = width(), oldh = height();
219 unsigned int neww = oldw, newh = oldh;
220 translateSize(orient, neww, newh);
221
222 // reverse height/width for new pixmap
223 FbPixmap new_pm(drawable(), neww, newh, depth());
224
225 // width|height could be 0. this happens (for example) if
226 // the systemtray-tool is ROT90. in that case 'src_image'
227 // becomes NULL and caused a SIGSEV upon XDestroyImage()
228 // TODO: catch dimensions with '0' earlier?
229 //
230 // make an image copy
231 XImage *src_image = XGetImage(display(), drawable(),
232 0, 0, // pos
233 oldw, oldh, // size
234 ~0, // plane mask
235 ZPixmap); // format
236 if (src_image) {
237
238 GContext gc(drawable());
239
240 if (orient == ROT180) {
241 unsigned int srcx, srcy, destx, desty;
242 for (srcy = 0, desty = oldh; srcy < oldh; ++srcy, --desty) {
243 for (srcx = 0, destx = oldw; srcx < oldw; ++srcx, --destx) {
244 gc.setForeground(XGetPixel(src_image, srcx, srcy));
245 XDrawPoint(display(), new_pm.drawable(), gc.gc(), destx, desty);
246 }
247 }
248 } else {
249 // need to flip x and y
250
251 // set start, end and direction based on rotation
252 // NOTE that startx etc are in the direction of the OLD pixmap
253 unsigned int startx = 0, starty = 0;
254 int dirx = 0, diry = 0;
255 switch (orient) {
256 case ROT90:
257 startx = neww-1;
258 starty = 0;
259 dirx = -1;
260 diry = 1;
261 break;
262 case ROT270:
263 startx = 0;
264 starty = newh-1;
265 dirx = 1;
266 diry = -1;
267 break;
268 default: // kill warning
269 break;
270 }
271
272
273 // copy new area
274 unsigned int srcx, srcy, destx, desty;
275 for (srcy = 0, destx = startx; srcy < oldh; ++srcy, destx+=dirx) {
276 for (srcx = 0, desty = starty; srcx < oldw; ++srcx, desty+=diry) {
277 gc.setForeground(XGetPixel(src_image, srcx, srcy));
278 XDrawPoint(display(), new_pm.drawable(), gc.gc(), destx, desty);
279 }
280 }
281 }
282
283 XDestroyImage(src_image);
284 }
285
286 // free old pixmap and set new from new_pm
287 free();
288
289 m_width = new_pm.width();
290 m_height = new_pm.height();
291 m_depth = new_pm.depth();
292 m_pm = new_pm.release();
293 }
294
scale(unsigned int dest_width,unsigned int dest_height)295 void FbPixmap::scale(unsigned int dest_width, unsigned int dest_height) {
296
297 if (drawable() == 0 ||
298 (dest_width == width() && dest_height == height()))
299 return;
300
301 XImage *src_image = XGetImage(display(), drawable(),
302 0, 0, // pos
303 width(), height(), // size
304 ~0, // plane mask
305 ZPixmap); // format
306 if (src_image == 0)
307 return;
308
309 // create new pixmap with dest size
310 FbPixmap new_pm(drawable(), dest_width, dest_height, depth());
311
312 GContext gc(drawable());
313 // calc zoom
314 float zoom_x = static_cast<float>(width())/static_cast<float>(dest_width);
315 float zoom_y = static_cast<float>(height())/static_cast<float>(dest_height);
316
317 // start scaling
318 float src_x = 0, src_y = 0;
319 for (unsigned int tx=0; tx < dest_width; ++tx, src_x += zoom_x) {
320 src_y = 0;
321 for (unsigned int ty=0; ty < dest_height; ++ty, src_y += zoom_y) {
322 gc.setForeground(XGetPixel(src_image,
323 static_cast<int>(src_x),
324 static_cast<int>(src_y)));
325 XDrawPoint(display(), new_pm.drawable(), gc.gc(), tx, ty);
326 }
327 }
328
329 XDestroyImage(src_image);
330
331 // free old pixmap and set new from new_pm
332 free();
333
334 m_width = new_pm.width();
335 m_height = new_pm.height();
336 m_depth = new_pm.depth();
337 m_pm = new_pm.release();
338 }
339
tile(unsigned int dest_width,unsigned int dest_height)340 void FbPixmap::tile(unsigned int dest_width, unsigned int dest_height) {
341 if (drawable() == 0 ||
342 (dest_width == width() && dest_height == height()))
343 return;
344
345 FbPixmap new_pm(drawable(), width(), height(), depth());
346
347 new_pm.copy(m_pm, 0, 0);
348
349 resize(dest_width, dest_height);
350
351 FbTk::GContext gc(*this);
352
353 gc.setTile(new_pm);
354 gc.setFillStyle(FillTiled);
355
356 fillRectangle(gc.gc(), 0, 0, dest_width, dest_height);
357
358 }
359
360
361
resize(unsigned int width,unsigned int height)362 void FbPixmap::resize(unsigned int width, unsigned int height) {
363 FbPixmap pm(drawable(), width, height, depth());
364 *this = pm.release();
365 }
366
release()367 Pixmap FbPixmap::release() {
368 Pixmap ret = m_pm;
369 m_pm = 0;
370 m_width = 0;
371 m_height = 0;
372 m_depth = 0;
373 return ret;
374 }
375
376 // returns whether or not the background was changed
rootwinPropertyNotify(int screen_num,Atom atom)377 bool FbPixmap::rootwinPropertyNotify(int screen_num, Atom atom) {
378 if (!FbTk::Transparent::haveRender())
379 return false;
380
381 checkAtoms();
382 for (size_t i = 0; i < sizeof(root_props)/sizeof(RootProps); ++i) {
383 if (root_props[i].atom == atom) {
384 Pixmap root_pm = None;
385 Atom real_type;
386 int real_format;
387 unsigned long items_read, items_left;
388 unsigned long *data;
389
390 if (XGetWindowProperty(display(),
391 RootWindow(display(), screen_num),
392 root_props[i].atom,
393 0l, 1l,
394 False, XA_PIXMAP,
395 &real_type, &real_format,
396 &items_read, &items_left,
397 (unsigned char **) &data) == Success) {
398 if (real_format == 32 && items_read == 1) {
399 root_pm = (Pixmap) (*data);
400 }
401 XFree(data);
402 if (root_pm != None)
403 return setRootPixmap(screen_num, root_pm);
404 }
405 return false;
406 }
407 }
408 return false;
409 }
410
411 // returns whether or not the background was changed
setRootPixmap(int screen_num,Pixmap pm)412 bool FbPixmap::setRootPixmap(int screen_num, Pixmap pm) {
413
414 if (s_root_pixmaps.empty()) {
415 int i;
416 for (i = 0; i < ScreenCount(display()); ++i)
417 s_root_pixmaps.push_back(None);
418 }
419
420 if (s_root_pixmaps[screen_num] != pm) {
421 s_root_pixmaps[screen_num] = pm;
422 FbWindow::updatedAlphaBackground(screen_num);
423 return true;
424 }
425 return false;
426 }
427
getRootPixmap(int screen_num,bool force_update)428 Pixmap FbPixmap::getRootPixmap(int screen_num, bool force_update) {
429 /*
430 if (!FbTk::Transparent::haveRender())
431 return None;
432 */
433
434 // check and see if if we have the pixmaps in cache
435 if (!s_root_pixmaps.empty() && !force_update)
436 return s_root_pixmaps[screen_num];
437
438 checkAtoms();
439
440 // else setup pixmap cache
441 int numscreens = ScreenCount(display());
442 for (int i=0; i < numscreens; ++i) {
443 Atom real_type;
444 int real_format;
445 unsigned long items_read, items_left;
446 unsigned long *data;
447
448 static bool print_error = true; // print error_message only once
449
450 Pixmap root_pm = None;
451
452 unsigned int prop = 0;
453 for (prop = 0; prop < sizeof(root_props)/sizeof(RootProps); ++prop) {
454 if (XGetWindowProperty(display(),
455 RootWindow(display(), i),
456 root_props[prop].atom,
457 0l, 1l,
458 False, XA_PIXMAP,
459 &real_type, &real_format,
460 &items_read, &items_left,
461 (unsigned char **) &data) == Success) {
462 if (real_format == 32 && items_read == 1) {
463 if (print_error && strcmp(root_props[prop].name, "_XSETROOT_ID") == 0) {
464 static const char* error_message = {
465 "\n\n !!! WARNING WARNING WARNING WARNING !!!!!\n"
466 " if you experience problems with transparency:\n"
467 " you are using a wallpapersetter that \n"
468 " uses _XSETROOT_ID .. which we do not support.\n"
469 " consult 'fbsetbg -i' or try any other wallpapersetter\n"
470 " that uses _XROOTPMAP_ID !\n"
471 " !!! WARNING WARNING WARNING WARNING !!!!!!\n\n"
472 };
473 cerr<<error_message;
474 print_error = false;
475 } else
476 root_pm = (Pixmap) (*data);
477 }
478 XFree(data);
479 if (root_pm != None)
480 break;
481 }
482 }
483 setRootPixmap(i, root_pm);
484 }
485
486 return s_root_pixmaps[screen_num];
487 }
488
free()489 void FbPixmap::free() {
490 if (!m_dont_free && m_pm != 0)
491 XFreePixmap(display(), m_pm);
492
493 /* note: m_dont_free shouldnt be required anywhere else,
494 because then free() isn't being called appropriately! */
495 m_dont_free = false;
496 m_pm = 0;
497 m_width = 0;
498 m_height = 0;
499 m_depth = 0;
500 }
501
create(Drawable src,unsigned int width,unsigned int height,unsigned int depth)502 void FbPixmap::create(Drawable src,
503 unsigned int width, unsigned int height,
504 unsigned int depth) {
505 if (src == 0)
506 return;
507
508 m_pm = XCreatePixmap(display(),
509 src, width, height, depth);
510 if (m_pm == 0)
511 return;
512
513 m_width = width;
514 m_height = height;
515 m_depth = depth;
516 }
517
518 } // end namespace FbTk
519
520