From d4768073fa67e7f78872bc80f4dff1dd8aa32f69 Mon Sep 17 00:00:00 2001 From: Manolo Gouy Date: Thu, 10 Mar 2016 17:19:34 +0000 Subject: Implement non-rectangular windows using the Window Driver mechanism. git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3-porting@11336 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- FL/Fl_Double_Window.H | 1 + FL/Fl_Window.H | 47 ++------ FL/Fl_Window_Driver.H | 9 +- FL/fl_draw.H | 3 +- src/CMakeLists.txt | 1 - src/Fl_Double_Window.cxx | 72 ++---------- src/Fl_Graphics_Driver.cxx | 10 ++ src/Fl_Overlay_Window.cxx | 17 +++ src/Fl_Window.cxx | 20 ++-- src/Fl_Window_Driver.cxx | 123 +++++++++++++++++++- src/Fl_cocoa.mm | 2 +- src/Fl_x.cxx | 5 +- src/Makefile | 1 - src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H | 17 +++ src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx | 134 ++++++++++++++++++++++ src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H | 12 ++ src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx | 151 ++++++++++++++++++++++++- src/drivers/X11/Fl_X11_Window_Driver.H | 15 ++- src/drivers/X11/Fl_X11_Window_Driver.cxx | 112 +++++++++++++++++- 19 files changed, 625 insertions(+), 127 deletions(-) diff --git a/FL/Fl_Double_Window.H b/FL/Fl_Double_Window.H index 4ffcb65d1..fc7d1a93d 100644 --- a/FL/Fl_Double_Window.H +++ b/FL/Fl_Double_Window.H @@ -41,6 +41,7 @@ class Fl_Overlay_Window; class FL_EXPORT Fl_Double_Window : public Fl_Window { protected: void flush(int eraseoverlay); + Fl_Double_Window(int X, int Y, int W, int H, const char *l, Fl_Window_Driver *driver); public: /** Return non-null if this is an Fl_Overlay_Window object. diff --git a/FL/Fl_Window.H b/FL/Fl_Window.H index ce9582475..8884d0b67 100644 --- a/FL/Fl_Window.H +++ b/FL/Fl_Window.H @@ -101,42 +101,6 @@ class FL_EXPORT Fl_Window : public Fl_Group { // cursor stuff Fl_Cursor cursor_default; -protected: - /** Data supporting a non-rectangular window shape */ - struct shape_data_type { - int lw_; ///< width of shape image - int lh_; ///< height of shape image - Fl_Image* shape_; ///< shape image -#if defined(__APPLE__) // PORTME: Fl_Window_Driver - per-window shape information, move to Fl_X/Fl_Window_Driver - typedef struct CGImage* CGImageRef; - CGImageRef mask; -#elif defined(WIN32) - // not needed -#elif defined(FL_PORTING) -# pragma message "FL_PORTING: define storage for a window mask here if needed" -#else // X11 - // not needed -#endif - Fl_Bitmap *todelete_; ///< auxiliary bitmap image - }; - - shape_data_type *shape_data_; ///< non-null means the window has a non-rectangular shape -private: - void shape_bitmap_(Fl_Image* b); - void shape_alpha_(Fl_Image* img, int offset); - void shape_pixmap_(Fl_Image* pixmap); -public: - void shape(const Fl_Image* img); - /** Set the window's shape with an Fl_Image. - \see void shape(const Fl_Image* img) - */ - inline void shape(const Fl_Image& b) { shape(&b); } -#if defined(WIN32) || defined(__APPLE__) || defined(FL_DOXYGEN) // PORTME: Fl_Window_Driver - per-window shape -#elif defined(FL_PORTING) -# pragma message "FL_PORTING: do you need a combine_mask() function in Fl_Window?" -#else // X11 - void combine_mask(void); -#endif private: void size_range_(); @@ -152,7 +116,7 @@ protected: /** Stores the last window that was made current. See current() const */ static Fl_Window *current_; - virtual void draw(); + void draw(); /** Forces the window to be drawn, this window is also made current and calls draw(). */ virtual void flush(); @@ -180,6 +144,7 @@ protected: void free_icons(); + Fl_Window(int x, int y, int w, int h, const char* title, Fl_Window_Driver *driver); public: /** @@ -640,9 +605,13 @@ public: Fl_Window_Driver *driver() { return pWindowDriver; } /** - Return non-null if this is an Fl_Overlay_Window object. + Return non-null if this is an Fl_Double_Window object. */ - virtual Fl_Double_Window *as_double_window() {return NULL; } + virtual Fl_Double_Window *as_double_window() {return NULL;} + void shape(const Fl_Image* img); + void shape(const Fl_Image& b) ; + int is_shaped(); + }; #endif diff --git a/FL/Fl_Window_Driver.H b/FL/Fl_Window_Driver.H index e252d22bf..98934a3d4 100644 --- a/FL/Fl_Window_Driver.H +++ b/FL/Fl_Window_Driver.H @@ -28,14 +28,18 @@ class Fl_Window; +class Fl_Image; /** \brief A base class for platform specific window handling code. */ class FL_EXPORT Fl_Window_Driver { + friend class Fl_Window; protected: Fl_Window *pWindow; + struct shape_data_type; + shape_data_type *shape_data_; ///< non-null means the window has a non-rectangular shape public: Fl_Window_Driver(Fl_Window *); virtual ~Fl_Window_Driver(); @@ -47,7 +51,10 @@ public: virtual void take_focus() { } virtual int double_flush(int eraseoverlay); virtual void destroy_double_buffer(); - void draw(); + virtual void draw(); + void shape_pixmap_(Fl_Image* pixmap); + virtual void shape(const Fl_Image* img) {} + virtual void shape_alpha_(Fl_Image* img, int offset) {} }; diff --git a/FL/fl_draw.H b/FL/fl_draw.H index ecd6c1410..605f17d51 100644 --- a/FL/fl_draw.H +++ b/FL/fl_draw.H @@ -755,8 +755,7 @@ inline void fl_draw_image_mono(Fl_Draw_Image_Cb cb, void* data, int X,int Y,int \returns 1 if true alpha blending supported by platform \returns 0 not supported so FLTK will use screen door transparency */ -/* note: doxygen comment here to avoid triplication in os-speciic files */ -FL_EXPORT char fl_can_do_alpha_blending(); +inline char fl_can_do_alpha_blending() {return Fl_Display_Device::display_device()->driver()->can_do_alpha_blending();} /** Reads an RGB(A) image from the current window or off-screen buffer. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7e9a3dc3b..8b568a7ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,6 @@ set(CPPFILES Fl_Window_fullscreen.cxx Fl_Window_hotspot.cxx Fl_Window_iconize.cxx - Fl_Window_shape.cxx Fl_Wizard.cxx Fl_XBM_Image.cxx Fl_XPM_Image.cxx diff --git a/src/Fl_Double_Window.cxx b/src/Fl_Double_Window.cxx index b517be2c3..8edb316b8 100644 --- a/src/Fl_Double_Window.cxx +++ b/src/Fl_Double_Window.cxx @@ -21,7 +21,6 @@ #include #include -#include #include #include #include @@ -33,14 +32,20 @@ Fl_Double_Window::Fl_Double_Window(int W, int H, const char *l) -: Fl_Window(W,H,l) +: Fl_Window(0, 0, W, H, l, Fl_Window_Driver::newWindowDriver(this)) { type(FL_DOUBLE_WINDOW); + clear_flag(FORCE_POSITION); } Fl_Double_Window::Fl_Double_Window(int X, int Y, int W, int H, const char *l) -: Fl_Window(X,Y,W,H,l) +: Fl_Window(X,Y,W,H,l, Fl_Window_Driver::newWindowDriver(this)) +{ + type(FL_DOUBLE_WINDOW); +} + +Fl_Double_Window::Fl_Double_Window(int x, int y, int w, int h, const char* title, Fl_Window_Driver *driver) : Fl_Window(x,y,w,h,title, driver) { type(FL_DOUBLE_WINDOW); } @@ -51,19 +56,9 @@ void Fl_Double_Window::show() { } -/** see fl_copy_offscreen() */ -void Fl_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) -{ - fl_begin_offscreen(pixmap); - uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0); - fl_end_offscreen(); - fl_draw_image(img, x, y, w, h, 3, 0); - delete[] img; -} - -char fl_can_do_alpha_blending() { +/*char fl_can_do_alpha_blending() { return Fl_Display_Device::display_device()->driver()->can_do_alpha_blending(); -} +}*/ /** Forces the window to be redrawn. @@ -94,29 +89,6 @@ void Fl_Double_Window::flush(int eraseoverlay) { } } -int Fl_Window_Driver::double_flush(int eraseoverlay) { - /* This is a working, platform-independent implementation. - Some platforms may re-implement it for their own logic: - - on Mac OS, the system double buffers all windows, so it is - reimplemented to do the same as Fl_Window::flush(), except for - Fl_Overlay_Window's which fall back on this implementation. - - on Xlib, it is reimplemented if the Xdbe extension is available. - */ - Fl_X *i = Fl_X::i(pWindow); - - if (!i->other_xid) { - i->other_xid = fl_create_offscreen(pWindow->w(), pWindow->h()); - pWindow->clear_damage(FL_DAMAGE_ALL); - } - if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) { - fl_clip_region(i->region); i->region = 0; - fl_begin_offscreen(i->other_xid); - fl_graphics_driver->clip_region( 0 ); - draw(); - fl_end_offscreen(); - } - return 0; -} void Fl_Double_Window::resize(int X,int Y,int W,int H) { int ow = w(); @@ -135,16 +107,6 @@ void Fl_Double_Window::hide() { Fl_Window::hide(); } -void Fl_Window_Driver::destroy_double_buffer() { - Fl_X *i = Fl_X::i(pWindow); - /* This is a working, platform-independent implementation. - Some platforms may re-implement it for their own logic: - - on Xlib, it is reimplemented if the Xdbe extension is available. - */ - fl_delete_offscreen(i->other_xid); - i->other_xid = 0; -} - /** The destructor also deletes all the children. This allows a @@ -156,20 +118,6 @@ Fl_Double_Window::~Fl_Double_Window() { } -Fl_Overlay_Window::Fl_Overlay_Window(int W, int H, const char *l) -: Fl_Double_Window(W,H,l) -{ - overlay_ = 0; - image(0); -} - - -Fl_Overlay_Window::Fl_Overlay_Window(int X, int Y, int W, int H, const char *l) -: Fl_Double_Window(X,Y,W,H,l) -{ - overlay_ = 0; - image(0); -} // diff --git a/src/Fl_Graphics_Driver.cxx b/src/Fl_Graphics_Driver.cxx index d8d0a8d5a..83c6eccf4 100644 --- a/src/Fl_Graphics_Driver.cxx +++ b/src/Fl_Graphics_Driver.cxx @@ -61,6 +61,16 @@ int Fl_Graphics_Driver::draw_scaled(Fl_Image *img, int X, int Y, int W, int H) { return 0; } +/** see fl_copy_offscreen() */ +void Fl_Graphics_Driver::copy_offscreen(int x, int y, int w, int h, Fl_Offscreen pixmap, int srcx, int srcy) +{ + fl_begin_offscreen(pixmap); + uchar *img = fl_read_image(NULL, srcx, srcy, w, h, 0); + fl_end_offscreen(); + fl_draw_image(img, x, y, w, h, 3, 0); + delete[] img; +} + // // End of "$Id$". diff --git a/src/Fl_Overlay_Window.cxx b/src/Fl_Overlay_Window.cxx index c5257abd7..58224f399 100644 --- a/src/Fl_Overlay_Window.cxx +++ b/src/Fl_Overlay_Window.cxx @@ -26,6 +26,23 @@ #include #include #include +#include + + +Fl_Overlay_Window::Fl_Overlay_Window(int W, int H, const char *l) +: Fl_Double_Window(0,0,W,H,l,Fl_Window_Driver::newWindowDriver(this)) +{ + overlay_ = 0; + image(0); + clear_flag(FORCE_POSITION); +} + +Fl_Overlay_Window::Fl_Overlay_Window(int X, int Y, int W, int H, const char *l) +: Fl_Double_Window(X,Y,W,H,l,Fl_Window_Driver::newWindowDriver(this)) +{ + overlay_ = 0; + image(0); +} void Fl_Overlay_Window::show() { Fl_Double_Window::show(); diff --git a/src/Fl_Window.cxx b/src/Fl_Window.cxx index c3cb7f498..c1b51a071 100644 --- a/src/Fl_Window.cxx +++ b/src/Fl_Window.cxx @@ -35,6 +35,7 @@ char *Fl_Window::default_xclass_ = 0L; void Fl_Window::_Fl_Window() { + cursor_default = FL_CURSOR_DEFAULT; type(FL_WINDOW); box(FL_FLAT_BOX); if (Fl::scheme_bg_) { @@ -52,7 +53,6 @@ void Fl_Window::_Fl_Window() { resizable(0); size_range_set = 0; minw = maxw = minh = maxh = 0; - shape_data_ = NULL; no_fullscreen_x = 0; no_fullscreen_y = 0; no_fullscreen_w = w(); @@ -68,19 +68,22 @@ Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l) : Fl_Group(X, Y, W, H, l), pWindowDriver(Fl_Window_Driver::newWindowDriver(this)) { - cursor_default = FL_CURSOR_DEFAULT; + _Fl_Window(); + set_flag(FORCE_POSITION); +} +Fl_Window::Fl_Window(int X,int Y,int W, int H, const char *l, Fl_Window_Driver *driver) : Fl_Group(X, Y, W, H, l), pWindowDriver(driver) +{ _Fl_Window(); set_flag(FORCE_POSITION); } + Fl_Window::Fl_Window(int W, int H, const char *l) : // fix common user error of a missing end() with current(0): Fl_Group((Fl_Group::current(0),0), 0, W, H, l), pWindowDriver(Fl_Window_Driver::newWindowDriver(this)) { - cursor_default = FL_CURSOR_DEFAULT; - _Fl_Window(); clear_visible(); } @@ -92,15 +95,6 @@ Fl_Window::~Fl_Window() { } free_icons(); delete icon_; - if (shape_data_) { - if (shape_data_->todelete_) delete shape_data_->todelete_; -#if defined(__APPLE__) // PORTME: Fl_Window_Driver - platform window driver - if (shape_data_->mask) { - CGImageRelease(shape_data_->mask); - } -#endif - delete shape_data_; - } } diff --git a/src/Fl_Window_Driver.cxx b/src/Fl_Window_Driver.cxx index 37ce9637f..de8760f90 100644 --- a/src/Fl_Window_Driver.cxx +++ b/src/Fl_Window_Driver.cxx @@ -22,11 +22,13 @@ #include "config_lib.h" #include #include +#include Fl_Window_Driver::Fl_Window_Driver(Fl_Window *win) : pWindow(win) { + shape_data_ = NULL; } @@ -34,10 +36,129 @@ Fl_Window_Driver::~Fl_Window_Driver() { } +int Fl_Window_Driver::double_flush(int eraseoverlay) { + /* This is a working, platform-independent implementation. + Some platforms may re-implement it for their own logic: + - on Mac OS, the system double buffers all windows, so it is + reimplemented to do the same as Fl_Window::flush(), except for + Fl_Overlay_Window's which fall back on this implementation. + - on Xlib, it is reimplemented if the Xdbe extension is available. + */ + Fl_X *i = Fl_X::i(pWindow); + + if (!i->other_xid) { + i->other_xid = fl_create_offscreen(pWindow->w(), pWindow->h()); + pWindow->clear_damage(FL_DAMAGE_ALL); + } + if (pWindow->damage() & ~FL_DAMAGE_EXPOSE) { + fl_clip_region(i->region); i->region = 0; + fl_begin_offscreen(i->other_xid); + fl_graphics_driver->clip_region( 0 ); + draw(); + fl_end_offscreen(); + } + return 0; +} + +void Fl_Window_Driver::destroy_double_buffer() { + Fl_X *i = Fl_X::i(pWindow); + /* This is a working, platform-independent implementation. + Some platforms may re-implement it for their own logic: + - on Xlib, it is reimplemented if the Xdbe extension is available. + */ + fl_delete_offscreen(i->other_xid); + i->other_xid = 0; +} + void Fl_Window_Driver::draw() { - pWindow->draw(); + // The following is similar to Fl_Group::draw(), but ... + // + // - draws the box at (0,0), i.e. with x=0 and y=0 instead of x() and y() + // - does NOT draw the label (text) + // - draws the image only if FL_ALIGN_INSIDE is set + // + // Note: The label (text) of top level windows is drawn in the title bar. + // Other windows do not draw their labels at all, unless drawn by their + // parent widgets or by special draw() methods (derived classes). + + if (pWindow->damage() & ~FL_DAMAGE_CHILD) { // draw the entire thing + pWindow->draw_box(pWindow->box(),0,0,pWindow->w(),pWindow->h(),pWindow->color()); // draw box with x/y = 0 + + if (pWindow->image() && (pWindow->align() & FL_ALIGN_INSIDE)) { // draw the image only + Fl_Label l1; + memset(&l1,0,sizeof(l1)); + l1.align_ = pWindow->align(); + l1.image = pWindow->image(); + if (!pWindow->active_r() && l1.image && l1.deimage) l1.image = l1.deimage; + l1.type = pWindow->labeltype(); + l1.draw(0,0,pWindow->w(),pWindow->h(),pWindow->align()); + } + } + pWindow->draw_children(); + +# if defined(FLTK_USE_CAIRO) + Fl::cairo_make_current(this); // checkout if an update is necessary +# endif +} + +void Fl_Window::draw() {pWindowDriver->draw();} + +/** Assigns a non-rectangular shape to the window. + This function gives an arbitrary shape (not just a rectangular region) to an Fl_Window. + An Fl_Image of any dimension can be used as mask; it is rescaled to the window's dimension as needed. + + The layout and widgets inside are unaware of the mask shape, and most will act as though the window's + rectangular bounding box is available + to them. It is up to you to make sure they adhere to the bounds of their masking shape. + + The \p img argument can be an Fl_Bitmap, Fl_Pixmap, Fl_RGB_Image or Fl_Shared_Image: + \li With Fl_Bitmap or Fl_Pixmap, the shaped window covers the image part where bitmap bits equal one, + or where the pixmap is not fully transparent. + \li With an Fl_RGB_Image with an alpha channel (depths 2 or 4), the shaped window covers the image part + that is not fully transparent. + \li With an Fl_RGB_Image of depth 1 (gray-scale) or 3 (RGB), the shaped window covers the non-black image part. + \li With an Fl_Shared_Image, the shape is determined by rules above applied to the underlying image. + The shared image should not have been scaled through Fl_Shared_Image::scale(). + + Platform details: + \li On the unix/linux platform, the SHAPE extension of the X server is required. + This function does control the shape of Fl_Gl_Window instances. + \li On the MSWindows platform, this function does nothing with class Fl_Gl_Window. + \li On the Mac platform, OS version 10.4 or above is required. + An 8-bit shape-mask is used when \p img is an Fl_RGB_Image: + with depths 2 or 4, the image alpha channel becomes the shape mask such that areas with alpha = 0 + are out of the shaped window; + with depths 1 or 3, white and black are in and out of the + shaped window, respectively, and other colors give intermediate masking scores. + This function does nothing with class Fl_Gl_Window. + + The window borders and caption created by the window system are turned off by default. They + can be re-enabled by calling Fl_Window::border(1). + + A usage example is found at example/shapedwindow.cxx. + + \version 1.3.3 (and requires compilation with FL_ABI_VERSION >= 10303) + */ +void Fl_Window::shape(const Fl_Image* img) {pWindowDriver->shape(img);} + + +/** Set the window's shape with an Fl_Image. + \see void shape(const Fl_Image* img) + */ +void Fl_Window::shape(const Fl_Image& img) {pWindowDriver->shape(&img);} + +/** Returns non NULL when the window has been assigned a non-rectangular shape */ +int Fl_Window::is_shaped() {return pWindowDriver->shape_data_ != NULL;} + +void Fl_Window_Driver::shape_pixmap_(Fl_Image* pixmap) { + Fl_RGB_Image* rgba = new Fl_RGB_Image((Fl_Pixmap*)pixmap); + shape_alpha_(rgba, 3); + delete rgba; } + + + // // End of "$Id$". // diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm index bbd26c389..256569a04 100644 --- a/src/Fl_cocoa.mm +++ b/src/Fl_cocoa.mm @@ -2998,7 +2998,7 @@ void Fl_X::make(Fl_Window* w) [cw setHasShadow:YES]; [cw setAcceptsMouseMovedEvents:YES]; } - if (w->shape_data_) { + if (w->is_shaped()) { [cw setOpaque:NO]; // shaped windows must be non opaque [cw setBackgroundColor:[NSColor clearColor]]; // and with transparent background color } diff --git a/src/Fl_x.cxx b/src/Fl_x.cxx index 26cec974b..cd3a70f6c 100644 --- a/src/Fl_x.cxx +++ b/src/Fl_x.cxx @@ -53,6 +53,7 @@ class Fl_Widget *fl_selection_requestor; # include # include "flstring.h" # include "drivers/X11/Fl_X11_Screen_Driver.H" +# include "drivers/X11/Fl_X11_Window_Driver.H" # include # include # include @@ -2557,8 +2558,8 @@ void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap) } #endif - if (win->shape_data_) { - win->combine_mask(); + if (win->is_shaped()) { + ((Fl_X11_Window_Driver*)win->pWindowDriver)->combine_mask(); } XMapWindow(fl_display, xp->xid); if (showit) { diff --git a/src/Makefile b/src/Makefile index 3f1f317b0..00711c077 100644 --- a/src/Makefile +++ b/src/Makefile @@ -103,7 +103,6 @@ CPPFILES = \ Fl_Window_fullscreen.cxx \ Fl_Window_hotspot.cxx \ Fl_Window_iconize.cxx \ - Fl_Window_shape.cxx \ Fl_Wizard.cxx \ Fl_XBM_Image.cxx \ Fl_XPM_Image.cxx \ diff --git a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H index b0c756aa1..05e0cd93a 100644 --- a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H +++ b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.H @@ -26,7 +26,10 @@ #define FL_COCOA_WINDOW_DRIVER_H #include +#include +class Fl_Image; +class Fl_Window; /* Move everything here that manages the native window interface. @@ -41,12 +44,26 @@ ? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx? */ +struct Fl_Window_Driver::shape_data_type { + int lw_; ///< width of shape image + int lh_; ///< height of shape image + Fl_Image* shape_; ///< shape image + typedef struct CGImage* CGImageRef; + CGImageRef mask; + Fl_Bitmap *todelete_; ///< auxiliary bitmap image +}; class FL_EXPORT Fl_Cocoa_Window_Driver : public Fl_Window_Driver { +private: + void shape_bitmap_(Fl_Image* b); + void shape_alpha_(Fl_Image* img, int offset); public: Fl_Cocoa_Window_Driver(Fl_Window*); + ~Fl_Cocoa_Window_Driver(); virtual void take_focus(); + virtual void shape(const Fl_Image* img); + virtual void draw(); }; diff --git a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx index 4764390ab..8a3644bb1 100644 --- a/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx +++ b/src/drivers/Cocoa/Fl_Cocoa_Window_Driver.cxx @@ -21,6 +21,7 @@ #include "Fl_Cocoa_Window_Driver.h" #include #include +#include // class used for Fl_Double_Window but not for Fl_Overlay_Window class Fl_Cocoa_Double_Window_Driver : public Fl_Cocoa_Window_Driver { @@ -46,6 +47,16 @@ Fl_Cocoa_Window_Driver::Fl_Cocoa_Window_Driver(Fl_Window *win) { } +Fl_Cocoa_Window_Driver::~Fl_Cocoa_Window_Driver() +{ + if (shape_data_) { + if (shape_data_->todelete_) delete shape_data_->todelete_; + if (shape_data_->mask) { + CGImageRelease(shape_data_->mask); + } + delete shape_data_; + } +} extern Fl_Window *fl_xfocus; @@ -56,6 +67,129 @@ void Fl_Cocoa_Window_Driver::take_focus() if (x) x->set_key_window(); } + +static void MyProviderReleaseData (void *info, const void *data, size_t size) { + delete[] (uchar*)data; +} + +// bitwise inversion of all 4-bit quantities +static const unsigned char swapped[16] = {0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15}; + +static inline uchar swap_byte(const uchar b) { + // reverse the order of bits of byte b: 1->8 becomes 8->1 + return (swapped[b & 0xF] << 4) | swapped[b >> 4]; +} + + +void Fl_Cocoa_Window_Driver::shape_bitmap_(Fl_Image* b) { + shape_data_->shape_ = b; + if (b) { + // complement mask bits and perform bitwise inversion of all bytes and also reverse top and bottom + int bytes_per_row = (b->w() + 7)/8; + uchar *from = new uchar[bytes_per_row * b->h()]; + for (int i = 0; i < b->h(); i++) { + uchar *p = (uchar*)(*b->data()) + bytes_per_row * i; + uchar *last = p + bytes_per_row; + uchar *q = from + (b->h() - 1 - i) * bytes_per_row; + while (p < last) { + *q++ = swap_byte(~*p++); + } + } + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, bytes_per_row * b->h(), MyProviderReleaseData); + shape_data_->mask = CGImageMaskCreate(b->w(), b->h(), 1, 1, bytes_per_row, provider, NULL, false); + CFRelease(provider); + } +} + + +void Fl_Cocoa_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { + int i, d = img->d(), w = img->w(), h = img->h(); + shape_data_->shape_ = img; + if (shape_data_->shape_) { + // reverse top and bottom and convert to gray scale if img->d() == 3 and complement bits + int bytes_per_row = w * d; + uchar *from = new uchar[w * h]; + for ( i = 0; i < h; i++) { + uchar *p = (uchar*)(*img->data()) + bytes_per_row * i + offset; + uchar *last = p + bytes_per_row; + uchar *q = from + (h - 1 - i) * w; + while (p < last) { + if (d == 3) { + unsigned u = *p++; + u += *p++; + u += *p++; + *q++ = ~(u/3); + } + else { + *q++ = ~(*p); + p += d; + } + } + } + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, from, w * h, MyProviderReleaseData); + shape_data_->mask = CGImageMaskCreate(w, h, 8, 8, w, provider, NULL, false); + CFRelease(provider); + } +} + + +void Fl_Cocoa_Window_Driver::shape(const Fl_Image* img) { +# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + if (shape_data_) { + if (shape_data_->todelete_) { delete shape_data_->todelete_; } + if (shape_data_->mask) { CGImageRelease(shape_data_->mask); } + } + else { + shape_data_ = new shape_data_type; + } + memset(shape_data_, 0, sizeof(shape_data_type)); + pWindow->border(false); + int d = img->d(); + if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img); + else if (d == 0) shape_bitmap_((Fl_Image*)img); + else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); + else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); +#endif +} + + +void Fl_Cocoa_Window_Driver::draw() +{ + CGContextRef gc = (CGContextRef)Fl_Surface_Device::surface()->driver()->gc(); + if (shape_data_) { +# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + if (shape_data_->mask && (&CGContextClipToMask != NULL)) { + CGContextClipToMask(gc, CGRectMake(0,0, pWindow->w(), pWindow->h()), shape_data_->mask); // requires Mac OS 10.4 + } + CGContextSaveGState(gc); +# endif + } + Fl_Window_Driver::draw(); + // on OS X, windows have no frame. Before OS X 10.7, to resize a window, we drag the lower right + // corner. This code draws a little ribbed triangle for dragging. + if (fl_mac_os_version < 100700 && gc && !pWindow->parent() && pWindow->resizable() ) { + int dx = Fl::box_dw(pWindow->box())-Fl::box_dx(pWindow->box()); + int dy = Fl::box_dh(pWindow->box())-Fl::box_dy(pWindow->box()); + if (dx<=0) dx = 1; + if (dy<=0) dy = 1; + int x1 = pWindow->w()-dx-1, x2 = x1, y1 = pWindow->h()-dx-1, y2 = y1; + Fl_Color c[4] = { + pWindow->color(), + fl_color_average(pWindow->color(), FL_WHITE, 0.7f), + fl_color_average(pWindow->color(), FL_BLACK, 0.6f), + fl_color_average(pWindow->color(), FL_BLACK, 0.8f), + }; + int i; + for (i=dx; i<12; i++) { + fl_color(c[i&3]); + fl_line(x1--, y1, x2, y2--); + } + } +# if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + if (shape_data_) CGContextRestoreGState(gc); +# endif +} + // // End of "$Id$". // diff --git a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H index 8a4998753..252550a56 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H +++ b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.H @@ -42,10 +42,22 @@ ? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx? */ +struct Fl_Window_Driver::shape_data_type { + int lw_; ///< width of shape image + int lh_; ///< height of shape image + Fl_Image* shape_; ///< shape image + Fl_Bitmap *todelete_; ///< auxiliary bitmap image +}; + class FL_EXPORT Fl_WinAPI_Window_Driver : public Fl_Window_Driver { +private: + void shape_bitmap_(Fl_Image* b); + void shape_alpha_(Fl_Image* img, int offset); public: Fl_WinAPI_Window_Driver(Fl_Window*); + virtual void shape(const Fl_Image* img); + virtual void draw(); }; diff --git a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx index c8d18a6c5..4947ff6e2 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx +++ b/src/drivers/WinAPI/Fl_WinAPI_Window_Driver.cxx @@ -19,7 +19,7 @@ #include "../../config_lib.h" #include "Fl_WinAPI_Window_Driver.H" - +#include Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) { @@ -32,6 +32,155 @@ Fl_WinAPI_Window_Driver::Fl_WinAPI_Window_Driver(Fl_Window *win) { } +void Fl_WinAPI_Window_Driver::shape_bitmap_(Fl_Image* b) { + shape_data_->shape_ = b; +} + +void Fl_WinAPI_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { + int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8; + unsigned u; + uchar byte, onebit; + // build an Fl_Bitmap covering the non-fully transparent/black part of the image + const uchar* bits = new uchar[h*bytesperrow]; // to store the bitmap + const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of rgba pixels + for (i = 0; i < h; i++) { + uchar *p = (uchar*)bits + i * bytesperrow; + byte = 0; + onebit = 1; + for (j = 0; j < w; j++) { + if (d == 3) { + u = *alpha; + u += *(alpha+1); + u += *(alpha+2); + } + else u = *alpha; + if (u > 0) { // if the pixel is not fully transparent/black + byte |= onebit; // turn on the corresponding bit of the bitmap + } + onebit = onebit << 1; // move the single set bit one position to the left + if (onebit == 0 || j == w-1) { + onebit = 1; + *p++ = byte; // store in bitmap one pack of bits + byte = 0; + } + alpha += d; // point to alpha value of next pixel + } + } + Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h); + bitmap->alloc_array = 1; + shape_bitmap_(bitmap); + shape_data_->todelete_ = bitmap; +} + +void Fl_WinAPI_Window_Driver::shape(const Fl_Image* img) { + if (shape_data_) { + if (shape_data_->todelete_) { delete shape_data_->todelete_; } + } + else { + shape_data_ = new shape_data_type; + } + memset(shape_data_, 0, sizeof(shape_data_type)); + pWindow->border(false); + int d = img->d(); + if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img); + else if (d == 0) shape_bitmap_((Fl_Image*)img); + else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); + else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); +} + + +static inline BYTE bit(int x) { return (BYTE)(1 << (x%8)); } + +static HRGN bitmap2region(Fl_Image* image) { + HRGN hRgn = 0; + /* Does this need to be dynamically determined, perhaps? */ + const int ALLOC_UNIT = 100; + DWORD maxRects = ALLOC_UNIT; + + RGNDATA* pData = (RGNDATA*)malloc(sizeof(RGNDATAHEADER)+(sizeof(RECT)*maxRects)); + pData->rdh.dwSize = sizeof(RGNDATAHEADER); + pData->rdh.iType = RDH_RECTANGLES; + pData->rdh.nCount = pData->rdh.nRgnSize = 0; + SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); + + const int bytesPerLine = (image->w() + 7)/8; + BYTE* p, *data = (BYTE*)*image->data(); + for (int y = 0; y < image->h(); y++) { + // each row, left to right + for (int x = 0; x < image->w(); x++) { + int x0 = x; + while (x < image->w()) { + p = data + x / 8; + if (!((*p) & bit(x))) break; // transparent pixel + x++; + } + if (x > x0) { + RECT *pr; + /* Add the pixels (x0, y) to (x, y+1) as a new rectangle + * in the region + */ + if (pData->rdh.nCount >= maxRects) { + maxRects += ALLOC_UNIT; + pData = (RGNDATA*)realloc(pData, sizeof(RGNDATAHEADER) + + (sizeof(RECT)*maxRects)); + } + pr = (RECT*)&pData->Buffer; + SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1); + if (x0 < pData->rdh.rcBound.left) + pData->rdh.rcBound.left = x0; + if (y < pData->rdh.rcBound.top) + pData->rdh.rcBound.top = y; + if (x > pData->rdh.rcBound.right) + pData->rdh.rcBound.right = x; + if (y+1 > pData->rdh.rcBound.bottom) + pData->rdh.rcBound.bottom = y+1; + pData->rdh.nCount++; + /* On Windows98, ExtCreateRegion() may fail if the + * number of rectangles is too large (ie: > + * 4000). Therefore, we have to create the region by + * multiple steps. + */ + if (pData->rdh.nCount == 2000) { + HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + + (sizeof(RECT)*maxRects), pData); + if (hRgn) { + CombineRgn(hRgn, hRgn, h, RGN_OR); + DeleteObject(h); + } else + hRgn = h; + pData->rdh.nCount = 0; + SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); + } + } + } + /* Go to next row */ + data += bytesPerLine; + } + /* Create or extend the region with the remaining rectangles*/ + HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + + (sizeof(RECT)*maxRects), pData); + if (hRgn) { + CombineRgn(hRgn, hRgn, h, RGN_OR); + DeleteObject(h); + } else hRgn = h; + free(pData); // I've created the region so I can free this now, right? + return hRgn; +} + + +void Fl_WinAPI_Window_Driver::draw() { + if (shape_data_) { + if ((shape_data_->lw_ != pWindow->w() || shape_data_->lh_ != pWindow->h()) && shape_data_->shape_) { + // size of window has changed since last time + shape_data_->lw_ = pWindow->w(); + shape_data_->lh_ = pWindow->h(); + Fl_Image* temp = shape_data_->shape_->copy(shape_data_->lw_, shape_data_->lh_); + HRGN region = bitmap2region(temp); + SetWindowRgn(fl_xid(pWindow), region, TRUE); // the system deletes the region when it's no longer needed + delete temp; + } + } Fl_Window_Driver::draw(); +} // // End of "$Id$". diff --git a/src/drivers/X11/Fl_X11_Window_Driver.H b/src/drivers/X11/Fl_X11_Window_Driver.H index 36c5b67cb..458d1bd1c 100644 --- a/src/drivers/X11/Fl_X11_Window_Driver.H +++ b/src/drivers/X11/Fl_X11_Window_Driver.H @@ -42,15 +42,28 @@ ? where do we handle the interface between OpenGL/DirectX and Cocoa/WIN32/Glx? */ +struct Fl_Window_Driver::shape_data_type { + int lw_; ///< width of shape image + int lh_; ///< height of shape image + Fl_Image* shape_; ///< shape image + Fl_Bitmap *todelete_; ///< auxiliary bitmap image +}; + class FL_EXPORT Fl_X11_Window_Driver : public Fl_Window_Driver { + friend class Fl_X; +private: + void combine_mask(); + void shape_bitmap_(Fl_Image* b); + void shape_alpha_(Fl_Image* img, int offset); public: Fl_X11_Window_Driver(Fl_Window*); virtual void take_focus(); + virtual void shape(const Fl_Image* img); + virtual void draw(); }; - #endif // FL_X11_WINDOW_DRIVER_H // diff --git a/src/drivers/X11/Fl_X11_Window_Driver.cxx b/src/drivers/X11/Fl_X11_Window_Driver.cxx index 10988bd7c..efaa31851 100644 --- a/src/drivers/X11/Fl_X11_Window_Driver.cxx +++ b/src/drivers/X11/Fl_X11_Window_Driver.cxx @@ -20,8 +20,16 @@ #include "../../config_lib.h" #include "Fl_X11_Window_Driver.H" #include +#include +#if HAVE_DLFCN_H +#include +#endif +#define ShapeBounding 0 +#define ShapeSet 0 + #if USE_XDBE #include + static int can_xdbe(); // forward // class to be used only if Xdbe is used @@ -31,7 +39,7 @@ public: virtual int double_flush(int eraseoverlay); virtual void destroy_double_buffer(); }; -#endif +#endif // USE_XDBE Fl_Window_Driver *Fl_Window_Driver::newWindowDriver(Fl_Window *w) @@ -68,6 +76,7 @@ static int can_xdbe() { // whether the Xdbe extension is usable if (!tried) { tried = 1; int event_base, error_base; + fl_open_display(); if (!XdbeQueryExtension(fl_display, &event_base, &error_base)) return 0; Drawable root = RootWindow(fl_display,fl_screen); int numscreens = 1; @@ -116,9 +125,108 @@ void Fl_X11_Dbe_Window_Driver::destroy_double_buffer() { XdbeDeallocateBackBufferName(fl_display, i->other_xid); i->other_xid = 0; } - #endif // USE_XDBE + +void Fl_X11_Window_Driver::shape_bitmap_(Fl_Image* b) { + shape_data_->shape_ = b; +} + +void Fl_X11_Window_Driver::shape_alpha_(Fl_Image* img, int offset) { + int i, j, d = img->d(), w = img->w(), h = img->h(), bytesperrow = (w+7)/8; + unsigned u; + uchar byte, onebit; + // build an Fl_Bitmap covering the non-fully transparent/black part of the image + const uchar* bits = new uchar[h*bytesperrow]; // to store the bitmap + const uchar* alpha = (const uchar*)*img->data() + offset; // points to alpha value of rgba pixels + for (i = 0; i < h; i++) { + uchar *p = (uchar*)bits + i * bytesperrow; + byte = 0; + onebit = 1; + for (j = 0; j < w; j++) { + if (d == 3) { + u = *alpha; + u += *(alpha+1); + u += *(alpha+2); + } + else u = *alpha; + if (u > 0) { // if the pixel is not fully transparent/black + byte |= onebit; // turn on the corresponding bit of the bitmap + } + onebit = onebit << 1; // move the single set bit one position to the left + if (onebit == 0 || j == w-1) { + onebit = 1; + *p++ = byte; // store in bitmap one pack of bits + byte = 0; + } + alpha += d; // point to alpha value of next pixel + } + } + Fl_Bitmap* bitmap = new Fl_Bitmap(bits, w, h); + bitmap->alloc_array = 1; + shape_bitmap_(bitmap); + shape_data_->todelete_ = bitmap; +} + +void Fl_X11_Window_Driver::shape(const Fl_Image* img) { + if (shape_data_) { + if (shape_data_->todelete_) { delete shape_data_->todelete_; } + } + else { + shape_data_ = new shape_data_type; + } + memset(shape_data_, 0, sizeof(shape_data_type)); + pWindow->border(false); + int d = img->d(); + if (d && img->count() >= 2) shape_pixmap_((Fl_Image*)img); + else if (d == 0) shape_bitmap_((Fl_Image*)img); + else if (d == 2 || d == 4) shape_alpha_((Fl_Image*)img, d - 1); + else if ((d == 1 || d == 3) && img->count() == 1) shape_alpha_((Fl_Image*)img, 0); +} + + +void Fl_X11_Window_Driver::combine_mask() +{ + typedef void (*XShapeCombineMask_type)(Display*, int, int, int, int, Pixmap, int); + static XShapeCombineMask_type XShapeCombineMask_f = NULL; + static int beenhere = 0; + typedef Bool (*XShapeQueryExtension_type)(Display*, int*, int*); + if (!beenhere) { + beenhere = 1; +#if HAVE_DLSYM && HAVE_DLFCN_H + fl_open_display(); + void *handle = dlopen(NULL, RTLD_LAZY); // search symbols in executable + XShapeQueryExtension_type XShapeQueryExtension_f = (XShapeQueryExtension_type)dlsym(handle, "XShapeQueryExtension"); + XShapeCombineMask_f = (XShapeCombineMask_type)dlsym(handle, "XShapeCombineMask"); + // make sure that the X server has the SHAPE extension + int error_base, shapeEventBase; + if ( !( XShapeQueryExtension_f && XShapeCombineMask_f && + XShapeQueryExtension_f(fl_display, &shapeEventBase, &error_base) ) ) XShapeCombineMask_f = NULL; +#endif + } + if (!XShapeCombineMask_f) return; + shape_data_->lw_ = pWindow->w(); + shape_data_->lh_ = pWindow->h(); + Fl_Image* temp = shape_data_->shape_->copy(shape_data_->lw_, shape_data_->lh_); + Pixmap pbitmap = XCreateBitmapFromData(fl_display, fl_xid(pWindow), + (const char*)*temp->data(), + temp->w(), temp->h()); + XShapeCombineMask_f(fl_display, fl_xid(pWindow), ShapeBounding, 0, 0, pbitmap, ShapeSet); + if (pbitmap != None) XFreePixmap(fl_display, pbitmap); + delete temp; +} + + +void Fl_X11_Window_Driver::draw() { + if (shape_data_) { + if (( shape_data_->lw_ != pWindow->w() || shape_data_->lh_ != pWindow->h() ) && shape_data_->shape_) { + // size of window has changed since last time + combine_mask(); + } + } + Fl_Window_Driver::draw(); +} + // // End of "$Id$". // -- cgit v1.2.3