Merge: gamnit: portable services to setup an OpenGL ES 2.0 display
authorJean Privat <jean@pryen.org>
Tue, 15 Sep 2015 03:08:47 +0000 (23:08 -0400)
committerJean Privat <jean@pryen.org>
Tue, 15 Sep 2015 03:08:47 +0000 (23:08 -0400)
Intro portable services to setup and use an OpenGL ES 2.0 display: `GamnitDisplay, setup, close, flip`. These services can be used standalone when importing `gamnit::display`, or integrated in `app.nit` by importing the whole `gamnit`.

This is an important step for Gamnit as it allows to develop portable games and will simplify building new graphic APIs gradually. However, some very useful services are still missing: textures loading, input events, full app life-cycle support (pause/resume), etc.

The display still depends on the same libraries as mnit (sdl, egl, native_app_glue, etc.), but it is much cleaner and changing the libs should be easier.

You may notice that the examples duplicate a lot of code between themselves and with `lib/glesv2/examples/opengles2_hello_triangle.nit`. All 3 examples are very useful to understand and debug the different abstraction levels, and even if they are pretty long, they are pretty much the shortest possible OpenGL ES 2.0 program I could write. I plan on keeping this duplication to keep these debugging tool, but I may move one of them (standalone) to a test folder as to not attract unwanted attention to it.

The first 3 commits are from #1708 but they are each required by either the examples or the Android support.

Pull-Request: #1709
Reviewed-by: Jean Privat <jean@pryen.org>

15 files changed:
lib/gamnit/display.nit [new file with mode: 0644]
lib/gamnit/display_android.nit [new file with mode: 0644]
lib/gamnit/display_linux.nit [new file with mode: 0644]
lib/gamnit/egl.nit [new file with mode: 0644]
lib/gamnit/examples/triangle/Makefile [new file with mode: 0644]
lib/gamnit/examples/triangle/art/icon.svg [new file with mode: 0644]
lib/gamnit/examples/triangle/bin/.gitignore [new file with mode: 0644]
lib/gamnit/examples/triangle/package.ini [new file with mode: 0644]
lib/gamnit/examples/triangle/res/.gitignore [new file with mode: 0644]
lib/gamnit/examples/triangle/src/portable_triangle.nit [new file with mode: 0644]
lib/gamnit/examples/triangle/src/standalone_triangle.nit [new file with mode: 0644]
lib/gamnit/gamnit.nit
lib/gamnit/gamnit_android.nit [new file with mode: 0644]
lib/linux/linux.nit
lib/mnit/linux/linux_app.nit

diff --git a/lib/gamnit/display.nit b/lib/gamnit/display.nit
new file mode 100644 (file)
index 0000000..94ece8a
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# 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.
+
+# Abstract display services
+module display
+
+import ::glesv2
+
+import display_linux is conditional(linux)
+import display_android is conditional(android)
+
+# Should Gamnit be more verbose?
+fun debug_gamnit: Bool do return false
+
+# General display class, is sized and drawable
+class GamnitDisplay
+
+       # Width of the display, in pixels
+       fun width: Int is abstract
+
+       # Height of the display, in pixels
+       fun height: Int is abstract
+
+       # Prepare this display
+       #
+       # The implementation varies per platform.
+       fun setup is abstract
+
+       # Close this display and free underlying resources
+       #
+       # The implementation varies per platform.
+       fun close do end
+
+       # Flip the display buffers
+       #
+       # The implementation varies per platform.
+       fun flip do end
+end
diff --git a/lib/gamnit/display_android.nit b/lib/gamnit/display_android.nit
new file mode 100644 (file)
index 0000000..d0e5235
--- /dev/null
@@ -0,0 +1,47 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# 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.
+
+# Gamnit display implementation for Android
+#
+# Generated APK files require OpenGL ES 2.0.
+#
+# This modules uses `android::native_app_glue` and the Android NDK.
+module display_android is
+       android_manifest """<uses-feature android:glEsVersion="0x00020000"/>"""
+end
+
+import ::android
+
+private import gamnit::egl
+
+redef class GamnitDisplay
+
+       redef fun setup
+       do
+               var native_display = egl_default_display
+               var native_window = app.native_app_glue.window
+
+               setup_egl_display native_display
+
+               # We need 8 bits per color for selection by color
+               select_egl_config(8, 8, 8, 0, 8, 0, 0)
+
+               var format = egl_config.attribs(egl_display).native_visual_id
+               native_window.set_buffers_geometry(0, 0, format)
+
+               setup_egl_context native_window
+       end
+
+       redef fun close do close_egl
+end
diff --git a/lib/gamnit/display_linux.nit b/lib/gamnit/display_linux.nit
new file mode 100644 (file)
index 0000000..109ff51
--- /dev/null
@@ -0,0 +1,91 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# 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.
+
+# Gamnit display implementation for GNU/Linux using `egl`, `sdl` and `x11`
+module display_linux
+
+import sdl
+import x11
+
+import egl # local to gamnit
+import display
+
+redef class GamnitDisplay
+
+       # Actual width or desired width of the window, can be set before calling `setup`
+       fun width=(value: Int) do requested_width = value
+       private var requested_width = 1920
+
+       # Actual height or desired height of the window, can be set before calling `setup`
+       fun height=(value: Int) do requested_height = value
+       private var requested_height = 1080
+
+       # Setup SDL, X11, EGL in order
+       redef fun setup
+       do
+               if debug_gamnit then print "Setting up SDL"
+               self.sdl_display = setup_sdl(requested_width, requested_height)
+
+               if debug_gamnit then print "Setting up X11"
+               var x11_display = setup_x11
+               var window_handle = window_handle
+               setup_egl_display x11_display
+
+               if debug_gamnit then print "Setting up EGL context"
+               select_egl_config(8, 8, 8, 8, 8, 0, 0)
+               setup_egl_context window_handle
+       end
+
+       # Close EGL and SDL in reverse order of `setup` (nothing to do for X11)
+       redef fun close
+       do
+               close_egl
+               close_sdl
+       end
+
+       # ---
+       # SDL
+
+       # The SDL display managing the window and events
+       var sdl_display: SDLDisplay is noautoinit
+
+       # Setup the SDL display and lib
+       fun setup_sdl(window_width, window_height: Int): SDLDisplay
+       do
+               var sdl_display = new SDLDisplay(window_width, window_height)
+               assert not sdl_display.address_is_null else print "Opening SDL display failed"
+               return sdl_display
+       end
+
+       # Close the SDL display
+       fun close_sdl do sdl_display.destroy
+
+       # Get a native handle to the current SDL window
+       fun window_handle: Pointer
+       do
+               var sdl_wm_info = new SDLSystemWindowManagerInfo
+               return sdl_wm_info.x11_window_handle
+       end
+
+       # ---
+       # X11
+
+       # Get a native handle to the current X11 display
+       fun setup_x11: Pointer
+       do
+               var x11_display = x_open_default_display
+               assert not x11_display.address_is_null else print "Opening X11 display failed"
+               return x11_display
+       end
+end
diff --git a/lib/gamnit/egl.nit b/lib/gamnit/egl.nit
new file mode 100644 (file)
index 0000000..3fcb8c8
--- /dev/null
@@ -0,0 +1,115 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# 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.
+
+# Use of EGL to implement Gamnit on GNU/Linux and Android
+module egl
+
+import ::egl
+
+import gamnit::display
+
+redef class GamnitDisplay
+
+       # The EGL display
+       var egl_display: EGLDisplay is noautoinit
+
+       # The EGL context
+       var egl_context: EGLContext is noautoinit
+
+       # The EGL surface for the window
+       var window_surface: EGLSurface is noautoinit
+
+       # The selected EGL configuration
+       var egl_config: EGLConfig is noautoinit
+
+       # Setup the EGL display for the given `x11_display`
+       protected fun setup_egl_display(x11_display: Pointer)
+       do
+               var egl_display = new EGLDisplay(x11_display)
+               assert egl_display.is_valid else print "new EGL display is not valid"
+
+               egl_display.initialize
+               assert egl_display.is_valid else print "EGL initialize error: {egl_display.error}"
+
+               self.egl_display = egl_display
+       end
+
+       # Select an EGL config
+       protected fun select_egl_config(blue, green, red, alpha, depth, stencil, sample: Int)
+       do
+               var config_chooser = new EGLConfigChooser
+               config_chooser.renderable_type_egl
+               config_chooser.surface_type_egl
+               config_chooser.blue_size = blue
+               config_chooser.green_size = green
+               config_chooser.red_size = red
+               if alpha > 0 then config_chooser.alpha_size = alpha
+               if depth > 0 then config_chooser.depth_size = depth
+               if stencil > 0 then config_chooser.stencil_size = stencil
+               if sample > 0 then config_chooser.sample_buffers = sample
+               config_chooser.close
+
+               var configs = config_chooser.choose(egl_display)
+               assert configs != null else print "Choosing EGL config failed: {egl_display.error}"
+               assert not configs.is_empty else print "Found no EGL config"
+
+               if debug_gamnit then
+                       print "EGL available configurations:"
+                       for config in configs do
+                               var attribs = config.attribs(egl_display)
+                               print "* Conformant to: {attribs.conformant}"
+                               print "  Caveats: {attribs.caveat}"
+                               print "  Size of RGBA: {attribs.red_size} {attribs.green_size} {attribs.blue_size} {attribs.alpha_size}"
+                               print "  Buffer, depth, stencil: {attribs.buffer_size} {attribs.depth_size} {attribs.stencil_size}"
+                       end
+               end
+
+               # We use the first one, it is recommended
+               self.egl_config = configs.first
+       end
+
+       # Setup the EGL context for the given `window_handle`
+       protected fun setup_egl_context(window_handle: Pointer)
+       do
+               var window_surface = egl_display.create_window_surface(egl_config, window_handle, [0])
+               assert window_surface.is_ok else print "Creating EGL window surface failed: {egl_display.error}"
+               self.window_surface = window_surface
+
+               egl_context = egl_display.create_context(egl_config)
+               assert egl_context.is_ok else print "Creating EGL context failed: {egl_display.error}"
+
+               var make_current_res = egl_display.make_current(window_surface, window_surface, egl_context)
+               assert make_current_res else print "Creating EGL make current failed: {egl_display.error}"
+
+               # TODO make the API selection configurable per platform
+               assert egl_bind_opengl_es_api else print "EGL bind API failed: {egl_display.error}"
+       end
+
+       redef fun width do return window_surface.attribs(egl_display).width
+
+       redef fun height do return window_surface.attribs(egl_display).height
+
+       # Close the EGL context
+       fun close_egl
+       do
+               egl_display.make_current(new EGLSurface.none, new EGLSurface.none, new EGLContext.none)
+               egl_display.destroy_context(egl_context)
+               egl_display.destroy_surface(window_surface)
+       end
+
+       redef fun flip
+       do
+               egl_display.swap_buffers(window_surface)
+       end
+end
diff --git a/lib/gamnit/examples/triangle/Makefile b/lib/gamnit/examples/triangle/Makefile
new file mode 100644 (file)
index 0000000..22172de
--- /dev/null
@@ -0,0 +1,21 @@
+NITC=../../../../bin/nitc
+NITLS=../../../../bin/nitls
+
+all: bin/standalone_triangle bin/triangle bin/triangle.apk
+
+bin/standalone_triangle: $(shell ${NITLS} -M src/standalone_triangle.nit linux) ${NITC}
+       ${NITC} src/standalone_triangle.nit -m linux -o $@
+
+bin/triangle: $(shell ${NITLS} -M src/portable_triangle.nit linux) ${NITC}
+       ${NITC} src/portable_triangle.nit -m linux -o $@
+
+check: bin/standalone_triangle bin/triangle
+       bin/standalone_triangle
+       bin/triangle
+
+android: bin/triangle.apk
+bin/triangle.apk: $(shell ${NITLS} -M src/portable_triangle.nit android) ${NITC} res/drawable-hdpi/icon.png
+       ${NITC} src/portable_triangle.nit -m android -o $@
+
+res/drawable-hdpi/icon.png: art/icon.svg
+       ../../../../contrib/inkscape_tools/bin/svg_to_icons --out res --android art/icon.svg
diff --git a/lib/gamnit/examples/triangle/art/icon.svg b/lib/gamnit/examples/triangle/art/icon.svg
new file mode 100644 (file)
index 0000000..26f7c04
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="512"
+   height="512"
+   id="svg2"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="New document 1">
+  <defs
+     id="defs4" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="292.54393"
+     inkscape:cy="311.81525"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     showgrid="false"
+     inkscape:window-width="1279"
+     inkscape:window-height="1379"
+     inkscape:window-x="3840"
+     inkscape:window-y="27"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(0,-540.36218)">
+    <rect
+       style="fill:#ab00cf;fill-opacity:1;stroke:none"
+       id="rect2985"
+       width="512"
+       height="512"
+       x="0"
+       y="540.36218"
+       ry="69.507881"
+       rx="69.507881" />
+    <path
+       sodipodi:type="star"
+       style="fill:#ff001b;fill-opacity:1;stroke:none"
+       id="path3755"
+       sodipodi:sides="3"
+       sodipodi:cx="22.857143"
+       sodipodi:cy="-299.06638"
+       sodipodi:r1="235.13782"
+       sodipodi:r2="118.85303"
+       sodipodi:arg1="-1.5707963"
+       sodipodi:arg2="-0.52359875"
+       inkscape:flatsided="false"
+       inkscape:rounded="0"
+       inkscape:randomized="0"
+       d="m 22.85715,-534.20419 102.92974,175.7113 100.70557,176.99543 -203.63532,1.28411 -203.63532,-1.28412 100.705584,-176.99542 z"
+       inkscape:transform-center-y="-22.453664"
+       transform="translate(232.85714,1144.2857)" />
+  </g>
+</svg>
diff --git a/lib/gamnit/examples/triangle/bin/.gitignore b/lib/gamnit/examples/triangle/bin/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/lib/gamnit/examples/triangle/package.ini b/lib/gamnit/examples/triangle/package.ini
new file mode 100644 (file)
index 0000000..f673878
--- /dev/null
@@ -0,0 +1,11 @@
+[package]
+name=triangle
+tags=example
+maintainer=Alexis Laferrière <alexis.laf@xymus.net>
+license=Apache-2.0
+[upstream]
+browse=https://github.com/nitlang/nit/tree/master/lib/gamnit/examples/triangle/
+git=https://github.com/nitlang/nit.git
+git.directory=lib/gamnit/examples/triangle/
+homepage=http://nitlanguage.org
+issues=https://github.com/nitlang/nit/issues
diff --git a/lib/gamnit/examples/triangle/res/.gitignore b/lib/gamnit/examples/triangle/res/.gitignore
new file mode 100644 (file)
index 0000000..72e8ffc
--- /dev/null
@@ -0,0 +1 @@
+*
diff --git a/lib/gamnit/examples/triangle/src/portable_triangle.nit b/lib/gamnit/examples/triangle/src/portable_triangle.nit
new file mode 100644 (file)
index 0000000..ac8e0e6
--- /dev/null
@@ -0,0 +1,138 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Portable example of using Gamnit with custom calls to OpenGL ES 2.0
+#
+# References:
+# * The book OpenGL ES 2.0 Programming Guide
+# * https://code.google.com/p/opengles-book-samples/source/browse/trunk/LinuxX11/Chapter_2/Hello_Triangle/Hello_Triangle.c
+module portable_triangle is
+       app_name "gamnit Triangle"
+       app_namespace "org.nitlanguage.triangle"
+       app_version(1, 0, git_revision)
+end
+
+import gamnit
+
+redef class App
+
+       # Our only program for the graphic card
+       var program: GLProgram is noautoinit
+
+       # The only vertex sharder
+       var vertex_shader: GLVertexShader is noautoinit
+
+       # The only fragment sharder
+       var fragment_shader: GLFragmentShader is noautoinit
+
+       # Vertex data for the triangle
+       var vertex_array: VertexArray is noautoinit
+
+       redef fun on_create
+       do
+               super
+
+               var display = display
+               assert display != null
+
+               print "Width: {display.width}"
+               print "Height: {display.height}"
+
+               assert_no_gl_error
+               assert gl.shader_compiler else print "Cannot compile shaders"
+
+               # GL program
+               program = new GLProgram
+               if not program.is_ok then
+                       print "Program is not ok: {gl.error.to_s}\nLog:"
+                       print program.info_log
+                       abort
+               end
+               assert_no_gl_error
+
+               # Vertex shader
+               vertex_shader = new GLVertexShader
+               assert vertex_shader.is_ok else print "Vertex shader is not ok: {gl.error}"
+               vertex_shader.source = """
+               attribute vec4 vPosition;
+               void main()
+               {
+                 gl_Position = vPosition;
+               }
+               """@glsl_vertex_shader.to_cstring
+               vertex_shader.compile
+               assert vertex_shader.is_compiled else print "Vertex shader compilation failed with: {vertex_shader.info_log} {program.info_log}"
+               assert_no_gl_error
+
+               # Fragment shader
+               fragment_shader = new GLFragmentShader
+               assert fragment_shader.is_ok else print "Fragment shader is not ok: {gl.error}"
+               fragment_shader.source = """
+               precision mediump float;
+               void main()
+               {
+                       gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+               }
+               """@glsl_fragment_shader.to_cstring
+               fragment_shader.compile
+               assert fragment_shader.is_compiled else print "Fragment shader compilation failed with: {fragment_shader.info_log}"
+               assert_no_gl_error
+
+               # Attach to program
+               program.attach_shader vertex_shader
+               program.attach_shader fragment_shader
+               program.bind_attrib_location(0, "vPosition")
+               program.link
+               assert program.is_linked else print "Linking failed: {program.info_log}"
+               assert_no_gl_error
+
+               # Draw!
+               var vertices = [0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0]
+               vertex_array = new VertexArray(0, 3, vertices)
+               vertex_array.attrib_pointer
+       end
+
+       redef fun frame_core
+       do
+               var display = display
+               if display != null then
+                       gl.clear_color(0.5, 0.0, 0.5, 1.0)
+
+                       assert_no_gl_error
+                       gl.viewport(0, 0, display.width, display.height)
+                       gl.clear((new GLBuffer).color)
+                       program.use
+                       vertex_array.enable
+
+                       glDrawArrays(new GLDrawMode.triangles, 0, 3)
+
+                       display.flip
+               end
+       end
+
+       redef fun on_stop
+       do
+               # Clean up
+               program.delete
+               vertex_shader.delete
+               fragment_shader.delete
+
+               # Close gamnit
+               var display = display
+               if display != null then display.close
+       end
+end
+
+if "NIT_TESTING".environ == "true" then exit(0)
+super
diff --git a/lib/gamnit/examples/triangle/src/standalone_triangle.nit b/lib/gamnit/examples/triangle/src/standalone_triangle.nit
new file mode 100644 (file)
index 0000000..04e14db
--- /dev/null
@@ -0,0 +1,112 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Example of using `GamnitDisplay` to setup a screen for custom calls to OpenGL ES 2.0
+#
+# This example does not support the lifecycle of mobile platforms, as such it only works on desktop computers.
+#
+# References:
+# * The book OpenGL ES 2.0 Programming Guide
+# * https://code.google.com/p/opengles-book-samples/source/browse/trunk/LinuxX11/Chapter_2/Hello_Triangle/Hello_Triangle.c
+module standalone_triangle
+
+import app
+import gamnit::display
+
+if "NIT_TESTING".environ == "true" then exit(0)
+
+# Setup gamnit
+var display = new GamnitDisplay
+display.setup
+
+var width = display.width
+var height = display.height
+print "Width: {width}"
+print "Height: {height}"
+
+# Custom calls to OpenGL ES 2.0
+assert_no_gl_error
+assert gl.shader_compiler else print "Cannot compile shaders"
+
+# GL program
+print gl.error.to_s
+var program = new GLProgram
+if not program.is_ok then
+       print "Program is not ok: {gl.error.to_s}\nLog:"
+       print program.info_log
+       abort
+end
+assert_no_gl_error
+
+# Vertex shader
+var vertex_shader = new GLVertexShader
+assert vertex_shader.is_ok else print "Vertex shader is not ok: {gl.error}"
+vertex_shader.source = """
+attribute vec4 vPosition;
+void main()
+{
+  gl_Position = vPosition;
+}
+"""@glsl_vertex_shader.to_cstring
+vertex_shader.compile
+assert vertex_shader.is_compiled else print "Vertex shader compilation failed with: {vertex_shader.info_log} {program.info_log}"
+assert_no_gl_error
+
+# Fragment shader
+var fragment_shader = new GLFragmentShader
+assert fragment_shader.is_ok else print "Fragment shader is not ok: {gl.error}"
+fragment_shader.source = """
+precision mediump float;
+void main()
+{
+       gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+}
+"""@glsl_fragment_shader.to_cstring
+fragment_shader.compile
+assert fragment_shader.is_compiled else print "Fragment shader compilation failed with: {fragment_shader.info_log}"
+assert_no_gl_error
+
+# Attach to program
+program.attach_shader vertex_shader
+program.attach_shader fragment_shader
+program.bind_attrib_location(0, "vPosition")
+program.link
+assert program.is_linked else print "Linking failed: {program.info_log}"
+assert_no_gl_error
+
+# Draw!
+var vertices = [0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0]
+var vertex_array = new VertexArray(0, 3, vertices)
+vertex_array.attrib_pointer
+gl.clear_color(0.5, 0.0, 0.5, 1.0)
+for i in [0..1000[ do
+       printn "."
+       assert_no_gl_error
+       gl.viewport(0, 0, width, height)
+       gl.clear((new GLBuffer).color)
+       program.use
+       vertex_array.enable
+
+       glDrawArrays(new GLDrawMode.triangles, 0, 3)
+
+       display.flip
+end
+
+# Clean up
+program.delete
+vertex_shader.delete
+fragment_shader.delete
+
+# Close gamnit
+display.close
index 2fa5b8e..43ec95c 100644 (file)
 
 # Game and multimedia framework for Nit
 module gamnit
+
+import app
+
+import display
+
+import gamnit_android is conditional(android)
+
+redef class App
+
+       # Main `GamnitDisplay` initialized by `on_create`
+       var display: nullable GamnitDisplay = null
+
+       redef fun on_create
+       do
+               super
+
+               var display = new GamnitDisplay
+               display.setup
+               self.display = display
+       end
+
+       # Core of the frame logic, executed only when the display is visible
+       #
+       # This method should be redefined by user modules to customize the behavior of the game.
+       protected fun frame_core do end
+
+       # Full frame logic, executed even if the display is not visible
+       #
+       # This method wraps `frame_core` and other services to be executed in the main app loop.
+       #
+       # To customize the behavior on each turn, it is preferable to redefined `frame_core`.
+       # Still, `frame_full` can be redefined with care for more control.
+       protected fun frame_full
+       do
+               var display = display
+               if display != null then frame_core
+
+               feed_events
+       end
+
+       redef fun run
+       do
+               # TODO manage exit condition
+               loop frame_full
+       end
+
+       # Loop on available events and feed them back to the app
+       #
+       # The implementation varies per platform.
+       private fun feed_events do end
+end
diff --git a/lib/gamnit/gamnit_android.nit b/lib/gamnit/gamnit_android.nit
new file mode 100644 (file)
index 0000000..647c741
--- /dev/null
@@ -0,0 +1,24 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# 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.
+
+# Support services for Gamnit on Android
+module gamnit_android
+
+import android
+
+intrude import gamnit
+
+redef class App
+       redef fun feed_events do app.poll_looper 0
+end
index 8b90102..04a0c5d 100644 (file)
 module linux
 
 import app
+
+redef class App
+       redef fun setup
+       do
+               super
+
+               on_create
+               on_restore_state
+               on_start
+               on_resume
+       end
+
+       redef fun run
+       do
+               super
+
+               on_pause
+               on_save_state
+               on_stop
+               on_destroy
+       end
+end
index 938bfd8..7bc03a0 100644 (file)
@@ -35,21 +35,6 @@ redef class App
                display = new Opengles1Display
 
                super
-
-               on_create
-               on_restore_state
-               on_start
-               on_resume
-       end
-
-       redef fun run
-       do
-               super
-
-               on_pause
-               on_save_state
-               on_stop
-               on_destroy
        end
 
        redef fun generate_input