diff options
Diffstat (limited to 'plugins/mouse/msd-locate-pointer.c')
-rw-r--r-- | plugins/mouse/msd-locate-pointer.c | 504 |
1 files changed, 504 insertions, 0 deletions
diff --git a/plugins/mouse/msd-locate-pointer.c b/plugins/mouse/msd-locate-pointer.c new file mode 100644 index 0000000..8582074 --- /dev/null +++ b/plugins/mouse/msd-locate-pointer.c @@ -0,0 +1,504 @@ +/* msd-locate-pointer.c + * + * Copyright (C) 2008 Carlos Garnacho <[email protected]> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include "msd-timeline.h" +#include "msd-locate-pointer.h" + +#include <gdk/gdkkeysyms.h> +#include <gdk/gdkx.h> +#include <X11/keysym.h> + +#define ANIMATION_LENGTH 750 +#define WINDOW_SIZE 101 +#define N_CIRCLES 4 + +/* All circles are supposed to be moving when progress + * reaches 0.5, and each of them are supposed to long + * for half of the progress, hence the need of 0.5 to + * get the circles interval, and the multiplication + * by 2 to know a circle progress */ +#define CIRCLES_PROGRESS_INTERVAL (0.5 / N_CIRCLES) +#define CIRCLE_PROGRESS(p) (MIN (1., ((gdouble) (p) * 2.))) + +typedef struct MsdLocatePointerData MsdLocatePointerData; + +struct MsdLocatePointerData +{ + MsdTimeline *timeline; + GtkWidget *widget; + GdkWindow *window; + + gdouble progress; +}; + +static MsdLocatePointerData *data = NULL; + +static void +locate_pointer_paint (MsdLocatePointerData *data, + cairo_t *cr, + gboolean composite) +{ + GdkColor color; + gdouble progress, circle_progress; + gint width, height, i; + GtkStyle *style; + + progress = data->progress; + + #if GTK_CHECK_VERSION(3, 0, 0) + width = gdk_window_get_width(GDK_WINDOW(data->window)); + height = gdk_window_get_height(GDK_WINDOW(data->window)); + #else + gdk_drawable_get_size(data->window, &width, &height); + #endif + + style = gtk_widget_get_style (data->widget); + color = style->bg[GTK_STATE_SELECTED]; + + cairo_set_source_rgba (cr, 1., 1., 1., 0.); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + for (i = 0; i <= N_CIRCLES; i++) + { + if (progress < 0.) + break; + + circle_progress = MIN (1., (progress * 2)); + progress -= CIRCLES_PROGRESS_INTERVAL; + + if (circle_progress >= 1.) + continue; + + if (composite) + { + cairo_set_source_rgba (cr, + color.red / 65535., + color.green / 65535., + color.blue / 65535., + 1 - circle_progress); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + + cairo_fill (cr); + cairo_stroke (cr); + } + else + { + cairo_set_source_rgb (cr, 0., 0., 0.); + cairo_set_line_width (cr, 3.); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + cairo_stroke (cr); + + cairo_set_source_rgb (cr, 1., 1., 1.); + cairo_set_line_width (cr, 1.); + cairo_arc (cr, + width / 2, + height / 2, + circle_progress * width / 2, + 0, 2 * G_PI); + cairo_stroke (cr); + } + } +} + +static gboolean +locate_pointer_expose (GtkWidget *widget, + GdkEventExpose *event, + gpointer user_data) +{ + MsdLocatePointerData *data = (MsdLocatePointerData *) user_data; + cairo_t *cr; + + if (event->window != data->window) + return FALSE; + + cr = gdk_cairo_create (data->window); + locate_pointer_paint (data, cr, gtk_widget_is_composited (data->widget)); + cairo_destroy (cr); + + return TRUE; +} + +static void +update_shape (MsdLocatePointerData *data) +{ + cairo_t *cr; + GdkBitmap *mask; + + mask = gdk_pixmap_new (data->window, WINDOW_SIZE, WINDOW_SIZE, 1); + cr = gdk_cairo_create (mask); + locate_pointer_paint (data, cr, FALSE); + gdk_window_shape_combine_mask (data->window, mask, 0, 0); + g_object_unref (mask); + cairo_destroy (cr); +} + +static void +timeline_frame_cb (MsdTimeline *timeline, + gdouble progress, + gpointer user_data) +{ + MsdLocatePointerData *data = (MsdLocatePointerData *) user_data; + GdkScreen *screen; + gint cursor_x, cursor_y; + + if (gtk_widget_is_composited (data->widget)) + { + gdk_window_invalidate_rect (data->window, NULL, FALSE); + data->progress = progress; + } + else if (progress >= data->progress + CIRCLES_PROGRESS_INTERVAL) + { + /* only invalidate window each circle interval */ + update_shape (data); + gdk_window_invalidate_rect (data->window, NULL, FALSE); + data->progress += CIRCLES_PROGRESS_INTERVAL; + } + + screen = gdk_drawable_get_screen (data->window); + gdk_window_get_pointer (gdk_screen_get_root_window (screen), + &cursor_x, &cursor_y, NULL); + gdk_window_move (data->window, + cursor_x - WINDOW_SIZE / 2, + cursor_y - WINDOW_SIZE / 2); +} + +static void +set_transparent_shape (GdkWindow *window) +{ + GdkBitmap *mask; + cairo_t *cr; + + mask = gdk_pixmap_new (data->window, WINDOW_SIZE, WINDOW_SIZE, 1); + cr = gdk_cairo_create (mask); + + cairo_set_source_rgba (cr, 1., 1., 1., 0.); + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); + cairo_paint (cr); + + gdk_window_shape_combine_mask (data->window, mask, 0, 0); + g_object_unref (mask); + cairo_destroy (cr); +} + +static void +unset_transparent_shape (GdkWindow *window) +{ + gdk_window_shape_combine_mask (data->window, NULL, 0, 0); +} + +static void +composited_changed (GtkWidget *widget, + MsdLocatePointerData *data) +{ + if (!gtk_widget_is_composited (widget)) + set_transparent_shape (data->window); + else + unset_transparent_shape (data->window); +} + +static void +timeline_finished_cb (MsdTimeline *timeline, + gpointer user_data) +{ + MsdLocatePointerData *data = (MsdLocatePointerData *) user_data; + + /* set transparent shape and hide window */ + if (!gtk_widget_is_composited (data->widget)) + set_transparent_shape (data->window); + + gdk_window_hide (data->window); +} + +static void +create_window (MsdLocatePointerData *data, + GdkScreen *screen) +{ + GdkColormap *colormap; + GdkVisual *visual; + GdkWindowAttr attributes; + + colormap = gdk_screen_get_rgba_colormap (screen); + visual = gdk_screen_get_rgba_visual (screen); + + if (!colormap) + { + colormap = gdk_screen_get_rgb_colormap (screen); + visual = gdk_screen_get_rgb_visual (screen); + } + + attributes.window_type = GDK_WINDOW_TEMP; + attributes.wclass = GDK_INPUT_OUTPUT; + attributes.visual = visual; + attributes.colormap = colormap; + attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK; + attributes.width = 1; + attributes.height = 1; + + data->window = gdk_window_new (gdk_screen_get_root_window (screen), + &attributes, + GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP); + + gdk_window_set_user_data (data->window, data->widget); +} + +static MsdLocatePointerData * +msd_locate_pointer_data_new (GdkScreen *screen) +{ + MsdLocatePointerData *data; + + data = g_new0 (MsdLocatePointerData, 1); + + /* this widget will never be shown, it's + * mainly used to get signals/events from + */ + data->widget = gtk_window_new (GTK_WINDOW_POPUP); + gtk_widget_realize (data->widget); + + g_signal_connect (G_OBJECT (data->widget), "expose_event", + G_CALLBACK (locate_pointer_expose), + data); + + data->timeline = msd_timeline_new (ANIMATION_LENGTH); + g_signal_connect (data->timeline, "frame", + G_CALLBACK (timeline_frame_cb), data); + g_signal_connect (data->timeline, "finished", + G_CALLBACK (timeline_finished_cb), data); + + create_window (data, screen); + + return data; +} + +static void +move_locate_pointer_window (MsdLocatePointerData *data, + GdkScreen *screen) +{ + gint cursor_x, cursor_y; + GdkBitmap *mask; + GdkColor col; + GdkGC *gc; + + gdk_window_get_pointer (gdk_screen_get_root_window (screen), &cursor_x, &cursor_y, NULL); + + gdk_window_move_resize (data->window, + cursor_x - WINDOW_SIZE / 2, + cursor_y - WINDOW_SIZE / 2, + WINDOW_SIZE, WINDOW_SIZE); + + col.pixel = 0; + mask = gdk_pixmap_new (data->window, WINDOW_SIZE, WINDOW_SIZE, 1); + + gc = gdk_gc_new (mask); + gdk_gc_set_foreground (gc, &col); + gdk_draw_rectangle (mask, gc, TRUE, 0, 0, WINDOW_SIZE, WINDOW_SIZE); + + /* allow events to happen through the window */ + gdk_window_input_shape_combine_mask (data->window, mask, 0, 0); + + g_object_unref (mask); + g_object_unref (gc); +} + +void +msd_locate_pointer (GdkScreen *screen) +{ + if (!data) + data = msd_locate_pointer_data_new (screen); + + msd_timeline_pause (data->timeline); + msd_timeline_rewind (data->timeline); + + /* Create again the window if it is not for the current screen */ + if (gdk_screen_get_number (screen) != gdk_screen_get_number (gdk_drawable_get_screen (data->window))) + { + gdk_window_set_user_data (data->window, NULL); + gdk_window_destroy (data->window); + + create_window (data, screen); + } + + data->progress = 0.; + + g_signal_connect (data->widget, "composited-changed", + G_CALLBACK (composited_changed), data); + + move_locate_pointer_window (data, screen); + composited_changed (data->widget, data); + gdk_window_show (data->window); + + msd_timeline_start (data->timeline); +} + + +#define KEYBOARD_GROUP_SHIFT 13 +#define KEYBOARD_GROUP_MASK ((1 << 13) | (1 << 14)) + +/* Owen magic */ +static GdkFilterReturn +filter (GdkXEvent *xevent, + GdkEvent *event, + gpointer data) +{ + XEvent *xev = (XEvent *) xevent; + guint keyval; + gint group; + + GdkScreen *screen = (GdkScreen *)data; + + if (xev->type == KeyPress || xev->type == KeyRelease) + { + /* get the keysym */ + group = (xev->xkey.state & KEYBOARD_GROUP_MASK) >> KEYBOARD_GROUP_SHIFT; + gdk_keymap_translate_keyboard_state (gdk_keymap_get_default (), + xev->xkey.keycode, + xev->xkey.state, + group, + &keyval, + NULL, NULL, NULL); + if (keyval == GDK_Control_L || keyval == GDK_Control_R) + { + if (xev->type == KeyPress) + { + XAllowEvents (xev->xkey.display, + SyncKeyboard, + xev->xkey.time); + } + else + { + XAllowEvents (xev->xkey.display, + AsyncKeyboard, + xev->xkey.time); + msd_locate_pointer (screen); + } + } + else + { + XAllowEvents (xev->xkey.display, + ReplayKeyboard, + xev->xkey.time); + XUngrabKeyboard (gdk_x11_get_default_xdisplay (), + xev->xkey.time); + } + } + + return GDK_FILTER_CONTINUE; +} + +static void +set_locate_pointer (void) +{ + GdkKeymapKey *keys; + GdkDisplay *display; + int n_screens; + int n_keys; + gboolean has_entries; + static const guint keyvals[] = { GDK_Control_L, GDK_Control_R }; + unsigned j; + + display = gdk_display_get_default (); + n_screens = gdk_display_get_n_screens (display); + + for (j = 0 ; j < G_N_ELEMENTS (keyvals) ; j++) + { + has_entries = gdk_keymap_get_entries_for_keyval (gdk_keymap_get_default (), + keyvals[j], + &keys, + &n_keys); + if (has_entries) + { + gint i, j; + for (i = 0; i < n_keys; i++) + { + for (j=0; j< n_screens; j++) + { + GdkScreen *screen; + Window xroot; + + screen = gdk_display_get_screen (display, j); + xroot = gdk_x11_drawable_get_xid (gdk_screen_get_root_window (screen)); + + XGrabKey (GDK_DISPLAY_XDISPLAY (display), + keys[i].keycode, + 0, + xroot, + False, + GrabModeAsync, + GrabModeSync); + XGrabKey (GDK_DISPLAY_XDISPLAY (display), + keys[i].keycode, + LockMask, + xroot, + False, + GrabModeAsync, + GrabModeSync); + XGrabKey (GDK_DISPLAY_XDISPLAY (display), + keys[i].keycode, + Mod2Mask, + xroot, + False, + GrabModeAsync, + GrabModeSync); + XGrabKey (GDK_DISPLAY_XDISPLAY (display), + keys[i].keycode, + Mod4Mask, + xroot, + False, + GrabModeAsync, + GrabModeSync); + } + } + + g_free (keys); + + for (i = 0; i < n_screens; i++) + { + GdkScreen *screen; + + screen = gdk_display_get_screen (display, i); + gdk_window_add_filter (gdk_screen_get_root_window (screen), + filter, + screen); + } + } + } +} + + +int +main (int argc, char *argv[]) +{ + gtk_init (&argc, &argv); + + set_locate_pointer (); + + gtk_main (); + + return 0; +} + |