// // Special Cairo support for the Fast Light Tool Kit (FLTK). // // Copyright 1998-2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // // This file implements the FLTK Cairo Window support (since 1.3.x): // // - cmake -D FLTK_OPTION_CAIRO_WINDOW or // - cmake -D FLTK_OPTION_CAIRO_EXT // // Preprocessor macro FLTK_HAVE_CAIRO is defined for both options. // Preprocessor macro FLTK_HAVE_CAIRO_EXT is defined only for "CAIRO_EXT". // Both macros are defined in 'FL/fl_config.h'. #include "Fl_Private.H" // includes #if FLTK_HAVE_CAIRO #include #include #include // static initialization Fl_Cairo_State Fl_Private_cairo_state_; ///< current Cairo context information /** When FLTK_HAVE_CAIRO is defined and cairo_autolink_context() is true, any current window dc is linked to a current Cairo context. This is not the default, because it may not be necessary to add Cairo support to all fltk supported windows. When you wish to associate a Cairo context in this mode, you need to call explicitly in your draw() overridden method, Fl::cairo_make_current(Fl_Window*). This will create a Cairo context only for this Window. Still in custom Cairo application it is possible to handle completely this process automatically by setting \p alink to true. In this last case, you don't need anymore to call Fl::cairo_make_current(). You can use Fl::cairo_cc() to get the current Cairo context anytime. \note Only available if built with CMake option FLTK_OPTION_CAIRO_WINDOW=ON. */ void Fl::cairo_autolink_context(bool alink) { Fl_Private_cairo_state_.autolink(alink); } /** Gets the current autolink mode for Cairo support. \retval false if no Cairo context autolink is made for each window. \retval true if any fltk window is attached a Cairo context when it is current. \see void cairo_autolink_context(bool alink) \note Only available if built with CMake option FLTK_OPTION_CAIRO_EXT=ON. */ bool Fl::cairo_autolink_context() { return Fl_Private_cairo_state_.autolink(); } /** Gets the current Cairo context linked with a fltk window. */ cairo_t *Fl::cairo_cc() { return Fl_Private_cairo_state_.cc(); } /** Sets the current Cairo context to \p c. Set \p own to true if you want fltk to handle this cc deletion. \note Only available if built with CMake option FLTK_OPTION_CAIRO_WINDOW=ON. */ void Fl::cairo_cc(cairo_t *c, bool own /* = false */) { Fl_Private_cairo_state_.cc(c, own); } // Fl_Cairo_State void Fl_Cairo_State::autolink(bool b) { #if FLTK_HAVE_CAIROEXT autolink_ = b; #else Fl::fatal("In Fl::autolink(bool): Cairo autolink() feature is only " "available with CMake FLTK_OPTION_CAIRO_EXT.\n" "Quitting now."); #endif } /** Provides a Cairo context for window \a wi. This is needed in a draw() if Fl::cairo_autolink_context() returns false, which is the default. The cairo_context() does not need to be freed as it is freed every time a new Cairo context is created. When the program terminates, a call to Fl::cairo_make_current(0) will destroy any residual context. \note A new Cairo context is not always re-created when this method is used. In particular, if the current graphical context and the current window didn't change between two calls, the previous gc is internally kept, thus optimizing the drawing performances. Also, after this call, Fl::cairo_cc() is adequately updated with this Cairo context. \note Only available when CMake option '-D FLTK_OPTION_CAIRO_WINDOW' is set. \return The valid cairo_t *cairo context associated to this window. \retval NULL if \a wi is NULL */ cairo_t *Fl::cairo_make_current(Fl_Window *wi) { if (!wi) return NULL; cairo_t *cairo_ctxt; if (fl_gc == 0) { // means remove current cc Fl::cairo_cc(0); // destroy any previous cc Fl_Private_cairo_state_.window(0); return 0; } // don't re-create a context if it's the same gc/window combination if (fl_gc == Fl_Private_cairo_state_.gc() && fl_xid(wi) == (Window)Fl_Private_cairo_state_.window()) return Fl::cairo_cc(); // Scale the Cairo context appropriately float scale = Fl::screen_scale(wi->screen_num()); cairo_ctxt = Fl_Private_cairo_make_current(0, wi->w() * scale, wi->h() * scale); Fl_Private_cairo_state_.window((void *)fl_xid(wi)); cairo_scale(cairo_ctxt, scale, scale); return cairo_ctxt; } /* Creates a cairo_surface_t object using X11/Xlib. */ static cairo_surface_t *cairo_create_surface(void *gc, int W, int H) { return cairo_xlib_surface_create(fl_display, fl_window, fl_visual->visual, W, H); } /** Creates a Cairo context from a \p gc and the given size. \note Only available if CMake FLTK_OPTION_CAIRO_WINDOW is enabled. */ cairo_t *Fl_Private_cairo_make_current(void *gc, int W, int H) { if (gc == Fl_Private_cairo_state_.gc() && fl_window == (Window)Fl_Private_cairo_state_.window() && Fl_Private_cairo_state_.cc() != 0) // no need to create a cc, just return that one return Fl_Private_cairo_state_.cc(); // we need to (re-)create a fresh cc ... Fl_Private_cairo_state_.gc(gc); // keep track for next time cairo_surface_t *s = cairo_create_surface(gc, W, H); cairo_t *c = cairo_create(s); Fl_Private_cairo_state_.cc(c); // and purge any previously owned context cairo_surface_destroy(s); return c; } /** Flush Cairo drawings on Cairo context \p c. Call this when all your drawings on the Cairo context are finished. You don't need to use this if you use an Fl_Cairo_Window which does this automatically after the draw callback returns. Code example for "Cairo autolink" mode: In the overridden draw() method of your subclass of Fl_Window or any widget: \code cairo_t *cc = Fl::cairo_cc(); // get the "autolink" Cairo context // ... your Cairo drawings are here ... Fl::cairo_flush(cc); // flush Cairo drawings to the device \endcode If you configure FLTK with CMake option \c 'FLTK_OPTION_CAIRO_WINDOW' (i.e. without CMake option \c 'FLTK_OPTION_CAIRO_EXT') or if you don't enable the \c 'autolink' Cairo context you may do the equivalent to use Cairo drawings in an overridden draw() method of derived classes by using \code // get the Cairo context for the \c window cairo_t *cc = Fl::cairo_make_current(window); // ... your Cairo drawings are here ... Fl::cairo_flush(cc); // flush Cairo drawings to the device \endcode \see Fl::cairo_autolink_context(bool) \see Fl::cairo_make_current(Fl_Window*); */ void Fl::cairo_flush(cairo_t *c) { // flush Cairo drawings cairo_surface_t *s = cairo_get_target(c); cairo_surface_flush(s); } // Silence compiler warning if none of the Cairo options has been selected #else FL_EXPORT int fltk_cairo_dummy() { return 1; } #endif // FLTK_HAVE_CAIRO