Tweak themes for more color consistency.
[ntk.git] / src / Fl_Double_Window.cxx
blobe26b4ad3cd5c5a3615a27942fbb1a3415583b234
1 //
2 // "$Id: Fl_Double_Window.cxx 8383 2011-02-06 12:20:16Z AlbrechtS $"
3 //
4 // Double-buffered window code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2010 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #include <config.h>
29 #include <FL/Fl.H>
30 #include <FL/Fl_Double_Window.H>
31 #include <FL/Fl_Printer.H>
32 #include <FL/x.H>
33 #include <FL/fl_draw.H>
35 #include <FL/Fl_Cairo.H>
37 //#define DEBUG_EXPOSE
39 // On systems that support double buffering "naturally" the base
40 // Fl_Window class will probably do double-buffer and this subclass
41 // does nothing.
43 void Fl_Double_Window::show() {
44 Fl_Window::show();
47 static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy);
49 /** \addtogroup fl_drawings
52 /** Copy a rectangular area of the given offscreen buffer into the current drawing destination.
53 \param x,y position where to draw the copied rectangle
54 \param w,h size of the copied rectangle
55 \param pixmap offscreen buffer containing the rectangle to copy
56 \param srcx,srcy origin in offscreen buffer of rectangle to copy
58 void fl_copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) {
59 if (fl_graphics_driver == Fl_Display_Device::display_device()->driver()) {
60 fl_copy_offscreen_to_display(x, y, w, h, pixmap, srcx, srcy);
62 else { // when copy is not to the display
63 fl_begin_offscreen(pixmap);
64 uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0);
65 fl_end_offscreen();
66 fl_draw_image(img, x, y, w, h, 3, 0);
67 delete[] img;
70 /** @} */
72 #if defined(USE_X11)
74 static void fl_copy_offscreen_to_display(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) {
75 XCopyArea(fl_display, pixmap, fl_window, fl_gc, srcx, srcy, w, h, x, y);
79 // maybe someone feels inclined to implement alpha blending on X11?
80 char fl_can_do_alpha_blending() {
81 return 0;
83 #elif defined(WIN32)
85 // Code used to switch output to an off-screen window. See macros in
86 // win32.H which save the old state in local variables.
88 typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION;
89 typedef BOOL (WINAPI* fl_alpha_blend_func)
90 (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION);
91 static fl_alpha_blend_func fl_alpha_blend = NULL;
92 static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1};
95 * This function checks if the version of MSWindows that we
96 * curently run on supports alpha blending for bitmap transfers
97 * and finds the required function if so.
99 char fl_can_do_alpha_blending() {
100 static char been_here = 0;
101 static char can_do = 0;
102 // do this test only once
103 if (been_here) return can_do;
104 been_here = 1;
105 // load the library that implements alpha blending
106 HMODULE hMod = LoadLibrary("MSIMG32.DLL");
107 // give up if that doesn't exist (Win95?)
108 if (!hMod) return 0;
109 // now find the blending function inside that dll
110 fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend");
111 // give up if we can't find it (Win95)
112 if (!fl_alpha_blend) return 0;
113 // we have the call, but does our display support alpha blending?
114 // get the desktop's device context
115 HDC dc = GetDC(0L);
116 if (!dc) return 0;
117 // check the device capabilities flags. However GetDeviceCaps
118 // does not return anything useful, so we have to do it manually:
120 HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1);
121 HDC new_gc = CreateCompatibleDC(dc);
122 int save = SaveDC(new_gc);
123 SelectObject(new_gc, bm);
124 /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101);
125 BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc);
126 RestoreDC(new_gc, save);
127 DeleteDC(new_gc);
128 DeleteObject(bm);
129 ReleaseDC(0L, dc);
131 if (alpha_ok) can_do = 1;
132 return can_do;
135 HDC fl_makeDC(HBITMAP bitmap) {
136 HDC new_gc = CreateCompatibleDC(fl_gc);
137 SetTextAlign(new_gc, TA_BASELINE|TA_LEFT);
138 SetBkMode(new_gc, TRANSPARENT);
139 #if USE_COLORMAP
140 if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE);
141 #endif
142 SelectObject(new_gc, bitmap);
143 return new_gc;
146 static void fl_copy_offscreen_to_display(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
147 HDC new_gc = CreateCompatibleDC(fl_gc);
148 int save = SaveDC(new_gc);
149 SelectObject(new_gc, bitmap);
150 BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
151 RestoreDC(new_gc, save);
152 DeleteDC(new_gc);
155 void fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
156 HDC new_gc = CreateCompatibleDC(fl_gc);
157 int save = SaveDC(new_gc);
158 SelectObject(new_gc, bitmap);
159 BOOL alpha_ok = 0;
160 // first try to alpha blend
161 // if to printer, always try alpha_blend
162 int to_display = Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id; // true iff display output
163 if ( (to_display && fl_can_do_alpha_blending()) || Fl_Surface_Device::surface()->class_name() == Fl_Printer::class_id) {
164 alpha_ok = fl_alpha_blend(fl_gc, x, y, w, h, new_gc, srcx, srcy, w, h, blendfunc);
166 // if that failed (it shouldn't), still copy the bitmap over, but now alpha is 1
167 if (!alpha_ok) {
168 BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
170 RestoreDC(new_gc, save);
171 DeleteDC(new_gc);
174 extern void fl_restore_clip();
176 #elif defined(__APPLE_QUARTZ__) || defined(FL_DOXYGEN)
178 char fl_can_do_alpha_blending() {
179 return 1;
182 Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h) {
183 void *data = calloc(w*h,4);
184 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
185 CGContextRef ctx = CGBitmapContextCreate(
186 data, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast);
187 CGColorSpaceRelease(lut);
188 return (Fl_Offscreen)ctx;
191 /** \addtogroup fl_drawings
195 /**
196 Creation of an offscreen graphics buffer.
197 \param w,h width and height in pixels of the buffer.
198 \return the created graphics buffer.
200 Fl_Offscreen fl_create_offscreen(int w, int h) {
201 void *data = calloc(w*h,4);
202 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
203 CGContextRef ctx = CGBitmapContextCreate(
204 data, w, h, 8, w*4, lut, kCGImageAlphaNoneSkipLast);
205 CGColorSpaceRelease(lut);
206 return (Fl_Offscreen)ctx;
209 static void bmProviderRelease (void *src, const void *data, size_t size) {
210 CFIndex count = CFGetRetainCount(src);
211 CFRelease(src);
212 if(count == 1) free((void*)data);
215 static void fl_copy_offscreen_to_display(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy) {
216 CGContextRef src = (CGContextRef)osrc;
217 void *data = CGBitmapContextGetData(src);
218 int sw = CGBitmapContextGetWidth(src);
219 int sh = CGBitmapContextGetHeight(src);
220 CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src);
221 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
222 // when output goes to a Quartz printercontext, release of the bitmap must be
223 // delayed after the end of the print page
224 CFRetain(src);
225 CGDataProviderRef src_bytes = CGDataProviderCreateWithData( src, data, sw*sh*4, bmProviderRelease);
226 CGImageRef img = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha,
227 src_bytes, 0L, false, kCGRenderingIntentDefault);
228 // fl_push_clip();
229 CGRect rect = { { x, y }, { w, h } };
230 Fl_X::q_begin_image(rect, srcx, srcy, sw, sh);
231 CGContextDrawImage(fl_gc, rect, img);
232 Fl_X::q_end_image();
233 CGImageRelease(img);
234 CGColorSpaceRelease(lut);
235 CGDataProviderRelease(src_bytes);
238 /** Deletion of an offscreen graphics buffer.
239 \param ctx the buffer to be deleted.
241 void fl_delete_offscreen(Fl_Offscreen ctx) {
242 if (!ctx) return;
243 void *data = CGBitmapContextGetData((CGContextRef)ctx);
244 CFIndex count = CFGetRetainCount(ctx);
245 CGContextRelease((CGContextRef)ctx);
246 if(count == 1) free(data);
249 const int stack_max = 16;
250 static int stack_ix = 0;
251 static CGContextRef stack_gc[stack_max];
252 static Window stack_window[stack_max];
253 static Fl_Surface_Device *_ss;
255 /** Send all subsequent drawing commands to this offscreen buffer.
256 \param ctx the offscreen buffer.
258 void fl_begin_offscreen(Fl_Offscreen ctx) {
259 _ss = Fl_Surface_Device::surface();
260 Fl_Display_Device::display_device()->set_current();
261 if (stack_ix<stack_max) {
262 stack_gc[stack_ix] = fl_gc;
263 stack_window[stack_ix] = fl_window;
264 } else
265 fprintf(stderr, "FLTK CGContext Stack overflow error\n");
266 stack_ix++;
268 fl_gc = (CGContextRef)ctx;
269 fl_window = 0;
270 CGContextSaveGState(fl_gc);
271 fl_push_no_clip();
274 /** Quit sending drawing commands to the current offscreen buffer.
276 void fl_end_offscreen() {
277 Fl_X::q_release_context();
278 fl_pop_clip();
279 if (stack_ix>0)
280 stack_ix--;
281 else
282 fprintf(stderr, "FLTK CGContext Stack underflow error\n");
283 if (stack_ix<stack_max) {
284 fl_gc = stack_gc[stack_ix];
285 fl_window = stack_window[stack_ix];
287 _ss->set_current();
290 /** @} */
292 extern void fl_restore_clip();
294 #else
295 # error unsupported platform
296 #endif
299 Forces the window to be redrawn.
301 void Fl_Double_Window::flush() {flush(0);}
304 Forces the window to be redrawn.
305 \param[in] eraseoverlay non-zero to erase overlay, zero to ignore
307 Fl_Overlay_Window relies on flush(1) copying the back buffer to the
308 front everywhere, even if damage() == 0, thus erasing the overlay,
309 and leaving the clip region set to the entire window.
311 void Fl_Double_Window::flush(int eraseoverlay) {
312 Fl_X *myi = Fl_X::i(this);
314 if (!myi->other_xid) {
316 #if defined(USE_X11) || defined(WIN32)
317 myi->other_xid = fl_create_offscreen(w(), h());
318 clear_damage(FL_DAMAGE_ALL);
319 #elif defined(__APPLE_QUARTZ__)
320 if (force_doublebuffering_) {
321 myi->other_xid = fl_create_offscreen(w(), h());
322 clear_damage(FL_DAMAGE_ALL);
325 #else
326 # error unsupported platform
327 #endif
328 cairo_surface_t *cs = Fl::cairo_create_surface( myi->other_xid, w(), h() );
329 myi->other_cc = cairo_create( cs );
330 cairo_surface_destroy( cs );
332 if ( myi->region ) { cairo_region_destroy( myi->region ); myi->region = 0; }
335 fl_clip_region(myi->region);
337 if ( damage() & FL_DAMAGE_EXPOSE )
339 /* copy exposed area from backbuffer before drawing anything
340 else. this is because when a window is hidden/shown, the focus
341 changes and any widgets that handle FL_FOCUS may have requested
342 a redraw... and yet we still have this pristine back buffer
343 ready to be copied to the screen and it would be a shame to
344 waste it and redraw everything just because one widget wants to
345 change its border color */
347 cairo_set_source_surface( myi->cc, cairo_get_target( myi->other_cc ), 0, 0 );
348 cairo_set_operator( myi->cc, CAIRO_OPERATOR_SOURCE );
349 cairo_paint( myi->cc );
350 cairo_set_operator( myi->cc, CAIRO_OPERATOR_OVER );
353 if (damage() & ~FL_DAMAGE_EXPOSE) {
355 Fl::cairo_make_current( myi->other_cc );
357 #ifdef WIN32
358 HDC _sgc = fl_gc;
359 fl_gc = fl_makeDC(myi->other_xid);
360 int save = SaveDC(fl_gc);
361 fl_restore_clip(); // duplicate region into new gc
362 draw();
363 RestoreDC(fl_gc, save);
364 DeleteDC(fl_gc);
365 fl_gc = _sgc;
366 #elif defined(__APPLE__)
367 if ( myi->other_xid ) {
368 fl_begin_offscreen( myi->other_xid );
369 fl_clip_region( 0 );
370 draw();
372 fl_end_offscreen();
373 } else {
374 draw();
376 #else // X:
377 fl_window = myi->other_xid;
379 // fl_restore_clip();
380 fl_clip_region(myi->region);
382 draw();
384 #ifdef DEBUG_EXPOSE
385 fl_rectf( 0,0, w(), h(), fl_color_add_alpha( FL_RED, 20 ));
386 #endif
388 /* cairo_surface_flush( myi->other_cs ); */
390 fl_window = myi->xid;
392 Fl::cairo_make_current( myi->cc );
394 // fl_restore_clip();
395 fl_clip_region(myi->region);
397 #endif
400 if (eraseoverlay)
402 fl_clip_region(0);
405 // on Irix (at least) it is faster to reduce the area copied to
406 // the current clip region:
408 #if 1 // FLTK_USE_CAIRO
409 cairo_set_source_surface( myi->cc, cairo_get_target( myi->other_cc ), 0, 0 );
410 cairo_set_operator( myi->cc, CAIRO_OPERATOR_SOURCE );
411 cairo_paint( myi->cc );
412 cairo_set_operator( myi->cc, CAIRO_OPERATOR_OVER );
413 #else
414 int X,Y,W,H; fl_clip_box(0,0,w(),h(),X,Y,W,H);
415 if (myi->other_xid) fl_copy_offscreen(X, Y, W, H, myi->other_xid, X, Y);
416 #endif
420 void Fl_Double_Window::resize(int X,int Y,int W,int H) {
421 int ow = w();
422 int oh = h();
423 Fl_Window::resize(X,Y,W,H);
424 Fl_X* myi = Fl_X::i(this);
425 if (myi && myi->other_xid && (ow != w() || oh != h())) {
426 if ( myi->other_cc )
428 cairo_destroy( myi->other_cc ); myi->other_cc = 0;
430 fl_delete_offscreen(myi->other_xid);
431 myi->other_xid = 0;
435 void Fl_Double_Window::hide() {
436 Fl_X* myi = Fl_X::i(this);
437 if (myi && myi->other_xid) {
438 if ( myi->other_cc )
439 cairo_destroy( myi->other_cc ); myi->other_cc = 0;
440 fl_delete_offscreen(myi->other_xid);
441 myi->other_xid = 0;
443 Fl_Window::hide();
447 The destructor <I>also deletes all the children</I>. This allows a
448 whole tree to be deleted at once, without having to keep a pointer to
449 all the children in the user code.
451 Fl_Double_Window::~Fl_Double_Window() {
452 hide();
456 // End of "$Id: Fl_Double_Window.cxx 8383 2011-02-06 12:20:16Z AlbrechtS $".