com.varaneckas.hawkscope.hotkey.X11HotkeyListener.java Source code

Java tutorial

Introduction

Here is the source code for com.varaneckas.hawkscope.hotkey.X11HotkeyListener.java

Source

/*
 * Copyright (c) 2008-2009 Tomas Varaneckas
 * http://www.varaneckas.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 3 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, see <http://www.gnu.org/licenses/>.
 */
package com.varaneckas.hawkscope.hotkey;

import java.awt.AWTException;
import java.awt.Robot;
import java.awt.event.InputEvent;

import jxgrabkey.HotkeyListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import com.varaneckas.hawkscope.menu.MenuFactory;
import com.varaneckas.hawkscope.menu.state.StateEvent;

/**
 * X11 Global {@link HotkeyListener} that tries to invoke Hawkscope menu
 * at mouse cursor location
 * 
 * FIXME this is all based on dirty hacks
 * Due to swt bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=11570
 * 
 * @author Tomas Varaneckas
 * @version $Id$
 */
public class X11HotkeyListener implements HotkeyListener {

    /**
     * Logger
     */
    private static final Log log = LogFactory.getLog(X11HotkeyListener.class);

    /**
     * Hawkscope launch shell for dirty hacks
     */
    private Shell hawkscopeLaunchShell = new Shell(Display.getDefault(), SWT.ON_TOP);

    /**
     * Hawkscope support shell for dirty hacks
     */
    private Shell supportShell = new Shell(Display.getDefault(), SWT.ON_TOP);

    /**
     * A robot to simulate mouse clicks
     */
    private Robot robo = createRobot();

    /**
     * Creates the Robot
     * 
     * @return
     */
    private Robot createRobot() {
        try {
            return new Robot();
        } catch (final AWTException e) {
            log.error("Could not create Robot", e);
            return null;
        }
    }

    public void onHotkey(final int key) {
        log.debug("hotkey found " + key);
        displayHawkscopeMenu();
    }

    /**
     * Displays Hawkscope menu at mouse location. Based on dirty hacks and 
     * blind experiments. Basically, it creates a window beneath the cursor
     * and executes a real mouse click with help of {@link Robot}. This way
     * invoking the menu works.
     * 
     * A bug that prevents the normal activity:
     * https://bugs.eclipse.org/bugs/show_bug.cgi?id=11570
     */
    protected void displayHawkscopeMenu() {
        try {
            MenuFactory.getMainMenu().getSwtMenuObject().getDisplay().syncExec(new Runnable() {
                public void run() {
                    if (MenuFactory.getMainMenu().getSwtMenuObject().isVisible()) {
                        MenuFactory.getMainMenu().forceHide();
                    }
                    try {
                        final StateEvent se = new StateEvent();
                        final Point loc = Display.getDefault().getCursorLocation();
                        se.setX(loc.x);
                        se.setY(loc.y);
                        hawkscopeLaunchShell.setLocation(loc.x - 30, loc.y - 30);
                        hawkscopeLaunchShell.setSize(60, 60);
                        hawkscopeLaunchShell.setVisible(true);
                        //for a weird reason, if the second shell is not
                        //displayed, the whole hack does not work.
                        supportShell.setLocation(loc.x - 30, loc.y - 30);
                        supportShell.setSize(60, 60);
                        supportShell.setVisible(true);
                        //need all the sleeps
                        //if duration is lowered, hack starts to randomly
                        //fail to work.
                        Thread.sleep(50L);
                        Thread.yield();
                        if (robo != null) {
                            robo.mousePress(InputEvent.BUTTON1_MASK);
                            Thread.sleep(50L);
                        }
                        MenuFactory.getMainMenu().getState().act(se);
                        Thread.sleep(50L);
                        if (robo != null) {
                            robo.mouseRelease(InputEvent.BUTTON1_MASK);
                        }
                        supportShell.setVisible(false);
                        hawkscopeLaunchShell.setVisible(false);
                    } catch (final Exception e) {
                        throw new RuntimeException("Failed invoking hawkscope " + "menu with shortcut", e);
                    }
                }
            });
        } catch (final Exception e) {
            throw new RuntimeException("Failed invoking hawkscope with " + "shortcut key", e);
        }
    }
}