diff options
Diffstat (limited to 'src/net/java/games')
24 files changed, 970 insertions, 299 deletions
diff --git a/src/net/java/games/gluegen/CMethodBindingEmitter.java b/src/net/java/games/gluegen/CMethodBindingEmitter.java index 45035f640..763fe6695 100644 --- a/src/net/java/games/gluegen/CMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/CMethodBindingEmitter.java @@ -50,6 +50,10 @@ public class CMethodBindingEmitter extends FunctionEmitter { protected static final CommentEmitter defaultCommentEmitter = new DefaultCommentEmitter(); + + protected static final String arrayResLength = "_array_res_length"; + protected static final String arrayRes = "_array_res"; + protected static final String arrayIdx = "_array_idx"; private MethodBinding binding; @@ -89,6 +93,12 @@ public class CMethodBindingEmitter extends FunctionEmitter */ private MessageFormat returnValueCapacityExpression = null; + /** + * Length of the returned array. Is ignored if + * binding.getJavaReturnType().isArray() is false. + */ + private MessageFormat returnValueLengthExpression = null; + // Note: the VC++ 6.0 compiler emits hundreds of warnings when the // (necessary) null-checking code is enabled. This appears to just // be a compiler bug, but would be good to track down exactly why it @@ -168,6 +178,49 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Get the expression for the length of the returned array + */ + public final MessageFormat getReturnValueLengthExpression() + { + return returnValueLengthExpression; + } + + /** + * If this function returns an array, sets the expression for the + * length of the returned array. + * + * @param expression a MessageFormat which, when applied to an array + * of type String[] that contains each of the arguments names of the + * Java-side binding, returns an expression that will (when compiled + * by a C compiler) evaluate to an integer-valued expression. The + * value of this expression is the length of the array returned from + * this method. + * + * @throws IllegalArgumentException if the <code> + * binding.getJavaReturnType().isNIOBuffer() == false + * </code> + */ + public final void setReturnValueLengthExpression(MessageFormat expression) + { + returnValueLengthExpression = expression; + + if (!binding.getJavaReturnType().isArray()) + { + throw new IllegalArgumentException( + "Cannot specify return value length for a method that does not " + + "return an array: \"" + binding + "\""); + } + } + + /** + * Returns the List of Strings containing declarations for temporary + * C variables to be assigned to after the underlying function call. + */ + public final List/*<String>*/ getTemporaryCVariableDeclarations() { + return temporaryCVariableDeclarations; + } + + /** * Sets up a List of Strings containing declarations for temporary C * variables to be assigned to after the underlying function call. A * null argument indicates that no manual declarations are to be made. @@ -177,6 +230,16 @@ public class CMethodBindingEmitter extends FunctionEmitter } /** + * Returns the List of Strings containing assignments for temporary + * C variables which are made after the underlying function call. A + * null argument indicates that no manual assignments are to be + * made. + */ + public final List/*<String>*/ getTemporaryCVariableAssignments() { + return temporaryCVariableAssignments; + } + + /** * Sets up a List of Strings containing assignments for temporary C * variables which are made after the underlying function call. A * null argument indicates that no manual assignments are to be made. @@ -340,8 +403,6 @@ public class CMethodBindingEmitter extends FunctionEmitter Type cReturnType = binding.getCReturnType(); JavaType javaReturnType = binding.getJavaReturnType(); - String arrayResLength = "_array_res_length"; - String arrayRes = "_array_res"; String capitalizedComponentType = null; if (!cReturnType.isVoid()) { writer.print(" "); @@ -349,24 +410,36 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(binding.getCSymbol().getReturnType().getName(true)); writer.println(" _res;"); if (javaReturnType.isArray()) { - writer.print(" int "); - writer.print(arrayResLength); - writer.println(";"); + if (javaReturnType.isNIOByteBufferArray()) { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); + writer.print(" int "); + writer.print(arrayIdx); + writer.println(";"); + writer.print(" jobjectArray "); + writer.print(arrayRes); + writer.println(";"); + } else { + writer.print(" int "); + writer.print(arrayResLength); + writer.println(";"); - Class componentType = javaReturnType.getJavaClass().getComponentType(); - if (componentType.isArray()) { - throw new RuntimeException("Multi-dimensional arrays not supported yet"); - } + Class componentType = javaReturnType.getJavaClass().getComponentType(); + if (componentType.isArray()) { + throw new RuntimeException("Multi-dimensional arrays not supported yet"); + } - String javaTypeName = componentType.getName(); - capitalizedComponentType = - "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); - String javaArrayTypeName = "j" + javaTypeName + "Array"; - writer.print(" "); - writer.print(javaArrayTypeName); - writer.print(" "); - writer.print(arrayRes); - writer.println(";"); + String javaTypeName = componentType.getName(); + capitalizedComponentType = + "" + Character.toUpperCase(javaTypeName.charAt(0)) + javaTypeName.substring(1); + String javaArrayTypeName = "j" + javaTypeName + "Array"; + writer.print(" "); + writer.print(javaArrayTypeName); + writer.print(" "); + writer.print(arrayRes); + writer.println(";"); + } } } } @@ -568,6 +641,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -696,6 +774,11 @@ public class CMethodBindingEmitter extends FunctionEmitter if (EMIT_NULL_CHECKS) { writer.println(" }"); } + } else if (javaArgType.isArrayOfCompoundTypeWrappers()) { + + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } } } @@ -826,35 +909,63 @@ public class CMethodBindingEmitter extends FunctionEmitter writer.print(" if (_res == NULL) return NULL;"); writer.println(" return (*env)->NewStringUTF(env, _res);"); } else if (javaReturnType.isArray()) { - // FIXME: must have user provide length of array in .cfg file - // by providing a constant value, input parameter, or - // expression which computes the array size (already present - // as ReturnValueCapacity, not yet implemented / tested here) - - throw new RuntimeException( - "Could not emit native code for function \"" + binding + - "\": array return values for non-char types not implemented yet"); - - // FIXME: This is approximately what will be required here - // - //writer.print(" "); - //writer.print(arrayRes); - //writer.print(" = (*env)->New"); - //writer.print(capitalizedComponentType); - //writer.print("Array(env, "); - //writer.print(arrayResLength); - //writer.println(");"); - //writer.print(" (*env)->Set"); - //writer.print(capitalizedComponentType); - //writer.print("ArrayRegion(env, "); - //writer.print(arrayRes); - //writer.print(", 0, "); - //writer.print(arrayResLength); - //writer.println(", _res);"); - //writer.print(" return "); - //writer.print(arrayRes); - //writer.println(";"); - + if (javaReturnType.isNIOByteBufferArray()) { + writer.println(" if (_res == NULL) return NULL;"); + if (returnValueLengthExpression == null) { + throw new RuntimeException("Error while generating C code: no length specified for array returned from function " + + binding); + } + String[] argumentNames = new String[binding.getNumArguments()]; + for (int i = 0; i < binding.getNumArguments(); i++) { + argumentNames[i] = binding.getArgumentName(i); + } + writer.println(" " + arrayResLength + " = " + returnValueLengthExpression.format(argumentNames) + ";"); + writer.println(" " + arrayRes + " = (*env)->NewObjectArray(env, " + arrayResLength + ", (*env)->FindClass(env, \"java/nio/ByteBuffer\"), NULL);"); + writer.println(" for (" + arrayIdx + " = 0; " + arrayIdx + " < " + arrayResLength + "; " + arrayIdx + "++) {"); + Type retType = binding.getCSymbol().getReturnType(); + Type baseType; + if (retType.isPointer()) { + baseType = retType.asPointer().getTargetType().asPointer().getTargetType(); + } else { + baseType = retType.asArray().getElementType().asPointer().getTargetType(); + } + int sz = baseType.getSize(); + if (sz < 0) + sz = 0; + writer.println(" (*env)->SetObjectArrayElement(env, " + arrayRes + ", " + arrayIdx + + ", (*env)->NewDirectByteBuffer(env, _res[" + arrayIdx + "], " + sz + "));"); + writer.println(" }"); + writer.println(" return " + arrayRes + ";"); + } else { + // FIXME: must have user provide length of array in .cfg file + // by providing a constant value, input parameter, or + // expression which computes the array size (already present + // as ReturnValueCapacity, not yet implemented / tested here) + + throw new RuntimeException( + "Could not emit native code for function \"" + binding + + "\": array return values for non-char types not implemented yet"); + + // FIXME: This is approximately what will be required here + // + //writer.print(" "); + //writer.print(arrayRes); + //writer.print(" = (*env)->New"); + //writer.print(capitalizedComponentType); + //writer.print("Array(env, "); + //writer.print(arrayResLength); + //writer.println(");"); + //writer.print(" (*env)->Set"); + //writer.print(capitalizedComponentType); + //writer.print("ArrayRegion(env, "); + //writer.print(arrayRes); + //writer.print(", 0, "); + //writer.print(arrayResLength); + //writer.println(", _res);"); + //writer.print(" return "); + //writer.print(arrayRes); + //writer.println(";"); + } } } } @@ -1034,6 +1145,11 @@ public class CMethodBindingEmitter extends FunctionEmitter throw new RuntimeException("Unsupported pointer type: \"" + cType.getName() + "\""); } } + else if (javaType.isArrayOfCompoundTypeWrappers()) + { + // FIXME + throw new RuntimeException("Outgoing arrays of StructAccessors not yet implemented"); + } else { ptrTypeString = cType.getName(); diff --git a/src/net/java/games/gluegen/DebugEmitter.java b/src/net/java/games/gluegen/DebugEmitter.java index 677c3e7e6..8618e1576 100644 --- a/src/net/java/games/gluegen/DebugEmitter.java +++ b/src/net/java/games/gluegen/DebugEmitter.java @@ -95,8 +95,13 @@ public class DebugEmitter implements GlueEmitter { TypeDictionary structDictionary, Map canonMap) { } - public void emitStruct(CompoundType t) { - System.out.println("Referenced type \"" + t.getName() + "\""); + public void emitStruct(CompoundType t, String alternateName) { + String name = t.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + System.out.println("Referenced type \"" + name + "\""); } public void endStructs() {} } diff --git a/src/net/java/games/gluegen/GlueEmitter.java b/src/net/java/games/gluegen/GlueEmitter.java index 3d55474e0..e816ffc9d 100644 --- a/src/net/java/games/gluegen/GlueEmitter.java +++ b/src/net/java/games/gluegen/GlueEmitter.java @@ -93,7 +93,11 @@ public interface GlueEmitter { public void beginStructs(TypeDictionary typedefDictionary, TypeDictionary structDictionary, Map canonMap) throws Exception; - /** Emit glue code for the given CompoundType. */ - public void emitStruct(CompoundType t) throws Exception; + /** Emit glue code for the given CompoundType. alternateName is + provided when the CompoundType (e.g. "struct foo_t") has not + been typedefed to anything but the type of "pointer to struct + foo_t" has (e.g. "typedef struct foo_t {} *Foo"); in this case + alternateName would be set to Foo. */ + public void emitStruct(CompoundType t, String alternateName) throws Exception; public void endStructs() throws Exception; } diff --git a/src/net/java/games/gluegen/GlueGen.java b/src/net/java/games/gluegen/GlueGen.java index 374611c81..2e5f3421a 100644 --- a/src/net/java/games/gluegen/GlueGen.java +++ b/src/net/java/games/gluegen/GlueGen.java @@ -241,20 +241,35 @@ public class GlueGen implements GlueEmitterControls { System.err.println("WARNING: during forced struct emission: type \"" + name + "\" was not a struct"); } else { type.visit(referencedStructs); - } + } } // Lay out structs emit.beginStructLayout(); for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { - emit.layoutStruct((CompoundType) iter.next()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.layoutStruct(t.asCompound()); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + emit.layoutStruct(c); + } } emit.endStructLayout(); // Emit structs emit.beginStructs(td, sd, headerParser.getCanonMap()); for (Iterator iter = referencedStructs.results(); iter.hasNext(); ) { - emit.emitStruct((CompoundType) iter.next()); + Type t = (Type) iter.next(); + if (t.isCompound()) { + emit.emitStruct(t.asCompound(), null); + } else if (t.isPointer()) { + PointerType p = t.asPointer(); + CompoundType c = p.getTargetType().asCompound(); + assert p.hasTypedefedName() && c.getName() == null : "ReferencedStructs incorrectly recorded pointer type " + p; + emit.emitStruct(c, p.getName()); + } } emit.endStructs(); diff --git a/src/net/java/games/gluegen/JavaConfiguration.java b/src/net/java/games/gluegen/JavaConfiguration.java index ecd953480..3950a7014 100644 --- a/src/net/java/games/gluegen/JavaConfiguration.java +++ b/src/net/java/games/gluegen/JavaConfiguration.java @@ -105,6 +105,7 @@ public class JavaConfiguration { private List/*<String>*/ forcedStructs = new ArrayList(); private Map/*<String,List<Integer>>*/ mirroredArgs = new HashMap(); private Map/*<String, String>*/ returnValueCapacities = new HashMap(); + private Map/*<String, String>*/ returnValueLengths = new HashMap(); private Map/*<String, List<String>>*/ temporaryCVariableDeclarations = new HashMap(); private Map/*<String, List<String>>*/ temporaryCVariableAssignments = new HashMap(); private Map/*<String,List<String>>*/ extendedInterfaces = new HashMap(); @@ -288,7 +289,9 @@ public class JavaConfiguration { /** Provides a Java MessageFormat expression indicating the number of elements in the returned array from the specified function - name as defined by the ReturnsArray directive. */ + name. Indicates that the given return value, which must be a + pointer to a CompoundType, is actually an array of the + CompoundType rather than a pointer to a single object. */ public String returnedArrayLength(String functionName) { return (String) returnedArrayLengths.get(functionName); } @@ -378,6 +381,13 @@ public class JavaConfiguration { return (String) returnValueCapacities.get(functionName); } + /** Returns a MessageFormat string of the C expression calculating + the length of the array being returned from a native method, or + null if no expression has been specified. */ + public String returnValueLength(String functionName) { + return (String) returnValueLengths.get(functionName); + } + /** Returns a List of Strings of expressions declaring temporary C variables in the glue code for the specified function. */ public List/*<String>*/ temporaryCVariableDeclarations(String functionName) { @@ -582,6 +592,10 @@ public class JavaConfiguration { readReturnValueCapacity(tok, filename, lineNo); // Warning: make sure delimiters are reset at the top of this loop // because ReturnValueCapacity changes them. + } else if (cmd.equalsIgnoreCase("ReturnValueLength")) { + readReturnValueLength(tok, filename, lineNo); + // Warning: make sure delimiters are reset at the top of this loop + // because ReturnValueLength changes them. } else if (cmd.equalsIgnoreCase("Include")) { doInclude(tok, file, filename, lineNo); } else if (cmd.equalsIgnoreCase("IncludeAs")) { @@ -926,6 +940,18 @@ public class JavaConfiguration { } } + protected void readReturnValueLength(StringTokenizer tok, String filename, int lineNo) { + try { + String functionName = tok.nextToken(); + String restOfLine = tok.nextToken("\n\r\f"); + restOfLine = restOfLine.trim(); + returnValueLengths.put(functionName, restOfLine); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"ReturnValueLength\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + protected void readTemporaryCVariableDeclaration(StringTokenizer tok, String filename, int lineNo) { try { String functionName = tok.nextToken(); diff --git a/src/net/java/games/gluegen/JavaEmitter.java b/src/net/java/games/gluegen/JavaEmitter.java index 228b5aff2..26e2a4f23 100644 --- a/src/net/java/games/gluegen/JavaEmitter.java +++ b/src/net/java/games/gluegen/JavaEmitter.java @@ -451,18 +451,26 @@ public class JavaEmitter implements GlueEmitter { this.canonMap = canonMap; } - public void emitStruct(CompoundType structType) throws Exception { - if (structType.getName() == null) { + public void emitStruct(CompoundType structType, String alternateName) throws Exception { + String name = structType.getName(); + if (name == null && alternateName != null) { + name = alternateName; + } + + if (name == null) { System.err.println("WARNING: skipping emission of unnamed struct \"" + structType + "\""); return; } - if (cfg.shouldIgnore(structType.getName())) { + if (cfg.shouldIgnore(name)) { return; } Type containingCType = canonicalize(new PointerType(machDesc.pointerSizeInBytes(), structType, 0)); JavaType containingType = typeToJavaType(containingCType, false); + if (!containingType.isCompoundTypeWrapper()) { + return; + } String containingTypeName = containingType.getName(); boolean needsNativeCode = false; @@ -473,7 +481,7 @@ public class JavaEmitter implements GlueEmitter { } } - String structClassPkg = cfg.packageForStruct(structType.getName()); + String structClassPkg = cfg.packageForStruct(name); PrintWriter writer = null; PrintWriter cWriter = null; try @@ -534,7 +542,7 @@ public class JavaEmitter implements GlueEmitter { for (int i = 0; i < structType.getNumFields(); i++) { Field field = structType.getField(i); Type fieldType = field.getType(); - if (!cfg.shouldIgnore(structType.getName() + " " + field.getName())) { + if (!cfg.shouldIgnore(name + " " + field.getName())) { if (fieldType.isFunctionPointer()) { try { // Emit method call and associated native code @@ -566,7 +574,7 @@ public class JavaEmitter implements GlueEmitter { cWriter); cEmitter.emit(); } catch (Exception e) { - System.err.println("While processing field " + field + " of type " + structType.getName() + ":"); + System.err.println("While processing field " + field + " of type " + name + ":"); throw(e); } } else if (fieldType.isCompound()) { @@ -575,7 +583,7 @@ public class JavaEmitter implements GlueEmitter { // a name?) if (fieldType.getName() == null) { throw new RuntimeException("Anonymous structs as fields not supported yet (field \"" + - field + "\" in type \"" + structType.getName() + "\")"); + field + "\" in type \"" + name + "\")"); } writer.println(); @@ -586,7 +594,7 @@ public class JavaEmitter implements GlueEmitter { // FIXME: add setter by autogenerating "copyTo" for all compound type wrappers } else if (fieldType.isArray()) { - System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + structType.getName() + + System.err.println("WARNING: Array fields (field \"" + field + "\" of type \"" + name + "\") not implemented yet"); } else { JavaType javaType = null; @@ -594,7 +602,7 @@ public class JavaEmitter implements GlueEmitter { javaType = typeToJavaType(fieldType, false); } catch (Exception e) { System.err.println("Error occurred while creating accessor for field \"" + - field.getName() + "\" in type \"" + structType.getName() + "\""); + field.getName() + "\" in type \"" + name + "\""); e.printStackTrace(); throw(e); } @@ -629,10 +637,6 @@ public class JavaEmitter implements GlueEmitter { writer.println(" }"); } else { // FIXME - String name = structType.getName(); - if (name == null) { - name = structType.toString(); - } System.err.println("WARNING: Complicated fields (field \"" + field + "\" of type \"" + name + "\") not implemented yet"); // throw new RuntimeException("Complicated fields (field \"" + field + "\" of type \"" + t + @@ -663,6 +667,7 @@ public class JavaEmitter implements GlueEmitter { String bindingJavaClassName, PrintWriter output) { MessageFormat returnValueCapacityFormat = null; + MessageFormat returnValueLengthFormat = null; JavaType javaReturnType = binding.getJavaReturnType(); if (javaReturnType.isNIOBuffer()) { // See whether capacity has been specified @@ -670,6 +675,12 @@ public class JavaEmitter implements GlueEmitter { if (capacity != null) { returnValueCapacityFormat = new MessageFormat(capacity); } + } else if (javaReturnType.isArray()) { + // See whether length has been specified + String len = cfg.returnValueLength(binding.getName()); + if (len != null) { + returnValueLengthFormat = new MessageFormat(len); + } } CMethodBindingEmitter cEmitter; if (doingImplRoutine) { @@ -686,6 +697,9 @@ public class JavaEmitter implements GlueEmitter { if (returnValueCapacityFormat != null) { cEmitter.setReturnValueCapacityExpression(returnValueCapacityFormat); } + if (returnValueLengthFormat != null) { + cEmitter.setReturnValueLengthExpression(returnValueLengthFormat); + } cEmitter.setTemporaryCVariableDeclarations(cfg.temporaryCVariableDeclarations(binding.getName())); cEmitter.setTemporaryCVariableAssignments(cfg.temporaryCVariableAssignments(binding.getName())); return cEmitter; @@ -758,12 +772,21 @@ public class JavaEmitter implements GlueEmitter { throw new RuntimeException("Arrays of compound types not handled yet"); } // Special cases for known JNI types (in particular for converting jawt.h) - if (cType.getName() != null && - cType.getName().equals("jobject")) { + if (t.getName() != null && + t.getName().equals("jobject")) { return javaType(java.lang.Object.class); } - return JavaType.createForCStruct(cfg.renameJavaType(targetType.getName())); + String name = targetType.getName(); + if (name == null) { + // Try containing pointer type for any typedefs + name = t.getName(); + if (name == null) { + throw new RuntimeException("Couldn't find a proper type name for pointer type " + t); + } + } + + return JavaType.createForCStruct(cfg.renameJavaType(name)); } else { throw new RuntimeException("Don't know how to convert pointer/array type \"" + t + "\""); @@ -775,15 +798,16 @@ public class JavaEmitter implements GlueEmitter { // Get the target type of the target type (targetType was computer earlier // as to be a pointer to the target type, so now we need to get its // target type) + Type bottomType; if (targetType.isPointer()) { // t is<type>**, targetType is <type>*, we need to get <type> - targetType = targetType.asPointer().getTargetType(); + bottomType = targetType.asPointer().getTargetType(); } else { // t is<type>[][], targetType is <type>[], we need to get <type> - targetType = targetType.asArray().getElementType(); + bottomType = targetType.asArray().getElementType(); } - if (targetType.isInt()) { - switch (targetType.getSize()) + if (bottomType.isInt()) { + switch (bottomType.getSize()) { case 1: return javaType(ArrayTypes.byteArrayArrayClass); // FIXME: handle 2,4,8-byte int types here @@ -793,10 +817,13 @@ public class JavaEmitter implements GlueEmitter { "Java type; Currently, the only supported depth=2 " + "pointer/array integer types are \"char**\" and \"char[][]\""); } + } else if (targetType.isPointer() && (targetType.pointerDepth() == 1)) { + // Array of pointers; convert as array of StructAccessors + return JavaType.createForCArray(targetType); } else { throw new RuntimeException( "Could not convert C type \"" + t + "\" " + - "to appropriate Java type; need to add support for " + + "to appropriate Java type; need to add more support for " + "depth=2 pointer/array types with non-integral target " + "types [debug info: targetType=\"" + targetType + "\"]"); } diff --git a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java index 003ea8643..850a2c701 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingEmitter.java @@ -122,7 +122,7 @@ public class JavaMethodBindingEmitter extends FunctionEmitter } protected String getReturnTypeString(boolean skipArray) { - if (skipArray || getReturnedArrayLengthExpression() == null) { + if (skipArray || (getReturnedArrayLengthExpression() == null && !binding.getJavaReturnType().isArrayOfCompoundTypeWrappers())) { return binding.getJavaReturnType().getName(); } return binding.getJavaReturnType().getName() + "[]"; diff --git a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java index 2357e7c38..2ec2723bd 100644 --- a/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java +++ b/src/net/java/games/gluegen/JavaMethodBindingImplEmitter.java @@ -113,6 +113,9 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter returnType.isNIOByteBuffer()) { writer.println("ByteBuffer _res;"); writer.print(" _res = "); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println("ByteBuffer[] _res;"); + writer.print(" _res = "); } else { writer.print("return "); } @@ -205,7 +208,7 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter writer.println(" _tmp.order(ByteOrder.nativeOrder());"); writer.println(" _res.position(0);"); writer.println(" _res.limit(_res.capacity());"); - writer.println(" _retarray[_count] = new " + returnType.getName() + "(_tmp);"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_tmp);"); writer.println(" }"); writer.print (" return _retarray"); } @@ -213,6 +216,14 @@ public class JavaMethodBindingImplEmitter extends JavaMethodBindingEmitter writer.println(";"); writer.println(" if (_res == null) return null;"); writer.print(" return _res.order(ByteOrder.nativeOrder())"); + } else if (returnType.isArrayOfCompoundTypeWrappers()) { + writer.println(";"); + writer.println(" if (_res == null) return null;"); + writer.println(" " + getReturnTypeString(false) + " _retarray = new " + getReturnTypeString(true) + "[_res.length];"); + writer.println(" for (int _count = 0; _count < _res.length; _count++) {"); + writer.println(" _retarray[_count] = new " + getReturnTypeString(true) + "(_res[_count]);"); + writer.println(" }"); + writer.print (" return _retarray"); } writer.println(";"); } diff --git a/src/net/java/games/gluegen/JavaType.java b/src/net/java/games/gluegen/JavaType.java index ac42aa0f1..ea771613e 100644 --- a/src/net/java/games/gluegen/JavaType.java +++ b/src/net/java/games/gluegen/JavaType.java @@ -39,6 +39,10 @@ package net.java.games.gluegen; +import java.nio.*; + +import net.java.games.gluegen.cgram.types.*; + /** * Describes a java-side representation of a type that is used to represent * the same data on both the Java-side and C-side during a JNI operation. Also @@ -47,8 +51,10 @@ package net.java.games.gluegen; public class JavaType { private Class clazz; // Primitive types and other types representable as Class objects private String name; // Types we're generating glue code for (i.e., C structs) + private Type elementType; // Element type if this JavaType represents a C array private static JavaType nioBufferType; private static JavaType nioByteBufferType; + private static JavaType nioByteBufferArrayType; public boolean equals(Object arg) { if ((arg == null) || (!(arg instanceof JavaType))) { @@ -70,14 +76,28 @@ public class JavaType { return clazz.hashCode(); } + /** Creates a JavaType corresponding to the given Java type. This + can be used to represent arrays of primitive values or Strings; + the emitters understand how to perform proper conversion from + the corresponding C type. */ public static JavaType createForClass(Class clazz) { return new JavaType(clazz); } + /** Creates a JavaType corresponding to the specified C CompoundType + name; for example, if "Foo" is supplied, then this JavaType + represents a "Foo *" by way of a StructAccessor. */ public static JavaType createForCStruct(String name) { return new JavaType(name); } + /** Creates a JavaType corresponding to an array of the given + element type. This is used to represent arrays of "Foo **" which + should be mapped to Foo[] in Java. */ + public static JavaType createForCArray(Type elementType) { + return new JavaType(elementType); + } + public static JavaType createForVoidPointer() { return new JavaType(); } @@ -100,6 +120,14 @@ public class JavaType { return nioByteBufferType; } + public static JavaType forNIOByteBufferArrayClass() { + if (nioByteBufferArrayType == null) { + ByteBuffer[] tmp = new ByteBuffer[0]; + nioByteBufferArrayType = createForClass(tmp.getClass()); + } + return nioByteBufferArrayType; + } + /** * Returns the Java Class corresponding to this type. Returns null if this * object corresponds to a C "void*" type. @@ -119,6 +147,9 @@ public class JavaType { } return clazz.getName(); } + if (elementType != null) { + return elementType.getName(); + } return name; } @@ -151,6 +182,10 @@ public class JavaType { return "jobjectArray /*elements are String*/"; //return "jobjectArray"; } + else if (elementType == java.nio.ByteBuffer.class) + { + return "jobjectArray /*elements are ByteBuffer*/"; + } else if (elementType.isArray()) { // Type is array-of-arrays-of-something @@ -189,6 +224,10 @@ public class JavaType { return (clazz == java.nio.ByteBuffer.class); } + public boolean isNIOByteBufferArray() { + return (this == nioByteBufferArrayType); + } + public boolean isString() { return (clazz == java.lang.String.class); } @@ -205,17 +244,16 @@ public class JavaType { return (clazz == Void.TYPE); } - public boolean isObjectType() { - // FIXME: what about char* -> String conversion? - return (isNIOBuffer() || isArray()); - } - public boolean isCompoundTypeWrapper() { return (clazz == null && name != null && !isJNIEnv()); } + public boolean isArrayOfCompoundTypeWrappers() { + return (elementType != null); + } + public boolean isVoidPointerType() { - return (clazz == null && name == null); + return (clazz == null && name == null && elementType == null); } public boolean isJNIEnv() { @@ -252,6 +290,11 @@ public class JavaType { this.name = name; } + /** Constructs a type representing an array of C pointers. */ + private JavaType(Type elementType) { + this.elementType = elementType; + } + /** * Default constructor; the type is initialized to the equivalent of a * C-language "void *". diff --git a/src/net/java/games/gluegen/MethodBinding.java b/src/net/java/games/gluegen/MethodBinding.java index 9cdcd4b0b..b8f0eefdf 100644 --- a/src/net/java/games/gluegen/MethodBinding.java +++ b/src/net/java/games/gluegen/MethodBinding.java @@ -174,7 +174,8 @@ public class MethodBinding { public boolean needsBody() { if (!computedNeedsBody) { if (javaReturnType.isCompoundTypeWrapper() || - javaReturnType.isNIOByteBuffer()) { + javaReturnType.isNIOByteBuffer() || + javaReturnType.isArrayOfCompoundTypeWrappers()) { // Needs wrapping and/or setting of byte order (neither of // which can be done easily from native code) needsBody = true; @@ -204,6 +205,8 @@ public class MethodBinding { binding.thisPointerIndex = thisPointerIndex; if (javaReturnType.isCompoundTypeWrapper()) { binding.setJavaReturnType(JavaType.forNIOByteBufferClass()); + } else if (javaReturnType.isArrayOfCompoundTypeWrappers()) { + binding.setJavaReturnType(JavaType.forNIOByteBufferArrayClass()); } else { binding.setJavaReturnType(javaReturnType); } diff --git a/src/net/java/games/gluegen/ReferencedStructs.java b/src/net/java/games/gluegen/ReferencedStructs.java index 1b3b75198..4a7a8aadf 100644 --- a/src/net/java/games/gluegen/ReferencedStructs.java +++ b/src/net/java/games/gluegen/ReferencedStructs.java @@ -54,7 +54,18 @@ public class ReferencedStructs implements TypeVisitor { } public void visitType(Type t) { - if (t.isCompound()) { + if (t.isPointer()) { + PointerType p = t.asPointer(); + if (p.hasTypedefedName()) { + CompoundType c = p.getTargetType().asCompound(); + if (c != null && c.getName() == null) { + // This otherwise-unnamed CompoundType is referred to by a + // PointerType that has a typedef name. Assume that it is + // referred to in the glue code and emit it. + results.add(p); + } + } + } else if (t.isCompound()) { results.add(t); } } diff --git a/src/net/java/games/gluegen/cgram/types/PointerType.java b/src/net/java/games/gluegen/cgram/types/PointerType.java index 62bfe9c1a..64acbade4 100644 --- a/src/net/java/games/gluegen/cgram/types/PointerType.java +++ b/src/net/java/games/gluegen/cgram/types/PointerType.java @@ -100,6 +100,10 @@ public class PointerType extends Type { } } + public boolean hasTypedefedName() { + return hasTypedefedName; + } + public PointerType asPointer() { return this; } public Type getTargetType() { return targetType; } diff --git a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java index 47476a54c..c6ac77c7b 100644 --- a/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java +++ b/src/net/java/games/gluegen/opengl/BuildComposablePipeline.java @@ -209,7 +209,7 @@ public class BuildComposablePipeline { output.print(" public "); output.print(' '); - output.print(m.getReturnType().getName()); + output.print(JavaType.createForClass(m.getReturnType()).getName()); output.print(' '); output.print(m.getName()); output.print('('); diff --git a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java index cd0554dc3..2a1559e52 100644 --- a/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/CGLPAWrapperEmitter.java @@ -68,6 +68,15 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter methodToWrap.getIsJavaMethodStatic(), methodToWrap.getDefaultOutput() ); + + if (methodToWrap.getReturnValueCapacityExpression() != null) { + setReturnValueCapacityExpression(methodToWrap.getReturnValueCapacityExpression()); + } + if (methodToWrap.getReturnValueLengthExpression() != null) { + setReturnValueLengthExpression(methodToWrap.getReturnValueLengthExpression()); + } + setTemporaryCVariableDeclarations(methodToWrap.getTemporaryCVariableDeclarations()); + setTemporaryCVariableAssignments (methodToWrap.getTemporaryCVariableAssignments ()); setCommentEmitter(defaultCommentEmitter); } @@ -192,6 +201,13 @@ public class CGLPAWrapperEmitter extends CMethodBindingEmitter writer.println(");"); } + protected String jniMangle(MethodBinding binding) { + StringBuffer buf = new StringBuffer(); + buf.append(super.jniMangle(binding)); + jniMangle(Long.TYPE, buf); + return buf.toString(); + } + /** This class emits the comment for the wrapper method */ private static class CGLPAWrapperCommentEmitter extends CMethodBindingEmitter.DefaultCommentEmitter { protected void emitBeginning(FunctionEmitter methodEmitter, PrintWriter writer) { diff --git a/src/net/java/games/gluegen/opengl/GLEmitter.java b/src/net/java/games/gluegen/opengl/GLEmitter.java index 27cc07e2c..f6e26fd3b 100644 --- a/src/net/java/games/gluegen/opengl/GLEmitter.java +++ b/src/net/java/games/gluegen/opengl/GLEmitter.java @@ -44,6 +44,7 @@ import java.text.MessageFormat; import java.util.*; import net.java.games.gluegen.*; import net.java.games.gluegen.cgram.types.*; +import net.java.games.gluegen.runtime.*; /** * A subclass of JavaEmitter that modifies the normal emission of C and Java @@ -52,11 +53,12 @@ import net.java.games.gluegen.cgram.types.*; */ public class GLEmitter extends JavaEmitter { - public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; + public static final String PROCADDRESS_VAR_PREFIX = ProcAddressHelper.PROCADDRESS_VAR_PREFIX; protected static final String WRAP_PREFIX = "dispatch_"; private TypeDictionary typedefDictionary; private PrintWriter tableWriter; - private String tableClassName = "ProcAddressTable"; + private String tableClassPackage; + private String tableClassName; private int numProcAddressEntries; public void beginFunctions(TypeDictionary typedefDictionary, @@ -70,7 +72,7 @@ public class GLEmitter extends JavaEmitter cWriter().println(); } - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { beginGLProcAddressTable(); } @@ -79,7 +81,7 @@ public class GLEmitter extends JavaEmitter public void endFunctions() throws Exception { - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { endGLProcAddressTable(); } @@ -118,7 +120,7 @@ public class GLEmitter extends JavaEmitter // 9 is default # expanded bindings for void* ArrayList modifiedEmitters = new ArrayList(9); - if (((GLConfiguration)getConfig()).emitProcAddressTable()) + if (getGLConfig().emitProcAddressTable()) { // emit an entry in the GL proc address table for this method. emitGLProcAddressTableEntryForSymbol(sym); @@ -185,7 +187,7 @@ public class GLEmitter extends JavaEmitter return null; return baseJavaEmitter; } - return new JavaGLPAWrapperEmitter(baseJavaEmitter); + return new JavaGLPAWrapperEmitter(baseJavaEmitter, getGLConfig().getProcAddressTableExpr()); } private CMethodBindingEmitter generateModifiedEmitter(CMethodBindingEmitter baseCEmitter) @@ -205,11 +207,17 @@ public class GLEmitter extends JavaEmitter { String symName = sym.getName(); + GLConfiguration config = getGLConfig(); + // We should only wrap the GL symbol if its function pointer typedef has // been defined (most likely in glext.h). String funcPointerTypedefName = getGLFunctionPointerTypedefName(sym); boolean shouldWrap = typedefDictionary.containsKey(funcPointerTypedefName); //System.err.println(funcPointerTypedefName + " defined: " + shouldWrap); + + if (config.skipProcAddressGen(symName)) { + shouldWrap = false; + } if (!shouldWrap) { @@ -221,26 +229,27 @@ public class GLEmitter extends JavaEmitter private void beginGLProcAddressTable() throws Exception { - String implPackageName = getImplPackageName(); + tableClassPackage = getGLConfig().tableClassPackage(); + tableClassName = getGLConfig().tableClassName(); + + // Table defaults to going into the impl directory unless otherwise overridden + String implPackageName = tableClassPackage; + if (implPackageName == null) { + implPackageName = getImplPackageName(); + } String jImplRoot = getJavaOutputDir() + File.separator + CodeGenUtils.packageAsPath(implPackageName); - // HACK: until we have a way to make the impl dir different from the - // WindowsGLImpl dir and the interface dir - //tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); - File tmpFile = new File(jImplRoot); - tmpFile = tmpFile.getParentFile(); - tmpFile = new File(tmpFile, tableClassName + ".java"); - tableWriter = openFile(tmpFile.getPath()); - // tableWriter = openFile(jImplRoot + File.separator + ".." + File.separator + tableClassName + ".java"); + tableWriter = openFile(jImplRoot + File.separator + tableClassName + ".java"); CodeGenUtils.emitAutogeneratedWarning(tableWriter, this); - // HACK: until we have a way to make the impl dir different from the - // WindowsGLImpl dir and the interface dir - //tableWriter.println("package " + implPackageName + ";"); - tableWriter.println("package " + getJavaPackageName() + ".impl;"); + tableWriter.println("package " + implPackageName + ";"); + tableWriter.println(); + for (Iterator iter = getConfig().imports().iterator(); iter.hasNext(); ) { + tableWriter.println("import " + ((String) iter.next()) + ";"); + } tableWriter.println(); tableWriter.println("/**"); tableWriter.println(" * This table is a cache of the native pointers to OpenGL extension"); @@ -260,9 +269,6 @@ public class GLEmitter extends JavaEmitter private void endGLProcAddressTable() throws Exception { PrintWriter w = tableWriter; - w.print(" protected static long __PROCADDRESSINDEX__LASTINDEX = "); - w.print(numProcAddressEntries-1); - w.println(';'); w.println(); w.println(" /**"); @@ -301,30 +307,69 @@ public class GLEmitter extends JavaEmitter private void emitGLProcAddressTableEntryForSymbol(FunctionSymbol cFunc) { - tableWriter.print(" public static long "); + tableWriter.print(" public long "); tableWriter.print(PROCADDRESS_VAR_PREFIX); tableWriter.print(cFunc.getName()); tableWriter.println(";"); ++numProcAddressEntries; } + private GLConfiguration getGLConfig() { + return (GLConfiguration) getConfig(); + } + protected static class GLConfiguration extends JavaConfiguration { private boolean emitProcAddressTable = false; - + private String tableClassPackage; + private String tableClassName = "ProcAddressTable"; + private Set/*<String>*/ skipProcAddressGen = new HashSet(); + private String getProcAddressTableExpr = "context.getGLProcAddressTable()"; + protected void dispatch(String cmd, StringTokenizer tok, File file, String filename, int lineNo) throws IOException { if (cmd.equalsIgnoreCase("EmitProcAddressTable")) { emitProcAddressTable = readBoolean("EmitProcAddressTable", tok, filename, lineNo).booleanValue(); } + else if (cmd.equalsIgnoreCase("ProcAddressTablePackage")) + { + tableClassPackage = readString("ProcAddressTablePackage", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("ProcAddressTableClassName")) + { + tableClassName = readString("ProcAddressTableClassName", tok, filename, lineNo); + } + else if (cmd.equalsIgnoreCase("SkipProcAddressGen")) + { + String sym = readString("SkipProcAddressGen", tok, filename, lineNo); + skipProcAddressGen.add(sym); + } + else if (cmd.equalsIgnoreCase("GetProcAddressTableExpr")) + { + getProcAddressTableExpr = readGetProcAddressTableExpr(tok, filename, lineNo); + } else { super.dispatch(cmd,tok,file,filename,lineNo); } } - public boolean emitProcAddressTable() { return emitProcAddressTable; } + protected String readGetProcAddressTableExpr(StringTokenizer tok, String filename, int lineNo) { + try { + String restOfLine = tok.nextToken("\n\r\f"); + return restOfLine.trim(); + } catch (NoSuchElementException e) { + throw new RuntimeException("Error parsing \"GetProcAddressTableExpr\" command at line " + lineNo + + " in file \"" + filename + "\"", e); + } + } + + public boolean emitProcAddressTable() { return emitProcAddressTable; } + public String tableClassPackage() { return tableClassPackage; } + public String tableClassName() { return tableClassName; } + public boolean skipProcAddressGen (String name) { return skipProcAddressGen.contains(name); } + public String getProcAddressTableExpr() { return getProcAddressTableExpr; } } // end class GLConfiguration } diff --git a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java index e0a098f2a..c3b17f6cb 100644 --- a/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java +++ b/src/net/java/games/gluegen/opengl/JavaGLPAWrapperEmitter.java @@ -50,10 +50,12 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter new WrappedMethodCommentEmitter(); private JavaMethodBindingEmitter emitterBeingWrapped; + private String getProcAddressTableExpr; - public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap) + public JavaGLPAWrapperEmitter(JavaMethodBindingEmitter methodToWrap, String getProcAddressTableExpr) { super(methodToWrap.getBinding(), methodToWrap.getDefaultOutput(), methodToWrap.getRuntimeExceptionType()); + this.getProcAddressTableExpr = getProcAddressTableExpr; if (methodToWrap.getBinding().hasContainingType()) { @@ -119,6 +121,9 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter // Now make our binding use the original access of the wrapped method this.addModifier(origAccess); + if (emitterBeingWrapped.hasModifier(STATIC)) { + this.addModifier(STATIC); + } } protected boolean needsBody() { @@ -152,9 +157,7 @@ public class JavaGLPAWrapperEmitter extends JavaMethodBindingImplEmitter String procAddressVariable = GLEmitter.PROCADDRESS_VAR_PREFIX + wrappedBinding.getName(); - writer.print(" final long addr = context.getGLProcAddressTable()."); - writer.print(procAddressVariable); - writer.println(';'); + writer.println(" final long addr = " + getProcAddressTableExpr + "." + procAddressVariable + ";"); writer.println(" if (addr == 0) {"); writer.println(" throw new GLException(\"Method \\\"" + binding.getName() + "\\\" not available\");"); writer.println(" }"); diff --git a/src/net/java/games/gluegen/runtime/ProcAddressHelper.java b/src/net/java/games/gluegen/runtime/ProcAddressHelper.java new file mode 100644 index 000000000..fec0eb366 --- /dev/null +++ b/src/net/java/games/gluegen/runtime/ProcAddressHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2003 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 + * MIDROSYSTEMS, 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. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package net.java.games.gluegen.runtime; + +/** Contains constants used in glue code generation. */ + +public class ProcAddressHelper { + public static final String PROCADDRESS_VAR_PREFIX = "_addressof_"; +} diff --git a/src/net/java/games/jogl/impl/GLContext.java b/src/net/java/games/jogl/impl/GLContext.java index 98b1c6065..eb7d21988 100644 --- a/src/net/java/games/jogl/impl/GLContext.java +++ b/src/net/java/games/jogl/impl/GLContext.java @@ -42,6 +42,7 @@ package net.java.games.jogl.impl; import java.awt.Component; import java.awt.EventQueue; import net.java.games.jogl.*; +import net.java.games.gluegen.runtime.*; public abstract class GLContext { protected static final boolean DEBUG = false; @@ -75,8 +76,10 @@ public abstract class GLContext { // All GLU interfaces eventually route calls down to gluRoot. It can be // static because GLU it doesn't actually need to own context, it just makes // GL calls and assumes some context is active. - protected static final GLU gluRoot = new GLUImpl(); - protected static GLU glu = gluRoot; // this is the context's GLU interface + protected static final GLUProcAddressTable gluProcAddressTable = new GLUProcAddressTable(); + protected static final GLU gluRoot = new GLUImpl(gluProcAddressTable); + protected static boolean haveResetGLUProcAddressTable; + protected GLU glu = gluRoot; // this is the context's GLU interface protected Thread renderingThread; protected Runnable deferredReshapeAction; @@ -282,6 +285,13 @@ public abstract class GLContext { */ protected void resetGLFunctionAvailability() { functionAvailability.flush(); + if (!haveResetGLUProcAddressTable) { + if (DEBUG) { + System.err.println("!!! Initializing GLU extension address table"); + } + resetProcAddressTable(gluProcAddressTable); + haveResetGLUProcAddressTable = true; // Only need to do this once globally + } } /** @@ -391,6 +401,40 @@ public abstract class GLContext { conditions cause a GLException to be thrown. */ protected abstract void swapBuffers() throws GLException; + /** Helper routine which resets a ProcAddressTable generated by the + GLEmitter by looking up anew all of its function pointers. */ + protected void resetProcAddressTable(Object table) { + Class tableClass = table.getClass(); + java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); + + for (int i = 0; i < fields.length; ++i) { + String addressFieldName = fields[i].getName(); + if (!addressFieldName.startsWith(ProcAddressHelper.PROCADDRESS_VAR_PREFIX)) { + // not a proc address variable + continue; + } + int startOfMethodName = ProcAddressHelper.PROCADDRESS_VAR_PREFIX.length(); + String glFuncName = addressFieldName.substring(startOfMethodName); + try { + java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); + assert(addressField.getType() == Long.TYPE); + long newProcAddress = dynamicLookupFunction(glFuncName); + // set the current value of the proc address variable in the table object + addressField.setLong(table, newProcAddress); + if (DEBUG) { + System.err.println(glFuncName + " = 0x" + Long.toHexString(newProcAddress)); + } + } catch (Exception e) { + throw new GLException("Cannot get GL proc address for method \"" + + glFuncName + "\": Couldn't set value of field \"" + addressFieldName + + "\" in class " + tableClass.getName(), e); + } + } + } + + /** Dynamically looks up the given function. */ + protected abstract long dynamicLookupFunction(String glFuncName); + //---------------------------------------------------------------------- // Internals only below this point // diff --git a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java index 23367c045..d35392ac1 100644 --- a/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java +++ b/src/net/java/games/jogl/impl/macosx/MacOSXGLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.macosx; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -49,6 +49,9 @@ public abstract class MacOSXGLContext extends GLContext { private static JAWT jawt; protected long nsContext; // NSOpenGLContext + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; public MacOSXGLContext(Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser) { @@ -90,74 +93,25 @@ public abstract class MacOSXGLContext extends GLContext protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + return CGL.getProcAddress(glFuncName); + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() - { if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } - - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = CGL.getProcAddress(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } - } - + resetProcAddressTable(getGLProcAddressTable()); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -171,10 +125,6 @@ public abstract class MacOSXGLContext extends GLContext // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) diff --git a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java index 4575b6210..5a6021acf 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsGLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.windows; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -53,6 +53,11 @@ public abstract class WindowsGLContext extends GLContext { private boolean wglGetExtensionsStringEXTAvailable; private static final Map/*<String, String>*/ functionNameMap; private static final Map/*<String, String>*/ extensionNameMap; + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; + // Handle to GLU32.dll + private long hglu32; static { functionNameMap = new HashMap(); @@ -132,72 +137,34 @@ public abstract class WindowsGLContext extends GLContext { protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + long res = WGL.wglGetProcAddress(glFuncName); + if (res == 0) { + // GLU routines aren't known to the OpenGL function lookup + if (hglu32 == 0) { + hglu32 = WGL.LoadLibraryA("GLU32"); + if (hglu32 == 0) { + throw new GLException("Error loading GLU32.DLL"); + } + } + res = WGL.GetProcAddress(hglu32, glFuncName); + } + return res; + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() { - if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } - - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = WGL.wglGetProcAddress(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } - } - + resetProcAddressTable(getGLProcAddressTable()); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() { + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -234,10 +201,6 @@ public abstract class WindowsGLContext extends GLContext { // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) { JAWT j = new JAWT(); diff --git a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java index 300e9cb85..484c8f8af 100644 --- a/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java +++ b/src/net/java/games/jogl/impl/windows/WindowsPbufferGLContext.java @@ -119,6 +119,10 @@ public class WindowsPbufferGLContext extends WindowsGLContext { public void createPbuffer(long parentHdc, long parentHglrc) { GL gl = getGL(); + // Must initally grab OpenGL function pointers while parent's + // context is current because otherwise we don't have the wgl + // extensions available to us + resetGLFunctionAvailability(); int[] iattributes = new int [2*MAX_ATTRIBS]; float[] fattributes = new float[2*MAX_ATTRIBS]; diff --git a/src/net/java/games/jogl/impl/x11/X11GLContext.java b/src/net/java/games/jogl/impl/x11/X11GLContext.java index 8e9804578..ec430bfe8 100644 --- a/src/net/java/games/jogl/impl/x11/X11GLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11GLContext.java @@ -41,7 +41,7 @@ package net.java.games.jogl.impl.x11; import java.awt.Component; import java.util.*; -import net.java.games.gluegen.opengl.*; // for PROCADDRESS_VAR_PREFIX +import net.java.games.gluegen.runtime.*; // for PROCADDRESS_VAR_PREFIX import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -53,6 +53,11 @@ public abstract class X11GLContext extends GLContext { private boolean glXQueryExtensionsStringInitialized; private boolean glXQueryExtensionsStringAvailable; private static final Map/*<String, String>*/ functionNameMap; + private boolean isGLX13; + // Table that holds the addresses of the native C-language entry points for + // OpenGL functions. + private GLProcAddressTable glProcAddressTable; + private static boolean haveResetGLXProcAddressTable; static { functionNameMap = new HashMap(); @@ -105,6 +110,14 @@ public abstract class X11GLContext extends GLContext { */ protected abstract void create(); + public boolean isExtensionAvailable(String glExtensionName) { + if (glExtensionName.equals("GL_ARB_pbuffer") || + glExtensionName.equals("GL_ARB_pixel_format")) { + return isGLX13; + } + return super.isExtensionAvailable(glExtensionName); + } + protected synchronized boolean makeCurrent(Runnable initAction) throws GLException { boolean created = false; if (context == 0) { @@ -140,72 +153,44 @@ public abstract class X11GLContext extends GLContext { protected abstract void swapBuffers() throws GLException; + protected long dynamicLookupFunction(String glFuncName) { + long res = GLX.glXGetProcAddressARB(glFuncName); + if (res == 0) { + // GLU routines aren't known to the OpenGL function lookup + res = GLX.dlsym(glFuncName); + } + return res; + } protected void resetGLFunctionAvailability() { super.resetGLFunctionAvailability(); - resetGLProcAddressTable(); - } - - protected void resetGLProcAddressTable() { - if (DEBUG) { System.err.println("!!! Initializing OpenGL extension address table"); } + resetProcAddressTable(getGLProcAddressTable()); - net.java.games.jogl.impl.ProcAddressTable table = getGLProcAddressTable(); - - // if GL is no longer an interface, we'll have to re-implement the code - // below so it only iterates through gl methods (a non-interface might - // have constructors, custom methods, etc). For now we assume all methods - // will be gl methods. - GL gl = getGL(); - - Class tableClass = table.getClass(); - - java.lang.reflect.Field[] fields = tableClass.getDeclaredFields(); - - for (int i = 0; i < fields.length; ++i) { - String addressFieldName = fields[i].getName(); - if (!addressFieldName.startsWith(GLEmitter.PROCADDRESS_VAR_PREFIX)) - { - // not a proc address variable - continue; - } - int startOfMethodName = GLEmitter.PROCADDRESS_VAR_PREFIX.length(); - String glFuncName = addressFieldName.substring(startOfMethodName); - try - { - java.lang.reflect.Field addressField = tableClass.getDeclaredField(addressFieldName); - assert(addressField.getType() == Long.TYPE); - // get the current value of the proc address variable in the table object - long oldProcAddress = addressField.getLong(table); - long newProcAddress = GLX.glXGetProcAddressARB(glFuncName); - /* - System.err.println( - "!!! Address=" + (newProcAddress == 0 - ? "<NULL> " - : ("0x" + - Long.toHexString(newProcAddress))) + - "\tGL func: " + glFuncName); - */ - // set the current value of the proc address variable in the table object - addressField.setLong(gl, newProcAddress); - } catch (Exception e) { - throw new GLException( - "Cannot get GL proc address for method \"" + - glFuncName + "\": Couldn't get value of field \"" + addressFieldName + - "\" in class " + tableClass.getName(), e); - } + if (!haveResetGLXProcAddressTable) { + resetProcAddressTable(GLX.getGLXProcAddressTable()); } + // Figure out whether we are running GLX version 1.3 or above and + // therefore have pbuffer support + if (display == 0) { + throw new GLException("Expected non-null DISPLAY for querying GLX version"); + } + int[] major = new int[1]; + int[] minor = new int[1]; + if (!GLX.glXQueryVersion(display, major, minor)) { + throw new GLException("glXQueryVersion failed"); + } + isGLX13 = ((major[0] > 1) || (minor[0] > 2)); } - public net.java.games.jogl.impl.ProcAddressTable getGLProcAddressTable() { + public GLProcAddressTable getGLProcAddressTable() { if (glProcAddressTable == null) { // FIXME: cache ProcAddressTables by capability bits so we can // share them among contexts with the same capabilities - glProcAddressTable = - new net.java.games.jogl.impl.ProcAddressTable(); + glProcAddressTable = new GLProcAddressTable(); } return glProcAddressTable; } @@ -245,10 +230,6 @@ public abstract class X11GLContext extends GLContext { // Internals only below this point // - // Table that holds the addresses of the native C-language entry points for - // OpenGL functions. - private net.java.games.jogl.impl.ProcAddressTable glProcAddressTable; - protected JAWT getJAWT() { if (jawt == null) { JAWT j = new JAWT(); diff --git a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java index 496e113a5..385874da7 100644 --- a/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java +++ b/src/net/java/games/jogl/impl/x11/X11OnscreenGLContext.java @@ -40,6 +40,8 @@ package net.java.games.jogl.impl.x11; import java.awt.Component; +import java.util.*; + import net.java.games.jogl.*; import net.java.games.jogl.impl.*; @@ -49,6 +51,9 @@ public class X11OnscreenGLContext extends X11GLContext { private JAWT_DrawingSurfaceInfo dsi; private JAWT_X11DrawingSurfaceInfo x11dsi; + // Variables for pbuffer support + List pbuffersToInstantiate = new ArrayList(); + public X11OnscreenGLContext(Component component, GLCapabilities capabilities, GLCapabilitiesChooser chooser) { super(component, capabilities, chooser); } @@ -75,14 +80,16 @@ public class X11OnscreenGLContext extends X11GLContext { } public boolean canCreatePbufferContext() { - // For now say no - return false; + // FIXME: should we gate this on GLX 1.3 being available? + return true; } public synchronized GLContext createPbufferContext(GLCapabilities capabilities, int initialWidth, int initialHeight) { - throw new GLException("Not yet supported"); + X11PbufferGLContext ctx = new X11PbufferGLContext(capabilities, initialWidth, initialHeight); + pbuffersToInstantiate.add(ctx); + return ctx; } public void bindPbufferToTexture() { @@ -98,7 +105,16 @@ public class X11OnscreenGLContext extends X11GLContext { if (!lockSurface()) { return false; } - return super.makeCurrent(initAction); + boolean ret = super.makeCurrent(initAction); + if (ret) { + // Instantiate any pending pbuffers + while (!pbuffersToInstantiate.isEmpty()) { + X11PbufferGLContext ctx = + (X11PbufferGLContext) pbuffersToInstantiate.remove(pbuffersToInstantiate.size() - 1); + ctx.createPbuffer(display, context); + } + } + return ret; } catch (RuntimeException e) { try { unlockSurface(); diff --git a/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java new file mode 100644 index 000000000..264d60fc7 --- /dev/null +++ b/src/net/java/games/jogl/impl/x11/X11PbufferGLContext.java @@ -0,0 +1,338 @@ +/* + * Copyright (c) 2003 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 + * MIDROSYSTEMS, 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. + * + * You acknowledge that this software is not designed or intended for use + * in the design, construction, operation or maintenance of any nuclear + * facility. + * + * Sun gratefully acknowledges that this software was originally authored + * and developed by Kenneth Bradley Russell and Christopher John Kline. + */ + +package net.java.games.jogl.impl.x11; + +import net.java.games.jogl.*; +import net.java.games.jogl.impl.*; + +public class X11PbufferGLContext extends X11GLContext { + private static final boolean DEBUG = false; + + private int initWidth; + private int initHeight; + + private long buffer; // GLXPbuffer + private GLXFBConfig fbConfig; + private int width; + private int height; + + // FIXME: kept around because we create the OpenGL context lazily to + // better integrate with the X11GLContext framework + private long parentContext; + + private static final int MAX_PFORMATS = 256; + private static final int MAX_ATTRIBS = 256; + + // FIXME: figure out how to support render-to-texture and + // render-to-texture-rectangle (which appear to be supported, though + // it looks like floating-point buffers are not) + + public X11PbufferGLContext(GLCapabilities capabilities, int initialWidth, int initialHeight) { + super(null, capabilities, null); + this.initWidth = initialWidth; + this.initHeight = initialHeight; + if (initWidth <= 0 || initHeight <= 0) { + throw new GLException("Initial width and height of pbuffer must be positive (were (" + + initWidth + ", " + initHeight + "))"); + } + } + + public boolean canCreatePbufferContext() { + return false; + } + + public GLContext createPbufferContext(GLCapabilities capabilities, + int initialWidth, + int initialHeight) { + throw new GLException("Not supported"); + } + + public void bindPbufferToTexture() { + // FIXME: figure out how to implement this + throw new GLException("Not yet implemented"); + } + + public void releasePbufferFromTexture() { + // FIXME: figure out how to implement this + throw new GLException("Not yet implemented"); + } + + public void createPbuffer(long display, long parentContext) { + if (display == 0) { + throw new GLException("Null display"); + } + + if (parentContext == 0) { + throw new GLException("Null parentContext"); + } + + if (capabilities.getOffscreenFloatingPointBuffers()) { + throw new GLException("Floating-point pbuffers not supported yet on X11"); + } + + if (capabilities.getOffscreenRenderToTexture()) { + throw new GLException("Render-to-texture pbuffers not supported yet on X11"); + } + + if (capabilities.getOffscreenRenderToTextureRectangle()) { + throw new GLException("Render-to-texture-rectangle pbuffers not supported yet on X11"); + } + + int[] iattributes = new int [2*MAX_ATTRIBS]; + float[] fattributes = new float[2*MAX_ATTRIBS]; + int nfattribs = 0; + int niattribs = 0; + + // Since we are trying to create a pbuffer, the GLXFBConfig we + // request (and subsequently use) must be "p-buffer capable". + iattributes[niattribs++] = GL.GLX_DRAWABLE_TYPE; + iattributes[niattribs++] = GL.GLX_PBUFFER_BIT; + + iattributes[niattribs++] = GL.GLX_RENDER_TYPE; + iattributes[niattribs++] = GL.GLX_RGBA_BIT; + + iattributes[niattribs++] = GLX.GLX_DOUBLEBUFFER; + if (capabilities.getDoubleBuffered()) { + iattributes[niattribs++] = GL.GL_TRUE; + } else { + iattributes[niattribs++] = GL.GL_FALSE; + } + + iattributes[niattribs++] = GLX.GLX_DEPTH_SIZE; + iattributes[niattribs++] = capabilities.getDepthBits(); + + iattributes[niattribs++] = GLX.GLX_RED_SIZE; + iattributes[niattribs++] = capabilities.getRedBits(); + + iattributes[niattribs++] = GLX.GLX_GREEN_SIZE; + iattributes[niattribs++] = capabilities.getGreenBits(); + + iattributes[niattribs++] = GLX.GLX_BLUE_SIZE; + iattributes[niattribs++] = capabilities.getBlueBits(); + + iattributes[niattribs++] = GLX.GLX_ALPHA_SIZE; + iattributes[niattribs++] = capabilities.getAlphaBits(); + + if (capabilities.getStencilBits() > 0) { + iattributes[niattribs++] = GLX.GLX_STENCIL_SIZE; + iattributes[niattribs++] = capabilities.getStencilBits(); + } + + if (capabilities.getAccumRedBits() > 0 || + capabilities.getAccumGreenBits() > 0 || + capabilities.getAccumBlueBits() > 0) { + iattributes[niattribs++] = GLX.GLX_ACCUM_RED_SIZE; + iattributes[niattribs++] = capabilities.getAccumRedBits(); + iattributes[niattribs++] = GLX.GLX_ACCUM_GREEN_SIZE; + iattributes[niattribs++] = capabilities.getAccumGreenBits(); + iattributes[niattribs++] = GLX.GLX_ACCUM_BLUE_SIZE; + iattributes[niattribs++] = capabilities.getAccumBlueBits(); + } + + iattributes[niattribs++] = 0; // null-terminate + + int screen = 0; // FIXME: provide way to specify this? + int[] nelementsTmp = new int[1]; + GLXFBConfig[] fbConfigs = GLX.glXChooseFBConfig(display, screen, iattributes, nelementsTmp); + if (fbConfigs == null || fbConfigs.length == 0 || fbConfigs[0] == null) { + throw new GLException("pbuffer creation error: glXChooseFBConfig() failed"); + } + // Note that we currently don't allow selection of anything but + // the first GLXFBConfig in the returned list + GLXFBConfig fbConfig = fbConfigs[0]; + int nelements = nelementsTmp[0]; + if (nelements <= 0) { + throw new GLException("pbuffer creation error: couldn't find a suitable frame buffer configuration"); + } + + if (DEBUG) { + System.err.println("Found " + fbConfigs.length + " matching GLXFBConfigs"); + System.err.println("Parameters of default one:"); + System.err.println("render type: 0x" + Integer.toHexString(queryFBConfig(display, fbConfig, GLX.GLX_RENDER_TYPE))); + System.err.println("rgba: " + ((queryFBConfig(display, fbConfig, GLX.GLX_RENDER_TYPE) & GLX.GLX_RGBA_BIT) != 0)); + System.err.println("r: " + queryFBConfig(display, fbConfig, GLX.GLX_RED_SIZE)); + System.err.println("g: " + queryFBConfig(display, fbConfig, GLX.GLX_GREEN_SIZE)); + System.err.println("b: " + queryFBConfig(display, fbConfig, GLX.GLX_BLUE_SIZE)); + System.err.println("a: " + queryFBConfig(display, fbConfig, GLX.GLX_ALPHA_SIZE)); + System.err.println("depth: " + queryFBConfig(display, fbConfig, GLX.GLX_DEPTH_SIZE)); + System.err.println("double buffered: " + queryFBConfig(display, fbConfig, GLX.GLX_DOUBLEBUFFER)); + } + + // Create the p-buffer. + niattribs = 0; + + iattributes[niattribs++] = GL.GLX_PBUFFER_WIDTH; + iattributes[niattribs++] = initWidth; + iattributes[niattribs++] = GL.GLX_PBUFFER_HEIGHT; + iattributes[niattribs++] = initHeight; + + iattributes[niattribs++] = 0; + + int tmpBuffer = GLX.glXCreatePbuffer(display, fbConfig, iattributes); + if (tmpBuffer == 0) { + // FIXME: query X error code for detail error message + throw new GLException("pbuffer creation error: glXCreatePbuffer() failed"); + } + + // Set up instance variables + this.display = display; + this.parentContext = parentContext; + buffer = tmpBuffer; + this.fbConfig = fbConfig; + + // Determine the actual width and height we were able to create. + int[] tmp = new int[1]; + GLX.glXQueryDrawable(display, (int) buffer, GL.GLX_WIDTH, tmp); + width = tmp[0]; + GLX.glXQueryDrawable(display, (int) buffer, GL.GLX_HEIGHT, tmp); + height = tmp[0]; + + if (DEBUG) { + System.err.println("Created pbuffer " + width + " x " + height); + } + } + + protected synchronized boolean makeCurrent(Runnable initAction) throws GLException { + if (buffer == 0) { + // pbuffer not instantiated yet + return false; + } + + lockAWT(); + try { + boolean created = false; + if (context == 0) { + create(); + if (DEBUG) { + System.err.println("!!! Created GL context for " + getClass().getName()); + } + created = true; + } + + // FIXME: this cast to int would be wrong on 64-bit platforms + // where the argument type to glXMakeCurrent would change (should + // probably make GLXDrawable, and maybe XID, Opaque as long) + if (!GLX.glXMakeContextCurrent(display, (int) buffer, (int) buffer, context)) { + throw new GLException("Error making context current"); + } + + if (created) { + resetGLFunctionAvailability(); + initAction.run(); + } + return true; + } finally { + unlockAWT(); + } + } + + protected synchronized void free() throws GLException { + lockAWT(); + try { + if (!GLX.glXMakeContextCurrent(display, 0, 0, 0)) { + throw new GLException("Error freeing OpenGL context"); + } + } finally { + unlockAWT(); + } + } + + public void handleModeSwitch(long parentHdc, long parentHglrc) { + throw new GLException("Not yet implemented"); + } + + protected boolean isOffscreen() { + // FIXME: currently the only caller of this won't cause proper + // resizing of the pbuffer anyway. + return false; + } + + public int getOffscreenContextBufferedImageType() { + throw new GLException("Should not call this"); + } + + public int getOffscreenContextReadBuffer() { + throw new GLException("Should not call this"); + } + + public boolean offscreenImageNeedsVerticalFlip() { + throw new GLException("Should not call this"); + } + + protected void create() { + if (DEBUG) { + System.err.println("Creating context for pbuffer " + width + " x " + height); + } + + // Create a gl context for the p-buffer. + // FIXME: provide option to not share display lists with subordinate pbuffer? + context = GLX.glXCreateNewContext(display, fbConfig, GL.GLX_RGBA_TYPE, parentContext, true); + if (context == 0) { + throw new GLException("pbuffer creation error: glXCreateNewContext() failed"); + } + + if (DEBUG) { + System.err.println("Created context for pbuffer " + width + " x " + height); + } + } + + protected void swapBuffers() throws GLException { + // FIXME: do we need to do anything if the pbuffer is double-buffered? + } + + private int queryFBConfig(long display, GLXFBConfig fbConfig, int attrib) { + int[] tmp = new int[1]; + if (GLX.glXGetFBConfigAttrib(display, fbConfig, attrib, tmp) != 0) { + throw new GLException("glXGetFBConfigAttrib failed"); + } + return tmp[0]; + } + + // These synchronization primitives, which prevent the AWT from + // making requests from the X server asynchronously to this code, + // are required for pbuffers to work properly on X11. + private void lockAWT() { + getJAWT().Lock(); + } + + private void unlockAWT() { + getJAWT().Unlock(); + } +} |