1 /* 2 * Copyright (c) 2002-2008 LWJGL Project 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * * Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * * Neither the name of 'LWJGL' nor the names of 17 * its contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 package org.lwjgl.opengl; 33 34 /** 35 * @author elias_naur 36 */ 37 38 import java.nio.ByteBuffer; 39 import java.nio.IntBuffer; 40 41 import org.lwjgl.BufferUtils; 42 import org.lwjgl.LWJGLException; 43 import org.lwjgl.input.Mouse; 44 45 final class LinuxMouse { 46 private static final int POINTER_WARP_BORDER = 10; 47 // scale the mouse wheel according to DirectInput 48 private static final int WHEEL_SCALE = 120; 49 50 private int button_count; 51 52 /* X11 constants */ 53 private static final int Button1 = 1; 54 private static final int Button2 = 2; 55 private static final int Button3 = 3; 56 private static final int Button4 = 4; 57 private static final int Button5 = 5; 58 59 private static final int Button6 = 6; // wheel tilt left *rare* 60 private static final int Button7 = 7; // wheel tilt right *rare* 61 private static final int Button8 = 8; // back button 62 private static final int Button9 = 9; // forward button 63 64 private static final int ButtonPress = 4; 65 private static final int ButtonRelease = 5; 66 67 private final long display; 68 private final long window; 69 private final long input_window; 70 private final long warp_atom; 71 private final IntBuffer query_pointer_buffer = BufferUtils.createIntBuffer(4); 72 private final ByteBuffer event_buffer = ByteBuffer.allocate(Mouse.EVENT_SIZE); 73 74 private int last_x; 75 private int last_y; 76 private int accum_dx; 77 private int accum_dy; 78 private int accum_dz; 79 private byte[] buttons; 80 private EventQueue event_queue; 81 private long last_event_nanos; 82 LinuxMouse(long display, long window, long input_window)83 LinuxMouse(long display, long window, long input_window) throws LWJGLException { 84 this.display = display; 85 this.window = window; 86 this.input_window = input_window; 87 this.warp_atom = LinuxDisplay.nInternAtom(display, "_LWJGL", false); 88 button_count = nGetButtonCount(display); 89 buttons = new byte[button_count]; 90 reset(false, false); 91 } 92 reset(boolean grab, boolean warp_pointer)93 private void reset(boolean grab, boolean warp_pointer) { 94 event_queue = new EventQueue(event_buffer.capacity()); 95 accum_dx = accum_dy = 0; 96 long root_window = nQueryPointer(display, window, query_pointer_buffer); 97 98 int root_x = query_pointer_buffer.get(0); 99 int root_y = query_pointer_buffer.get(1); 100 int win_x = query_pointer_buffer.get(2); 101 int win_y = query_pointer_buffer.get(3); 102 // Pretend that the cursor never moved 103 last_x = win_x; 104 last_y = transformY(win_y); 105 doHandlePointerMotion(grab, warp_pointer, root_window, root_x, root_y, win_x, win_y, last_event_nanos); 106 } 107 read(ByteBuffer buffer)108 public void read(ByteBuffer buffer) { 109 event_queue.copyEvents(buffer); 110 } 111 poll(boolean grab, IntBuffer coord_buffer, ByteBuffer buttons_buffer)112 public void poll(boolean grab, IntBuffer coord_buffer, ByteBuffer buttons_buffer) { 113 if (grab) { 114 coord_buffer.put(0, accum_dx); 115 coord_buffer.put(1, accum_dy); 116 } else { 117 coord_buffer.put(0, last_x); 118 coord_buffer.put(1, last_y); 119 } 120 coord_buffer.put(2, accum_dz); 121 accum_dx = accum_dy = accum_dz = 0; 122 for (int i = 0; i < buttons.length; i++) 123 buttons_buffer.put(i, buttons[i]); 124 } 125 putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos)126 private void putMouseEventWithCoords(byte button, byte state, int coord1, int coord2, int dz, long nanos) { 127 event_buffer.clear(); 128 event_buffer.put(button).put(state).putInt(coord1).putInt(coord2).putInt(dz).putLong(nanos); 129 event_buffer.flip(); 130 event_queue.putEvent(event_buffer); 131 last_event_nanos = nanos; 132 } 133 setCursorPos(boolean grab, int x, int y, long nanos)134 private void setCursorPos(boolean grab, int x, int y, long nanos) { 135 y = transformY(y); 136 int dx = x - last_x; 137 int dy = y - last_y; 138 if (dx != 0 || dy != 0) { 139 accum_dx += dx; 140 accum_dy += dy; 141 last_x = x; 142 last_y = y; 143 if (grab) { 144 putMouseEventWithCoords((byte)-1, (byte)0, dx, dy, 0, nanos); 145 } else { 146 putMouseEventWithCoords((byte)-1, (byte)0, x, y, 0, nanos); 147 } 148 } 149 } 150 doWarpPointer(int center_x, int center_y)151 private void doWarpPointer(int center_x, int center_y) { 152 nSendWarpEvent(display, input_window, warp_atom, center_x, center_y); 153 nWarpCursor(display, window, center_x, center_y); 154 } nSendWarpEvent(long display, long window, long warp_atom, int center_x, int center_y)155 private static native void nSendWarpEvent(long display, long window, long warp_atom, int center_x, int center_y); 156 doHandlePointerMotion(boolean grab, boolean warp_pointer, long root_window, int root_x, int root_y, int win_x, int win_y, long nanos)157 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) { 158 setCursorPos(grab, win_x, win_y, nanos); 159 if (!warp_pointer) 160 return; 161 int root_window_height = nGetWindowHeight(display, root_window); 162 int root_window_width = nGetWindowWidth(display, root_window); 163 int window_height = nGetWindowHeight(display, window); 164 int window_width = nGetWindowWidth(display, window); 165 166 // find the window position in root coordinates 167 int win_left = root_x - win_x; 168 int win_top = root_y - win_y; 169 int win_right = win_left + window_width; 170 int win_bottom = win_top + window_height; 171 // cap the window position to the screen dimensions 172 int border_left = Math.max(0, win_left); 173 int border_top = Math.max(0, win_top); 174 int border_right = Math.min(root_window_width, win_right); 175 int border_bottom = Math.min(root_window_height, win_bottom); 176 // determine whether the cursor is outside the bounds 177 boolean outside_limits = root_x < border_left + POINTER_WARP_BORDER || root_y < border_top + POINTER_WARP_BORDER || 178 root_x > border_right - POINTER_WARP_BORDER || root_y > border_bottom - POINTER_WARP_BORDER; 179 if (outside_limits) { 180 // Find the center of the limits in window coordinates 181 int center_x = (border_right - border_left)/2; 182 int center_y = (border_bottom - border_top)/2; 183 doWarpPointer(center_x, center_y); 184 } 185 } 186 changeGrabbed(boolean grab, boolean warp_pointer)187 public void changeGrabbed(boolean grab, boolean warp_pointer) { 188 reset(grab, warp_pointer); 189 } 190 getButtonCount()191 public int getButtonCount() { 192 return buttons.length; 193 } 194 transformY(int y)195 private int transformY(int y) { 196 return nGetWindowHeight(display, window) - 1 - y; 197 } nGetWindowHeight(long display, long window)198 private static native int nGetWindowHeight(long display, long window); nGetWindowWidth(long display, long window)199 private static native int nGetWindowWidth(long display, long window); 200 nGetButtonCount(long display)201 private static native int nGetButtonCount(long display); 202 nQueryPointer(long display, long window, IntBuffer result)203 private static native long nQueryPointer(long display, long window, IntBuffer result); 204 setCursorPosition(int x, int y)205 public void setCursorPosition(int x, int y) { 206 nWarpCursor(display, window, x, transformY(y)); 207 } nWarpCursor(long display, long window, int x, int y)208 private static native void nWarpCursor(long display, long window, int x, int y); 209 handlePointerMotion(boolean grab, boolean warp_pointer, long millis, long root_window, int x_root, int y_root, int x, int y)210 private void handlePointerMotion(boolean grab, boolean warp_pointer, long millis, long root_window, int x_root, int y_root, int x, int y) { 211 doHandlePointerMotion(grab, warp_pointer, root_window, x_root, y_root, x, y, millis*1000000); 212 } 213 handleButton(boolean grab, int button, byte state, long nanos)214 private void handleButton(boolean grab, int button, byte state, long nanos) { 215 byte button_num; 216 switch (button) { 217 case Button1: 218 button_num = (byte)0; 219 break; 220 case Button2: 221 button_num = (byte)2; 222 break; 223 case Button3: 224 button_num = (byte)1; 225 break; 226 case Button6: 227 button_num = (byte)5; 228 break; 229 case Button7: 230 button_num = (byte)6; 231 break; 232 case Button8: 233 button_num = (byte)3; // back button 234 break; 235 case Button9: 236 button_num = (byte)4; // forward button 237 break; 238 default: 239 if (button > Button9 && button <= button_count) { 240 button_num = (byte)(button-1); 241 break; 242 } 243 return; 244 } 245 buttons[button_num] = state; 246 putMouseEvent(grab, button_num, state, 0, nanos); 247 } 248 putMouseEvent(boolean grab, byte button, byte state, int dz, long nanos)249 private void putMouseEvent(boolean grab, byte button, byte state, int dz, long nanos) { 250 if (grab) 251 putMouseEventWithCoords(button, state, 0, 0, dz, nanos); 252 else 253 putMouseEventWithCoords(button, state, last_x, last_y, dz, nanos); 254 } 255 handleButtonPress(boolean grab, byte button, long nanos)256 private void handleButtonPress(boolean grab, byte button, long nanos) { 257 int delta = 0; 258 switch (button) { 259 case Button4: 260 delta = WHEEL_SCALE; 261 putMouseEvent(grab, (byte)-1, (byte)0, delta, nanos); 262 accum_dz += delta; 263 break; 264 case Button5: 265 delta = -WHEEL_SCALE; 266 putMouseEvent(grab, (byte)-1, (byte)0, delta, nanos); 267 accum_dz += delta; 268 break; 269 default: 270 handleButton(grab, button, (byte)1, nanos); 271 break; 272 } 273 } 274 handleButtonEvent(boolean grab, long millis, int type, byte button)275 private void handleButtonEvent(boolean grab, long millis, int type, byte button) { 276 long nanos = millis*1000000; 277 switch (type) { 278 case ButtonRelease: 279 handleButton(grab, button, (byte)0, nanos); 280 break; 281 case ButtonPress: 282 handleButtonPress(grab, button, nanos); 283 break; 284 default: 285 break; 286 } 287 } 288 resetCursor(int x, int y)289 private void resetCursor(int x, int y) { 290 last_x = x; 291 last_y = transformY(y); 292 } 293 handleWarpEvent(int x, int y)294 private void handleWarpEvent(int x, int y) { 295 resetCursor(x, y); 296 } 297 filterEvent(boolean grab, boolean warp_pointer, LinuxEvent event)298 public boolean filterEvent(boolean grab, boolean warp_pointer, LinuxEvent event) { 299 switch (event.getType()) { 300 case LinuxEvent.ClientMessage: 301 if (event.getClientMessageType() == warp_atom) { 302 handleWarpEvent(event.getClientData(0), event.getClientData(1)); 303 return true; 304 } 305 break; 306 case LinuxEvent.ButtonPress: /* Fall through */ 307 case LinuxEvent.ButtonRelease: 308 handleButtonEvent(grab, event.getButtonTime(), event.getButtonType(), (byte)event.getButtonButton()); 309 return true; 310 case LinuxEvent.MotionNotify: 311 handlePointerMotion(grab, warp_pointer, event.getButtonTime(), event.getButtonRoot(), event.getButtonXRoot(), event.getButtonYRoot(), event.getButtonX(), event.getButtonY()); 312 return true; 313 default: 314 break; 315 } 316 return false; 317 } 318 } 319