summaryrefslogtreecommitdiff
path: root/src/wormouse.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wormouse.cpp')
-rw-r--r--src/wormouse.cpp202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/wormouse.cpp b/src/wormouse.cpp
new file mode 100644
index 0000000..5aa865e
--- /dev/null
+++ b/src/wormouse.cpp
@@ -0,0 +1,202 @@
+/**
+ * wormouse.cpp
+ *
+ * Copyright (c) 2010 Rodolfo Granata <warlock.cc@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ **/
+
+#include <math.h>
+#include "wormouse.h"
+
+COMPIZ_PLUGIN_20090315 (wormouse, WormousePluginVTable);
+
+/*boilerplate */
+WormouseScreen::WormouseScreen (CompScreen *screen):
+ PluginClassHandler <WormouseScreen, CompScreen> (screen),
+ cr (NULL), img (NULL), work (NULL), cursor (0), xci (NULL)
+{
+ poller.setCallback (boost::bind (&WormouseScreen::mouseMove, this, _1));
+
+ optionSetCursorImageNotify(boost::bind (&WormouseScreen::updateConfig, this, _1, _2));
+ optionSetShowHotspotNotify(boost::bind (&WormouseScreen::updateConfig, this, _1, _2));
+ optionSetHotspotXNotify(boost::bind (&WormouseScreen::updateConfig, this, _1, _2));
+ optionSetHotspotYNotify(boost::bind (&WormouseScreen::updateConfig, this, _1, _2));
+ optionSetTailFactorNotify(boost::bind (&WormouseScreen::updateConfig, this, _1, _2));
+
+ if (!updateConfig(NULL, Options()))
+ setFailed ();
+}
+
+WormouseScreen::~WormouseScreen ()
+{
+ freeResources ();
+}
+
+bool
+WormouseScreen::updateConfig (CompOption *opt, Options num)
+{
+ show_hotspot = optionGetShowHotspot ();
+
+ CompString path = optionGetCursorImage ();
+ compLogMessage ("wormouse", CompLogLevelInfo,
+ "Loading [%s]\n", path.c_str ());
+ if (!loadCursor (path, optionGetHotspotX (),
+ optionGetHotspotY (), optionGetTailFactor ())) {
+ // Try to load image from standard path if we can't find complete path
+ path = IMAGEDIR + ("/images/" + optionGetCursorImage ());
+ compLogMessage ("wormouse", CompLogLevelInfo,
+ "Loading [%s]\n", path.c_str ());
+ if (!loadCursor (path, optionGetHotspotX (),
+ optionGetHotspotY (), optionGetTailFactor ()))
+ return false;
+ }
+ return true;
+}
+
+bool
+WormouseScreen::loadCursor (CompString &path, int hotx, int hoty, float tailf)
+{
+ freeResources ();
+
+ img = cairo_image_surface_create_from_png (path.c_str ());
+ if (CAIRO_STATUS_SUCCESS != cairo_surface_status (img)) {
+ compLogMessage ("wormouse", CompLogLevelError,
+ "Failed to load cursor image [%s]\n", path.c_str ());
+ return false;
+ }
+
+ // calculate dimensions needed for rotate image
+ wrk_w = cairo_image_surface_get_width (img);
+ wrk_h = cairo_image_surface_get_height (img);
+ wrk_r = sqrt(wrk_w*wrk_w/4.0 + wrk_h*wrk_h/4.0);
+
+ // create a destination canvas
+ work = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 2*wrk_r, 2*wrk_r);
+ cr = cairo_create (work);
+ // clear canvas
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ //cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.2);
+ cairo_paint(cr);
+
+ // center our image in the newly created context
+ cairo_translate (cr, (2.0*wrk_r-wrk_w)/2, (2.0*wrk_r-wrk_h)/2);
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface (cr, img, 0, 0);
+ cairo_paint(cr);
+
+ // create a cursor image and set it
+ xci = XcursorImageCreate (2*wrk_r, 2*wrk_r);
+ xci->xhot = (2.0*wrk_r-wrk_w)/2 + hotx;
+ xci->yhot = (2.0*wrk_r-wrk_h)/2 + hoty;
+ xci->pixels = (XcursorPixel *)cairo_image_surface_get_data (work);
+
+ if (show_hotspot) {
+ cairo_identity_matrix (cr);
+ cairo_set_source_rgba (cr, 1, 0, 0, 1);
+ cairo_set_line_width (cr, 1.0);
+ cairo_arc (cr, xci->xhot, xci->yhot, 1.0, 0, 2 * M_PI);
+ cairo_stroke (cr);
+ }
+
+ cursor = XcursorImageLoadCursor (screen->dpy(), xci);
+ XDefineCursor (screen->dpy (), screen->root (), cursor);
+ XFlush (screen->dpy ());
+
+ hot_a = atan2f(wrk_r - xci->yhot, xci->xhot - wrk_r);
+ hot_d = sqrt((xci->xhot - wrk_r) * (xci->xhot - wrk_r) +
+ (xci->yhot - wrk_r) * (xci->yhot - wrk_r));
+ piv_d = tailf * hot_d;
+
+ poller.start ();
+ return true;
+}
+
+void
+WormouseScreen::freeResources ()
+{
+ poller.stop ();
+ //XUndefineCursor(screen->dpy(), screen->root());
+ if (cursor) {
+ XFreeCursor (screen->dpy(), cursor);
+ cursor = 0;
+ }
+ if (xci) {
+ XcursorImageDestroy (xci);
+ xci = NULL;
+ }
+ if (cr) {
+ cairo_destroy (cr);
+ cr = NULL;
+ }
+ if (img) {
+ cairo_surface_destroy (img);
+ img = NULL;
+ }
+ if (work) {
+ cairo_surface_destroy (work);
+ work = NULL;
+ }
+}
+
+void
+WormouseScreen::mouseMove (const CompPoint &pos)
+{
+
+ float a = atan2f(piv_y - pos.y(), pos.x() - piv_x);
+ // update pivoting position
+ piv_x = pos.x() - piv_d * cosf(a);
+ piv_y = pos.y() + piv_d * sinf(a);
+
+ cairo_identity_matrix (cr);
+ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+ //cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ //cairo_set_source_rgba (cr, 0.2, 0.2, 0.2, 0.2);
+ cairo_paint(cr);
+
+ // center our image in the newly created context
+ cairo_translate (cr, wrk_r, wrk_r);
+ cairo_rotate (cr, hot_a - a);
+ cairo_translate (cr, -wrk_w/2.0, -wrk_h/2.0);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+ cairo_set_source_surface (cr, img, 0, 0);
+ cairo_paint(cr);
+
+ // create a cursor image and set it
+ xci->xhot = wrk_r + hot_d * cosf(a);
+ xci->yhot = wrk_r - hot_d * sinf(a);
+ xci->pixels = (XcursorPixel *)cairo_image_surface_get_data (work);
+
+ if (show_hotspot) {
+ cairo_identity_matrix (cr);
+ cairo_set_source_rgba (cr, 1, 0, 0, 1);
+ cairo_set_line_width (cr, 1.0);
+ cairo_arc (cr, xci->xhot, xci->yhot, 1.0, 0, 2 * M_PI);
+ cairo_stroke (cr);
+ }
+
+ Cursor newcur = XcursorImageLoadCursor (screen->dpy(), xci);
+ XFixesChangeCursor (screen->dpy (), newcur, cursor);
+ XFreeCursor (screen->dpy (), cursor);
+ cursor = newcur;
+}
+
+bool
+WormousePluginVTable::init ()
+{
+ if (!CompPlugin::checkPluginABI ("core", CORE_ABIVERSION))
+ return false;
+ //TODO: query Xfixes / Xcursor extensions
+
+ return true;
+}