diff --git a/src/java/org/lwjgl/opengl/MacOSX.java b/src/java/org/lwjgl/opengl/MacOSX.java index ab683666..426b4032 100644 --- a/src/java/org/lwjgl/opengl/MacOSX.java +++ b/src/java/org/lwjgl/opengl/MacOSX.java @@ -32,24 +32,80 @@ package org.lwjgl.opengl; +import java.lang.reflect.*; + +import org.lwjgl.Sys; + /** * $Id$ * * Mac OS X specific hacks * * @author elias_naur + * @author Brian Matzon * @version $Revision$ */ class MacOSX { + /** + * Initializes the Mac OS X specific hack + */ public static void initMacOSX() { + java.awt.Toolkit.getDefaultToolkit(); - new com.apple.eawt.Application().addApplicationListener(new com.apple.eawt.ApplicationAdapter() { - public void handleQuit(com.apple.eawt.ApplicationEvent e) { - e.setHandled(false); - setQuitRequested(); + + // Add ourselves to quit requested, using reflection to allow + // compiling on other platforms + try { + Class appClass = Class.forName("com.apple.eawt.Application"); + Class listenerClass = Class.forName("com.apple.eawt.ApplicationListener"); + Object appInstance = appClass.newInstance(); + // create proxy for adapter + Object proxyInvoker = Proxy.newProxyInstance(listenerClass.getClassLoader(), new Class[]{listenerClass}, new Invokee()); + Method addApplicationListener = appClass.getMethod("addApplicationListener", new Class[]{listenerClass}); + addApplicationListener.invoke(appInstance, new Object[]{proxyInvoker}); + } catch (Exception e) { + // validate success + if (Sys.DEBUG) { + System.out.println("Unable to invoke 'addApplicationListener' method because of " + e); } - }); + } } + /** + * Even more hackish proxy class for allowing mac os x to be compiled on all platforms + * + * @author Brian Matzon + */ + static private class Invokee implements InvocationHandler { + /** + * Called when the actual method of the proxied class is called + * + * @param proxy Object being proxied + * @param method Method being invoked + * @param args Arguments for that specific method + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { + // Return if we were not called through handleQuit + Class applicationEventClass = Class.forName("com.apple.eawt.ApplicationEvent"); + Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); + Method handleQuitMethod = applicationListenerClass.getMethod("handleQuit", new Class[]{applicationEventClass}); + if (!method.equals(handleQuitMethod)) + return null; + // invoke setHandled(false); + Method setHandled = args[0].getClass().getMethod("setHandled", new Class[] {boolean.class}); + setHandled.invoke(args[0], new Object[]{new Boolean(false)}); + // just call setQuitRequested + setQuitRequested(); + } catch (Exception e) { + if (Sys.DEBUG) { + System.out.println("Unable to invoke 'setHandled' because of " + e); + } + } + return null; + } + } + + /** Notifies the native implementation that a quit event has been received */ private static native void setQuitRequested(); } diff --git a/src/java/org/lwjgl/opengl/Window.java b/src/java/org/lwjgl/opengl/Window.java index 9dfe8923..c1999363 100644 --- a/src/java/org/lwjgl/opengl/Window.java +++ b/src/java/org/lwjgl/opengl/Window.java @@ -55,17 +55,7 @@ public final class Window { * */ if (Display.getPlatform() == Display.PLATFORM_AGL) - initMacOSX(); - } - - private static void initMacOSX() { - try { - Class mac_class = Class.forName("org.lwjgl.opengl.MacOSX"); - Method init_method = mac_class.getMethod("initMacOSX", null); - init_method.invoke(null, null); - } catch (Exception e) { - e.printStackTrace(); - } + MacOSX.initMacOSX(); } /** Whether the window is currently created, ie. has a native peer */ diff --git a/src/native/macosx/Window.h b/src/native/macosx/Window.h index e35ced0f..ce3f3c4d 100644 --- a/src/native/macosx/Window.h +++ b/src/native/macosx/Window.h @@ -47,5 +47,6 @@ extern void setQuitRequested(void); extern bool registerKeyboardHandler(JNIEnv* env, WindowRef win_ref); extern bool registerMouseHandler(JNIEnv* env, WindowRef win_ref); + extern bool isMouseCreatedAndNotNativeCursor(void); #endif /* _LWJGL_WINDOW_H_INCLUDED_ */ diff --git a/src/native/macosx/org_lwjgl_input_Keyboard.cpp b/src/native/macosx/org_lwjgl_input_Keyboard.cpp index bd35fef1..532ab012 100644 --- a/src/native/macosx/org_lwjgl_input_Keyboard.cpp +++ b/src/native/macosx/org_lwjgl_input_Keyboard.cpp @@ -42,53 +42,24 @@ #include "Window.h" #include "tools.h" #include "org_lwjgl_input_Keyboard.h" +#include "common_tools.h" -#define EVENT_BUFFER_SIZE 100 #define KEYBOARD_SIZE 256 #define UNICODE_BUFFER_SIZE 10 static unsigned char key_buf[KEYBOARD_SIZE]; static unsigned char key_map[KEYBOARD_SIZE]; -static unsigned char input_event_buffer[EVENT_BUFFER_SIZE]; -static unsigned char output_event_buffer[EVENT_BUFFER_SIZE]; - -static int list_start = 0; -static int list_end = 0; static bool buffer_enabled = false; static bool translation_enabled = false; - -static void putEventElement(unsigned char byte) { - int next_index = (list_end + 1)%EVENT_BUFFER_SIZE; - if (next_index == list_start) { -#ifdef _DEBUG - printf("Keyboard buffer overflow!\n"); -#endif - return; - } - input_event_buffer[list_end] = byte; - list_end = next_index; -} - -static bool hasMoreEvents(void) { - return list_start != list_end; -} - -static void copyEvent(int event_size, int event_index) { - int output_index = event_index*event_size; - for (int i = 0; i < event_size; i++) { - output_event_buffer[output_index] = input_event_buffer[list_start]; - list_start = (list_start + 1)%EVENT_BUFFER_SIZE; - output_index++; - } -} +static event_queue_t event_queue; static bool handleMappedKey(unsigned char mapped_code, unsigned char state) { unsigned char old_state = key_buf[mapped_code]; if (old_state != state) { key_buf[mapped_code] = state; if (buffer_enabled) { - putEventElement(mapped_code); - putEventElement(state); + putEventElement(&event_queue, mapped_code); + putEventElement(&event_queue, state); return true; } } @@ -125,15 +96,15 @@ static bool writeChars(int num_chars, UniChar *buffer) { return false; unsigned char b0 = getFirstByte(buffer[0]); unsigned char b1 = getSecondByte(buffer[0]); - putEventElement(b0); - putEventElement(b1); + putEventElement(&event_queue, b0); + putEventElement(&event_queue, b1); for (int i = 1; i < num_chars; i++) { - putEventElement(0); - putEventElement(0); + putEventElement(&event_queue, 0); + putEventElement(&event_queue, 0); b0 = getFirstByte(buffer[i]); b1 = getSecondByte(buffer[i]); - putEventElement(b0); - putEventElement(b1); + putEventElement(&event_queue, b0); + putEventElement(&event_queue, b1); } return true; } @@ -179,12 +150,12 @@ static pascal OSStatus doKeyDown(EventHandlerCallRef next_handler, EventRef even if (handleKey(key_code, 1)) { if (translation_enabled) { if (!handleUnicode(event)) { - putEventElement(0); - putEventElement(0); + putEventElement(&event_queue, 0); + putEventElement(&event_queue, 0); } } else { - putEventElement(0); - putEventElement(0); + putEventElement(&event_queue, 0); + putEventElement(&event_queue, 0); } } unlock(); @@ -202,8 +173,8 @@ static pascal OSStatus doKeyUp(EventHandlerCallRef next_handler, EventRef event, } lock(); if (handleKey(key_code, 0)) { - putEventElement(0); - putEventElement(0); + putEventElement(&event_queue, 0); + putEventElement(&event_queue, 0); } unlock(); return noErr; @@ -213,8 +184,8 @@ static void handleModifier(UInt32 modifier_bit_mask, UInt32 modifier_bit, unsign bool key_down = (modifier_bit_mask & modifier_bit) == modifier_bit; unsigned char key_state = key_down ? 1 : 0; if (handleMappedKey(key_code, key_state)) { - putEventElement(0); - putEventElement(0); + putEventElement(&event_queue, 0); + putEventElement(&event_queue, 0); } } @@ -346,8 +317,7 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Keyboard_nCreate(JNIEnv * env, jclas lock(); buffer_enabled = false; translation_enabled = false; - list_end = 0; - list_start = 0; + initEventQueue(&event_queue); memset(key_buf, 0, KEYBOARD_SIZE*sizeof(unsigned char)); setupMappings(); unlock(); @@ -371,10 +341,7 @@ JNIEXPORT jint JNICALL Java_org_lwjgl_input_Keyboard_nRead(JNIEnv * env, jclass event_size = 4; else event_size = 2; - while (hasMoreEvents()) { - copyEvent(event_size, num_events); - num_events++; - } + num_events = copyEvents(&event_queue, event_size); unlock(); return num_events; } @@ -383,12 +350,10 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Keyboard_nEnableTranslation(JNIEnv * translation_enabled = true; } -JNIEXPORT jint JNICALL Java_org_lwjgl_input_Keyboard_nEnableBuffer(JNIEnv * env, jclass clazz) { - jfieldID fid_readBuffer = env->GetStaticFieldID(clazz, "readBuffer", "Ljava/nio/ByteBuffer;"); - jobject new_buffer = env->NewDirectByteBuffer(&output_event_buffer, EVENT_BUFFER_SIZE); - env->SetStaticObjectField(clazz, fid_readBuffer, new_buffer); +JNIEXPORT jobject JNICALL Java_org_lwjgl_input_Keyboard_nEnableBuffer(JNIEnv * env, jclass clazz) { + jobject new_buffer = env->NewDirectByteBuffer(getOutputList(&event_queue), getEventBufferSize(&event_queue)); buffer_enabled = true; - return EVENT_BUFFER_SIZE/2; + return new_buffer; } JNIEXPORT jint JNICALL Java_org_lwjgl_input_Keyboard_nisStateKeySet(JNIEnv *env, jclass clazz, jint key) { diff --git a/src/native/macosx/org_lwjgl_input_Mouse.cpp b/src/native/macosx/org_lwjgl_input_Mouse.cpp index c370dc8b..5d8bae9e 100644 --- a/src/native/macosx/org_lwjgl_input_Mouse.cpp +++ b/src/native/macosx/org_lwjgl_input_Mouse.cpp @@ -39,6 +39,11 @@ * @version $Revision$ */ +#include +#include +#include +#include +#include #include "Window.h" #include "tools.h" #include "org_lwjgl_input_Mouse.h" @@ -54,6 +59,18 @@ static unsigned char button_state[NUM_BUTTONS]; static int last_x; static int last_y; static int wheel_dz; +static bool native_cursor; +static bool created; +static IOHIDDeviceInterface **device_interface; + +/*static pascal OSStatus doMouseMoved(EventHandlerCallRef next_handler, EventRef event, void *user_data) { +printf("Mouse moved\n"); + return eventNotHandledErr; // allow the event to propagate +} +*/ +bool isMouseCreatedAndNotNativeCursor(void) { + return created && !native_cursor; +} static pascal OSStatus doMouseDown(EventHandlerCallRef next_handler, EventRef event, void *user_data) { printf("Mouse down\n"); @@ -79,18 +96,132 @@ printf("Mouse wheel\n"); bool registerMouseHandler(JNIEnv* env, WindowRef win_ref) { bool error = registerHandler(env, win_ref, doMouseDown, kEventClassMouse, kEventMouseDown); error = error || registerHandler(env, win_ref, doMouseUp, kEventClassMouse, kEventMouseUp); + //error = error || registerHandler(env, win_ref, doMouseMoved, kEventClassMouse, kEventMouseMoved); error = error || registerHandler(env, win_ref, doMouseWheel, kEventClassMouse, kEventMouseWheelMoved); return !error; } +static void printCFString(CFStringRef str) { + CFIndex buffer_size = CFStringGetLength(str) + 1; + char * buffer = (char *)malloc(buffer_size); + if (buffer != NULL) { + if (CFStringGetCString(str, buffer, buffer_size, CFStringGetSystemEncoding())) + printf("%s", buffer); + free(buffer); + } +} + +static void printCFNumber(CFNumberRef num) { + long number; + + if (CFNumberGetValue(num, kCFNumberLongType, &number)) + printf("0x%lx (%ld)", number, number); +} + +static bool getIntProperty(CFDictionaryRef dict, CFStringRef key, int *key_value) { + CFTypeRef val = CFDictionaryGetValue(dict, key); + if (val != NULL) { + CFTypeID type = CFGetTypeID(val); + if (type == CFNumberGetTypeID()) + if (CFNumberGetValue((CFNumberRef)val, kCFNumberIntType, key_value)) + return true; + } + return false; +} + +static void printProperty(CFDictionaryRef dict, CFStringRef key) { + CFTypeRef val = CFDictionaryGetValue(dict, key); + if (val != NULL) { + CFTypeID type = CFGetTypeID(val); + if (type == CFArrayGetTypeID()) printf("array\n"); + else if (type == CFBooleanGetTypeID()) printf("boolean\n"); + else if (type == CFDictionaryGetTypeID()) printf("dictionary\n"); + else if (type == CFNumberGetTypeID()) printCFNumber((CFNumberRef)val); + else if (type == CFStringGetTypeID()) printCFString((CFStringRef)val); + else printf("\n"); + } +} + +static void releaseDevice(void) { + (*device_interface)->close(device_interface); +} + +static bool initDevice(io_object_t hid_device) { + io_name_t class_name; + IOCFPlugInInterface **plugin_interface; + SInt32 score; + IOReturn io_err = IOObjectGetClass(hid_device, class_name); + if (io_err != kIOReturnSuccess) + return false; +printf("Found device type %s\n", class_name); + io_err = IOCreatePlugInInterfaceForService(hid_device, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin_interface, &score); + if (io_err != kIOReturnSuccess) + return false; + HRESULT plugin_err = (*plugin_interface)->QueryInterface(plugin_interface, + CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID), + device_interface + ); + (*plugin_interface)->Release(plugin_interface); + if (plugin_err != S_OK) + return false; + io_err = (*device_interface)->open(device_interface, 0); + if (io_err != kIOReturnSuccess) { + releaseDevice(); + return false; + } else + return true; +} + +static void findDevice(void) { + io_iterator_t device_iterator; + io_object_t hid_device; + kern_return_t kern_err; + bool success = false; + CFMutableDictionaryRef dev_props; + CFMutableDictionaryRef matching_dic = IOServiceMatching(kIOHIDDeviceKey); + IOReturn err = IOServiceGetMatchingServices(kIOMasterPortDefault, matching_dic, &device_iterator); + if (err != kIOReturnSuccess) { +#ifdef _DEBUG + printf("Could not find matching devices\n"); +#endif + return; + } + while (!success && (hid_device = IOIteratorNext(device_iterator)) != NULL) { + kern_err = IORegistryEntryCreateCFProperties(hid_device, &dev_props, kCFAllocatorDefault, kNilOptions); + if (kern_err == KERN_SUCCESS && dev_props != NULL) { + /*printf(" usage "); + printProperty(dev_props, CFSTR(kIOHIDPrimaryUsageKey)); + printf(" usage page "); + printProperty(dev_props, CFSTR(kIOHIDPrimaryUsagePageKey));*/ + int usage; + int usage_page; + if (getIntProperty(dev_props, CFSTR(kIOHIDPrimaryUsageKey), &usage) && + getIntProperty(dev_props, CFSTR(kIOHIDPrimaryUsagePageKey), &usage_page) && + usage_page == kHIDPage_GenericDesktop && usage == kHIDUsage_GD_Mouse) { + printProperty(dev_props, CFSTR(kIOHIDProductKey)); + printf("\n"); + success = initDevice(hid_device); +if (success) + printf("Device found: "); + } + CFRelease(dev_props); + } + IOObjectRelease(hid_device); + } + IOObjectRelease(device_iterator); +} + +static void pollDevice(void) { +} + JNIEXPORT jboolean JNICALL Java_org_lwjgl_input_Mouse_nHasWheel(JNIEnv *, jclass) { return JNI_TRUE; } - + JNIEXPORT jint JNICALL Java_org_lwjgl_input_Mouse_nGetButtonCount(JNIEnv *, jclass) { return NUM_BUTTONS; } - + JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_initIDs(JNIEnv * env, jclass clazz) { fid_dx = env->GetStaticFieldID(clazz, "dx", "I"); fid_dy = env->GetStaticFieldID(clazz, "dy", "I"); @@ -114,31 +245,39 @@ JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nCreate(JNIEnv * env, jclass c last_x = 0; last_y = 0; wheel_dz = 0; - CGAssociateMouseAndMouseCursorPosition(FALSE); + native_cursor = false; + findDevice(); + //CGAssociateMouseAndMouseCursorPosition(FALSE); + created = true; } JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nDestroy(JNIEnv * env, jclass clazz) { - CGAssociateMouseAndMouseCursorPosition(TRUE); + if (!native_cursor) + CGAssociateMouseAndMouseCursorPosition(TRUE); + created = false; } JNIEXPORT void JNICALL Java_org_lwjgl_input_Mouse_nPoll(JNIEnv * env, jclass clazz) { lock(); - /*Point cursor_pos; - GetMouse(&cursor_pos); - int dx = cursor_pos.v - last_x; - int dy = cursor_pos.h - last_y; - last_x += dx; - last_y += dy;*/ - CGMouseDelta dx, dy; - CGGetLastMouseDelta(&dx, &dy); + int dx; + int dy; + if (!native_cursor) { + pollDevice(); + } else { + Point cursor_pos; + GetMouse(&cursor_pos); + dx = cursor_pos.v - last_x; + dy = cursor_pos.h - last_y; + last_x += dx; + last_y += dy; + } env->SetStaticIntField(clazz, fid_dx, (jint)dx); env->SetStaticIntField(clazz, fid_dy, (jint)dy); env->SetStaticIntField(clazz, fid_dwheel, (jint)wheel_dz); jbooleanArray buttons_array = (jbooleanArray)env->GetStaticObjectField(clazz, fid_buttons); env->SetBooleanArrayRegion(buttons_array, 0, NUM_BUTTONS, button_state); wheel_dz = 0; - unlock(); -/* - if (dx != 0 || dy != 0) + /*if (dx != 0 || dy != 0) printf("dx %d dy %d, lx %d ly %d\n", dx, dy, last_x, last_y);*/ + unlock(); } diff --git a/src/native/macosx/org_lwjgl_opengl_Window.cpp b/src/native/macosx/org_lwjgl_opengl_Window.cpp index 727d9281..dced05fc 100644 --- a/src/native/macosx/org_lwjgl_opengl_Window.cpp +++ b/src/native/macosx/org_lwjgl_opengl_Window.cpp @@ -44,6 +44,7 @@ #include "org_lwjgl_opengl_Window.h" #include "extgl.h" #include "tools.h" +#include "common_tools.h" static WindowRef win_ref; static AGLContext context; @@ -52,6 +53,8 @@ static Ptr fullscreen_ptr; static bool current_fullscreen; static bool miniaturized; static bool activated; +static int center_x; +static int center_y; static void setWindowTitle(JNIEnv *env, jstring title_obj) { const char* title = env->GetStringUTFChars(title_obj, NULL); @@ -87,10 +90,17 @@ static pascal OSStatus doMaximize(EventHandlerCallRef next_handler, EventRef eve return eventNotHandledErr; } +static void warpCursorToCenter(void) { + CGPoint p = {center_x, center_y}; + CGWarpMouseCursorPosition(p); +} + static pascal OSStatus doActivate(EventHandlerCallRef next_handler, EventRef event, void *user_data) { lock(); miniaturized = false; activated = true; + if (isMouseCreatedAndNotNativeCursor()) + warpCursorToCenter(); unlock(); return eventNotHandledErr; } @@ -107,6 +117,22 @@ static pascal OSStatus doQuit(EventHandlerCallRef next_handler, EventRef event, return noErr; } +static pascal OSStatus doBoundsChanged(EventHandlerCallRef next_handler, EventRef event, void *user_data) { + Rect rect; + OSStatus err = GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, NULL, sizeof(rect), NULL, &rect); + if (err != noErr) { +#ifdef _DEBUG + printf("Could not get bounds from bounds changed event\n"); +#endif + return eventNotHandledErr; + } + lock(); + center_x = (rect.left + rect.right)/2; + center_y = (rect.top + rect.bottom)/2; + unlock(); + return noErr; +} + static bool registerEventHandlers(JNIEnv *env) { bool error; error = registerHandler(env, win_ref, doQuit, kEventClassWindow, kEventWindowClose); @@ -114,6 +140,7 @@ static bool registerEventHandlers(JNIEnv *env) { error = error || registerHandler(env, win_ref, doDeactivate, kEventClassWindow, kEventWindowDeactivated); error = error || registerHandler(env, win_ref, doMiniaturized, kEventClassWindow, kEventWindowCollapsed); error = error || registerHandler(env, win_ref, doMaximize, kEventClassWindow, kEventWindowExpanded); + error = error || registerHandler(env, win_ref, doBoundsChanged, kEventClassWindow, kEventWindowBoundsChanged); return !error && registerKeyboardHandler(env, win_ref) && registerMouseHandler(env, win_ref); } @@ -185,6 +212,8 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nCreate(JNIEnv *env, jclass kWindowCollapseBoxAttribute| kWindowStandardHandlerAttribute; SetRect(&rect, x, y, x + width, y + height); + center_x = x + width/2; + center_y = y + height/2; current_fullscreen = fullscreen == JNI_TRUE; miniaturized = false; activated = true; @@ -232,8 +261,8 @@ JNIEXPORT void JNICALL Java_org_lwjgl_opengl_Window_nCreate(JNIEnv *env, jclass } ShowWindow(win_ref); SelectWindow(win_ref); - CGPoint p = {x, y}; - CGWarpMouseCursorPosition(p); + warpCursorToCenter(); + CGPoint p = {center_x, center_y}; CGPostMouseEvent(p, FALSE, 1, TRUE); CGPostMouseEvent(p, FALSE, 1, FALSE); } diff --git a/src/native/macosx/tools.cpp b/src/native/macosx/tools.cpp index 7503b3a9..290329bb 100644 --- a/src/native/macosx/tools.cpp +++ b/src/native/macosx/tools.cpp @@ -1,5 +1,6 @@ -#include "tools.h" #include +#include "tools.h" +#include "common_tools.h" MPCriticalRegionID critical_region;