diff --git a/Cargo.toml b/Cargo.toml index f7dd0b7d..236cf4e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "gl", "gl_generator", + "webgl", "webgl_generator", "tests/test_add_registries", "tests/test_gen_symbols", diff --git a/tests/test_webgl_stdweb/build.rs b/tests/test_webgl_stdweb/build.rs index 2820cb03..c6ae9e0a 100644 --- a/tests/test_webgl_stdweb/build.rs +++ b/tests/test_webgl_stdweb/build.rs @@ -23,7 +23,7 @@ fn main() { let dest = env::var("OUT_DIR").unwrap(); let mut file = File::create(&Path::new(&dest).join("test_webgl_stdweb.rs")).unwrap(); - Registry::new(Api::WebGl2) + Registry::new(Api::WebGl2, Exts::ALL) .write_bindings(StdwebGenerator, &mut file) .unwrap(); } diff --git a/tests/test_webgl_stdweb/lib.rs b/tests/test_webgl_stdweb/lib.rs index 6e2e8590..b0b64746 100644 --- a/tests/test_webgl_stdweb/lib.rs +++ b/tests/test_webgl_stdweb/lib.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#![allow(unused_parens)] +#![allow(unused_parens, non_camel_case_types)] #[macro_use] extern crate stdweb as _stdweb; diff --git a/webgl/Cargo.toml b/webgl/Cargo.toml new file mode 100644 index 00000000..2505e40f --- /dev/null +++ b/webgl/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "webgl_stdweb" +version = "0.1.0" +authors = [ + "Diggory Blake", +] +description = "WebGL bindings (stdweb)" +license = "Apache-2.0" +build = "build.rs" +documentation = "https://docs.rs/webgl_stdweb" +homepage = "https://github.com/brendanzab/gl-rs/webgl/" +repository = "https://github.com/brendanzab/gl-rs/" +readme = "README.md" +categories = ["api-bindings", "rendering::graphics-api"] +keywords = ["webgl", "stdweb"] + +[build-dependencies] +webgl_generator = { version = "0.1.0", path = "../webgl_generator" } + +[dependencies] +stdweb = { path = "../../stdweb" } +serde = "1.0.0" +serde_derive = "1.0.0" diff --git a/webgl/README.md b/webgl/README.md new file mode 100644 index 00000000..261a0fb7 --- /dev/null +++ b/webgl/README.md @@ -0,0 +1,3 @@ +# webgl-stdweb + +WebGL bindings using stdweb diff --git a/webgl/build.rs b/webgl/build.rs new file mode 100644 index 00000000..12fd1785 --- /dev/null +++ b/webgl/build.rs @@ -0,0 +1,29 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate webgl_generator; + +use webgl_generator::*; +use std::env; +use std::fs::File; +use std::path::*; + +fn main() { + let dest = env::var("OUT_DIR").unwrap(); + let mut file = File::create(&Path::new(&dest).join("bindings.rs")).unwrap(); + + Registry::new(Api::WebGl2, Exts::ALL) + .write_bindings(StdwebGenerator, &mut file) + .unwrap(); +} diff --git a/webgl/src/lib.rs b/webgl/src/lib.rs new file mode 100644 index 00000000..a96b0381 --- /dev/null +++ b/webgl/src/lib.rs @@ -0,0 +1,24 @@ +// Copyright 2015 Brendan Zabarauskas and the gl-rs developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(unused_parens, non_camel_case_types)] +#![crate_name = "webgl_stdweb"] +#![crate_type = "lib"] + +#[macro_use] +extern crate stdweb as _stdweb; +#[macro_use] +extern crate serde_derive as _serde_derive; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/webgl_generator/Cargo.toml b/webgl_generator/Cargo.toml index 18f4c0a9..fbbb988e 100644 --- a/webgl_generator/Cargo.toml +++ b/webgl_generator/Cargo.toml @@ -21,3 +21,7 @@ path = "lib.rs" khronos_api = { version = "2.0.0", path = "../khronos_api" } webidl = { version = "0.4.1" } heck = { version = "0.3.0" } +serde-xml-rs = "0.2.1" +serde_derive = "1.0" +serde = "1.0" +regex = "0.2.5" \ No newline at end of file diff --git a/webgl_generator/lib.rs b/webgl_generator/lib.rs index 54ff3f84..11dc4a45 100644 --- a/webgl_generator/lib.rs +++ b/webgl_generator/lib.rs @@ -43,6 +43,8 @@ //! include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs")); //! ``` //! +#[macro_use] +extern crate serde_derive; mod webgl_generators; mod webgl_registry; diff --git a/webgl_generator/webgl_generators/stdweb_gen.rs b/webgl_generator/webgl_generators/stdweb_gen.rs index 20b140b5..1eb1bf1e 100644 --- a/webgl_generator/webgl_generators/stdweb_gen.rs +++ b/webgl_generator/webgl_generators/stdweb_gen.rs @@ -57,6 +57,7 @@ enum ArgWrapper { Optional(Box), Sequence(Box), DoubleCast, + Once, } impl ArgWrapper { @@ -67,6 +68,7 @@ impl ArgWrapper { &ArgWrapper::Optional(ref inner) => format!("{}.map(|inner| {})", arg, inner.wrap("inner")), &ArgWrapper::Sequence(ref inner) => format!("{}.iter().map(|inner| {}).collect::>()", arg, inner.wrap("inner")), &ArgWrapper::DoubleCast => format!("({} as f64)", arg), + &ArgWrapper::Once => format!("Once({})", arg), } } } @@ -139,6 +141,11 @@ fn process_arg_type_kind(name: &str, type_kind: &TypeKind, registry: &Registry, let gp = gc.arg("T"); gc.constrain(format!("{}: JsSerializable", gp)); ProcessedArg::simple(gp) + }, + &TypeKind::Callback(ref _args, ref _return_type) => { + let gp = gc.arg("F"); + gc.constrain(format!("{}: FnOnce() + 'static", gp)); + ProcessedArg { type_: gp, wrapper: ArgWrapper::Once, optional: false } } } } @@ -218,7 +225,8 @@ fn process_result_type_kind(name: &str, type_kind: &TypeKind, registry: &Registr optional: inner.optional } }, - &TypeKind::Any | &TypeKind::Object => ProcessedResult::simple("Value") + &TypeKind::Any | &TypeKind::Object => ProcessedResult::simple("Value"), + &TypeKind::Callback(ref _args, ref _return_type) => unimplemented!() } } @@ -239,7 +247,7 @@ fn write_header(registry: &Registry, dest: &mut W) -> io::Result<()> where W: extern crate stdweb; extern crate serde; -use self::stdweb::{{Reference, Value, UnsafeTypedArray}}; +use self::stdweb::{{Reference, Value, UnsafeTypedArray, Once}}; use self::stdweb::private::{{ JsSerializable, FromReferenceUnchecked, @@ -266,6 +274,10 @@ pub trait AsTypedArray<'a, T> {{ unsafe fn as_typed_array(self) -> Self::Result; }} +pub trait Extension: TryFrom {{ + const NAME: &'static str; +}} + macro_rules! define_array {{ ($elem:ty) => {{ impl<'a> AsTypedArray<'a, $elem> for &'a TypedArray<$elem> {{ @@ -303,6 +315,7 @@ impl super::Generator for StdwebGenerator { write_enums(registry, dest)?; write_dictionaries(registry, dest)?; write_interfaces(registry, dest)?; + write_extensions(registry, dest)?; Ok(()) } } @@ -420,6 +433,14 @@ fn write_interface(name: &str, interface: &Interface, registry: &Registry, de return Ok(()); } + let instance_check = if name == "GLContext" { + "return [WebGLRenderingContext, WebGL2RenderingContext].includes(Module.STDWEB.acquire_js_reference( $0 ).constructor) | 0;".into() + } else if interface.has_class { + format!("return (Module.STDWEB.acquire_js_reference( $0 ) instanceof {}) | 0;", name) + } else { + format!("return (Module.STDWEB.acquire_js_reference( $0 ).constructor.name == {:?}) | 0;", name) + }; + write!(dest, r#" #[derive(Debug, Clone)] pub struct {name}(Reference); @@ -435,7 +456,7 @@ impl FromReference for {name} {{ fn from_reference(reference: Reference) -> Option {{ if {{ __js_raw_asm!( - "return (Module.STDWEB.acquire_js_reference( $0 ) instanceof {name}) | 0;", + {instance_check:?}, reference.as_raw() ) == 1 }} {{ @@ -500,7 +521,7 @@ impl JsSerializable for {name} {{ __js_serializable_boilerplate!(() ({name}) ()); impl {name} {{ - "#, name=name)?; + "#, name=name, instance_check=instance_check)?; for (name, members) in interface.collect_members(registry, &VisitOptions::default()) { for (index, member) in members.into_iter().enumerate() { @@ -586,7 +607,20 @@ fn write_attribute(name: &str, attribute: &Attribute, registry: &Registry, de Ok(()) } +fn write_get_extension(dest: &mut W) -> io::Result<()> where W: io::Write { + write!(dest, r#" + + pub fn get_extension(&self) -> Option {{ + (js! {{ return @{{self}}.getExtension({{E::NAME}}); }} ).try_into().ok() + }}"#) +} + fn write_operation(name: &str, index: usize, operation: &Operation, registry: &Registry, dest: &mut W) -> io::Result<()> where W: io::Write { + match name { + "getExtension" => return write_get_extension(dest), + _ => {} + } + let mut rust_name = unreserve(snake(name)); if index > 0 { rust_name = format!("{}_{}", rust_name, index); @@ -643,3 +677,18 @@ fn write_operation(name: &str, index: usize, operation: &Operation, registry: } Ok(()) } + +fn write_extensions(registry: &Registry, dest: &mut W) -> io::Result<()> where W: io::Write { + for name in ®istry.extensions { + write_extension(name, registry, dest)?; + } + Ok(()) +} + +fn write_extension(name: &str, _registry: &Registry, dest: &mut W) -> io::Result<()> where W: io::Write { + writeln!(dest, r#" +impl Extension for {name} {{ + const NAME: &'static str = "{name}"; +}}"#, + name=name) +} diff --git a/webgl_generator/webgl_registry/mod.rs b/webgl_generator/webgl_registry/mod.rs index 93378f27..d74c9c02 100644 --- a/webgl_generator/webgl_registry/mod.rs +++ b/webgl_generator/webgl_registry/mod.rs @@ -1,6 +1,8 @@ extern crate webidl; extern crate heck; extern crate khronos_api; +extern crate serde_xml_rs; +extern crate regex; use std::{str, fmt, io}; use std::collections::{BTreeMap, BTreeSet}; @@ -28,6 +30,49 @@ pub enum Api { WebGl2, } +#[derive(Debug, Deserialize)] +#[serde(rename = "extension")] +struct ExtensionIDL { + pub name: String, + pub idl: String, +} + +pub enum Exts<'a> { + Include(&'a [&'a str]), + Exclude(&'a [&'a str]), +} + +impl<'a> Exts<'a> { + pub const NONE: Exts<'a> = Exts::Include(&[]); + pub const ALL: Exts<'a> = Exts::Exclude(&[]); + + fn enumerate(&self) -> Vec { + use self::serde_xml_rs::deserialize; + use self::regex::{Regex, RegexBuilder}; + + // The Khronos IDL files are... not quite right, so let's fix them up! + let enum_regex = Regex::new("([(, ])enum\\b").unwrap(); + let missing_semicolon_regex = RegexBuilder::new("^}$").multi_line(true).build().unwrap(); + let shared_callback_regex = Regex::new("\\bAcquireResourcesCallback\\b").unwrap(); + + let mut result = Vec::new(); + for &ext_xml in khronos_api::WEBGL_EXT_XML { + let mut ext: ExtensionIDL = deserialize(ext_xml).unwrap(); + if match self { + &Exts::Include(names) => names.contains(&&*ext.name), + &Exts::Exclude(names) => !names.contains(&&*ext.name) + } { + ext.idl = enum_regex.replace_all(&ext.idl, "${1}GLenum").into(); + ext.idl = missing_semicolon_regex.replace_all(&ext.idl, "};").into(); + ext.idl = shared_callback_regex.replace_all(&ext.idl, "AcquireSharedResourcesCallback").into(); + result.push(ext); + } + } + + result + } +} + impl Api { fn idl_consts(&self) -> &'static [&'static [u8]] { match *self { @@ -106,6 +151,7 @@ pub enum TypeKind { Typedef(Type), Any, Object, + Callback(Vec, Option), } impl TypeKind { @@ -119,13 +165,13 @@ impl TypeKind { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Const { pub type_: Type, pub value: String, } -#[derive(Debug, Eq)] +#[derive(Debug, Eq, Clone)] pub struct Argument { pub name: String, pub optional: bool, @@ -139,32 +185,33 @@ impl PartialEq for Argument { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Operation { pub args: Vec, pub return_type: Option } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub struct Attribute { pub type_: Type, pub setter: bool, pub getter: bool, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum Member { Const(Const), Operation(Operation), Attribute(Attribute), } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Interface { pub inherits: Option, pub mixins: BTreeSet, pub members: BTreeMap>, pub is_hidden: bool, + pub has_class: bool, pub rendering_context: Option<&'static str>, } @@ -305,10 +352,11 @@ pub struct Registry { pub dictionaries: BTreeMap, pub enums: BTreeMap, pub types: BTreeMap, + pub extensions: BTreeSet, } impl Registry { - pub fn new(api: Api) -> Registry { + pub fn new(api: Api, exts: Exts) -> Registry { let mut result = Registry::default(); for idl_const in api.idl_consts() { @@ -317,6 +365,21 @@ impl Registry { } } + for ext in exts.enumerate() { + result.extensions.insert(ext.name); + for def in parse_defs(ext.idl.as_bytes()) { + result.load_definition(def); + } + } + + for name in RENDERING_CONTEXTS.into_iter().rev() { + if let Some(mut iface) = result.interfaces.get(name.1).cloned() { + iface.rendering_context = None; + result.interfaces.insert("GLContext".into(), iface); + break; + } + } + for &hidden_name in HIDDEN_NAMES { if let Some(interface) = result.interfaces.get_mut(hidden_name) { interface.is_hidden = true; @@ -448,6 +511,18 @@ impl Registry { } fn load_interface(&mut self, interface: ast::NonPartialInterface) { + fn has_attr(attrs: &Vec>, name: &str) -> bool { + use self::ast::ExtendedAttribute::*; + for attr in attrs { + if let NoArguments(ref other) = **attr { + if let &ast::Other::Identifier(ref n) = other { + return n == name; + } + } + } + false + } + let mut members = BTreeMap::new(); for (name, member) in interface.members.into_iter().flat_map(|m| { self.load_interface_member(m) @@ -460,6 +535,7 @@ impl Registry { mixins: BTreeSet::new(), members, is_hidden: false, + has_class: !has_attr(&interface.extended_attributes, "NoInterfaceObject"), rendering_context: None, }; @@ -493,7 +569,7 @@ impl Registry { fn load_type_inner(&mut self, kind: ast::TypeKind) -> Type { use self::ast::TypeKind::*; - let name = format!("{:?}", kind); + let mut name = format!("{:?}", kind); let type_kind = match kind { // Primitives @@ -550,6 +626,11 @@ impl Registry { Object => TypeKind::Object, _ => TypeKind::Any, }; + + if let TypeKind::Primitive(ref p) = type_kind { + name = p.name().into(); + } + self.load_type_kind(&name, type_kind) } @@ -596,6 +677,16 @@ impl Registry { self.enums.insert(enum_.name, Enum { variants }); } + fn load_callback(&mut self, callback: ast::Callback) { + use self::ast::ReturnType; + let args = callback.arguments.into_iter().map(|a| self.load_argument(a)).collect(); + let return_type = match callback.return_type { + ReturnType::NonVoid(t) => Some(self.load_type(*t)), + ReturnType::Void => None, + }; + self.load_type_kind(&callback.name, TypeKind::Callback(args, return_type)); + } + fn load_definition(&mut self, def: ast::Definition) { use self::ast::Definition::*; match def { @@ -605,6 +696,7 @@ impl Registry { Typedef(t) => self.load_typedef(t), Dictionary(ast::Dictionary::NonPartial(d)) => self.load_dictionary(d), Enum(e) => self.load_enum(e), + Callback(c) => self.load_callback(c), _ => {} } }