34 XDestroyWindow(dpy, win); |
34 XDestroyWindow(dpy, win); |
35 XCloseDisplay(dpy); |
35 XCloseDisplay(dpy); |
36 // std::cerr << "~Shark()" << std::endl; |
36 // std::cerr << "~Shark()" << std::endl; |
37 } |
37 } |
38 |
38 |
|
39 void Shark::setTitle(const std::string& suffix) { |
|
40 std::stringstream title; |
|
41 title << "ShaderShark"; |
|
42 if (suffix.size()) title << ": " << suffix.c_str(); |
|
43 XStoreName(dpy, win, title.str().c_str()); |
|
44 XFlush(dpy); |
|
45 } |
|
46 |
39 void Shark::run() { |
47 void Shark::run() { |
40 dpy = XOpenDisplay(NULL); |
48 dpy = XOpenDisplay(NULL); |
41 |
49 |
42 if (dpy == NULL) throw std::logic_error("Unable to connect to X server"); |
50 if (dpy == NULL) throw std::logic_error("Unable to connect to X server"); |
43 |
51 |
66 dpy, parent, 0, 0, width, height, 0, |
74 dpy, parent, 0, 0, width, height, 0, |
67 vi->depth, InputOutput, vi->visual, |
75 vi->depth, InputOutput, vi->visual, |
68 CWColormap | CWEventMask, &swa); |
76 CWColormap | CWEventMask, &swa); |
69 |
77 |
70 XMapWindow(dpy, win); |
78 XMapWindow(dpy, win); |
71 XStoreName(dpy, win, "ShaderShark"); |
79 setTitle(); |
72 setX11PID(dpy, win); |
80 setX11PID(dpy, win); |
73 // XSetWindowBackground(dpy, win, 0) vs. glClearColor() |
81 // XSetWindowBackground(dpy, win, 0) vs. glClearColor() |
74 |
82 |
75 glc = glXCreateContext(dpy, vi, NULL, GL_TRUE); |
83 glc = glXCreateContext(dpy, vi, NULL, GL_TRUE); |
76 glXMakeCurrent(dpy, win, glc); |
84 glXMakeCurrent(dpy, win, glc); |
115 |
123 |
116 for (XEvent xev; keepRunningX11;) { |
124 for (XEvent xev; keepRunningX11;) { |
117 int epollEventCount = epoll.wait(); |
125 int epollEventCount = epoll.wait(); |
118 //std::cout << "trace: epoll.wait() = " << epollEventCount << std::endl; |
126 //std::cout << "trace: epoll.wait() = " << epollEventCount << std::endl; |
119 for (int epollEvent = 0; epollEvent < epollEventCount; epollEvent++) { |
127 for (int epollEvent = 0; epollEvent < epollEventCount; epollEvent++) { |
|
128 bool redraw = false; |
120 if (epoll[epollEvent].data.fd == x11fd) { |
129 if (epoll[epollEvent].data.fd == x11fd) { |
121 if (!XPending(dpy)) { |
130 if (!XPending(dpy)) { |
122 // otherwise STDIN events are held until the first X11 event |
131 // otherwise STDIN events are held until the first X11 event |
123 logOutput << "trace: no pending X11 event" << std::endl; |
132 logOutput << "trace: no pending X11 event" << std::endl; |
124 break; |
133 break; |
125 } |
134 } |
126 XWindowAttributes gwa; |
135 XWindowAttributes gwa; |
127 XNextEvent(dpy, &xev); |
136 XNextEvent(dpy, &xev); |
128 bool redraw = false; |
|
129 |
137 |
130 if (xev.type == Expose) { |
138 if (xev.type == Expose) { |
131 std::cout << "XEvent: Expose" << std::endl; |
139 std::cout << "XEvent: Expose" << std::endl; |
132 XGetWindowAttributes(dpy, win, &gwa); |
140 XGetWindowAttributes(dpy, win, &gwa); |
133 glViewport(0, 0, gwa.width, gwa.height); |
141 glViewport(0, 0, gwa.width, gwa.height); |
191 std::cout << "XEvent: DestroyNotify → finish" << std::endl; |
199 std::cout << "XEvent: DestroyNotify → finish" << std::endl; |
192 break; |
200 break; |
193 } else { |
201 } else { |
194 std::cout << "XEvent: type=" << xev.type << std::endl; |
202 std::cout << "XEvent: type=" << xev.type << std::endl; |
195 } |
203 } |
196 |
|
197 if (redraw) { |
|
198 runShaders(); |
|
199 glXSwapBuffers(dpy, win); |
|
200 } |
|
201 } else if (epoll[epollEvent].data.fd == STDIN_FILENO) { |
204 } else if (epoll[epollEvent].data.fd == STDIN_FILENO) { |
202 int epollFD = epoll[epollEvent].data.fd; |
205 int epollFD = epoll[epollEvent].data.fd; |
203 logOutput << "other event: fd=" << epollFD << " data="; |
206 logOutput << "other event: fd=" << epollFD << " data="; |
204 for (char ch; read(epollFD, &ch, 1) > 0;) { |
207 for (char ch; read(epollFD, &ch, 1) > 0;) { |
205 std::stringstream msg; |
208 std::stringstream msg; |
213 logOutput << std::endl; |
216 logOutput << std::endl; |
214 |
217 |
215 } else if (epoll[epollEvent].data.fd == fileMonitor.getFD()) { |
218 } else if (epoll[epollEvent].data.fd == fileMonitor.getFD()) { |
216 std::cout << "FileMonitor event:" << std::endl; |
219 std::cout << "FileMonitor event:" << std::endl; |
217 for (FileEvent fe; fileMonitor.readEvent(fe);) { |
220 for (FileEvent fe; fileMonitor.readEvent(fe);) { |
218 std::cout << " " |
221 logOutput << " " |
219 << " file: " << fe.fileName |
222 << " file=" << fe.fileName |
220 << " mask: " << fe.mask |
223 << " mask=" << fe.mask |
221 << std::endl; |
224 << std::endl; |
|
225 try { |
|
226 redraw |= reloadTexture(fe.fileName); |
|
227 redraw |= reloadShader(fe.fileName); |
|
228 setTitle(); |
|
229 } catch (const std::exception& e) { |
|
230 setTitle("[ERROR]"); |
|
231 logOutput << "error while reloading '" |
|
232 << fe.fileName.c_str() |
|
233 << "': " << e.what() << std::endl; |
|
234 } |
222 } |
235 } |
223 } else { |
236 } else { |
224 logOutput |
237 logOutput |
225 << "error: event on an unexpected FD: " |
238 << "error: event on an unexpected FD: " |
226 << epoll[epollEvent].data.fd |
239 << epoll[epollEvent].data.fd |
227 << std::endl; |
240 << std::endl; |
|
241 } |
|
242 |
|
243 if (redraw) { |
|
244 runShaders(); |
|
245 glXSwapBuffers(dpy, win); |
228 } |
246 } |
229 } |
247 } |
230 } |
248 } |
231 } |
249 } |
232 |
250 |
368 // glUniform1i(ProgAttr.jazz, jazz); |
386 // glUniform1i(ProgAttr.jazz, jazz); |
369 // checkError(&std::cerr); |
387 // checkError(&std::cerr); |
370 } |
388 } |
371 } |
389 } |
372 |
390 |
|
391 bool Shark::reloadTexture(const std::string& fileName) { |
|
392 for (const Configuration::Texture& tex : cfg.textures) { |
|
393 if (tex.fileName == fileName) { |
|
394 logOutput << "TODO: reload texture: " << fileName.c_str() << "\n"; |
|
395 return true; |
|
396 } |
|
397 } |
|
398 return false; |
|
399 } |
|
400 |
373 std::shared_ptr<Program> Shark::loadShaders() { |
401 std::shared_ptr<Program> Shark::loadShaders() { |
374 try { |
402 try { |
375 // Vertex Array Object (VAO) |
403 // Vertex Array Object (VAO) |
376 GLuint vao; |
404 GLuint vao; |
377 glGenVertexArrays(1, &vao); |
405 glGenVertexArrays(1, &vao); |
410 |
438 |
411 program->attachShader(*shader.get()); |
439 program->attachShader(*shader.get()); |
412 shaders.push_back(shader); |
440 shaders.push_back(shader); |
413 watchedFiles.push_back(fileMonitor.watch(fileName)); |
441 watchedFiles.push_back(fileMonitor.watch(fileName)); |
414 std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl; |
442 std::cerr << "GLSL loaded: " << fileName.c_str() << std::endl; |
|
443 // We may detach and delete shaders, |
|
444 // but our shaders are small, so we keep them for later reloading. |
415 } |
445 } |
416 |
446 |
417 // GLSL compiler does very efficient / aggressive optimization. |
447 // GLSL compiler does very efficient / aggressive optimization. |
418 // Attributes and uniforms that are not used in the shader are deleted. |
448 // Attributes and uniforms that are not used in the shader are deleted. |
419 // And even if we e.g. read color from a texture and overwrite it, |
449 // And even if we e.g. read color from a texture and overwrite it, |
439 std::cerr << "Error while loading shaders: unknown" << std::endl; |
469 std::cerr << "Error while loading shaders: unknown" << std::endl; |
440 } |
470 } |
441 throw std::logic_error("GLSL: loadShaders() failed"); |
471 throw std::logic_error("GLSL: loadShaders() failed"); |
442 } |
472 } |
443 |
473 |
|
474 bool Shark::reloadShader(const std::string& fileName) { |
|
475 for (auto shader : shaders) { |
|
476 if (shader->getFileName() == fileName) { |
|
477 shader->update(MappedFile(fileName)); |
|
478 shaderProgram->link(); |
|
479 return true; |
|
480 } |
|
481 } |
|
482 return false; |
|
483 } |