diff options
Diffstat (limited to 'src/newt/native')
-rw-r--r-- | src/newt/native/BroadcomEGL.c | 194 | ||||
-rw-r--r-- | src/newt/native/InputEvent.h | 46 | ||||
-rw-r--r-- | src/newt/native/IntelGDL.c | 401 | ||||
-rw-r--r-- | src/newt/native/KDWindow.c | 347 | ||||
-rw-r--r-- | src/newt/native/KeyEvent.h | 200 | ||||
-rw-r--r-- | src/newt/native/MacWindow.m | 504 | ||||
-rw-r--r-- | src/newt/native/MouseEvent.h | 15 | ||||
-rw-r--r-- | src/newt/native/NewtCommon.c | 55 | ||||
-rw-r--r-- | src/newt/native/NewtCommon.h | 15 | ||||
-rw-r--r-- | src/newt/native/NewtMacWindow.h | 75 | ||||
-rw-r--r-- | src/newt/native/NewtMacWindow.m | 498 | ||||
-rw-r--r-- | src/newt/native/ScreenMode.h | 16 | ||||
-rw-r--r-- | src/newt/native/WindowEvent.h | 12 | ||||
-rw-r--r-- | src/newt/native/WindowsWindow.c | 1731 | ||||
-rw-r--r-- | src/newt/native/X11Window.c | 1633 |
15 files changed, 5742 insertions, 0 deletions
diff --git a/src/newt/native/BroadcomEGL.c b/src/newt/native/BroadcomEGL.c new file mode 100644 index 000000000..ddac0ef45 --- /dev/null +++ b/src/newt/native/BroadcomEGL.c @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifdef _WIN32 + #include <windows.h> +#else + #include <inttypes.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "com_jogamp_newt_impl_opengl_broadcom_egl_Window.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +#include <EGL/egl.h> + +typedef unsigned int GLuint; + +EGLDisplay EGLUtil_CreateDisplayByNative( GLuint uiWidth, GLuint uiHeight ); +void EGLUtil_DestroyDisplay( EGLDisplay eglDisplay ); + +EGLSurface EGLUtil_CreateWindowByNative( EGLDisplay eglDisplay, /* bool */ GLuint bChromakey, GLuint *puiWidth, GLuint *puiHeight ); +void EGLUtil_DestroyWindow( EGLDisplay eglDisplay, EGLSurface eglSurface ); +void EGLUtil_SwapWindow( EGLDisplay eglDisplay, EGLSurface eglSurface ); + +#define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +static jmethodID windowCreatedID = NULL; + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Display_DispatchMessages + (JNIEnv *env, jobject obj) +{ + // FIXME: n/a + (void) env; + (void) obj; +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Display_CreateDisplay + (JNIEnv *env, jobject obj, jint width, jint height) +{ + (void) env; + (void) obj; + EGLDisplay dpy = EGLUtil_CreateDisplayByNative( (GLuint) width, (GLuint) height ); + if(NULL==dpy) { + fprintf(stderr, "[CreateDisplay] failed: NULL\n"); + } else { + DBG_PRINT( "[CreateDisplay] ok: %p, %ux%u\n", dpy, width, height); + } + return (jlong) (intptr_t) dpy; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Display_DestroyDisplay + (JNIEnv *env, jobject obj, jlong display) +{ + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + (void) env; + (void) obj; + DBG_PRINT( "[DestroyDisplay] dpy %p\n", dpy); + + EGLUtil_DestroyDisplay(dpy); + + DBG_PRINT( "[DestroyDisplay] X\n"); +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Window_initIDs + (JNIEnv *env, jclass clazz) +{ + windowCreatedID = (*env)->GetMethodID(env, clazz, "windowCreated", "(III)V"); + if (windowCreatedID == NULL) { + DBG_PRINT( "initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT( "initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Window_CreateWindow + (JNIEnv *env, jobject obj, jlong display, jboolean chromaKey, jint width, jint height) +{ + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + EGLSurface window = 0; + GLuint uiWidth=(GLuint)width, uiHeight=(GLuint)height; + + if(dpy==NULL) { + fprintf(stderr, "[RealizeWindow] invalid display connection..\n"); + return 0; + } + DBG_PRINT( "[RealizeWindow.Create] dpy %p %ux%u\n", dpy, uiWidth, uiHeight); + + window = EGLUtil_CreateWindowByNative( dpy, chromaKey, &uiWidth, &uiHeight ); + + if(NULL==window) { + fprintf(stderr, "[RealizeWindow.Create] failed: NULL\n"); + return 0; + } + EGLint cfgID=0; + if(EGL_FALSE==eglQuerySurface(dpy, window, EGL_CONFIG_ID, &cfgID)) { + fprintf(stderr, "[RealizeWindow.ConfigID] failed: window %p\n", window); + EGLUtil_DestroyWindow(dpy, window); + return 0; + } + (*env)->CallVoidMethod(env, obj, windowCreatedID, (jint) cfgID, (jint)uiWidth, (jint)uiHeight); + DBG_PRINT( "[RealizeWindow.Create] ok: win %p, cfgid %d, %ux%u\n", window, cfgID, uiWidth, uiHeight); + + // release and destroy already made context .. + EGLContext ctx = eglGetCurrentContext(); + DBG_PRINT( "[RealizeWindow.Create] ctx %p - KEEP ALIVE \n", ctx); + /*eglMakeCurrent(dpy, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT); */ + DBG_PRINT( "[RealizeWindow.Create] 2\n"); + // eglDestroyContext(dpy, ctx); // culprit ? FIXME ? + DBG_PRINT( "[RealizeWindow.Create] 2 - eglDestroyContext - DISABLED - Duh ?\n"); + + DBG_PRINT( "[RealizeWindow.Create] X\n"); + + return (jlong) (intptr_t) window; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Window_CloseWindow + (JNIEnv *env, jobject obj, jlong display, jlong window) +{ + EGLDisplay dpy = (EGLDisplay) (intptr_t) display; + EGLSurface surf = (EGLSurface) (intptr_t) window; + + DBG_PRINT( "[CloseWindow] dpy %p, win %p\n", dpy, surf); + + EGLUtil_DestroyWindow(dpy, surf); + + DBG_PRINT( "[CloseWindow] X\n"); +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_broadcom_egl_Window_SwapWindow + (JNIEnv *env, jobject obj, jlong display, jlong window) +{ + EGLDisplay dpy = (EGLDisplay) (intptr_t) display; + EGLSurface surf = (EGLSurface) (intptr_t) window; + + DBG_PRINT( "[SwapWindow] dpy %p, win %p\n", dpy, surf); + + EGLUtil_SwapWindow( dpy, surf ); + + DBG_PRINT( "[SwapWindow] X\n"); +} + diff --git a/src/newt/native/InputEvent.h b/src/newt/native/InputEvent.h new file mode 100644 index 000000000..b42c06d21 --- /dev/null +++ b/src/newt/native/InputEvent.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifndef _INPUT_EVENT_H_ +#define _INPUT_EVENT_H_ + +#define EVENT_SHIFT_MASK 1 +#define EVENT_CTRL_MASK 2 +#define EVENT_META_MASK 4 +#define EVENT_ALT_MASK 8 +#define EVENT_ALT_GRAPH_MASK 32 +#define EVENT_BUTTON1_MASK (1<<6) +#define EVENT_BUTTON2_MASK (1<<7) +#define EVENT_BUTTON3_MASK (1<<8) + +#endif diff --git a/src/newt/native/IntelGDL.c b/src/newt/native/IntelGDL.c new file mode 100644 index 000000000..eb6becbbd --- /dev/null +++ b/src/newt/native/IntelGDL.c @@ -0,0 +1,401 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <inttypes.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "com_jogamp_newt_impl_intel_gdl_Display.h" +#include "com_jogamp_newt_impl_intel_gdl_Screen.h" +#include "com_jogamp_newt_impl_intel_gdl_Window.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +#include <gdl.h> +#include <gdl_version.h> + +#define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, "*** INTEL-GDL: " __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +static jmethodID screenCreatedID = NULL; +static jmethodID updateBoundsID = NULL; + +#define NUM_PLANES 5 +static jobject newtWindows[NUM_PLANES] = { NULL, NULL, NULL, NULL, NULL } ; +static gdl_plane_id_t planes[NUM_PLANES] = { GDL_PLANE_ID_UPP_A, GDL_PLANE_ID_UPP_B, GDL_PLANE_ID_UPP_C, GDL_PLANE_ID_UPP_D, GDL_PLANE_ID_UPP_E }; + +static int getWindowIdx(jobject win) { + int i; + for(i=0; i<NUM_PLANES && newtWindows[i]!=win; i++) ; + return (i<NUM_PLANES)?i:-1; +} +static int getPlaneIdx(gdl_plane_id_t plane) { + int i; + for(i=0; i<NUM_PLANES && planes[i]!=plane; i++) ; + return (i<NUM_PLANES)?i:-1; +} + +static jobject getNewtWindow(gdl_plane_id_t plane) { + int idx = getPlaneIdx(plane); + if(idx>0) { + return newtWindows[idx]; + } + return NULL; +} +static gdl_plane_id_t getPlane(jobject win) { + int idx = getWindowIdx(win); + if(idx>0) { + return planes[idx]; + } + return GDL_PLANE_ID_UNDEFINED; +} + +static gdl_plane_id_t allocPlane(JNIEnv *env, jobject newtWindow) { + int i = getWindowIdx(NULL); + if (i<NUM_PLANES) { + newtWindows[i] = (*env)->NewGlobalRef(env, newtWindow); + return planes[i]; + } + return GDL_PLANE_ID_UNDEFINED; +} +static void freePlane(JNIEnv *env, gdl_plane_id_t plane) { + int i = getPlaneIdx(plane); + if (i<NUM_PLANES) { + if(NULL!=newtWindows[i]) { + (*env)->DeleteGlobalRef(env, newtWindows[i]); + newtWindows[i] = NULL;; + } + } +} + +static void JNI_ThrowNew(JNIEnv *env, const char *throwable, const char* message) { + jclass throwableClass = (*env)->FindClass(env, throwable); + if (throwableClass == NULL) { + (*env)->FatalError(env, "Failed to load throwable class"); + } + + if ((*env)->ThrowNew(env, throwableClass, message) != 0) { + (*env)->FatalError(env, "Failed to throw throwable"); + } +} + + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_intel_gdl_Display_DispatchMessages + (JNIEnv *env, jobject obj, jlong displayHandle, jobject focusedWindow) +{ + // FIXME: n/a + (void) env; + (void) obj; + (void) displayHandle; + /** + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + jobject newtWin = getNewtWindow(plane); + if(NULL!=newtWin) { + // here we can dispatch messages .. etc + } */ +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_intel_gdl_Display_CreateDisplay + (JNIEnv *env, jobject obj) +{ + gdl_ret_t retval; + gdl_driver_info_t * p_driver_info = NULL; + + (void) env; + (void) obj; + + DBG_PRINT("[CreateDisplay]\n"); + + retval = gdl_init(0); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_init"); + return (jlong)0; + } + + p_driver_info = calloc(sizeof(gdl_driver_info_t), 1); + retval = gdl_get_driver_info(p_driver_info); + if (retval != GDL_SUCCESS) { + free(p_driver_info); + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_get_driver_info"); + return (jlong)0; + } + DBG_PRINT("[gdl_get_driver_info: major %d minor %d vers %d build %d flags %x name %s size %d avail %d]\n", + p_driver_info->header_version_major, p_driver_info->header_version_minor, + p_driver_info->gdl_version, p_driver_info->build_tag, p_driver_info->flags, + p_driver_info->name, p_driver_info->mem_size, p_driver_info->mem_avail); + + + return (jlong) (intptr_t) p_driver_info; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_intel_gdl_Display_DestroyDisplay + (JNIEnv *env, jobject obj, jlong displayHandle) +{ + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + (void) env; + (void) obj; + + if(NULL!=p_driver_info) { + gdl_close(); + free(p_driver_info); + } + + DBG_PRINT("[DestroyDisplay] X\n"); +} + +/** + * Screen + */ + +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_intel_gdl_Screen_initIDs + (JNIEnv *env, jclass clazz) +{ + screenCreatedID = (*env)->GetMethodID(env, clazz, "screenCreated", "(II)V"); + if (screenCreatedID == NULL) { + DBG_PRINT("initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT("initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_intel_gdl_Screen_GetScreenInfo + (JNIEnv *env, jobject obj, jlong displayHandle, jint idx) +{ + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + gdl_display_info_t display_info; + gdl_display_id_t id; + gdl_ret_t retval; + + switch(idx) { + case 1: + id = GDL_DISPLAY_ID_1; + break; + default: + id = GDL_DISPLAY_ID_0; + } + + retval = gdl_get_display_info(id, &display_info); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_get_display_info"); + return; + } + + DBG_PRINT("[gdl_get_display_info: width %d height %d]\n", + display_info.tvmode.width, display_info.tvmode.height); + + (*env)->CallVoidMethod(env, obj, screenCreatedID, (jint)display_info.tvmode.width, (jint)display_info.tvmode.height); +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_intel_gdl_Window_initIDs + (JNIEnv *env, jclass clazz) +{ + updateBoundsID = (*env)->GetMethodID(env, clazz, "updateBounds", "(IIII)V"); + if (updateBoundsID == NULL) { + DBG_PRINT("initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT("initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_intel_gdl_Window_CreateSurface + (JNIEnv *env, jobject obj, jlong displayHandle, jint scr_width, jint scr_height, jint x, jint y, jint width, jint height) { + + gdl_driver_info_t * p_driver_info = (gdl_driver_info_t *) (intptr_t) displayHandle; + gdl_ret_t retval; + gdl_pixel_format_t pixelFormat = GDL_PF_ARGB_32; + gdl_color_space_t colorSpace = GDL_COLOR_SPACE_RGB; + gdl_rectangle_t srcRect, dstRect; + + (void) env; + (void) obj; + + gdl_plane_id_t plane = allocPlane(env, obj); + if(plane == GDL_PLANE_ID_UNDEFINED) { + DBG_PRINT("CreateSurface failed, couldn't alloc plane\n" ); + return 0; + } + + DBG_PRINT("[CreateSurface: screen %dx%d, win %d/%d %dx%d plane %d]\n", + scr_width, scr_height, x, y, width, height, plane); + + /** Overwrite - TEST - Check semantics of dstRect! + x = 0; + y = 0; + width = scr_width; + height = scr_height; */ + + srcRect.origin.x = x; + srcRect.origin.y = y; + srcRect.width = width; + srcRect.height = height; + + dstRect.origin.x = x; + dstRect.origin.y = y; + dstRect.width = width; + dstRect.height = height; + + retval = gdl_plane_reset(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_reset"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_config_begin(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_begin"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_COLOR_SPACE, &colorSpace); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr color space"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_PIXEL_FORMAT, &pixelFormat); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr pixel format"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_DST_RECT, &dstRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr dstRect"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_RECT, &srcRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr srcRect"); + freePlane(env, plane); + return (jlong)0; + } + + retval = gdl_plane_config_end(GDL_FALSE); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_end"); + freePlane(env, plane); + return (jlong)0; + } + + (*env)->CallVoidMethod(env, obj, updateBoundsID, (jint)x, (jint)y, (jint)width, (jint)height); + + DBG_PRINT("[CreateSurface] returning plane %d\n", plane); + + return (jlong) (intptr_t) plane; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_intel_gdl_Window_CloseSurface + (JNIEnv *env, jobject obj, jlong display, jlong surface) +{ + gdl_plane_id_t plane = (gdl_plane_id_t) (intptr_t) surface ; + freePlane(env, plane); + + DBG_PRINT("[CloseSurface] plane %d\n", plane); +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_intel_gdl_Window_SetBounds0 + (JNIEnv *env, jobject obj, jlong surface, jint scr_width, jint scr_height, jint x, jint y, jint width, jint height) { + + gdl_plane_id_t plane = (gdl_plane_id_t) (intptr_t) surface ; + gdl_ret_t retval; + gdl_rectangle_t srcRect, dstRect; + + (void) env; + (void) obj; + + DBG_PRINT("[SetBounds0: screen %dx%d, win %d/%d %dx%d plane %d]\n", + scr_width, scr_height, x, y, width, height, plane); + + srcRect.origin.x = x; + srcRect.origin.y = y; + srcRect.width = width; + srcRect.height = height; + + dstRect.origin.x = x; + dstRect.origin.y = y; + dstRect.width = width; + dstRect.height = height; + + retval = gdl_plane_config_begin(plane); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_begin"); + return; + } + + retval = gdl_plane_set_attr(GDL_PLANE_DST_RECT, &dstRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr dstRect"); + return; + } + + retval = gdl_plane_set_attr(GDL_PLANE_SRC_RECT, &srcRect); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_set_attr srcRect"); + return; + } + + retval = gdl_plane_config_end(GDL_FALSE); + if (retval != GDL_SUCCESS) { + JNI_ThrowNew(env, "java/lang/IllegalStateException", "gdl_plane_config_end"); + return; + } + + (*env)->CallVoidMethod(env, obj, updateBoundsID, (jint)x, (jint)y, (jint)width, (jint)height); + + DBG_PRINT("[SetBounds0] returning plane %d\n", plane); +} + diff --git a/src/newt/native/KDWindow.c b/src/newt/native/KDWindow.c new file mode 100644 index 000000000..33af4d963 --- /dev/null +++ b/src/newt/native/KDWindow.c @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#ifdef _WIN32 + #include <windows.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#ifdef _WIN32 + /* This typedef is apparently needed for Microsoft compilers before VC8, + and on Windows CE */ + #if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1400 ) + #ifdef _WIN64 + typedef long long intptr_t; + #else + typedef int intptr_t; + #endif + #elif !defined(__MINGW64__) && _MSC_VER <= 1500 + #ifdef _WIN64 // [ + typedef __int64 intptr_t; + #else // _WIN64 ][ + typedef int intptr_t; + #endif // _WIN64 ] + #else + #include <inttypes.h> + #endif +#else + #include <inttypes.h> +#endif + +#include <KD/kd.h> + +#include "com_jogamp_newt_impl_opengl_kd_KDWindow.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" + +// #define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stdout, __VA_ARGS__) +#else + #define DBG_PRINT(...) +#endif + +#ifdef VERBOSE_ON + #ifdef _WIN32_WCE + #define STDOUT_FILE "\\Storage Card\\stdout.txt" + #define STDERR_FILE "\\Storage Card\\stderr.txt" + #endif +#endif + +#define JOGL_KD_USERDATA_MAGIC 0xDEADBEEF +typedef struct { + long magic; + KDWindow * kdWindow; + jobject javaWindow; +} JOGLKDUserdata; + +static jmethodID windowCreatedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowDestroyedID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID sendKeyEventID = NULL; + +/** + * Display + */ + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDDisplay_DispatchMessages + (JNIEnv *env, jobject obj) +{ + const KDEvent * evt; + int numEvents = 0; + + // Periodically take a break + while( numEvents<100 && NULL!=(evt=kdWaitEvent(0)) ) { + KDWindow *kdWindow; + jobject javaWindow; + JOGLKDUserdata * userData = (JOGLKDUserdata *)(intptr_t)evt->userptr; + if(NULL == userData || userData->magic!=JOGL_KD_USERDATA_MAGIC) { + DBG_PRINT( "event unrelated: evt type: 0x%X\n", evt->type); + continue; + } + kdWindow = userData->kdWindow; + javaWindow = userData->javaWindow; + DBG_PRINT( "[DispatchMessages]: userData %p, evt type: 0x%X\n", userData, evt->type); + + numEvents++; + + // FIXME: support resize and window re-positioning events + + switch(evt->type) { + case KD_EVENT_WINDOW_FOCUS: + { + KDboolean hasFocus; + kdGetWindowPropertybv(kdWindow, KD_WINDOWPROPERTY_FOCUS, &hasFocus); + DBG_PRINT( "event window focus : src: %p\n", userData); + } + break; + case KD_EVENT_WINDOW_CLOSE: + { + DBG_PRINT( "event window close : src: %p\n", userData); + (*env)->CallVoidMethod(env, javaWindow, windowDestroyNotifyID); + // Called by Window.java: DestroyWindow(wnd); + // (*env)->CallVoidMethod(env, javaWindow, windowDestroyedID); + } + break; + case KD_EVENT_WINDOWPROPERTY_CHANGE: + { + const KDEventWindowProperty* prop = &evt->data.windowproperty; + switch (prop->pname) { + case KD_WINDOWPROPERTY_SIZE: + { + KDint32 v[2]; + if(!kdGetWindowPropertyiv(kdWindow, KD_WINDOWPROPERTY_SIZE, v)) { + DBG_PRINT( "event window size change : src: %p %dx%d\n", userData, v[0], v[1]); + (*env)->CallVoidMethod(env, javaWindow, sizeChangedID, (jint) v[0], (jint) v[1], JNI_FALSE); + } else { + DBG_PRINT( "event window size change error: src: %p %dx%d\n", userData, v[0], v[1]); + } + } + break; + case KD_WINDOWPROPERTY_FOCUS: + DBG_PRINT( "event window focus: src: %p\n", userData); + break; + case KD_WINDOWPROPERTY_VISIBILITY: + { + KDboolean visible; + kdGetWindowPropertybv(kdWindow, KD_WINDOWPROPERTY_VISIBILITY, &visible); + DBG_PRINT( "event window visibility: src: %p, v:%d\n", userData, visible); + (*env)->CallVoidMethod(env, javaWindow, visibleChangedID, visible?JNI_TRUE:JNI_FALSE); + } + break; + default: + break; + } + } + break; + case KD_EVENT_INPUT_POINTER: + { + const KDEventInputPointer* ptr = &(evt->data.inputpointer); + // button idx: evt->data.input.index + // pressed = ev->data.input.value.i + // time = ev->timestamp + if(KD_INPUT_POINTER_SELECT==ptr->index) { + DBG_PRINT( "event mouse click: src: %p, s:%d, (%d,%d)\n", userData, ptr->select, ptr->x, ptr->y); + (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, + (ptr->select==0) ? (jint) EVENT_MOUSE_RELEASED : (jint) EVENT_MOUSE_PRESSED, + (jint) 0, + (jint) ptr->x, (jint) ptr->y, 1, 0); + } else { + DBG_PRINT( "event mouse: src: %d, s:%p, i:0x%X (%d,%d)\n", userData, ptr->select, ptr->index, ptr->x, ptr->y); + (*env)->CallVoidMethod(env, javaWindow, sendMouseEventID, (jint) EVENT_MOUSE_MOVED, + 0, + (jint) ptr->x, (jint) ptr->y, 0, 0); + } + } + break; + } + } +} + +/** + * Window + */ + +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_initIDs + (JNIEnv *env, jclass clazz) +{ +#ifdef VERBOSE_ON + #ifdef _WIN32_WCE + _wfreopen(TEXT(STDOUT_FILE),L"w",stdout); + _wfreopen(TEXT(STDERR_FILE),L"w",stderr); + #endif +#endif + windowCreatedID = (*env)->GetMethodID(env, clazz, "windowCreated", "(J)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + if (windowCreatedID == NULL || + sizeChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + windowDestroyedID == NULL || + sendMouseEventID == NULL || + sendKeyEventID == NULL) { + DBG_PRINT( "initIDs failed\n" ); + return JNI_FALSE; + } + DBG_PRINT( "initIDs ok\n" ); + return JNI_TRUE; +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_CreateWindow + (JNIEnv *env, jobject obj, jlong display, jintArray jAttrs) +{ + jint * attrs = NULL; + jsize attrsLen; + EGLDisplay dpy = (EGLDisplay)(intptr_t)display; + KDWindow *window = 0; + + if(dpy==NULL) { + fprintf(stderr, "[CreateWindow] invalid display connection..\n"); + return 0; + } + + attrsLen = (*env)->GetArrayLength(env, jAttrs); + if(0==attrsLen) { + fprintf(stderr, "[CreateWindow] attribute array size 0..\n"); + return 0; + } + attrs = (*env)->GetIntArrayElements(env, jAttrs, 0); + if(NULL==attrs) { + fprintf(stderr, "[CreateWindow] attribute array NULL..\n"); + return 0; + } + + JOGLKDUserdata * userData = kdMalloc(sizeof(JOGLKDUserdata)); + userData->magic = JOGL_KD_USERDATA_MAGIC; + window = kdCreateWindow(dpy, attrs, (void *)userData); + + (*env)->ReleaseIntArrayElements(env, jAttrs, attrs, 0); + + if(NULL==window) { + kdFree(userData); + fprintf(stderr, "[CreateWindow] failed: 0x%X\n", kdGetError()); + } else { + userData->javaWindow = (*env)->NewGlobalRef(env, obj); + userData->kdWindow = window; + (*env)->CallVoidMethod(env, obj, windowCreatedID, (jlong) (intptr_t) userData); + DBG_PRINT( "[CreateWindow] ok: %p, userdata %p\n", window, userData); + } + return (jlong) (intptr_t) window; +} + +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_RealizeWindow + (JNIEnv *env, jobject obj, jlong window) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + EGLNativeWindowType nativeWindow=0; + + jint res = kdRealizeWindow(w, &nativeWindow); + if(res) { + fprintf(stderr, "[RealizeWindow] failed: 0x%X, 0x%X\n", res, kdGetError()); + nativeWindow = NULL; + } + DBG_PRINT( "[RealizeWindow] ok: %p\n", nativeWindow); + return (jlong) (intptr_t) nativeWindow; +} + +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_CloseWindow + (JNIEnv *env, jobject obj, jlong window, jlong juserData) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + JOGLKDUserdata * userData = (JOGLKDUserdata*) (intptr_t) juserData; + int res = kdDestroyWindow(w); + (*env)->DeleteGlobalRef(env, userData->javaWindow); + kdFree(userData); + + DBG_PRINT( "[CloseWindow] res: %d\n", res); + return res; +} + +/* + * Class: com_jogamp_newt_impl_opengl_kd_KDWindow + * Method: setVisible0 + * Signature: (JJZ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_setVisible0 + (JNIEnv *env, jobject obj, jlong window, jboolean visible) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + KDboolean v = (visible==JNI_TRUE)?KD_TRUE:KD_FALSE; + kdSetWindowPropertybv(w, KD_WINDOWPROPERTY_VISIBILITY, &v); + DBG_PRINT( "[setVisible] v=%d\n", visible); + (*env)->CallVoidMethod(env, obj, visibleChangedID, visible); // FIXME: or send via event ? +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_setFullScreen0 + (JNIEnv *env, jobject obj, jlong window, jboolean fullscreen) +{ +/** not supported, due to missing NV property .. + KDWindow *w = (KDWindow*) (intptr_t) window; + KDboolean v = fullscreen; + + int res = kdSetWindowPropertybv(w, KD_WINDOWPROPERTY_FULLSCREEN_NV, &v); + DBG_PRINT( "[setFullScreen] v=%d, res=%d\n", fullscreen, res); + (void)res; +*/ + (void)env; + (void)obj; + (void)window; + (void)fullscreen; +} + +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_opengl_kd_KDWindow_setSize0 + (JNIEnv *env, jobject obj, jlong window, jint width, jint height) +{ + KDWindow *w = (KDWindow*) (intptr_t) window; + KDint32 v[] = { width, height }; + + int res = kdSetWindowPropertyiv(w, KD_WINDOWPROPERTY_SIZE, v); + DBG_PRINT( "[setSize] v=%dx%d, res=%d\n", width, height, res); + (void)res; + + (*env)->CallVoidMethod(env, obj, sizeChangedID, (jint) width, (jint) height, JNI_FALSE); +} + diff --git a/src/newt/native/KeyEvent.h b/src/newt/native/KeyEvent.h new file mode 100644 index 000000000..1ead0f5e8 --- /dev/null +++ b/src/newt/native/KeyEvent.h @@ -0,0 +1,200 @@ + +#ifndef _KEY_EVENT_H_ +#define _KEY_EVENT_H_ + +#define EVENT_KEY_PRESSED 300 +#define EVENT_KEY_RELEASED 301 +#define EVENT_KEY_TYPED 302 + +#define J_CHAR_UNDEFINED 0xFFFF; +#define J_VK_ENTER '\n' +#define J_VK_BACK_SPACE '\b' +#define J_VK_TAB '\t' +#define J_VK_CANCEL 0x03 +#define J_VK_CLEAR 0x0C +#define J_VK_SHIFT 0x10 +#define J_VK_CONTROL 0x11 +#define J_VK_ALT 0x12 +#define J_VK_PAUSE 0x13 +#define J_VK_CAPS_LOCK 0x14 +#define J_VK_ESCAPE 0x1B +#define J_VK_SPACE 0x20 +#define J_VK_PAGE_UP 0x21 +#define J_VK_PAGE_DOWN 0x22 +#define J_VK_END 0x23 +#define J_VK_HOME 0x24 +#define J_VK_LEFT 0x25 +#define J_VK_UP 0x26 +#define J_VK_RIGHT 0x27 +#define J_VK_DOWN 0x28 +#define J_VK_COMMA 0x2C +#define J_VK_MINUS 0x2D +#define J_VK_PERIOD 0x2E +#define J_VK_SLASH 0x2F +#define J_VK_0 0x30 +#define J_VK_1 0x31 +#define J_VK_2 0x32 +#define J_VK_3 0x33 +#define J_VK_4 0x34 +#define J_VK_5 0x35 +#define J_VK_6 0x36 +#define J_VK_7 0x37 +#define J_VK_8 0x38 +#define J_VK_9 0x39 +#define J_VK_SEMICOLON 0x3B +#define J_VK_EQUALS 0x3D +#define J_VK_A 0x41 +#define J_VK_B 0x42 +#define J_VK_C 0x43 +#define J_VK_D 0x44 +#define J_VK_E 0x45 +#define J_VK_F 0x46 +#define J_VK_G 0x47 +#define J_VK_H 0x48 +#define J_VK_I 0x49 +#define J_VK_J 0x4A +#define J_VK_K 0x4B +#define J_VK_L 0x4C +#define J_VK_M 0x4D +#define J_VK_N 0x4E +#define J_VK_O 0x4F +#define J_VK_P 0x50 +#define J_VK_Q 0x51 +#define J_VK_R 0x52 +#define J_VK_S 0x53 +#define J_VK_T 0x54 +#define J_VK_U 0x55 +#define J_VK_V 0x56 +#define J_VK_W 0x57 +#define J_VK_X 0x58 +#define J_VK_Y 0x59 +#define J_VK_Z 0x5A +#define J_VK_OPEN_BRACKET 0x5B +#define J_VK_BACK_SLASH 0x5C +#define J_VK_CLOSE_BRACKET 0x5D +#define J_VK_NUMPAD0 0x60 +#define J_VK_NUMPAD1 0x61 +#define J_VK_NUMPAD2 0x62 +#define J_VK_NUMPAD3 0x63 +#define J_VK_NUMPAD4 0x64 +#define J_VK_NUMPAD5 0x65 +#define J_VK_NUMPAD6 0x66 +#define J_VK_NUMPAD7 0x67 +#define J_VK_NUMPAD8 0x68 +#define J_VK_NUMPAD9 0x69 +#define J_VK_MULTIPLY 0x6A +#define J_VK_ADD 0x6B +#define J_VK_SEPARATOR 0x6C +#define J_VK_SUBTRACT 0x6D +#define J_VK_DECIMAL 0x6E +#define J_VK_DIVIDE 0x6F +#define J_VK_DELETE 0x7F /* ASCII DEL */ +#define J_VK_NUM_LOCK 0x90 +#define J_VK_SCROLL_LOCK 0x91 +#define J_VK_F1 0x70 +#define J_VK_F2 0x71 +#define J_VK_F3 0x72 +#define J_VK_F4 0x73 +#define J_VK_F5 0x74 +#define J_VK_F6 0x75 +#define J_VK_F7 0x76 +#define J_VK_F8 0x77 +#define J_VK_F9 0x78 +#define J_VK_F10 0x79 +#define J_VK_F11 0x7A +#define J_VK_F12 0x7B +#define J_VK_F13 0xF000 +#define J_VK_F14 0xF001 +#define J_VK_F15 0xF002 +#define J_VK_F16 0xF003 +#define J_VK_F17 0xF004 +#define J_VK_F18 0xF005 +#define J_VK_F19 0xF006 +#define J_VK_F20 0xF007 +#define J_VK_F21 0xF008 +#define J_VK_F22 0xF009 +#define J_VK_F23 0xF00A +#define J_VK_F24 0xF00B +#define J_VK_PRINTSCREEN 0x9A +#define J_VK_INSERT 0x9B +#define J_VK_HELP 0x9C +#define J_VK_META 0x9D +#define J_VK_BACK_QUOTE 0xC0 +#define J_VK_QUOTE 0xDE +#define J_VK_KP_UP 0xE0 +#define J_VK_KP_DOWN 0xE1 +#define J_VK_KP_LEFT 0xE2 +#define J_VK_KP_RIGHT 0xE3 +#define J_VK_DEAD_GRAVE 0x80 +#define J_VK_DEAD_ACUTE 0x81 +#define J_VK_DEAD_CIRCUMFLEX 0x82 +#define J_VK_DEAD_TILDE 0x83 +#define J_VK_DEAD_MACRON 0x84 +#define J_VK_DEAD_BREVE 0x85 +#define J_VK_DEAD_ABOVEDOT 0x86 +#define J_VK_DEAD_DIAERESIS 0x87 +#define J_VK_DEAD_ABOVERING 0x88 +#define J_VK_DEAD_DOUBLEACUTE 0x89 +#define J_VK_DEAD_CARON 0x8a +#define J_VK_DEAD_CEDILLA 0x8b +#define J_VK_DEAD_OGONEK 0x8c +#define J_VK_DEAD_IOTA 0x8d +#define J_VK_DEAD_VOICED_SOUND 0x8e +#define J_VK_DEAD_SEMIVOICED_SOUND 0x8f +#define J_VK_AMPERSAND 0x96 +#define J_VK_ASTERISK 0x97 +#define J_VK_QUOTEDBL 0x98 +#define J_VK_LESS 0x99 +#define J_VK_GREATER 0xa0 +#define J_VK_BRACELEFT 0xa1 +#define J_VK_BRACERIGHT 0xa2 +#define J_VK_AT 0x0200 +#define J_VK_COLON 0x0201 +#define J_VK_CIRCUMFLEX 0x0202 +#define J_VK_DOLLAR 0x0203 +#define J_VK_EURO_SIGN 0x0204 +#define J_VK_EXCLAMATION_MARK 0x0205 +#define J_VK_INVERTED_EXCLAMATION_MARK 0x0206 +#define J_VK_LEFT_PARENTHESIS 0x0207 +#define J_VK_NUMBER_SIGN 0x0208 +#define J_VK_PLUS 0x0209 +#define J_VK_RIGHT_PARENTHESIS 0x020A +#define J_VK_UNDERSCORE 0x020B +#define J_VK_WINDOWS 0x020C +#define J_VK_CONTEXT_MENU 0x020D +#define J_VK_FINAL 0x0018 +#define J_VK_CONVERT 0x001C +#define J_VK_NONCONVERT 0x001D +#define J_VK_ACCEPT 0x001E +#define J_VK_MODECHANGE 0x001F +#define J_VK_KANA 0x0015 +#define J_VK_KANJI 0x0019 +#define J_VK_ALPHANUMERIC 0x00F0 +#define J_VK_KATAKANA 0x00F1 +#define J_VK_HIRAGANA 0x00F2 +#define J_VK_FULL_WIDTH 0x00F3 +#define J_VK_HALF_WIDTH 0x00F4 +#define J_VK_ROMAN_CHARACTERS 0x00F5 +#define J_VK_ALL_CANDIDATES 0x0100 +#define J_VK_PREVIOUS_CANDIDATE 0x0101 +#define J_VK_CODE_INPUT 0x0102 +#define J_VK_JAPANESE_KATAKANA 0x0103 +#define J_VK_JAPANESE_HIRAGANA 0x0104 +#define J_VK_JAPANESE_ROMAN 0x0105 +#define J_VK_KANA_LOCK 0x0106 +#define J_VK_INPUT_METHOD_ON_OFF 0x0107 +#define J_VK_CUT 0xFFD1 +#define J_VK_COPY 0xFFCD +#define J_VK_PASTE 0xFFCF +#define J_VK_UNDO 0xFFCB +#define J_VK_AGAIN 0xFFC9 +#define J_VK_FIND 0xFFD0 +#define J_VK_PROPS 0xFFCA +#define J_VK_STOP 0xFFC8 +#define J_VK_COMPOSE 0xFF20 +#define J_VK_ALT_GRAPH 0xFF7E +#define J_VK_BEGIN 0xFF58 +#define J_VK_UNDEFINED 0x0 + +#endif + diff --git a/src/newt/native/MacWindow.m b/src/newt/native/MacWindow.m new file mode 100644 index 000000000..e5021dcad --- /dev/null +++ b/src/newt/native/MacWindow.m @@ -0,0 +1,504 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import <inttypes.h> + +#import "com_jogamp_newt_impl_macosx_MacWindow.h" +#import "NewtMacWindow.h" + +#import "MouseEvent.h" +#import "KeyEvent.h" + +#import <ApplicationServices/ApplicationServices.h> + +#import <stdio.h> + +NSString* jstringToNSString(JNIEnv* env, jstring jstr) +{ + const jchar* jstrChars = (*env)->GetStringChars(env, jstr, NULL); + NSString* str = [[NSString alloc] initWithCharacters: jstrChars length: (*env)->GetStringLength(env, jstr)]; + (*env)->ReleaseStringChars(env, jstr, jstrChars); + return str; +} + +void setFrameTopLeftPoint(NSWindow* pwin, NSWindow* win, jint x, jint y) +{ + NSScreen* screen = [NSScreen mainScreen]; + + // this allows for better compatibility with awt behavior + NSRect visibleRect; // either screen or parent-window + NSPoint pt; + int d_pty=0; // parent titlebar height + int d_ptx=0; + + if(NULL==pwin) { + visibleRect = [screen frame]; + } else { + visibleRect = [pwin frame]; + NSView* pview = [pwin contentView]; + NSRect viewRect = [pview frame]; + d_pty = visibleRect.size.height - viewRect.size.height; + (void) d_ptx; + //d_pty = visibleRect.origin.y - viewRect.size.height; + //d_ptx = visibleRect.size.height - viewRect.size.height; + fprintf(stderr, "pwin %lf/%lf %lfx%lf, pview %lf/%lf %lfx%lf -> %d/%d\n", + visibleRect.origin.x, + visibleRect.origin.y, + visibleRect.size.width, + visibleRect.size.height, + viewRect.origin.x, + viewRect.origin.y, + viewRect.size.width, + viewRect.size.height, + (int)x, (int)y); + + } + + pt = NSMakePoint(visibleRect.origin.x + x, visibleRect.origin.y + visibleRect.size.height - y - d_pty); + + [win setFrameTopLeftPoint: pt]; +} + +static NewtView * changeContentView(JNIEnv *env, jobject javaWindowObject, NSWindow *pwin, NSWindow *win, NewtView *newView) { + NSView* oldNSView = [win contentView]; + NewtView* oldView = NULL; + + if(NULL!=oldNSView) { +NS_DURING + // Available >= 10.5 - Makes the menubar disapear + if([oldNSView isInFullScreenMode]) { + [oldNSView exitFullScreenModeWithOptions: NULL]; + } +NS_HANDLER +NS_ENDHANDLER + if( [oldNSView isMemberOfClass:[NewtView class]] ) { + oldView = (NewtView *) oldNSView; + + jobject globJavaWindowObject = [oldView getJavaWindowObject]; + (*env)->DeleteGlobalRef(env, globJavaWindowObject); + [oldView setJavaWindowObject: NULL]; + } + /** FIXME: Tried child window: auto clip or message reception .. + if(NULL!=pwin) { + [oldView removeFromSuperview]; + } */ + } + if(NULL!=newView) { + jobject globJavaWindowObject = (*env)->NewGlobalRef(env, javaWindowObject); + [newView setJavaWindowObject: globJavaWindowObject]; + [newView setJNIEnv: env]; + + /** FIXME: Tried child window: auto clip or message reception .. + if(NULL!=pwin) { + NSView* pview = [pwin contentView]; + [pview addSubview: newView]; + } */ + } + [win setContentView: newView]; + + // make sure the insets are updated in the java object + NewtMacWindow* newtw = (NewtMacWindow*)win; + [newtw updateInsets: env]; + + return oldView; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacDisplay + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_macosx_MacDisplay_initNSApplication0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + // This little bit of magic is needed in order to receive mouse + // motion events and allow key focus to be properly transferred. + // FIXME: are these Carbon APIs? They come from the + // ApplicationServices.framework. + ProcessSerialNumber psn; + if (GetCurrentProcess(&psn) == noErr) { + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess(&psn); + } + + // Initialize the shared NSApplication instance + [NSApplication sharedApplication]; + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + return (jboolean) JNI_TRUE; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacDisplay + * Method: dispatchMessages0 + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacDisplay_dispatchMessages0 + (JNIEnv *env, jobject unused) +{ + NSEvent* event = NULL; + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + +NS_DURING + + int num_events = 0; + + // Periodically take a break + do { + // FIXME: ignoring event mask for the time being + event = [NSApp nextEventMatchingMask: NSAnyEventMask + untilDate: [NSDate distantPast] + inMode: NSDefaultRunLoopMode + dequeue: YES]; + if (event != NULL) { + [NSApp sendEvent: event]; + + num_events++; + } + } while (num_events<100 && event != NULL); + +NS_HANDLER + + // just ignore it .. + +NS_ENDHANDLER + + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacScreen + * Method: getWidthImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_macosx_MacScreen_getWidthImpl0 + (JNIEnv *env, jclass clazz, jint screen_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + NSRect rect = [screen frame]; + + [pool release]; + + return (jint) (rect.size.width); +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacScreen + * Method: getHeightImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_macosx_MacScreen_getHeightImpl0 + (JNIEnv *env, jclass clazz, jint screen_idx) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + NSRect rect = [screen frame]; + + [pool release]; + + return (jint) (rect.size.height); +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_initIDs0 + (JNIEnv *env, jclass clazz) +{ + static int initialized = 0; + + if(initialized) return JNI_TRUE; + initialized = 1; + + // Need this when debugging, as it is necessary to attach gdb to + // the running java process -- "gdb java" doesn't work + // printf("Going to sleep for 10 seconds\n"); + // sleep(10); + + return (jboolean) [NewtMacWindow initNatives: env forClass: clazz]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: createWindow0 + * Signature: (JIIIIZIIIJ)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_createWindow0 + (JNIEnv *env, jobject jthis, jlong parent, jint x, jint y, jint w, jint h, jboolean fullscreen, jint styleMask, + jint bufferingType, jint screen_idx, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSRect rect = NSMakeRect(x, y, w, h); + + NSArray *screens = [NSScreen screens]; + if(screen_idx<0) screen_idx=0; + if(screen_idx>=[screens count]) screen_idx=0; + NSScreen *screen = (NSScreen *) [screens objectAtIndex: screen_idx]; + + if (fullscreen) { + styleMask = NSBorderlessWindowMask; + NSRect rect = [screen frame]; + w = (jint) (rect.size.width); + h = (jint) (rect.size.height); + } + + // Allocate the window + NSWindow* window = [[[NewtMacWindow alloc] initWithContentRect: rect + styleMask: (NSUInteger) styleMask + backing: (NSBackingStoreType) bufferingType + screen: screen] retain]; + + NSObject *nsParentObj = (NSObject*) ((intptr_t) parent); + NSWindow* parentWindow = NULL; + if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSWindow class]] ) { + parentWindow = (NSWindow*) nsParentObj; + } else if( nsParentObj != NULL && [nsParentObj isKindOfClass:[NSView class]] ) { + NSView* view = (NSView*) nsParentObj; + parentWindow = [view window]; + fprintf(stderr, "createWindow0 - Parent is NSView : %p -> %p (win) \n", nsParentObj, parentWindow); + } else { + fprintf(stderr, "createWindow0 - Parent is neither NSWindow nor NSView : %p\n", nsParentObj); + } + if(NULL!=parentWindow) { + [parentWindow addChildWindow: window ordered: NSWindowAbove]; + [window setParentWindow: parentWindow]; + } + + if (fullscreen) { + [window setOpaque: YES]; + } else { + // If the window is undecorated, assume we want the possibility of + // a shaped window, so make it non-opaque and the background color clear + if ((styleMask & NSTitledWindowMask) == 0) { + [window setOpaque: NO]; + [window setBackgroundColor: [NSColor clearColor]]; + } + } + + // Immediately re-position the window based on an upper-left coordinate system + setFrameTopLeftPoint(parentWindow, window, x, y); + + // specify we want mouse-moved events + [window setAcceptsMouseMovedEvents:YES]; + + // Use given NewtView or allocate an NewtView if NULL + NewtView* view = (0==jview)? [[NewtView alloc] initWithFrame: rect] : (NewtView*) ((intptr_t) jview) ; + + // Set the content view + (void) changeContentView(env, jthis, parentWindow, window, view); + +NS_DURING + // Available >= 10.5 - Makes the menubar disapear + if(fullscreen) { + [view enterFullScreenMode: screen withOptions:NULL]; + } +NS_HANDLER +NS_ENDHANDLER + + // Set the next responder to be the window so that we can forward + // right mouse button down events + [view setNextResponder: window]; + + [pool release]; + + return (jlong) ((intptr_t) window); +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: makeKeyAndOrderFront + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_makeKeyAndOrderFront0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win makeKeyAndOrderFront: win]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: makeKey + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_makeKey0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win makeKeyWindow]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: orderOut + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_orderOut0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + [win orderOut: win]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: close0 + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_close0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSView* view = [win contentView]; + [win orderOut: win]; +NS_DURING + if(NULL!=view) { + // Available >= 10.5 - Makes the menubar disapear + if([view isInFullScreenMode]) { + [view exitFullScreenModeWithOptions: NULL]; + } + } +NS_HANDLER +NS_ENDHANDLER + [win close]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: setTitle0 + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_setTitle0 + (JNIEnv *env, jobject unused, jlong window, jstring title) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSString* str = jstringToNSString(env, title); + [str autorelease]; + [win setTitle: str]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: contentView + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_contentView0 + (JNIEnv *env, jobject unused, jlong window) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + jlong res = (jlong) ((intptr_t) [win contentView]); + [pool release]; + return res; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: changeContentView + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_changeContentView0 + (JNIEnv *env, jobject jthis, jlong parent, jlong window, jlong jview) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* pwin = (NewtMacWindow*) ((intptr_t) parent); + NSWindow* win = (NewtMacWindow*) ((intptr_t) window); + NewtView* newView = (NewtView *) ((intptr_t) jview); + + NewtView* oldView = changeContentView(env, jthis, pwin, win, newView); + + [pool release]; + + return (jlong) ((intptr_t) oldView); +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: setContentSize + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_setContentSize0 + (JNIEnv *env, jobject unused, jlong window, jint w, jint h) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* win = (NSWindow*) ((intptr_t) window); + NSSize sz = NSMakeSize(w, h); + [win setContentSize: sz]; + [pool release]; +} + +/* + * Class: com_jogamp_newt_impl_macosx_MacWindow + * Method: setFrameTopLeftPoint + * Signature: (JII)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_macosx_MacWindow_setFrameTopLeftPoint0 + (JNIEnv *env, jobject unused, jlong parent, jlong window, jint x, jint y) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSWindow* pwin = (NSWindow*) ((intptr_t) parent); + NSWindow* win = (NSWindow*) ((intptr_t) window); + setFrameTopLeftPoint(pwin, win, x, y); + [pool release]; +} + diff --git a/src/newt/native/MouseEvent.h b/src/newt/native/MouseEvent.h new file mode 100644 index 000000000..e9c0476ef --- /dev/null +++ b/src/newt/native/MouseEvent.h @@ -0,0 +1,15 @@ + +#ifndef _MOUSE_EVENT_H_ +#define _MOUSE_EVENT_H_ + +// Generated by Java: EVENT_MOUSE_CLICKED = 200; +#define EVENT_MOUSE_ENTERED 201 +#define EVENT_MOUSE_EXITED 202 +#define EVENT_MOUSE_PRESSED 203 +#define EVENT_MOUSE_RELEASED 204 +#define EVENT_MOUSE_MOVED 205 +// can't find how to regenerate this file, adding manually +#define EVENT_MOUSE_WHEEL_MOVED 207 +// Generated by Java: EVENT_MOUSE_DRAGGED = 206; + +#endif diff --git a/src/newt/native/NewtCommon.c b/src/newt/native/NewtCommon.c new file mode 100644 index 000000000..0e3f99282 --- /dev/null +++ b/src/newt/native/NewtCommon.c @@ -0,0 +1,55 @@ + +#include "NewtCommon.h" + +static const char * const ClazzNameRuntimeException = "java/lang/RuntimeException"; +static jclass runtimeExceptionClz=NULL; + +void NewtCommon_FatalError(JNIEnv *env, const char* msg, ...) +{ + char buffer[512]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + fprintf(stderr, "%s\n", buffer); + (*env)->FatalError(env, buffer); +} + +void NewtCommon_throwNewRuntimeException(JNIEnv *env, const char* msg, ...) +{ + char buffer[512]; + va_list ap; + + va_start(ap, msg); + vsnprintf(buffer, sizeof(buffer), msg, ap); + va_end(ap); + + (*env)->ThrowNew(env, runtimeExceptionClz, buffer); +} + +void NewtCommon_init(JNIEnv *env) { + if(NULL==runtimeExceptionClz) { + jclass c = (*env)->FindClass(env, ClazzNameRuntimeException); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT X11Window: can't find %s", ClazzNameRuntimeException); + } + runtimeExceptionClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==runtimeExceptionClz) { + NewtCommon_FatalError(env, "NEWT X11Window: can't use %s", ClazzNameRuntimeException); + } + } +} + +jchar* NewtCommon_GetNullTerminatedStringChars(JNIEnv* env, jstring str) +{ + jchar* strChars = NULL; + strChars = calloc((*env)->GetStringLength(env, str) + 1, sizeof(jchar)); + if (strChars != NULL) { + (*env)->GetStringRegion(env, str, 0, (*env)->GetStringLength(env, str), strChars); + } + return strChars; +} + diff --git a/src/newt/native/NewtCommon.h b/src/newt/native/NewtCommon.h new file mode 100644 index 000000000..f5835f7c8 --- /dev/null +++ b/src/newt/native/NewtCommon.h @@ -0,0 +1,15 @@ + +#ifndef NEWT_COMMON_H +#define NEWT_COMMON_H 1 + +#include <jni.h> +#include <stdlib.h> + +void NewtCommon_init(JNIEnv *env); + +jchar* NewtCommon_GetNullTerminatedStringChars(JNIEnv* env, jstring str); + +void NewtCommon_FatalError(JNIEnv *env, const char* msg, ...); +void NewtCommon_throwNewRuntimeException(JNIEnv *env, const char* msg, ...); + +#endif diff --git a/src/newt/native/NewtMacWindow.h b/src/newt/native/NewtMacWindow.h new file mode 100644 index 000000000..7f0cd60c6 --- /dev/null +++ b/src/newt/native/NewtMacWindow.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import <AppKit/AppKit.h> +#import "jni.h" + +@interface NewtView : NSView +{ + jobject javaWindowObject; + + // This is set while messages are being dispatched and cleared afterward + JNIEnv* env; +} + +/* Set during event dispatching cycle */ +- (void) setJNIEnv: (JNIEnv*) env; +- (JNIEnv*) getJNIEnv; + +/* Register or deregister (NULL) the java Window object, + ie, if NULL, no events are send */ +- (void) setJavaWindowObject: (jobject) javaWindowObj; +- (jobject) getJavaWindowObject; + +- (void) rightMouseDown: (NSEvent*) theEvent; + +@end + +#if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 +@interface NewtMacWindow : NSWindow <NSWindowDelegate> +#else +@interface NewtMacWindow : NSWindow +#endif +{ +} + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jobject) clazz; + +- (void) updateInsets: (JNIEnv*) env; + +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSBackingStoreType) bufferingType + screen:(NSScreen *)screen; + +@end diff --git a/src/newt/native/NewtMacWindow.m b/src/newt/native/NewtMacWindow.m new file mode 100644 index 000000000..2d8a84b4b --- /dev/null +++ b/src/newt/native/NewtMacWindow.m @@ -0,0 +1,498 @@ +/* + * Copyright (c) 2009 Sun Microsystems, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#import "NewtMacWindow.h" +#import "InputEvent.h" +#import "KeyEvent.h" +#import "MouseEvent.h" + +jint GetDeltaY(NSEvent *event, jint javaMods) { + CGFloat deltaY = 0.0; + CGEventRef cgEvent = [event CGEvent]; + + if (CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventIsContinuous)) { + // mouse pad case + deltaY = + CGEventGetIntegerValueField(cgEvent, kCGScrollWheelEventPointDeltaAxis1); + } else { + // traditional mouse wheel case + deltaY = [event deltaY]; + if (deltaY == 0.0 && (javaMods & EVENT_SHIFT_MASK) != 0) { + // shift+vertical wheel scroll produces horizontal scroll + // we convert it to vertical + deltaY = [event deltaX]; + } + if (deltaY < 1.0 && deltaY > -1.0) { + deltaY *= 10.0; + } else { + if (deltaY < 0.0) { + deltaY = deltaY - 0.5f; + } else { + deltaY = deltaY + 0.5f; + } + } + } + + if (deltaY > 0) { + return (NSInteger)deltaY; + } else if (deltaY < 0) { + return -(NSInteger)deltaY; + } + + return 0; +} + +static jmethodID sendMouseEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID insetsChangedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowDestroyedID = NULL; + +@implementation NewtView +- (void) setJNIEnv: (JNIEnv*) theEnv +{ + env = theEnv; +} +- (JNIEnv*) getJNIEnv +{ + return env; +} + +- (void) setJavaWindowObject: (jobject) javaWindowObj +{ + javaWindowObject = javaWindowObj; +} + +- (jobject) getJavaWindowObject +{ + return javaWindowObject; +} + +- (void) rightMouseDown: (NSEvent*) theEvent +{ + NSResponder* next = [self nextResponder]; + if (next != nil) { + [next rightMouseDown: theEvent]; + } +} + +- (void)viewWillDraw +{ + fprintf(stderr, "*************** viewWillDraw: 0x%p", javaWindowObject); fflush(stderr); + [super viewWillDraw]; +} + +- (void)viewDidHide +{ + (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_FALSE); + [super viewDidHide]; +} + +- (void)viewDidUnhide +{ + (*env)->CallVoidMethod(env, javaWindowObject, visibleChangedID, JNI_TRUE); + [super viewDidUnhide]; +} + +@end + +@implementation NewtMacWindow + ++ (BOOL) initNatives: (JNIEnv*) env forClass: (jclass) clazz +{ + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(IIII)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V"); + if (sendMouseEventID && sendKeyEventID && sizeChangedID && visibleChangedID && insetsChangedID && + positionChangedID && focusChangedID && windowDestroyedID && windowDestroyNotifyID) + { + return YES; + } + return NO; +} + +- (void) updateInsets: (JNIEnv*) env +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + + // note: this is a simplistic implementation which doesn't take + // into account DPI and scaling factor + CGFloat l = contentRect.origin.x - frameRect.origin.x; + jint top = (jint)(frameRect.size.height - contentRect.size.height); + jint left = (jint)l; + jint bottom = (jint)(contentRect.origin.y - frameRect.origin.y); + jint right = (jint)(frameRect.size.width - (contentRect.size.width + l)); + + (*env)->CallVoidMethod(env, javaWindowObject, insetsChangedID, + left, top, right, bottom); +} + +- (id) initWithContentRect: (NSRect) contentRect + styleMask: (NSUInteger) windowStyle + backing: (NSBackingStoreType) bufferingType + screen:(NSScreen *)screen +{ + id res = [super initWithContentRect: contentRect + styleMask: windowStyle + backing: bufferingType + defer: YES + screen: screen]; + // Why is this necessary? Without it we don't get any of the + // delegate methods like resizing and window movement. + [self setDelegate: self]; + return res; +} + +- (BOOL) canBecomeKeyWindow +{ + // Even if the window is borderless, we still want it to be able + // to become the key window to receive keyboard events + return YES; +} + +static jint mods2JavaMods(NSUInteger mods) +{ + int javaMods = 0; + if (mods & NSShiftKeyMask) { + javaMods |= EVENT_SHIFT_MASK; + } + if (mods & NSControlKeyMask) { + javaMods |= EVENT_CTRL_MASK; + } + if (mods & NSCommandKeyMask) { + javaMods |= EVENT_META_MASK; + } + if (mods & NSAlternateKeyMask) { + javaMods |= EVENT_ALT_MASK; + } + return javaMods; +} + +- (void) sendKeyEvent: (NSEvent*) event eventType: (jint) evType +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + int i; + jint keyCode = (jint) [event keyCode]; + NSString* chars = [event charactersIgnoringModifiers]; + int len = [chars length]; + jint javaMods = mods2JavaMods([event modifierFlags]); + + for (i = 0; i < len; i++) { + // Note: the key code in the NSEvent does not map to anything we can use + jchar keyChar = (jchar) [chars characterAtIndex: i]; + + (*env)->CallVoidMethod(env, javaWindowObject, sendKeyEventID, + evType, javaMods, keyCode, keyChar); + } +} + +- (void) keyDown: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_PRESSED]; +} + +- (void) keyUp: (NSEvent*) theEvent +{ + [self sendKeyEvent: theEvent eventType: EVENT_KEY_RELEASED]; + [self sendKeyEvent: theEvent eventType: EVENT_KEY_TYPED]; +} + +- (void) sendMouseEvent: (NSEvent*) event eventType: (jint) evType +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + jint javaMods = mods2JavaMods([event modifierFlags]); + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + // NSPoint location = [event locationInWindow]; + // The following computation improves the behavior of mouse drag + // events when they also affect the location of the window, but it + // still isn't perfect + NSPoint curLocation = [NSEvent mouseLocation]; + NSPoint location = NSMakePoint(curLocation.x - frameRect.origin.x, + curLocation.y - frameRect.origin.y); + + // convert to 1-based button number (or use zero if no button is involved) + // TODO: detect mouse button when mouse wheel scrolled + jint javaButtonNum = 0; + jint scrollDeltaY = 0; + switch ([event type]) { + case NSScrollWheel: { + scrollDeltaY = GetDeltaY(event, javaMods); + break; + } + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSLeftMouseDragged: + javaButtonNum = 1; + break; + case NSRightMouseDown: + case NSRightMouseUp: + case NSRightMouseDragged: + javaButtonNum = 3; + break; + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSOtherMouseDragged: + javaButtonNum = 2; + break; + default: + javaButtonNum = 0; + break; + } + + if (evType == EVENT_MOUSE_WHEEL_MOVED && scrollDeltaY == 0) { + // ignore 0 increment wheel scroll events + return; + } + (*env)->CallVoidMethod(env, javaWindowObject, sendMouseEventID, + evType, javaMods, + (jint) location.x, + (jint) (contentRect.size.height - location.y), + javaButtonNum, scrollDeltaY); +} + +- (void) mouseEntered: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_ENTERED]; +} + +- (void) mouseExited: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_EXITED]; +} + +- (void) mouseMoved: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) scrollWheel: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_WHEEL_MOVED]; +} + +- (void) mouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) mouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) mouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void) rightMouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) rightMouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) rightMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void) otherMouseDown: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_PRESSED]; +} + +- (void) otherMouseDragged: (NSEvent*) theEvent +{ + // Note use of MOUSE_MOVED event type because mouse dragged events are synthesized by Java + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_MOVED]; +} + +- (void) otherMouseUp: (NSEvent*) theEvent +{ + [self sendMouseEvent: theEvent eventType: EVENT_MOUSE_RELEASED]; +} + +- (void)windowDidResize: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + // update insets on every window resize for lack of better hook place + [self updateInsets: env]; + + NSRect frameRect = [self frame]; + NSRect contentRect = [self contentRectForFrameRect: frameRect]; + + (*env)->CallVoidMethod(env, javaWindowObject, sizeChangedID, + (jint) contentRect.size.width, + (jint) contentRect.size.height, JNI_FALSE); +} + +- (void)windowDidMove: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + NSRect rect = [self frame]; + NSScreen* screen = NULL; + NSRect screenRect; + NSPoint pt; + + screen = [self screen]; + // this allows for better compatibility with awt behavior + screenRect = [screen frame]; + pt = NSMakePoint(rect.origin.x, screenRect.origin.y + screenRect.size.height - rect.origin.y - rect.size.height); + + (*env)->CallVoidMethod(env, javaWindowObject, positionChangedID, + (jint) pt.x, (jint) pt.y); +} + +- (void)windowWillClose: (NSNotification*) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, windowDestroyNotifyID); + // Can't issue call here - locked window state, done from Java method + // (*env)->CallVoidMethod(env, javaWindowObject, windowDestroyedID); // No OSX hook for DidClose, so do it here + + // EOL .. + (*env)->DeleteGlobalRef(env, javaWindowObject); + [view setJavaWindowObject: NULL]; +} + +- (void) windowDidBecomeKey: (NSNotification *) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_TRUE); +} + +- (void) windowDidResignKey: (NSNotification *) notification +{ + NSView* nsview = [self contentView]; + if( ! [nsview isMemberOfClass:[NewtView class]] ) { + return; + } + NewtView* view = (NewtView *) nsview; + jobject javaWindowObject = [view getJavaWindowObject]; + JNIEnv* env = [view getJNIEnv]; + if (env==NULL || javaWindowObject == NULL) { + return; + } + + (*env)->CallVoidMethod(env, javaWindowObject, focusChangedID, JNI_FALSE); +} + +@end diff --git a/src/newt/native/ScreenMode.h b/src/newt/native/ScreenMode.h new file mode 100644 index 000000000..0a760d54a --- /dev/null +++ b/src/newt/native/ScreenMode.h @@ -0,0 +1,16 @@ +/** + * WARNING: must be synced with com.jogamp.newt.util.ScreenModeUtil#streamIn*(int[]) + */ + +#ifndef _SCREEN_MODE_H +#define _SCREEN_MODE_H + +#define NUM_RESOLUTION_PROPERTIES 2 /* width, height */ +#define NUM_SURFACE_SIZE_PROPERTIES 1 /* bpp */ +#define NUM_MONITOR_MODE_PROPERTIES 3 /* ScreenSizeMM[width, height], refresh-rate */ +#define NUM_SCREEN_MODE_PROPERTIES 1 /* rotation */ + +#define NUM_SCREEN_MODE_PROPERTIES_ALL 8 /* count + the above */ + +#endif + diff --git a/src/newt/native/WindowEvent.h b/src/newt/native/WindowEvent.h new file mode 100644 index 000000000..6274f9443 --- /dev/null +++ b/src/newt/native/WindowEvent.h @@ -0,0 +1,12 @@ + +#ifndef _WINDOW_EVENT_H_ +#define _WINDOW_EVENT_H_ + +#define EVENT_WINDOW_RESIZED = 100; +#define EVENT_WINDOW_MOVED = 101; +#define EVENT_WINDOW_DESTROY_NOTIFY = 102; +#define EVENT_WINDOW_GAINED_FOCUS = 103; +#define EVENT_WINDOW_LOST_FOCUS = 104; +#define EVENT_WINDOW_REPAINT = 105; // TODO + +#endif diff --git a/src/newt/native/WindowsWindow.c b/src/newt/native/WindowsWindow.c new file mode 100644 index 000000000..46d710eda --- /dev/null +++ b/src/newt/native/WindowsWindow.c @@ -0,0 +1,1731 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <Windows.h> +#include <Windowsx.h> +#include <tchar.h> +#include <stdlib.h> +// NOTE: it looks like SHFullScreen and/or aygshell.dll is not available on the APX 2500 any more +// #ifdef UNDER_CE +// #include "aygshell.h" +// #endif + +/* This typedef is apparently needed for Microsoft compilers before VC8, + and on Windows CE and MingW32 */ +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1400 ) + #ifdef _WIN64 + typedef long long intptr_t; + #else + typedef int intptr_t; + #endif +#elif !defined(__MINGW64__) && _MSC_VER <= 1500 + #ifdef _WIN64 // [ + typedef __int64 intptr_t; + #else // _WIN64 ][ + typedef int intptr_t; + #endif // _WIN64 ] +#else + #include <inttypes.h> +#endif + +#if !defined(__MINGW64__) && _MSC_VER <= 1500 + // FIXME: Determine for which MSVC versions .. + #define strdup(s) _strdup(s) +#endif + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif //WM_MOUSEWHEEL + +#ifndef WHEEL_DELTA +#define WHEEL_DELTA 120 +#endif //WHEEL_DELTA + +#ifndef WHEEL_PAGESCROLL +#define WHEEL_PAGESCROLL (UINT_MAX) +#endif //WHEEL_PAGESCROLL + +#ifndef GET_WHEEL_DELTA_WPARAM // defined for (_WIN32_WINNT >= 0x0500) +#define GET_WHEEL_DELTA_WPARAM(wParam) ((short)HIWORD(wParam)) +#endif + +#ifndef MONITOR_DEFAULTTONULL +#define MONITOR_DEFAULTTONULL 0 +#endif +#ifndef MONITOR_DEFAULTTOPRIMARY +#define MONITOR_DEFAULTTOPRIMARY 1 +#endif +#ifndef MONITOR_DEFAULTTONEAREST +#define MONITOR_DEFAULTTONEAREST 2 +#endif +#ifndef EDS_ROTATEDMODE +#define EDS_ROTATEDMODE 0x00000004 +#endif +#ifndef DISPLAY_DEVICE_ACTIVE +#define DISPLAY_DEVICE_ACTIVE 0x00000001 +#endif + +#include "com_jogamp_newt_impl_windows_WindowsDisplay.h" +#include "com_jogamp_newt_impl_windows_WindowsScreen.h" +#include "com_jogamp_newt_impl_windows_WindowsWindow.h" + +#include "MouseEvent.h" +#include "InputEvent.h" +#include "KeyEvent.h" +#include "ScreenMode.h" + +#include "NewtCommon.h" + +// #define VERBOSE_ON 1 +// #define DEBUG_KEYS 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) +#else + #define DBG_PRINT(...) +#endif + +#define STD_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) + +static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point"; +static const char * const ClazzAnyCstrName = "<init>"; +static const char * const ClazzNamePointCstrSignature = "(II)V"; + +static jclass pointClz = NULL; +static jmethodID pointCstr = NULL; + +static jmethodID insetsChangedID = NULL; +static jmethodID sizeChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowDestroyedID = NULL; +static jmethodID windowRepaintID = NULL; +static jmethodID enqueueMouseEventID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID enqueueKeyEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID focusActionID = NULL; +static jmethodID enqueueRequestFocusID = NULL; + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd); + +typedef struct { + JNIEnv* jenv; + jobject jinstance; +} WindowUserData; + +typedef struct { + UINT javaKey; + UINT windowsKey; +} KeyMapEntry; + +// Static table, arranged more or less spatially. +static KeyMapEntry keyMapTable[] = { + // Modifier keys + {J_VK_CAPS_LOCK, VK_CAPITAL}, + {J_VK_SHIFT, VK_SHIFT}, + {J_VK_CONTROL, VK_CONTROL}, + {J_VK_ALT, VK_MENU}, + {J_VK_NUM_LOCK, VK_NUMLOCK}, + + // Miscellaneous Windows keys + {J_VK_WINDOWS, VK_LWIN}, + {J_VK_WINDOWS, VK_RWIN}, + {J_VK_CONTEXT_MENU, VK_APPS}, + + // Alphabet + {J_VK_A, 'A'}, + {J_VK_B, 'B'}, + {J_VK_C, 'C'}, + {J_VK_D, 'D'}, + {J_VK_E, 'E'}, + {J_VK_F, 'F'}, + {J_VK_G, 'G'}, + {J_VK_H, 'H'}, + {J_VK_I, 'I'}, + {J_VK_J, 'J'}, + {J_VK_K, 'K'}, + {J_VK_L, 'L'}, + {J_VK_M, 'M'}, + {J_VK_N, 'N'}, + {J_VK_O, 'O'}, + {J_VK_P, 'P'}, + {J_VK_Q, 'Q'}, + {J_VK_R, 'R'}, + {J_VK_S, 'S'}, + {J_VK_T, 'T'}, + {J_VK_U, 'U'}, + {J_VK_V, 'V'}, + {J_VK_W, 'W'}, + {J_VK_X, 'X'}, + {J_VK_Y, 'Y'}, + {J_VK_Z, 'Z'}, + {J_VK_0, '0'}, + {J_VK_1, '1'}, + {J_VK_2, '2'}, + {J_VK_3, '3'}, + {J_VK_4, '4'}, + {J_VK_5, '5'}, + {J_VK_6, '6'}, + {J_VK_7, '7'}, + {J_VK_8, '8'}, + {J_VK_9, '9'}, + {J_VK_ENTER, VK_RETURN}, + {J_VK_SPACE, VK_SPACE}, + {J_VK_BACK_SPACE, VK_BACK}, + {J_VK_TAB, VK_TAB}, + {J_VK_ESCAPE, VK_ESCAPE}, + {J_VK_INSERT, VK_INSERT}, + {J_VK_DELETE, VK_DELETE}, + {J_VK_HOME, VK_HOME}, + {J_VK_END, VK_END}, + {J_VK_PAGE_UP, VK_PRIOR}, + {J_VK_PAGE_DOWN, VK_NEXT}, + {J_VK_CLEAR, VK_CLEAR}, // NumPad 5 + + // NumPad with NumLock off & extended arrows block (triangular) + {J_VK_LEFT, VK_LEFT}, + {J_VK_RIGHT, VK_RIGHT}, + {J_VK_UP, VK_UP}, + {J_VK_DOWN, VK_DOWN}, + + // NumPad with NumLock on: numbers + {J_VK_NUMPAD0, VK_NUMPAD0}, + {J_VK_NUMPAD1, VK_NUMPAD1}, + {J_VK_NUMPAD2, VK_NUMPAD2}, + {J_VK_NUMPAD3, VK_NUMPAD3}, + {J_VK_NUMPAD4, VK_NUMPAD4}, + {J_VK_NUMPAD5, VK_NUMPAD5}, + {J_VK_NUMPAD6, VK_NUMPAD6}, + {J_VK_NUMPAD7, VK_NUMPAD7}, + {J_VK_NUMPAD8, VK_NUMPAD8}, + {J_VK_NUMPAD9, VK_NUMPAD9}, + + // NumPad with NumLock on + {J_VK_MULTIPLY, VK_MULTIPLY}, + {J_VK_ADD, VK_ADD}, + {J_VK_SEPARATOR, VK_SEPARATOR}, + {J_VK_SUBTRACT, VK_SUBTRACT}, + {J_VK_DECIMAL, VK_DECIMAL}, + {J_VK_DIVIDE, VK_DIVIDE}, + + // Functional keys + {J_VK_F1, VK_F1}, + {J_VK_F2, VK_F2}, + {J_VK_F3, VK_F3}, + {J_VK_F4, VK_F4}, + {J_VK_F5, VK_F5}, + {J_VK_F6, VK_F6}, + {J_VK_F7, VK_F7}, + {J_VK_F8, VK_F8}, + {J_VK_F9, VK_F9}, + {J_VK_F10, VK_F10}, + {J_VK_F11, VK_F11}, + {J_VK_F12, VK_F12}, + {J_VK_F13, VK_F13}, + {J_VK_F14, VK_F14}, + {J_VK_F15, VK_F15}, + {J_VK_F16, VK_F16}, + {J_VK_F17, VK_F17}, + {J_VK_F18, VK_F18}, + {J_VK_F19, VK_F19}, + {J_VK_F20, VK_F20}, + {J_VK_F21, VK_F21}, + {J_VK_F22, VK_F22}, + {J_VK_F23, VK_F23}, + {J_VK_F24, VK_F24}, + + {J_VK_PRINTSCREEN, VK_SNAPSHOT}, + {J_VK_SCROLL_LOCK, VK_SCROLL}, + {J_VK_PAUSE, VK_PAUSE}, + {J_VK_CANCEL, VK_CANCEL}, + {J_VK_HELP, VK_HELP}, + + // Japanese +/* + {J_VK_CONVERT, VK_CONVERT}, + {J_VK_NONCONVERT, VK_NONCONVERT}, + {J_VK_INPUT_METHOD_ON_OFF, VK_KANJI}, + {J_VK_ALPHANUMERIC, VK_DBE_ALPHANUMERIC}, + {J_VK_KATAKANA, VK_DBE_KATAKANA}, + {J_VK_HIRAGANA, VK_DBE_HIRAGANA}, + {J_VK_FULL_WIDTH, VK_DBE_DBCSCHAR}, + {J_VK_HALF_WIDTH, VK_DBE_SBCSCHAR}, + {J_VK_ROMAN_CHARACTERS, VK_DBE_ROMAN}, +*/ + + {J_VK_UNDEFINED, 0} +}; + +/* +Dynamic mapping table for OEM VK codes. This table is refilled +by BuildDynamicKeyMapTable when keyboard layout is switched. +(see NT4 DDK src/input/inc/vkoem.h for OEM VK_ values). +*/ +typedef struct { + // OEM VK codes known in advance + UINT windowsKey; + // depends on input langauge (kbd layout) + UINT javaKey; +} DynamicKeyMapEntry; + +static DynamicKeyMapEntry dynamicKeyMapTable[] = { + {0x00BA, J_VK_UNDEFINED}, // VK_OEM_1 + {0x00BB, J_VK_UNDEFINED}, // VK_OEM_PLUS + {0x00BC, J_VK_UNDEFINED}, // VK_OEM_COMMA + {0x00BD, J_VK_UNDEFINED}, // VK_OEM_MINUS + {0x00BE, J_VK_UNDEFINED}, // VK_OEM_PERIOD + {0x00BF, J_VK_UNDEFINED}, // VK_OEM_2 + {0x00C0, J_VK_UNDEFINED}, // VK_OEM_3 + {0x00DB, J_VK_UNDEFINED}, // VK_OEM_4 + {0x00DC, J_VK_UNDEFINED}, // VK_OEM_5 + {0x00DD, J_VK_UNDEFINED}, // VK_OEM_6 + {0x00DE, J_VK_UNDEFINED}, // VK_OEM_7 + {0x00DF, J_VK_UNDEFINED}, // VK_OEM_8 + {0x00E2, J_VK_UNDEFINED}, // VK_OEM_102 + {0, 0} +}; + +// Auxiliary tables used to fill the above dynamic table. We first +// find the character for the OEM VK code using ::MapVirtualKey and +// then go through these auxiliary tables to map it to Java VK code. + +typedef struct { + WCHAR c; + UINT javaKey; +} CharToVKEntry; + +static const CharToVKEntry charToVKTable[] = { + {L'!', J_VK_EXCLAMATION_MARK}, + {L'"', J_VK_QUOTEDBL}, + {L'#', J_VK_NUMBER_SIGN}, + {L'$', J_VK_DOLLAR}, + {L'&', J_VK_AMPERSAND}, + {L'\'', J_VK_QUOTE}, + {L'(', J_VK_LEFT_PARENTHESIS}, + {L')', J_VK_RIGHT_PARENTHESIS}, + {L'*', J_VK_ASTERISK}, + {L'+', J_VK_PLUS}, + {L',', J_VK_COMMA}, + {L'-', J_VK_MINUS}, + {L'.', J_VK_PERIOD}, + {L'/', J_VK_SLASH}, + {L':', J_VK_COLON}, + {L';', J_VK_SEMICOLON}, + {L'<', J_VK_LESS}, + {L'=', J_VK_EQUALS}, + {L'>', J_VK_GREATER}, + {L'@', J_VK_AT}, + {L'[', J_VK_OPEN_BRACKET}, + {L'\\', J_VK_BACK_SLASH}, + {L']', J_VK_CLOSE_BRACKET}, + {L'^', J_VK_CIRCUMFLEX}, + {L'_', J_VK_UNDERSCORE}, + {L'`', J_VK_BACK_QUOTE}, + {L'{', J_VK_BRACELEFT}, + {L'}', J_VK_BRACERIGHT}, + {0x00A1, J_VK_INVERTED_EXCLAMATION_MARK}, + {0x20A0, J_VK_EURO_SIGN}, // ???? + {0,0} +}; + +// For dead accents some layouts return ASCII punctuation, while some +// return spacing accent chars, so both should be listed. NB: MS docs +// say that conversion routings return spacing accent character, not +// combining. +static const CharToVKEntry charToDeadVKTable[] = { + {L'`', J_VK_DEAD_GRAVE}, + {L'\'', J_VK_DEAD_ACUTE}, + {0x00B4, J_VK_DEAD_ACUTE}, + {L'^', J_VK_DEAD_CIRCUMFLEX}, + {L'~', J_VK_DEAD_TILDE}, + {0x02DC, J_VK_DEAD_TILDE}, + {0x00AF, J_VK_DEAD_MACRON}, + {0x02D8, J_VK_DEAD_BREVE}, + {0x02D9, J_VK_DEAD_ABOVEDOT}, + {L'"', J_VK_DEAD_DIAERESIS}, + {0x00A8, J_VK_DEAD_DIAERESIS}, + {0x02DA, J_VK_DEAD_ABOVERING}, + {0x02DD, J_VK_DEAD_DOUBLEACUTE}, + {0x02C7, J_VK_DEAD_CARON}, // aka hacek + {L',', J_VK_DEAD_CEDILLA}, + {0x00B8, J_VK_DEAD_CEDILLA}, + {0x02DB, J_VK_DEAD_OGONEK}, + {0x037A, J_VK_DEAD_IOTA}, // ASCII ??? + {0x309B, J_VK_DEAD_VOICED_SOUND}, + {0x309C, J_VK_DEAD_SEMIVOICED_SOUND}, + {0,0} +}; + +// ANSI CP identifiers are no longer than this +#define MAX_ACP_STR_LEN 7 + +static void BuildDynamicKeyMapTable() +{ + HKL hkl = GetKeyboardLayout(0); + // Will need this to reset layout after dead keys. + UINT spaceScanCode = MapVirtualKeyEx(VK_SPACE, 0, hkl); + DynamicKeyMapEntry *dynamic; + + LANGID idLang = LOWORD(GetKeyboardLayout(0)); + UINT codePage; + TCHAR strCodePage[MAX_ACP_STR_LEN]; + // use the LANGID to create a LCID + LCID idLocale = MAKELCID(idLang, SORT_DEFAULT); + // get the ANSI code page associated with this locale + if (GetLocaleInfo(idLocale, LOCALE_IDEFAULTANSICODEPAGE, + strCodePage, sizeof(strCodePage)/sizeof(TCHAR)) > 0 ) + { + codePage = _ttoi(strCodePage); + } else { + codePage = GetACP(); + } + + // Entries in dynamic table that maps between Java VK and Windows + // VK are built in three steps: + // 1. Map windows VK to ANSI character (cannot map to unicode + // directly, since ::ToUnicode is not implemented on win9x) + // 2. Convert ANSI char to Unicode char + // 3. Map Unicode char to Java VK via two auxilary tables. + + for (dynamic = dynamicKeyMapTable; dynamic->windowsKey != 0; ++dynamic) + { + char cbuf[2] = { '\0', '\0'}; + WCHAR ucbuf[2] = { L'\0', L'\0' }; + int nchars; + UINT scancode; + const CharToVKEntry *charMap; + int nconverted; + WCHAR uc; + BYTE kbdState[256]; + + // Defaults to J_VK_UNDEFINED + dynamic->javaKey = J_VK_UNDEFINED; + + GetKeyboardState(kbdState); + + kbdState[dynamic->windowsKey] |= 0x80; // Press the key. + + // Unpress modifiers, since they are most likely pressed as + // part of the keyboard switching shortcut. + kbdState[VK_CONTROL] &= ~0x80; + kbdState[VK_SHIFT] &= ~0x80; + kbdState[VK_MENU] &= ~0x80; + + scancode = MapVirtualKeyEx(dynamic->windowsKey, 0, hkl); + nchars = ToAsciiEx(dynamic->windowsKey, scancode, kbdState, + (WORD*)cbuf, 0, hkl); + + // Auxiliary table used to map Unicode character to Java VK. + // Will assign a different table for dead keys (below). + charMap = charToVKTable; + + if (nchars < 0) { // Dead key + char junkbuf[2] = { '\0', '\0'}; + // Use a different table for dead chars since different layouts + // return different characters for the same dead key. + charMap = charToDeadVKTable; + + // We also need to reset layout so that next translation + // is unaffected by the dead status. We do this by + // translating <SPACE> key. + kbdState[dynamic->windowsKey] &= ~0x80; + kbdState[VK_SPACE] |= 0x80; + + ToAsciiEx(VK_SPACE, spaceScanCode, kbdState, + (WORD*)junkbuf, 0, hkl); + } + + nconverted = MultiByteToWideChar(codePage, 0, + cbuf, 1, ucbuf, 2); + + uc = ucbuf[0]; + { + const CharToVKEntry *map; + for (map = charMap; map->c != 0; ++map) { + if (uc == map->c) { + dynamic->javaKey = map->javaKey; + break; + } + } + } + + } // for each VK_OEM_* +} + +static jint GetModifiers() { + jint modifiers = 0; + // have to do &0xFFFF to avoid runtime assert caused by compiling with + // /RTCcsu + if (HIBYTE((GetKeyState(VK_CONTROL) & 0xFFFF)) != 0) { + modifiers |= EVENT_CTRL_MASK; + } + if (HIBYTE((GetKeyState(VK_SHIFT) & 0xFFFF)) != 0) { + modifiers |= EVENT_SHIFT_MASK; + } + if (HIBYTE((GetKeyState(VK_MENU) & 0xFFFF)) != 0) { + modifiers |= EVENT_ALT_MASK; + } + if (HIBYTE((GetKeyState(VK_LBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON1_MASK; + } + if (HIBYTE((GetKeyState(VK_MBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON2_MASK; + } + if (HIBYTE((GetKeyState(VK_RBUTTON) & 0xFFFF)) != 0) { + modifiers |= EVENT_BUTTON3_MASK; + } + + return modifiers; +} + +static int WmChar(JNIEnv *env, jobject window, UINT character, UINT repCnt, + UINT flags, BOOL system) +{ + // The Alt modifier is reported in the 29th bit of the lParam, + // i.e., it is the 13th bit of `flags' (which is HIWORD(lParam)). + BOOL alt_is_down = (flags & (1<<13)) != 0; + if (system && alt_is_down) { + if (character == VK_SPACE) { + return 1; + } + } + + if (character == VK_RETURN) { + character = J_VK_ENTER; + } + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_TYPED, + GetModifiers(), + (jint) -1, + (jchar) character); + return 1; +} + +UINT WindowsKeyToJavaKey(UINT windowsKey, UINT modifiers) +{ + int i, j; + // for the general case, use a bi-directional table + for (i = 0; keyMapTable[i].windowsKey != 0; i++) { + if (keyMapTable[i].windowsKey == windowsKey) { + return keyMapTable[i].javaKey; + } + } + for (j = 0; dynamicKeyMapTable[j].windowsKey != 0; j++) { + if (dynamicKeyMapTable[j].windowsKey == windowsKey) { + if (dynamicKeyMapTable[j].javaKey != J_VK_UNDEFINED) { + return dynamicKeyMapTable[j].javaKey; + } else { + break; + } + } + } + + return J_VK_UNDEFINED; +} + +static int WmKeyDown(JNIEnv *env, jobject window, UINT wkey, UINT repCnt, + UINT flags, BOOL system) +{ + UINT modifiers = 0, jkey = 0, character = -1; + if (wkey == VK_PROCESSKEY) { + return 1; + } + + modifiers = GetModifiers(); + jkey = WindowsKeyToJavaKey(wkey, modifiers); + +/* + character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); +*/ + + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_PRESSED, + modifiers, + (jint) jkey, + (jchar) character); + + /* windows does not create a WM_CHAR for the Del key + for some reason, so we need to create the KEY_TYPED event on the + WM_KEYDOWN. + */ + if (jkey == J_VK_DELETE) { + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_TYPED, + GetModifiers(), + (jint) -1, + (jchar) '\177'); + } + + return 0; +} + +static int WmKeyUp(JNIEnv *env, jobject window, UINT wkey, UINT repCnt, + UINT flags, BOOL system) +{ + UINT modifiers = 0, jkey = 0, character = -1; + if (wkey == VK_PROCESSKEY) { + return 1; + } + + modifiers = GetModifiers(); + jkey = WindowsKeyToJavaKey(wkey, modifiers); +/* + character = WindowsKeyToJavaChar(wkey, modifiers, SAVE); +*/ + + (*env)->CallVoidMethod(env, window, sendKeyEventID, + (jint) EVENT_KEY_RELEASED, + modifiers, + (jint) jkey, + (jchar) character); + + return 0; +} + +static void NewtWindows_requestFocus (JNIEnv *env, jobject window, HWND hwnd, jboolean force) { + HWND pHwnd, current; + pHwnd = GetParent(hwnd); + current = GetFocus(); + DBG_PRINT("*** WindowsWindow: requestFocus.S parent %p, window %p, isCurrent %d\n", + (void*) pHwnd, (void*)hwnd, current==hwnd); + if( JNI_TRUE==force || current!=hwnd) { + if( JNI_TRUE==force || JNI_FALSE == (*env)->CallBooleanMethod(env, window, focusActionID) ) { + UINT flags = SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE; + SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, flags); + SetForegroundWindow(hwnd); // Slightly Higher Priority + SetFocus(hwnd);// Sets Keyboard Focus To Window + if(NULL!=pHwnd) { + SetActiveWindow(hwnd); + } + DBG_PRINT("*** WindowsWindow: requestFocus.X1\n"); + } else { + DBG_PRINT("*** WindowsWindow: requestFocus.X0\n"); + } + } + DBG_PRINT("*** WindowsWindow: requestFocus.XX\n"); +} + +#if 0 + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd) +{ + // being naughty here + static RECT m_insets = { 0, 0, 0, 0 }; + RECT outside; + RECT inside; + POINT *rp_inside = (POINT *) (void *) &inside; + int dx, dy, dw, dh; + + if (IsIconic(hwnd)) { + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = -1; + return FALSE; + } + + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + + GetClientRect(hwnd, &inside); + GetWindowRect(hwnd, &outside); + + DBG_PRINT("*** WindowsWindow: UpdateInsets (a1) window %p, Inside CC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)inside.left, (int)inside.top, (int)inside.right, (int)inside.bottom, + (int)(inside.right - inside.left), (int)(inside.bottom - inside.top)); + DBG_PRINT("*** WindowsWindow: UpdateInsets (a1) window %p, Outside SC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)outside.left, (int)outside.top, (int)outside.right, (int)outside.bottom, + (int)(outside.right - outside.left), (int)(outside.bottom - outside.top)); + + // xform client -> screen coord + ClientToScreen(hwnd, rp_inside); + ClientToScreen(hwnd, rp_inside+1); + + DBG_PRINT("*** WindowsWindow: UpdateInsets (a2) window %p, Inside SC: %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)inside.left, (int)inside.top, (int)inside.right, (int)inside.bottom, + (int)(inside.right - inside.left), (int)(inside.bottom - inside.top)); + + m_insets.top = inside.top - outside.top; + m_insets.bottom = outside.bottom - inside.bottom; + m_insets.left = inside.left - outside.left; + m_insets.right = outside.right - inside.right; + + DBG_PRINT("*** WindowsWindow: UpdateInsets (1.0) window %p, %d/%d - %d/%d %dx%d\n", + (void*)hwnd, + (int)m_insets.left, (int)m_insets.top, (int)m_insets.right, (int)m_insets.bottom, + (int)(m_insets.right-m_insets.left), (int)(m_insets.top-m_insets.bottom)); + + (*env)->CallVoidMethod(env, window, insetsChangedID, + m_insets.left, m_insets.top, + m_insets.right, m_insets.bottom); + return &m_insets; +} + +#else + +static RECT* UpdateInsets(JNIEnv *env, jobject window, HWND hwnd) +{ + // being naughty here + static RECT m_insets = { 0, 0, 0, 0 }; + RECT outside; + RECT inside; + + if (IsIconic(hwnd)) { + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = -1; + return FALSE; + } + + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + + GetClientRect(hwnd, &inside); + GetWindowRect(hwnd, &outside); + + if (outside.right - outside.left > 0 && outside.bottom - outside.top > 0) { + MapWindowPoints(hwnd, 0, (LPPOINT)&inside, 2); + m_insets.top = inside.top - outside.top; + m_insets.bottom = outside.bottom - inside.bottom; + m_insets.left = inside.left - outside.left; + m_insets.right = outside.right - inside.right; + } else { + m_insets.top = -1; + } + if (m_insets.left < 0 || m_insets.top < 0 || + m_insets.right < 0 || m_insets.bottom < 0) + { + LONG style = GetWindowLong(hwnd, GWL_STYLE); + // TODO: TDV: better undecorated checking needed + + BOOL bIsUndecorated = (style & (WS_CHILD|WS_POPUP|WS_SYSMENU)) != 0; + if (!bIsUndecorated) { + /* Get outer frame sizes. */ + if (style & WS_THICKFRAME) { + m_insets.left = m_insets.right = + GetSystemMetrics(SM_CXSIZEFRAME); + m_insets.top = m_insets.bottom = + GetSystemMetrics(SM_CYSIZEFRAME); + } else { + m_insets.left = m_insets.right = + GetSystemMetrics(SM_CXDLGFRAME); + m_insets.top = m_insets.bottom = + GetSystemMetrics(SM_CYDLGFRAME); + } + + /* Add in title. */ + m_insets.top += GetSystemMetrics(SM_CYCAPTION); + } else { + /* undo the -1 set above */ + m_insets.left = m_insets.top = m_insets.right = m_insets.bottom = 0; + } + } + + DBG_PRINT("*** WindowsWindow: UpdateInsets window %p, %d/%d %dx%d\n", + (void*)hwnd, (int)m_insets.left, (int)m_insets.top, (int)(m_insets.right-m_insets.left), (int)(m_insets.top-m_insets.bottom)); + + (*env)->CallVoidMethod(env, window, insetsChangedID, + m_insets.left, m_insets.top, + m_insets.right, m_insets.bottom); + return &m_insets; +} + +#endif + +static void WmSize(JNIEnv *env, jobject window, HWND wnd, UINT type) +{ + RECT rc; + int w, h; + BOOL isVisible = IsWindowVisible(wnd); + + // make sure insets are up to date + (void)UpdateInsets(env, window, wnd); + + if (type == SIZE_MINIMIZED) { + // TODO: deal with minimized window sizing + return; + } + + GetClientRect(wnd, &rc); + + // we report back the dimensions of the client area + w = rc.right - rc.left; + h = rc.bottom - rc.top; + + DBG_PRINT("*** WindowsWindow: WmSize window %p, %dx%d, visible %d\n", (void*)wnd, w, h, isVisible); + + if(isVisible) { + (*env)->CallVoidMethod(env, window, sizeChangedID, w, h, JNI_FALSE); + } +} + +static LRESULT CALLBACK wndProc(HWND wnd, UINT message, + WPARAM wParam, LPARAM lParam) +{ + LRESULT res = 0; + int useDefWindowProc = 0; + JNIEnv *env = NULL; + jobject window = NULL; + BOOL isKeyDown = FALSE; + WindowUserData * wud; + +#ifdef DEBUG_KEYS + if ( WM_KEYDOWN == message ) { + STD_PRINT("*** WindowsWindow: wndProc window %p, 0x%X %d/%d\n", wnd, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); + } +#endif + +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + wud = (WindowUserData *) GetWindowLong(wnd, GWL_USERDATA); +#else + wud = (WindowUserData *) GetWindowLongPtr(wnd, GWLP_USERDATA); +#endif + if(NULL==wud) { + return DefWindowProc(wnd, message, wParam, lParam); + } + env = wud->jenv; + window = wud->jinstance; + + // DBG_PRINT("*** WindowsWindow: thread 0x%X - window %p -> %p, 0x%X %d/%d\n", (int)GetCurrentThreadId(), wnd, window, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); + + if (NULL==window || NULL==env) { + return DefWindowProc(wnd, message, wParam, lParam); + } + + switch (message) { + + // + // The signal pipeline for destruction is: + // Java::DestroyWindow(wnd) _or_ window-close-button -> + // WM_CLOSE -> Java::windowDestroyNotify -> W_DESTROY -> Java::windowDestroyed -> + // Java::CleanupWindowResources() + case WM_CLOSE: + (*env)->CallVoidMethod(env, window, windowDestroyNotifyID); + break; + + case WM_DESTROY: + { +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + SetWindowLong(wnd, GWL_USERDATA, (intptr_t) NULL); +#else + SetWindowLongPtr(wnd, GWLP_USERDATA, (intptr_t) NULL); +#endif + free(wud); wud=NULL; + (*env)->CallVoidMethod(env, window, windowDestroyedID); + (*env)->DeleteGlobalRef(env, window); + } + break; + + case WM_SYSCHAR: + useDefWindowProc = WmChar(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_CHAR: + useDefWindowProc = WmChar(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), TRUE); + break; + + case WM_KEYDOWN: +#ifdef DEBUG_KEYS + STD_PRINT("*** WindowsWindow: windProc sending window %p -> %p, 0x%X %d/%d\n", wnd, window, message, (int)LOWORD(lParam), (int)HIWORD(lParam)); +#endif + useDefWindowProc = WmKeyDown(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_KEYUP: + useDefWindowProc = WmKeyUp(env, window, wParam, + LOWORD(lParam), HIWORD(lParam), FALSE); + break; + + case WM_SIZE: + WmSize(env, window, wnd, (UINT)wParam); + break; + + case WM_SETTINGCHANGE: + if (wParam == SPI_SETNONCLIENTMETRICS) { + // make sure insets are updated, we don't need to resize the window + // because the size of the client area doesn't change + (void)UpdateInsets(env, window, wnd); + } else { + useDefWindowProc = 1; + } + break; + + + case WM_LBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: LBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 1, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_LBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 1, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: MBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 2, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 2, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_RBUTTONDOWN: + DBG_PRINT("*** WindowsWindow: RBUTTONDOWN\n"); + (*env)->CallVoidMethod(env, window, enqueueRequestFocusID, JNI_FALSE); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 3, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_RBUTTONUP: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 3, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MOUSEMOVE: + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_MOVED, + GetModifiers(), + (jint) LOWORD(lParam), (jint) HIWORD(lParam), + (jint) 0, (jint) 0); + useDefWindowProc = 1; + break; + + case WM_MOUSEWHEEL: { + // need to convert the coordinates to component-relative + int x = GET_X_LPARAM(lParam); + int y = GET_Y_LPARAM(lParam); + POINT eventPt; + eventPt.x = x; + eventPt.y = y; + ScreenToClient(wnd, &eventPt); + (*env)->CallVoidMethod(env, window, sendMouseEventID, + (jint) EVENT_MOUSE_WHEEL_MOVED, + GetModifiers(), + (jint) eventPt.x, (jint) eventPt.y, + (jint) 0, (jint) (GET_WHEEL_DELTA_WPARAM(wParam)/120.0f)); + useDefWindowProc = 1; + break; + } + + case WM_SETFOCUS: + (*env)->CallVoidMethod(env, window, focusChangedID, JNI_TRUE); + useDefWindowProc = 1; + break; + + case WM_KILLFOCUS: + (*env)->CallVoidMethod(env, window, focusChangedID, JNI_FALSE); + useDefWindowProc = 1; + break; + + case WM_SHOWWINDOW: + (*env)->CallVoidMethod(env, window, visibleChangedID, wParam==TRUE?JNI_TRUE:JNI_FALSE); + break; + + case WM_MOVE: + DBG_PRINT("*** WindowsWindow: WM_MOVE window %p, %d/%d\n", wnd, (int)LOWORD(lParam), (int)HIWORD(lParam)); + (*env)->CallVoidMethod(env, window, positionChangedID, + (jint)LOWORD(lParam), (jint)HIWORD(lParam)); + useDefWindowProc = 1; + break; + + case WM_PAINT: { + RECT r; + useDefWindowProc = 0; + if (GetUpdateRect(wnd, &r, TRUE /* erase background */)) { + /* + jint width = r.right-r.left; + jint height = r.bottom-r.top; + if (width > 0 && height > 0) { + (*env)->CallVoidMethod(env, window, windowRepaintID, r.left, r.top, width, height); + } + ValidateRect(wnd, &r); + */ + } + break; + } + case WM_ERASEBKGND: + // ignore erase background + (*env)->CallVoidMethod(env, window, windowRepaintID, 0, 0, -1, -1); + useDefWindowProc = 0; + res = 1; // OpenGL, etc .. erases the background, hence we claim to have just done this + break; + + + // FIXME: generate EVENT_MOUSE_ENTERED, EVENT_MOUSE_EXITED + default: + useDefWindowProc = 1; + } + + if (useDefWindowProc) + return DefWindowProc(wnd, message, wParam, lParam); + return res; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsDisplay + * Method: DispatchMessages + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsDisplay_DispatchMessages0 + (JNIEnv *env, jclass clazz) +{ + int i = 0; + MSG msg; + BOOL gotOne; + + // Periodically take a break + do { + gotOne = PeekMessage(&msg, (HWND) NULL, 0, 0, PM_REMOVE); + // DBG_PRINT("*** WindowsWindow.DispatchMessages0: thread 0x%X - gotOne %d\n", (int)GetCurrentThreadId(), (int)gotOne); + if (gotOne) { + ++i; +#ifdef DEBUG_KEYS + if(WM_KEYDOWN == msg.message) { + STD_PRINT("*** WindowsWindow: DispatchMessages window %p, 0x%X %d/%d\n", msg.hwnd, msg.message, (int)LOWORD(msg.lParam), (int)HIWORD(msg.lParam)); + } +#endif + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } while (gotOne && i < 100); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsDisplay + * Method: LoadLibraryW + * Signature: (Ljava/lang/String;)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_windows_WindowsDisplay_LoadLibraryW0 + (JNIEnv *env, jclass clazz, jstring dllName) +{ + jchar* _dllName = NewtCommon_GetNullTerminatedStringChars(env, dllName); + HMODULE lib = LoadLibraryW(_dllName); + free(_dllName); + return (jlong) (intptr_t) lib; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsDisplay + * Method: RegisterWindowClass + * Signature: (Ljava/lang/String;J)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_windows_WindowsDisplay_RegisterWindowClass0 + (JNIEnv *env, jclass clazz, jstring wndClassName, jlong hInstance) +{ + ATOM res; + WNDCLASS wc; +#ifndef UNICODE + const char* _wndClassName = NULL; +#endif + + /* register class */ + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = (WNDPROC)wndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + /* This cast is legal because the HMODULE for a DLL is the same as + its HINSTANCE -- see MSDN docs for DllMain */ + wc.hInstance = (HINSTANCE) (intptr_t) hInstance; + wc.hIcon = NULL; + wc.hCursor = LoadCursor( NULL, IDC_ARROW); + wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.lpszMenuName = NULL; +#ifdef UNICODE + wc.lpszClassName = NewtCommon_GetNullTerminatedStringChars(env, wndClassName); +#else + _wndClassName = (*env)->GetStringUTFChars(env, wndClassName, NULL); + wc.lpszClassName = strdup(_wndClassName); + (*env)->ReleaseStringUTFChars(env, wndClassName, _wndClassName); +#endif + res = RegisterClass(&wc); + + free((void *)wc.lpszClassName); + + return (jint)res; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsDisplay + * Method: CleanupWindowResources + * Signature: (java/lang/String;J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsDisplay_UnregisterWindowClass0 + (JNIEnv *env, jclass clazz, jint wndClassAtom, jlong hInstance) +{ + UnregisterClass(MAKEINTATOM(wndClassAtom), (HINSTANCE) (intptr_t) hInstance); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsScreen + * Method: getWidthImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_windows_WindowsScreen_getWidthImpl0 + (JNIEnv *env, jobject obj, jint scrn_idx) +{ + return (jint)GetSystemMetrics(SM_CXSCREEN); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsScreen + * Method: getHeightImpl + * Signature: (I)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_windows_WindowsScreen_getHeightImpl0 + (JNIEnv *env, jobject obj, jint scrn_idx) +{ + return (jint)GetSystemMetrics(SM_CYSCREEN); +} + +static int NewtScreen_RotationNativeCCW2NewtCCW(JNIEnv *env, int native) { + int newt; + switch (native) { + case DMDO_DEFAULT: + newt = 0; + break; + case DMDO_90: + newt = 90; + break; + case DMDO_180: + newt = 180; + break; + case DMDO_270: + newt = 270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "invalid native rotation: %d", native); + break; + } + return newt; +} + +static int NewtScreen_RotationNewtCCW2NativeCCW(JNIEnv *env, jint newt) { + int native; + switch (newt) { + case 0: + native = DMDO_DEFAULT; + break; + case 90: + native = DMDO_90; + break; + case 180: + native = DMDO_180; + break; + case 270: + native = DMDO_270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "invalid newt rotation: %d", newt); + } + return native; +} + +/* +static void NewtScreen_scanDisplayDevices() { + DISPLAY_DEVICE device; + int i = 0; + LPCTSTR name; + while(NULL != (name = NewtScreen_getDisplayDeviceName(&device, i))) { + fprintf(stderr, "*** [%d]: <%s> active %d\n", i, name, ( 0 != ( device.StateFlags & DISPLAY_DEVICE_ACTIVE ) ) ); + i++; + } +}*/ + +static LPCTSTR NewtScreen_getDisplayDeviceName(DISPLAY_DEVICE * device, int scrn_idx) { + device->cb = sizeof(DISPLAY_DEVICE); + if( FALSE == EnumDisplayDevices(NULL, scrn_idx, device, 0) ) { + DBG_PRINT("*** WindowsWindow: getDisplayDeviceName.EnumDisplayDevices(scrn_idx %d) -> FALSE\n", scrn_idx); + return NULL; + } + + if( 0 == ( device->StateFlags & DISPLAY_DEVICE_ACTIVE ) ) { + DBG_PRINT("*** WindowsWindow: !DISPLAY_DEVICE_ACTIVE(scrn_idx %d)\n", scrn_idx); + return NULL; + } + + return device->DeviceName; +} + +static HDC NewtScreen_createDisplayDC(LPCTSTR displayDeviceName) { + return CreateDC("DISPLAY", displayDeviceName, NULL, NULL); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsScreen + * Method: getScreenMode0 + * Signature: (II)[I + */ +JNIEXPORT jintArray JNICALL Java_com_jogamp_newt_impl_windows_WindowsScreen_getScreenMode0 + (JNIEnv *env, jobject obj, jint scrn_idx, jint mode_idx) +{ + DISPLAY_DEVICE device; + int prop_num = NUM_SCREEN_MODE_PROPERTIES_ALL; + LPCTSTR deviceName = NewtScreen_getDisplayDeviceName(&device, scrn_idx); + if(NULL == deviceName) { + DBG_PRINT("*** WindowsWindow: getScreenMode.getDisplayDeviceName(scrn_idx %d) -> NULL\n", scrn_idx); + return (*env)->NewIntArray(env, 0); + } + + int devModeID; + int widthmm, heightmm; + if(-1 < mode_idx) { + // only at initialization time, where index >= 0 + HDC hdc = NewtScreen_createDisplayDC(deviceName); + widthmm = GetDeviceCaps(hdc, HORZSIZE); + heightmm = GetDeviceCaps(hdc, VERTSIZE); + DeleteDC(hdc); + devModeID = (int) mode_idx; + prop_num++; // add 1st extra prop, mode_idx + } else { + widthmm = 0; + heightmm = 0; + devModeID = ENUM_CURRENT_SETTINGS; + } + + DEVMODE dm; + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (0 == EnumDisplaySettingsEx(deviceName, devModeID, &dm, ( ENUM_CURRENT_SETTINGS == devModeID ) ? 0 : EDS_ROTATEDMODE)) { + DBG_PRINT("*** WindowsWindow: getScreenMode.EnumDisplaySettingsEx(mode_idx %d/%d) -> NULL\n", mode_idx, devModeID); + return (*env)->NewIntArray(env, 0); + } + + // swap width and height, since Windows reflects rotated dimension, we don't + if (DMDO_90 == dm.dmDisplayOrientation || DMDO_270 == dm.dmDisplayOrientation) { + int tempWidth = dm.dmPelsWidth; + dm.dmPelsWidth = dm.dmPelsHeight; + dm.dmPelsHeight = tempWidth; + } + + jint prop[ prop_num ]; + int propIndex = 0; + + if( -1 < mode_idx ) { + prop[propIndex++] = mode_idx; + } + prop[propIndex++] = 0; // set later for verification of iterator + prop[propIndex++] = dm.dmPelsWidth; + prop[propIndex++] = dm.dmPelsHeight; + prop[propIndex++] = dm.dmBitsPerPel; + prop[propIndex++] = widthmm; + prop[propIndex++] = heightmm; + prop[propIndex++] = dm.dmDisplayFrequency; + prop[propIndex++] = NewtScreen_RotationNativeCCW2NewtCCW(env, dm.dmDisplayOrientation); + prop[propIndex - NUM_SCREEN_MODE_PROPERTIES_ALL] = ( -1 < mode_idx ) ? propIndex-1 : propIndex ; // count == NUM_SCREEN_MODE_PROPERTIES_ALL + + jintArray properties = (*env)->NewIntArray(env, prop_num); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", prop_num); + } + (*env)->SetIntArrayRegion(env, properties, 0, prop_num, prop); + + return properties; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsScreen + * Method: setScreenMode0 + * Signature: (IIIIII)Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_windows_WindowsScreen_setScreenMode0 + (JNIEnv *env, jobject object, jint scrn_idx, jint width, jint height, jint bits, jint rate, jint rot) +{ + DISPLAY_DEVICE device; + LPCTSTR deviceName = NewtScreen_getDisplayDeviceName(&device, scrn_idx); + if(NULL == deviceName) { + DBG_PRINT("*** WindowsWindow: setScreenMode.getDisplayDeviceName(scrn_idx %d) -> NULL\n", scrn_idx); + return JNI_FALSE; + } + + DEVMODE dm; + // initialize the DEVMODE structure + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmPelsWidth = (int)width; + dm.dmPelsHeight = (int)height; + dm.dmBitsPerPel = (int)bits; + dm.dmDisplayFrequency = (int)rate; + dm.dmDisplayOrientation = NewtScreen_RotationNewtCCW2NativeCCW(env, rot); + + // swap width and height, since Windows reflects rotated dimension, we don't + if ( DMDO_90 == dm.dmDisplayOrientation || DMDO_270 == dm.dmDisplayOrientation ) { + int tempWidth = dm.dmPelsWidth; + dm.dmPelsWidth = dm.dmPelsHeight; + dm.dmPelsHeight = tempWidth; + } + + dm.dmFields = DM_DISPLAYORIENTATION | DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY; + + return ( DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettings(&dm, 0) ) ? JNI_TRUE : JNI_FALSE ; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: initIDs0 + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_initIDs0 + (JNIEnv *env, jclass clazz) +{ + NewtCommon_init(env); + + if(NULL==pointClz) { + jclass c = (*env)->FindClass(env, ClazzNamePoint); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT WindowsWindows: can't find %s", ClazzNamePoint); + } + pointClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==pointClz) { + NewtCommon_FatalError(env, "NEWT WindowsWindows: can't use %s", ClazzNamePoint); + } + pointCstr = (*env)->GetMethodID(env, pointClz, ClazzAnyCstrName, ClazzNamePointCstrSignature); + if(NULL==pointCstr) { + NewtCommon_FatalError(env, "NEWT WindowsWindows: can't fetch %s.%s %s", + ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature); + } + } + + insetsChangedID = (*env)->GetMethodID(env, clazz, "insetsChanged", "(IIII)V"); + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(IIII)V"); + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIII)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + enqueueRequestFocusID = (*env)->GetMethodID(env, clazz, "enqueueRequestFocus", "(Z)V"); + focusActionID = (*env)->GetMethodID(env, clazz, "focusAction", "()Z"); + + if (insetsChangedID == NULL || + sizeChangedID == NULL || + positionChangedID == NULL || + focusChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + windowDestroyedID == NULL || + windowRepaintID == NULL || + enqueueMouseEventID == NULL || + sendMouseEventID == NULL || + enqueueKeyEventID == NULL || + sendKeyEventID == NULL || + focusActionID == NULL || + enqueueRequestFocusID == NULL) { + return JNI_FALSE; + } + BuildDynamicKeyMapTable(); + return JNI_TRUE; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: CreateWindow + * Signature: (JILjava/lang/String;JJZIIII)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_CreateWindow0 + (JNIEnv *env, jobject obj, jlong parent, jint wndClassAtom, jstring jWndName, jlong hInstance, jlong visualID, + jboolean bIsUndecorated, + jint jx, jint jy, jint defaultWidth, jint defaultHeight) +{ + HWND parentWindow = (HWND) (intptr_t) parent; + const TCHAR* wndName = NULL; + DWORD windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE | WS_TABSTOP; + int x=(int)jx, y=(int)jy; + int width=(int)defaultWidth, height=(int)defaultHeight; + HWND window = NULL; + +#ifdef UNICODE + wndName = NewtCommon_GetNullTerminatedStringChars(env, jWndName); +#else + wndName = (*env)->GetStringUTFChars(env, jWndName, NULL); +#endif + + if(NULL!=parentWindow) { + if (!IsWindow(parentWindow)) { + DBG_PRINT("*** WindowsWindow: CreateWindow failure: Passed parentWindow %p is invalid\n", parentWindow); + return 0; + } + windowStyle |= WS_CHILD ; + } else if (bIsUndecorated) { + windowStyle |= WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; + } else { + windowStyle |= WS_OVERLAPPEDWINDOW; + x = CW_USEDEFAULT; + y = 0; + } + + (void) visualID; // FIXME: use the visualID .. + + window = CreateWindow(MAKEINTATOM(wndClassAtom), wndName, windowStyle, + x, y, width, height, + parentWindow, NULL, + (HINSTANCE) (intptr_t) hInstance, + NULL); + + DBG_PRINT("*** WindowsWindow: CreateWindow thread 0xX, parent %p, window %p, %d/%d %dx%d\n", + (int)GetCurrentThreadId(), parentWindow, window, x, y, width, height); + + if (NULL == window) { + int lastError = (int) GetLastError(); + DBG_PRINT("*** WindowsWindow: CreateWindow failure: 0x%X %d\n", lastError, lastError); + return 0; + } else { + WindowUserData * wud = (WindowUserData *) malloc(sizeof(WindowUserData)); + wud->jinstance = (*env)->NewGlobalRef(env, obj); + wud->jenv = env; +#if !defined(__MINGW64__) && ( defined(UNDER_CE) || _MSC_VER <= 1200 ) + SetWindowLong(window, GWL_USERDATA, (intptr_t) wud); +#else + SetWindowLongPtr(window, GWLP_USERDATA, (intptr_t) wud); +#endif + + UpdateInsets(env, obj, window); + } + +#ifdef UNICODE + free((void*) wndName); +#else + (*env)->ReleaseStringUTFChars(env, jWndName, wndName); +#endif + + return (jlong) (intptr_t) window; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: DestroyWindow + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_DestroyWindow0 + (JNIEnv *env, jobject obj, jlong window) +{ + DBG_PRINT("*** WindowsWindow: DestroyWindow thread 0x%X, window %p\n", (int)GetCurrentThreadId(), window); + DestroyWindow((HWND) (intptr_t) window); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: GetDC + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_GetDC0 + (JNIEnv *env, jobject obj, jlong window) +{ + return (jlong) (intptr_t) GetDC((HWND) (intptr_t) window); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: ReleaseDC + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_ReleaseDC0 + (JNIEnv *env, jobject obj, jlong window, jlong dc) +{ + ReleaseDC((HWND) (intptr_t) window, (HDC) (intptr_t) dc); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: MonitorFromWindow + * Signature: (J)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_MonitorFromWindow0 + (JNIEnv *env, jobject obj, jlong window) +{ + #if (_WIN32_WINNT >= 0x0500 || _WIN32_WINDOWS >= 0x0410 || WINVER >= 0x0500) && !defined(_WIN32_WCE) + return (jlong) (intptr_t) MonitorFromWindow((HWND) (intptr_t) window, MONITOR_DEFAULTTOPRIMARY); + #else + return 0; + #endif +} + +/*** + * returns bits: 1: size change, 2: pos change + */ +int NewtWindow_setVisiblePosSize(JNIEnv *env, jobject obj, HWND hwnd, jboolean top, jboolean visible, + int x, int y, int width, int height) +{ + UINT flags; + HWND hWndInsertAfter; + BOOL bRes; + int iRes=0; + int wwidth = width; // final window width + int wheight = height; // final window height + + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize %d/%d %dx%d, top %d, visible %d\n", + x, y, width, height, (int)top, (int)visible); + + if(JNI_TRUE == visible) { + flags = SWP_SHOWWINDOW; + } else { + flags = SWP_NOACTIVATE | SWP_NOZORDER; + } + + if(0>x || 0>y ) { + flags |= SWP_NOMOVE; + } else { + iRes |= 2; + } + if(0>=width || 0>=height ) { + flags |= SWP_NOSIZE; + } else { + iRes |= 1; + } + + if(JNI_TRUE == top) { + hWndInsertAfter = HWND_TOPMOST; + if ( 0 == ( flags & SWP_NOSIZE ) ) { + + // since width, height are the size of the client area, we need to add insets + RECT *pInsets = UpdateInsets(env, obj, hwnd); + + wwidth += pInsets->left + pInsets->right; + wheight += pInsets->top + pInsets->bottom; + } + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize top size w/ insets: %d/%d %dx%d\n", x, y, wwidth, wheight); + } else { + hWndInsertAfter = HWND_TOP; + DBG_PRINT("*** WindowsWindow: NewtWindow_setVisiblePosSize client size: %d/%d %dx%d\n", x, y, wwidth, wheight); + } + + SetWindowPos(hwnd, hWndInsertAfter, x, y, wwidth, wheight, flags); + + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + + // we report back the size of client area + (*env)->CallVoidMethod(env, obj, sizeChangedID, (jint) width, (jint) height, JNI_FALSE); + + return iRes; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: setVisible0 + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_setVisible0 + (JNIEnv *env, jobject obj, jlong window, jboolean visible, jboolean top, jint x, jint y, jint width, jint height) +{ + HWND hwnd = (HWND) (intptr_t) window; + DBG_PRINT("*** WindowsWindow: setVisible window %p, visible: %d, top %d, %d/%d %dx%d\n", + hwnd, (int)visible, (int)top, x, y, width, height); + if (visible) { + NewtWindow_setVisiblePosSize(env, obj, hwnd, top, visible, x, y, width, height); + ShowWindow(hwnd, SW_SHOW); + } else { + ShowWindow(hwnd, SW_HIDE); + } +} + +static jboolean NewtWindows_setFullScreen(jboolean fullscreen) +{ + int flags = 0; + DEVMODE dm; + // initialize the DEVMODE structure + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (0 == EnumDisplaySettings(NULL /*current display device*/, ENUM_CURRENT_SETTINGS, &dm)) + { + return JNI_FALSE; + } + + flags = ( JNI_TRUE == fullscreen ) ? CDS_FULLSCREEN : CDS_RESET ; + + return ( DISP_CHANGE_SUCCESSFUL == ChangeDisplaySettings(&dm, flags) ) ? JNI_TRUE : JNI_FALSE; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: reconfigureWindow0 + * Signature: (JIIIIZZII)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_reconfigureWindow0 + (JNIEnv *env, jobject obj, jlong parent, jlong window, jint x, jint y, jint width, jint height, + jboolean visible, jboolean parentChange, jint fullScreenChange, jint decorationChange) +{ + HWND hwndP = (HWND) (intptr_t) parent; + HWND hwnd = (HWND) (intptr_t) window; + DWORD windowStyle = WS_CLIPSIBLINGS | WS_CLIPCHILDREN ; + BOOL styleChange = ( 0 != decorationChange || 0 != fullScreenChange || JNI_TRUE == parentChange ) ? TRUE : FALSE ; + UINT flags = SWP_SHOWWINDOW; + HWND hWndInsertAfter; + + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 parent %p, window %p, %d/%d %dx%d, parentChange %d, fullScreenChange %d, visible %d, decorationChange %d -> styleChange %d\n", + parent, window, x, y, width, height, parentChange, fullScreenChange, visible, decorationChange, styleChange); + + if (!IsWindow(hwnd)) { + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 failure: Passed window %p is invalid\n", (void*)hwnd); + return; + } + + if (NULL!=hwndP && !IsWindow(hwndP)) { + DBG_PRINT("*** WindowsWindow: reconfigureWindow0 failure: Passed parent window %p is invalid\n", (void*)hwndP); + return; + } + + if(JNI_TRUE == visible) { + windowStyle |= WS_VISIBLE ; + } + + if(fullScreenChange < 0) + { + NewtWindows_setFullScreen(JNI_FALSE); + } + + // order of call sequence: (MS documentation) + // TOP: SetParent(.., NULL); Clear WS_CHILD [, Set WS_POPUP] + // CHILD: Set WS_CHILD [, Clear WS_POPUP]; SetParent(.., PARENT) + // + if ( JNI_TRUE == parentChange && NULL == hwndP ) { + SetParent(hwnd, NULL); + } + + if(fullScreenChange > 0) + { + NewtWindows_setFullScreen(JNI_TRUE); + } + + if ( styleChange ) { + if(NULL!=hwndP) { + windowStyle |= WS_CHILD ; + } else if ( decorationChange < 0 || 0 < fullScreenChange ) { + windowStyle |= WS_POPUP | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX; + } else { + windowStyle |= WS_OVERLAPPEDWINDOW; + } + SetWindowLong(hwnd, GWL_STYLE, windowStyle); + SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER ); + } + + if ( JNI_TRUE == parentChange && NULL != hwndP ) { + SetParent(hwnd, hwndP ); + } + + NewtWindow_setVisiblePosSize(env, obj, hwnd, (NULL == hwndP) ? JNI_TRUE : JNI_FALSE /* top */, visible, + x, y, width, height); + + DBG_PRINT("*** WindowsWindow: reconfigureWindow0.X\n"); +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: setTitle + * Signature: (JLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_setTitle0 + (JNIEnv *env, jclass clazz, jlong window, jstring title) +{ + HWND hwnd = (HWND) (intptr_t) window; + if (title != NULL) { + jchar *titleString = NewtCommon_GetNullTerminatedStringChars(env, title); + if (titleString != NULL) { + SetWindowTextW(hwnd, titleString); + free(titleString); + } + } +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindow + * Method: requestFocus + * Signature: (JZ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_requestFocus0 + (JNIEnv *env, jobject obj, jlong window, jboolean force) +{ + DBG_PRINT("*** WindowsWindow: RequestFocus0\n"); + NewtWindows_requestFocus ( env, obj, (HWND) (intptr_t) window, force) ; +} + +/* + * Class: com_jogamp_newt_impl_windows_WindowsWindows + * Method: getRelativeLocation0 + * Signature: (JJII)Ljavax/media/nativewindow/util/Point; + */ +JNIEXPORT jobject JNICALL Java_com_jogamp_newt_impl_windows_WindowsWindow_getRelativeLocation0 + (JNIEnv *env, jobject obj, jlong jsrc_win, jlong jdest_win, jint src_x, jint src_y) +{ + HWND src_win = (HWND) (intptr_t) jsrc_win; + HWND dest_win = (HWND) (intptr_t) jdest_win; + POINT dest = { src_x, src_y } ; + int res; + + res = MapWindowPoints(src_win, dest_win, &dest, 1); + + DBG_PRINT("*** WindowsWindow: getRelativeLocation0: %p %d/%d -> %p %d/%d - ok: %d\n", + (void*)src_win, src_x, src_y, (void*)dest_win, (int)dest.x, (int)dest.y, res); + + return (*env)->NewObject(env, pointClz, pointCstr, (jint)dest.x, (jint)dest.y); +} + diff --git a/src/newt/native/X11Window.c b/src/newt/native/X11Window.c new file mode 100644 index 000000000..becfa7596 --- /dev/null +++ b/src/newt/native/X11Window.c @@ -0,0 +1,1633 @@ +/* + * Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010 JogAmp Community. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistribution 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 Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN + * MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE FOR + * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR + * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR + * ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR + * DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE + * DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, + * ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, EVEN IF + * SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + * + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +// Building on obsolete platform on SPARC right now +#ifdef __sparc + #include <inttypes.h> +#else + #include <stdint.h> +#endif +#include <unistd.h> +#include <errno.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include <X11/keysym.h> +#include <X11/Xatom.h> + +#include <X11/extensions/Xrandr.h> + +#include "com_jogamp_newt_impl_x11_X11Screen.h" +#include "com_jogamp_newt_impl_x11_X11Display.h" +#include "com_jogamp_newt_impl_x11_X11Window.h" + +#include "MouseEvent.h" +#include "KeyEvent.h" +#include "WindowEvent.h" +#include "ScreenMode.h" + +#include "NewtCommon.h" + +// #define VERBOSE_ON 1 + +#ifdef VERBOSE_ON + #define DBG_PRINT(...) fprintf(stderr, __VA_ARGS__); fflush(stderr) + + #define DUMP_VISUAL_INFO(a,b) _dumpVisualInfo((a),(b)) + + static void _dumpVisualInfo(const char * msg, XVisualInfo *pVisualQuery) { + if(pVisualQuery!=NULL) { + fprintf(stderr, "%s: screen %d, visual: %p, visual-id: 0x%X, depth: %d, class %d, cmap sz: %d, bpp: 3x%d, rgb 0x%X 0x%X 0x%X\n", + msg, + pVisualQuery->screen, + pVisualQuery->visual, + (int)pVisualQuery->visualid, + pVisualQuery->depth, + pVisualQuery->class, + pVisualQuery->colormap_size, + pVisualQuery->bits_per_rgb, + (int)pVisualQuery->red_mask, + (int)pVisualQuery->green_mask, + (int)pVisualQuery->blue_mask + ); + } else { + fprintf(stderr, "%s: NULL XVisualInfo\n", msg); + } + } + +#else + + #define DBG_PRINT(...) + + #define DUMP_VISUAL_INFO(a,b) + +#endif + +/** + * Keycode + */ + +#define IS_WITHIN(k,a,b) ((a)<=(k)&&(k)<=(b)) + +static jint X11KeySym2NewtVKey(KeySym keySym) { + if(IS_WITHIN(keySym,XK_F1,XK_F12)) + return (keySym-XK_F1)+J_VK_F1; + + switch(keySym) { + case XK_Alt_L: + case XK_Alt_R: + return J_VK_ALT; + + case XK_Left: + return J_VK_LEFT; + case XK_Right: + return J_VK_RIGHT; + case XK_Up: + return J_VK_UP; + case XK_Down: + return J_VK_DOWN; + case XK_Page_Up: + return J_VK_PAGE_UP; + case XK_Page_Down: + return J_VK_PAGE_DOWN; + case XK_Shift_L: + case XK_Shift_R: + return J_VK_SHIFT; + case XK_Control_L: + case XK_Control_R: + return J_VK_CONTROL; + case XK_Escape: + return J_VK_ESCAPE; + case XK_Delete: + return J_VK_DELETE; + } + return keySym; +} + +static const char * const ClazzNameNewtWindow = "com/jogamp/newt/Window"; + +static const char * const ClazzNamePoint = "javax/media/nativewindow/util/Point"; +static const char * const ClazzAnyCstrName = "<init>"; +static const char * const ClazzNamePointCstrSignature = "(II)V"; + +static jclass newtWindowClz=NULL; + +static jclass pointClz = NULL; +static jmethodID pointCstr = NULL; + +static jmethodID sizeChangedID = NULL; +static jmethodID positionChangedID = NULL; +static jmethodID focusChangedID = NULL; +static jmethodID visibleChangedID = NULL; +static jmethodID windowDestroyNotifyID = NULL; +static jmethodID windowDestroyedID = NULL; +static jmethodID windowRepaintID = NULL; +static jmethodID windowReparentedID = NULL; +static jmethodID enqueueMouseEventID = NULL; +static jmethodID sendMouseEventID = NULL; +static jmethodID enqueueKeyEventID = NULL; +static jmethodID sendKeyEventID = NULL; +static jmethodID focusActionID = NULL; +static jmethodID enqueueRequestFocusID = NULL; + +static jmethodID displayCompletedID = NULL; + + +/** + * Display + */ + +static JNIEnv * x11ErrorHandlerJNIEnv = NULL; +static XErrorHandler origErrorHandler = NULL ; + +static int displayDispatchErrorHandler(Display *dpy, XErrorEvent *e) +{ + fprintf(stderr, "Warning: NEWT X11 Error: DisplayDispatch %p, Code 0x%X, errno %s\n", dpy, e->error_code, strerror(errno)); + + if (e->error_code == BadAtom) { + fprintf(stderr, " BadAtom (%p): Atom probably already removed\n", (void*)e->resourceid); + } else if (e->error_code == BadWindow) { + fprintf(stderr, " BadWindow (%p): Window probably already removed\n", (void*)e->resourceid); + } else { + NewtCommon_throwNewRuntimeException(x11ErrorHandlerJNIEnv, "NEWT X11 Error: Display %p, Code 0x%X, errno %s", + dpy, e->error_code, strerror(errno)); + } + + return 0; +} + +static void displayDispatchErrorHandlerEnable(int onoff, JNIEnv * env) { + if(onoff) { + if(NULL==origErrorHandler) { + x11ErrorHandlerJNIEnv = env; + origErrorHandler = XSetErrorHandler(displayDispatchErrorHandler); + } + } else { + if(NULL!=origErrorHandler) { + XSetErrorHandler(origErrorHandler); + origErrorHandler = NULL; + } + } +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Display + * Method: initIDs + * Signature: (Z)Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_x11_X11Display_initIDs0 + (JNIEnv *env, jclass clazz) +{ + jclass c; + + NewtCommon_init(env); + + displayCompletedID = (*env)->GetMethodID(env, clazz, "displayCompleted", "(JJ)V"); + if (displayCompletedID == NULL) { + return JNI_FALSE; + } + + if(NULL==newtWindowClz) { + c = (*env)->FindClass(env, ClazzNameNewtWindow); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT X11Window: can't find %s", ClazzNameNewtWindow); + } + newtWindowClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==newtWindowClz) { + NewtCommon_FatalError(env, "NEWT X11Window: can't use %s", ClazzNameNewtWindow); + } + } + + if(NULL==pointClz) { + c = (*env)->FindClass(env, ClazzNamePoint); + if(NULL==c) { + NewtCommon_FatalError(env, "NEWT X11Windows: can't find %s", ClazzNamePoint); + } + pointClz = (jclass)(*env)->NewGlobalRef(env, c); + (*env)->DeleteLocalRef(env, c); + if(NULL==pointClz) { + NewtCommon_FatalError(env, "NEWT X11Windows: can't use %s", ClazzNamePoint); + } + pointCstr = (*env)->GetMethodID(env, pointClz, ClazzAnyCstrName, ClazzNamePointCstrSignature); + if(NULL==pointCstr) { + NewtCommon_FatalError(env, "NEWT X11Windows: can't fetch %s.%s %s", + ClazzNamePoint, ClazzAnyCstrName, ClazzNamePointCstrSignature); + } + } + return JNI_TRUE; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Display + * Method: CompleteDisplay + * Signature: (J)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Display_CompleteDisplay0 + (JNIEnv *env, jobject obj, jlong display) +{ + Display * dpy = (Display *)(intptr_t)display; + jlong javaObjectAtom; + jlong windowDeleteAtom; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + javaObjectAtom = (jlong) XInternAtom(dpy, "JOGL_JAVA_OBJECT", False); + if(None==javaObjectAtom) { + NewtCommon_throwNewRuntimeException(env, "could not create Atom JOGL_JAVA_OBJECT, bail out!"); + return; + } + + windowDeleteAtom = (jlong) XInternAtom(dpy, "WM_DELETE_WINDOW", False); + if(None==windowDeleteAtom) { + NewtCommon_throwNewRuntimeException(env, "could not create Atom WM_DELETE_WINDOW, bail out!"); + return; + } + + // XSetCloseDownMode(dpy, RetainTemporary); // Just a try .. + + DBG_PRINT("X11: X11Display_completeDisplay dpy %p\n", dpy); + + (*env)->CallVoidMethod(env, obj, displayCompletedID, javaObjectAtom, windowDeleteAtom); +} + +/** + * Window + */ + +static int putPtrIn32Long(unsigned long * dst, uintptr_t src) { + int i=0; + dst[i++] = (unsigned long) ( ( src >> 0 ) & 0xFFFFFFFF ) ; + if(sizeof(uintptr_t) == 8) { + dst[i++] = (unsigned long) ( ( src >> 32 ) & 0xFFFFFFFF ) ; + } + return i; +} + +static uintptr_t getPtrOut32Long(unsigned long * src) { + uintptr_t res = ( (uintptr_t) ( src[0] & 0xFFFFFFFF ) ) << 0 ; + if(sizeof(uintptr_t) == 8) { + res |= ( (uintptr_t) ( src[1] & 0xFFFFFFFF ) ) << 32 ; + } + return res; +} + +static void setJavaWindowProperty(JNIEnv *env, Display *dpy, Window window, jlong javaObjectAtom, jobject jwindow) { + unsigned long jogl_java_object_data[2]; // X11 is based on 'unsigned long' + int nitems_32 = putPtrIn32Long( jogl_java_object_data, (uintptr_t) jwindow); + + { + jobject test = (jobject) getPtrOut32Long(jogl_java_object_data); + if( ! (jwindow==test) ) { + NewtCommon_FatalError(env, "Internal Error .. Encoded Window ref not the same %p != %p !", jwindow, test); + } + } + + XChangeProperty( dpy, window, (Atom)javaObjectAtom, (Atom)javaObjectAtom, 32, PropModeReplace, + (unsigned char *)&jogl_java_object_data, nitems_32); +} + +static jobject getJavaWindowProperty(JNIEnv *env, Display *dpy, Window window, jlong javaObjectAtom, Bool showWarning) { + Atom actual_type_return; + int actual_format_return; + int nitems_32 = ( sizeof(uintptr_t) == 8 ) ? 2 : 1 ; + unsigned char * jogl_java_object_data_pp = NULL; + jobject jwindow; + + { + unsigned long nitems_return = 0; + unsigned long bytes_after_return = 0; + jobject jwindow = NULL; + int res; + + res = XGetWindowProperty(dpy, window, (Atom)javaObjectAtom, 0, nitems_32, False, + (Atom)javaObjectAtom, &actual_type_return, &actual_format_return, + &nitems_return, &bytes_after_return, &jogl_java_object_data_pp); + + if ( Success != res ) { + if(True==showWarning) { + fprintf(stderr, "Warning: NEWT X11Window: Could not fetch Atom JOGL_JAVA_OBJECT window property (res %d) nitems_return %ld, bytes_after_return %ld, result 0!\n", res, nitems_return, bytes_after_return); + } + return NULL; + } + + if(actual_type_return!=(Atom)javaObjectAtom || nitems_return<nitems_32 || NULL==jogl_java_object_data_pp) { + XFree(jogl_java_object_data_pp); + if(True==showWarning) { + fprintf(stderr, "Warning: NEWT X11Window: Fetched invalid Atom JOGL_JAVA_OBJECT window property (res %d) nitems_return %ld, bytes_after_return %ld, actual_type_return %ld, JOGL_JAVA_OBJECT %ld, result 0!\n", + res, nitems_return, bytes_after_return, (long)actual_type_return, javaObjectAtom); + } + return NULL; + } + } + + jwindow = (jobject) getPtrOut32Long( (unsigned long *) jogl_java_object_data_pp ) ; + XFree(jogl_java_object_data_pp); + +#ifdef VERBOSE_ON + if(JNI_FALSE == (*env)->IsInstanceOf(env, jwindow, newtWindowClz)) { + NewtCommon_throwNewRuntimeException(env, "fetched Atom JOGL_JAVA_OBJECT window is not a NEWT Window: javaWindow 0x%X !", jwindow); + } +#endif + return jwindow; +} + +/** @return zero if fails, non zero if OK */ +static Status NewtWindows_getRootAndParent (Display *dpy, Window w, Window * root_return, Window * parent_return) { + Window *children_return=NULL; + unsigned int nchildren_return=0; + + Status res = XQueryTree(dpy, w, root_return, parent_return, &children_return, &nchildren_return); + if(NULL!=children_return) { + XFree(children_return); + } + return res; +} +static Window NewtWindows_getRoot (Display *dpy, Window w) { + Window root_return; + Window parent_return; + if( 0 != NewtWindows_getRootAndParent(dpy, w, &root_return, &parent_return) ) { + return root_return; + } + return 0; +} +static Window NewtWindows_getParent (Display *dpy, Window w) { + Window root_return; + Window parent_return; + if( 0 != NewtWindows_getRootAndParent(dpy, w, &root_return, &parent_return) ) { + return parent_return; + } + return 0; +} + + +static void NewtWindows_requestFocus (JNIEnv *env, jobject window, Display *dpy, Window w, jboolean force) { + XWindowAttributes xwa; + Window focus_return; + int revert_to_return; + + XGetInputFocus(dpy, &focus_return, &revert_to_return); + if( JNI_TRUE==force || focus_return!=w) { + if( JNI_TRUE==force || JNI_FALSE == (*env)->CallBooleanMethod(env, window, focusActionID) ) { + XRaiseWindow(dpy, w); + // Avoid 'BadMatch' errors from XSetInputFocus, ie if window is not viewable + XGetWindowAttributes(dpy, w, &xwa); + if(xwa.map_state == IsViewable) { + XSetInputFocus(dpy, w, RevertToParent, CurrentTime); + } + } + } + XSync(dpy, False); +} + +#define MWM_HINTS_DECORATIONS (1L << 1) +#define PROP_MWM_HINTS_ELEMENTS 5 + +static void NewtWindows_setDecorations (Display *dpy, Window w, Bool decorated) { + unsigned long mwmhints[PROP_MWM_HINTS_ELEMENTS] = { 0, 0, 0, 0, 0 }; // flags, functions, decorations, input_mode, status + Atom _MOTIF_WM_HINTS_DECORATIONS = XInternAtom( dpy, "_MOTIF_WM_HINTS", False ); + Atom _NET_WM_WINDOW_TYPE = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE", False ); + Atom types[3]={0}; + int ntypes=0; + if(True==decorated) { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + } else { + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_POPUP_MENU", False ); + types[ntypes++] = XInternAtom( dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False ); + } + + mwmhints[0] = MWM_HINTS_DECORATIONS; + mwmhints[2] = decorated ; + + XChangeProperty( dpy, w, _MOTIF_WM_HINTS_DECORATIONS, _MOTIF_WM_HINTS_DECORATIONS, 32, PropModeReplace, (unsigned char *)&mwmhints, PROP_MWM_HINTS_ELEMENTS); + XChangeProperty( dpy, w, _NET_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); +} + +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 + +static void NewtWindows_setFullscreen (Display *dpy, Window root, Window w, Bool fullscreen) { + Atom _NET_WM_STATE = XInternAtom( dpy, "_NET_WM_STATE", False ); + Atom _NET_WM_STATE_ABOVE = XInternAtom( dpy, "_NET_WM_STATE_ABOVE", False ); + Atom _NET_WM_STATE_FULLSCREEN = XInternAtom( dpy, "_NET_WM_STATE_FULLSCREEN", False ); + + Atom types[2]={0}; + int ntypes=0; + + types[ntypes++] = _NET_WM_STATE_FULLSCREEN; + types[ntypes++] = _NET_WM_STATE_ABOVE; + + XEvent xev; + memset ( &xev, 0, sizeof(xev) ); + + xev.type = ClientMessage; + xev.xclient.window = w; + xev.xclient.message_type = _NET_WM_STATE; + xev.xclient.format = 32; + + if(True==fullscreen) { + xev.xclient.data.l[0] = _NET_WM_STATE_ADD; + xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; + xev.xclient.data.l[3] = 1; //source indication for normal applications + } else { + xev.xclient.data.l[0] = _NET_WM_STATE_REMOVE; + xev.xclient.data.l[1] = _NET_WM_STATE_FULLSCREEN; + xev.xclient.data.l[2] = _NET_WM_STATE_ABOVE; + xev.xclient.data.l[3] = 1; //source indication for normal applications + } + + XChangeProperty( dpy, w, _NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (unsigned char *)&types, ntypes); + XSync(dpy, False); + XSendEvent (dpy, root, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev ); +} + +#define USE_SENDIO_DIRECT 1 + +/* + * Class: com_jogamp_newt_impl_x11_X11Display + * Method: DispatchMessages + * Signature: (JIJJ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Display_DispatchMessages0 + (JNIEnv *env, jobject obj, jlong display, jlong javaObjectAtom, jlong wmDeleteAtom) +{ + Display * dpy = (Display *) (intptr_t) display; + int num_events = 100; + + if ( NULL == dpy ) { + return; + } + + // Periodically take a break + while( num_events > 0 ) { + jobject jwindow = NULL; + XEvent evt; + KeySym keySym; + char keyChar; + char text[255]; + + // num_events = XPending(dpy); // I/O Flush .. + // num_events = XEventsQueued(dpy, QueuedAfterFlush); // I/O Flush only of no already queued events are available + // num_events = XEventsQueued(dpy, QueuedAlready); // no I/O Flush at all, doesn't work on some cards (eg ATI) + if ( 0 >= XEventsQueued(dpy, QueuedAfterFlush) ) { + // DBG_PRINT( "X11: DispatchMessages 0x%X - Leave 1\n", dpy); + return; + } + + XNextEvent(dpy, &evt); + num_events--; + + if( 0==evt.xany.window ) { + NewtCommon_throwNewRuntimeException(env, "event window NULL, bail out!"); + return ; + } + + if(dpy!=evt.xany.display) { + NewtCommon_throwNewRuntimeException(env, "wrong display, bail out!"); + return ; + } + + // DBG_PRINT( "X11: DispatchMessages dpy %p, win %p, Event %d\n", (void*)dpy, (void*)evt.xany.window, evt.type); + + displayDispatchErrorHandlerEnable(1, env); + + jwindow = getJavaWindowProperty(env, dpy, evt.xany.window, javaObjectAtom, + #ifdef VERBOSE_ON + True + #else + False + #endif + ); + + displayDispatchErrorHandlerEnable(0, env); + + if(NULL==jwindow) { + fprintf(stderr, "Warning: NEWT X11 DisplayDispatch %p, Couldn't handle event %d for X11 window %p\n", + (void*)dpy, evt.type, (void*)evt.xany.window); + continue; + } + + switch(evt.type) { + case KeyRelease: + case KeyPress: + if(XLookupString(&evt.xkey,text,255,&keySym,0)==1) { + keyChar=text[0]; + } else { + keyChar=0; + } + break; + default: + break; + } + + switch(evt.type) { + case ButtonPress: + (*env)->CallVoidMethod(env, jwindow, enqueueRequestFocusID, JNI_FALSE); + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, + (jint) EVENT_MOUSE_PRESSED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, + JNI_FALSE, + (jint) EVENT_MOUSE_PRESSED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #endif + break; + case ButtonRelease: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, + (jint) EVENT_MOUSE_RELEASED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, + JNI_FALSE, + (jint) EVENT_MOUSE_RELEASED, + (jint) evt.xbutton.state, + (jint) evt.xbutton.x, (jint) evt.xbutton.y, (jint) evt.xbutton.button, 0 /*rotation*/); + #endif + break; + case MotionNotify: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendMouseEventID, + (jint) EVENT_MOUSE_MOVED, + (jint) evt.xmotion.state, + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0 /*rotation*/); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueMouseEventID, + JNI_FALSE, + (jint) EVENT_MOUSE_MOVED, + (jint) evt.xmotion.state, + (jint) evt.xmotion.x, (jint) evt.xmotion.y, (jint) 0, 0 /*rotation*/); + #endif + break; + case KeyPress: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, + (jint) EVENT_KEY_PRESSED, + (jint) evt.xkey.state, + X11KeySym2NewtVKey(keySym), (jchar) keyChar); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, + JNI_FALSE, + (jint) EVENT_KEY_PRESSED, + (jint) evt.xkey.state, + X11KeySym2NewtVKey(keySym), (jchar) keyChar); + #endif + + break; + case KeyRelease: + #ifdef USE_SENDIO_DIRECT + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, + (jint) EVENT_KEY_RELEASED, + (jint) evt.xkey.state, + X11KeySym2NewtVKey(keySym), (jchar) keyChar); + + (*env)->CallVoidMethod(env, jwindow, sendKeyEventID, + (jint) EVENT_KEY_TYPED, + (jint) evt.xkey.state, + (jint) -1, (jchar) keyChar); + #else + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, + JNI_FALSE, + (jint) EVENT_KEY_RELEASED, + (jint) evt.xkey.state, + X11KeySym2NewtVKey(keySym), (jchar) keyChar); + + (*env)->CallVoidMethod(env, jwindow, enqueueKeyEventID, + JNI_FALSE, + (jint) EVENT_KEY_TYPED, + (jint) evt.xkey.state, + (jint) -1, (jchar) keyChar); + #endif + + break; + case DestroyNotify: + DBG_PRINT( "X11: event . DestroyNotify call %p, parent %p, child-event: %d\n", + (void*)evt.xdestroywindow.window, (void*)evt.xdestroywindow.event, evt.xdestroywindow.window != evt.xdestroywindow.event); + if ( evt.xdestroywindow.window == evt.xdestroywindow.event ) { + // ignore child destroy notification + (*env)->CallVoidMethod(env, jwindow, windowDestroyedID); + } + break; + case CreateNotify: + DBG_PRINT( "X11: event . CreateNotify call %p, parent %p, child-event: 1\n", + (void*)evt.xcreatewindow.window, (void*) evt.xcreatewindow.parent); + break; + case ConfigureNotify: + DBG_PRINT( "X11: event . ConfigureNotify call %p (parent %p, above %p) %d/%d %dx%d %d, child-event: %d\n", + (void*)evt.xconfigure.window, (void*)evt.xconfigure.event, (void*)evt.xconfigure.above, + evt.xconfigure.x, evt.xconfigure.y, evt.xconfigure.width, evt.xconfigure.height, + evt.xconfigure.override_redirect, evt.xconfigure.window != evt.xconfigure.event); + if ( evt.xconfigure.window == evt.xconfigure.event ) { + // ignore child window change notification + (*env)->CallVoidMethod(env, jwindow, sizeChangedID, + (jint) evt.xconfigure.width, (jint) evt.xconfigure.height, JNI_FALSE); + (*env)->CallVoidMethod(env, jwindow, positionChangedID, + (jint) evt.xconfigure.x, (jint) evt.xconfigure.y); + } + break; + case ClientMessage: + if (evt.xclient.send_event==True && evt.xclient.data.l[0]==(Atom)wmDeleteAtom) { + DBG_PRINT( "X11: event . ClientMessage call %p type 0x%X !!!\n", + (void*)evt.xclient.window, (unsigned int)evt.xclient.message_type); + (*env)->CallVoidMethod(env, jwindow, windowDestroyNotifyID); + // Called by Window.java: CloseWindow(); + } + break; + + case FocusIn: + DBG_PRINT( "X11: event . FocusIn call %p\n", (void*)evt.xvisibility.window); + (*env)->CallVoidMethod(env, jwindow, focusChangedID, JNI_TRUE); + break; + + case FocusOut: + DBG_PRINT( "X11: event . FocusOut call %p\n", (void*)evt.xvisibility.window); + (*env)->CallVoidMethod(env, jwindow, focusChangedID, JNI_FALSE); + break; + + case Expose: + DBG_PRINT( "X11: event . Expose call %p %d/%d %dx%d count %d\n", (void*)evt.xexpose.window, + evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height, evt.xexpose.count); + + if (evt.xexpose.count == 0 && evt.xexpose.width > 0 && evt.xexpose.height > 0) { + (*env)->CallVoidMethod(env, jwindow, windowRepaintID, + evt.xexpose.x, evt.xexpose.y, evt.xexpose.width, evt.xexpose.height); + } + break; + + case MapNotify: + DBG_PRINT( "X11: event . MapNotify call Event %p, Window %p, override_redirect %d, child-event: %d\n", + (void*)evt.xmap.event, (void*)evt.xmap.window, (int)evt.xmap.override_redirect, + evt.xmap.event!=evt.xmap.window); + if( evt.xmap.event == evt.xmap.window ) { + // ignore child window notification + (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_TRUE); + } + break; + + case UnmapNotify: + DBG_PRINT( "X11: event . UnmapNotify call Event %p, Window %p, from_configure %d, child-event: %d\n", + (void*)evt.xunmap.event, (void*)evt.xunmap.window, (int)evt.xunmap.from_configure, + evt.xunmap.event!=evt.xunmap.window); + if( evt.xunmap.event == evt.xunmap.window ) { + // ignore child window notification + (*env)->CallVoidMethod(env, jwindow, visibleChangedID, JNI_FALSE); + } + break; + + case ReparentNotify: + { + jlong parentResult; // 0 if root, otherwise proper value + Window winRoot, winTopParent; + #ifdef VERBOSE_ON + Window oldParentRoot, oldParentTopParent; + Window parentRoot, parentTopParent; + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.event, &oldParentRoot, &oldParentTopParent) ) { + oldParentRoot=0; oldParentTopParent = 0; + } + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.parent, &parentRoot, &parentTopParent) ) { + parentRoot=0; parentTopParent = 0; + } + #endif + if( 0 == NewtWindows_getRootAndParent(dpy, evt.xreparent.window, &winRoot, &winTopParent) ) { + winRoot=0; winTopParent = 0; + } + if(evt.xreparent.parent == winRoot) { + parentResult = 0; // our java indicator for root window + } else { + parentResult = (jlong) (intptr_t) evt.xreparent.parent; + } + #ifdef VERBOSE_ON + DBG_PRINT( "X11: event . ReparentNotify: call OldParent %p (root %p, top %p), NewParent %p (root %p, top %p), Window %p (root %p, top %p)\n", + (void*)evt.xreparent.event, (void*)oldParentRoot, (void*)oldParentTopParent, + (void*)evt.xreparent.parent, (void*)parentRoot, (void*)parentTopParent, + (void*)evt.xreparent.window, (void*)winRoot, (void*)winTopParent); + #endif + + (*env)->CallVoidMethod(env, jwindow, windowReparentedID, parentResult); + } + break; + + // unhandled events .. yet .. + + default: + DBG_PRINT("X11: event . unhandled %d 0x%X call %p\n", (int)evt.type, (unsigned int)evt.type, (void*)evt.xunmap.window); + } + } +} + + +/** + * Screen + */ + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: GetScreen + * Signature: (JI)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_GetScreen0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_index) +{ + Display * dpy = (Display *)(intptr_t)display; + Screen * scrn= NULL; + + DBG_PRINT("X11: X11Screen_GetScreen0 dpy %p START\n", dpy); + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + scrn = ScreenOfDisplay(dpy,screen_index); + if(scrn==NULL) { + scrn=DefaultScreenOfDisplay(dpy); + } + if(scrn==NULL) { + fprintf(stderr, "couldn't get screen ..\n"); + } + DBG_PRINT("X11: X11Screen_GetScreen0 scrn %p DONE\n", scrn); + return (jlong) (intptr_t) scrn; +} + +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getWidth0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display * dpy = (Display *) (intptr_t) display; + return (jint) XDisplayWidth( dpy, scrn_idx); +} + +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getHeight0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display * dpy = (Display *) (intptr_t) display; + return (jint) XDisplayHeight( dpy, scrn_idx); +} + + +static Bool NewtScreen_getRANDRVersion(Display *dpy, int *major, int *minor) { + if( 0 == XRRQueryVersion(dpy, major, minor) ) { + return False; + } + return True; +} + +static Bool NewtScreen_hasRANDR(Display *dpy) { + int major, minor; + return NewtScreen_getRANDRVersion(dpy, &major, &minor); +} + +static int NewtScreen_XRotation2Degree(JNIEnv *env, int xrotation) { + int rot; + if(xrotation == RR_Rotate_0) { + rot = 0; + } + else if(xrotation == RR_Rotate_90) { + rot = 90; + } + else if(xrotation == RR_Rotate_180) { + rot = 180; + } + else if(xrotation == RR_Rotate_270) { + rot = 270; + } else { + NewtCommon_throwNewRuntimeException(env, "invalid native rotation: %d", xrotation); + } + return rot; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getAvailableScreenModeRotations0 + * Signature: (JI)I + */ +JNIEXPORT jintArray JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getAvailableScreenModeRotations0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + int num_rotations = 0; + Rotation cur_rotation, rotations_supported; + int rotations[4]; + int major, minor; + + if(False == NewtScreen_getRANDRVersion(dpy, &major, &minor)) { + fprintf(stderr, "RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + rotations_supported = XRRRotations (dpy, (int)scrn_idx, &cur_rotation); + + if(0 != (rotations_supported & RR_Rotate_0)) { + rotations[num_rotations++] = 0; + } + if(0 != (rotations_supported & RR_Rotate_90)) { + rotations[num_rotations++] = 90; + } + if(0 != (rotations_supported & RR_Rotate_180)) { + rotations[num_rotations++] = 180; + } + if(0 != (rotations_supported & RR_Rotate_270)) { + rotations[num_rotations++] = 270; + } + + jintArray properties = NULL; + + if(num_rotations>0) { + properties = (*env)->NewIntArray(env, num_rotations); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", num_rotations); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, num_rotations, rotations); + } + + return properties; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getNumScreenModeResolution0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getNumScreenModeResolutions0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getNumScreenModeResolutions0: RANDR not available\n"); + return 0; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + return num_sizes; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getScreenModeResolutions0 + * Signature: (JII)[I + */ +JNIEXPORT jintArray JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getScreenModeResolution0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx, jint resMode_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getScreenModeResolution0: RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + // Fill the properties in temp jint array + int propIndex = 0; + jint prop[4]; + + prop[propIndex++] = xrrs[(int)resMode_idx].width; + prop[propIndex++] = xrrs[(int)resMode_idx].height; + prop[propIndex++] = xrrs[(int)resMode_idx].mwidth; + prop[propIndex++] = xrrs[(int)resMode_idx].mheight; + + jintArray properties = (*env)->NewIntArray(env, 4); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", 4); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, 4, prop); + + return properties; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getScreenModeRates0 + * Signature: (JII)[I + */ +JNIEXPORT jintArray JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getScreenModeRates0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx, jint resMode_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getScreenModeRates0: RANDR not available\n"); + return (*env)->NewIntArray(env, 0); + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)scrn_idx, &num_sizes); //get possible screen resolutions + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + int num_rates; + short *rates = XRRRates(dpy, (int)scrn_idx, (int)resMode_idx, &num_rates); + + jint prop[num_rates]; + int i; + for(i=0; i<num_rates; i++) { + prop[i] = (int) rates[i]; + /** fprintf(stderr, "rate[%d, %d, %d/%d]: %d\n", (int)scrn_idx, resMode_idx, i, num_rates, prop[i]); */ + } + + jintArray properties = (*env)->NewIntArray(env, num_rates); + if (properties == NULL) { + NewtCommon_throwNewRuntimeException(env, "Could not allocate int array of size %d", num_rates); + } + + // move from the temp structure to the java structure + (*env)->SetIntArrayRegion(env, properties, 0, num_rates, prop); + + return properties; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getCurrentScreenRate0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenRate0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenRate0: RANDR not available\n"); + return -1; + } + + // get current resolutions and frequencies + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + short original_rate = XRRConfigCurrentRate(conf); + + //free + XRRFreeScreenConfigInfo(conf); + + return (jint) original_rate; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getCurrentScreenRotation0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenRotation0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenRotation0: RANDR not available\n"); + return -1; + } + + //get current resolutions and frequencies + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + + Rotation rotation; + XRRConfigCurrentConfiguration(conf, &rotation); + + //free + XRRFreeScreenConfigInfo(conf); + + return NewtScreen_XRotation2Degree(env, rotation); +} + + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: getCurrentScreenResolutionIndex0 + * Signature: (JI)I + */ +JNIEXPORT jint JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenResolutionIndex0 + (JNIEnv *env, jclass clazz, jlong display, jint scrn_idx) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)scrn_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_getCurrentScreenResolutionIndex0: RANDR not available\n"); + return -1; + } + + // get current resolutions and frequency configuration + XRRScreenConfiguration *conf = XRRGetScreenInfo(dpy, root); + short original_rate = XRRConfigCurrentRate(conf); + + Rotation original_rotation; + SizeID original_size_id = XRRConfigCurrentConfiguration(conf, &original_rotation); + + //free + XRRFreeScreenConfigInfo(conf); + + return (jint)original_size_id; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: setCurrentScreenModeStart0 + * Signature: (JIIII)Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_setCurrentScreenModeStart0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_idx, jint resMode_idx, jint freq, jint rotation) +{ + Display *dpy = (Display *) (intptr_t) display; + Window root = RootWindow(dpy, (int)screen_idx); + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_setCurrentScreenModeStart0: RANDR not available\n"); + return JNI_FALSE; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)screen_idx, &num_sizes); //get possible screen resolutions + XRRScreenConfiguration *conf; + int rot; + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + conf = XRRGetScreenInfo(dpy, root); + + switch(rotation) { + case 0: + rot = RR_Rotate_0; + break; + case 90: + rot = RR_Rotate_90; + break; + case 180: + rot = RR_Rotate_180; + break; + case 270: + rot = RR_Rotate_270; + break; + default: + NewtCommon_throwNewRuntimeException(env, "Invalid rotation: %d", rotation); + } + + DBG_PRINT("X11Screen.setCurrentScreenMode0: CHANGED TO %d: %d x %d PIXELS, %d Hz, %d degree\n", + resMode_idx, xrrs[resMode_idx].width, xrrs[resMode_idx].height, (int)freq, rotation); + + XRRSelectInput (dpy, root, RRScreenChangeNotifyMask); + + XSync(dpy, False); + XRRSetScreenConfigAndRate(dpy, conf, root, (int)resMode_idx, rot, (short)freq, CurrentTime); + XSync(dpy, False); + + //free + XRRFreeScreenConfigInfo(conf); + XSync(dpy, False); + + return JNI_TRUE; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Screen + * Method: setCurrentScreenModePollEnd0 + * Signature: (J)Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_x11_X11Screen_setCurrentScreenModePollEnd0 + (JNIEnv *env, jclass clazz, jlong display, jint screen_idx, jint resMode_idx, jint freq, jint rotation) +{ + Display *dpy = (Display *) (intptr_t) display; + int randr_event_base, randr_error_base; + XEvent evt; + XRRScreenChangeNotifyEvent * scn_event = (XRRScreenChangeNotifyEvent *) &evt; + + if(False == NewtScreen_hasRANDR(dpy)) { + DBG_PRINT("Java_com_jogamp_newt_impl_x11_X11Screen_setCurrentScreenModePollEnd0: RANDR not available\n"); + return JNI_FALSE; + } + + int num_sizes; + XRRScreenSize *xrrs = XRRSizes(dpy, (int)screen_idx, &num_sizes); //get possible screen resolutions + XRRScreenConfiguration *conf; + + if( 0 > resMode_idx || resMode_idx >= num_sizes ) { + NewtCommon_throwNewRuntimeException(env, "Invalid resolution index: ! 0 < %d < %d", resMode_idx, num_sizes); + } + + XRRQueryExtension(dpy, &randr_event_base, &randr_error_base); + + int done = 0; + int rot; + do { + if ( 0 >= XEventsQueued(dpy, QueuedAfterFlush) ) { + return; + } + XNextEvent(dpy, &evt); + + switch (evt.type - randr_event_base) { + case RRScreenChangeNotify: + rot = NewtScreen_XRotation2Degree(env, (int)scn_event->rotation); + DBG_PRINT( "XRANDR: event . RRScreenChangeNotify call %p (root %p) resIdx %d rot %d %dx%d\n", + (void*)scn_event->window, (void*)scn_event->root, + (int)scn_event->size_index, rot, + scn_event->width, scn_event->height); + // done = scn_event->size_index == resMode_idx; // not reliable .. + done = rot == rotation && + scn_event->width == xrrs[resMode_idx].width && + scn_event->height == xrrs[resMode_idx].height; + break; + default: + DBG_PRINT("RANDR: event . unhandled %d 0x%X call %p\n", (int)evt.type, (int)evt.type, (void*)evt.xany.window); + } + XRRUpdateConfiguration(&evt); + } while(!done); + + XSync(dpy, False); + +} + +/** + * Window + */ + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: initIDs + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_com_jogamp_newt_impl_x11_X11Window_initIDs0 + (JNIEnv *env, jclass clazz) +{ + sizeChangedID = (*env)->GetMethodID(env, clazz, "sizeChanged", "(IIZ)V"); + positionChangedID = (*env)->GetMethodID(env, clazz, "positionChanged", "(II)V"); + focusChangedID = (*env)->GetMethodID(env, clazz, "focusChanged", "(Z)V"); + visibleChangedID = (*env)->GetMethodID(env, clazz, "visibleChanged", "(Z)V"); + windowDestroyNotifyID = (*env)->GetMethodID(env, clazz, "windowDestroyNotify", "()V"); + windowDestroyedID = (*env)->GetMethodID(env, clazz, "windowDestroyed", "()V"); + windowRepaintID = (*env)->GetMethodID(env, clazz, "windowRepaint", "(IIII)V"); + windowReparentedID = (*env)->GetMethodID(env, clazz, "windowReparented", "(J)V"); + enqueueMouseEventID = (*env)->GetMethodID(env, clazz, "enqueueMouseEvent", "(ZIIIIII)V"); + sendMouseEventID = (*env)->GetMethodID(env, clazz, "sendMouseEvent", "(IIIIII)V"); + enqueueKeyEventID = (*env)->GetMethodID(env, clazz, "enqueueKeyEvent", "(ZIIIC)V"); + sendKeyEventID = (*env)->GetMethodID(env, clazz, "sendKeyEvent", "(IIIC)V"); + enqueueRequestFocusID = (*env)->GetMethodID(env, clazz, "enqueueRequestFocus", "(Z)V"); + focusActionID = (*env)->GetMethodID(env, clazz, "focusAction", "()Z"); + + if (sizeChangedID == NULL || + positionChangedID == NULL || + focusChangedID == NULL || + visibleChangedID == NULL || + windowDestroyNotifyID == NULL || + windowDestroyedID == NULL || + windowRepaintID == NULL || + windowReparentedID == NULL || + enqueueMouseEventID == NULL || + sendMouseEventID == NULL || + enqueueKeyEventID == NULL || + sendKeyEventID == NULL || + focusActionID == NULL || + enqueueRequestFocusID == NULL) { + return JNI_FALSE; + } + return JNI_TRUE; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: CreateWindow + * Signature: (JJIJIIII)J + */ +JNIEXPORT jlong JNICALL Java_com_jogamp_newt_impl_x11_X11Window_CreateWindow0 + (JNIEnv *env, jobject obj, jlong parent, jlong display, jint screen_index, + jlong visualID, + jlong javaObjectAtom, jlong windowDeleteAtom, + jint x, jint y, jint width, jint height, + jboolean undecorated) +{ + Display * dpy = (Display *)(intptr_t)display; + int scrn_idx = (int)screen_index; + Window windowParent = (Window) parent; + Window window = 0; + + XVisualInfo visualTemplate; + XVisualInfo *pVisualQuery = NULL; + Visual *visual = NULL; + int depth; + + XSetWindowAttributes xswa; + unsigned long attrMask; + int n; + + Screen* scrn; + Atom wm_delete_atom; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + if(visualID<0) { + NewtCommon_throwNewRuntimeException(env, "invalid VisualID .."); + return 0; + } + + XSync(dpy, False); + + scrn = ScreenOfDisplay(dpy, scrn_idx); + if(0==windowParent) { + windowParent = XRootWindowOfScreen(scrn); + } + if( XRootWindowOfScreen(scrn) != XRootWindow(dpy, scrn_idx) ) { + NewtCommon_FatalError(env, "XRoot Malfunction: %p != %p"+XRootWindowOfScreen(scrn), XRootWindow(dpy, scrn_idx)); + } + DBG_PRINT( "X11: CreateWindow dpy %p, parent %p, %x/%d %dx%d, undeco %d\n", + (void*)dpy, (void*)windowParent, x, y, width, height, undecorated); + + // try given VisualID on screen + memset(&visualTemplate, 0, sizeof(XVisualInfo)); + visualTemplate.screen = scrn_idx; + visualTemplate.visualid = (VisualID)visualID; + pVisualQuery = XGetVisualInfo(dpy, VisualIDMask|VisualScreenMask, &visualTemplate,&n); + DUMP_VISUAL_INFO("Given VisualID,ScreenIdx", pVisualQuery); + if(pVisualQuery!=NULL) { + visual = pVisualQuery->visual; + depth = pVisualQuery->depth; + visualID = (jlong)pVisualQuery->visualid; + XFree(pVisualQuery); + pVisualQuery=NULL; + } + DBG_PRINT( "X11: [CreateWindow] trying given (dpy %p, screen %d, visualID: %d, parent %p) found: %p\n", + dpy, scrn_idx, (int)visualID, (void*)windowParent, visual); + + if (visual==NULL) + { + NewtCommon_throwNewRuntimeException(env, "could not query Visual by given VisualID, bail out!"); + return 0; + } + + if(pVisualQuery!=NULL) { + XFree(pVisualQuery); + pVisualQuery=NULL; + } + + attrMask = ( CWBackingStore | CWBackingPlanes | CWBackingPixel | CWBackPixel | + CWBorderPixel | CWColormap | CWOverrideRedirect ) ; + + memset(&xswa, 0, sizeof(xswa)); + // xswa.override_redirect = ( 0 != parent ) ? False : True; + xswa.override_redirect = False; // use the window manager, always + xswa.border_pixel = 0; + xswa.background_pixel = 0; + xswa.backing_store=NotUseful; /* NotUseful, WhenMapped, Always */ + xswa.backing_planes=0; /* planes to be preserved if possible */ + xswa.backing_pixel=0; /* value to use in restoring planes */ + + xswa.colormap = XCreateColormap(dpy, + windowParent, + visual, + AllocNone); + + window = XCreateWindow(dpy, + windowParent, + x, y, + width, height, + 0, // border width + depth, + InputOutput, + visual, + attrMask, + &xswa); + + if(0==window) { + NewtCommon_throwNewRuntimeException(env, "could not create Window, bail out!"); + return 0; + } + + wm_delete_atom = (Atom)windowDeleteAtom; + XSetWMProtocols(dpy, window, &wm_delete_atom, 1); + + setJavaWindowProperty(env, dpy, window, javaObjectAtom, (*env)->NewGlobalRef(env, obj)); + + // XClearWindow(dpy, window); + XSync(dpy, False); + + { + long xevent_mask = 0; + xevent_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask ; + xevent_mask |= KeyPressMask | KeyReleaseMask ; + xevent_mask |= FocusChangeMask | SubstructureNotifyMask | StructureNotifyMask | ExposureMask ; + + XSelectInput(dpy, window, xevent_mask); + } + + NewtWindows_setDecorations(dpy, window, ( JNI_TRUE == undecorated ) ? False : True ); + XSync(dpy, False); + + DBG_PRINT( "X11: [CreateWindow] created window %p on display %p\n", (void*)window, dpy); + return (jlong) window; +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: CloseWindow + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Window_CloseWindow0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jlong javaObjectAtom, jlong wmDeleteAtom) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + jobject jwindow; + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + DBG_PRINT( "X11: CloseWindow START dpy %p, win %p\n", (void*)dpy, (void*)w); + + jwindow = getJavaWindowProperty(env, dpy, w, javaObjectAtom, True); + if(NULL==jwindow) { + NewtCommon_throwNewRuntimeException(env, "could not fetch Java Window object, bail out!"); + return; + } + if ( JNI_FALSE == (*env)->IsSameObject(env, jwindow, obj) ) { + NewtCommon_throwNewRuntimeException(env, "Internal Error .. Window global ref not the same!"); + return; + } + + XSync(dpy, False); + XSelectInput(dpy, w, 0); + XUnmapWindow(dpy, w); + + // Drain all events related to this window .. + Java_com_jogamp_newt_impl_x11_X11Display_DispatchMessages0(env, obj, display, javaObjectAtom, wmDeleteAtom); + + XDestroyWindow(dpy, w); + XSync(dpy, False); + + (*env)->DeleteGlobalRef(env, jwindow); + + DBG_PRINT( "X11: CloseWindow END\n"); + + (*env)->CallVoidMethod(env, obj, windowDestroyedID); +} + +static void NewtWindows_setPosSize(Display *dpy, Window w, jint x, jint y, jint width, jint height) +{ + if(width>0 && height>0 || x>=0 && y>=0) { // resize/position if requested + XWindowChanges xwc; + unsigned int mod_flags = ( (x>=0)?CWX:0 ) | ( (y>=0)?CWY:0 ) | + ( (width>0)?CWWidth:0 ) | ( (height>0)?CWHeight:0 ) ; + DBG_PRINT( "X11: reconfigureWindow0 pos/size mod: 0x%X\n", mod_flags); + memset(&xwc, 0, sizeof(XWindowChanges)); + xwc.x=x; + xwc.y=y; + xwc.width=width; + xwc.height=height; + XConfigureWindow(dpy, w, mod_flags, &xwc); + XSync(dpy, False); + } +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: setVisible0 + * Signature: (JJZIIII)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Window_setVisible0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jboolean visible, jint x, jint y, jint width, jint height) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + DBG_PRINT( "X11: setVisible0 vis %d\n", visible); + + if(dpy==NULL) { + NewtCommon_FatalError(env, "invalid display connection.."); + } + + if(visible==JNI_TRUE) { + XMapRaised(dpy, w); + } else { + XUnmapWindow(dpy, w); + } + XSync(dpy, False); + + NewtWindows_setPosSize(dpy, w, x, y, width, height); +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: reconfigureWindow0 + * Signature: (JIJJIIIIZZII)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Window_reconfigureWindow0 + (JNIEnv *env, jobject obj, jlong jdisplay, jint screen_index, jlong jparent, jlong jwindow, + jint x, jint y, jint width, jint height, jboolean isVisible, jboolean parentChange, jint fullscreenChange, jint decorationChange) +{ + Display * dpy = (Display *) (intptr_t) jdisplay; + Screen * scrn = ScreenOfDisplay(dpy, (int)screen_index); + Window w = (Window)jwindow; + Window root = XRootWindowOfScreen(scrn); + Window parent = (0!=jparent)?(Window)jparent:root; + Window topParentParent; + Window topParentWindow; + Bool moveIntoParent = False; + + displayDispatchErrorHandlerEnable(1, env); + + topParentParent = NewtWindows_getParent (dpy, parent); + topParentWindow = NewtWindows_getParent (dpy, w); + + DBG_PRINT( "X11: reconfigureWindow0 dpy %p, scrn %d/%p, parent %p/%p (top %p), win %p (top %p), %d/%d %dx%d visible %d, parentChange %d, fullscreenChange %d, decorationChange %d\n", + (void*)dpy, screen_index, (void*)scrn, (void*) jparent, (void*)parent, (void*) topParentParent, (void*)w, (void*)topParentWindow, + x, y, width, height, isVisible, parentChange, fullscreenChange, decorationChange); + + if(parentChange && JNI_TRUE == isVisible) { // unmap window if visible, reduce X11 internal signaling (WM unmap) + XUnmapWindow(dpy, w); + XSync(dpy, False); + } + + if(0 > fullscreenChange ) { // FS off + NewtWindows_setFullscreen(dpy, root, w, False ); + XSync(dpy, False); + } + + if(parentChange) { + if(0 != jparent) { // move into parent .. + moveIntoParent = True; + NewtWindows_setDecorations (dpy, w, False); + XSync(dpy, False); + } + XReparentWindow( dpy, w, parent, x, y ); // actual reparent call + XSync(dpy, False); + } + + if(!moveIntoParent && 0!=decorationChange) { + NewtWindows_setDecorations (dpy, w, (0 < decorationChange) ? True : False); + XSync(dpy, False); + } + + NewtWindows_setPosSize(dpy, w, x, y, width, height); + + if(0 < fullscreenChange ) { // FS on + NewtWindows_setFullscreen(dpy, root, w, True ); + XSync(dpy, False); + } + + if(parentChange && JNI_TRUE == isVisible) { // map window + XMapRaised(dpy, w); + XSync(dpy, False); + } + + displayDispatchErrorHandlerEnable(0, env); + + DBG_PRINT( "X11: reconfigureWindow0 X\n"); +} + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: requestFocus0 + * Signature: (JJ)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Window_requestFocus0 + (JNIEnv *env, jobject obj, jlong display, jlong window, jboolean force) +{ + NewtWindows_requestFocus ( env, obj, (Display *) (intptr_t) display, (Window)window, force ) ; +} + +/* + * Class: Java_com_jogamp_newt_impl_x11_X11Window + * Method: setTitle0 + * Signature: (JJLjava/lang/String;)V + */ +JNIEXPORT void JNICALL Java_com_jogamp_newt_impl_x11_X11Window_setTitle0 + (JNIEnv *env, jclass clazz, jlong display, jlong window, jstring title) +{ + Display * dpy = (Display *) (intptr_t) display; + Window w = (Window)window; + +#if 1 + const char* title_str; + if (NULL != title) { + title_str = (*env)->GetStringUTFChars(env, title, NULL); + if(NULL != title_str) { + DBG_PRINT( "X11: setTitle: <%s> SET\n", title_str); + XStoreName(dpy, w, title_str); + (*env)->ReleaseStringUTFChars(env, title, title_str); + } else { + DBG_PRINT( "X11: setTitle: NULL - NOT SET (1)\n"); + } + } else { + DBG_PRINT( "X11: setTitle: NULL TITLE\n"); + } +#else + char *str_list[] = { NULL }; + XTextProperty text_prop; + if (NULL != title) { + str_list[0] = (char *) NewtCommon_GetNullTerminatedStringChars(env, title); + if (str_list[0] != NULL) { + memset(&text_prop, 0, sizeof(XTextProperty)); + if ( Success != XmbTextListToTextProperty(dpy, str_list, 1, XStringStyle, &text_prop) ) { + DBG_PRINT( "X11: setTitle.XmbTextListToTextProperty not completly successfull\n"); + fprintf(stderr, "X11: setTitle.XmbTextListToTextProperty not completly successfull\n"); + } + if(NULL!=text_prop.value) { + DBG_PRINT( "X11: setTitle: <%s> SET\n", str_list[0]); + XSetWMName(dpy, w, &text_prop); + XFree(text_prop.value); + } else { + DBG_PRINT( "X11: setTitle: <%s> NOT SET (1)\n", str_list[0]); + } + free(str_list[0]); + } else { + DBG_PRINT( "X11: setTitle: NULL\n"); + } + } +#endif +} + + + +/* + * Class: com_jogamp_newt_impl_x11_X11Window + * Method: getRelativeLocation0 + * Signature: (JIJJII)Ljavax/media/nativewindow/util/Point; + */ +JNIEXPORT jobject JNICALL Java_com_jogamp_newt_impl_x11_X11Window_getRelativeLocation0 + (JNIEnv *env, jobject obj, jlong jdisplay, jint screen_index, jlong jsrc_win, jlong jdest_win, jint src_x, jint src_y) +{ + Display * dpy = (Display *) (intptr_t) jdisplay; + Screen * scrn = ScreenOfDisplay(dpy, (int)screen_index); + Window root = XRootWindowOfScreen(scrn); + Window src_win = (Window)jsrc_win; + Window dest_win = (Window)jdest_win; + int dest_x=-1; + int dest_y=-1; + Window child; + Bool res; + + if( 0 == jdest_win ) { dest_win = root; } + if( 0 == jsrc_win ) { src_win = root; } + + displayDispatchErrorHandlerEnable(1, env); + + res = XTranslateCoordinates(dpy, src_win, dest_win, src_x, src_y, &dest_x, &dest_y, &child); + + displayDispatchErrorHandlerEnable(0, env); + + DBG_PRINT( "X11: getRelativeLocation0: %p %d/%d -> %p %d/%d - ok: %d\n", + (void*)src_win, src_x, src_y, (void*)dest_win, dest_x, dest_y, (int)res); + + return (*env)->NewObject(env, pointClz, pointCstr, (jint)dest_x, (jint)dest_y); +} + |