slop.cpp 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. #ifdef SLOP_OPENGL
  2. #include <GL/glew.h>
  3. #include <GL/gl.h>
  4. #endif
  5. #include <chrono>
  6. #include <thread>
  7. #include <string>
  8. #include <vector>
  9. #include <stdlib.h>
  10. #include "slopstates.hpp"
  11. #include "mouse.hpp"
  12. #include "resource.hpp"
  13. #include "keyboard.hpp"
  14. #ifdef SLOP_OPENGL
  15. #include "window.hpp"
  16. #include "shader.hpp"
  17. #include "framebuffer.hpp"
  18. #include "glrectangle.hpp"
  19. #endif
  20. #include "xshaperectangle.hpp"
  21. #include "slop.hpp"
  22. namespace slop {
  23. X11* x11;
  24. Mouse* mouse;
  25. Keyboard* keyboard;
  26. Resource* resource;
  27. #ifdef SLOP_OPENGL
  28. SlopSelection GLSlopSelect( slop::SlopOptions* options, slop::SlopWindow* window );
  29. #endif
  30. SlopSelection XShapeSlopSelect( slop::SlopOptions* options );
  31. static int TmpXError(Display * d, XErrorEvent * ev) {
  32. return 0;
  33. }
  34. }
  35. template<typename Out>
  36. static void split(const std::string &s, char delim, Out result) {
  37. std::stringstream ss;
  38. ss.str(s);
  39. std::string item;
  40. while (std::getline(ss, item, delim)) {
  41. *(result++) = item;
  42. }
  43. }
  44. static std::vector<std::string> split(const std::string &s, char delim) {
  45. std::vector<std::string> elems;
  46. split(s, delim, std::back_inserter(elems));
  47. return elems;
  48. }
  49. using namespace slop;
  50. char slop_default_xdisplay[] = ":0";
  51. char slop_default_shaders[] = "textured";
  52. // Defaults!
  53. slop::SlopOptions::SlopOptions() {
  54. border = 1;
  55. nokeyboard = false;
  56. noopengl = false;
  57. nodecorations = false;
  58. tolerance = 2;
  59. padding = 0;
  60. shaders = slop_default_shaders;
  61. highlight = false;
  62. r = 0.5;
  63. g = 0.5;
  64. b = 0.5;
  65. a = 1;
  66. quiet = false;
  67. char* envdisplay = getenv("DISPLAY");
  68. if (envdisplay == NULL) {
  69. xdisplay = slop_default_xdisplay;
  70. } else {
  71. xdisplay = envdisplay;
  72. }
  73. }
  74. slop::SlopSelection::SlopSelection( float x, float y, float w, float h, int id, bool cancelled ) {
  75. this->x = x;
  76. this->y = y;
  77. this->w = w;
  78. this->h = h;
  79. this->id = id;
  80. this->cancelled = cancelled;
  81. }
  82. slop::SlopSelection slop::SlopSelect( slop::SlopOptions* options ) {
  83. slop::SlopSelection returnval(0,0,0,0,0,true);
  84. bool deleteOptions = false;
  85. if ( !options ) {
  86. deleteOptions = true;
  87. options = new slop::SlopOptions();
  88. }
  89. resource = new Resource();
  90. // Set up x11 temporarily
  91. x11 = new X11(options->xdisplay);
  92. if ( !options->nokeyboard ) {
  93. XErrorHandler ph = XSetErrorHandler(slop::TmpXError);
  94. keyboard = new Keyboard( x11 );
  95. XSetErrorHandler(ph);
  96. }
  97. #ifdef SLOP_OPENGL
  98. bool success = false;
  99. std::string errorstring = "";
  100. SlopWindow* window;
  101. // First we check if we have a compositor available
  102. if ( x11->hasCompositor() && !options->noopengl ) {
  103. // If we have a compositor, we try using OpenGL
  104. try {
  105. window = new SlopWindow();
  106. if (!GLEW_VERSION_3_0) {
  107. delete window;
  108. throw 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." );
  109. }
  110. success = true;
  111. } catch( std::exception& e ) {
  112. errorstring += std::string(e.what()) + "\n";
  113. success = false;
  114. } catch (...) {
  115. success = false;
  116. }
  117. } else {
  118. errorstring += "Failed to detect a compositor, OpenGL hardware-accelleration disabled...\n";
  119. }
  120. if ( !success ) {
  121. // If we fail, we launch the XShape version of slop.
  122. if ( !options->quiet && !options->noopengl ) {
  123. if ( errorstring.length() <= 0 ) {
  124. errorstring += "Failed to launch OpenGL context, --shader parameter will be ignored.\n";
  125. std::cerr << errorstring;
  126. } else {
  127. std::cerr << errorstring;
  128. }
  129. }
  130. #endif // SLOP_OPENGL
  131. returnval = slop::XShapeSlopSelect( options );
  132. #ifdef SLOP_OPENGL
  133. } else {
  134. returnval = slop::GLSlopSelect( options, window );
  135. }
  136. #endif
  137. delete x11;
  138. delete slop::resource;
  139. if ( deleteOptions ) {
  140. delete options;
  141. }
  142. return returnval;
  143. }
  144. slop::SlopSelection slop::XShapeSlopSelect( slop::SlopOptions* options ) {
  145. // Init our little state machine, memory is a tad of a misnomer
  146. bool cancelled = false;
  147. slop::SlopMemory* memory = new slop::SlopMemory( options, new XShapeRectangle(glm::vec2(0,0), glm::vec2(0,0), options->border, options->padding, glm::vec4( options->r, options->g, options->b, options->a ), options->highlight) );
  148. slop::mouse = new slop::Mouse( x11, options->nodecorations, ((XShapeRectangle*)memory->rectangle)->window );
  149. // We have no GL context, so the matrix is useless...
  150. glm::mat4 fake;
  151. // This is where we'll run through all of our stuffs
  152. auto last = std::chrono::high_resolution_clock::now();
  153. while( memory->running ) {
  154. slop::mouse->update();
  155. if ( !options->nokeyboard ) {
  156. slop::keyboard->update();
  157. }
  158. // We move our statemachine forward.
  159. auto current = std::chrono::high_resolution_clock::now();
  160. std::chrono::duration<double, std::milli> frametime = current-last;
  161. last = current;
  162. memory->update( frametime.count()/1000.f );
  163. // We don't actually draw anything, but the state machine uses
  164. // this to know when to spawn the window.
  165. memory->draw( fake );
  166. // X11 explodes if we update as fast as possible, here's a tiny sleep.
  167. XFlush(x11->display);
  168. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  169. // Then we draw the framebuffer to the screen
  170. if ( (!options->nokeyboard && slop::keyboard->anyKeyDown()) || slop::mouse->getButton( 3 ) ) {
  171. memory->running = false;
  172. cancelled = true;
  173. }
  174. }
  175. // Now we should have a selection! We parse everything we know about it here.
  176. glm::vec4 output = memory->rectangle->getRect();
  177. // Lets now clear both front and back buffers before closing.
  178. // hopefully it'll be completely transparent while closing!
  179. // Then we clean up.
  180. delete slop::mouse;
  181. Window selectedWindow = memory->selectedWindow;
  182. delete memory;
  183. // Try to detect the window dying.
  184. int tries = 0;
  185. while( tries < 50 ) {
  186. XEvent event;
  187. if ( XCheckTypedEvent( x11->display, UnmapNotify, &event ) ) { break; }
  188. if ( XCheckTypedEvent( x11->display, DestroyNotify, &event ) ) { break; }
  189. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  190. tries++;
  191. }
  192. // Finally return the data.
  193. return slop::SlopSelection( output.x, output.y, output.z, output.w, selectedWindow, cancelled );
  194. }
  195. #ifdef SLOP_OPENGL
  196. slop::SlopSelection slop::GLSlopSelect( slop::SlopOptions* options, SlopWindow* window ) {
  197. bool cancelled = false;
  198. slop::mouse = new slop::Mouse( x11, options->nodecorations, window->window );
  199. 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";
  200. std::string frag = "#version 120\nuniform sampler2D texture;\nvarying vec2 uvCoord;\nvoid main()\n {\ngl_FragColor = texture2D( texture, uvCoord );\n}\n";
  201. slop::Shader* textured = new slop::Shader( vert, frag, false );
  202. std::vector<slop::Shader*> shaders;
  203. std::vector<std::string> stringShaders = split( options->shaders, ',' );
  204. for( int i=0;i<stringShaders.size();i++ ) {
  205. std::string sn = stringShaders[i];
  206. if ( sn != "textured" ) {
  207. shaders.push_back( new slop::Shader( sn + ".vert", sn + ".frag" ) );
  208. } else {
  209. shaders.push_back( textured );
  210. }
  211. }
  212. // Init our little state machine, memory is a tad of a misnomer
  213. slop::SlopMemory* memory = new slop::SlopMemory( options, new GLRectangle(glm::vec2(0,0), glm::vec2(0,0), options->border, options->padding, glm::vec4( options->r, options->g, options->b, options->a ), options->highlight) );
  214. slop::Framebuffer* pingpong = new slop::Framebuffer(WidthOfScreen(x11->screen), HeightOfScreen(x11->screen));
  215. // This is where we'll run through all of our stuffs
  216. auto start = std::chrono::high_resolution_clock::now();
  217. auto last = start;
  218. while( memory->running ) {
  219. slop::mouse->update();
  220. if ( !options->nokeyboard ) {
  221. slop::keyboard->update();
  222. }
  223. // We move our statemachine forward.
  224. auto current = std::chrono::high_resolution_clock::now();
  225. std::chrono::duration<double, std::milli> frametime = current-last;
  226. last = current;
  227. memory->update( frametime.count()/1000.f );
  228. // Then we draw our junk to a framebuffer.
  229. window->framebuffer->setShader( textured );
  230. window->framebuffer->bind();
  231. glClearColor (0.0, 0.0, 0.0, 0.0);
  232. glClear (GL_COLOR_BUFFER_BIT);
  233. memory->draw( window->camera );
  234. window->framebuffer->unbind();
  235. std::chrono::duration<double, std::milli> elapsed = current-start;
  236. int i;
  237. // We have our clean buffer, now to slather it with some juicy shader chains.
  238. glEnable( GL_BLEND );
  239. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  240. for (i=0;i<=(int)shaders.size()-2;i+=2) {
  241. pingpong->bind();
  242. glClearColor (0.0, 0.0, 0.0, 0.0);
  243. glClear (GL_COLOR_BUFFER_BIT);
  244. window->framebuffer->setShader( shaders[i] );
  245. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  246. pingpong->unbind();
  247. window->framebuffer->bind();
  248. glClearColor (0.0, 0.0, 0.0, 0.0);
  249. glClear (GL_COLOR_BUFFER_BIT);
  250. pingpong->setShader( shaders[i+1] );
  251. pingpong->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  252. window->framebuffer->unbind();
  253. }
  254. for (;i<shaders.size();i++) {
  255. pingpong->bind();
  256. glClearColor (0.0, 0.0, 0.0, 0.0);
  257. glClear (GL_COLOR_BUFFER_BIT);
  258. window->framebuffer->setShader( shaders[i] );
  259. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  260. pingpong->unbind();
  261. }
  262. glDisable( GL_BLEND );
  263. if ( i%2 != 0 ) {
  264. window->framebuffer->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  265. } else {
  266. pingpong->draw(slop::mouse->getMousePos(), elapsed.count()/1000.f, glm::vec4( options->r, options->g, options->b, options->a ) );
  267. }
  268. window->display();
  269. // Here we sleep just to prevent our CPU usage from going to 100%.
  270. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  271. GLenum err = glGetError();
  272. if ( err != GL_NO_ERROR ) {
  273. std::string error;
  274. switch(err) {
  275. case GL_INVALID_OPERATION: error="INVALID_OPERATION"; break;
  276. case GL_INVALID_ENUM: error="INVALID_ENUM"; break;
  277. case GL_INVALID_VALUE: error="INVALID_VALUE"; break;
  278. case GL_OUT_OF_MEMORY: error="OUT_OF_MEMORY"; break;
  279. case GL_INVALID_FRAMEBUFFER_OPERATION: error="INVALID_FRAMEBUFFER_OPERATION"; break;
  280. }
  281. throw std::runtime_error( "OpenGL threw an error: " + error );
  282. }
  283. if ( (!options->nokeyboard && slop::keyboard->anyKeyDown()) || slop::mouse->getButton( 3 ) ) {
  284. memory->running = false;
  285. cancelled = true;
  286. }
  287. }
  288. // Now we should have a selection! We parse everything we know about it here.
  289. glm::vec4 output = memory->rectangle->getRect();
  290. // Lets now clear both front and back buffers before closing.
  291. // hopefully it'll be completely transparent while closing!
  292. glClearColor (0.0, 0.0, 0.0, 0.0);
  293. glClear (GL_COLOR_BUFFER_BIT);
  294. window->display();
  295. glClear (GL_COLOR_BUFFER_BIT);
  296. window->display();
  297. // Then we clean up.
  298. for( int i=0;i<shaders.size();i++ ) {
  299. delete shaders[i];
  300. }
  301. delete pingpong;
  302. delete window;
  303. delete slop::mouse;
  304. Window selectedWindow = memory->selectedWindow;
  305. delete memory;
  306. // Try to detect the window dying.
  307. int tries = 0;
  308. while( tries < 50 ) {
  309. XEvent event;
  310. if ( XCheckTypedEvent( x11->display, UnmapNotify, &event ) ) { break; }
  311. if ( XCheckTypedEvent( x11->display, DestroyNotify, &event ) ) { break; }
  312. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  313. tries++;
  314. }
  315. // Finally return the data.
  316. return slop::SlopSelection( output.x, output.y, output.z, output.w, selectedWindow, cancelled );
  317. }
  318. #endif
  319. extern "C" struct slop_options slop_options_default() {
  320. struct slop_options options;
  321. options.border = 1;
  322. options.nokeyboard = false;
  323. options.noopengl = false;
  324. options.nodecorations = false;
  325. options.tolerance = 2;
  326. options.padding = 0;
  327. options.shaders = slop_default_shaders;
  328. options.highlight = false;
  329. options.r = 0.5;
  330. options.g = 0.5;
  331. options.b = 0.5;
  332. options.a = 1;
  333. options.quiet = false;
  334. char* envdisplay = getenv("DISPLAY");
  335. if (envdisplay == NULL) {
  336. options.xdisplay = slop_default_xdisplay;
  337. } else {
  338. options.xdisplay = envdisplay;
  339. }
  340. return options;
  341. }
  342. extern "C" struct slop_selection slop_select( struct slop_options* options ) {
  343. slop::SlopOptions realOptions = slop::SlopOptions();
  344. if ( options != NULL ) {
  345. realOptions.border = options->border;
  346. realOptions.nokeyboard = options->nokeyboard;
  347. realOptions.noopengl = options->noopengl;
  348. realOptions.nodecorations = options->nodecorations;
  349. realOptions.tolerance = options->tolerance;
  350. realOptions.padding = options->padding;
  351. realOptions.shaders = options->shaders;
  352. realOptions.highlight = options->highlight;
  353. realOptions.r = options->r;
  354. realOptions.g = options->g;
  355. realOptions.b = options->b;
  356. realOptions.a = options->a;
  357. realOptions.quiet = options->quiet;
  358. realOptions.xdisplay = options->xdisplay;
  359. }
  360. slop::SlopSelection select = SlopSelect( &realOptions );
  361. slop_selection realSelect;
  362. realSelect.x = select.x;
  363. realSelect.y = select.y;
  364. realSelect.w = select.w;
  365. realSelect.h = select.h;
  366. realSelect.id = select.id;
  367. realSelect.cancelled = select.cancelled;
  368. return realSelect;
  369. }