PageRenderTime 96ms CodeModel.GetById 9ms app.highlight 75ms RepoModel.GetById 1ms app.codeStats 1ms

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