1 /* 2 * BSD 3-Clause License 3 * 4 * Copyright (c) 2016, Mango-Engine Team 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions are met: 9 * 10 * * Redistributions of source code must retain the above copyright notice, this 11 * list of conditions and the following disclaimer. 12 * 13 * * Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * * Neither the name of the copyright holder nor the names of its 18 * contributors may be used to endorse or promote products derived from 19 * this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 module mango_engine.graphics.opengl.gl_window; 33 34 import mango_engine.graphics.opengl.gl_backend; 35 import mango_engine.graphics.window; 36 import mango_engine.logging; 37 import mango_engine.game; 38 import mango_engine.event.core; 39 import mango_engine.event.input; 40 41 import blocksound.util : toCString, toDString; 42 43 import derelict.glfw3; 44 import derelict.opengl3.gl3 : glGetString, GL_VERSION, GL_RENDERER, GL_VENDOR; 45 46 import std.conv; 47 48 package __gshared GLWindow[GLFWwindow*] windows; 49 50 extern(C) void glwindow_glfwKeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) @system nothrow { 51 windows[window].onKey(key, scancode); 52 } 53 54 class GLWindow : Window { 55 private GLFWwindow* window; 56 private shared size_t keyEventCounter; 57 private shared KeyPressEvent[size_t] keyEvents; 58 59 this(in string title, in uint width, in uint height, SyncType syncType) @safe { 60 super(title, width, height, syncType); 61 gl_check(); 62 63 createWindow(); 64 } 65 66 private void createWindow() @trusted { 67 // Set OpenGL Information 68 glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); 69 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, MANGO_GL_VERSION_MAJOR); 70 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, MANGO_GL_VERSION_MINOR); 71 72 window = glfwCreateWindow(width, height, toCString(title), null, null); 73 if(!window) { 74 if(failedContext) { // From gl_backend 75 throw new WindowContextFailedException("Context Failed! Does the host machine support OpenGL " ~ MANGO_GL_VERSION_STRING ~ "?"); 76 } else 77 throw new Exception("Failed to create window!"); 78 } 79 80 glfwMakeContextCurrent(window); // Set our main OpenGL context 81 82 GLBackend.loadCoreMethods(); // Load the non-deprecated methods (core) 83 84 string glVersion = toDString(glGetString(GL_VERSION)); 85 // backendLogger: from gl_backend.d 86 (cast(Logger) backendLogger).logInfo("GL_VERSION: " ~ glVersion); 87 (cast(Logger) backendLogger).logInfo("GL_RENDERER: " ~ toDString(glGetString(GL_RENDERER))); 88 (cast(Logger) backendLogger).logInfo("GL_VENDOR: " ~ toDString(glGetString(GL_VENDOR))); 89 90 glfwSetKeyCallback(window, &glwindow_glfwKeyCallback); 91 } 92 93 package void onKey(int key, int scancode) @system nothrow { 94 import core.atomic; 95 keyEvents[atomicOp!"+="(keyEventCounter, 1)] = cast(shared) new KeyPressEvent(key, scancode); 96 } 97 98 private void evtHook_processInput(Event e) @system { 99 glfwPollEvents(); 100 101 size_t[] toRemove; 102 foreach(key, event; keyEvents) { 103 game.eventManager.fireEvent(cast(KeyPressEvent) event); 104 } 105 106 foreach(t; toRemove) 107 keyEvents.remove(t); 108 109 if(glfwWindowShouldClose(window)) { 110 game.stop(); 111 } 112 } 113 114 override { 115 protected void setGame_() @system { 116 game.eventManager.registerEventHook(TickEvent.classinfo.name, 117 EventHook(&this.evtHook_processInput, false) 118 ); 119 } 120 121 shared void updateBuffers() @system { 122 glfwSwapBuffers(cast(GLFWwindow*) window); 123 } 124 125 protected void setSync_(in SyncType syncType) @system { 126 final switch(syncType) { 127 case SyncType.SYNC_NONE: 128 glfwSwapInterval(0); 129 break; 130 case SyncType.SYNC_VSYNC: 131 glfwSwapInterval(1); 132 break; 133 case SyncType.SYNC_ADAPTIVE: 134 throw new Exception("Adaptive Sync not implemented!"); 135 } 136 } 137 138 protected void setTitle_(in string title) @system { 139 glfwSetWindowTitle(window, toCString(title)); 140 } 141 142 protected void resize_(in uint width, in uint height) @system { 143 glfwSetWindowSize(window, width, height); 144 } 145 } 146 }