slop.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #include "gl_core_3_0.hpp"
  2. #include <GL/gl.h>
  3. #include <chrono>
  4. #include <thread>
  5. #include <stdlib.h>
  6. #include "slopstates.hpp"
  7. #include "mouse.hpp"
  8. #include "resource.hpp"
  9. #include "keyboard.hpp"
  10. #include "window.hpp"
  11. #include "shader.hpp"
  12. #include "framebuffer.hpp"
  13. #include "glrectangle.hpp"
  14. #include "xshaperectangle.hpp"
  15. #include "slop.hpp"
  16. namespace slop {
  17. X11* x11;
  18. Mouse* mouse;
  19. Keyboard* keyboard;
  20. Resource* resource;
  21. SlopSelection GLSlopSelect( slop::SlopOptions* options, bool* cancelled, slop::SlopWindow* window );
  22. SlopSelection XShapeSlopSelect( slop::SlopOptions* options, bool* cancelled);
  23. static int TmpXError(Display * d, XErrorEvent * ev) {
  24. return 0;
  25. }
  26. }
  27. using namespace slop;
  28. // Defaults!
  29. slop::SlopOptions::SlopOptions() {
  30. borderSize = 1;
  31. nokeyboard = false;
  32. noopengl = false;
  33. nodecorations = false;
  34. tolerance = 2;
  35. padding = 0;
  36. shaders.push_back("textured");
  37. highlight = false;
  38. r = 0.5;
  39. g = 0.5;
  40. b = 0.5;
  41. a = 1;
  42. char * envdisplay = getenv("DISPLAY");
  43. if (envdisplay == NULL)
  44. xdisplay = ":0";
  45. else
  46. xdisplay = envdisplay;
  47. }
  48. slop::SlopSelection::SlopSelection( float x, float y, float w, float h, int id ) {
  49. this->x = x;
  50. this->y = y;
  51. this->w = w;
  52. this->h = h;
  53. this->id = id;
  54. }
  55. slop::SlopSelection slop::SlopSelect( slop::SlopOptions* options, bool* cancelled, bool quiet) {
  56. slop::SlopSelection returnval(0,0,0,0,0);
  57. bool deleteOptions = false;
  58. if ( !options ) {
  59. deleteOptions = true;
  60. options = new slop::SlopOptions();
  61. }
  62. resource = new Resource();
  63. // Set up x11 temporarily
  64. x11 = new X11(options->xdisplay);
  65. if ( !options->nokeyboard ) {
  66. XErrorHandler ph = XSetErrorHandler(slop::TmpXError);
  67. keyboard = new Keyboard( x11 );
  68. XSetErrorHandler(ph);
  69. }
  70. bool success = false;
  71. std::string errorstring = "";
  72. SlopWindow* window;
  73. // First we check if we have a compositor available
  74. if ( x11->hasCompositor() && !options->noopengl ) {
  75. // If we have a compositor, we try using OpenGL
  76. try {
  77. window = new SlopWindow();
  78. if (!gl::sys::IsVersionGEQ(3,0)) {
  79. delete window;
  80. throw new std::runtime_error( "OpenGL version is not high enough, slop requires OpenGL 3.0!\nOpenGL accelleration is disabled. Use -o or -q to suppress this message." );
  81. }
  82. success = true;
  83. } catch( std::exception* e ) {
  84. errorstring += std::string(e->what()) + "\n";
  85. success = false;
  86. } catch (...) {
  87. success = false;
  88. }
  89. } else {
  90. errorstring += "Failed to detect a compositor, OpenGL hardware-accelleration disabled...\n";
  91. }
  92. if ( !success ) {
  93. // If we fail, we launch the XShape version of slop.
  94. if ( !quiet && !options->noopengl ) {
  95. if ( errorstring.length() <= 0 ) {
  96. errorstring += "Failed to launch OpenGL context, --shader parameter will be ignored.\n";
  97. std::cerr << errorstring;
  98. } else {
  99. std::cerr << errorstring;
  100. }
  101. }
  102. returnval = slop::XShapeSlopSelect( options, cancelled );
  103. } else {
  104. returnval = slop::GLSlopSelect( options, cancelled, window );
  105. }
  106. delete x11;
  107. delete slop::resource;
  108. if ( deleteOptions ) {
  109. delete options;
  110. }
  111. return returnval;
  112. }
  113. slop::SlopSelection slop::XShapeSlopSelect( slop::SlopOptions* options, bool* cancelled ) {
  114. // Init our little state machine, memory is a tad of a misnomer
  115. slop::SlopMemory* memory = new slop::SlopMemory( options, new XShapeRectangle(glm::vec2(0,0), glm::vec2(0,0), options->borderSize, options->padding, glm::vec4( options->r, options->g, options->b, options->a ), options->highlight) );
  116. slop::mouse = new slop::Mouse( x11, options->nodecorations, ((XShapeRectangle*)memory->rectangle)->window );
  117. // We have no GL context, so the matrix is useless...
  118. glm::mat4 fake;
  119. // This is where we'll run through all of our stuffs
  120. auto last = std::chrono::high_resolution_clock::now();
  121. while( memory->running ) {
  122. slop::mouse->update();
  123. if ( !options->nokeyboard ) {
  124. slop::keyboard->update();
  125. }
  126. // We move our statemachine forward.
  127. auto current = std::chrono::high_resolution_clock::now();
  128. std::chrono::duration<double, std::milli> frametime = current-last;
  129. last = current;
  130. memory->update( frametime.count()/1000.f );
  131. // We don't actually draw anything, but the state machine uses
  132. // this to know when to spawn the window.
  133. memory->draw( fake );
  134. // X11 explodes if we update as fast as possible, here's a tiny sleep.
  135. XFlush(x11->display);
  136. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  137. // Then we draw the framebuffer to the screen
  138. if ( (!options->nokeyboard && slop::keyboard->anyKeyDown()) || slop::mouse->getButton( 3 ) ) {
  139. memory->running = false;
  140. if ( cancelled ) {
  141. *cancelled = true;
  142. }
  143. } else {
  144. *cancelled = false;
  145. }
  146. }
  147. // Now we should have a selection! We parse everything we know about it here.
  148. glm::vec4 output = memory->rectangle->getRect();
  149. // Lets now clear both front and back buffers before closing.
  150. // hopefully it'll be completely transparent while closing!
  151. // Then we clean up.
  152. delete slop::mouse;
  153. Window selectedWindow = memory->selectedWindow;
  154. delete memory;
  155. // Try to detect the window dying.
  156. int tries = 0;
  157. while( tries < 50 ) {
  158. XEvent event;
  159. if ( XCheckTypedEvent( x11->display, UnmapNotify, &event ) ) { break; }
  160. if ( XCheckTypedEvent( x11->display, DestroyNotify, &event ) ) { break; }
  161. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  162. tries++;
  163. }
  164. // Finally return the data.
  165. return slop::SlopSelection( output.x, output.y, output.z, output.w, selectedWindow );
  166. }
  167. slop::SlopSelection slop::GLSlopSelect( slop::SlopOptions* options, bool* cancelled, SlopWindow* window ) {
  168. slop::mouse = new slop::Mouse( x11, options->nodecorations, window->window );
  169. std::string vert = "#version 120\nattribute vec2 position;\nattribute vec2 uv;\nvarying vec2 uvCoord;\nvoid main()\n{\nuvCoord = uv;\ngl_Position = vec4(position,0,1);\n}\n";
  170. std::string frag = "#version 120\nuniform sampler2D texture;\nvarying vec2 uvCoord;\nvoid main()\n {\ngl_FragColor = texture2D( texture, uvCoord );\n}\n";
  171. slop::Shader* textured = new slop::Shader( vert, frag, false );
  172. std::vector<slop::Shader*> shaders;
  173. for( int i=0;i<options->shaders.size();i++ ) {
  174. std::string sn = options->shaders[i];
  175. if ( sn != "textured" ) {
  176. shaders.push_back( new slop::Shader( sn + ".vert", sn + ".frag" ) );
  177. } else {
  178. shaders.push_back( textured );
  179. }
  180. }
  181. // Init our little state machine, memory is a tad of a misnomer
  182. slop::SlopMemory* memory = new slop::SlopMemory( options, new GLRectangle(glm::vec2(0,0), glm::vec2(0,0), options->borderSize, options->padding, glm::vec4( options->r, options->g, options->b, options->a ), options->highlight) );
  183. slop::Framebuffer* pingpong = new slop::Framebuffer(WidthOfScreen(x11->screen), HeightOfScreen(x11->screen));
  184. // This is where we'll run through all of our stuffs
  185. auto start = std::chrono::high_resolution_clock::now();
  186. auto last = start;
  187. while( memory->running ) {
  188. slop::mouse->update();
  189. if ( !options->nokeyboard ) {
  190. slop::keyboard->update();
  191. }
  192. // We move our statemachine forward.
  193. auto current = std::chrono::high_resolution_clock::now();
  194. std::chrono::duration<double, std::milli> frametime = current-last;
  195. last = current;
  196. memory->update( frametime.count()/1000.f );
  197. // Then we draw our junk to a framebuffer.
  198. window->framebuffer->setShader( textured );
  199. window->framebuffer->bind();
  200. gl::ClearColor (0.0, 0.0, 0.0, 0.0);
  201. gl::Clear (gl::COLOR_BUFFER_BIT);
  202. memory->draw( window->camera );
  203. window->framebuffer->unbind();
  204. std::chrono::duration<double, std::milli> elapsed = current-start;
  205. int i;
  206. // We have our clean buffer, now to slather it with some juicy shader chains.
  207. gl::Enable( gl::BLEND );
  208. gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
  209. for (i=0;i<=(int)shaders.size()-2;i+=2) {
  210. pingpong->bind();
  211. gl::ClearColor (0.0, 0.0, 0.0, 0.0);
  212. gl::Clear (gl::COLOR_BUFFER_BIT);
  213. window->framebuffer->setShader( shaders[i] );
  214. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  215. pingpong->unbind();
  216. window->framebuffer->bind();
  217. gl::ClearColor (0.0, 0.0, 0.0, 0.0);
  218. gl::Clear (gl::COLOR_BUFFER_BIT);
  219. pingpong->setShader( shaders[i+1] );
  220. pingpong->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  221. window->framebuffer->unbind();
  222. }
  223. for (;i<shaders.size();i++) {
  224. pingpong->bind();
  225. gl::ClearColor (0.0, 0.0, 0.0, 0.0);
  226. gl::Clear (gl::COLOR_BUFFER_BIT);
  227. window->framebuffer->setShader( shaders[i] );
  228. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  229. pingpong->unbind();
  230. }
  231. gl::Disable( gl::BLEND );
  232. if ( i%2 != 0 ) {
  233. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  234. } else {
  235. pingpong->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  236. }
  237. window->display();
  238. // Here we sleep just to prevent our CPU usage from going to 100%.
  239. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  240. GLenum err = gl::GetError();
  241. if ( err != gl::NO_ERROR_ ) {
  242. std::string error;
  243. switch(err) {
  244. case gl::INVALID_OPERATION: error="INVALID_OPERATION"; break;
  245. case gl::INVALID_ENUM: error="INVALID_ENUM"; break;
  246. case gl::INVALID_VALUE: error="INVALID_VALUE"; break;
  247. case gl::OUT_OF_MEMORY: error="OUT_OF_MEMORY"; break;
  248. case gl::INVALID_FRAMEBUFFER_OPERATION: error="INVALID_FRAMEBUFFER_OPERATION"; break;
  249. }
  250. throw new std::runtime_error( "OpenGL threw an error: " + error );
  251. }
  252. if ( (!options->nokeyboard && slop::keyboard->anyKeyDown()) || slop::mouse->getButton( 3 ) ) {
  253. memory->running = false;
  254. if ( cancelled ) {
  255. *cancelled = true;
  256. }
  257. } else {
  258. *cancelled = false;
  259. }
  260. }
  261. // Now we should have a selection! We parse everything we know about it here.
  262. glm::vec4 output = memory->rectangle->getRect();
  263. // Lets now clear both front and back buffers before closing.
  264. // hopefully it'll be completely transparent while closing!
  265. gl::ClearColor (0.0, 0.0, 0.0, 0.0);
  266. gl::Clear (gl::COLOR_BUFFER_BIT);
  267. window->display();
  268. gl::Clear (gl::COLOR_BUFFER_BIT);
  269. window->display();
  270. // Then we clean up.
  271. for( int i=0;i<shaders.size();i++ ) {
  272. delete shaders[i];
  273. }
  274. delete pingpong;
  275. delete window;
  276. delete slop::mouse;
  277. Window selectedWindow = memory->selectedWindow;
  278. delete memory;
  279. // Try to detect the window dying.
  280. int tries = 0;
  281. while( tries < 50 ) {
  282. XEvent event;
  283. if ( XCheckTypedEvent( x11->display, UnmapNotify, &event ) ) { break; }
  284. if ( XCheckTypedEvent( x11->display, DestroyNotify, &event ) ) { break; }
  285. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  286. tries++;
  287. }
  288. // Finally return the data.
  289. return slop::SlopSelection( output.x, output.y, output.z, output.w, selectedWindow );
  290. }