1 //
2 // "$Id: Fl_Double_Window.cxx 5829 2007-05-14 15:51:00Z matt $"
3 //
4 // Double-buffered window code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2007 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.
12 //
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.
17 //
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
21 // USA.
22 //
23 // Please report all bugs and problems on the following page:
24 //
25 // http://www.fltk.org/str.php
26 //
27
28 #include <config.h>
29 #include <FL/Fl.H>
30 #include <FL/Fl_Double_Window.H>
31 #include <FL/x.H>
32 #include <FL/fl_draw.H>
33
34 // On systems that support double buffering "naturally" the base
35 // Fl_Window class will probably do double-buffer and this subclass
36 // does nothing.
37
38 #if USE_XDBE
39
40 #include <X11/extensions/Xdbe.h>
41
42 static int use_xdbe;
43
can_xdbe()44 static int can_xdbe() {
45 static int tried;
46 if (!tried) {
47 tried = 1;
48 int event_base, error_base;
49 if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0;
50 Drawable root = RootWindow(fl_display,fl_screen);
51 int numscreens = 1;
52 XdbeScreenVisualInfo *a = XdbeGetVisualInfo(fl_display,&root,&numscreens);
53 if (!a) return 0;
54 for (int j = 0; j < a->count; j++)
55 if (a->visinfo[j].visual == fl_visual->visualid
56 /*&& a->visinfo[j].perflevel > 0*/) {use_xdbe = 1; break;}
57 XdbeFreeVisualInfo(a);
58 }
59 return use_xdbe;
60 }
61 #endif
62
show()63 void Fl_Double_Window::show() {
64 Fl_Window::show();
65 }
66
67 #ifdef WIN32
68
69 // Code used to switch output to an off-screen window. See macros in
70 // win32.H which save the old state in local variables.
71
72 typedef struct { BYTE a; BYTE b; BYTE c; BYTE d; } FL_BLENDFUNCTION;
73 typedef BOOL (WINAPI* fl_alpha_blend_func)
74 (HDC,int,int,int,int,HDC,int,int,int,int,FL_BLENDFUNCTION);
75 static fl_alpha_blend_func fl_alpha_blend = NULL;
76 static FL_BLENDFUNCTION blendfunc = { 0, 0, 255, 1};
77
78 /*
79 * This function checks if the version of MSWindows that we
80 * curently run on supports alpha blending for bitmap transfers
81 * and finds the required function if so.
82 */
fl_can_do_alpha_blending()83 char fl_can_do_alpha_blending() {
84 static char been_here = 0;
85 static char can_do = 0;
86 // do this test only once
87 if (been_here) return can_do;
88 been_here = 1;
89 // load the library that implements alpha blending
90 HMODULE hMod = LoadLibrary("MSIMG32.DLL");
91 // give up if that doesn't exist (Win95?)
92 if (!hMod) return 0;
93 // now find the blending function inside that dll
94 fl_alpha_blend = (fl_alpha_blend_func)GetProcAddress(hMod, "AlphaBlend");
95 // give up if we can't find it (Win95)
96 if (!fl_alpha_blend) return 0;
97 // we have the call, but does our display support alpha blending?
98 HDC dc = 0L;//fl_gc;
99 // get the current or the desktop's device context
100 if (!dc) dc = GetDC(0L);
101 if (!dc) return 0;
102 // check the device capabilities flags. However GetDeviceCaps
103 // does not return anything useful, so we have to do it manually:
104
105 HBITMAP bm = CreateCompatibleBitmap(dc, 1, 1);
106 HDC new_gc = CreateCompatibleDC(dc);
107 int save = SaveDC(new_gc);
108 SelectObject(new_gc, bm);
109 /*COLORREF set = */ SetPixel(new_gc, 0, 0, 0x01010101);
110 BOOL alpha_ok = fl_alpha_blend(dc, 0, 0, 1, 1, new_gc, 0, 0, 1, 1, blendfunc);
111 RestoreDC(new_gc, save);
112 DeleteDC(new_gc);
113 DeleteObject(bm);
114
115 if (!fl_gc) ReleaseDC(0L, dc);
116 if (alpha_ok) can_do = 1;
117 return can_do;
118 }
119
fl_makeDC(HBITMAP bitmap)120 HDC fl_makeDC(HBITMAP bitmap) {
121 HDC new_gc = CreateCompatibleDC(fl_gc);
122 SetTextAlign(new_gc, TA_BASELINE|TA_LEFT);
123 SetBkMode(new_gc, TRANSPARENT);
124 #if USE_COLORMAP
125 if (fl_palette) SelectPalette(new_gc, fl_palette, FALSE);
126 #endif
127 SelectObject(new_gc, bitmap);
128 return new_gc;
129 }
130
fl_copy_offscreen(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy)131 void fl_copy_offscreen(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
132 HDC new_gc = CreateCompatibleDC(fl_gc);
133 int save = SaveDC(new_gc);
134 SelectObject(new_gc, bitmap);
135 BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
136 RestoreDC(new_gc, save);
137 DeleteDC(new_gc);
138 }
139
fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy)140 void fl_copy_offscreen_with_alpha(int x,int y,int w,int h,HBITMAP bitmap,int srcx,int srcy) {
141 HDC new_gc = CreateCompatibleDC(fl_gc);
142 int save = SaveDC(new_gc);
143 SelectObject(new_gc, bitmap);
144 BOOL alpha_ok = 0;
145 // first try to alpha blend
146 if (fl_can_do_alpha_blending())
147 alpha_ok = fl_alpha_blend(fl_gc, x, y, w, h, new_gc, srcx, srcy, w, h, blendfunc);
148 // if that failed (it shouldn,t), still copy the bitmap over, but now alpha is 1
149 if (!alpha_ok)
150 BitBlt(fl_gc, x, y, w, h, new_gc, srcx, srcy, SRCCOPY);
151 RestoreDC(new_gc, save);
152 DeleteDC(new_gc);
153 }
154
155 extern void fl_restore_clip();
156
157 #elif defined(__APPLE_QD__)
158
fl_can_do_alpha_blending()159 char fl_can_do_alpha_blending() {
160 return 0;
161 }
162
fl_create_offscreen(int w,int h)163 GWorldPtr fl_create_offscreen(int w, int h) {
164 GWorldPtr gw;
165 Rect bounds;
166 bounds.left=0; bounds.right=w; bounds.top=0; bounds.bottom=h;
167 QDErr err = NewGWorld(&gw, 0, &bounds, 0L, 0L, 0); // 'useTempMem' should not be used (says the Carbon port manual)
168 if ( err == -108 )
169 { }
170 // fl_message( "The application memory is low. Please increase the initial memory assignment.\n" );
171 if (err!=noErr || gw==0L) return 0L;
172 return gw;
173 }
174
fl_copy_offscreen(int x,int y,int w,int h,GWorldPtr gWorld,int srcx,int srcy)175 void fl_copy_offscreen(int x,int y,int w,int h,GWorldPtr gWorld,int srcx,int srcy) {
176 Rect src;
177 if ( !gWorld ) return;
178 src.top = srcy; src.left = srcx; src.bottom = srcy+h; src.right = srcx+w;
179 Rect dst;
180 GrafPtr dstPort; GetPort(&dstPort);
181 dst.top = y; dst.left = x; dst.bottom = y+h; dst.right = x+w;
182 RGBColor rgb, oldbg, oldfg;
183 GetForeColor(&oldfg);
184 GetBackColor(&oldbg);
185 rgb.red = 0xffff; rgb.green = 0xffff; rgb.blue = 0xffff;
186 RGBBackColor( &rgb );
187 rgb.red = 0x0000; rgb.green = 0x0000; rgb.blue = 0x0000;
188 RGBForeColor( &rgb );
189 CopyBits(GetPortBitMapForCopyBits(gWorld), GetPortBitMapForCopyBits(dstPort), &src, &dst, srcCopy, 0L);
190 RGBBackColor(&oldbg);
191 RGBForeColor(&oldfg);
192 }
193
fl_delete_offscreen(GWorldPtr gWorld)194 void fl_delete_offscreen(GWorldPtr gWorld) {
195 DisposeGWorld(gWorld);
196 }
197
198 static GrafPtr prevPort;
199 static GDHandle prevGD;
200
fl_begin_offscreen(GWorldPtr gWorld)201 void fl_begin_offscreen(GWorldPtr gWorld) {
202 GetGWorld( &prevPort, &prevGD );
203 if ( gWorld )
204 {
205 SetGWorld( gWorld, 0 ); // sets the correct port
206 PixMapHandle pm = GetGWorldPixMap(gWorld);
207 Boolean ret = LockPixels(pm);
208 if ( ret == false )
209 {
210 Rect rect;
211 GetPortBounds( gWorld, &rect );
212 UpdateGWorld( &gWorld, 0, &rect, 0, 0, 0 );
213 pm = GetGWorldPixMap( gWorld );
214 LockPixels( pm );
215 }
216 fl_window = 0;
217 }
218 fl_push_no_clip();
219 }
220
fl_end_offscreen()221 void fl_end_offscreen() {
222 GWorldPtr currPort;
223 GDHandle currGD;
224 GetGWorld( &currPort, &currGD );
225 fl_pop_clip();
226 PixMapHandle pm = GetGWorldPixMap(currPort);
227 UnlockPixels(pm);
228 SetGWorld( prevPort, prevGD );
229 fl_window = GetWindowFromPort( prevPort );
230 }
231
232 extern void fl_restore_clip();
233
234 #elif defined(__APPLE_QUARTZ__)
235
fl_can_do_alpha_blending()236 char fl_can_do_alpha_blending() {
237 return 1;
238 }
239
fl_create_offscreen(int w,int h)240 Fl_Offscreen fl_create_offscreen(int w, int h) {
241 void *data = calloc(w*h,4);
242 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
243 CGContextRef ctx = CGBitmapContextCreate(
244 data, w, h, 8, w*4, lut, kCGImageAlphaNoneSkipLast);
245 CGColorSpaceRelease(lut);
246 return (Fl_Offscreen)ctx;
247 }
248
fl_create_offscreen_with_alpha(int w,int h)249 Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h) {
250 void *data = calloc(w*h,4);
251 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
252 CGContextRef ctx = CGBitmapContextCreate(
253 data, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast);
254 CGColorSpaceRelease(lut);
255 return (Fl_Offscreen)ctx;
256 }
257
fl_copy_offscreen(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy)258 void fl_copy_offscreen(int x,int y,int w,int h,Fl_Offscreen osrc,int srcx,int srcy) {
259 CGContextRef src = (CGContextRef)osrc;
260 void *data = CGBitmapContextGetData(src);
261 int sw = CGBitmapContextGetWidth(src);
262 int sh = CGBitmapContextGetHeight(src);
263 CGImageAlphaInfo alpha = CGBitmapContextGetAlphaInfo(src);
264 CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB();
265 CGDataProviderRef src_bytes = CGDataProviderCreateWithData( 0L, data, sw*sh*4, 0L);
266 CGImageRef img = CGImageCreate( sw, sh, 8, 4*8, 4*sw, lut, alpha,
267 src_bytes, 0L, false, kCGRenderingIntentDefault);
268 // fl_push_clip();
269 CGRect rect = { { x, y }, { w, h } };
270 Fl_X::q_begin_image(rect, srcx, srcy, sw, sh);
271 CGContextDrawImage(fl_gc, rect, img);
272 Fl_X::q_end_image();
273 CGImageRelease(img);
274 CGColorSpaceRelease(lut);
275 CGDataProviderRelease(src_bytes);
276 }
277
fl_delete_offscreen(Fl_Offscreen ctx)278 void fl_delete_offscreen(Fl_Offscreen ctx) {
279 if (!ctx) return;
280 void *data = CGBitmapContextGetData((CGContextRef)ctx);
281 CGContextRelease((CGContextRef)ctx);
282 if (!data) return;
283 free(data);
284 }
285
286 const int stack_max = 16;
287 static int stack_ix = 0;
288 static CGContextRef stack_gc[stack_max];
289 static Window stack_window[stack_max];
290
fl_begin_offscreen(Fl_Offscreen ctx)291 void fl_begin_offscreen(Fl_Offscreen ctx) {
292 if (stack_ix<stack_max) {
293 stack_gc[stack_ix] = fl_gc;
294 stack_window[stack_ix] = fl_window;
295 } else
296 fprintf(stderr, "FLTK CGContext Stack overflow error\n");
297 stack_ix++;
298
299 fl_gc = (CGContextRef)ctx;
300 fl_window = 0;
301 //fl_push_no_clip();
302 CGContextSaveGState(fl_gc);
303 Fl_X::q_fill_context();
304 }
305
fl_end_offscreen()306 void fl_end_offscreen() {
307 Fl_X::q_release_context();
308 //fl_pop_clip();
309 if (stack_ix>0)
310 stack_ix--;
311 else
312 fprintf(stderr, "FLTK CGContext Stack underflow error\n");
313 if (stack_ix<stack_max) {
314 fl_gc = stack_gc[stack_ix];
315 fl_window = stack_window[stack_ix];
316 }
317 }
318
319 extern void fl_restore_clip();
320
321 #else // X11
322
323 // maybe someone feels inclined to implement alpha blending on X11?
fl_can_do_alpha_blending()324 char fl_can_do_alpha_blending() {
325 return 0;
326 }
327
328 #endif
329
330 // Fl_Overlay_Window relies on flush(1) copying the back buffer to the
331 // front everywhere, even if damage() == 0, thus erasing the overlay,
332 // and leaving the clip region set to the entire window.
333
flush()334 void Fl_Double_Window::flush() {flush(0);}
335
flush(int eraseoverlay)336 void Fl_Double_Window::flush(int eraseoverlay) {
337 make_current(); // make sure fl_gc is non-zero
338 Fl_X *myi = Fl_X::i(this);
339 if (!myi->other_xid) {
340 #if USE_XDBE
341 if (can_xdbe()) {
342 myi->other_xid =
343 XdbeAllocateBackBufferName(fl_display, fl_xid(this), XdbeCopied);
344 myi->backbuffer_bad = 1;
345 } else
346 #endif
347 #ifdef __APPLE_QD__
348 if ( ( !QDIsPortBuffered( GetWindowPort(myi->xid) ) )
349 || force_doublebuffering_ ) {
350 myi->other_xid = fl_create_offscreen(w(), h());
351 clear_damage(FL_DAMAGE_ALL);
352 }
353 #elif defined(__APPLE_QUARTZ__)
354 if (force_doublebuffering_) {
355 myi->other_xid = fl_create_offscreen(w(), h());
356 clear_damage(FL_DAMAGE_ALL);
357 }
358 #else
359 myi->other_xid = fl_create_offscreen(w(), h());
360 clear_damage(FL_DAMAGE_ALL);
361 #endif
362 }
363 #if USE_XDBE
364 if (use_xdbe) {
365 if (myi->backbuffer_bad) {
366 // Make sure we do a complete redraw...
367 if (myi->region) {XDestroyRegion(myi->region); myi->region = 0;}
368 clear_damage(FL_DAMAGE_ALL);
369 myi->backbuffer_bad = 0;
370 }
371
372 // Redraw as needed...
373 if (damage()) {
374 fl_clip_region(myi->region); myi->region = 0;
375 fl_window = myi->other_xid;
376 draw();
377 fl_window = myi->xid;
378 }
379
380 // Copy contents of back buffer to window...
381 XdbeSwapInfo s;
382 s.swap_window = fl_xid(this);
383 s.swap_action = XdbeCopied;
384 XdbeSwapBuffers(fl_display, &s, 1);
385 return;
386 } else
387 #endif
388 if (damage() & ~FL_DAMAGE_EXPOSE) {
389 fl_clip_region(myi->region); myi->region = 0;
390 #ifdef WIN32
391 HDC _sgc = fl_gc;
392 fl_gc = fl_makeDC(myi->other_xid);
393 int save = SaveDC(fl_gc);
394 fl_restore_clip(); // duplicate region into new gc
395 draw();
396 RestoreDC(fl_gc, save);
397 DeleteDC(fl_gc);
398 fl_gc = _sgc;
399 #elif defined(__APPLE__)
400 if ( myi->other_xid ) {
401 fl_begin_offscreen( myi->other_xid );
402 fl_clip_region( 0 );
403 draw();
404 fl_end_offscreen();
405 } else {
406 draw();
407 }
408 #else // X:
409 fl_window = myi->other_xid;
410 draw();
411 fl_window = myi->xid;
412 #endif
413 }
414 if (eraseoverlay) fl_clip_region(0);
415 // on Irix (at least) it is faster to reduce the area copied to
416 // the current clip region:
417 int X,Y,W,H; fl_clip_box(0,0,w(),h(),X,Y,W,H);
418 if (myi->other_xid) fl_copy_offscreen(X, Y, W, H, myi->other_xid, X, Y);
419 }
420
resize(int X,int Y,int W,int H)421 void Fl_Double_Window::resize(int X,int Y,int W,int H) {
422 int ow = w();
423 int oh = h();
424 Fl_Window::resize(X,Y,W,H);
425 #if USE_XDBE
426 if (use_xdbe) return;
427 #endif
428 Fl_X* myi = Fl_X::i(this);
429 if (myi && myi->other_xid && (ow != w() || oh != h())) {
430 fl_delete_offscreen(myi->other_xid);
431 myi->other_xid = 0;
432 }
433 }
434
hide()435 void Fl_Double_Window::hide() {
436 Fl_X* myi = Fl_X::i(this);
437 if (myi && myi->other_xid) {
438 #if USE_XDBE
439 if (!use_xdbe)
440 #endif
441 fl_delete_offscreen(myi->other_xid);
442 }
443 Fl_Window::hide();
444 }
445
~Fl_Double_Window()446 Fl_Double_Window::~Fl_Double_Window() {
447 hide();
448 }
449
450 //
451 // End of "$Id: Fl_Double_Window.cxx 5829 2007-05-14 15:51:00Z matt $".
452 //
453