lwjgl/src/java/org/lwjgl/opengl/LinuxMouse.java

319 lines
10 KiB
Java

/*
* Copyright (c) 2002-2008 LWJGL Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'LWJGL' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.lwjgl.opengl;
/**
* @author elias_naur
*/
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Mouse;
final class LinuxMouse {
private static final int POINTER_WARP_BORDER = 10;
// scale the mouse wheel according to DirectInput
private static final int WHEEL_SCALE = 120;
private int button_count;
/* X11 constants */
private static final int Button1 = 1;
private static final int Button2 = 2;
private static final int Button3 = 3;
private static final int Button4 = 4;
private static final int Button5 = 5;
private static final int Button6 = 6; // wheel tilt left *rare*
private static final int Button7 = 7; // wheel tilt right *rare*
private static final int Button8 = 8; // back button
private static final int Button9 = 9; // forward button
private static final int ButtonPress = 4;
private static final int ButtonRelease = 5;
private final long display;
private final long window;
private final long input_window;
private final long warp_atom;
private final IntBuffer query_pointer_buffer = BufferUtils.createIntBuffer(4);
private final ByteBuffer event_buffer = ByteBuffer.allocate(Mouse.EVENT_SIZE);
private int last_x;
private int last_y;
private int accum_dx;
private int accum_dy;
private int accum_dz;
private byte[] buttons;
private EventQueue event_queue;
private long last_event_nanos;
LinuxMouse(long display, long window, long input_window) throws LWJGLException {
this.display = display;
this.window = window;
this.input_window = input_window;
this.warp_atom = LinuxDisplay.nInternAtom(display, "_LWJGL", false);
button_count = nGetButtonCount(display);
buttons = new byte[button_count];
reset(false, false);
}
private void reset(boolean grab, boolean warp_pointer) {
event_queue = new EventQueue(event_buffer.capacity());
accum_dx = accum_dy = 0;
long root_window = nQueryPointer(display, window, query_pointer_buffer);
int root_x = query_pointer_buffer.get(0);
int root_y = query_pointer_buffer.get(1);
int win_x = query_pointer_buffer.get(2);
int win_y = query_pointer_buffer.get(3);
// Pretend that the cursor never moved
last_x = win_x;
last_y = transformY(win_y);
doHandlePointerMotion(grab, warp_pointer, root_window, root_x, root_y, win_x, win_y, last_event_nanos);
}
public void read(ByteBuffer buffer) {
event_queue.copyEvents(buffer);
}
public void poll(boolean grab, IntBuffer coord_buffer, ByteBuffer buttons_buffer) {
if (grab) {
coord_buffer.put(0, accum_dx);
coord_buffer.put(1, accum_dy);
} else {
coord_buffer.put(0, last_x);
coord_buffer.put(1, last_y);
}
coord_buffer.put(2, accum_dz);
accum_dx = accum_dy = accum_dz = 0;
for (int i = 0; i < buttons.length; i++)
buttons_buffer.put(i, buttons[i]);
}
private void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) {
event_buffer.clear();
event_buffer.put(button).put(state).putInt(coord1).putInt(coord2).putInt(dz).putLong(nanos);
event_buffer.flip();
event_queue.putEvent(event_buffer);
last_event_nanos = nanos;
}
private void setCursorPos(boolean grab, int x, int y, long nanos) {
y = transformY(y);
int dx = x - last_x;
int dy = y - last_y;
if (dx != 0 || dy != 0) {
accum_dx += dx;
accum_dy += dy;
last_x = x;
last_y = y;
if (grab) {
putMouseEventWithCoords((byte)-1, (byte)0, dx, dy, 0, nanos);
} else {
putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos);
}
}
}
private void doWarpPointer(int center_x, int center_y) {
nSendWarpEvent(display, input_window, warp_atom, center_x, center_y);
nWarpCursor(display, window, center_x, center_y);
}
private static native void nSendWarpEvent(long display, long window, long warp_atom, int center_x, int center_y);
private void doHandlePointerMotion(boolean grab, boolean warp_pointer, long root_window, int root_x, int root_y, int win_x, int win_y, long nanos) {
setCursorPos(grab, win_x, win_y, nanos);
if (!warp_pointer)
return;
int root_window_height = nGetWindowHeight(display, root_window);
int root_window_width = nGetWindowWidth(display, root_window);
int window_height = nGetWindowHeight(display, window);
int window_width = nGetWindowWidth(display, window);
// find the window position in root coordinates
int win_left = root_x - win_x;
int win_top = root_y - win_y;
int win_right = win_left + window_width;
int win_bottom = win_top + window_height;
// cap the window position to the screen dimensions
int border_left = Math.max(0, win_left);
int border_top = Math.max(0, win_top);
int border_right = Math.min(root_window_width, win_right);
int border_bottom = Math.min(root_window_height, win_bottom);
// determine whether the cursor is outside the bounds
boolean outside_limits = root_x < border_left + POINTER_WARP_BORDER || root_y < border_top + POINTER_WARP_BORDER ||
root_x > border_right - POINTER_WARP_BORDER || root_y > border_bottom - POINTER_WARP_BORDER;
if (outside_limits) {
// Find the center of the limits in window coordinates
int center_x = (border_right - border_left)/2;
int center_y = (border_bottom - border_top)/2;
doWarpPointer(center_x, center_y);
}
}
public void changeGrabbed(boolean grab, boolean warp_pointer) {
reset(grab, warp_pointer);
}
public int getButtonCount() {
return buttons.length;
}
private int transformY(int y) {
return nGetWindowHeight(display, window) - 1 - y;
}
private static native int nGetWindowHeight(long display, long window);
private static native int nGetWindowWidth(long display, long window);
private static native int nGetButtonCount(long display);
private static native long nQueryPointer(long display, long window, IntBuffer result);
public void setCursorPosition(int x, int y) {
nWarpCursor(display, window, x, transformY(y));
}
private static native void nWarpCursor(long display, long window, int x, int y);
private void handlePointerMotion(boolean grab, boolean warp_pointer, long millis, long root_window, int x_root, int y_root, int x, int y) {
doHandlePointerMotion(grab, warp_pointer, root_window, x_root, y_root, x, y, millis*1000000);
}
private void handleButton(boolean grab, int button, byte state, long nanos) {
byte button_num;
switch (button) {
case Button1:
button_num = (byte)0;
break;
case Button2:
button_num = (byte)2;
break;
case Button3:
button_num = (byte)1;
break;
case Button6:
button_num = (byte)5;
break;
case Button7:
button_num = (byte)6;
break;
case Button8:
button_num = (byte)3; // back button
break;
case Button9:
button_num = (byte)4; // forward button
break;
default:
if (button > Button9 && button <= button_count) {
button_num = (byte)(button-1);
break;
}
return;
}
buttons[button_num] = state;
putMouseEvent(grab, button_num, state, 0, nanos);
}
private void putMouseEvent(boolean grab, byte button, byte state, int dz, long nanos) {
if (grab)
putMouseEventWithCoords(button, state, 0, 0, dz, nanos);
else
putMouseEventWithCoords(button, state, last_x, last_y, dz, nanos);
}
private void handleButtonPress(boolean grab, byte button, long nanos) {
int delta = 0;
switch (button) {
case Button4:
delta = WHEEL_SCALE;
putMouseEvent(grab, (byte)-1, (byte)0, delta, nanos);
accum_dz += delta;
break;
case Button5:
delta = -WHEEL_SCALE;
putMouseEvent(grab, (byte)-1, (byte)0, delta, nanos);
accum_dz += delta;
break;
default:
handleButton(grab, button, (byte)1, nanos);
break;
}
}
private void handleButtonEvent(boolean grab, long millis, int type, byte button) {
long nanos = millis*1000000;
switch (type) {
case ButtonRelease:
handleButton(grab, button, (byte)0, nanos);
break;
case ButtonPress:
handleButtonPress(grab, button, nanos);
break;
default:
break;
}
}
private void resetCursor(int x, int y) {
last_x = x;
last_y = transformY(y);
}
private void handleWarpEvent(int x, int y) {
resetCursor(x, y);
}
public boolean filterEvent(boolean grab, boolean warp_pointer, LinuxEvent event) {
switch (event.getType()) {
case LinuxEvent.ClientMessage:
if (event.getClientMessageType() == warp_atom) {
handleWarpEvent(event.getClientData(0), event.getClientData(1));
return true;
}
break;
case LinuxEvent.ButtonPress: /* Fall through */
case LinuxEvent.ButtonRelease:
handleButtonEvent(grab, event.getButtonTime(), event.getButtonType(), (byte)event.getButtonButton());
return true;
case LinuxEvent.MotionNotify:
handlePointerMotion(grab, warp_pointer, event.getButtonTime(), event.getButtonRoot(), event.getButtonXRoot(), event.getButtonYRoot(), event.getButtonX(), event.getButtonY());
return true;
default:
break;
}
return false;
}
}