17#include <unordered_map>
19#include <boost/compute/detail/lru_cache.hpp>
20#include <glibmm/miscutils.h>
21#include <giomm/file.h>
22#include <gdkmm/cursor.h>
23#include <gdkmm/pixbuf.h>
24#include <gtkmm/icontheme.h>
25#include <gtkmm/settings.h>
26#include <gtkmm/version.h>
27#include <gtkmm/widget.h>
50using Key = std::tuple<std::string, std::string, std::string, std::uint32_t, std::uint32_t, bool, double>;
53 std::unordered_map<std::string, std::unique_ptr<SPDocument>>
map;
56struct CursorInputParams
64struct CursorRenderResult
76CursorRenderResult render_svg_cursor(
double scale, CursorInputParams
const &in)
87 std::vector<std::string> theme_names;
91 Glib::ustring theme_name = prefs->
getString(
"/theme/iconTheme", prefs->
getString(
"/theme/defaultIconTheme",
""));
92 if (!theme_name.empty()) {
93 theme_names.push_back(std::move(theme_name));
97 theme_name = Gtk::Settings::get_default()->property_gtk_icon_theme_name();
98 theme_names.push_back(std::move(theme_name));
101 theme_names.emplace_back(
"hicolor");
107 const auto enable_drop_shadow = prefs->
getBool(
"/options/cursor-drop-shadow",
true);
111 bool cursor_scaling = prefs->
getBool(
"/options/cursorscaling",
true);
112 if (!cursor_scaling) {
116 constexpr int max_cached_cursors = 100;
117 static boost::compute::detail::lru_cache<Key, CursorRenderResult> cursor_cache(max_cached_cursors);
120 Key cursor_key = {theme_names[0], theme_names[1], in.file_name,
122 enable_drop_shadow,
scale};
123 if (
auto const it = cursor_cache.get(cursor_key)) {
128 auto theme_paths = in.icon_theme->get_search_path();
131 auto &cursor_docs = CursorDocCache::get().map;
134 if (
auto it = cursor_docs.find(in.file_name); it !=
end(cursor_docs)) {
135 root = it->second->getRoot();
140 Glib::RefPtr<Gio::File> file;
141 std::string full_file_path;
143 for (
auto const &theme_name : theme_names) {
144 for (
auto const &theme_path : theme_paths) {
145 full_file_path = Glib::build_filename(theme_path, theme_name,
"cursors", in.file_name);
147 file = Gio::File::create_for_path(full_file_path);
149 if (file_exists)
break;
151 if (file_exists)
break;
154 if (!file->query_exists()) {
155 std::cerr <<
"load_svg_cursor: Cannot locate cursor file: " << in.file_name << std::endl;
162 std::cerr <<
"load_svg_cursor: Could not open document: " << full_file_path << std::endl;
166 root = document->getRoot();
168 std::cerr <<
"load_svg_cursor: Could not find SVG element: " << full_file_path << std::endl;
172 cursor_docs[in.file_name] = std::move(document);
188 if (!enable_drop_shadow) {
190 Glib::ustring shadow(
"drop-shadow");
191 auto objects =
root->document->getObjectsByClass(shadow);
192 for (
auto&& el : objects) {
193 if (
auto val = el->getAttribute(
"class")) {
194 Glib::ustring cls = val;
195 auto pos = cls.find(shadow);
196 if (pos != Glib::ustring::npos) {
197 cls.erase(pos, shadow.length());
199 el->setAttribute(
"class", cls);
207 int w =
root->document->getWidth().value(
"px");
208 int h =
root->document->getHeight().value(
"px");
216 std::cerr <<
"load_svg_cursor: failed to create pixbuf for: " << in.file_name << std::endl;
222 auto const hotspot = (area.clamp(root_pos) *
scale).round();
224 auto cursor = CursorRenderResult{
225 .texture =
to_texture(ink_pixbuf->getSurface()),
230 cursor_cache.insert(std::move(cursor_key), cursor);
237Glib::RefPtr<Gdk::Cursor>
240 std::optional<Colors::Color> maybe_fill,
241 std::optional<Colors::Color> maybe_stroke)
243 auto params = CursorInputParams{
244 .icon_theme = Gtk::IconTheme::get_for_display(widget.get_display()),
250#if GTKMM_CHECK_VERSION(4, 16, 0)
251 return Gdk::Cursor::create_from_slot([params] (
int,
double scale,
int &
width,
int &
height,
int &hotspot_x,
int &hotspot_y) {
252 auto res = render_svg_cursor(
scale, params);
253 width = res.size.x();
255 hotspot_x = res.hotspot.x();
256 hotspot_y = res.hotspot.y();
257 return std::move(res.texture);
260 auto res = render_svg_cursor(widget.get_scale_factor(), params);
266 return Gdk::Cursor::create(std::move(res.texture), res.hotspot.x(), res.hotspot.y());
277 std::optional<Colors::Color>
fill,
278 std::optional<Colors::Color>
stroke)
281 widget.set_cursor(std::move(cursor));
Cairo integration helpers.
Two-dimensional point with integer coordinates.
Two-dimensional point that doubles as a vector.
Axis aligned, non-empty rectangle.
std::string toString(bool opacity=true) const
Format the color as a css string and return it.
uint32_t toRGBA(double opacity=1.0) const
Return an sRGB conversion of the color in RGBA int32 format.
bool setOpacity(double opacity)
Set the opacity of this color object.
double getOpacity() const
Get the opacity in this color, if it's stored.
Preference storage class.
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
static double convert(double from_dist, Unit const *from, Unit const *to)
Convert distances.
std::shared_ptr< Css const > css
std::unordered_map< std::string, std::unique_ptr< SPDocument > > map
Glib::RefPtr< Gtk::IconTheme > icon_theme
Glib::RefPtr< Gdk::Texture > texture
void sp_file_fix_hotspot(SPRoot *o)
TODO: insert short description here.
std::unique_ptr< SPDocument > ink_file_open(std::span< char const > buffer)
Open a document from memory.
bool file_exists(const std::string &filepath)
Helper class to stream background task notifications as a series of messages.
Glib::RefPtr< Gdk::Cursor > load_svg_cursor(Gtk::Widget &widget, std::string const &file_name, std::optional< Colors::Color > maybe_fill, std::optional< Colors::Color > maybe_stroke)
void set_svg_cursor(Gtk::Widget &widget, std::string const &file_name, std::optional< Colors::Color > fill, std::optional< Colors::Color > stroke)
Loads an SVG cursor from the specified file name, and sets it as the cursor of the given widget.
Inkscape::Pixbuf * sp_generate_internal_bitmap(SPDocument *document, Geom::Rect const &area, double dpi, std::vector< SPItem const * > items, bool opaque, uint32_t const *checkerboard_color, double device_scale, std::optional< Antialiasing > antialias)
Generates a bitmap from given items.
Singleton class to access the preferences file in a convenient way.
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
void sp_repr_css_set_property_string(SPCSSAttr *css, char const *name, std::string const &value)
Set a style property to a standard string.
Inkscape::IO::Resource - simple resource API.
SPRoot: SVG <svg> implementation.
Static objects with destruction before main() exit.
Glib::RefPtr< Gdk::Texture > to_texture(Cairo::RefPtr< Cairo::Surface > const &surface)
Convert an image surface in ARGB32 format to a texture.