comparison src/iris/iris.c @ 116:a578bf9b2851 trunk

[svn] - iris vis plugin port
author nenolod
date Tue, 24 Oct 2006 21:25:31 -0700
parents
children
comparison
equal deleted inserted replaced
115:2e77e3fdd3c1 116:a578bf9b2851
1 /* Iris - visualization plugin for XMMS
2 * Copyright (C) 2000-2002 Cédric DELFOSSE (cdelfosse@free.fr)
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
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 #include "config.h"
20
21 #include <X11/Xlib.h>
22 #include <X11/keysym.h>
23 #include <gtk/gtk.h>
24 #include <math.h>
25
26 #include <GL/gl.h>
27 #include <GL/glx.h>
28 #include <X11/extensions/xf86vmode.h>
29 #include <pthread.h>
30 #ifdef HAVE_SCHED_SETSCHEDULER
31 #include <sched.h>
32 #endif
33 #include <stdlib.h>
34 #include <sys/time.h>
35 #include <unistd.h>
36
37 #include <audacious/plugin.h>
38 #include <audacious/util.h>
39 #include <audacious/beepctrl.h>
40 #include <audacious/configdb.h>
41 #include "iris.h"
42
43 #define BEAT_MAX 100
44
45 GLfloat y_angle = 45.0, y_speed = 0.5;
46 GLfloat x_angle = 20.0, x_speed = 0.0;
47 GLfloat z_angle = 0.0, z_speed = 0.0;
48 static GLfloat scale;
49 static GLfloat x_angle_wanted;
50
51 GLWindow GLWin;
52
53 /* attributes for a single buffered visual in RGBA format with at least
54 * 4 bits per color and a 16 bit depth buffer */
55 static int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 4,
56 GLX_GREEN_SIZE, 4,
57 GLX_BLUE_SIZE, 4,
58 GLX_DEPTH_SIZE, 16,
59 None
60 };
61
62 /* attributes for a double buffered visual in RGBA format with at least
63 * 4 bits per color and a 16 bit depth buffer */
64 static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER,
65 GLX_RED_SIZE, 4,
66 GLX_GREEN_SIZE, 4,
67 GLX_BLUE_SIZE, 4,
68 GLX_DEPTH_SIZE, 16,
69 None
70 };
71
72 static gboolean going = FALSE;
73 static gboolean initialized = FALSE;
74 static gboolean grabbed_pointer = FALSE, firsttime = TRUE;
75 static Atom wmDelete;
76 static pthread_t draw_thread;
77
78 datas_t datas;
79
80 static int num_bands = NUM_BANDS;
81 static int beat = 0;
82
83 unsigned int transition_frames = 0;
84 unsigned int max_transition_frames = 0;
85
86 static void iris_init (void);
87 static void iris_cleanup (void);
88 static void iris_about (void);
89 static void iris_playback_start (void);
90 static void iris_playback_stop (void);
91 static void iris_render_freq (gint16 data[][256]);
92
93 static float dps = 0;
94
95 gint beat_before = -1;
96
97 extern iris_config newconfig;
98
99 VisPlugin iris_vp = {
100 NULL,
101 NULL,
102 0,
103 NULL, /* Description */
104 0,
105 1,
106 iris_init, /* init */
107 iris_cleanup, /* cleanup */
108 iris_about, /* about */
109 iris_configure, /* configure */
110 NULL, /* disable_plugin */
111 iris_playback_start, /* playback_start */
112 iris_playback_stop, /* playback_stop */
113 NULL, /* render_pcm */
114 iris_render_freq /* render_freq */
115 };
116
117
118 void
119 about_close_clicked (GtkWidget * w, GtkWidget ** window)
120 {
121 gtk_widget_destroy (*window);
122 *window = NULL;
123 }
124
125
126 void
127 about_closed (GtkWidget * w, GdkEvent * e, GtkWidget ** window)
128 {
129 about_close_clicked (w, window);
130 }
131
132
133 static void
134 iris_about (void)
135 {
136 static GtkWidget *window_about = NULL;
137 GtkWidget *vbox, *button, *close, *label;
138
139 if (window_about)
140 return;
141
142 window_about = gtk_window_new (GTK_WINDOW_TOPLEVEL);
143 gtk_window_set_title (GTK_WINDOW (window_about), "About IRIS plugin");
144 gtk_window_set_policy (GTK_WINDOW (window_about), FALSE, FALSE, FALSE);
145 gtk_window_set_position (GTK_WINDOW (window_about), GTK_WIN_POS_MOUSE);
146 vbox = gtk_vbox_new (FALSE, 4);
147 gtk_container_add (GTK_CONTAINER (window_about), vbox);
148 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
149 gtk_widget_show (vbox);
150
151 label = gtk_label_new ("\n\
152 Iris XMMS Plugin.\n\
153 Copyright (C) 2001-2003, Cédric Delfosse.\n\
154 \n\
155 Email: <cdelfosse@free.fr> \n\
156 Homepage: <http://cdelfosse.free.fr/xmms-iris/>\n\
157 Development: <http://savannah.gnu.org/projects/iris/>\n\
158 \n\
159 Authors:\n\
160 Cédric Delfosse\n\
161 Marinus Schraal\n\
162 Ron Lockwood-Childs\n\
163 Ported to Audacious by Arthur Taylor\n\
164 \n\
165 This program is free software; you can redistribute it and/or modify\n\
166 it under the terms of the GNU General Public License as published by\n\
167 the Free Software Foundation; either version 2 of the License, or\n\
168 (at your option) any later version.\n\
169 \n\
170 This program is distributed in the hope that it will be useful,\n\
171 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
172 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
173 GNU General Public License for more details.\n\
174 \n\
175 You should have received a copy of the GNU General Public License\n\
176 along with this program; if not, write to the Free Software\n\
177 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307\n\
178 USA");
179 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 8);
180 gtk_widget_show (label);
181
182 button = gtk_hbutton_box_new ();
183 gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 8);
184 gtk_widget_show (button);
185
186 close = gtk_button_new_with_label ("Close");
187 GTK_WIDGET_SET_FLAGS (close, GTK_CAN_DEFAULT);
188 gtk_window_set_default (GTK_WINDOW (window_about), close);
189 gtk_hbutton_box_set_layout_default (GTK_BUTTONBOX_END);
190 gtk_box_pack_end (GTK_BOX (button), close, FALSE, FALSE, 8);
191 gtk_widget_show (close);
192
193 gtk_signal_connect (GTK_OBJECT (close), "clicked",
194 GTK_SIGNAL_FUNC (about_close_clicked), &window_about);
195 gtk_signal_connect (GTK_OBJECT (window_about), "delete-event",
196 GTK_SIGNAL_FUNC (about_closed), &window_about);
197
198 gtk_widget_show (window_about);
199 }
200
201
202 /* Creates an empty cursor icon for when hovering over our window content */
203 void
204 hide_cursor ()
205 {
206 Cursor no_ptr;
207 Pixmap bm_no;
208 Colormap cmap;
209 XColor black, dummy;
210
211 static unsigned char bm_no_data[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
212
213 cmap = DefaultColormap (GLWin.dpy, DefaultScreen (GLWin.dpy));
214 XAllocNamedColor (GLWin.dpy, cmap, "black", &black, &dummy);
215 bm_no = XCreateBitmapFromData (GLWin.dpy, GLWin.window, bm_no_data, 8, 8);
216 no_ptr =
217 XCreatePixmapCursor (GLWin.dpy, bm_no, bm_no, &black, &black, 0, 0);
218 XDefineCursor (GLWin.dpy, GLWin.window, no_ptr);
219 }
220
221
222 /* this function creates our window and sets it up properly */
223 static Window
224 create_window (char *title)
225 {
226 XVisualInfo *vi;
227 int i;
228
229 /* get a connection */
230 GLWin.dpy = XOpenDisplay (0);
231 if (GLWin.dpy == NULL)
232 g_log (NULL, G_LOG_LEVEL_CRITICAL,
233 __FILE__ ": XOpenDisplay returns NULL");
234 GLWin.screen = DefaultScreen (GLWin.dpy);
235
236 /* get an appropriate visual */
237 vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListDbl);
238 if (vi == NULL)
239 {
240 vi = glXChooseVisual (GLWin.dpy, GLWin.screen, attrListSgl);
241 GLWin.DoubleBuffered = 0;
242 }
243 else
244 GLWin.DoubleBuffered = 1;
245
246 if (vi == NULL)
247 g_log (NULL, G_LOG_LEVEL_CRITICAL,
248 __FILE__ ": glXChooseVisual returns NULL");
249
250 glXQueryVersion (GLWin.dpy, &GLWin.glxMajorVersion, &GLWin.glxMinorVersion);
251 /* create a GLX context */
252 GLWin.ctx = glXCreateContext (GLWin.dpy, vi, 0, GL_TRUE);
253 if (GLWin.ctx == NULL)
254 g_log (NULL, G_LOG_LEVEL_CRITICAL,
255 __FILE__ ": glXCreateContext can\'t create a rendering context");
256
257 /* create a color map */
258 GLWin.attr.colormap =
259 XCreateColormap (GLWin.dpy, RootWindow (GLWin.dpy, vi->screen),
260 vi->visual, AllocNone);
261 GLWin.attr.border_pixel = 0;
262
263 if ((config.fullscreen && firsttime) || GLWin.fs)
264 {
265 int bestMode = 0;
266
267 GLWin.fs = True;
268 GLWin.fs_width = config.fs_width;
269 GLWin.fs_height = config.fs_height;
270 /* look for mode with requested resolution */
271 for (i = 0; i < GLWin.modeNum; i++)
272 {
273 if ((GLWin.modes[i]->hdisplay == GLWin.fs_width)
274 && (GLWin.modes[i]->vdisplay == GLWin.fs_height))
275 {
276 bestMode = i;
277 }
278 }
279 XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen,
280 GLWin.modes[bestMode]);
281 XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0);
282 GLWin.fs_width = GLWin.modes[bestMode]->hdisplay;
283 GLWin.fs_height = GLWin.modes[bestMode]->vdisplay;
284
285 /* create a fullscreen window */
286 GLWin.attr.override_redirect = True;
287 GLWin.attr.event_mask =
288 ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
289 GLWin.window =
290 XCreateWindow (GLWin.dpy,
291 RootWindow (GLWin.dpy, vi->screen), 0,
292 0, GLWin.fs_width, GLWin.fs_height, 0, vi->depth,
293 InputOutput, vi->visual,
294 CWBorderPixel | CWColormap |
295 CWEventMask | CWOverrideRedirect, &GLWin.attr);
296 XWarpPointer (GLWin.dpy, None, GLWin.window, 0, 0, 0, 0, 0, 0);
297 XMapRaised (GLWin.dpy, GLWin.window);
298 XGrabKeyboard (GLWin.dpy, GLWin.window, True, GrabModeAsync,
299 GrabModeAsync, CurrentTime);
300 XGrabPointer (GLWin.dpy, GLWin.window, True, ButtonPressMask,
301 GrabModeAsync, GrabModeAsync, GLWin.window,
302 FALSE, CurrentTime);
303 }
304 else
305 {
306 XClassHint xclasshint={"xmms","visplugin"};
307 GLWin.window_width = config.window_width;
308 GLWin.window_height = config.window_height;
309 /* needed if those aren't set yet - ideally need a way to get the default values for these */
310 if (config.window_height == 0 || config.window_width == 0)
311 {
312 config.window_width = 640;
313 config.window_height = 480;
314 }
315
316 /* create a window in window mode */
317 GLWin.attr.event_mask =
318 ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask;
319 GLWin.window =
320 XCreateWindow (GLWin.dpy,
321 RootWindow (GLWin.dpy, vi->screen), 0,
322 0, GLWin.window_width, GLWin.window_height, 0,
323 vi->depth, InputOutput, vi->visual,
324 CWBorderPixel | CWColormap | CWEventMask, &GLWin.attr);
325 XmbSetWMProperties(GLWin.dpy, GLWin.window, "iris","iris", NULL, 0, NULL, NULL, &xclasshint);
326
327 /* only set window title and handle wm_delete_events if in windowed mode */
328 wmDelete = XInternAtom (GLWin.dpy, "WM_DELETE_WINDOW", True);
329 XSetWMProtocols (GLWin.dpy, GLWin.window, &wmDelete, 1);
330 XSetStandardProperties (GLWin.dpy, GLWin.window, title,
331 title, None, NULL, 0, NULL);
332 XMapRaised (GLWin.dpy, GLWin.window);
333 }
334 /* connect the glx-context to the window */
335 if (!glXMakeCurrent (GLWin.dpy, GLWin.window, GLWin.ctx))
336 g_log (NULL, G_LOG_LEVEL_CRITICAL,
337 __FILE__ ": glXMakeCurrent returns an error");
338 if (glXIsDirect (GLWin.dpy, GLWin.ctx))
339 GLWin.DRI = 1;
340
341 hide_cursor ();
342
343 XFree (vi);
344 firsttime = FALSE;
345 return GLWin.window;
346 }
347
348
349 VisPlugin *
350 get_vplugin_info (void)
351 {
352 iris_vp.description = g_strdup_printf ("iris 3D analyzer %s", VERSION);
353 return &iris_vp;
354 }
355
356
357 static int
358 detect_beat (gint32 loudness)
359 {
360 static gint32 aged; /* smoothed out oudness */
361 static gint32 lowest; /* quietest point in current beat */
362 static int elapsed; /* frames since last beat */
363 static int isquiet; /* was previous frame quiet */
364 int detected_beat;
365
366 /* Incorporate the current loudness into history */
367 aged = (aged * 7 + loudness) >> 3;
368 elapsed++;
369
370 /* If silent, then clobber the beat */
371 if (aged < 2000 || elapsed > BEAT_MAX)
372 {
373 elapsed = 0;
374 lowest = aged;
375 }
376 else if (aged < lowest)
377 lowest = aged;
378
379 /* Beats are detected by looking for a sudden loudness after a lull.
380 * They are also limited to occur no more than once every 15 frames,
381 * so the beat flashes don't get too annoying.
382 */
383 detected_beat = (loudness * 4 > aged * 3 && aged * 2 > lowest * 3
384 && elapsed > 15);
385 if (detected_beat)
386 lowest = aged, elapsed = 0;
387
388 /* Silence is computed from the aged loudness. The quietref value is
389 * set to TRUE only at the start of silence, not throughout the silent
390 * period. Also, there is some hysteresis so that silence followed
391 * by a slight noise and more silence won't count as two silent
392 * periods -- that sort of thing happens during many fade edits, so
393 * we have to account for it.
394 */
395 if (aged < (isquiet ? 1500 : 500))
396 /* Quiet now -- is this the start of quiet? */
397 isquiet = TRUE;
398 else
399 isquiet = FALSE;
400
401 /* return the result */
402 return detected_beat;
403 }
404
405
406 /* limit_rotation_speed keep rotation speed to at least dps_config
407 by second */
408 static void
409 limit_rotation_speed (gboolean init)
410 {
411 static struct timeval tv_past;
412 struct timezone tz;
413 struct timeval tv_now;
414 float dps_config = 15;
415
416 if (!init)
417 {
418 long t;
419
420 gettimeofday (&tv_now, &tz);
421 t =
422 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec -
423 tv_past.tv_usec);
424 dps = y_speed * ((float) 1000000 / (float) t);
425 if (dps >= (float) dps_config)
426 y_speed -= 0.02;
427 else
428 y_speed += 0.02;
429
430 memcpy (&tv_past, &tv_now, sizeof (struct timeval));
431 }
432 else
433 gettimeofday (&tv_past, &tz);
434 }
435
436
437 /* limit_fps tries to keep the number of
438 frame per seconds to config.fps */
439 static void
440 limit_fps (gboolean init)
441 {
442 static float fps = 0;
443 static struct timeval tv_past;
444 struct timezone tz;
445 struct timeval tv_now;
446 static int usec = 0;
447
448 if (init)
449 gettimeofday (&tv_past, &tz);
450 else
451 {
452 long t;
453
454 gettimeofday (&tv_now, &tz);
455 t =
456 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec -
457 tv_past.tv_usec);
458 fps = (float) 1000000 / (float) t;
459
460 if (fps >= (float) config.fps)
461 usec += 100;
462 else
463 {
464 if (usec > 0)
465 usec -= 100;
466 }
467 xmms_usleep (usec);
468 memcpy (&tv_past, &tv_now, sizeof (struct timeval));
469 }
470 }
471
472 static void
473 init_general_draw_mode (int num)
474 {
475 int transparency;
476 int wireframe;
477
478 if (theme[num].config->global->transparency != -1)
479 transparency = theme[num].config->global->transparency;
480 else
481 transparency = (int) (2.0 * rand () / (RAND_MAX + 1.0));
482
483 if (theme[num].config->global->wireframe != -1)
484 wireframe = theme[num].config->global->wireframe;
485 else
486 wireframe = (int) (2.0 * rand () / (RAND_MAX + 1.0));
487
488 if (transparency)
489 {
490 if (!glIsEnabled (GL_BLEND))
491 {
492 glBlendFunc (GL_SRC_ALPHA, GL_ONE);
493 glEnable (GL_BLEND);
494 }
495 glDisable (GL_DEPTH_TEST);
496 }
497 else
498 {
499 if (glIsEnabled (GL_BLEND))
500 {
501 glDisable (GL_BLEND);
502 }
503 glEnable (GL_DEPTH_TEST);
504 }
505
506 if (wireframe)
507 glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
508 else
509 glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
510
511 if (glIsEnabled(GL_TEXTURE_2D))
512 glDisable(GL_TEXTURE_2D);
513 }
514
515
516 static int
517 compute_theme ()
518 {
519 gfloat f_rand, f;
520 gint i;
521
522 for (i = 0, f_rand = 0.0; i < THEME_NUMBER; i++)
523 f_rand += theme[i].config->global->priority;
524 f_rand = f_rand * rand () / (RAND_MAX + 1.0);
525
526 i = 0;
527 f = 0;
528 while (i < THEME_NUMBER)
529 {
530 gfloat f_theme;
531 f_theme = theme[i].config->global->priority;
532 if (f_theme)
533 {
534 f += f_theme;
535 if (f_rand < f)
536 break;
537 }
538 i++;
539 }
540
541 if (!f)
542 return (int) ((gfloat) THEME_NUMBER * rand () / (RAND_MAX + 1.0));
543 else
544 return i;
545 }
546
547
548 static int
549 choose_theme (gboolean init)
550 {
551 static long sec_btw_theme = 10 * 10000000;
552 static struct timeval tv_past;
553 struct timezone tz;
554 struct timeval tv_now;
555 static int th, th_tmp;
556
557 if (!init)
558 {
559 long t;
560
561 gettimeofday (&tv_now, &tz);
562 t =
563 (tv_now.tv_sec - tv_past.tv_sec) * 10000000 + (tv_now.tv_usec -
564 tv_past.tv_usec);
565 if ((t > sec_btw_theme) || (beat && config.change_theme_on_beat))
566 {
567 /* we come here if:
568 - time for the theme is expired
569 - a beat is detected and the change_theme_on_beat option is on
570 */
571 if (config.transition)
572 {
573 if (transition_frames == 0)
574 {
575 th_tmp = compute_theme ();
576 if (th != th_tmp)
577 {
578 transition_frames =
579 (unsigned int) config.fps * config.trans_duration /
580 10;
581 max_transition_frames = transition_frames;
582 memcpy (&tv_past, &tv_now, sizeof (struct timeval));
583 init_theme_transition (transition_frames,
584 max_transition_frames);
585 }
586 }
587 }
588 else
589 {
590 th = compute_theme ();
591 init_general_draw_mode (th);
592 if (theme[th].init_draw_mode != NULL)
593 theme[th].init_draw_mode ();
594 memcpy (&tv_past, &tv_now, sizeof (struct timeval));
595 x_angle_wanted = theme[th].get_x_angle ();
596 x_speed = copysign (0.08, x_angle_wanted - x_angle);
597 }
598 }
599 /* change theme after half of the set frames have passed */
600 else if ((((int) max_transition_frames / 2) == transition_frames)
601 && config.transition && (transition_frames != 0))
602 {
603 th = th_tmp;
604 init_general_draw_mode (th);
605 if (theme[th].init_draw_mode != NULL)
606 theme[th].init_draw_mode ();
607 x_angle_wanted = theme[th].get_x_angle ();
608 x_speed = copysign (0.08, x_angle_wanted - x_angle);
609 }
610 }
611 else
612 {
613 /* the first time choose_theme is called,
614 tv_past is initialized */
615 gettimeofday (&tv_past, &tz);
616 th = compute_theme ();
617 init_general_draw_mode (th);
618 if (theme[th].init_draw_mode != NULL)
619 theme[th].init_draw_mode ();
620 }
621
622 return (th);
623 }
624
625
626 static void
627 draw_iris (void)
628 {
629 int th;
630
631 limit_fps (FALSE);
632 th = choose_theme (FALSE);
633
634 if ((config.color_beat) && (beat_before > 0))
635 glClearColor (config.color_flash_red, config.color_flash_green,
636 config.color_flash_blue, 1);
637 else
638 glClearColor (config.bgc_red, config.bgc_green, config.bgc_blue, 1);
639 glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
640
641 glPushMatrix ();
642 glTranslatef (0.0, -0.5, -7.0);
643 glRotatef (x_angle, 1.0, 0.0, 0.0);
644 glRotatef (y_angle, 0.0, 1.0, 0.0);
645 glRotatef (z_angle, 0.0, 0.0, 1.0);
646
647 if (transition_frames > 0 && config.transition)
648 {
649 theme_transition ();
650 transition_frames--;
651 }
652
653 theme[th].draw_one_frame (beat);
654
655 glEnd ();
656 glPopMatrix ();
657
658 glXSwapBuffers (GLWin.dpy, GLWin.window);
659 }
660
661
662 static gint
663 disable_func (gpointer data)
664 {
665 iris_vp.disable_plugin (&iris_vp);
666 return FALSE;
667 }
668
669
670 GLvoid
671 init_gl (GLvoid)
672 {
673 glMatrixMode (GL_PROJECTION);
674 glLoadIdentity ();
675 glFrustum (-1, 1, -1.5, 1, 1.5, 12);
676 glMatrixMode (GL_MODELVIEW);
677 glLoadIdentity ();
678 }
679
680
681 GLvoid
682 kill_gl_window (GLvoid)
683 {
684 if (GLWin.ctx)
685 {
686 glXMakeCurrent (GLWin.dpy, None, NULL);
687 glXDestroyContext (GLWin.dpy, GLWin.ctx);
688 GLWin.ctx = NULL;
689 }
690 /* switch back to original desktop resolution if we were in fs */
691 if (GLWin.fs)
692 {
693 XF86VidModeSwitchToMode (GLWin.dpy, GLWin.screen, &GLWin.deskMode);
694 XF86VidModeSetViewPort (GLWin.dpy, GLWin.screen, 0, 0);
695 }
696 }
697
698
699 void *
700 draw_thread_func (void *arg)
701 {
702 Bool configured = FALSE;
703
704 g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Starting.");
705
706 if ((GLWin.window = create_window ("Iris")) == 0)
707 {
708 g_log (NULL, G_LOG_LEVEL_CRITICAL,
709 __FILE__ ": unable to create window");
710 pthread_exit (NULL);
711 }
712
713 init_gl ();
714 choose_theme (TRUE);
715
716
717 #ifdef HAVE_SCHED_SETSCHEDULER
718 if (xmms_check_realtime_priority ())
719 {
720 struct sched_param sparam;
721 sparam.sched_priority = sched_get_priority_max (SCHED_OTHER);
722 pthread_setschedparam (pthread_self (), SCHED_OTHER, &sparam);
723 }
724 #endif
725
726 while (going) /* plugin enabled */
727 {
728 while (XPending (GLWin.dpy))
729 {
730 XEvent event;
731 KeySym keysym;
732 char buf[16];
733
734 XNextEvent (GLWin.dpy, &event);
735 switch (event.type)
736 {
737 case Expose:
738 if (event.xexpose.count != 0)
739 break;
740 configured = TRUE;
741 break;
742 case ConfigureNotify:
743 glViewport (0, 0, event.xconfigure.width,
744 event.xconfigure.height);
745 configured = TRUE;
746 break;
747 case KeyPress:
748
749 XLookupString (&event.xkey, buf, 16, &keysym, NULL);
750 switch (keysym)
751 {
752 case XK_Escape:
753
754 /* Ugly hack to get the disable_plugin call in the main thread. */
755 GDK_THREADS_ENTER ();
756 gtk_idle_add (disable_func, NULL);
757 GDK_THREADS_LEAVE ();
758 break;
759 case XK_z:
760 xmms_remote_playlist_prev (iris_vp.xmms_session);
761 break;
762 case XK_x:
763 xmms_remote_play (iris_vp.xmms_session);
764 break;
765 case XK_c:
766 xmms_remote_pause (iris_vp.xmms_session);
767 break;
768 case XK_v:
769 xmms_remote_stop (iris_vp.xmms_session);
770 break;
771 case XK_b:
772 xmms_remote_playlist_next (iris_vp.xmms_session);
773 break;
774 case XK_Left:
775 y_speed -= 0.1;
776 if (y_speed < -3.0)
777 y_speed = -3.0;
778 break;
779 case XK_Right:
780 y_speed += 0.1;
781 if (y_speed > 3.0)
782 y_speed = 3.0;
783 break;
784 case XK_Return:
785 x_speed = 0.0;
786 y_speed = 0.3;
787 z_speed = 0.0;
788 x_angle = 70.0;
789 y_angle = 45.0;
790 z_angle = 0.0;
791 break;
792 case XK_Tab:
793 iris_configure ();
794 break;
795 case XK_f:
796 //iris_save_window_attributes ();
797 kill_gl_window ();
798 XCloseDisplay (GLWin.dpy);
799 GLWin.fs = !GLWin.fs;
800 create_window ("Iris");
801 init_gl ();
802 choose_theme (TRUE);
803 break;
804 }
805
806 break;
807 case ClientMessage:
808 if ((Atom) event.xclient.data.l[0] == wmDelete)
809 {
810 GDK_THREADS_ENTER ();
811 gtk_idle_add (disable_func, NULL);
812 GDK_THREADS_LEAVE ();
813 }
814 break;
815 }
816 }
817 if (configured)
818 {
819 limit_rotation_speed (FALSE);
820
821 if ((x_angle > x_angle_wanted) && (x_speed > 0))
822 x_angle = x_angle_wanted;
823 else if ((x_angle < x_angle_wanted) && (x_speed < 0))
824 x_angle = x_angle_wanted;
825
826 x_angle += x_speed;
827 if (x_angle > 85.0)
828 x_angle = 85.0;
829 else if (x_angle < 0.0)
830 x_angle = 0.0;
831
832 y_angle += y_speed;
833 if (y_angle >= 360.0)
834 y_angle -= 360.0;
835
836 z_angle += z_speed;
837 if (z_angle >= 360.0)
838 z_angle -= 360.0;
839
840 draw_iris ();
841 }
842 }
843
844 g_log (NULL, G_LOG_LEVEL_DEBUG, __FILE__ ": draw_thread_func: Exiting.");
845 pthread_exit (NULL);
846 }
847
848
849 static void
850 start_display (void)
851 {
852 scale = 1.0 / log (256.0);
853
854 x_speed = 0.0;
855 y_speed = 0.3;
856 z_speed = 0.0;
857 x_angle = 45.0;
858 y_angle = 45.0;
859 z_angle = 0.0;
860
861 going = TRUE;
862 limit_fps (TRUE);
863 limit_rotation_speed (TRUE);
864 if (pthread_create (&draw_thread, NULL, draw_thread_func, NULL))
865 g_log (NULL, G_LOG_LEVEL_CRITICAL, __FILE__ ": pthread_create: Can't create drawing thread.");
866
867 }
868
869
870 static void
871 stop_display (void)
872 {
873 if (going)
874 {
875 going = FALSE;
876 pthread_join (draw_thread, NULL);
877 }
878
879 kill_gl_window ();
880
881 if (GLWin.window)
882 {
883 if (grabbed_pointer)
884 {
885 XUngrabPointer (GLWin.dpy, CurrentTime);
886 grabbed_pointer = FALSE;
887 }
888
889 XDestroyWindow (GLWin.dpy, GLWin.window);
890 GLWin.window = 0;
891 }
892 XCloseDisplay (GLWin.dpy);
893 }
894
895
896 static void
897 iris_init (void)
898 {
899 int i;
900
901 initialized = TRUE;
902 iris_first_init ();
903 datas.loudness = 0;
904 /* if the config window is open, we don't want to trash the config the user
905 * has done */
906 if (!config_window)
907 iris_config_read ();
908 for (i = 0; i < THEME_NUMBER; i++)
909 if (theme[i].init != NULL)
910 theme[i].init ();
911 srand (666);
912 start_display ();
913 }
914
915
916 static void
917 iris_cleanup (void)
918 {
919 int i;
920
921 if(initialized)
922 {
923 stop_display ();
924 for (i = 0; i < THEME_NUMBER; i++)
925 if (theme[i].cleanup != NULL)
926 theme[i].cleanup ();
927 }
928 }
929
930
931 static void
932 iris_playback_start (void)
933 {
934 }
935
936
937 static void
938 iris_playback_stop (void)
939 {
940 }
941
942
943 static void
944 iris_render_freq (gint16 data[2][256])
945 {
946 GLfloat val;
947 static int angle = 0;
948 int i;
949
950 for (i = 0; i < num_bands; i++)
951 {
952 int y, c;
953 gint xscale[] =
954 { 0, 1, 2, 3, 5, 7, 10, 14, 20, 28, 40, 54, 74, 101, 137, 156,
955 255
956 };
957 gint xscale8[] = { 0, 2, 5, 10, 20, 40, 74, 137, 255 };
958
959 if (num_bands == 16)
960 for (c = xscale[i], y = 0; c < xscale[i + 1]; c++)
961 {
962 if (data[0][c] > y)
963 y = data[0][c];
964 }
965 else
966 for (c = xscale8[i], y = 0; c < xscale8[i + 1]; c++)
967 {
968 if (data[0][c] > y)
969 y = data[0][c];
970 }
971
972 datas.loudness +=
973 (y / (xscale[i + 1] - xscale[i] + 1)) * (abs (i - NUM_BANDS / 2) +
974 NUM_BANDS / 2) * (4 + i);
975
976 y >>= 7;
977 if (y > 0)
978 val = (log (y) * scale);
979 else
980 val = 0;
981 datas.data360[angle][i] = val;
982 datas.data1[i] = val;
983 }
984
985 datas.loudness /= (NUM_BANDS * 4);
986
987 beat = detect_beat (datas.loudness);
988 if (beat)
989 {
990 beat_before = config.flash_speed;
991 if (dps <= 90.0)
992 y_speed += 0.7;
993
994 if (config.bgc_random)
995 {
996 config.bgc_red = 1.0 * rand () / (RAND_MAX + 1.0);
997 config.bgc_green = 1.0 * rand () / (RAND_MAX + 1.0);
998 config.bgc_blue = 1.0 * rand () / (RAND_MAX + 1.0);
999 }
1000
1001 if (config.color12_random)
1002 {
1003 config.color1_red = 1.0 * rand () / (RAND_MAX + 1.0);
1004 config.color1_green = 1.0 * rand () / (RAND_MAX + 1.0);
1005 config.color1_blue = 1.0 * rand () / (RAND_MAX + 1.0);
1006 config.color2_red = 1.0 * rand () / (RAND_MAX + 1.0);
1007 config.color2_green = 1.0 * rand () / (RAND_MAX + 1.0);
1008 config.color2_blue = 1.0 * rand () / (RAND_MAX + 1.0);
1009 }
1010
1011 if (config.color_random)
1012 {
1013 config.color_red = 1.0 * rand () / (RAND_MAX + 1.0);
1014 config.color_green = 1.0 * rand () / (RAND_MAX + 1.0);
1015 config.color_blue = 1.0 * rand () / (RAND_MAX + 1.0);
1016 }
1017 }
1018
1019 if (beat_before > 0)
1020 beat_before--;
1021
1022 angle++;
1023 if (angle == 360)
1024 angle = 0;
1025 }