1 module hoekjed.kern.venster; 2 import bindbc.glfw; 3 import bindbc.opengl; 4 import hoekjed.kern; 5 import std.container.rbtree; 6 import std.conv : to; 7 8 struct ToetsInvoer { 9 int toets, toets_verwijzing, gebeurtenis, toevoeging; 10 } 11 12 struct MuisknopInvoer { 13 int knop, gebeurtenis, toevoeging; 14 } 15 16 struct MuisplekInvoer { 17 double x, y; 18 } 19 20 struct MuiswielInvoer { 21 double x, y; 22 } 23 24 alias ToetsTerugroeper = void delegate(ToetsInvoer invoer) nothrow; 25 alias MuisknopTerugroeper = void delegate(MuisknopInvoer invoer) nothrow; 26 alias MuisplekTerugroeper = void delegate(MuisplekInvoer invoer) nothrow; 27 alias MuiswielTerugroeper = void delegate(MuiswielInvoer invoer) nothrow; 28 29 enum Muissoort { 30 NORMAAL = GLFW_CURSOR_NORMAL, 31 GEVANGEN = GLFW_CURSOR_DISABLED, 32 ONZICHTBAAR = GLFW_CURSOR_HIDDEN 33 } 34 35 class Venster { 36 static public Venster[GLFWwindow* ] vensters; 37 package GLFWwindow* glfw_venster; 38 39 // Eigenschappen 40 string naam; 41 int breedte, hoogte; 42 Scherm scherm; 43 44 // Invoer 45 ToetsTerugroeper[] toetsTerugroepers = []; 46 MuisknopTerugroeper[] muisknopTerugroepers = []; 47 MuisplekTerugroeper[] muisplekTerugroepers = []; 48 MuiswielTerugroeper[] muiswielTerugroepers = []; 49 ToetsInvoer[] toetsInvoer = []; 50 MuisplekInvoer[] muisplekInvoer = []; 51 MuisknopInvoer[] muisknopInvoer = []; 52 MuiswielInvoer[] muiswielInvoer = []; 53 54 alias scherm this; 55 56 static void zetStandaardZichtbaar(bool zichtbaar) { 57 glfwWindowHint(GLFW_VISIBLE, zichtbaar); 58 } 59 60 static void zetStandaardRand(bool rand) { 61 glfwWindowHint(GLFW_DECORATED, rand); 62 } 63 64 static void zetStandaardDoorzichtig(bool doorzichtig) { 65 glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, doorzichtig); 66 } 67 68 void zetAchtergrondKleur(Vec!(4, float) kleur) { 69 glClearColor(kleur.x, kleur.y, kleur.z, kleur.w); 70 } 71 72 void zetMuissoort(Muissoort soort) { 73 glfwSetInputMode(glfw_venster, GLFW_CURSOR, soort); 74 } 75 76 this(string naam = "HoekjeD", int glfw_breedte = 1920 / 2, int glfw_hoogte = 1080 / 2) { 77 debug glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); 78 79 this.glfw_venster = glfwCreateWindow(glfw_breedte, glfw_hoogte, naam.ptr, null, null); 80 assert(glfw_venster !is null, "GLFW kon geen scherm aanmaken."); 81 82 Venster.vensters[glfw_venster] = this; 83 glfwMakeContextCurrent(glfw_venster); // VOEG TOE: Moet voor multithreading & meerdere vensters nog een oplossing vinden. 84 //glfwSwapInterval(0); Kan met 1 vsynch gebruiken. 85 86 glfwSetKeyCallback(glfw_venster, &venster_toets_terugroeper); 87 glfwSetMouseButtonCallback(glfw_venster, &venster_muisknop_terugroeper); 88 glfwSetCursorPosCallback(glfw_venster, &venster_muisplek_terugroeper); 89 glfwSetScrollCallback(glfw_venster, &venster_muiswiel_terugroeper); 90 // glfwSetWindowSizeCallback(glfw_venster, &venster_grootte_terugroeper); 91 glfwSetFramebufferSizeCallback(glfw_venster, &venster_grootte_terugroeper); 92 93 this.naam = naam; 94 glfwGetFramebufferSize(glfw_venster, &breedte, &hoogte); 95 this.scherm = Scherm(); 96 this.scherm.hervorm(Vec!(2, int)([0, 0]), Vec!(2, int)([breedte, hoogte])); 97 98 GLSupport gl_versie = loadOpenGL(); 99 assert(gl_versie == GLSupport.gl46, "GL laadt niet: " ~ gl_versie.to!string); 100 101 debug { 102 glEnable(GL_DEBUG_OUTPUT); 103 glDebugMessageCallback(&gl_fout_terugroeper, null); 104 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, 105 GL_DEBUG_SEVERITY_NOTIFICATION, 0, null, false); 106 } 107 108 glfwSetCursorPos(glfw_venster, 0, 0); 109 glEnable(GL_DEPTH_TEST); 110 } 111 112 void bekijk() { 113 glfwFocusWindow(glfw_venster); 114 } 115 116 void toon() { 117 glfwShowWindow(glfw_venster); 118 glClear(GL_COLOR_BUFFER_BIT); 119 glfwSwapBuffers(glfw_venster); 120 } 121 122 void verstop() { 123 glfwHideWindow(glfw_venster); 124 } 125 126 void verwerkInvoer() { 127 // Behoudt volgorde van invoer over alle terugroepers. 128 foreach (ToetsInvoer invoer; toetsInvoer) 129 foreach (ToetsTerugroeper terugroeper; toetsTerugroepers) 130 terugroeper(invoer); 131 132 foreach (MuisknopInvoer invoer; muisknopInvoer) 133 foreach (MuisknopTerugroeper terugroeper; muisknopTerugroepers) 134 terugroeper(invoer); 135 136 foreach (MuisplekInvoer invoer; muisplekInvoer) 137 foreach (MuisplekTerugroeper terugroeper; muisplekTerugroepers) 138 terugroeper(invoer); 139 140 foreach (MuiswielInvoer invoer; muiswielInvoer) 141 foreach (MuiswielTerugroeper terugroeper; muiswielTerugroepers) 142 terugroeper(invoer); 143 144 //PAS OP: neemt onafhankelijkheid van muis & toets volgorde aan op korte tijdsverschillen. 145 } 146 147 // PAS OP: Moet mogelijk testen wat de toevoeging is bij gebrek aan toevoeging of dubbele 148 // toevoegingen. Hier is de documentatie niet duidelijk. 149 public bool krijgToets(int toets) { 150 foreach (ToetsInvoer t; this.toetsInvoer) 151 if (t.toets == toets && (t.gebeurtenis == GLFW_PRESS || t.gebeurtenis == GLFW_REPEAT)) 152 return true; 153 return false; 154 } 155 156 void leegInvoer() { 157 toetsInvoer = []; 158 muisknopInvoer = []; 159 muisplekInvoer = []; 160 muiswielInvoer = []; 161 } 162 163 void teken() { 164 glViewport(0, 0, breedte, hoogte); // Zet het tekengebied. 165 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Verschoont het scherm. 166 scherm.teken(); 167 168 // VOEG TOE: 169 // Hebben een beter denksysteem nodig. Per ding of/en ook per scherm. 170 // VOEG TOE: alle denk functies in een globale lijst, voorkomt herhaalde instructies & maakt 171 // het mogelijk de "bijgewerkt" te verwijderen en "werkBij" sneller te maken door veranderde 172 // dingen in een globale set te plaatsen & hun en hun kinderen specifiek bij te werken. 173 174 // Mogelijk is het beter dit niet blobaal te doen maar per wereld, met werelden in een 175 // globale lijst, omdat het zo eenvoudiger is een wereld te pauzeren. 176 177 // Kan net als veel motoren dingen eigenschappen geven opdat het uitschakelen van een 178 // ding bijvoorbeeld eenvoudig door te voeren is naar het verwijderen van zijn denk opdracht(en). 179 glfwSwapBuffers(glfw_venster); 180 } 181 182 unittest { 183 import hoekjed.kern; 184 185 hdZetOp(); 186 Venster.zetStandaardDoorzichtig(true); 187 188 bool called = false; 189 ToetsTerugroeper foo = (ToetsInvoer invoer) { called = true; }; 190 Venster venster = new Venster(); 191 venster.toetsTerugroepers ~= foo; 192 193 venster_toets_terugroeper(venster.glfw_venster, 0, 0, 0, 0); 194 venster.verwerkInvoer(); 195 196 assert(called); 197 } 198 199 // VOEG TOE: glfw mogelijkheden, zoals openen/sluiten/focus/muis/toetsen (toetsen per scherm of venster?) 200 201 protected void hervorm() nothrow { 202 Vec!(2, int) lb = {[0, 0]}; 203 Vec!(2, int) grootte = {[breedte, hoogte]}; 204 scherm.hervorm(lb, grootte); 205 } 206 207 unittest { 208 import hoekjed.kern; 209 210 // TODO: 211 // hdZetOp(); 212 // Venster.zetStandaardZichtbaar(false); 213 // Venster venster = new Venster(); 214 // Vec!(2, int) ro = venster.scherm.rechtsonder; 215 // venster.breedte *= 2; 216 // venster.hervorm(); 217 // Vec!(2, int) ro2 = venster.scherm.rechtsonder; 218 // assert(ro2.x == 2 * ro.x && ro2.y == ro.y, 219 // "[2 * " ~ ro.x.to!string ~ ", " ~ ro.y.to!string ~ "] != " ~ ro2.to!string); 220 } 221 } 222 223 import hoekjed.dingen.zicht; 224 225 // VERBETER: Vensters, Schremen & Zichten volledig herwerken. 226 227 struct Scherm { 228 Vec!2 linksboven_f = {[0, 0]}; 229 Vec!2 rechtsonder_f = {[1, 1]}; 230 Vec!(2, int) linksboven; 231 Vec!(2, int) rechtsonder; 232 233 Scherm[] deelschermen; 234 Wereld wereld; 235 Zicht zicht; // In principe kan deze uit een andere wereld komen. . . Parallele werelden? 236 237 void teken() { 238 foreach (Scherm scherm; deelschermen) 239 scherm.teken(); 240 if (zicht is null) 241 return; 242 glViewport(linksboven.x, linksboven.y, rechtsonder.x, rechtsonder.y); // Zet het tekengebied. 243 if (deelschermen.length != 0) 244 glClear(GL_DEPTH_BUFFER_BIT); // Over deelscherm heen tekenen. 245 246 wereld.tekenWereld(zicht); 247 } 248 249 protected void hervorm(Vec!(2, int) lb, Vec!(2, int) grootte) nothrow { 250 linksboven = lb + cast(Vec!(2, int))(linksboven_f * grootte); 251 rechtsonder = lb + cast(Vec!(2, int))(rechtsonder_f * grootte); 252 if (deelschermen.length != 0) { 253 Vec!(2, int) eigen_grootte = rechtsonder - linksboven; 254 foreach (Scherm scherm; deelschermen) { 255 scherm.hervorm(linksboven, eigen_grootte); 256 } 257 } 258 } 259 260 unittest { 261 // TODO: 262 // Scherm s = {rechtsonder_f: {[0.25, 0.5]}}; 263 // Vec!(2, int) a = {[5, 5]}; 264 // Vec!(2, int) b = {[1, 2]}; 265 // s.hervorm(a, b); 266 // assert(s.rechtsonder.x == 5 && s.rechtsonder.y == 6); 267 } 268 } 269 270 extern (C) void venster_grootte_terugroeper(GLFWwindow* glfw_venster, int breedte, int hoogte) nothrow { 271 Venster venster = Venster.vensters[glfw_venster]; 272 venster.breedte = breedte; 273 venster.hoogte = hoogte; 274 venster.hervorm(); 275 } 276 277 extern (C) void venster_toets_terugroeper(GLFWwindow* glfw_venster, int toets, 278 int toets_sleutel, int gebeurtenis, int toevoeging) nothrow { 279 debug { 280 import core.sys.windows.windows; 281 282 if (toets == GLFW_KEY_GRAVE_ACCENT) { 283 ShowWindow(console, _console_zichtbaar ? SW_HIDE : SW_RESTORE); 284 glfwFocusWindow(glfw_venster); 285 _console_zichtbaar = !_console_zichtbaar; 286 } 287 } 288 if (toets == GLFW_KEY_ESCAPE) 289 glfwSetWindowShouldClose(glfw_venster, true); 290 291 Venster venster = Venster.vensters[glfw_venster]; 292 ToetsInvoer invoer = ToetsInvoer(toets, toets_sleutel, gebeurtenis, toevoeging); 293 venster.toetsInvoer ~= invoer; 294 } 295 296 extern (C) void venster_muisknop_terugroeper(GLFWwindow* glfw_venster, int knop, 297 int gebeurtenis, int toevoeging) nothrow { 298 Venster venster = Venster.vensters[glfw_venster]; 299 MuisknopInvoer invoer = MuisknopInvoer(knop, gebeurtenis, toevoeging); 300 venster.muisknopInvoer ~= invoer; 301 } 302 303 extern (C) void venster_muisplek_terugroeper(GLFWwindow* glfw_venster, double x, double y) nothrow { 304 Venster venster = Venster.vensters[glfw_venster]; 305 MuisplekInvoer invoer = MuisplekInvoer(x, y); 306 venster.muisplekInvoer ~= invoer; 307 } 308 309 extern (C) void venster_muiswiel_terugroeper(GLFWwindow* glfw_venster, double x, double y) nothrow { 310 Venster venster = Venster.vensters[glfw_venster]; 311 MuiswielInvoer invoer = MuiswielInvoer(x, y); 312 venster.muiswielInvoer ~= invoer; 313 } 314 315 debug { 316 extern (System) void gl_fout_terugroeper(GLenum bron, GLenum soort, GLuint id, 317 GLenum ernstigheid, GLsizei length, const GLchar* message, const void* userParam) nothrow { 318 import std.stdio : write, writeln; 319 import std.conv : to; 320 import bindbc.opengl.bind.types; 321 322 try { 323 writeln("OpenGL Fout #" ~ id.to!string); 324 write("\tBron: "); 325 switch (bron) { 326 case GL_DEBUG_SOURCE_API: 327 writeln("OpenGL API"); 328 break; 329 case GL_DEBUG_SOURCE_WINDOW_SYSTEM: 330 writeln("Venster Systeem API"); 331 break; 332 case GL_DEBUG_SOURCE_SHADER_COMPILER: 333 writeln("Shader Compiler"); 334 break; 335 case GL_DEBUG_SOURCE_THIRD_PARTY: 336 writeln("Derde Partij"); 337 break; 338 case GL_DEBUG_SOURCE_APPLICATION: 339 writeln("Gebruikerscode"); 340 break; 341 case GL_DEBUG_SOURCE_OTHER: 342 writeln("Overig"); 343 break; 344 default: 345 assert(false); 346 } 347 348 write("\tSoort: "); 349 switch (soort) { 350 case GL_DEBUG_TYPE_ERROR: 351 writeln("Fout ╮(. ❛ ᴗ ❛.)╭"); 352 break; 353 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: 354 writeln("Verouderd gebruik"); 355 break; 356 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: 357 writeln("Ongedefiniëerd gedrag"); 358 break; 359 case GL_DEBUG_TYPE_PORTABILITY: 360 writeln("Systeem overzetbaarheid"); 361 break; 362 case GL_DEBUG_TYPE_PERFORMANCE: 363 writeln("Uitvoeringsproblemen"); 364 break; 365 case GL_DEBUG_TYPE_MARKER: 366 writeln("\"Command stream annotation\""); 367 break; 368 case GL_DEBUG_TYPE_PUSH_GROUP: 369 writeln("\"Group pushing\""); 370 break; 371 case GL_DEBUG_TYPE_POP_GROUP: 372 writeln("\"foo\""); 373 break; 374 case GL_DEBUG_TYPE_OTHER: 375 writeln("Overig"); 376 break; 377 default: 378 assert(false); 379 } 380 381 write("\tErnstigheid: "); 382 switch (ernstigheid) { 383 case GL_DEBUG_SEVERITY_HIGH: 384 writeln("Hoog"); 385 break; 386 case GL_DEBUG_SEVERITY_MEDIUM: 387 writeln("Middelmatig"); 388 break; 389 case GL_DEBUG_SEVERITY_LOW: 390 writeln("Laag"); 391 break; 392 case GL_DEBUG_SEVERITY_NOTIFICATION: 393 writeln("Melding (Overig)"); 394 break; 395 default: 396 assert(false); 397 } 398 399 writeln("\tBericht: " ~ message.to!string); 400 } catch (Exception e) { 401 } 402 } 403 }