/* ===========================================================================
 * AScd: the AfterStep and WindowMaker CD player
 * ascd.c: main source
 * ===========================================================================
 * Copyright (c) 1999 Denis Bourez and Rob Malda. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Denis Bourez & Rob Malda
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY DENIS BOUREZ AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL DENIS BOUREZ, ROB MALDA OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 * ===========================================================================
 */

#include <stdio.h>
#include <sys/param.h>
#include <sys/types.h>

#include <stdlib.h>
#include <string.h>
#include <math.h>

#include <X11/Xlib.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>

#include <errno.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>

#include "config.h"
#include "ascd.h"
#include "faktory_prot.h"
#include <workman/workman.h>

XpmIcon alphaXPM;
XpmIcon ralphaXPM;
XpmIcon backXPM;
XpmIcon iconXPM;

/* External functions */

extern time();
extern open();
extern cd_control(int);
extern cd_control_version();

/* the various switches: */

int debug = FALSE;                     /* verbose mode */
unsigned int autoplay = 0;         /* if set, play a disk when it is inserted -> see -a c.l. option  */
unsigned int autoprobe = TRUE;     /* probe the drive, but do not play (autoplay) */
unsigned int intro_mode = 0;       /* play only the beginning of the tracks */
unsigned int autorepeat = FALSE;   /* zzzzzzzzzzzzz */
unsigned int blind_mode = FALSE;   /* no counter updates = no drive LED flashes */
unsigned int loop_mode = 0;        /* loop from loop_1 to loop_2 */
unsigned int loop_start_track = 0; /* track of the beggining of the loop */
unsigned int loop_end_track =0;    /* track of the end of the loop */
unsigned int loop_1 = 0;           /* pos. 1 */
unsigned int loop_2 = 0;           /* pos. 2 */
unsigned int show_db = FALSE;      /* do we have to scroll song names? */
unsigned int show_artist = FALSE;  /* if show_db, do you append artist name? */
unsigned int show_db_pos = 0;      /* internal, used for the song name scrolling */
unsigned int show_icon_db_pos = 0; /* internal, used for the song name scrolling */
unsigned int force_upper = FALSE;  /* all messages must be in uppercase */
int ignore_avoid = FALSE;          /* if set, we play *all* tracks, ignoring 'avoid' tags in database */
unsigned int fast_track = 0;       /* the fast track select method */
unsigned int xflaunch = FALSE;     /* do we launch xfascd when right click on the display? */
unsigned int time_mode = 0;        /* display mode for the counter. see function RedrawWindow() */
unsigned int cue_time = 10;        /* nbr of seconds for cue -> see -c command line option */

/* internals */

int lasttime = -1;
extern char *cd_device;            /* the hardware device pointing to the CD ROM player */
unsigned int datatrack = 0;        /* is the current track a data track ? */
unsigned int direct_access = 0;    /* pos. to reach with the ruler */
unsigned int direct_track = 0;     /* if we want to go directly to another track */
unsigned int wanna_play = FALSE;
unsigned int do_autorepeat = FALSE;
int wanted_track = 0;
unsigned int old_track = 0;
unsigned int anomalie = 0;         /* cd_control return value */

/* let's talk about X... */

Display *Disp;
Window Root;
Window Iconwin;
Window Win;
char *Geometry = 0;
char device[128]=DEFAULTDEVICE;
char xv[128];
int withdrawn=FALSE;
GC WinGC;
int CarrierOn = FALSE;
int screen;

/* everything dealing with the hardware volume: */

int volume = MAX_VOL ;             /* CD volume */
int muted_volume = 0;              /* CD volume in muted mode */
int unmuted_volume = MAX_VOL;      /* CD volume to restore when leaving muted mode */
unsigned int muted = FALSE;        /* is the CD muted? */
unsigned int fade_out = FALSE;     /* do you have to start a fade in/out? */
unsigned int fade_step = 5;        /* the fading speed */
unsigned int fade_ok = 0;          /* can't remember. sorry!!!! */
extern int min_volume,max_volume;  /* from LibWorkMan */
int cur_balance = 10;              /* ? */

char led_text[9];                  /* the 'help' messages */
unsigned int text_timeout = 1;     /* messages timemout in seconds */
long text_start = 0;               /* timeout */

/* misc, have to add explanations here...! */

int selectors_timeout = 0;
int redraw = TRUE;
int has_clicked = FALSE;
int info_modified = 0;
int big_spaces = 0;
int slow_down = 0;

/* ====================================================================
   Faktory: theme subsystem
   ==================================================================== */

char theme[FAK_CMAX];
char selected_theme[FAK_CMAX];
unsigned int theme_select = 0;
unsigned int theme_select_nbr = 0;

unsigned int panel = 1;           /* current panel */
unsigned int panels = 0;          /* how many panels? */
unsigned int but_max = 0;         /* how many buttons? */
unsigned int but_msg = 0;         /* which one is the message zone? */
unsigned int but_counter = 0;     /* which one is the counter? */
unsigned int but_tracknbr = 0;    /* which one is the track number? */
unsigned int but_db = 0;          /* which one is the database zone? */
unsigned int icon_msg = 0;
unsigned int icon_counter = 0;
unsigned int icon_tracknbr = 0;
unsigned int but_current = 0;     /* last selected button */

unsigned int panel_stop = 0;      /* autoswitch to this panel on stop/eject */
unsigned int panel_play = 0;      /* autoswitch to this panel on play */

char th_name[FAK_CMAX];
char th_author[FAK_CMAX];
char th_release[FAK_CMAX];
char th_email[FAK_CMAX];
char th_url[FAK_CMAX];
char th_comment[FAK_CMAX];
char th_alpha1[FAK_CMAX];
char th_alpha2[FAK_CMAX];
char th_background[FAK_CMAX];
char th_icon_window[FAK_CMAX];

int th_no_minus = FALSE;
int th_no_icon_window = FALSE;

struct fak_button thdata[FAK_BMAX];

/*****************************************************************************/

/* ====================================================================
   The optional modules globals:
   ==================================================================== */

#ifdef WMK
#include "wings_global.c"
#endif

#ifdef MIXER
#include "mixer_global.c"
#endif

/*****************************************************************************/

void mouse_events(XEvent Event)
{
    int i;
    int j;
    int do_it = TRUE;

    for (i=1 ; i <= but_max ; i++) {
	if ((thdata[i].panel == panel) || (thdata[i].panel == 0)) {
	    if (strlen(thdata[i].xpm_file) > 0) {
		if ((Event.xbutton.y >= thdata[i].y) && (Event.xbutton.y <= thdata[i].y + thdata[i].xpm.attributes.height)) {
		    if ((Event.xbutton.x >= thdata[i].x) && (Event.xbutton.x <= thdata[i].x + thdata[i].xpm.attributes.width)) {
			if (th_no_icon_window) {
			    /* Special Icon Mode: we must check in which window the user clicked */
			    do_it = TRUE;
			    if (Event.xbutton.window == Iconwin) {
				if (!thdata[i].icon) do_it = FALSE;
			    } else {
				if (thdata[i].icon) do_it = FALSE;
			    }
			}
			/* no commands defined? We skip this one! */
			if (((thdata[i].left != 0) || (thdata[i].mid != 0) || (thdata[i].right != 0)) && (do_it)) {
			    if (debug) fprintf(stderr, "** User selected button %d with mouse button %d\n", i, Event.xbutton.button);
			    j = 0;
			    switch(Event.xbutton.button) {
			    case 1: j = thdata[i].left; break;
			    case 2: j = thdata[i].mid; break;
			    default: j = thdata[i].right; break;
			    }
			    but_current = i;
			    if (debug) fprintf(stderr, "-> command = %d\n", j);
			    if ((j >= 1) && (j <= 49)) fak_event_handle(j, Event);
			    else if ((j >= 50) && (j <= 99)) cd_event_handle(j, Event);
#ifdef MIXER
			    else if ((j >= 100) && (j <= 199)) mixer_event_handle(j, Event);
#endif
			    break;
			}
		    }
		}
	    }
	}
    }

}

/* ------------------------------------------------------------------------
   GUI control
   ------------------------------------------------------------------------ */

Pixel get_color(char *ColorName)
{
    XColor Color;
    XWindowAttributes Attributes;

    XGetWindowAttributes(Disp,Root,&Attributes);
    Color.pixel = 0;

    if (!XParseColor (Disp, Attributes.colormap, ColorName, &Color))
      fprintf(stderr,"ascd: can't parse %s\n", ColorName);
    else if(!XAllocColor (Disp, Attributes.colormap, &Color))
      fprintf(stderr,"ascd: can't allocate %s\n", ColorName);

    return Color.pixel;
}

void create_window(int argc, char **argv)
{
    int i;
    unsigned int borderwidth ;
    char *display_name = NULL;
    char *wname = "AScd";
    XGCValues gcv;
    unsigned long gcm;
    XTextProperty name;
    Pixel back_pix, fore_pix;
    int x_fd;
    int d_depth;
    int ScreenWidth, ScreenHeight;
    XSizeHints SizeHints;
    XWMHints WmHints;
    XClassHint classHint;

    /* Open display */
    if (!(Disp = XOpenDisplay(display_name))) {
	fprintf(stderr,"ascd: can't open display %s\n", XDisplayName(display_name));
	exit (1);
    }

    screen = DefaultScreen(Disp);

#ifdef WMK
    scr = WMCreateScreen(Disp, DefaultScreen(Disp));
    create_big_window(scr);
    create_db_window(scr);
    create_about_window(scr);
#endif

    Root = RootWindow(Disp, screen);
    d_depth = DefaultDepth(Disp, screen);
    x_fd = XConnectionNumber(Disp);
    ScreenHeight = DisplayHeight(Disp,screen);
    ScreenWidth = DisplayWidth(Disp,screen);

    /* it's time to load the visual theme! */

    if (fak_load_theme(theme, FALSE)) {
       if (debug) fprintf(stderr, "-> back from fak_load_theme!\n");
    } else {
	fprintf(stderr, "ascd: fatal error\n\n");
	fprintf(stderr, "The '%s' theme definition file can't be read.\n\n", theme);
	exit(1);
    }

    SizeHints.flags= USSize|USPosition;
    SizeHints.x = 0;
    SizeHints.y = 0;
    back_pix = get_color("black");
    fore_pix = get_color("white");

    XWMGeometry(Disp, screen, Geometry, NULL, (borderwidth = 1), &SizeHints,
		&SizeHints.x,&SizeHints.y,&SizeHints.width,
		&SizeHints.height, &i);

    SizeHints.width = backXPM.attributes.width;
    SizeHints.height= backXPM.attributes.height;

    Win = XCreateSimpleWindow(Disp, Root,
			      SizeHints.x,
			      SizeHints.y,
			      SizeHints.width,
			      SizeHints.height,
			      borderwidth,
			      fore_pix,
			      back_pix);

    if (!th_no_icon_window) {
	Iconwin = XCreateSimpleWindow(Disp,Win,
				      SizeHints.x,SizeHints.y,
				      SizeHints.width,
				      SizeHints.height,
				      borderwidth,
				      fore_pix,
				      back_pix);
    } else {
	Iconwin = XCreateSimpleWindow(Disp, Win,
				      SizeHints.x,
				      SizeHints.y,
				      iconXPM.attributes.width,
				      iconXPM.attributes.height,
				      borderwidth,
				      fore_pix,
				      back_pix);
    }

    XSetWMNormalHints(Disp, Win, &SizeHints);

    classHint.res_name = "ascd" ;
    classHint.res_class = "AScd";

    XSetClassHint (Disp, Win, &classHint);

    XSelectInput(Disp, Win, (ExposureMask | ButtonPressMask | StructureNotifyMask));

    XSelectInput(Disp, Iconwin, (ExposureMask | ButtonPressMask |
				 StructureNotifyMask));

    if (XStringListToTextProperty(&wname, 1, &name) ==0) {
	fprintf(stderr, "ascd: can't allocate window name\n");
	exit(-1);
    }

    XSetWMName(Disp, Win, &name);

    /* Create WinGC */
    gcm = GCForeground|GCBackground|GCGraphicsExposures;
    gcv.foreground = fore_pix;
    gcv.background = back_pix;
    gcv.graphics_exposures = False;
    WinGC = XCreateGC(Disp, Root, gcm, &gcv);

    WmHints.initial_state = withdrawn ? WithdrawnState : NormalState ;
    WmHints.icon_window = Iconwin;
    WmHints.window_group = Win;
    WmHints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
    WmHints.icon_x = SizeHints.x;
    WmHints.icon_y = SizeHints.y;
    XSetWMHints(Disp, Win, &WmHints);
    XSetCommand(Disp, Win, argv, argc);

    XMapWindow(Disp, Win);


    if (debug) fprintf(stderr, "-> calling fak_maskset() from create_window()...\n");
    fak_maskset();
    if (debug) fprintf(stderr, "-> calling fak_redraw() from create_window()...\n");
    fak_redraw();
    if (debug) fprintf(stderr, "-> Leaving create_window()\n");
}

void newtext(char *txt) {
    strcpy(led_text, txt);
    text_start = 0;
}

void show_icon_db_f() {
    /* scroll the song title */
    char txt[256];
    char txt2[256];
    char dsp[9];
    int track;
    int red;

    if (!th_no_icon_window) return;
    if (icon_msg == 0) return;

    if (theme_select == 0) {

	/* there is a message to display, we'll wait for the message to
	   time-out before handling the track title... */

	if (strlen(led_text) > 0) return;

	/* if in fast track selector, we show the title of the track to reach */

	if (fast_track > 0) {
	    track = fast_track - 1;
	    red = FALSE;
	} else {
	    track = cur_track - 1;
	    red = TRUE;
	}

        if (track > cur_ntracks - 1) return;

	if (cd->trk[track].songname != NULL) {
	    if (show_artist) {
		if (cd->artist != NULL) {
		    sprintf(txt2, "%s: %s", cd->artist, cd->trk[track].songname);
		} else {
		    strcpy(txt2, cd->trk[track].songname);
		}
	    } else {
		strcpy(txt2, cd->trk[track].songname);
	    }
	    fak_icon_text("", MSG_PANEL, 0, red);
	    if (strlen(txt2) > thdata[icon_msg].w) {
		strcpy(txt, "       ");
		strcat(txt, txt2);
		if (show_icon_db_pos > strlen(txt)) show_icon_db_pos = 0;
		tes_sncpy(dsp, txt + show_icon_db_pos, thdata[icon_msg].w);
		fak_icon_text(dsp, MSG_PANEL, 0, red);
		show_icon_db_pos ++;
	    } else {
		fak_icon_text(txt2, MSG_PANEL, 0, red);
	    }
	} else {
	    strcpy(txt, "");
	}
    }
}

void show_db_f() {
    /* scroll the song title */
    char txt[256];
    char txt2[256];
    char dsp[9];
    int track;
    int red;
    int where;
    int longueur;
    int i;

    if (but_db > 0) {
	where = DB_PANEL;
	longueur = thdata[but_db].w;
    } else {
	where = MSG_PANEL;
	longueur = thdata[but_msg].w;
    }

    if ((theme_select == 0) || (where == DB_PANEL)) {

	/*
	   there is a message to display, we'll wait for the message
	   to time-out before handling the track title... (but only if
	   there is no separate areas)
	*/

	if ((strlen(led_text) > 0) && (where == MSG_PANEL)) return;

	/* if in fast track selector, we show the title of the track to reach */

	if (fast_track > 0) {
	    track = fast_track - 1;
	    red = FALSE;
	} else {
	    track = cur_track - 1;
	    red = TRUE;
	}

	/* warning: avoid weird datas: */

        if (track > cur_ntracks - 1) return;
	if (track < 0) return;

       	if (debug > 1) fprintf(stderr, "** Show DB TRACK = %d\n", track);

	if (cd->trk[track].songname != NULL) {
	    if (show_artist) {
		if (cd->artist != NULL) {
		    sprintf(txt2, "%s: %s", cd->artist, cd->trk[track].songname);
		} else {
		    strcpy(txt2, cd->trk[track].songname);
		}
	    } else {
		strcpy(txt2, cd->trk[track].songname);
	    }
	    fak_text("", where, 0, red);
	    if (strlen(txt2) > longueur) {
		strcpy(txt, "");
		for (i = 0 ;  i < longueur ; i++) strcat(txt, " ");
		strcat(txt, txt2);
		if (show_db_pos > strlen(txt)) show_db_pos = 0;
		tes_sncpy(dsp, txt + show_db_pos, longueur);
		fak_text(dsp, DB_PANEL, 0, red);
		show_db_pos ++;
	    } else {
		fak_text(txt2, DB_PANEL, 0, red);
	    }
	} else {
	    strcpy(txt, "");
	}
    }

    show_icon_db_f();
}

/* ------------------------------------------------------------------------
   Loooooooooooooooooping.........
   ------------------------------------------------------------------------ */

void main_loop()
{
    unsigned int no_disk = 0;
    long int dodo = RDTIME;
    XEvent Event;

    while(1) {

        if (debug > 1) fprintf(stderr, "** [Main Loop] mode = %02d track = %02d \n", cur_cdmode, cur_track);

	if (cur_cdmode == WM_CDM_EJECTED) no_disk = 1;

	slow_down++;

	if (slow_down > 10) {

#ifdef WMK
	    /* in 0.13, we no longer close WINGs windows if ejected
	    if ((cur_cdmode == WM_CDM_EJECTED) && (en_vue)) {
		WMCloseWindow(win);
		en_vue = FALSE;
	    }
	    */
#endif

	    if (no_disk == 1) {
		cur_ntracks = 0; /* 0.13pr6: what a hack!!!! */

		if (autoplay || autoprobe) {
		    dodo = RDTIME2;
		    wm_cd_status();
		}

		if (cur_cdmode != WM_CDM_EJECTED) no_disk = 0;
		if ( (cur_cdmode == WM_CDM_STOPPED) && (autoplay) )  {
		    newtext("Autoplay");
		    if (cd->volume > 0) {
			volume=cd->volume;
			cd_volume(volume, 10, max_volume);
		    }
		    cd_control(PLAY);
		    wm_cd_status();
		    dodo = RDTIME;
		    fak_maskset();
		    fak_redraw();
		}
	    }

	    /* The Loop Mode : */
	    if ( (cur_track == loop_end_track ) && (cur_pos_rel >= loop_2) && (loop_mode) ) {
		cd_control(LOOP);
		fak_redraw();
	    }

	    /* The Intro Scan Mode : */
	    if ( (cur_pos_rel > cue_time) && (intro_mode) ) {
		cd_control(INTRONEXT);
		fak_redraw();
	    }
	}

	if ((slow_down == 1) || (slow_down == 6)) {
	    if ((show_db) && (cur_cdmode == WM_CDM_PLAYING)) show_db_f();
	}

	/* Check events */

	while (XPending(Disp))
	    {

		XNextEvent(Disp, &Event);

#ifdef WMK
		if (!WMHandleEvent(&Event)) {
#endif
		switch(Event.type) {

		    /* ---------------------- Redraw Window --------------------- */

		case Expose:
		    if(Event.xexpose.count == 0) {
			lasttime=01;
			redraw = TRUE;
			fak_redraw();
		    } else {
			if (debug > 1) fprintf(stderr, "** XEVent - expose, not handled, count = %d\n", Event.xexpose.count);
		    }
		    break;

		    /* ----------------------- Mouse Click ---------------------- */

		case ButtonPress:

		    wm_cd_status();
		    mouse_events(Event);
		    break;

		    /* ------------------------ Destroy Window ------------------- */

		case DestroyNotify:
		    XFreeGC(Disp, WinGC);
		    XDestroyWindow(Disp, Win);
		    XDestroyWindow(Disp, Iconwin);
		    XCloseDisplay(Disp);
		    exit(0);
		    break;
		default:
		    if (debug > 1) fprintf(stderr, "** XEvent - unknown event type = %d\n", Event.type);
		    break;
		}

#ifdef WMK
		}
#endif
		XFlush(Disp);
	    } /* check event */

	usleep(dodo);

	/* ----------------- now we have to redraw the screen: ---------------- */

	if ((slow_down > 10) || (has_clicked)) {
	    fak_redraw();
	    slow_down = 0;
	    has_clicked = FALSE;
	}
    }
}

/* ------------------------------------------------------------------------
   So... let's go!
   ------------------------------------------------------------------------ */

int main(int argc, char *argv[])
{
    extern char *rcfile, *dbfiles;

    /*printf("AScd %s\n", VERSION);*/

    /* CD device: */

#ifndef NO_D_DEVICE
    cd_device = malloc(strlen(DEFAULTDEVICE) + 1);
    strcpy(cd_device, DEFAULTDEVICE);
#endif

    strcpy(led_text, "");
    strcpy(theme, "default");
    strcpy(xv, "xv");

    /* the WorkMan database. It's still not used in ascd, but we need this
       to start the WorkMan code
    */

    rcfile = getenv("WORKMANRC");
    if (rcfile) rcfile = (char *)wm_strdup(rcfile);
    dbfiles = getenv("WORKMANDB");
    if (dbfiles) dbfiles = (char *)wm_strdup(dbfiles);
    split_workmandb();

    load_rc_file(FALSE);
    command_line_parse(argc, argv);

#ifdef WMK
   if (debug) fprintf(stderr, "** [WINGs] init app...\n");
    WMInitializeApplication("AScd", &argc, argv);
#endif

#ifdef MIXER
    mixer_ok = 1;
   if (debug) fprintf(stderr, "** [mixer] checking mixer device...\n");
    if (! check_mixer()) {
        fprintf(stderr, "ascd: can't initialize mixer device. Mixing support disabled.\n");
	mixer_ok = 0;
    }
#endif

   if (debug) fprintf(stderr, "** creating main window...\n");
   create_window(argc, argv);
   if (debug) fprintf(stderr, "** checking CD status...\n");
   wm_cd_status();

    if (cur_cdmode != WM_CDM_EJECTED) {
       if (debug) fprintf(stderr, "** reading CD initial volume...\n");
	volume = wm_cd_read_initial_volume(max_volume);
    } else {
	if (debug) fprintf(stderr, "** CD is ejected: volume ignored.\n");
    }

    if (debug) fprintf(stderr, "** checking autoplay...\n");
    if ((autoplay) && (cur_cdmode == WM_CDM_STOPPED)) {
	if (cur_track < 1) {
	    cur_track = 1;
	    wm_cd_status();
	}
       if (debug) fprintf(stderr, "-> autoplay: play command\n");
	cd_control(PLAY);
       if (debug) fprintf(stderr, "-> autoplay: checking status\n");
	wm_cd_status();
	if (debug) fprintf(stderr, "-> autoplay: redrawing\n");
	fak_redraw();
       if (debug) fprintf(stderr, "-> autoplay: done\n");
    } else {
       if (debug) fprintf(stderr, "-> no autoplay\n");
    }

   if (debug) fprintf(stderr, "** checking current CD volume in database\n");

    if (((cur_cdmode == WM_CDM_PLAYING) || (cur_cdmode == WM_CDM_PAUSED)) && (cd->volume > 0)) {
	volume=cd->volume;
	cd_volume(volume, 10, max_volume);
    }

    fak_maskset();

    if (debug) fprintf(stderr, "** Init passed. Entering main loop.\n");
    redraw = TRUE;
    main_loop();
    return 0;
}
