Witam wszystkich! Powracam w wielkim stylu (czyt. z trzecim problemem w ciągu ostatniego tygodnia)!

Stworzyłem klasę testową, która miała za zadanie centrować myszkę. Po każdym przesunięciu myszy, kursor wracał z powrotem na środek ekranu (później zrobię "zniknięty" kursor, więc gapienie się na niego nie będzie takie wkurzające). Dzięki centrowaniu kursora, można przesuwać myszkę w jedną stronę aż się zepsuje w nieskończoność. Spacją wyłącza i włącza się centrowanie. Aby wykryć, czy naprawdę wykonuję jakiś ruch, tło przesuwa się w tym samym kierunku, co kursor, ale nie powinien się centrować. Zamiast tego, gdy jest włączone centrowanie kursora, obrazek drga tak samo jak kursor. Gdy centrowanie jest wyłączone, wszystko działa poprawnie, ale wtedy kursor (jak było zaplanowane) zatrzymuje się na krawędzi ekranu, zaś dalej się obraz nie przesuwa.
Jedyny mój problem w tej klasie polega na tym, dlaczego we włączonym trybie centrowania (tryb domyślny) obraz centruje się razem z kursorem. Klasa programu MouseTest:

import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.SwingUtilities;
import test.GameCore;

public class MouseTest extends GameCore implements MouseMotionListener, KeyListener {
    public static void main(String[] args) {
        new MouseTest().run();
    }
    private Image bgImage;
    private Robot robot;
    private Point mouseLocation;
    private Point centerLocation;
    private Point imageLocation;
    private boolean relativeMouseMode;
    private boolean isRecentering;
    public void init() {
        super.init();
        mouseLocation = new Point();
        centerLocation = new Point();
        imageLocation = new Point();
        relativeMouseMode = true;
        isRecentering = false;
        try {
            robot = new Robot();
            recenterMouse();
            mouseLocation.x = centerLocation.x;
            mouseLocation.y = centerLocation.y;
        }
        catch (AWTException ex) {
            System.out.println("AWT Exception. Nie można utworzyć obiektu Robot.");
        }
        Window window = screen.getFullScreenWindow();
        window.addMouseMotionListener(this);
        window.addKeyListener(this);
        bgImage = loadImage("src/images/background.jpg");
    }
    public synchronized void draw(Graphics2D g) {
        int w = screen.getWidth();
        int h = screen.getHeight();
        //upewnij się, że położenie jest prawidłowe
        imageLocation.x %= w;
        imageLocation.y %= h;
        if (imageLocation.x < 0) imageLocation.x += w;
        if (imageLocation.y < 0) imageLocation.y += h;
        //narysowanie rysunku w czterech miejscach, co spowoduje pokrycie nim całego ekranu
        int x = imageLocation.x;
        int y = imageLocation.y;
        g.drawImage(bgImage, x, y, null);
        g.drawImage(bgImage, x-w, y, null);
        g.drawImage(bgImage, x, y-h, null);
        g.drawImage(bgImage, x-w, y-h, null);
        //ustawienia rysowania
        g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
        g.drawString("Naciśnij spację, by zmienić tryb myszy", 5, FONT_SIZE);
        g.drawString("Naciśnij Escape, aby zakończyć", 5, FONT_SIZE*2);
    }
    //zastosowanie klasy Robot do ustawienia wskaźnika myszy na środku ekranu (może nie działać na rzadkich systemach)
    private synchronized void recenterMouse() {
        Window window = screen.getFullScreenWindow();
        if (robot != null && window.isShowing()) {
            centerLocation.x = window.getWidth() / 2;
            centerLocation.y = window.getHeight() / 2;
            SwingUtilities.convertPointToScreen(centerLocation, window);
            isRecentering = true;
            robot.mouseMove(centerLocation.x, centerLocation.y);
        }
    }
    //Następne dwie metody - z interfejsu MouseMotionListener
    public void mouseDragged(MouseEvent e) {
        mouseMoved(e);
    }
    public synchronized void mouseMoved(MouseEvent e) {
        //zdarzenie centrowania wskaźnika myszy - ignorowanie
        if (isRecentering && centerLocation.x == e.getX() && centerLocation.x == e.getY()) isRecentering = false;
        else {
            int dx = e.getX() - mouseLocation.x;
            int dy = e.getY() - mouseLocation.y;
            imageLocation.x += dx;
            imageLocation.y += dy;
            //centrowanie wskaźnika myszy
            if (relativeMouseMode) {
                recenterMouse();
            }
        }
        mouseLocation.x = e.getX();
        mouseLocation.y = e.getY();
    }
    //Następne 3 metody - z interfejsu KeyListener
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            //zakończenie programu
            stop();
        }
        else if (e.getKeyCode() == KeyEvent.VK_SPACE) {
            //zmiana trybu myszy
            relativeMouseMode = !relativeMouseMode;
        }
    }
    public void keyReleased(KeyEvent e) {
        //ignoruj
    }
    public void keyTyped(KeyEvent e) {
        //ignoruj
    }
}

Klasa ta dziedziczy po klasie GameCore z paczki test:

package test;

import graphic.ScreenManager;
import java.awt.*;
import javax.swing.ImageIcon;

public abstract class GameCore {
    protected static final int FONT_SIZE = 24;
    private static final DisplayMode POSSIBLE_MODES[] = {
        new DisplayMode(2560, 2048, 32, 0),//Najwyższa rozdzielczość dla ekranu 5:4
        new DisplayMode(2560, 2048, 16, 0),//jw. mniej kolorów
        new DisplayMode(2048, 1536, 32, 0),//Najwyższa rozdzielczość dla ekranu 4:3
        new DisplayMode(2048, 1536, 16, 0),//jw. mniej kolorów
        new DisplayMode(1920, 1200, 32, 0),//Najwyższa rozdzielczość dla ekranu 16:10
        new DisplayMode(1920, 1200, 16, 0),//jw. mniej kolorów
        new DisplayMode(1600, 900, 32, 0),//Najwyższa rozdzielczość dla ekranu 16:9
        new DisplayMode(1600, 900, 16, 0),//jw. mniej kolorów
        new DisplayMode(1280, 1024, 32, 0),//Optymalna dla 5:4
        new DisplayMode(1280, 1024, 16, 0),//jw. mniej kolorów
        new DisplayMode(1024, 768, 32, 0),//Optymalna dla 4:3
        new DisplayMode(1024, 768, 32, 0),//jw. mniej kolorów
        new DisplayMode(320, 240, 8, 0)//standard ratunkowy - rozdzielczość 4:3 o najniższym poziomie, 256 kolorów 
    };
    private boolean isRunning;
    protected ScreenManager screen;
    
    //metoda informująca pętlę gry, że należy zakończyć pracę
    public void stop() {
        isRunning = false;
    }
    
    //wywołanie init() i gameLoop()
    public void run() {
        try {
            init();
            gameLoop();
        }
        finally {
            screen.restoreScreen();
        }
    }
    
    
    //ustawia tryb pełnoekranowy i inicjuje obiekty
    public void init() {
        screen = new ScreenManager();
        DisplayMode displayMode = screen.findFirstCompatibleMode(POSSIBLE_MODES);
        screen.setFullScreen(displayMode);
        
        Window window = screen.getFullScreenWindow();
        window.setFont(new Font("Dialog", Font.PLAIN, FONT_SIZE));
        window.setBackground(Color.blue);
        window.setForeground(Color.white);
        
        isRunning = true;
    }
    
    public Image loadImage(String fileName) {
        return new ImageIcon(fileName).getImage();
    }
    
    //uruchamia pętlę gry, działającą aż do wywołania stop()
    public void gameLoop() {
        long startTime = System.currentTimeMillis();
        long currTime = startTime;
        Graphics2D g = screen.getGraphics();
        while (isRunning) {
            long elapsedTime = System.currentTimeMillis() - currTime;
            currTime += elapsedTime;
            //aktualizacja sprite
            update(elapsedTime);
            //narysowanie i aktualizacja zawartości ekranu
            draw(g);
            screen.update();
            //chwila przerwy
            try {
                Thread.sleep(20);
            }
            catch (InterruptedException ex) {}
        }
    }
    
    public void update(long elapsedTime) {
        //Nic nie wykonuj
    }
    //rysowanie na ekranie. Klasy pochodne muszą dziedziczyć tą metodę.
    public abstract void draw(Graphics2D g);
}

Zaś ta klasa wymaga do działania klasy ScreenManager z paczki graphic:

package graphic;


import java.awt.*;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.lang.reflect.InvocationTargetException;
import javax.swing.JFrame;

public class ScreenManager
{
	private GraphicsDevice device;
	
	public ScreenManager()
	{
		GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
		device = environment.getDefaultScreenDevice();
	}
        
        public DisplayMode[] getCompatibleModes() {
            return device.getDisplayModes();
        }
        
        public DisplayMode findFirstCompatibleMode(DisplayMode modes[]) {
            DisplayMode goodModes[] = device.getDisplayModes();
            for (int i = 0; i < modes.length; i++) {
                for (int j = 0; j <goodModes.length; j++) {
                    if (displayModesMatch(modes[i], goodModes[j])) {
                        return modes[i];
                    }
                }
            }
            
            return null;
        }
	
	public DisplayMode getCurrentDisplayMode() {
            return device.getDisplayMode();
        }
        
        public boolean displayModesMatch(DisplayMode mode1, DisplayMode mode2) {
            if (mode1.getWidth() != mode2.getWidth() || mode1.getHeight() != mode2.getHeight()) return false;
            if (mode1.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && mode2.getBitDepth() != DisplayMode.BIT_DEPTH_MULTI && mode1.getBitDepth() != mode2.getBitDepth()) return false;
            if (mode1.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN && mode2.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN && mode1.getRefreshRate() != mode2.getRefreshRate()) return false;
            return true;
        }
        
        public void setFullScreen(DisplayMode displayMode) {
            final JFrame frame = new JFrame();
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setUndecorated(true);
            frame.setIgnoreRepaint(true);
            frame.setResizable(false);
            
            device.setFullScreenWindow(frame);
            
            if (displayMode != null && device.isDisplayChangeSupported()) {
                try {
                    device.setDisplayMode(displayMode);
                }
                catch (IllegalArgumentException ex) {}
                //poprawka dla systemu MacOS X:
                frame.setSize(displayMode.getWidth(), displayMode.getHeight());
                }
                
                try {
                    EventQueue.invokeAndWait(new Runnable() {
                        public void run() {
                            frame.createBufferStrategy(2);
                        }
                    });
                }
                catch (InterruptedException | InvocationTargetException ex) {
                    //ignorowanie
                }
        }
        
        public Graphics2D getGraphics() {
            Window window = device.getFullScreenWindow();
            if (window != null) {
                BufferStrategy strategy = window.getBufferStrategy();
                return (Graphics2D)strategy.getDrawGraphics();
            }
            else return null;
        }
        
        public void update() {
            Window window = device.getFullScreenWindow();
            if (window != null) {
                BufferStrategy strategy = window.getBufferStrategy();
                if (!strategy.contentsLost()) strategy.show();
            }
            //naprawa problemu z kolejką zdarzeń w Linux
            Toolkit.getDefaultToolkit().sync();
        }
        
        public JFrame getFullScreenWindow() {
            return (JFrame)device.getFullScreenWindow();
        }
        
        public int getWidth() {
            Window window = device.getFullScreenWindow();
            if (window != null) return window.getWidth();
            else return 0;
                }
        public int getHeight() {
            Window window = device.getFullScreenWindow();
            if (window != null) return window.getHeight();
            else return 0;
                }
        
        public BufferedImage createCompatibleImage(int w, int h, int transparancy) {
            Window window = device.getFullScreenWindow();
            if (window != null) {
                GraphicsConfiguration gc = window.getGraphicsConfiguration();
                return gc.createCompatibleImage(w, h, transparancy);
            }
            return null;
        }

    public void restoreScreen() {
        Window window = device.getFullScreenWindow();
            if (window != null) window.dispose();
            device.setFullScreenWindow(null);
    }
}

Plik graficzny w tle może być dowolnym plikiem, ja w czasie testów korzystam z http://www.sendspace.pl/file/371867fe3e4f05adca7eb46

Gdy włączycie program, poruszajcie myszką, a następnie wciśnijcie spację i jeszcze raz poruszajcie myszką. Przed naciśnięciem spacji powinny być te same efekty, co po wciśnięciu, ale kursor powinien wracać na środek, a obraz powinien się przesuwać tylko wtedy, gdy ja przesuwam myszką.