summaryrefslogtreecommitdiffstats
path: root/kscd/kcompactdisc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kscd/kcompactdisc.cpp')
-rw-r--r--kscd/kcompactdisc.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/kscd/kcompactdisc.cpp b/kscd/kcompactdisc.cpp
new file mode 100644
index 00000000..b69325a3
--- /dev/null
+++ b/kscd/kcompactdisc.cpp
@@ -0,0 +1,486 @@
+/*
+ * KCompactDisc - A CD drive interface for the KDE Project.
+ *
+ * Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <qfile.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprotocolmanager.h>
+#include <krun.h>
+#include "kcompactdisc.h"
+#include <netwm.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/utsname.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+/* this is for glibc 2.x which the ust structure in ustat.h not stat.h */
+#ifdef __GLIBC__
+#include <sys/ustat.h>
+#endif
+
+#ifdef __FreeBSD__
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#endif
+
+#ifdef __linux__
+#include <mntent.h>
+#define KSCDMAGIC 0
+#endif
+
+#include <kprocess.h>
+#include <config.h>
+
+extern "C"
+{
+// We don't have libWorkMan installed already, so get everything
+// from within our own directory
+#include "libwm/include/wm_cddb.h"
+#include "libwm/include/wm_cdrom.h"
+#include "libwm/include/wm_cdtext.h"
+#include "libwm/include/wm_config.h"
+#include "libwm/include/wm_cdinfo.h"
+#include "libwm/include/wm_helpers.h"
+
+// Sun, Ultrix etc. have a canonical CD device specified in the
+// respective plat_xxx.c file. On those platforms you need not
+// specify the CD device and DEFAULT_CD_DEVICE is not defined
+// in config.h.
+#ifndef DEFAULT_CD_DEVICE
+#define DEFAULT_CD_DEVICE "/dev/cdrom"
+#endif
+}
+
+#include <qtextcodec.h>
+#include <fixx11h.h>
+
+// Our internal definition of when we have no disc. Used to guard some
+// internal arrays.
+#define NO_DISC ((m_discId == missingDisc) && (m_previousDiscId == 0))
+
+#define FRAMES_TO_MS(frames) \
+((frames) * 1000 / 75)
+
+#define TRACK_VALID(track) \
+((track) && (track <= m_tracks))
+
+const QString KCompactDisc::defaultDevice = DEFAULT_CD_DEVICE;
+const unsigned KCompactDisc::missingDisc = (unsigned)-1;
+
+KCompactDisc::KCompactDisc(InformationMode infoMode) :
+ m_device(QString::null),
+ m_status(0),
+ m_previousStatus(123456),
+ m_discId(missingDisc),
+ m_previousDiscId(0),
+ m_artist(QString::null),
+ m_title(QString::null),
+ m_track(0),
+ m_previousTrack(99999999),
+ m_infoMode(infoMode)
+{
+ // Debug.
+ // wm_cd_set_verbosity(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS_ALL );
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ connect(&timer, SIGNAL(timeout()), SLOT(timerExpired()));
+}
+
+KCompactDisc::~KCompactDisc()
+{
+ // Ensure nothing else starts happening.
+ timer.stop();
+ wm_cd_stop();
+ wm_cd_set_verbosity(0x0);
+ wm_cd_destroy();
+}
+
+const QString &KCompactDisc::device() const
+{
+ return m_device;
+}
+
+unsigned KCompactDisc::discLength() const
+{
+ if (NO_DISC || !m_tracks)
+ return 0;
+ return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]);
+}
+
+unsigned KCompactDisc::discPosition() const
+{
+ return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]);
+}
+
+QString KCompactDisc::discStatus(int status)
+{
+ QString message;
+
+ switch (status)
+ {
+ case WM_CDM_TRACK_DONE: // == WM_CDM_BACK
+ message = i18n("Back/Track Done");
+ break;
+ case WM_CDM_PLAYING:
+ message = i18n("Playing");
+ break;
+ case WM_CDM_FORWARD:
+ message = i18n("Forward");
+ break;
+ case WM_CDM_PAUSED:
+ message = i18n("Paused");
+ break;
+ case WM_CDM_STOPPED:
+ message = i18n("Stopped");
+ break;
+ case WM_CDM_EJECTED:
+ message = i18n("Ejected");
+ break;
+ case WM_CDM_NO_DISC:
+ message = i18n("No Disc");
+ break;
+ case WM_CDM_UNKNOWN:
+ message = i18n("Unknown");
+ break;
+ case WM_CDM_CDDAERROR:
+ message = i18n("CDDA Error");
+ break;
+ case WM_CDM_CDDAACK:
+ message = i18n("CDDA Ack");
+ break;
+ default:
+ if (status <= 0)
+ message = strerror(-status);
+ else
+ message = QString::number(status);
+ break;
+ }
+ return message;
+}
+
+/**
+ * Do everything needed if the user requested to eject the disc.
+ */
+void KCompactDisc::eject()
+{
+ if (m_status == WM_CDM_EJECTED)
+ {
+ emit trayClosing();
+ wm_cd_closetray();
+ }
+ else
+ {
+ wm_cd_stop();
+ wm_cd_eject();
+ }
+}
+
+unsigned KCompactDisc::track() const
+{
+ return m_track;
+}
+
+bool KCompactDisc::isPaused() const
+{
+ return (m_status == WM_CDM_PAUSED);
+}
+
+bool KCompactDisc::isPlaying() const
+{
+ return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE);
+}
+
+void KCompactDisc::pause()
+{
+ // wm_cd_pause "does the right thing" by flipping between pause and resume.
+ wm_cd_pause();
+}
+
+void KCompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack)
+{
+ wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK );
+}
+
+QString KCompactDisc::urlToDevice(const QString& device)
+{
+ KURL deviceUrl(device);
+ if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
+ {
+ kdDebug() << "Asking mediamanager for " << deviceUrl.fileName() << endl;
+ DCOPRef mediamanager("kded", "mediamanager");
+ DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
+ QStringList properties = reply;
+ if (!reply.isValid() || properties.count() < 6)
+ {
+ kdError() << "Invalid reply from mediamanager" << endl;
+ return defaultDevice;
+ }
+ else
+ {
+ kdDebug() << "Reply from mediamanager " << properties[5] << endl;
+ return properties[5];
+ }
+ }
+
+ return device;
+}
+
+bool KCompactDisc::setDevice(
+ const QString &device_,
+ unsigned volume,
+ bool digitalPlayback,
+ const QString &audioSystem,
+ const QString &audioDevice)
+{
+ timer.stop();
+
+ QString device = urlToDevice(device_);
+
+#if !defined(BUILD_CDDA)
+ digitalPlayback = false;
+#endif
+ int status = wm_cd_init(
+ digitalPlayback ? WM_CDDA : WM_CDIN,
+ QFile::encodeName(device),
+ digitalPlayback ? audioSystem.ascii() : 0,
+ digitalPlayback ? audioDevice.ascii() : 0,
+ 0);
+ m_device = wm_drive_device();
+ kdDebug() << "Device change: "
+ << (digitalPlayback ? "WM_CDDA, " : "WM_CDIN, ")
+ << m_device << ", "
+ << (digitalPlayback ? audioSystem : QString::null) << ", "
+ << (digitalPlayback ? audioDevice : QString::null) << ", status: "
+ << discStatus(status) << endl;
+
+ if (status < 0)
+ {
+ // Severe (OS-level) error.
+ m_device = QString::null;
+ }
+ else
+ {
+ // Init CD-ROM and display.
+ setVolume(volume);
+ }
+
+ m_previousStatus = m_status = wm_cd_status();
+
+ if (m_infoMode == Asynchronous)
+ timerExpired();
+ else
+ timer.start(1000, true);
+ return m_device != QString::null;
+}
+
+void KCompactDisc::setVolume(unsigned volume)
+{
+ int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED);
+ kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl;
+}
+
+void KCompactDisc::stop()
+{
+ wm_cd_stop();
+}
+
+const QString &KCompactDisc::trackArtist() const
+{
+ return trackArtist(m_track);
+}
+
+const QString &KCompactDisc::trackArtist(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackArtists[track - 1];
+}
+
+unsigned KCompactDisc::trackLength() const
+{
+ return trackLength(m_track);
+}
+
+unsigned KCompactDisc::trackLength(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return cd->trk[track - 1].length * 1000;
+}
+
+unsigned KCompactDisc::trackPosition() const
+{
+ return cur_pos_rel * 1000;
+}
+
+unsigned KCompactDisc::tracks() const
+{
+ return m_tracks;
+}
+
+const QString &KCompactDisc::trackTitle() const
+{
+ return trackTitle(m_track);
+}
+
+const QString &KCompactDisc::trackTitle(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return QString::null;
+ return m_trackTitles[track - 1];
+}
+
+bool KCompactDisc::isAudio(unsigned track) const
+{
+ if (NO_DISC || !TRACK_VALID(track))
+ return 0;
+ return !(cd->trk[track - 1].data);
+}
+
+/*
+ * timerExpired
+ *
+ * - Data discs not recognized as data discs.
+ *
+ */
+void KCompactDisc::timerExpired()
+{
+ m_status = wm_cd_status();
+
+ if (WM_CDS_NO_DISC(m_status) || (m_device == QString::null))
+ {
+ if (m_previousStatus != m_status)
+ {
+ m_previousStatus = m_status;
+ m_discId = missingDisc;
+ m_previousDiscId = 0;
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = 0;
+ m_track = 0;
+ emit discChanged(m_discId);
+ }
+ }
+ else
+ {
+ m_discId = cddb_discid();
+ if (m_previousDiscId != m_discId)
+ {
+ m_previousDiscId = m_discId;
+ kdDebug() << "New discId=" << m_discId << endl;
+ // Initialise the album and its signature from the CD.
+ struct cdtext_info *info = wm_cd_get_cdtext();
+ if (info && info->valid)
+ {
+ m_artist = reinterpret_cast<char*>(info->blocks[0]->performer[0]);
+ m_title = reinterpret_cast<char*>(info->blocks[0]->name[0]);
+ }
+ else
+ {
+ m_artist = i18n("Unknown Artist");
+ m_title = i18n("Unknown Title");
+ }
+
+ // Read or default CD data.
+ m_trackArtists.clear();
+ m_trackTitles.clear();
+ m_trackStartFrames.clear();
+ m_tracks = wm_cd_getcountoftracks();
+ for (unsigned i = 1; i <= m_tracks; i++)
+ {
+ if (info && info->valid)
+ {
+ m_trackArtists.append(reinterpret_cast<char*>(info->blocks[0]->performer[i]));
+ m_trackTitles.append(reinterpret_cast<char*>(info->blocks[0]->name[i]));
+ }
+ else
+ {
+ m_trackArtists.append(i18n("Unknown Artist"));
+ m_trackTitles.append(i18n("Track %1").arg(QString::number(i).rightJustify(2, '0')));
+ }
+ // FIXME: KDE4
+ // track.length = cd->trk[i - 1].length;
+ m_trackStartFrames.append(cd->trk[i - 1].start);
+ }
+ m_trackStartFrames.append(cd->trk[0].start);
+ m_trackStartFrames.append(cd->trk[m_tracks].start);
+ emit discChanged(m_discId);
+ }
+
+ // Per-event processing.
+ m_track = wm_cd_getcurtrack();
+ if (m_previousTrack != m_track)
+ {
+ m_previousTrack = m_track;
+
+ // Update the current track and its length.
+ emit trackChanged(m_track, trackLength());
+ }
+ if (isPlaying())
+ {
+ m_previousStatus = m_status;
+ // Update the current playing position.
+ emit trackPlaying(m_track, trackPosition());
+ }
+ else
+ if (m_previousStatus != m_status)
+ {
+ // If we are not playing, then we are either paused, or stopped.
+ switch (m_status)
+ {
+ case WM_CDM_PAUSED:
+ emit trackPaused(m_track, trackPosition());
+ break;
+ case WM_CDM_EJECTED:
+ emit trayOpening();
+ break;
+ default:
+ if (m_previousStatus == WM_CDM_PLAYING || m_previousStatus == WM_CDM_PAUSED
+ && m_status == WM_CDM_STOPPED)
+ {
+ emit discStopped();
+ }
+ break;
+ }
+
+ m_previousStatus = m_status;
+ }
+ }
+
+ // Now that we have incurred any delays caused by the signals, we'll start the timer.
+ timer.start(1000, true);
+}
+
+#include "kcompactdisc.moc"