/xbmc/screensavers/rsxs-0.9/src/common.cc

http://github.com/xbmc/xbmc · C++ · 622 lines · 536 code · 62 blank · 24 comment · 81 complexity · 66a9c483bf88bbe97713e03860628c8d MD5 · raw file

  1. /*
  2. * Really Slick XScreenSavers
  3. * Copyright (C) 2002-2006 Michael Chapman
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  17. *
  18. *****************************************************************************
  19. *
  20. * This is a Linux port of the Really Slick Screensavers,
  21. * Copyright (C) 2002 Terence M. Welsh, available from www.reallyslick.com
  22. */
  23. #include <common.hh>
  24. #if HAVE_SYS_TYPES_H
  25. #include <sys/types.h>
  26. #endif
  27. #if HAVE_SYS_SELECT_H
  28. #include <sys/select.h>
  29. #endif
  30. #include <sys/time.h>
  31. #include <GL/gl.h>
  32. #include <GL/glx.h>
  33. #include <hack.hh>
  34. #include <resource.hh>
  35. #include <X11/X.h>
  36. #include <X11/Xatom.h>
  37. #include <X11/Xlib.h>
  38. #include <X11/Xutil.h>
  39. #include <X11/Intrinsic.h>
  40. #include <X11/StringDefs.h>
  41. #include <X11/Shell.h>
  42. #if defined(__vms)
  43. #include <X11/StdCmap.h> /* for XmuLookupStandardColormap */
  44. #else
  45. #include <X11/Xmu/StdCmap.h> /* for XmuLookupStandardColormap */
  46. #endif
  47. #include <vroot.hh>
  48. #include <ctime>
  49. #define MAX_DELAY 10000
  50. #define MIN_DELAY 1000
  51. namespace Common {
  52. std::string program;
  53. Display* display;
  54. unsigned int screen;
  55. XVisualInfo *visualInfo;
  56. Window window;
  57. GLXContext context;
  58. std::string resourceDir;
  59. unsigned int width, height, depth;
  60. unsigned int centerX, centerY;
  61. float aspectRatio;
  62. Colormap colormap;
  63. bool doubleBuffered;
  64. bool running;
  65. unsigned int elapsedMicros;
  66. float elapsedSecs;
  67. float speed;
  68. float elapsedTime;
  69. ResourceManager* resources;
  70. error_t parse(int, char*, struct argp_state*);
  71. void init(int argc, char** argv);
  72. void run();
  73. void fini();
  74. };
  75. namespace Common {
  76. char* _displayName;
  77. Window _windowID;
  78. int _x, _y, _w, _h;
  79. bool _reverseX, _reverseY;
  80. bool _useOffset;
  81. bool _fullScreen;
  82. bool _onRoot;
  83. #ifndef NDEBUG
  84. bool _showFPS;
  85. #endif // !NDEBUG
  86. enum Arguments {
  87. ARG_ROOT = 1,
  88. #ifndef NDEBUG
  89. ARG_FPS,
  90. #endif // !NDEBUG
  91. ARG_GEOMETRY,
  92. ARG_FULLSCREEN,
  93. ARG_WINDOWID,
  94. ARG_RESOURCE_DIR,
  95. };
  96. struct HasCurrentVisualID;
  97. Colormap getColormap();
  98. Window createWindow(int, char**);
  99. void updateAttributes();
  100. void dumpErrors(const std::string&);
  101. };
  102. error_t Common::parse(int key, char* arg, struct argp_state* state) {
  103. switch (key) {
  104. case ARGP_KEY_INIT:
  105. visualInfo = NULL;
  106. window = None;
  107. context = None;
  108. running = false;
  109. resourceDir = std::string(PKGDATADIR "/") + Hack::getShortName();
  110. _displayName = NULL;
  111. _onRoot = false;
  112. _windowID = 0;
  113. _x = _y = 0;
  114. _reverseX = _reverseY = false;
  115. _w = 640;
  116. _h = 480;
  117. _useOffset = _fullScreen = false;
  118. return 0;
  119. case ARG_ROOT:
  120. _onRoot = true;
  121. return 0;
  122. case ARG_GEOMETRY:
  123. if (std::sscanf(arg, "%dx%d+%d+%d", &_w, &_h, &_x, &_y) == 4)
  124. _useOffset = true;
  125. else if (std::sscanf(arg, "%dx%d-%d+%d", &_w, &_h, &_x, &_y) == 4) {
  126. _useOffset = true; _reverseX = true;
  127. } else if (std::sscanf(arg, "%dx%d+%d-%d", &_w, &_h, &_x, &_y) == 4) {
  128. _useOffset = true; _reverseY = true;
  129. } else if (std::sscanf(arg, "%dx%d-%d-%d", &_w, &_h, &_x, &_y) == 4) {
  130. _useOffset = true; _reverseX = true; _reverseY = true;
  131. } else if (std::sscanf(arg, "%dx%d", &_w, &_h) == 2)
  132. ;
  133. else if (std::sscanf(arg, "%d%d", &_x, &_y) == 2)
  134. _useOffset = true;
  135. else {
  136. argp_error(state, "could not parse geometry `%s'", arg);
  137. return ARGP_ERR_UNKNOWN;
  138. }
  139. return 0;
  140. case ARG_FULLSCREEN:
  141. _fullScreen = true;
  142. return 0;
  143. case ARG_WINDOWID:
  144. if ((_windowID = std::strtol(arg, NULL, 0)) == 0) {
  145. argp_error(state, "invalid window ID `%s'", arg);
  146. return ARGP_ERR_UNKNOWN;
  147. }
  148. return 0;
  149. case ARG_RESOURCE_DIR:
  150. resourceDir = arg;
  151. return 0;
  152. #ifndef NDEBUG
  153. case ARG_FPS:
  154. _showFPS = true;
  155. return 0;
  156. #endif // !NDEBUG
  157. default:
  158. return ARGP_ERR_UNKNOWN;
  159. }
  160. }
  161. static struct timeval now;
  162. static struct timeval then;
  163. void Common::init(int argc, char** argv) {
  164. #ifdef NOXBMC
  165. display = XOpenDisplay(_displayName);
  166. if (!display) {
  167. if (_displayName != "")
  168. throw Exception(stdx::oss() << "Could not open display " << _displayName);
  169. else
  170. throw Exception("Could not open default display (DISPLAY variable not set?)");
  171. }
  172. screen = DefaultScreen(display);
  173. _displayName = XDisplayString(display);
  174. window = createWindow(argc, argv);
  175. if (!window) return;
  176. updateAttributes();
  177. XMapRaised(display, window);
  178. #endif
  179. running = true;
  180. speed = 1.0f;
  181. resources = new ResourceManager;
  182. gettimeofday(&now, NULL);
  183. }
  184. void Common::run() {
  185. #ifdef NOXBMC
  186. Hack::start();
  187. #ifndef NDEBUG
  188. dumpErrors("start");
  189. #endif // !NDEBUG
  190. while (running) {
  191. Hack::tick();
  192. #ifndef NDEBUG
  193. dumpErrors("tick");
  194. #endif // !NDEBUG
  195. while (XPending(display)) {
  196. XEvent event;
  197. XNextEvent(display, &event);
  198. switch (event.type) {
  199. case ConfigureNotify:
  200. updateAttributes();
  201. TRACE("Reshaping window");
  202. Hack::reshape();
  203. break;
  204. case MappingNotify:
  205. TRACE("Key mapping changed");
  206. XRefreshKeyboardMapping(&event.xmapping);
  207. break;
  208. case KeyPress:
  209. {
  210. char c;
  211. KeySym keysym;
  212. XLookupString(&event.xkey, &c, 1, &keysym, 0);
  213. TRACE("Key pressed: " << c);
  214. Hack::keyPress(c, keysym);
  215. }
  216. break;
  217. case KeyRelease:
  218. {
  219. char c;
  220. KeySym keysym;
  221. XLookupString(&event.xkey, &c, 1, &keysym, 0);
  222. TRACE("Key released: " << c);
  223. Hack::keyRelease(c, keysym);
  224. }
  225. break;
  226. case ButtonPress:
  227. {
  228. unsigned int button = event.xbutton.button;
  229. TRACE("Button pressed: " << button << " (" << event.xbutton.x <<
  230. ',' << event.xbutton.y << ')');
  231. Hack::buttonPress(button);
  232. }
  233. break;
  234. case ButtonRelease:
  235. {
  236. unsigned int button = event.xbutton.button;
  237. TRACE("Button released: " << button << " (" << event.xbutton.x <<
  238. ',' << event.xbutton.y << ')');
  239. Hack::buttonRelease(button);
  240. }
  241. break;
  242. case MotionNotify:
  243. {
  244. int x = event.xmotion.x;
  245. int y = event.xmotion.y;
  246. Hack::pointerMotion(x, y);
  247. }
  248. break;
  249. case EnterNotify:
  250. if (event.xcrossing.state & (
  251. Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask
  252. ))
  253. TRACE("Ignoring pointer entering window");
  254. else {
  255. TRACE("Pointer entered window");
  256. Hack::pointerEnter();
  257. }
  258. break;
  259. case LeaveNotify:
  260. if (event.xcrossing.state & (
  261. Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask
  262. ))
  263. TRACE("Ignoring pointer leaving window");
  264. else {
  265. TRACE("Pointer exited window");
  266. Hack::pointerLeave();
  267. }
  268. break;
  269. }
  270. }
  271. #endif
  272. then = now;
  273. gettimeofday(&now, NULL);
  274. #ifndef NDEBUG
  275. if (_showFPS) {
  276. elapsedMicros = 1000000 * (now.tv_sec - then.tv_sec) +
  277. now.tv_usec - then.tv_usec;
  278. elapsedSecs = float(elapsedMicros) / 1000000.0f;
  279. static float secsSinceUpdate = 0.0f;
  280. static unsigned int frames = 0;
  281. secsSinceUpdate += elapsedSecs;
  282. ++frames;
  283. if (secsSinceUpdate >= 5.0f) {
  284. float fps = float(frames) / secsSinceUpdate;
  285. std::cerr << frames << " frames in " << secsSinceUpdate <<
  286. " seconds = " << fps << " FPS" << std::endl;
  287. secsSinceUpdate = 0.0f;
  288. frames = 0;
  289. }
  290. } else {
  291. #endif // !NDEBUG
  292. elapsedMicros *= 4;
  293. elapsedMicros += 1000000 * (now.tv_sec - then.tv_sec) +
  294. now.tv_usec - then.tv_usec;
  295. elapsedMicros /= 5;
  296. elapsedSecs = float(elapsedMicros) / 1000000.0f;
  297. unsigned int remainingMicros =
  298. (elapsedMicros > MAX_DELAY - MIN_DELAY) ?
  299. MIN_DELAY : MAX_DELAY - elapsedMicros;
  300. struct timeval tv;
  301. tv.tv_sec = remainingMicros / 1000000L;
  302. tv.tv_usec = remainingMicros % 1000000L;
  303. select(0, 0, 0, 0, &tv);
  304. #ifndef NDEBUG
  305. }
  306. #endif // !NDEBUG
  307. elapsedTime = speed * elapsedSecs;
  308. #ifdef NOXBMC
  309. }
  310. Hack::stop();
  311. #ifndef NDEBUG
  312. dumpErrors("stop");
  313. #endif // !NDEBUG
  314. #endif
  315. }
  316. void Common::fini() {
  317. delete resources;
  318. if (context) glXDestroyContext(display, context);
  319. if (visualInfo) XFree(visualInfo);
  320. if (window) XDestroyWindow(display, window);
  321. if (display) XCloseDisplay(display);
  322. }
  323. /* See http://www.mesa3d.org/brianp/sig97/glxport.htm */
  324. Colormap Common::getColormap() {
  325. if (visualInfo->visual == DefaultVisual(display, screen))
  326. return DefaultColormap(display, screen);
  327. std::string serverString(glXQueryServerString(display, screen, GLX_VERSION));
  328. bool mesa = serverString.find("Mesa") != std::string::npos;
  329. if (mesa) {
  330. Atom atom = XInternAtom(display, "_HP_RGB_SMOOTH_MAP_LIST", True);
  331. if (
  332. atom &&
  333. visualInfo->visual->c_class == TrueColor &&
  334. depth == 8
  335. ) {
  336. XStandardColormap *colormaps;
  337. int numColormaps;
  338. Colormap result = None;
  339. if (
  340. XGetRGBColormaps(display, RootWindow(display, screen),
  341. &colormaps, &numColormaps, atom)
  342. ) {
  343. for (int i = 0; i < numColormaps; ++i)
  344. if (colormaps[i].visualid == Common::visualInfo->visualid)
  345. result = colormaps[i].colormap;
  346. XFree(colormaps);
  347. }
  348. if (result) return result;
  349. }
  350. }
  351. #ifndef SOLARIS_BUG
  352. if (XmuLookupStandardColormap(
  353. display, screen, visualInfo->visualid, depth,
  354. XA_RGB_DEFAULT_MAP, False, True
  355. )) {
  356. XStandardColormap* colormaps;
  357. int numColormaps;
  358. Colormap result = None;
  359. if (XGetRGBColormaps(
  360. display, RootWindow(display, screen),
  361. &colormaps, &numColormaps, XA_RGB_DEFAULT_MAP
  362. )) {
  363. for (int i = 0; i < numColormaps; ++i)
  364. if (colormaps[i].visualid == Common::visualInfo->visualid)
  365. result = colormaps[i].colormap;
  366. XFree(colormaps);
  367. }
  368. if (result) return result;
  369. }
  370. #endif
  371. return XCreateColormap(display, RootWindow(display, screen),
  372. visualInfo->visual, AllocNone);
  373. }
  374. Window Common::createWindow(int argc, char** argv) {
  375. Window window = 0;
  376. if (_onRoot || _windowID) {
  377. window = _windowID ? _windowID : RootWindow(display, screen);
  378. TRACE("Drawing on window: " << window);
  379. XWindowAttributes gwa;
  380. XGetWindowAttributes(display, window, &gwa);
  381. Visual* visual = gwa.visual;
  382. _w = gwa.width;
  383. _h = gwa.height;
  384. XVisualInfo templ;
  385. templ.screen = screen;
  386. templ.visualid = XVisualIDFromVisual(visual);
  387. int outCount;
  388. visualInfo = XGetVisualInfo(display, VisualScreenMask | VisualIDMask,
  389. &templ, &outCount);
  390. if (!visualInfo) {
  391. std::cerr << program << ": could not retrieve visual information for "
  392. "root window" << std::endl;
  393. return 0;
  394. }
  395. } else {
  396. # define R GLX_RED_SIZE
  397. # define G GLX_GREEN_SIZE
  398. # define B GLX_BLUE_SIZE
  399. # define D GLX_DEPTH_SIZE
  400. # define I GLX_BUFFER_SIZE
  401. # define DB GLX_DOUBLEBUFFER
  402. static int attributeLists[][20] = {
  403. { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, DB, None }, // rgb double
  404. { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, DB, None },
  405. { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, DB, None },
  406. { GLX_RGBA, R, 8, G, 8, B, 8, D, 8, None }, // rgb single
  407. { GLX_RGBA, R, 4, G, 4, B, 4, D, 4, None },
  408. { GLX_RGBA, R, 2, G, 2, B, 2, D, 2, None },
  409. { I, 8, D, 8, DB, None }, // cmap double
  410. { I, 4, D, 4, DB, None },
  411. { I, 8, D, 8, None }, // cmap single
  412. { I, 4, D, 4, None },
  413. { GLX_RGBA, R, 1, G, 1, B, 1, D, 1, None } // monochrome
  414. };
  415. int fullWidth = WidthOfScreen(DefaultScreenOfDisplay(display));
  416. int fullHeight = HeightOfScreen(DefaultScreenOfDisplay(display));
  417. if (_fullScreen) {
  418. _w = fullWidth;
  419. _h = fullHeight;
  420. _x = _y = 0;
  421. _useOffset = true;
  422. } else if (_useOffset) {
  423. if (_reverseX)
  424. _x = fullWidth - _w - _x;
  425. if (_reverseY)
  426. _y = fullHeight - _h - _y;
  427. }
  428. for (
  429. unsigned int i = 0;
  430. i < sizeof(attributeLists) / sizeof(*attributeLists);
  431. ++i
  432. ) {
  433. visualInfo = glXChooseVisual(display, screen, attributeLists[i]);
  434. if (visualInfo) break;
  435. }
  436. if (!visualInfo) {
  437. std::cerr << program <<
  438. ": could not find a GL-capable visual on display " <<
  439. _displayName << std::endl;
  440. return 0;
  441. }
  442. depth = visualInfo->depth;
  443. XSetWindowAttributes swa;
  444. swa.colormap = getColormap();
  445. swa.border_pixel = swa.background_pixel = swa.backing_pixel =
  446. BlackPixel(display, screen);
  447. swa.event_mask = KeyPressMask | KeyReleaseMask | ButtonPressMask |
  448. ButtonReleaseMask | PointerMotionMask | EnterWindowMask |
  449. LeaveWindowMask | StructureNotifyMask;
  450. window = XCreateWindow(display, RootWindow(display, screen),
  451. _x, _y, _w, _h, 0, visualInfo->depth, InputOutput, visualInfo->visual,
  452. CWBorderPixel | CWBackPixel | CWBackingPixel | CWColormap | CWEventMask,
  453. &swa);
  454. TRACE("Created window: 0x" << std::hex << window << std::dec);
  455. XSizeHints hints;
  456. hints.flags = USSize;
  457. hints.width = _w;
  458. hints.height = _h;
  459. if (_useOffset) {
  460. hints.flags |= USPosition;
  461. hints.x = _x;
  462. hints.y = _y;
  463. }
  464. XWMHints wmHints;
  465. wmHints.flags = InputHint;
  466. wmHints.input = True;
  467. XmbSetWMProperties(display, window, Hack::getName().c_str(),
  468. Hack::getName().c_str(), argv, argc, &hints, &wmHints, NULL);
  469. }
  470. int temp;
  471. if (glXGetConfig(display, visualInfo, GLX_DOUBLEBUFFER, &temp)) {
  472. std::cerr << program <<
  473. ": could not get GLX_DOUBLEBUFFER attribute from visual 0x" <<
  474. std::hex << visualInfo->visualid << std::dec << std::endl;
  475. return 0;
  476. }
  477. doubleBuffered = (temp != False);
  478. context = glXCreateContext(display, visualInfo, NULL, True);
  479. if (!context) {
  480. std::cerr << program << ": could not create rendering context" << std::endl;
  481. return 0;
  482. }
  483. if (!glXMakeCurrent(display, window, context)) {
  484. std::cerr << program << ": could not activate rendering context" <<
  485. std::endl;
  486. return 0;
  487. }
  488. return window;
  489. }
  490. void Common::updateAttributes() {
  491. XWindowAttributes attributes;
  492. XGetWindowAttributes(display, window, &attributes);
  493. width = attributes.width;
  494. height = attributes.height;
  495. depth = attributes.depth;
  496. centerX = width >> 1;
  497. centerY = height >> 1;
  498. aspectRatio = float(width) / float(height);
  499. colormap = attributes.colormap;
  500. }
  501. #ifndef NDEBUG
  502. void Common::dumpErrors(const std::string& func) {
  503. GLenum error;
  504. while ( (error = glGetError()) )
  505. WARN(func << ": " << gluErrorString(error));
  506. GLint i;
  507. glGetIntegerv(GL_ATTRIB_STACK_DEPTH, &i);
  508. if (i > 1) WARN(func << ": GL_ATTRIB_STACK_DEPTH == " << i);
  509. glGetIntegerv(GL_MODELVIEW_STACK_DEPTH, &i);
  510. if (i > 1) WARN(func << ": GL_MODELVIEW_STACK_DEPTH == " << i);
  511. glGetIntegerv(GL_NAME_STACK_DEPTH, &i);
  512. if (i > 1) WARN(func << ": GL_NAME_STACK_DEPTH == " << i);
  513. glGetIntegerv(GL_PROJECTION_STACK_DEPTH, &i);
  514. if (i > 1) WARN(func << ": GL_PROJECTION_STACK_DEPTH == " << i);
  515. glGetIntegerv(GL_TEXTURE_STACK_DEPTH, &i);
  516. if (i > 1) WARN(func << ": GL_TEXTURE_STACK_DEPTH == " << i);
  517. }
  518. #endif // !NDEBUG
  519. const char * program_name;
  520. int main(int argc, char** argv) {
  521. int exit_code = EXIT_FAILURE;
  522. Common::program = argv[0];
  523. program_name = Hack::getShortName().c_str();
  524. argp_program_version = PACKAGE_STRING;
  525. argp_program_bug_address = "<" PACKAGE_BUGREPORT ">";
  526. struct argp_option options[] = {
  527. { NULL, 0, NULL, 0, "Help options:", -1 },
  528. { NULL, 0, NULL, 0, "Common options:" },
  529. { "root", Common::ARG_ROOT, NULL, 0, "Draw on the root window" },
  530. { "geometry", Common::ARG_GEOMETRY, "GEOM", 0,
  531. "Draw on a window of the specified geometry (WxH+X+Y)" },
  532. { "fullscreen", Common::ARG_FULLSCREEN, NULL, 0,
  533. "Draw on a maximized window" },
  534. { "window-id", Common::ARG_WINDOWID, "ID", OPTION_HIDDEN },
  535. { "resource-dir", Common::ARG_RESOURCE_DIR, "DIR", OPTION_HIDDEN },
  536. #ifndef NDEBUG
  537. { "fps", Common::ARG_FPS, NULL, OPTION_HIDDEN },
  538. #endif // !NDEBUG
  539. {}
  540. };
  541. struct argp_child child[] = {
  542. { Hack::getParser(), 0, "" },
  543. {}
  544. };
  545. struct argp parser =
  546. { options, Common::parse, NULL, NULL, child };
  547. std::srand((unsigned int)std::time(NULL));
  548. // Use ARGP_LONG_ONLY to follow XScreenSaver tradition
  549. if (argp_parse(&parser, argc, argv, ARGP_LONG_ONLY, NULL, NULL))
  550. return EXIT_FAILURE;
  551. try {
  552. Common::init(argc, argv);
  553. Common::run();
  554. exit_code = EXIT_SUCCESS;
  555. } catch (Common::Exception e) {
  556. WARN(e);
  557. }
  558. Common::fini();
  559. return exit_code;
  560. }