commit 3d4331ba3b413b608742d0c5b81cd31e0fbd0ddf
Author: Brian C. Lane <bcl@brianlane.com>
Date: Tue, 11 Jan 2022 07:52:39 -0800
Initial commit: xfreecd v0.7.8
Diffstat:
86 files changed, 11605 insertions(+), 0 deletions(-)
diff --git a/COPYING b/COPYING
@@ -0,0 +1,340 @@
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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 of the License, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/HISTORY b/HISTORY
@@ -0,0 +1,64 @@
+0.7.8 Attempt to fix a bug that some people report happens 100% and that
+ happens about 5% for me. sigsegv after retrieving the cddb data. I
+ added some code to make sure displat.plabel was equal to NULL after
+ the progress box is destroyed (this didn't fix it, but cannot hurt)
+ And I removed a write to the progress box just before it is deleted.
+ At the moment this 'appears' to have fixed the problem. Tell me if
+ it hasn't!
+
+0.7.7 Small bugfix. In cddbd.c I had a small string that was a holdover
+ from the old way of ding things. It was catted onto the end of each
+ frame request sent to the server. Depending on the state of memory
+ it may have done nothing, or may have scrambled the request string.
+ It has now been fixed.
+
+0.7.6 Just a few minor bugfixes. I wasn't calculating the length of track
+ 1 correctly (because of the change from 1-99 ot 0-98 internally).
+ Fixed a problem with the first original submission to CDDB being
+ revision 2 instead of revision 1.
+ XfreeCD v0.7.6 and later are now certified to submit CD info to
+ the CDDB database. Thanks Steve!
+ Added real cddb submission email address.
+ Added RPM building to the Makefile. Thanks to Vincent Cautaerts
+ for creating the RPM version of 0.7.5 that I based this on.
+
+0.7.5 Massive Changes Again:
+ I removed all the static string storage used for the title
+ and track names and replaced with dynamic storage using the
+ GTK+ GString. Works pretty good.
+ Added support for extended CD information. This was required
+ to meet the standards for being able to submit CD information
+ the the CDDB database. You cannot edit this data in the
+ program, but it is preserved on disk, and can be edited with
+ a text editor if necisary
+ Fixed problems relating to 99 track CDs (NIN-Broken broke
+ things).
+ Added support for the Revision # downloaded from the cddb
+ database. When the CD is sent to the database for an updte
+ the Revision # is incremented by 1
+ Changing the category of a CD now automatically erases the
+ old copy of the data in the old category.
+ Added support for multiple DTITLE, TTITLE, EXTD, EXTT, etc.
+ lines in the cddb support routines.
+
+0.7.4 Added sending CDDB updates to the server via email
+ Fixed a bug where changing categories would leave the entry in the
+ old category directory.
+ Fixed a bug that would display the previous CDs info in the Track
+ window while retrieving new CD data from the server.
+ Fixed a problem with editing. It wasn't initalizing the title or
+ the category, so if these weren't edited it could have ended up
+ in the wrong place.
+ Added support for multiple TITLEx lines in the CDDB file.
+ Fixed a bug when editing CD tracks with > 8 tracks. The clist was
+ popping back to the top after selecting the track to edit. This
+ now works right (I changed the way I update the list).
+
+0.7.3 Added titlebar control using different class names for the different
+ windows.
+
+0.7.2 Added -geometry support
+
+
+0.7 Added CDDB support, rewrote low level controls. Split into 3
+ processes.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,45 @@
+#
+# Makefile for XfreeCD
+# Copyright 1998 by Brian C. Lane
+#
+ifeq ($(strip $(CC)),)
+ CC = gcc
+else
+ CC:=${CC}
+endif
+
+VERSION = 0.7.8
+CFLAGS = -O2 -Wall -pipe `gtk-config --cflags` -DVERSION=\"$(VERSION)\"
+LDFLAGS = `gtk-config --libs`
+
+OBJS = xfreecd.o cd_control.o cddbd.o cddb.o child_sync.o xpm_button.o
+
+all: xfreecd
+
+xfreecd: $(OBJS)
+ $(CC) $(OBJS) -o xfreecd $(LDFLAGS)
+
+clean:
+ rm -f *.o *~ xfreecd xfreecd-$(VERSION).tar.gz core
+ rm -rf xfreecd-$(VERSION)
+ rm -f xfreecd-$(VERSION)-1.spec
+ rm -f xfreecd-$(VERSION).lsm
+
+# Build the tarball
+dist: xfreecd
+# gpg --detach-sig xfreecd
+ rm -rf xfreecd-$(VERSION)
+ mkdir xfreecd-$(VERSION)
+ mkdir xfreecd-$(VERSION)/bitmaps
+ cp bitmaps/* xfreecd-$(VERSION)/bitmaps
+ cp {Makefile,*.c,*.h,xfreecd,README,HISTORY,COPYING,xfreecd.xpm,xfreecd.gif,xfreecd.wmconfig,xfreecd.spec,xfreecd.lsm,xfreecd.sig} xfreecd-$(VERSION)/
+ tar cvzf xfreecd-$(VERSION).tar.gz xfreecd-$(VERSION)/*
+ ln -s xfreecd.lsm xfreecd-$(VERSION).lsm
+
+# Build RedHat binary and source RPMs
+rpm: dist
+ cp xfreecd-$(VERSION).tar.gz /usr/src/redhat/SOURCES
+ cp xfreecd.gif /usr/src/redhat/SOURCES
+ rm -f xfreecd-$(VERSION)-1.spec
+ ln -s xfreecd.spec xfreecd-$(VERSION)-1.spec
+ rpm -ba -vv xfreecd-$(VERSION)-1.spec
diff --git a/README b/README
@@ -0,0 +1,225 @@
+
+ XfreeCD v0.7.8 (c) 1998 by Brian C. Lane http://www.tatoosh.com/nexus
+===============================================================================
+
+
+ Hello!
+
+ Thanks for trying out XfreeCD. The first thing is that you need to have a
+copy of GTK+ v1.0.2 or later installed. This is the Gimp Toolkit, so if you
+have a recent version of Gimp installed you are ready to compile XfreeCD by
+typing make. If you don't have GTK+ then you need to download and install it.
+It is available from http://www.gimp.org/gtk/ and the install is pretty easy.
+Sorry about this (I know I hate having to install other stuff just to run
+a program), but GTK+ is well worth it (as is Gimp) -- you will be seeing
+many more programs using GTK+ in the future.
+
+ Ok, so if you have GTK+ installed you can type make in the xfreecd directory
+to build xfreecd. Then copy the xfreecd binary it to your favorite location and
+run it. The standard place is in /usr/local/bin
+
+ The program initially uses /dev/cdrom to access the CD device. This can be
+changed in the setup dialog box (click on the question mark). It does not
+work with SCSI devices yet, but that's on ly list of things to find other
+people to do <G>.
+
+ You also have to make sure you have read permission for the CD device. You
+can do this by executing chmod ugo+r /dev/sbpcd (or whatever your device
+is). Make sure you change the device itself, not a symlink like /dev/cdrom
+since that won't work.
+
+ Please read over the rest of this document!
+
+
+
+
+ What is xfreecd?
+ ----------------
+
+ XfreeCD is a X windows program that looks like the frontpanel of a cd
+player. I stole the images from the win95 freeCD program. My thanks to Nate
+Smith for making his code freely available.
+
+ You can play CDs, move between tracks, adjust volume -- clicking the left
+button on the speaker icon increases the volume. Clicking the right button
+will decrease the volume. You can display 4 different times on the display:
+
+ 1. Time elapsed on the track (icon has a plus and a 1/4 cdrom on it)
+ 2. Time remaining on the track (icon has a minus and 1/4 cdrom on it)
+ 3. Time elapsed on cdrom (icon has a plus and a full cdrom)
+ 4. Time remaining on cdrom (icon has a minus and a full cdrom)
+
+ The repeat key causes the cdrom to keep playing when it reaches the end of
+the cdrom. The question mark key opens a setup dialog box with 3 tabs for
+the different setup screens. The 'Setup' tab allows you to set the CD device
+to use and 3 other buttons:
+
+ 1. AutoPlay When this is selected XfreeCD will start playing
+ the CD if there is one in the drive. If a CD is
+ already playing it doesn't disturb it.
+
+ 2. Eject when done When selected this will eject the CD when it is done
+ playing it if the repeat button on the front panel
+ is not selected.
+
+ 4. Eject on exit When this is selected and you exit XfreeCD with
+ the CD stopped or paused it will eject the CD.
+
+
+ A new feature for this version is support for the internet CD database CDDB
+created by Ti Kan and Steve Scherf (Thanks Guys!). Select the CDDB tab from
+the setup menu to setup this option. The Local CDDB path is where it will
+store the CD info when it is downloaded from the internet or entered locally.
+A number of directories are created under this directory for the different
+categories of CDs (this is handles automatically, you don't need to create
+any directories yourself, just make sure you have the right permissions for
+the location you select).
+
+ The CDDB server is the internet site that you want to use to retrieve CD
+track names from. This should probably be geographicly near you for the
+best performance. Initially only cddb.cddb.com is selected. A list of the
+current sites can be downloaded by pressing the Refresh Servers button.
+
+ The CDDB Submit email address is the email address of the CDDB server to
+submit new CDs to. The default is xmcd-cddb@amb.org and the test address
+is test-cddb@cddb.cddb.com -- The test address will tell you if the submission
+would have been accepted or not. You can submit CD info from the Track Edit
+window by pressing the 'Send to Server' button. This uses the 'cat' and 'mail'
+programs, and they should be in your current PATH for it to work.
+
+ As of version 0.7.6 XfreeCD submissions to the cddb database are accepted!
+Now you can add that obscure CD of yours to the worldwide database.
+
+ If the CDDB support button is selected XfreeCD will first search the local
+database (it does this even if CDDB is not selected) for the current CD's
+unique discid. If that fails then it will attempt to connect to the CDDB
+server that you have selected and download the CD info. It will then store
+that data locally. CDDB submission is available even when CDDB download
+support is turned off.
+
+ To use the CDDB download feature you will need to have your internet
+connection online or be using a program like diald to automatically connect
+to the internet.
+
+ The track names are displayed by doing a left click in the main display
+window (the one showing the time and track #). Doing a right click in
+this window allows you to drag the XfreeCD window anywhere on your desktop.
+
+
+ At the bottom of the track list window is a button labeled 'edit tracks'
+Click on it and another window similar to the first will be opened. Here
+you can edit the track names and save the changes to the local database.
+You don't need to have the internet CDDB support enabled.
+
+
+ Version 0.7 is a complete rewrite of the code. The old code didn't run
+as well as I liked, the user interface was sometimes slow because it was
+waiting for a response from the low-level CD routines which can sometimes
+take a noticeable amount of time. And I wanted to add CDDB support without
+the 'freezing' of alot of X programs.
+
+ Some people requested track seeking (skipping forward a few seconds
+within the track). I tried to implement this, but it seems that the
+low level CD device drivers don't implement this function very well and
+I removed the option (It would crash often and leave the cd_control
+process with no way to kill it since it was frozen waiting for a kernel
+call to finish). I have tested other players and none of them implement
+this feature any better. If you want to add it yourself and can get it
+to work, I'd be happy to integrate it into the next release of XfreeCD.
+
+ If you do a 'ps' while running xfreecd you will notice 3 processes
+running. This is perfectly normal. One process handles the GTK+ user
+interface, another handles the low-level CD control and the third is the
+cddb internet interface.
+
+ In version 0.7.5 I have reduced the amount of static memory storage used
+for strings, so you should notice a slightly smaller memory foorprint. On
+my system a 'ps xm | grep xfreecd' shows:
+
+
+v0.7.4
+ PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
+ 3183 p3 0 3 44 532 576 0 576 552 0 50 xfreecd
+ 3182 p3 14 7 48 588 636 0 636 592 0 52 xfreecd
+ 3181 p3 389 198 76 1500 1576 0 1576 1184 0 119 xfreecd
+
+v0.7.5
+ PID TTY MAJFLT MINFLT TRS DRS SIZE SWAP RSS SHRD LIB DT COMMAND
+ 3189 p3 0 3 40 500 540 0 540 516 0 43 ./xfreecd
+ 3188 p3 14 5 48 532 580 0 580 556 0 43 ./xfreecd
+ 3187 p3 408 169 80 1404 1484 0 1484 1184 0 96 ./xfreecd
+
+
+ Not a huge difference, but an improvement.
+
+
+ -geometry
+ ---------
+
+ XfreeCD now recognizes the -geometry command for x/y placement on the
+screen. Use the standard X geometry placement parameters. Width and Height
+are ignored. I use 'xfreecd -geometry +580-80' to place it at the end of my
+AfterStep Wharf bar.
+
+ The only small glitch with this is if you have titlebars on and use geometry
+the placement will be relative to the main XfreeCD window, without respect to
+the size of the titlebars.
+
+ Title Bars
+ ----------
+
+ If you want to modify the way the XfreeCD windows look (I like the main
+window to have no title bar and no resize bars) you can tell your window
+manager to remove them. This depends on what manager you are running. There
+are 5 types of windows associated with XfreeCD, but usually you will only be
+concerned with the main window.
+
+ XfreeCD is the main window.
+ XfreeCDt is the Track List window.
+ XfreeCDet is the Edit Track list window.
+ XfreeCDp is the progress window while connecting to the internet CDDB server.
+ XfreeCDs is the setup window.
+
+ I run AfterStep, and to setup the main window without title bar or resize
+handles you add a line like this to the database file:
+
+ Style "XfreeCD" NoTitle, NoHandles
+
+
+SCSI Support:
+
+ I don't have a SCSI CD drive, so it would be difficult for me to test
+any code I wrote. But it is possible for someone to modify the cd_control
+module to support SCSI drives, preferably through a user selectable
+button in the setup menu, not via compile time (I'm trying to make it
+easy to distribute this as a binary for those who don't have gcc
+installed on their system).
+
+ This is beta software and it may have memory leaks and other problems.
+Please report any problems or ideas to me for inclusion in future versions.
+
+ Brian Lane
+ Nexus Computing
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+
+Hall of Thanks
+--------------
+
+I would like to thank all of the beta testers for their help, bug reports,
+and suggestions. If I have forgotten anyone, please let me know and I'll add
+you to the list.
+
+Vincent Cautaerts (vincent@comf5.comm.eng.osaka-u.ac.jp) did the
+RPM package for RedHat users for v0.7.5 (I have taken over the rpm
+generation as of v0.7.6, so send any errors to me).
+
+The guys at www.cddb.com for their strict requirements for submission to
+the database. They revealed several bug that have now been squashed.
+
+Users of v0.7.6 for reporting the error 500 problem and forcing me to
+look over the code once again. I found the bug and squashed it!
+
+Users of v0.7.7 who kept insisting that it was sigsegving after a cddb
+retrieval. I believed you! Really!
diff --git a/bitmaps/a0.xpm b/bitmaps/a0.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a0_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+". .XXXXX. .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .",
+". .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/a1.xpm b/bitmaps/a1.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a1_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" ",
+" .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .",
+" .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .",
+" "};
diff --git a/bitmaps/a2.xpm b/bitmaps/a2.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a2_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .XXXXX. .",
+". .XXXXX. ",
+"X. ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"X. ",
+". .XXXXX. ",
+" .XXXXXXX. "};
diff --git a/bitmaps/a3.xpm b/bitmaps/a3.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a3_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .XXXXX. .",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/a4.xpm b/bitmaps/a4.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a4_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" ",
+". .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .",
+" "};
diff --git a/bitmaps/a5.xpm b/bitmaps/a5.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a5_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+". .XXXXX. ",
+"X. ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"X. ",
+". .XXXXX. ",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/a6.xpm b/bitmaps/a6.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a6_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+". .XXXXX. ",
+"X. ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"XX ",
+"X. ",
+". .XXXXX. ",
+". .XXXXX. .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/a7.xpm b/bitmaps/a7.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a7_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .",
+" .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .",
+" "};
diff --git a/bitmaps/a8.xpm b/bitmaps/a8.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a8_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+". .XXXXX. .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+". .XXXXX. .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/a9.xpm b/bitmaps/a9.xpm
@@ -0,0 +1,28 @@
+/* XPM */
+static char * a9_xpm[] = {
+"11 22 3 1",
+" c #000000000000",
+". c #861782078617",
+"X c #FFFFFFFFFFFF",
+" .XXXXXXX. ",
+". .XXXXX. .",
+"X. .X",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"XX XX",
+"X. .X",
+". .XXXXX. .",
+" .XXXXX. .",
+" .X",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" XX",
+" .X",
+" .XXXXX. .",
+" .XXXXXXX. "};
diff --git a/bitmaps/an.xpm b/bitmaps/an.xpm
@@ -0,0 +1,26 @@
+/* XPM */
+static char * an_xpm[] = {
+"11 22 1 1",
+" c #000000000000",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/b0.xpm b/bitmaps/b0.xpm
@@ -0,0 +1,35 @@
+/* XPM */
+static char * b0_xpm[] = {
+"8 16 16 1",
+" c #30C230C230C2",
+". c #DF7DDF7DDF7D",
+"X c #FFFFFFFFFFFF",
+"o c #EFBEEFBEEFBE",
+"O c #B6DAAEBAB6DA",
+"+ c #000000000000",
+"@ c #79E775D679E7",
+"# c #492441034924",
+"$ c #A6999E79A699",
+"% c #965896589658",
+"& c #38E338E338E3",
+"* c #410341034103",
+"= c #104008200820",
+"- c #208118612081",
+"; c #514451445144",
+": c #F7DEF7DEF7DE",
+" .XXXoO+",
+"@#$%$% O",
+"o&++++%X",
+"X&++++$X",
+"X*++++$X",
+"X*++++$X",
+"o&++++%X",
+"@=++++-O",
+";++++++@",
+". ++++@X",
+"X*++=+$X",
+":*++++$X",
+"X*++++$X",
+"X&++++$X",
+"O ***&*.",
+"&OXXXo@ "};
diff --git a/bitmaps/b1.xpm b/bitmaps/b1.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * b1_xpm[] = {
+"8 16 12 1",
+" c #000000000000",
+". c #208118612081",
+"X c #B6DAAEBAB6DA",
+"o c #410338E34103",
+"O c #FFFFFFFFFFFF",
+"+ c #28A228A228A2",
+"@ c #E79DE79DE79D",
+"# c #514451445144",
+"$ c #104008200820",
+"% c #861779E779E7",
+"& c #38E330C238E3",
+"* c #F7DEF7DEF7DE",
+" .",
+" .X",
+" oO",
+" oO",
+" oO",
+" oO",
+" +@",
+" #",
+" $%",
+" &@",
+" oO",
+" oO",
+" oO",
+" &*",
+" $%",
+" "};
diff --git a/bitmaps/b2.xpm b/bitmaps/b2.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * b2_xpm[] = {
+"8 16 12 1",
+" c #000000000000",
+". c #9E799E799E79",
+"X c #EFBEE79DEFBE",
+"o c #FFFFF7DEFFFF",
+"O c #FFFFFFFFFFFF",
+"+ c #AEBAA699AEBA",
+"@ c #38E338E338E3",
+"# c #082008200820",
+"$ c #38E330C238E3",
+"% c #410338E338E3",
+"& c #28A22CB228A2",
+"* c #618559656185",
+" .XoOO+@",
+" #$@%@$+",
+" # #%O",
+" # %O",
+" # # %O",
+" # %O",
+" # &X",
+" $XoOo*%",
+"+@....@ ",
+"O. # ",
+"O. #",
+"O. ",
+"o. ",
+"O. ####",
+"+@....@ ",
+" +XOOoX&"};
diff --git a/bitmaps/b3.xpm b/bitmaps/b3.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * b3_xpm[] = {
+"8 16 12 1",
+" c #000000000000",
+". c #8E388A288E38",
+"X c #F7DEEFBEEFBE",
+"o c #FFFFF7DEFFFF",
+"O c #FFFFFFFFFFFF",
+"+ c #AEBAA699AEBA",
+"@ c #38E334D338E3",
+"# c #082008200820",
+"$ c #410338E34103",
+"% c #28A228A228A2",
+"& c #514451445144",
+"* c #208118612081",
+" .XoOO+@",
+" #@@$@@+",
+" # #$O",
+" $O",
+" # # $O",
+" $O",
+" %X",
+" @XoOo&&",
+" *..++$.",
+" @X",
+" $O",
+" # $O",
+" $O",
+" @X",
+" *.+++$.",
+" +OOOoX*"};
diff --git a/bitmaps/b4.xpm b/bitmaps/b4.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * b4_xpm[] = {
+"8 16 12 1",
+" c #38E330C230C2",
+". c #000000000000",
+"X c #208118612081",
+"o c #DF7DD75CDF7D",
+"O c #38E338E338E3",
+"+ c #9E799E799E79",
+"@ c #FFFFFFFFFFFF",
+"# c #410338E34103",
+"$ c #082008200820",
+"% c #69A669A669A6",
+"& c #28A228A228A2",
+"* c #F7DEF7DEF7DE",
+" ......X",
+"oO....X+",
+"@+....#@",
+"@+....#@",
+"@+.$..#@",
+"@+....#@",
+"@%...$&o",
+"%Oo@@*%#",
+".X++++#+",
+"......O*",
+"......#*",
+"......#@",
+"......#@",
+"......O*",
+"......$%",
+"......$."};
diff --git a/bitmaps/b5.xpm b/bitmaps/b5.xpm
@@ -0,0 +1,33 @@
+/* XPM */
+static char * b5_xpm[] = {
+"8 16 14 1",
+" c #30C230C230C2",
+". c #861782078617",
+"X c #F7DEF3CEF7DE",
+"o c #FFFFF7DEF7DE",
+"O c #B6DAAEBAB6DA",
+"+ c #208118612081",
+"@ c #E79DDF7DDF7D",
+"# c #410338E34103",
+"$ c #410338E338E3",
+"% c #186110401861",
+"& c #000000000000",
+"* c #9E799E799E79",
+"= c #082000000000",
+"- c #596555555965",
+" .XoooO+",
+"@#$###%&",
+"o*&&&&&&",
+"o*&&==&&",
+"o*&&&&&&",
+"o*&=&&&&",
+"o-%&&=&&",
+". @ooo-=",
+"&+.***-.",
+"&&&&&& X",
+"&&&=&&#o",
+"&&&&&&#o",
+"&&&=&&#o",
+"&&&&=&$X",
+"&+****#.",
+"&OXooo@ "};
diff --git a/bitmaps/b6.xpm b/bitmaps/b6.xpm
@@ -0,0 +1,32 @@
+/* XPM */
+static char * b6_xpm[] = {
+"8 16 13 1",
+" c #30C230C230C2",
+". c #96588E388E38",
+"X c #F7DEEFBEEFBE",
+"o c #FFFFF7DEFFFF",
+"O c #AEBAA699AEBA",
+"+ c #208118612081",
+"@ c #38E338E338E3",
+"# c #41033CF338E3",
+"$ c #186110401861",
+"% c #000000000000",
+"& c #A6999E799E79",
+"* c #082000000000",
+"= c #69A665956185",
+" .XoooO+",
+"X@@@##$%",
+"o&%%%%%%",
+"o&%%**%%",
+"o&%%*%%%",
+"o&%*%*%%",
+"o=%%*%%%",
+". Xooo=%",
+"O@.&&&#.",
+"o.%%%%@X",
+"o&%%%%#o",
+"o&%%%%#o",
+"o&%%%%#o",
+"o.%%%*@X",
+"O@.&&&#.",
+"%OooooX "};
diff --git a/bitmaps/b7.xpm b/bitmaps/b7.xpm
@@ -0,0 +1,30 @@
+/* XPM */
+static char * b7_xpm[] = {
+"8 16 11 1",
+" c #000000000000",
+". c #71C675D671C6",
+"X c #F7DEF3CEF7DE",
+"o c #FFFFF7DEFFFF",
+"O c #DF7DDF7DDF7D",
+"+ c #28A228A228A2",
+"@ c #082008200820",
+"# c #104008200820",
+"$ c #38E338E338E3",
+"% c #410338E34103",
+"& c #082000000820",
+" .XoooO+",
+"@#$%$$+O",
+" @ @$X",
+" %o",
+" & & %o",
+" %o",
+" +O",
+" %",
+" #.",
+" $X",
+" %o",
+" %o",
+" %o",
+" $X",
+" @.",
+" "};
diff --git a/bitmaps/b8.xpm b/bitmaps/b8.xpm
@@ -0,0 +1,31 @@
+/* XPM */
+static char * b8_xpm[] = {
+"8 16 12 1",
+" c #30C230C230C2",
+". c #8E388A288E38",
+"X c #F7DEEFBEEFBE",
+"o c #FFFFF7DEFFFF",
+"O c #AEBAAAAAAEBA",
+"+ c #38E338E338E3",
+"@ c #DF7DDF7DDF7D",
+"# c #41033CF338E3",
+"$ c #A6999E799E79",
+"% c #000000000000",
+"& c #082000000000",
+"* c #61855D756185",
+" .XoooO+",
+"@#+### O",
+"o$%%%%#o",
+"o$%%&&#o",
+"o$%%%%#o",
+"o$%&%%#o",
+"o*&%&% @",
+". @ooo**",
+"O+.$$$#.",
+"o.%%%%+X",
+"o$%%%%#o",
+"o$%%%%#o",
+"o$%%%%#o",
+"o.%%%&+X",
+"O#.$$$#.",
+"%Ooooo@ "};
diff --git a/bitmaps/b9.xpm b/bitmaps/b9.xpm
@@ -0,0 +1,34 @@
+/* XPM */
+static char * b9_xpm[] = {
+"8 16 15 1",
+" c #30C230C230C2",
+". c #71C671C671C6",
+"X c #F7DEF3CEF7DE",
+"o c #FFFFFFFFFFFF",
+"O c #AEBAAAAAAEBA",
+"+ c #38E33CF338E3",
+"@ c #DF7DDF7DDF7D",
+"# c #492441034924",
+"$ c #A6999E799E79",
+"% c #000000000000",
+"& c #082000000000",
+"* c #28A228A228A2",
+"= c #FFFFF7DEF7DE",
+"- c #208118612081",
+"; c #965896589658",
+" .XoooO+",
+"@#++++ O",
+"o$%%%%+o",
+"o$%%&&+o",
+"o$%%&%+o",
+"o$%&%&+o",
+"o.%%%&*@",
+". @o=X.#",
+"%-;$$$+.",
+"%%%%%%+X",
+"%%%&%%+=",
+"%%%%%%+o",
+"%%%&%%+o",
+"%%%%&%+X",
+"%-;$$;#.",
+"%Ooooo@ "};
diff --git a/bitmaps/bar.xpm b/bitmaps/bar.xpm
@@ -0,0 +1,18 @@
+/* XPM */
+static char * bar_xpm[] = {
+"4 7 8 1",
+" c #000000000000",
+". c #208118612081",
+"X c #B6DAAEBAB6DA",
+"o c #410338E34103",
+"O c #FFFFFFFFFFFF",
+"+ c #28A228A228A2",
+"@ c #E79DE79DE79D",
+"# c #514451445144",
+" .X ",
+" oO ",
+" oO ",
+" oO ",
+" oO ",
+" +@ ",
+" # "};
diff --git a/bitmaps/bn.xpm b/bitmaps/bn.xpm
@@ -0,0 +1,20 @@
+/* XPM */
+static char * bn_xpm[] = {
+"8 16 1 1",
+" c #000000000000",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/cdrom.xpm b/bitmaps/cdrom.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * cdrom_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXoooooXXX.",
+" XXXXoooooooXX.",
+" XXXXooooooo.X.",
+" XXXXoooXooo.X.",
+" XXXXooooooo.X.",
+" XXXXooooooo.X.",
+" XXXXXooooo..X.",
+" XXXXXX.....XX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/concept1.xpm b/bitmaps/concept1.xpm
@@ -0,0 +1,294 @@
+/* XPM */
+static char * concept1_xpm[] = {
+"256 256 35 1",
+" c #FFFFFFFFFFFF",
+". c #000000000000",
+"X c #965896589658",
+"o c #492449244924",
+"O c #CF3CCF3CCF3C",
+"+ c #514451445144",
+"@ c #BEFBBAEABEFB",
+"# c #186118611861",
+"$ c #69A66DB669A6",
+"% c #EFBEEFBEEFBE",
+"& c #596559655965",
+"* c #38E338E338E3",
+"= c #DF7DDF7DDF7D",
+"- c #28A228A228A2",
+"; c #D75CD75CD75C",
+": c #082008200820",
+"> c #C71BC30BC71B",
+", c #CF3CCB2BCF3C",
+"< c #B6DAB6DAB6DA",
+"1 c #AEBAAEBAAEBA",
+"2 c #A699A699A699",
+"3 c #9E799E799E79",
+"4 c #965892489658",
+"5 c #8E388A288E38",
+"6 c #861782078617",
+"7 c #79E779E779E7",
+"8 c #618565956185",
+"9 c #59655D755965",
+"0 c #514455555144",
+"q c #410341034103",
+"w c #30C230C230C2",
+"e c #208124922081",
+"r c #18611C711861",
+"t c #104014511040",
+"y c #08200C300820",
+" ",
+" ....................................................................................................................................... ",
+" ..........................................................................................XXXXXXXXXXXXXX.XXXXXXXXXXXXXX.XXXXXXXXXXXXXX. ",
+" ..........................................................................................Xooooooooooooo.Xooooooooooooo.Xooooooooooooo. ",
+" ..........................................................................................XooooooOooOooo.Xooooooooooooo.XoOOooooooOOoo. ",
+" ..........................................................................................XoooooOO.ooOoo.Xooooooooooooo.XoOOOooooOOO.o. ",
+" ..........................................................................................XooooOOO.OoO.o.Xooooooooooooo.XooOOOooOOO..o. ",
+" ..........................................................................................XoooOOOO.O.O.o.XoOOOOOOOOOOoo.XoooOOOOOO..oo. ",
+" ..........................................................................................XoOOOOOO.O.O.o.XooOOOOOOOO..o.XooooOOOO..ooo. ",
+" ..........................................................................................XoOOOOOO.O.O.o.XoooOOOOOO..oo.XooooOOOO.oooo. ",
+" ..........................................................................................Xoo.OOOO.O.O.o.XooooOOOO..ooo.XoooOOOOOOoooo. ",
+" ..........................................................................................XooooOOO.O.O.o.XoooooOO..oooo.XooOOO..OOOooo. ",
+" ..........................................................................................XoooooOO.o.O.o.Xoooooo..ooooo.XoOOO..ooOOOoo. ",
+" ..........................................................................................XooooooO.oOo.o.Xooooooooooooo.XoOO..ooooOO.o. ",
+" ..........................................................................................Xooooooo.oo.oo.Xooooooooooooo.Xoo..oooooo..o. ",
+" ..........................................................................................Xooooooooooooo.Xooooooooooooo.Xooooooooooooo. ",
+" ....................................................................................................................................... ",
+" ....... +.....@@....#$@ %@&.............@ @&....* *...*@ =@&#.....*@= @$...........XXXXXXXXXXXXXX.XXXXXXXXXXXXXX.XXXXXXXXXXXXXX. ",
+" ....... %-....@@...-= ;$$X= O#...........@=$$$$; ;#..* *..o =X$X= =:...@ =X$$; ;#.........Xooooooooooooo.Xooooooooooooo.Xooooooooooooo. ",
+" ....... =-...@@..#=%*.....$ @...........@@.....+ X..* *..@=.....*$...$ @.....o%$.........XooooooooOoooo.Xooooooooooooo.XooooOOOOOoooo. ",
+" ....... @%=-..@@..$ -.......$ -..........@@......X #.* *..@%*.........%@.......:..........XooooooooOOooo.Xooooooooooooo.XoooOOOOOOOooo. ",
+" ....... $-%;#.@@..@;........# $..........@@......* *.* *..-% =@*..* o..................XoooOOOOOOOOoo.XoooooOOoooooo.XoooOO..oOO.oo. ",
+" ....... $.-%O#@@..@@......... $..........@@......* *.* *....*$$$X= *.* *..................XooOOOOOOOOO.o.XooooOOOOooooo.Xoooo..ooOO.oo. ",
+" ....... $..+ ;@@..X #.......o o..........@@......X #.* *.........#=@.# X.......-#.........XoOOO....OO..o.XoooOOOOOOoooo.XoooooooOOO.oo. ",
+" ....... $...+ @..#%;#.....-%=...........@@.....o X..* *..$*......=@..X +.....-%@.........XoOO..oooO..oo.XooOOOOOOOOooo.XooooooOOO..oo. ",
+" ....... $....+ @...+ =X**oX =-...........@=$$$$; ;#..* *.:% ;$**&; o..#; X&*oX %-.........XoOO.ooooo.ooo.XoOOOOOOOOOOoo.XooooooOO..ooo. ",
+" ....... $.....@@....-@ %X#............@ ;$#...* *..#$; =o....#$% X#..........XoOOOooooooooo.Xoo..........o.Xooooooo..oooo. ",
+" .......................**-...................................#**-.........-**.............XooOOOOOOOOOoo.XoOOOOOOOOOOoo.XooooooOOooooo. ",
+" ..........................................................................................XoooOOOOOOO..o.Xoo..........o.XooooooOO.oooo. ",
+" ..........................................................................................Xoooo.......oo.Xooooooooooooo.Xooooooo..oooo. ",
+" ..........................................................................................Xooooooooooooo.Xooooooooooooo.Xooooooooooooo. ",
+" ....................................................................................................................................... ",
+"O>>>>>>>>>>>>>>>>>>>>>>>>>>O.O>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>O.O>>>>>>>>>>>>>>>>>>>>>>>>>>O. ",
+"O,,,,,,,,,,,,,,,,,,,,,,,,,,O.O,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,O.O,,,,,,,,,,,,,,,,,,,,,,,,,,O. ",
+"O<<<<<<<<<<<<<<<<<<<<<<<<<<O.O<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<O.O<<<<<<<<<<<<<<<<<<<<<<<<<<O. ",
+"O11111111111111111111111111O.O1111111111111111111111111111111111111111111111111111111111111111111111111111O.O11111111111111111111111111O. ",
+"O22222222222222222222222222O.O2222222222222222222222222222222222222222222222222222222222222222222222222222O.O22222222222222222222222222O. ",
+"O33333333333333333333333333O.O3333333333333333333333333333333333333333333333333333333333333333333333333333O.O33333333333333333333333333O. ",
+"O44444444444444444444444444O.O4444444444444444444444444444444444444444444444444444444444444444444444444444O.O44444444444444444444444444O. ",
+"O55555555555555555555555555O.O5555555555555555555555555555555555555555555555555555555555555555555555555555O.O55555555555555555555555555O. ",
+"O6666OO666666OO666666OO6666O.O66666666666666666666666 66666666666666666666OOO66OOO66666666666666666666666O.O6666OO666666OO666666OO6666O. ",
+"O7777OO77777OOO77777OOO7777O.O77777777777777777777777 77777777777777777OOO77OOO77777777777777777777777O.O7777OOO77777OOO77777OO7777O. ",
+"O$$$$OO$$$$OOOO$$$$OOOO$$$$O.O$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$OOO$$OOO$$$$$$$$$$$$$$$$$$$$$$$O.O$$$$OOOO$$$$OOOO$$$$OO$$$$O. ",
+"O8888OO888OOOOO888OOOOO8888O.O88888888888888888888888 88888888888OOO88OOO88888888888888888888888O.O8888OOOOO888OOOOO888OO8888O. ",
+"O9999OO99OOOOOO99OOOOOO9999O.O99999999999999999999999 99999999OOO99OOO99999999999999999999999O.O9999OOOOOO99OOOOOO99OO9999O. ",
+"O0000OO0OOOOOOO0OOOOOOO0000O.O00000000000000000000000 00000OOO00OOO00000000000000000000000O.O0000OOOOOOO0OOOOOOO0OO0000O. ",
+",ooooOOOOOOOOOOOOOOOOOOoooo,.,ooooooooooooooooooooooo ooOOOooOOOooooooooooooooooooooooo,.,ooooOOOOOOOOOOOOOOOOOOoooo,. ",
+"<qqqqOOOOOOOOOOOOOOOOOOqqqq<.<qqqqqqqqqqqqqqqqqqqqqqq qqOOOqqOOOqqqqqqqqqqqqqqqqqqqqqqq<.<qqqqOOOOOOOOOOOOOOOOOOqqqq<. ",
+"2****OO*OOOOOOO*OOOOOOO****2.2*********************** *****OOO**OOO***********************2.2****OOOOOOO*OOOOOOO*OO****2. ",
+"4wwwwOOwwOOOOOOwwOOOOOOwwww4.4wwwwwwwwwwwwwwwwwwwwwww wwwwwwwwOOOwwOOOwwwwwwwwwwwwwwwwwwwwwww4.4wwwwOOOOOOwwOOOOOOwwOOwwww4. ",
+"6eeeeOOeeeOOOOOeeeOOOOOeeee6.6eeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeOOOeeOOOeeeeeeeeeeeeeeeeeeeeeee6.6eeeeOOOOOeeeOOOOOeeeOOeeee6. ",
+"$rrrrOOrrrrOOOOrrrrOOOOrrrr$.$rrrrrrrrrrrrrrrrrrrrrrr rrrrrrrrrrrrrrOOOrrOOOrrrrrrrrrrrrrrrrrrrrrrr$.$rrrrOOOOrrrrOOOOrrrrOOrrrr$. ",
+"9ttttOOtttttOOOtttttOOOtttt9.9ttttttttttttttttttttttt tttttttttttttttttOOOttOOOttttttttttttttttttttttt9.9ttttOOOtttttOOOtttttOOtttt9. ",
+"oyyyyOOyyyyyyOOyyyyyyOOyyyyo.oyyyyyyyyyyyyyyyyyyyyyyy yyyyyyyyyyyyyyyyyyyyOOOyyOOOyyyyyyyyyyyyyyyyyyyyyyyo.oyyyyOOyyyyyyOOyyyyyyOOyyyyo. ",
+"*..........................*.*............................................................................*.*..........................*. ",
+"e..........................e.e............................................................................e.e..........................e. ",
+"t..........................t.t............................................................................t.t..........................t. ",
+"......................................................................................................................................... ",
+"......................................................................................................................................... ",
+"......................................................................................................................................... ",
+"......................................................................................................................................... ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/ejdn.xpm b/bitmaps/ejdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * ejdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXooXXXXXX",
+" .XXXXooooXXXXX",
+" .XXXooooooXXXX",
+" .XXooooooooXXX",
+" .XooooooooooXX",
+" .XX X",
+" .XooooooooooXX",
+" .XX X",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/ejup.xpm b/bitmaps/ejup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * ejup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXooXXXXXX.",
+" XXXXooooXXXXX.",
+" XXXooooooXXXX.",
+" XXooooooooXXX.",
+" XooooooooooXX.",
+" XX..........X.",
+" XooooooooooXX.",
+" XX..........X.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/exitdn.xpm b/bitmaps/exitdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * exitdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XooXXXXXXooXX",
+" .XoooXXXXooo X",
+" .XXoooXXooo X",
+" .XXXoooooo XX",
+" .XXXXoooo XXX",
+" .XXXXoooo XXXX",
+" .XXXooooooXXXX",
+" .XXooo oooXXX",
+" .Xooo XXoooXX",
+" .Xoo XXXXoo X",
+" .XX XXXXXX X",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/exitup.xpm b/bitmaps/exitup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * exitup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XooXXXXXXooXX.",
+" XoooXXXXooo.X.",
+" XXoooXXooo..X.",
+" XXXoooooo..XX.",
+" XXXXoooo..XXX.",
+" XXXXoooo.XXXX.",
+" XXXooooooXXXX.",
+" XXooo..oooXXX.",
+" Xooo..XXoooXX.",
+" Xoo..XXXXoo.X.",
+" XX..XXXXXX..X.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/ffdn.xpm b/bitmaps/ffdn.xpm
@@ -0,0 +1,59 @@
+/* XPM */
+static char * ffdn_xpm[] = {
+"29 30 26 1",
+" c None",
+". c #000000",
+"+ c #CFCFCF",
+"@ c #C7C3C7",
+"# c #CFCBCF",
+"$ c #B6B6B6",
+"% c #AEAEAE",
+"& c #A6A6A6",
+"* c #9E9E9E",
+"= c #969296",
+"- c #8E8A8E",
+"; c #868286",
+"> c #FFFFFF",
+", c #797979",
+"' c #696D69",
+") c #616561",
+"! c #595D59",
+"~ c #515551",
+"{ c #494949",
+"] c #414141",
+"^ c #383838",
+"/ c #303030",
+"( c #202420",
+"_ c #181C18",
+": c #101410",
+"< c #080C08",
+".............................",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@+",
+".+##########################+",
+".+$$$$$$$$$$$$$$$$$$$$$$$$$$+",
+".+%%%%%%%%%%%%%%%%%%%%%%%%%%+",
+".+&&&&&&&&&&&&&&&&&&&&&&&&&&+",
+".+**************************+",
+".+==========================+",
+".+--------------------------+",
+".+;;;;;;;;;;;;>>;;;;;;>>;;;;+",
+".+,,,,,,,,,,,,>>>,,,,,>>,,,,+",
+".+''''''''''''>>>>''''>>''''+",
+".+))))))))))))>>>>>)))>>))))+",
+".+!!!!!!!!!!!!>>>>>>!!>>!!!!+",
+".+~~~~~~~~~~~~>>>>>>>~>>~~~~+",
+".#{{{{{{{{{{{{>>>>>>>>>>{{{{#",
+".$]]]]]]]]]]]]>>>>>>>>>>]]]]$",
+".&^^^^^^^^^^^^>>>>>>>^>>^^^^&",
+".=////////////>>>>>>//>>////=",
+".;((((((((((((>>>>>(((>>((((;",
+".'____________>>>>____>>____'",
+".!::::::::::::>>>:::::>>::::!",
+".{<<<<<<<<<<<<>><<<<<<>><<<<{",
+".^..........................^",
+".(..........................(",
+".:..........................:",
+".............................",
+".............................",
+".............................",
+"............................."};
diff --git a/bitmaps/ffup.xpm b/bitmaps/ffup.xpm
@@ -0,0 +1,57 @@
+/* XPM */
+static char * ffup_xpm[] = {
+"29 30 24 1",
+" c #CF3CCF3CCF3C",
+". c #C71BC30BC71B",
+"X c #000000000000",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #79E779E779E7",
+"= c #69A66DB669A6",
+"- c #618565956185",
+"; c #59655D755965",
+": c #514455555144",
+"> c #492449244924",
+", c #410341034103",
+"< c #38E338E338E3",
+"1 c #30C230C230C2",
+"2 c #208124922081",
+"3 c #18611C711861",
+"4 c #104014511040",
+"5 c #08200C300820",
+" .......................... X",
+" oooooooooooooooooooooooooo X",
+" OOOOOOOOOOOOOOOOOOOOOOOOOO X",
+" ++++++++++++++++++++++++++ X",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@ X",
+" ########################## X",
+" $$$$$$$$$$$$$$$$$$$$$$$$$$ X",
+" %%%%%%%%%%%%%%%%%%%%%%%%%% X",
+" &&&& &&&&&& &&&&&& &&&& X",
+" **** ***** ***** **** X",
+" ==== ==== ==== ==== X",
+" ---- --- --- ---- X",
+" ;;;; ;; ;; ;;;; X",
+" :::: : : :::: X",
+"o>>>> >>>>oX",
+"O,,,, ,,,,OX",
+"@<<<< < < <<<<@X",
+"$1111 11 11 1111$X",
+"&2222 222 222 2222&X",
+"=3333 3333 3333 3333=X",
+";4444 44444 44444 4444;X",
+">5555 555555 555555 5555>X",
+"<XXXXXXXXXXXXXXXXXXXXXXXXXX<X",
+"2XXXXXXXXXXXXXXXXXXXXXXXXXX2X",
+"4XXXXXXXXXXXXXXXXXXXXXXXXXX4X",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/bitmaps/freecd.xpm b/bitmaps/freecd.xpm
@@ -0,0 +1,153 @@
+/* XPM */
+static char * freecd_xpm[] = {
+"26 94 56 1",
+" c #FFFFFFFFFFFF",
+". c #FFFFFBEEFFFF",
+"X c #F7DEF7DEF7DE",
+"o c #EFBEEFBEEFBE",
+"O c #EFBEEBADEFBE",
+"+ c #E79DE79DE79D",
+"@ c #E79DE38DE79D",
+"# c #DF7DDF7DDF7D",
+"$ c #DF7DDB6CDF7D",
+"% c #D75CD75CD75C",
+"& c #D75CD34CD75C",
+"* c #CF3CCB2BCF3C",
+"= c #C71BC71BC71B",
+"- c #C71BC30BC71B",
+"; c #BEFBBEFBBEFB",
+": c #BEFBBAEABEFB",
+"> c #B6DAB6DAB6DA",
+", c #B6DAB2CAB6DA",
+"< c #AEBAAEBAAEBA",
+"1 c #AEBAAAAAAEBA",
+"2 c #A699A289A699",
+"3 c #9E799E799E79",
+"4 c #A699A699A699",
+"5 c #9E799A699E79",
+"6 c #965896589658",
+"7 c #965892489658",
+"8 c #514451445144",
+"9 c #18611C711861",
+"0 c #208124922081",
+"q c #28A228A228A2",
+"w c #30C230C230C2",
+"e c #30C234D330C2",
+"r c #410345144103",
+"t c #492449244924",
+"y c #618561856185",
+"u c #69A66DB669A6",
+"i c #49244D344924",
+"p c #71C671C671C6",
+"a c #79E77DF779E7",
+"s c #38E33CF338E3",
+"d c #208120812081",
+"f c #861782078617",
+"g c #71C675D671C6",
+"h c #69A669A669A6",
+"j c #618565956185",
+"k c #410341034103",
+"l c #38E338E338E3",
+"z c #CF3CCF3CCF3C",
+"x c #8E388A288E38",
+"c c #79E779E779E7",
+"v c #8E388E388E38",
+"b c #28A22CB228A2",
+"n c #596559655965",
+"m c #514455555144",
+"M c #861786178617",
+"N c #59655D755965",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .X*44233356667:>,<123",
+" .890qqqqqqqqqqw3,<123",
+" 59erttttttttttty,<123",
+" uqr888888888888i,<123",
+" pwi8888888888888,<123",
+" pea:,<<<114443i8,<123",
+" pe,O+@#$%&*=-;si,<123",
+" pe<O+@#$%&*=-;0r,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pef6guuuuhhhhj0r,<123",
+" petkeeeeeeeeeeli,<123",
+" pei8iiiiiiiiiii8,<123",
+" pei8888888888888,<123",
+" pei8888888888888,<123",
+" <;*z*=--;;:>>,<4,<123",
+" &x2O+@#$%&*=-<cv,<123",
+" udgO+@#$%&*=-1dw,<123",
+" pb4O+@#$%&*=-;0k,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" pe<O+@#$%&*=-;dr,<123",
+" penuirrkkkkkkeqt,<123",
+" pettsssssssssski,<123",
+" pei8888888888888,<123",
+" ;wi8888888888887,<123",
+" .3j888888888886,,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@=,<<1--;<>,<123",
+" .XoO+gdeskk1-;kq,<123",
+" .XoO*dipaaa;-;tm,<123",
+" .XoO<epv777--;ij,<123",
+" .XoO<sa7777--;ij,<123",
+" .XoO<k1&*,M--;ij,<123",
+" .XoO<k:$%6h;-;ij,<123",
+" .XoO<k>$%am;-;ij,<123",
+" .XoO<k>$%c8;-;ij,<123",
+" .XoO<k<$%c8;-;rj,<123",
+" .XoO<kgyllyg8wsp,<123",
+" .XoO<kcMccMxfccx,<123",
+" .XoO:sa777777775,<123",
+" .XoO+yg77777777;,<123",
+" .XoO+@>;-;;;::;>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+3wwlss1-;mi,<123",
+" .XoO%qkjggg:-;ti,<123",
+" .XoO<bhx777--;iy,<123",
+" .XoO<sc7777--;ij,<123",
+" .XoO<k5*-1x--;ij,<123",
+" .XoO<k:$%2p--;ij,<123",
+" .XoO<k>$%fn;-;ij,<123",
+" .XoO<k>$%c8;-;ij,<123",
+" .XoO<k>$%c8;-;ij,<123",
+" .XoO<kaMNrN7pmlu,<123",
+" .XoO<kcahhcMgjuM,<123",
+" .XoO<kavvxv7vxv7,<123",
+" .XoO+8c77777777:,<123",
+" .XoO+*31<<<<<<:>,<123",
+" .XoO:tu*%&*=-;:>,<123",
+" .XoO<wN&%&*=-;:>,<123",
+" .XoO<sc$%&*=-;:>,<123",
+" .XoO<ka$%&*=-;:>,<123",
+" .XoO<ka$%&*=-;:>,<123",
+" .XoO+rc>xcggggpN,<123",
+" .XoO<egMy888888m,<123",
+" .XoO<scvxMMMMMMM,<123",
+" .XoO<ka777777777,<123",
+" .XoO<ka777777777,<123",
+" .XoO$%#$%&*=-;:>,<123",
+" ,8gO+@#,<;*=-;:>,<123",
+" ce5O+@#9ex*=-;:>,<123",
+" ai;O+@#8y-*=-;:>,<123",
+" f8-O+@#8h=*=-;:>,<123",
+" f8-O+@#8j=*=-;:>,<123",
+" f8-O+@#8j=*=-;:>,<123",
+" f8-O+@#8j=*=-;:>,<123",
+" f8-O+@#8j=*=-;:>,<123",
+" f87MN88ku5gm8iiw,<123",
+" f8achjjufxgjjjjj,<123",
+" f8fvxxxvv7vxxxxv,<123",
+" f8M7777777777777,<123",
+" f8M7777777777777,<123",
+" %OoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123",
+" .XoO+@#$%&*=-;:>,<123"};
diff --git a/bitmaps/freecdbase.xpm b/bitmaps/freecdbase.xpm
@@ -0,0 +1,26 @@
+/* XPM */
+static char * freecdbase_xpm[] = {
+"26 1 22 1",
+" c #FFFFFFFFFFFF",
+". c #FFFFFBEEFFFF",
+"X c #F7DEF7DEF7DE",
+"o c #EFBEEFBEEFBE",
+"O c #EFBEEBADEFBE",
+"+ c #E79DE79DE79D",
+"@ c #E79DE38DE79D",
+"# c #DF7DDF7DDF7D",
+"$ c #DF7DDB6CDF7D",
+"% c #D75CD75CD75C",
+"& c #D75CD34CD75C",
+"* c #CF3CCB2BCF3C",
+"= c #C71BC71BC71B",
+"- c #C71BC30BC71B",
+"; c #BEFBBEFBBEFB",
+": c #BEFBBAEABEFB",
+"> c #B6DAB6DAB6DA",
+", c #B6DAB2CAB6DA",
+"< c #AEBAAEBAAEBA",
+"1 c #AEBAAAAAAEBA",
+"2 c #A699A289A699",
+"3 c #9E799E799E79",
+" .XoO+@#$%&*=-;:>,<123"};
diff --git a/bitmaps/helpdn.xpm b/bitmaps/helpdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * helpdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXoooooXXXX",
+" .XXXoooooooXXX",
+" .XXXoo Xoo XX",
+" .XXXX XXoo XX",
+" .XXXXXXXooo XX",
+" .XXXXXXooo XX",
+" .XXXXXXoo XXX",
+" .XXXXXXX XXXX",
+" .XXXXXXooXXXXX",
+" .XXXXXXoo XXXX",
+" .XXXXXXX XXXX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/helpup.xpm b/bitmaps/helpup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * helpup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXoooooXXXX.",
+" XXXoooooooXXX.",
+" XXXoo..Xoo.XX.",
+" XXXX..XXoo.XX.",
+" XXXXXXXooo.XX.",
+" XXXXXXooo..XX.",
+" XXXXXXoo..XXX.",
+" XXXXXXX..XXXX.",
+" XXXXXXooXXXXX.",
+" XXXXXXoo.XXXX.",
+" XXXXXXX..XXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/mindn.xpm b/bitmaps/mindn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * mindn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XooooooooooXX",
+" .XXoooooooo X",
+" .XXXoooooo XX",
+" .XXXXoooo XXX",
+" .XXXXXoo XXXX",
+" .XXXXXX XXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/minup.xpm b/bitmaps/minup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * minup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XooooooooooXX.",
+" XXoooooooo..X.",
+" XXXoooooo..XX.",
+" XXXXoooo..XXX.",
+" XXXXXoo..XXXX.",
+" XXXXXX..XXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/minus.xpm b/bitmaps/minus.xpm
@@ -0,0 +1,29 @@
+/* XPM */
+static char * minus_xpm[] = {
+"8 16 10 1",
+" c #000000000000",
+". c #38E330C238E3",
+"X c #F7DEEFBEEFBE",
+"o c #FFFFF7DEFFFF",
+"O c #FFFFFFFFFFFF",
+"+ c #514451445144",
+"@ c #208118612081",
+"# c #8E388E388E38",
+"$ c #AEBAA699AEBA",
+"% c #410338E34103",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+".XoOo+ ",
+"@##$$% ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/mnscddn.xpm b/bitmaps/mnscddn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * mnscddn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXoooooXXX",
+" .XXXXoooooooXX",
+" .XXXXooooooo X",
+" .XXXXoooXooo X",
+" .XXXXooooooo X",
+" .XXXXooooooo X",
+" .Xoooooo.oo X",
+" .Xoooooo XX",
+" .XX XXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/mnscdup.xpm b/bitmaps/mnscdup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * mnscdup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXoooooXXX.",
+" XXXXoooooooXX.",
+" XXXXooooooo.X.",
+" XXXXoooXooo.X.",
+" XXXXooooooo.X.",
+" XXXXooooooo.X.",
+" Xoooooo oo..X.",
+" Xoooooo....XX.",
+" XX......XXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/mnstrkdn.xpm b/bitmaps/mnstrkdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * mnstrkdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXoooXXX",
+" .XXXXXXXooooXX",
+" .XXXXXXXoooo X",
+" .XXXXXXXoooo X",
+" .XXXXXXXX X",
+" .XXXXXXXXXXXXX",
+" .XooooooXXXXXX",
+" .Xoooooo XXXXX",
+" .XX XXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/mnstrkup.xpm b/bitmaps/mnstrkup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * mnstrkup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXoooXXX.",
+" XXXXXXXooooXX.",
+" XXXXXXXoooo.X.",
+" XXXXXXXoooo.X.",
+" XXXXXXXX....X.",
+" XXXXXXXXXXXXX.",
+" XooooooXXXXXX.",
+" Xoooooo.XXXXX.",
+" XX......XXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/nobar.xpm b/bitmaps/nobar.xpm
@@ -0,0 +1,11 @@
+/* XPM */
+static char * nobar_xpm[] = {
+"4 7 1 1",
+" c #000000000000",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/nodisc.xpm b/bitmaps/nodisc.xpm
@@ -0,0 +1,49 @@
+/* XPM */
+static char * nodisc_xpm[] = {
+"92 30 16 1",
+" c #000000000000",
+". c #FFFFFFFFFFFF",
+"X c #514451445144",
+"o c #BEFBBAEABEFB",
+"O c #186118611861",
+"+ c #69A66DB669A6",
+"@ c #EFBEEFBEEFBE",
+"# c #596559655965",
+"$ c #38E338E338E3",
+"% c #DF7DDF7DDF7D",
+"& c #28A228A228A2",
+"* c #D75CD75CD75C",
+"= c #965896589658",
+"- c #CF3CCF3CCF3C",
+"; c #492449244924",
+": c #082008200820",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" .X oo O+o..@o# o.....o# $.$ $o..%o#O $o%..o+ ",
+" .@& oo &%.*++=%.-O o%++++*.*O $.$ ;.%=+=%.%: o.%=++*.*O ",
+" ..%& oo O%@$ +.o oo X.= $.$ o% $+ +.o ;@+ ",
+" .o@%& oo +.& +.& oo =.O $.$ o@$ @o : ",
+" .+&@*O oo o* O.+ oo $.$ $.$ &@....%o$ $.; ",
+" .+ &@-Ooo oo .+ oo $.$ $.$ $+++=%.$ $.$ ",
+" .+ X.*oo =.O ;.; oo =.O $.$ O%o O.= &O ",
+" .+ X..o O@*O &@% oo ;.= $.$ +$ %o =.X &@o ",
+" .+ X.o X.%=$$;=.%& o%++++*.*O $.$ :@.*+$$#*.; O*.=#$;=.@& ",
+" .+ oo &o....@=O o.....*+O $.$ O+*....%; O+@....=O ",
+" $$& O$$& &$$ ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/null.xpm b/bitmaps/null.xpm
@@ -0,0 +1,35 @@
+/* XPM */
+static char * null_xpm[] = {
+"92 30 2 1",
+" c #000000000000",
+". c #FFFFFFFFFFFF",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" ",
+" ",
+" ",
+" ",
+" . ",
+" . ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/pauseup.xpm b/bitmaps/pauseup.xpm
@@ -0,0 +1,58 @@
+/* XPM */
+static char * pauseup_xpm[] = {
+"79 30 25 1",
+" c #CF3CCF3CCF3C",
+". c #C71BC30BC71B",
+"X c #000000000000",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #FFFFFFFFFFFF",
+"= c #79E779E779E7",
+"- c #69A66DB669A6",
+"; c #618565956185",
+": c #59655D755965",
+"> c #514455555144",
+", c #492449244924",
+"< c #410341034103",
+"1 c #38E338E338E3",
+"2 c #30C230C230C2",
+"3 c #208124922081",
+"4 c #18611C711861",
+"5 c #104014511040",
+"6 c #08200C300820",
+" ............................................................................ X",
+" oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo X",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO X",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ X",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ X",
+" ############################################################################ X",
+" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ X",
+" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X",
+" &&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&&***&&***&&&&&&&&&&&&&&&&&&&&&&& X",
+" ======================= =================***==***======================= X",
+" ----------------------- --------------***--***----------------------- X",
+" ;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;***;;***;;;;;;;;;;;;;;;;;;;;;;; X",
+" ::::::::::::::::::::::: ::::::::***::***::::::::::::::::::::::: X",
+" >>>>>>>>>>>>>>>>>>>>>>> >>>>>***>>***>>>>>>>>>>>>>>>>>>>>>>> X",
+"o,,,,,,,,,,,,,,,,,,,,,,, ,,***,,***,,,,,,,,,,,,,,,,,,,,,,,oX",
+"O<<<<<<<<<<<<<<<<<<<<<<< <<***<<***<<<<<<<<<<<<<<<<<<<<<<<OX",
+"@11111111111111111111111 11111***11***11111111111111111111111@X",
+"$22222222222222222222222 22222222***22***22222222222222222222222$X",
+"&33333333333333333333333 33333333333***33***33333333333333333333333&X",
+"-44444444444444444444444 44444444444444***44***44444444444444444444444-X",
+":55555555555555555555555 55555555555555555***55***55555555555555555555555:X",
+",66666666666666666666666 66666666666666666666***66***66666666666666666666666,X",
+"1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1X",
+"3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3X",
+"5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX5X",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/bitmaps/playdn.xpm b/bitmaps/playdn.xpm
@@ -0,0 +1,58 @@
+/* XPM */
+static char * playdn_xpm[] = {
+"79 30 25 1",
+" c #000000000000",
+". c #CF3CCF3CCF3C",
+"X c #C71BC30BC71B",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #FFFFFFFFFFFF",
+"= c #79E779E779E7",
+"- c #69A66DB669A6",
+"; c #618565956185",
+": c #59655D755965",
+"> c #514455555144",
+", c #492449244924",
+"< c #410341034103",
+"1 c #38E338E338E3",
+"2 c #30C230C230C2",
+"3 c #208124922081",
+"4 c #18611C711861",
+"5 c #104014511040",
+"6 c #08200C300820",
+" ",
+" .XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.",
+" .oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo.",
+" .OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.",
+" .++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++.",
+" .@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@.",
+" .############################################################################.",
+" .$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$.",
+" .%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%.",
+" .&&&&&&&&&&&&&&&&&&&&&&&**&&&&&&&&&&&&&&&&&&&&***&&***&&&&&&&&&&&&&&&&&&&&&&&.",
+" .=======================*****=================***==***=======================.",
+" .-----------------------********--------------***--***-----------------------.",
+" .;;;;;;;;;;;;;;;;;;;;;;;***********;;;;;;;;;;;***;;***;;;;;;;;;;;;;;;;;;;;;;;.",
+" .:::::::::::::::::::::::**************::::::::***::***:::::::::::::::::::::::.",
+" .>>>>>>>>>>>>>>>>>>>>>>>*****************>>>>>***>>***>>>>>>>>>>>>>>>>>>>>>>>.",
+" o,,,,,,,,,,,,,,,,,,,,,,,********************,,***,,***,,,,,,,,,,,,,,,,,,,,,,,o",
+" O<<<<<<<<<<<<<<<<<<<<<<<********************<<***<<***<<<<<<<<<<<<<<<<<<<<<<<O",
+" @11111111111111111111111*****************11111***11***11111111111111111111111@",
+" $22222222222222222222222**************22222222***22***22222222222222222222222$",
+" &33333333333333333333333***********33333333333***33***33333333333333333333333&",
+" -44444444444444444444444********44444444444444***44***44444444444444444444444-",
+" :55555555555555555555555*****55555555555555555***55***55555555555555555555555:",
+" ,66666666666666666666666**66666666666666666666***66***66666666666666666666666,",
+" 1 1",
+" 3 3",
+" 5 5",
+" ",
+" ",
+" ",
+" "};
diff --git a/bitmaps/playup.xpm b/bitmaps/playup.xpm
@@ -0,0 +1,58 @@
+/* XPM */
+static char * playup_xpm[] = {
+"79 30 25 1",
+" c #CF3CCF3CCF3C",
+". c #C71BC30BC71B",
+"X c #000000000000",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #FFFFFFFFFFFF",
+"= c #79E779E779E7",
+"- c #69A66DB669A6",
+"; c #618565956185",
+": c #59655D755965",
+"> c #514455555144",
+", c #492449244924",
+"< c #410341034103",
+"1 c #38E338E338E3",
+"2 c #30C230C230C2",
+"3 c #208124922081",
+"4 c #18611C711861",
+"5 c #104014511040",
+"6 c #08200C300820",
+" ............................................................................ X",
+" oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo X",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO X",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ X",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ X",
+" ############################################################################ X",
+" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ X",
+" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X",
+" &&&&&&&&&&&&&&&&&&&&&&&**&&&&&&&&&&&&&&&&&&&& && &&&&&&&&&&&&&&&&&&&&&&& X",
+" =======================*****================= == ======================= X",
+" -----------------------********-------------- -- ----------------------- X",
+" ;;;;;;;;;;;;;;;;;;;;;;;***********;;;;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;; X",
+" :::::::::::::::::::::::**************:::::::: :: ::::::::::::::::::::::: X",
+" >>>>>>>>>>>>>>>>>>>>>>>*****************>>>>> >> >>>>>>>>>>>>>>>>>>>>>>> X",
+"o,,,,,,,,,,,,,,,,,,,,,,,********************,, ,, ,,,,,,,,,,,,,,,,,,,,,,,oX",
+"O<<<<<<<<<<<<<<<<<<<<<<<********************<< << <<<<<<<<<<<<<<<<<<<<<<<OX",
+"@11111111111111111111111*****************11111 11 11111111111111111111111@X",
+"$22222222222222222222222**************22222222 22 22222222222222222222222$X",
+"&33333333333333333333333***********33333333333 33 33333333333333333333333&X",
+"-44444444444444444444444********44444444444444 44 44444444444444444444444-X",
+":55555555555555555555555*****55555555555555555 55 55555555555555555555555:X",
+",66666666666666666666666**66666666666666666666 66 66666666666666666666666,X",
+"1XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1X",
+"3XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX3X",
+"5XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX5X",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/bitmaps/plscddn.xpm b/bitmaps/plscddn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * plscddn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXoooooXXX",
+" .XXXXoooooooXX",
+" .XXXXooooooo X",
+" .XXXXoooXooo X",
+" .XXXoo.ooooo X",
+" .XXXoo ooooo X",
+" .Xoooooo.oo X",
+" .Xoooooo XX",
+" .XX oo XXXXX",
+" .XXXoo XXXXXXX",
+" .XXXX XXXXXXX"};
diff --git a/bitmaps/plscdup.xpm b/bitmaps/plscdup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * plscdup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXoooooXXX.",
+" XXXXoooooooXX.",
+" XXXXooooooo.X.",
+" XXXXoooXooo.X.",
+" XXXoo ooooo.X.",
+" XXXoo.ooooo.X.",
+" Xoooooo oo..X.",
+" Xoooooo....XX.",
+" XX.oo...XXXXX.",
+" XXXoo.XXXXXXX.",
+" XXXX..XXXXXXX.",
+"..............."};
diff --git a/bitmaps/plstrkdn.xpm b/bitmaps/plstrkdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * plstrkdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXoooXXX",
+" .XXXXXXXooooXX",
+" .XXXXXXXoooo X",
+" .XXXXXXXoooo X",
+" .XXXooXXX X",
+" .XXXoo XXXXXXX",
+" .XooooooXXXXXX",
+" .Xoooooo XXXXX",
+" .XX oo XXXXX",
+" .XXXoo XXXXXXX",
+" .XXXX XXXXXXX"};
diff --git a/bitmaps/plstrkup.xpm b/bitmaps/plstrkup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * plstrkup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXoooXXX.",
+" XXXXXXXooooXX.",
+" XXXXXXXoooo.X.",
+" XXXXXXXoooo.X.",
+" XXXooXXX....X.",
+" XXXoo.XXXXXXX.",
+" XooooooXXXXXX.",
+" Xoooooo.XXXXX.",
+" XX.oo...XXXXX.",
+" XXXoo.XXXXXXX.",
+" XXXX..XXXXXXX.",
+"..............."};
diff --git a/bitmaps/redbar.xpm b/bitmaps/redbar.xpm
@@ -0,0 +1,16 @@
+/* XPM */
+static char * redbar_xpm[] = {
+"4 7 6 1",
+" c #000000000000",
+". c #79E700000000",
+"X c #CF3C00000000",
+"o c #AEBA00000000",
+"O c #FFFF00000000",
+"+ c #DF7D00000000",
+" .X ",
+" oO ",
+" oO ",
+" oO ",
+" oO ",
+" .+ ",
+" o "};
diff --git a/bitmaps/rewdn.xpm b/bitmaps/rewdn.xpm
@@ -0,0 +1,59 @@
+/* XPM */
+static char * rewdn_xpm[] = {
+"29 30 26 1",
+" c None",
+". c #000000",
+"+ c #CFCFCF",
+"@ c #C7C3C7",
+"# c #CFCBCF",
+"$ c #B6B6B6",
+"% c #AEAEAE",
+"& c #A6A6A6",
+"* c #9E9E9E",
+"= c #969296",
+"- c #8E8A8E",
+"; c #868286",
+"> c #FFFFFF",
+", c #797979",
+"' c #696D69",
+") c #616561",
+"! c #595D59",
+"~ c #515551",
+"{ c #494949",
+"] c #414141",
+"^ c #383838",
+"/ c #303030",
+"( c #202420",
+"_ c #181C18",
+": c #101410",
+"< c #080C08",
+".............................",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@+",
+".+##########################+",
+".+$$$$$$$$$$$$$$$$$$$$$$$$$$+",
+".+%%%%%%%%%%%%%%%%%%%%%%%%%%+",
+".+&&&&&&&&&&&&&&&&&&&&&&&&&&+",
+".+**************************+",
+".+==========================+",
+".+--------------------------+",
+".+;;;;>>;;;;;;>>;;;;;;;;;;;;+",
+".+,,,,>>,,,,,>>>,,,,,,,,,,,,+",
+".+''''>>''''>>>>''''''''''''+",
+".+))))>>)))>>>>>))))))))))))+",
+".+!!!!>>!!>>>>>>!!!!!!!!!!!!+",
+".+~~~~>>~>>>>>>>~~~~~~~~~~~~+",
+".#{{{{>>>>>>>>>>{{{{{{{{{{{{#",
+".$]]]]>>>>>>>>>>]]]]]]]]]]]]$",
+".&^^^^>>^>>>>>>>^^^^^^^^^^^^&",
+".=////>>//>>>>>>////////////=",
+".;((((>>(((>>>>>((((((((((((;",
+".'____>>____>>>>____________'",
+".!::::>>:::::>>>::::::::::::!",
+".{<<<<>><<<<<<>><<<<<<<<<<<<{",
+".^..........................^",
+".(..........................(",
+".:..........................:",
+".............................",
+".............................",
+".............................",
+"............................."};
diff --git a/bitmaps/rewup.xpm b/bitmaps/rewup.xpm
@@ -0,0 +1,57 @@
+/* XPM */
+static char * rewup_xpm[] = {
+"29 30 24 1",
+" c #CF3CCF3CCF3C",
+". c #C71BC30BC71B",
+"X c #000000000000",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #79E779E779E7",
+"= c #69A66DB669A6",
+"- c #618565956185",
+"; c #59655D755965",
+": c #514455555144",
+"> c #492449244924",
+", c #410341034103",
+"< c #38E338E338E3",
+"1 c #30C230C230C2",
+"2 c #208124922081",
+"3 c #18611C711861",
+"4 c #104014511040",
+"5 c #08200C300820",
+" .......................... X",
+" oooooooooooooooooooooooooo X",
+" OOOOOOOOOOOOOOOOOOOOOOOOOO X",
+" ++++++++++++++++++++++++++ X",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@ X",
+" ########################## X",
+" $$$$$$$$$$$$$$$$$$$$$$$$$$ X",
+" %%%%%%%%%%%%%%%%%%%%%%%%%% X",
+" &&&& &&&&&& &&&&&& &&&& X",
+" **** ***** ***** **** X",
+" ==== ==== ==== ==== X",
+" ---- --- --- ---- X",
+" ;;;; ;; ;; ;;;; X",
+" :::: : : :::: X",
+"o>>>> >>>>oX",
+"O,,,, ,,,,OX",
+"@<<<< < < <<<<@X",
+"$1111 11 11 1111$X",
+"&2222 222 222 2222&X",
+"=3333 3333 3333 3333=X",
+";4444 44444 44444 4444;X",
+">5555 555555 555555 5555>X",
+"<XXXXXXXXXXXXXXXXXXXXXXXXXX<X",
+"2XXXXXXXXXXXXXXXXXXXXXXXXXX2X",
+"4XXXXXXXXXXXXXXXXXXXXXXXXXX4X",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/bitmaps/rptdn.xpm b/bitmaps/rptdn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * rptdn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXXXoXXXX",
+" .XXXXXXXXooXXX",
+" .XXXooooooooXX",
+" .XXooooooooo X",
+" .Xooo oo X",
+" .Xoo XXXo XX",
+" .Xoo XXXXX XXX",
+" .XoooXXXXXXXXX",
+" .XXoooooooooXX",
+" .XXXooooooo X",
+" .XXXX XX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/rptup.xpm b/bitmaps/rptup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * rptup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXoXXXX.",
+" XXXXXXXXooXXX.",
+" XXXooooooooXX.",
+" XXooooooooo.X.",
+" Xooo....oo..X.",
+" Xoo..XXXo..XX.",
+" Xoo.XXXXX.XXX.",
+" XoooXXXXXXXXX.",
+" XXoooooooooXX.",
+" XXXooooooo..X.",
+" XXXX.......XX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/rptupact.xpm b/bitmaps/rptupact.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * rptupact_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXoXXXX.",
+" XXXXXXXXooXXX.",
+" XXXooooooooXX.",
+" XXooooooooo.X.",
+" Xooo....oo..X.",
+" Xoo..XXXo..XX.",
+" Xoo.XXXXX.XXX.",
+" XoooXXXXXXXXX.",
+" XXoooooooooXX.",
+" XXXooooooo..X.",
+" XXXX.......XX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/sfwddn.xpm b/bitmaps/sfwddn.xpm
@@ -0,0 +1,59 @@
+/* XPM */
+static char * sfwddn_xpm[] = {
+"29 30 26 1",
+" c None",
+". c #000000",
+"+ c #CFCFCF",
+"@ c #C7C3C7",
+"# c #CFCBCF",
+"$ c #B6B6B6",
+"% c #AEAEAE",
+"& c #A6A6A6",
+"* c #9E9E9E",
+"= c #969296",
+"- c #8E8A8E",
+"; c #868286",
+"> c #FFFFFF",
+", c #797979",
+"' c #696D69",
+") c #616561",
+"! c #595D59",
+"~ c #515551",
+"{ c #494949",
+"] c #414141",
+"^ c #383838",
+"/ c #303030",
+"( c #202420",
+"_ c #181C18",
+": c #101410",
+"< c #080C08",
+".............................",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@+",
+".+##########################+",
+".+$$$$$$$$$$$$$$$$$$$$$$$$$$+",
+".+%%%%%%%%%%%%%%%%%%%%%%%%%%+",
+".+&&&&&&&&&&&&&&&&&&&&&&&&&&+",
+".+**************************+",
+".+==========================+",
+".+--------------------------+",
+".+;;;;>>;;;;;;>>;;;;;;;;;;;;+",
+".+,,,,>>>,,,,,>>>,,,,,,,,,,,+",
+".+''''>>>>''''>>>>''''''''''+",
+".+))))>>>>>)))>>>>>)))))))))+",
+".+!!!!>>>>>>!!>>>>>>!!!!!!!!+",
+".+~~~~>>>>>>>~>>>>>>>~~~~~~~+",
+".#{{{{>>>>>>>>>>>>>>>>{{{{{{#",
+".$]]]]>>>>>>>>>>>>>>>>]]]]]]$",
+".&^^^^>>>>>>>^>>>>>>>^^^^^^^&",
+".=////>>>>>>//>>>>>>////////=",
+".;((((>>>>>(((>>>>>(((((((((;",
+".'____>>>>____>>>>__________'",
+".!::::>>>:::::>>>:::::::::::!",
+".{<<<<>><<<<<<>><<<<<<<<<<<<{",
+".^..........................^",
+".(..........................(",
+".:..........................:",
+".............................",
+".............................",
+".............................",
+"............................."};
diff --git a/bitmaps/slice.xpm b/bitmaps/slice.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * slice_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXoooXXX.",
+" XXXXXXXooooXX.",
+" XXXXXXXoooo.X.",
+" XXXXXXXoooo.X.",
+" XXXXXXXX....X.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/bitmaps/srevdn.xpm b/bitmaps/srevdn.xpm
@@ -0,0 +1,59 @@
+/* XPM */
+static char * srevdn_xpm[] = {
+"29 30 26 1",
+" c None",
+". c #000000",
+"+ c #CFCFCF",
+"@ c #C7C3C7",
+"# c #CFCBCF",
+"$ c #B6B6B6",
+"% c #AEAEAE",
+"& c #A6A6A6",
+"* c #9E9E9E",
+"= c #969296",
+"- c #8E8A8E",
+"; c #868286",
+"> c #FFFFFF",
+", c #797979",
+"' c #696D69",
+") c #616561",
+"! c #595D59",
+"~ c #515551",
+"{ c #494949",
+"] c #414141",
+"^ c #383838",
+"/ c #303030",
+"( c #202420",
+"_ c #181C18",
+": c #101410",
+"< c #080C08",
+".............................",
+".+@@@@@@@@@@@@@@@@@@@@@@@@@@+",
+".+##########################+",
+".+$$$$$$$$$$$$$$$$$$$$$$$$$$+",
+".+%%%%%%%%%%%%%%%%%%%%%%%%%%+",
+".+&&&&&&&&&&&&&&&&&&&&&&&&&&+",
+".+**************************+",
+".+==========================+",
+".+--------------------------+",
+".+;;;;;;;;;;;;>>;;;;;;>>;;;;+",
+".+,,,,,,,,,,,>>>,,,,,>>>,,,,+",
+".+''''''''''>>>>''''>>>>''''+",
+".+)))))))))>>>>>)))>>>>>))))+",
+".+!!!!!!!!>>>>>>!!>>>>>>!!!!+",
+".+~~~~~~~>>>>>>>~>>>>>>>~~~~+",
+".#{{{{{{>>>>>>>>>>>>>>>>{{{{#",
+".$]]]]]]>>>>>>>>>>>>>>>>]]]]$",
+".&^^^^^^^>>>>>>>^>>>>>>>^^^^&",
+".=////////>>>>>>//>>>>>>////=",
+".;(((((((((>>>>>(((>>>>>((((;",
+".'__________>>>>____>>>>____'",
+".!:::::::::::>>>:::::>>>::::!",
+".{<<<<<<<<<<<<>><<<<<<>><<<<{",
+".^..........................^",
+".(..........................(",
+".:..........................:",
+".............................",
+".............................",
+".............................",
+"............................."};
diff --git a/bitmaps/stopup.xpm b/bitmaps/stopup.xpm
@@ -0,0 +1,57 @@
+/* XPM */
+static char * stopup_xpm[] = {
+"79 30 24 1",
+" c #CF3CCF3CCF3C",
+". c #C71BC30BC71B",
+"X c #000000000000",
+"o c #CF3CCB2BCF3C",
+"O c #B6DAB6DAB6DA",
+"+ c #AEBAAEBAAEBA",
+"@ c #A699A699A699",
+"# c #9E799E799E79",
+"$ c #965892489658",
+"% c #8E388A288E38",
+"& c #861782078617",
+"* c #79E779E779E7",
+"= c #69A66DB669A6",
+"- c #618565956185",
+"; c #59655D755965",
+": c #514455555144",
+"> c #492449244924",
+", c #410341034103",
+"< c #38E338E338E3",
+"1 c #30C230C230C2",
+"2 c #208124922081",
+"3 c #18611C711861",
+"4 c #104014511040",
+"5 c #08200C300820",
+" ............................................................................ X",
+" oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo X",
+" OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO X",
+" ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ X",
+" @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ X",
+" ############################################################################ X",
+" $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ X",
+" %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% X",
+" &&&&&&&&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&&&&& && &&&&&&&&&&&&&&&&&&&&&&& X",
+" *********************** ***************** ** *********************** X",
+" ======================= ============== == ======================= X",
+" ----------------------- ----------- -- ----------------------- X",
+" ;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;; ;; ;;;;;;;;;;;;;;;;;;;;;;; X",
+" ::::::::::::::::::::::: ::::: :: ::::::::::::::::::::::: X",
+"o>>>>>>>>>>>>>>>>>>>>>>> >> >> >>>>>>>>>>>>>>>>>>>>>>>oX",
+"O,,,,,,,,,,,,,,,,,,,,,,, ,, ,, ,,,,,,,,,,,,,,,,,,,,,,,OX",
+"@<<<<<<<<<<<<<<<<<<<<<<< <<<<< << <<<<<<<<<<<<<<<<<<<<<<<@X",
+"$11111111111111111111111 11111111 11 11111111111111111111111$X",
+"&22222222222222222222222 22222222222 22 22222222222222222222222&X",
+"=33333333333333333333333 33333333333333 33 33333333333333333333333=X",
+";44444444444444444444444 44444444444444444 44 44444444444444444444444;X",
+">55555555555555555555555 55555555555555555555 55 55555555555555555555555>X",
+"<XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<X",
+"2XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX2X",
+"4XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX4X",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
+"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
diff --git a/bitmaps/voldn.xpm b/bitmaps/voldn.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * voldn_xpm[] = {
+"15 15 4 1",
+" c #000000000000",
+". c #965896589658",
+"X c #492449244924",
+"o c #FFFFFFFFFFFF",
+" ",
+" ..............",
+" .XXXXXXXXXXXXX",
+" .XXXXXXoXXoXXX",
+" .XXXXXoo XXoXX",
+" .XXXXooo oXo X",
+" .XXXoooo o o X",
+" .Xoooooo o o X",
+" .Xoooooo o o X",
+" .XX oooo o o X",
+" .XXXXooo o o X",
+" .XXXXXoo X o X",
+" .XXXXXXo XoX X",
+" .XXXXXXX XX XX",
+" .XXXXXXXXXXXXX"};
diff --git a/bitmaps/volup.xpm b/bitmaps/volup.xpm
@@ -0,0 +1,22 @@
+/* XPM */
+static char * volup_xpm[] = {
+"15 15 4 1",
+" c #965896589658",
+". c #000000000000",
+"X c #492449244924",
+"o c #CF3CCF3CCF3C",
+" .",
+" XXXXXXXXXXXXX.",
+" XXXXXXoXXoXXX.",
+" XXXXXoo.XXoXX.",
+" XXXXooo.oXo.X.",
+" XXXoooo.o.o.X.",
+" Xoooooo.o.o.X.",
+" Xoooooo.o.o.X.",
+" XX.oooo.o.o.X.",
+" XXXXooo.o.o.X.",
+" XXXXXoo.X.o.X.",
+" XXXXXXo.XoX.X.",
+" XXXXXXX.XX.XX.",
+" XXXXXXXXXXXXX.",
+"..............."};
diff --git a/cd_control.c b/cd_control.c
@@ -0,0 +1,415 @@
+/* -----------------------------------------------------------------------
+ cd_control process
+
+ part of XfreeCD
+
+ Copyright (C) 1998 Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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.
+
+ ==========================[ HISTORY ]==================================
+ 06/22/98 Bug in track length calculation. It was waiting until track
+ #2 before calculating the lengths (holdover from the change
+ to 0 offset from 1 offset). FIXED.
+
+ 05/08/98 Added the control and init code from old version of xfreecd
+ Added software control of the EJECT on CLOSE state - using
+ the CD_SET_EJECT command and an argument of 0 to disable
+ eject on exit and 1 to enable eject on exit.
+
+ 05/03/98 Wrote the synchronization code and basic switch statment.
+ Testing different shutdown situations. No hungup child
+ processes yet.
+
+ Things this process needs to do:
+ x 1. Get the data on the current CD and send it to the
+ parent, in response to a request by the parent.
+ x 2. Play a CD
+ x 3. Pause a CD
+ x 4. Resume a CD
+ x 5. Go to next track
+ x 6. Go to Previous track
+ x 8. Eject a CD
+ x 9. Close the CD tray
+ 10. Switch CDs for a CD changer
+ 11. Seek forward (may need a kernel patch?)
+ 12. Seek backwards (may need a kernel patch?)
+ 13. Return error/status codes (like if a play is tried and
+ there is no CD in the player).
+ 14. Shuffle play (on one Cd or among multiple CDs)
+ 15. Repeat Song
+ 16. Repeat whole CD, repeat group of CDs
+
+
+ ----------------------------------------------------------------------- */
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <gtk/gtk.h>
+#include "child_sync.h"
+#include "cd_control.h"
+#include "cddb.h"
+
+int cd_control( int );
+int readline( register int, register char *, register int );
+
+#undef DEBUG1
+#undef DEBUG3
+
+/*
+ Fire up the cd_control process.
+ Create a socket pair.
+ Fork a child process
+ Return the parent's file descriptor for the socket or -1 + errno
+ fill in childpid with the child's PID
+ Child calls cd_control
+*/
+int start_cd_control( int *childpid )
+{
+ int fd[2];
+
+ if( socketpair( AF_UNIX, SOCK_STREAM, 0, fd ) < 0 )
+ return(-1);
+
+ if( ( *childpid = fork() ) < 0 )
+ {
+ perror("start_cd_control, cannot fork");
+ return(-1);
+ } else if( *childpid == 0 ) {
+ close( fd[0] );
+
+ /* Synchronize with the parent, exit if it fails */
+ if( parent_sync( fd[1] ) == 0 )
+ cd_control( fd[1] );
+
+ close( fd[1] );
+ exit(0);
+ }
+ close( fd[1] );
+
+ if( child_sync( fd[0] ) == 0 )
+ return(fd[0]);
+
+ return(-1);
+}
+
+
+/*
+ Load a new CD, read all of its info into the cdinfo array
+*/
+int cdrom_init( struct CDINFO *cdinfo, struct cdrom_volctrl *volctrl, char *cd_device )
+{
+ int fd;
+ int x;
+
+ /* Try and open the /dev/cdrom device */
+ if( ( fd = open( cd_device, O_RDONLY ) ) < 0 )
+ {
+#ifdef DEBUG3
+ g_print( "device = %s\n", cd_device);
+#endif
+
+ perror("CDROM device" );
+ return -1;
+ }
+
+ /* Read the cdrom's header information */
+ if( ioctl( fd, CDROMREADTOCHDR, &cdinfo->tochdr ) != 0 )
+ {
+ perror("cdrom_init(CDROMREADTOCHDR-2)" );
+ return -1;
+ }
+
+ /* Get the time on the end of the last track */
+ cdinfo->leadout.cdte_track = CDROM_LEADOUT;
+ cdinfo->leadout.cdte_format = CDROM_MSF;
+
+ if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->leadout ) != 0 )
+ {
+ perror( "cdrom_init(CDROMREADTOCENTRY-2)" );
+ return -1;
+ }
+
+ /* Calculate the total length of the CD */
+ cdinfo->cd_length = cdinfo->leadout.cdte_addr.msf.second \
+ + (cdinfo->leadout.cdte_addr.msf.minute * 60);
+
+ /* Read the current CD volume level - different from soundcard volume */
+ if( ioctl( fd, CDROMVOLREAD, volctrl ) == 0 )
+ {
+ cdinfo->volume = volctrl->channel0;
+ } else {
+ perror( "cdrom_init(CDROMVOLREAD-2)" );
+ return -1;
+ }
+
+ /* Fill in the tracks[] array with info on all the tracks */
+ for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
+ {
+ /* Read the info for the current track */
+ cdinfo->track[x].te.cdte_track = x+1;
+ cdinfo->track[x].te.cdte_format = CDROM_MSF;
+ if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->track[x].te ) != 0 )
+ {
+ perror( "set_track_length(CDROMREADTOCENTRY)" );
+ return -1;
+ }
+
+ cdinfo->track[x].frame_offset = cdinfo->track[x].te.cdte_addr.msf.minute * 60 + cdinfo->track[x].te.cdte_addr.msf.second;
+
+ /* Convert seconds to frames */
+ cdinfo->track[x].frame_offset *= 75;
+ cdinfo->track[x].frame_offset += cdinfo->track[x].te.cdte_addr.msf.frame;
+
+ /*
+ * Set the length of the previous track
+ * Accomplished by taking the start of the current track - start
+ * of the previous track.
+ */
+ if( x > 0 )
+ {
+ /* Set length to the start of the current one */
+ cdinfo->track[x-1].length = (cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second;
+
+ /* Subtract the start of the previous track */
+ cdinfo->track[x-1].length = cdinfo->track[x-1].length - ((cdinfo->track[x-1].te.cdte_addr.msf.minute * 60) + cdinfo->track[x-1].te.cdte_addr.msf.second);
+
+#ifdef DEBUG1
+ /* Show some debug data on the tracks we find */
+ g_print("track %d - length = %d seconds\n", x-1, cdinfo->track[x-1].length );
+#endif
+ }
+ }
+
+ /*
+ * Set the length of the last track
+ * This is accomplished by subtracting the last track start from the
+ * length of the CD.
+ */
+ x--;
+ cdinfo->track[x].length = cdinfo->cd_length - ((cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second );
+
+#ifdef DEBUG1
+ /* Show debug data on the last track */
+ g_print("track %d - length = %d seconds\n", x, cdinfo->track[x].length );
+
+ g_print("New CD loaded: ");
+ g_print("%d tracks, cd_length = %d, ", cdinfo->tochdr.cdth_trk1, cdinfo->cd_length );
+ g_print("volume = %d\n", cdinfo->volume );
+#endif
+
+ /* Calculate the discid for this CD */
+ cdinfo->discid = cddb_discid( cdinfo );
+
+ cdinfo->revision = -1;
+ cdinfo->extd = NULL;
+
+
+ /* All is well */
+ return fd;
+}
+
+
+/* ----------------------------------------------------------------------
+ Control the low-level audio CD player
+
+ Wait for commands from the parent, execute the command and send back
+ a response if one is needed
+
+ A command is 2 bytes of data - a command and an optional argument.
+ The Commands are defined in cd_control.h as CD_*
+
+ To init the cd, if status is called the first time it will try to init
+ the CD. If this fails it sets cd_fd to -1, so status won't repeatedly
+ try the CD. After the initial failure only a play will try.
+
+ ----------------------------------------------------------------------- */
+int cd_control( int control_fd )
+{
+ char cmnd[2],
+ cd_device[80]; /* Device to talk to */
+ struct cdrom_volctrl volctrl; /* Volume control */
+ struct CDINFO cdinfo; /* All the info on the CD */
+ int fd_cd=0;
+
+ /* Default cdrom device */
+ strcpy( cd_device, "/dev/cdrom" );
+
+ for(;;)
+ {
+ /* Get the command from the parent process */
+ if( read( control_fd, &cmnd, 2 ) != 2 )
+ return(-1);
+
+ switch( cmnd[0] )
+ {
+ case CD_DIAG :
+ puts("cd_control diagnostic\n");
+ break;
+
+ case CD_STATUS :
+ if( fd_cd > 0 )
+ {
+ cdinfo.sc.cdsc_format = CDROM_MSF; /* Info in MM:SS:FF format*/
+ if( ioctl( fd_cd, CDROMSUBCHNL, &cdinfo.sc ) != 0 )
+ {
+ /* perror( "cd_control(CDROMSUBCHNL-2)" ); */
+ cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
+ }
+ } else {
+ cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
+ }
+
+ /* Send the new data back to the main process */
+ write( control_fd, &cdinfo, sizeof( struct CDINFO ) );
+ break;
+
+
+ case CD_PLAY :
+ /* If we are not open yet, try to open it first */
+ if( fd_cd < 1 )
+ {
+ fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device );
+
+ /* Start playing at the beginning*/
+ cmnd[1] = cdinfo.tochdr.cdth_trk0;
+ }
+
+ if( fd_cd > 0 )
+ {
+ /* Setup the CD to play the indicated track */
+ cdinfo.ti.cdti_trk0 = cmnd[1];
+ cdinfo.ti.cdti_trk1 = cdinfo.tochdr.cdth_trk1;
+ cdinfo.ti.cdti_ind0 = cdinfo.ti.cdti_ind1 = 0;
+
+ /* Stop playing previous track and start playing the next */
+ /* Try this without the stop and see what happens... */
+ if( ioctl( fd_cd, CDROMSTOP ) != 0 )
+ {
+ perror( "cd_control(CDROMSTOP-1)" );
+ } else if( ioctl( fd_cd, CDROMPLAYTRKIND, &cdinfo.ti ) != 0 ) {
+ perror( "cd_control(CDROMPLAYTRKIND-1)" );
+ }
+ }
+ break;
+
+ case CD_PAUSE :
+ if( fd_cd > 0 )
+ {
+ if( ioctl( fd_cd, CDROMPAUSE ) != 0 )
+ perror( "cd_control(CDROMPAUSE)" );
+ }
+ break;
+
+ case CD_RESUME :
+ if( fd_cd > 0 )
+ {
+ if( ioctl( fd_cd, CDROMRESUME ) != 0 )
+ perror( "cd_control(CDROMRESUME)" );
+ }
+ break;
+
+ case CD_STOP :
+ if( fd_cd > 0 )
+ {
+ if( ioctl( fd_cd, CDROMSTOP ) != 0 )
+ perror( "cd_control(CDROMSTOP-2)" );
+
+ close( fd_cd );
+ fd_cd = 0;
+ }
+ break;
+
+ case CD_VOLUME :
+ if( fd_cd > 0 )
+ {
+ cdinfo.volume = (unsigned char) cmnd[1];
+
+ volctrl.channel0 = cdinfo.volume;
+ volctrl.channel1 = cdinfo.volume;
+ if( ioctl( fd_cd, CDROMVOLCTRL, &volctrl ) != 0 )
+ perror( "cd_control(CDROMVOLCTRL-3)" );
+ }
+ break;
+
+ case CD_EJECT :
+ if( (fd_cd > 0) )
+ {
+ /* Stop playing, then eject the CD */
+ if( ioctl( fd_cd, CDROMSTOP ) != 0 )
+ {
+ perror( "cd_control(CDROMSTOP-3)" );
+ } else if( ioctl( fd_cd, CDROMEJECT ) != 0 ) {
+ perror( "cd_control(CDROMEJECT)" );
+ }
+ close( fd_cd );
+ fd_cd = 0;
+ }
+ break;
+
+ case CD_SET_EJECT :
+ if( fd_cd > 0 )
+ {
+ /* Set the software configurable eject on close state */
+ if( ioctl( fd_cd, CDROMEJECT_SW, cmnd[1] ) != 0 )
+ perror("cd_control(CDROMEJECT_SW)");
+ }
+ break;
+
+ /* Set the default device, close the current device and open the
+ new one */
+ case CD_SET_DEVICE :
+
+#ifdef DEBUG3
+ printf("got CD_SET_DEVICE\n");
+#endif
+
+ /*
+ This routine is different from the others, it reads a string from
+ the sending process after receiving the 2 byte command
+ */
+ if( readline( control_fd, cd_device, 80 ) < 0 )
+ {
+ perror("readline - 2");
+ }
+
+ /* Strip trailing CR */
+ if( cd_device[strlen(cd_device)-1] == '\n' )
+ cd_device[strlen(cd_device)-1] = 0;
+
+#ifdef DEBUG3
+ printf("Setting cd_control cd_device = %s\n", cd_device );
+#endif
+ /* If a device isn't open, then open one */
+ if( fd_cd < 1 )
+ fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device );
+
+ break;
+
+
+ case CD_QUIT :
+ if( fd_cd )
+ close( fd_cd );
+ return(0);
+ break;
+ }
+ }
+ return(0);
+}
diff --git a/cd_control.h b/cd_control.h
@@ -0,0 +1,85 @@
+/* ------------------------------------------------------------------------
+ cd_control include file for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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 <linux/cdrom.h>
+
+#define CD_DIAG 0x00
+#define CD_PLAY 0x01
+#define CD_PAUSE 0x02
+#define CD_STOP 0x03
+#define CD_EJECT 0x04
+#define CD_SEEK_FWD 0x05
+#define CD_SEEK_REV 0x06
+#define CD_RESUME 0x07
+#define CD_VOLUME 0x08
+#define CD_STATUS 0x09
+#define CD_TOC 0x0A
+#define CD_QUIT 0x0B
+#define CD_SET_EJECT 0x0C
+#define CD_SET_DEVICE 0x0D
+
+
+int start_cd_control( int * );
+
+
+struct song_info {
+ struct cdrom_tocentry te; /* TOC entry data */
+ int length; /* Length of this track in sec */
+ long frame_offset; /* Frame Offset in 1/75 frames */
+};
+
+struct CDINFO {
+ unsigned long discid; /* cddb disc ID */
+ int disc; /* disc # being played, for
+ future support for jukeboxes
+ */
+ int track_length; /* Length of current track */
+ int cd_length; /* Length of the CD */
+ int cd_remaining; /* Remaining # of seconds on CD */
+ int volume; /* Volume setting */
+
+ /* Info on the current cdrom in the drive */
+ struct cdrom_tochdr tochdr; /* TOC Headers */
+ struct cdrom_tocentry leadout; /* Last track info */
+ struct cdrom_ti ti; /* Track(s) to play
+ Track indexing
+ */
+ struct cdrom_subchnl sc; /* Drive status */
+ struct song_info track[99]; /* Info on songs on this CD */
+
+ GString *title; /* Title of this CD */
+ GString *category; /* CDDBD category */
+ GString *name[99]; /* Name of the track */
+ GString *extd; /* Extended data for the CD */
+ GString *extt[99]; /* Extended Track info */
+ int revision; /* CDDB entry revision # */
+
+
+ /* Data used for talking to the other processes */
+ int cddbd_cmnd, /* Command for cddbd */
+ cddbd_stat; /* Status of process */
+ char server[80]; /* Server to connect to */
+ int port; /* Port on server */
+ char line[255]; /* Text from MOTD or SITES */
+ char local_cddb[1024]; /* Path to local CDDB database */
+ char device[80]; /* CDROM device */
+};
diff --git a/cddb.c b/cddb.c
@@ -0,0 +1,888 @@
+/* ---------------------------------------------------------------------
+ basic cddb functions for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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.
+
+ =============================[ HISTORY ]=============================
+ 06/19/98 Adding write extended data to the database. DONE.
+ Switching to using g_malloc, g_free, g_realloc -- they're
+ safer than libc and can provide usage stats.
+
+ 06/16/98 Need to add support for extended data lines. Split lines
+ too of course. This data needs to make it into the
+ database, and be able to send it to the server when
+ the CD is edited.
+
+ Revision # support needs to be added to. Read it and
+ increment it when the CD is submitted to the server.
+ FIXED.
+
+ 06/06/98 I need to support split TTITLE lines, and get the
+ track # from the TTITLE line instead of assuming they
+ just go in order.
+ I found my garbage at the end of the file problem. If
+ A long file was written then it was shortened it
+ wouldn't erase it first, it would just write over
+ the top and leave the old stuff hanging off the end.
+ So I added an unlink to delete it first.
+
+ 05/27/98 Adding creation of the cddb database directories if
+ they don't exist (only creates the ones that don't
+ exist). Added to write_cddb(). Only creates what it
+ needs to. This way it isn't limited to a static list of
+ categories. Works.
+ Found a bug. CDDB read/write don't handle multiple
+ DTITLEs very well.
+
+ 05/16/98 Added expansion of ~/ to $HOME enviornmental variable
+ Added a local_cddb path in the cdinfo structure to point
+ to the local storage location for writing and reading
+ cd information.
+
+ 05/11/98 Split off reading code to read_cddb_file so it can be
+ used to read a specific temporary file after the data
+ is downloaded from the server.
+
+ 05/09/98 Started this part of the XfreeCD code.
+ Correct operation confirmed for discid using
+ Bryan Adams / Waking up the Neighbors id 0xce118c0f
+ Writing database entries works well so far, frames,
+ length, and discid are correct. Default track and title
+ strings are saved ok too.
+ find_discid works.
+
+ ---------------------------------------------------------------------
+ This file includes support for saving and loading local database
+ information.
+ --------------------------------------------------------------------- */
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+#include "xfreecd.h"
+#include "cd_control.h"
+#include "cddb.h"
+
+#undef DEBUG1
+#undef DEBUG2
+#undef DEBUG3
+#undef DEBUG6
+#undef DEBUG9
+
+int
+cddb_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%lu", n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return (ret);
+}
+
+unsigned long cddb_discid( struct CDINFO *cdinfo )
+{
+ int i,
+ t = 0,
+ n = 0,
+ tot_trks;
+
+ tot_trks = cdinfo->tochdr.cdth_trk1;
+
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < tot_trks; i++)
+ n += cddb_sum((cdinfo->track[i].te.cdte_addr.msf.minute * 60) + cdinfo->track[i].te.cdte_addr.msf.second);
+
+ t = ((cdinfo->leadout.cdte_addr.msf.minute * 60) + cdinfo->leadout.cdte_addr.msf.second) - ((cdinfo->track[0].te.cdte_addr.msf.minute * 60) + cdinfo->track[0].te.cdte_addr.msf.second);
+
+ return ((n % 0xff) << 24 | t << 8 | tot_trks);
+}
+
+
+/* -------------------------------------------------------------------------
+ Write the data from cdinfo structure to the correct file in the selected
+ category.
+
+ The master database directory is in cdinfo->local_cddb
+
+ How do I check for the same CD with a different ID so I can symlink?
+
+ if over is 0 then check to make sure the file doesn't already exist
+ if over is 1 then go ahead and overwrite it.
+
+ return -2 if the file already exists.
+ return -3 if the HOME variable cannot be found
+ return -4 if we had trouble creating the local cddb directories
+ ------------------------------------------------------------------------- */
+int write_cddb( struct CDINFO *cdinfo, int over )
+{
+ char fname[1024],
+ tmp_fname[1024],
+ tmpstr[255],
+ idstr[9],
+ *p;
+ int fd,
+ x,
+ i;
+ struct stat sbuf;
+
+ /* Copy the path to the local datbase to a temporary variable */
+ strncpy( fname, cdinfo->local_cddb, 1023 );
+
+ /* Convert a leading ~ into the user's HOME directory */
+ if( fname[0] == '~' )
+ {
+ /* Copy the reset of the path/filename to tmp_fname */
+ strncpy( tmp_fname, &fname[1], 1023 );
+
+ if( ( p = getenv("HOME") ) == NULL )
+ {
+ return(-3);
+ }
+ strncpy( fname, p, 1023 );
+
+ /* Make sure there is a slash inbetween */
+ if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
+ {
+ strcat( fname, "/" );
+ }
+
+ strncat( fname, tmp_fname, 1023-strlen( p ) );
+ }
+
+#ifdef DEBUG1
+ g_print("write_cddb( %s )\n", fname );
+#endif
+
+ /*
+ See if we have the top level directory. If not, try to create it.
+ */
+ if( stat( fname, &sbuf ) < 0 )
+ {
+ /* If the top level directory doesn't exist, create it */
+ if( errno == ENOENT )
+ {
+ /* Try to make the top level directory, if it fails, quit */
+ if( mkdir( fname, S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH | S_IROTH ) < 0 )
+ {
+ return(-4);
+ }
+ } else {
+ return(-4);
+ }
+ }
+
+
+ /* Make sure we have slashes between the path and category directory */
+ if( fname[strlen(fname)] != '/' )
+ strcat( fname, "/" );
+ strcat( fname, cdinfo->category->str );
+ if( fname[strlen(fname)] != '/' )
+ strcat( fname, "/" );
+
+ /*
+ See if the category directory exists. If not, create it.
+ */
+ if( stat( fname, &sbuf ) < 0 )
+ {
+ /* If the top level directory doesn't exist, create it */
+ if( errno == ENOENT )
+ {
+ /* Try to make the category directory, if it fails, quit with an error*/
+ if( mkdir( fname, S_IRWXU | S_IRGRP | S_IXGRP | S_IXOTH | S_IROTH ) < 0 )
+ {
+ return(-4);
+ }
+ } else {
+ return(-4);
+ }
+ }
+
+ sprintf( idstr, "%08lx", cdinfo->discid );
+ strcat( fname, idstr );
+
+ /* Should we check for prior existance? */
+ if( !over )
+ {
+ if( ( fd = open( fname, O_RDONLY ) ) != -1 )
+ {
+ close( fd );
+ return(-2);
+ }
+ }
+
+ /* Make sure it is deleted */
+ unlink( fname );
+
+ if( ( fd = open( fname, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ) < 0 )
+ {
+ return(-1);
+ }
+
+ /* Write the cddb entry from the info in the cdinfostructure */
+ sprintf( tmpstr, "# xmcd CD database file generated by XfreeCD %s\n", VERSION );
+
+ if( write( fd, tmpstr, strlen( tmpstr) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ sprintf( tmpstr, "#\n# Track frame offsets:\n" );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ /* Write the frame offset for each track */
+ for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
+ {
+ sprintf( tmpstr, "#\t%ld\n", cdinfo->track[x].frame_offset );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+ }
+
+ strcpy( tmpstr, "#\n" );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ sprintf( tmpstr, "# Disc length: %d seconds\n#\n", cdinfo->cd_length );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ sprintf( tmpstr, "# Revision: %d\n", cdinfo->revision );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ sprintf( tmpstr, "# Submitted via: XfreeCD %s\n#\n", VERSION );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ sprintf( tmpstr, "DISCID=%08lx\n", cddb_discid( cdinfo ) );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ if( (cdinfo->title == NULL) || (cdinfo->title->len == 0) )
+ {
+ if( write( fd, "DTITLE=\n", 8 ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+ } else {
+ /* If the title is too long, split it up into 70 byte chunks */
+ i = 0;
+ p = cdinfo->title->str;
+ while( i < cdinfo->title->len )
+ {
+ strcpy( tmpstr, "DTITLE=" );
+ strncat( tmpstr, p, 70 );
+ strcat( tmpstr, "\n" );
+
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ if( cdinfo->title->len > 70 )
+ {
+ p = p + 70;
+ i += 70;
+ } else {
+ i = cdinfo->title->len;
+ }
+ }
+ }
+
+ /* Write the titles */
+ for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
+ {
+ if( (cdinfo->name[x] == NULL) || (cdinfo->name[x]->len == 0) )
+ {
+ sprintf( tmpstr, "TTITLE%d=\n", x );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+ } else {
+
+ /* If the track name is too long, split it up into 70 byte chunks */
+ i = 0;
+ p = cdinfo->name[x]->str;
+ while( i < cdinfo->name[x]->len )
+ {
+ sprintf( tmpstr, "TTITLE%d=", x );
+ strncat( tmpstr, p, 70 );
+ strcat( tmpstr, "\n" );
+
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ if( cdinfo->name[x]->len > 70 )
+ {
+ p = p + 70;
+ i += 70;
+ } else {
+ i = cdinfo->name[x]->len;
+ }
+ }
+ }
+ }
+
+ /* Check for a null entry first */
+ if( (cdinfo->extd == NULL) || (cdinfo->extd->len == 0) )
+ {
+ if( write( fd, "EXTD=\n", 6 ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+ } else {
+ /* Write the extended disc data to the file. It it is too long, break it
+ up into 70 byte chuncks
+ */
+ i = 0;
+ p = cdinfo->extd->str;
+ while( i < cdinfo->extd->len )
+ {
+ strcpy( tmpstr, "EXTD=" );
+ strncat( tmpstr, p, 70 );
+ strcat( tmpstr, "\n" );
+
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ if( cdinfo->extd->len > 70 )
+ {
+ p = p + 70;
+ i += 70;
+ } else {
+ i = cdinfo->extd->len;
+ }
+ }
+ }
+
+ /* Write the extended title information to the database */
+ for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
+ {
+ if( (cdinfo->extt[x] == NULL) || (cdinfo->extt[x]->len == 0) )
+ {
+ sprintf( tmpstr, "EXTT%d=\n", x );
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+ } else {
+ /* If the track name is too long, split it up into 70 byte chunks */
+ i = 0;
+ p = cdinfo->extt[x]->str;
+ while( i < cdinfo->extt[x]->len )
+ {
+ sprintf( tmpstr, "EXTT%d=", x );
+ strncat( tmpstr, p, 70 );
+ strcat( tmpstr, "\n" );
+
+ if( write( fd, tmpstr, strlen( tmpstr ) ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ if( cdinfo->extt[x]->len > 70 )
+ {
+ p = p + 70;
+ i += 70;
+ } else {
+ i = cdinfo->extt[x]->len;
+ }
+ }
+ }
+ }
+
+ /* XfreeCD doesn't use this at all */
+ if( write( fd, "PLAYORDER=\n", 11 ) < 0 )
+ {
+ close( fd );
+ return(-1);
+ }
+
+ close( fd );
+
+ return(0);
+}
+
+
+/* -----------------------------------------------------------------------
+ Search all sub-directories below CDDB_PATH for the file to read
+ Fills in the category with the name of the trailing directory that
+ it is found in.
+
+
+ Return 0 on file found
+ Return -1 on an error
+ Return -2 on no file found
+ ----------------------------------------------------------------------- */
+int find_discid( char *fname, /* Path to local database */
+ unsigned long id, /* discid to search for */
+ char *path, /* Return full path */
+ GString **category ) /* Return category found */
+{
+ DIR *dp;
+ struct dirent *dirp;
+ char idstr[9],
+ tmp_fname[1024],
+ *p;
+ int fp;
+
+ /* Convert a leading ~ into the user's HOME directory */
+ if( fname[0] == '~' )
+ {
+ /* Copy the reset of the path/filename to tmp_fname */
+ strncpy( tmp_fname, &fname[1], 1023 );
+
+ if( ( p = (char *) getenv("HOME") ) == NULL )
+ {
+ return(-2);
+ }
+ strncpy( fname, p, 1023 );
+
+ /* Make sure there is a slash inbetween the two */
+ if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
+ {
+ strcat( fname, "/" );
+ }
+
+ strncat( fname, tmp_fname, 1023-strlen( p ) );
+ }
+
+#ifdef DEBUG1
+ g_print("find_discid( %s )\n", fname );
+#endif
+
+
+ sprintf( idstr, "%08lx", id );
+
+ if( ( dp = opendir( fname ) ) == NULL )
+ return(-1);
+
+ while( ( dirp = readdir( dp ) ) != NULL )
+ {
+ if( (strcmp(dirp->d_name,"." )==0) || (strcmp(dirp->d_name, "..")==0) )
+ continue;
+
+ strcpy( path, fname );
+ if( path[strlen(path)] != '/' )
+ strcat( path, "/" );
+ strcat( path, dirp->d_name );
+ if( path[strlen(path)] != '/' )
+ strcat( path, "/" );
+ strcat( path, idstr );
+
+ /* Copy the directory name as the category name */
+ if( *category == NULL )
+ *category = g_string_new( dirp->d_name );
+ else
+ *category = g_string_assign( *category, dirp->d_name );
+
+#ifdef DEBUG1
+ g_print("Checking %s\n", path );
+#endif
+
+ /* Does this file exist? */
+ if( ( fp = open( path, O_RDONLY ) ) != -1 )
+ {
+ close( fp );
+ closedir( dp );
+ return(0);
+ }
+ }
+
+ closedir( dp );
+
+ return(-2);
+}
+
+
+/* -----------------------------------------------------------------------
+ Read the CDDB info from a filename into a cdinfo structure
+ Allocate all needed string storage using gtk's g_string functions
+
+ return -2 = failed to find HOME enviornmental variable
+ ----------------------------------------------------------------------- */
+int read_cddb_file( char *fname, struct CDINFO *tmpinfo )
+{
+ int gotid,
+ i,
+ x,
+ f;
+ FILE *fp;
+ char line[255],
+ discid[9],
+ *p;
+
+ if( ( fp = fopen( fname, "r" ) ) == NULL )
+ {
+ return(-1);
+ }
+
+ /* Read the cddb file, placing data into tmpinfo */
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+
+ /* Check the file to make sure its a cddb file */
+ if( strncmp( line, "# xmcd", 6 ) != 0 )
+ {
+ fclose( fp );
+ return(-1);
+ }
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ /* Find the track offsets, abort serarch at the end of comments */
+ while( ( line[0] == '#' ) && ( strncmp( line, "# Track frame offsets:",22 ) != 0 ) )
+ {
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+ /* Skip all the reset of the comments up to Revision: */
+ while( line[0] == '#' && (strncmp( line, "# Revision:", 11)!=0) )
+ {
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+ /* If we got the Revision line, get the revision # */
+ if( strncmp( line, "# Revision:", 11)==0 )
+ {
+ sscanf( line, "# Revision: %d", &tmpinfo->revision );
+
+ /* Now read the rest of the comments */
+ while( line[0] == '#' )
+ {
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+ }
+
+ /* Read discid lines */
+ /*
+ How should this be handled? check for our id, and discard all others
+ that may be present?
+
+ DISCID= lines are comma seperated and can be multiple lines
+ */
+ gotid = 0;
+ sprintf( discid, "%08lx", tmpinfo->discid );
+ while( strncmp( line, "DISCID=", 7 ) == 0 )
+ {
+ if( strstr( line, discid ) != NULL )
+ gotid = 1;
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+ /* Process multiple DTITLE lines and concatanate them */
+ i = 0;
+ f = 0;
+ while( strncmp( line, "DTITLE", 6 ) == 0 )
+ {
+ p = strtok( line, "=\n" );
+ p = strtok( NULL, "=\n" );
+
+ /* Add the title to the tmpinfo.title string */
+ if( f == 0 )
+ {
+ tmpinfo->title = g_string_new( p );
+ f = 1;
+ } else {
+ tmpinfo->title = g_string_append( tmpinfo->title, p );
+ }
+
+ /* Keep reading DTITLE no matter what */
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+#ifdef DEBUG1
+ g_print("title read = %s\n", tmpinfo->title->str );
+#endif
+
+ /*
+ Copy the titles from the TTITLE strings
+
+ This has to:
+ Get the track # from the TTITLEx
+ strcat split title lines up to the limit of storage (255)
+ */
+ x = -1;
+ f = 0;
+ while( strncmp( line, "TTITLE", 6 ) == 0 )
+ {
+ /* Get the track # */
+ p = strtok( &line[6], "=\n" );
+
+ /* Is it a new track? */
+ if( atoi(p) != x )
+ {
+ /* Yes, reset the length counter and track name */
+ i = 0;
+ f = 0;
+ tmpinfo->name[atoi(p)] = NULL;
+ }
+
+ /* Get the track number and make sure its not too big. */
+ if( ( x = atoi( p ) ) < 99 )
+ {
+ /* Get the track name */
+ p = strtok( NULL, "=\n" );
+
+ /* If its blank, then insert default track name */
+ if( p == NULL )
+ {
+ tmpinfo->name[x] = NULL;
+ } else {
+ if( f == 0 )
+ {
+ tmpinfo->name[x] = g_string_new( p );
+ f = 1;
+ } else {
+ tmpinfo->name[x] = g_string_append( tmpinfo->name[x], p );
+ }
+ }
+ }
+
+ /* Read the next line */
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+
+ /* Process multiple EXTD lines and concatanate them, dynamically
+ allocating memory at tmpinfo->extd for it
+ */
+ i = 0;
+ f = 0;
+ tmpinfo->extd = NULL;
+ while( strncmp( line, "EXTD", 4 ) == 0 )
+ {
+ /* Add to the data until the end is reached */
+ p = strtok( line, "=\n" );
+ p = strtok( NULL, "=\n" );
+
+ if( p != NULL )
+ {
+ /* Move the pointer and copy the new string */
+ if( f == 0 )
+ {
+ tmpinfo->extd = g_string_new( p );
+ f = 1;
+ } else {
+ tmpinfo->extd = g_string_append( tmpinfo->extd, p );
+ }
+ }
+
+ /* Keep reading lines */
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+
+ /*
+ Copy the extended title data from the EXTTx entries
+
+ This has to:
+ Get the track # from the EXTTx
+ Allocate storage for it and copy it over, and handle multiple lines
+ for each track entry.
+ */
+ x = -1;
+ f = 0;
+ while( strncmp( line, "EXTT", 4 ) == 0 )
+ {
+ /* Get the track # */
+ p = strtok( &line[4], "=\n" );
+
+ /* Is it a new track? */
+ if( atoi(p) != x )
+ {
+ /* Yes, reset the length counter and track name */
+ i = 0;
+ f = 0;
+ tmpinfo->extt[atoi(p)] = NULL;
+ }
+
+ /* Get the track number, make sure it isn't too big */
+ if( ( x = atoi( p ) ) < 99 )
+ {
+ /* Get the extended data from the rest of the line*/
+ p = strtok( NULL, "=\n" );
+
+ /* Process multiple EXTT lines and concatanate them, dynamicly
+ allocating memory at tmpinfo->extd for it
+ */
+ if( p != NULL )
+ {
+ /* Move the pointer and copy the new string */
+ if( f == 0 )
+ {
+ tmpinfo->extt[x] = g_string_new( p );
+ f = 1;
+ } else {
+ tmpinfo->extt[x] = g_string_append( tmpinfo->extt[x], p );
+ }
+ }
+ }
+
+ /* Read the next line */
+ if( fgets( line, 255, fp ) == NULL )
+ {
+ fclose( fp );
+ return(-1);
+ }
+ }
+
+ fclose( fp );
+
+ return(0);
+}
+
+
+/* -----------------------------------------------------------------------
+ Read a cddb entry for the current CD
+
+ This needs to search through the sub-directories in CDDB_PATH to find
+ the discid of the current CD.
+
+ We then read the file, filling in the track names, etc.
+ We should also compare the frame count to make sure it is the
+ correct CD.
+
+ Returns -1 if there was an error
+ Returns -2 if it cannot find the file
+ ----------------------------------------------------------------------- */
+int read_cddb( struct CDINFO *cdinfo )
+{
+ int x;
+ char fname[255];
+ struct CDINFO tmpinfo;
+
+ /* Clean out the structure */
+ bzero( &tmpinfo, sizeof( struct CDINFO ) );
+
+ tmpinfo.discid = cdinfo->discid;
+
+ /* Find out where this ID lives and fill in fname with full name */
+ if( find_discid( cdinfo->local_cddb, tmpinfo.discid, fname, &tmpinfo.category ) < 0 )
+ {
+ /* Could not find an entry for this */
+ return(-2);
+ }
+
+ if( read_cddb_file( fname, &tmpinfo ) == 0 )
+ {
+ cdinfo->title = tmpinfo.title;
+ for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
+ {
+ cdinfo->name[x] = tmpinfo.name[x];
+ }
+
+ /* Copy the category over too */
+ cdinfo->category = tmpinfo.category;
+
+ /* Copy the revision # over if it is valid */
+ if( tmpinfo.revision >= 0 )
+ cdinfo->revision = tmpinfo.revision;
+ else
+ cdinfo->revision = 1;
+
+ /* Copy the extd pointer over */
+ cdinfo->extd = tmpinfo.extd;
+
+ /* Copy all the extended track pointers */
+ for( x = 0; x < 99; x++ )
+ cdinfo->extt[x] = tmpinfo.extt[x];
+ }
+
+ return(0);
+}
diff --git a/cddb.h b/cddb.h
@@ -0,0 +1,27 @@
+/* ------------------------------------------------------------------------
+ cddb include file for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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.
+
+ ------------------------------------------------------------------------ */
+unsigned long cddb_discid( struct CDINFO * );
+int write_cddb( struct CDINFO *, int );
+int read_cddb( struct CDINFO * );
+int read_cddb_file( char *, struct CDINFO *);
+int find_discid( char *, unsigned long, char *, GString ** );
diff --git a/cddbd.c b/cddbd.c
@@ -0,0 +1,1220 @@
+/* ---------------------------------------------------------------------
+ cddbd interface for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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.
+
+ =============================[ HISTORY ]=============================
+ 07/05/98 Removed the strcat of tmpstr from the track offset loop
+ when requesting CD info. This was causing the odd
+ behavior, depending on the state of memory.
+
+ 06/19/98 Changed offset in the track loop to be 0 reference
+ instead of 1.
+
+ 06/16/98 NIN Broken is living up to its name. It is causing the
+ server to return an error 500. Hmm, I somehow left out
+ the length of the cd. This is fixed. It now downloads
+ NIN correctly (woo hoo, ack, pthhhtttt). Anyone want
+ a copy of NIN Broken? I'll mail it to you in little
+ pieces...
+
+ 06/02/98 Sending the full line returned by the server to the
+ caling process in cdinfo.line
+
+ 05/24/98 Adding support for inexact matches.
+ Adding a return type of CDDBD_INEX_LINE
+
+ 05/11/98 Adding more information from main process. Passes a
+ cdinfo structure.
+
+ 05/10/98 Added basic socket open/close/read/write functions.
+
+ 05/09/98 Started this part of the XfreeCD code.
+
+
+ ---------------------------------------------------------------------
+ This implements the internet connection to a cddbd server. Local data
+ is accessed using the functions in ...
+
+ Hmm, how to implemetn all this?
+ State machine for error responses and link to next command?
+
+ I also need to get more info on the CD than just the CD's discid
+
+ --------------------------------------------------------------------- */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include "xfreecd.h"
+#include "child_sync.h"
+#include "cd_control.h"
+#include "cddbd.h"
+#include "cddb.h"
+
+
+#undef DEBUG1
+#undef DEBUG3
+
+int cddbd( int );
+
+/*
+ Fire up the cddb process.
+ Create a socket pair.
+ Fork a child process
+ Return the parent's file descriptor for the socket or -1 + errno
+ fill in childpid with the child's PID
+ Child calls cddbd
+*/
+int start_cddbd( int *childpid )
+{
+ int fd[2];
+
+ if( socketpair( AF_UNIX, SOCK_STREAM, 0, fd ) < 0 )
+ return(-1);
+
+ if( ( *childpid = fork() ) < 0 )
+ {
+ perror("start_cddbd, cannot fork");
+ return(-1);
+ } else if( *childpid == 0 ) {
+ close( fd[0] );
+
+ /* Synchronize with the parent, exit if it fails */
+ if( parent_sync( fd[1] ) == 0 )
+ cddbd( fd[1] );
+
+ close( fd[1] );
+ exit(0);
+ }
+ close( fd[1] );
+
+ if( child_sync( fd[0] ) == 0 )
+ return(fd[0]);
+
+ return(-1);
+}
+
+/* -----------------------------------------------------------------------
+ Read a line from the socket
+ ----------------------------------------------------------------------- */
+int readn( register int fd, register char *ptr, register int nbytes )
+{
+ int nleft,
+ nread;
+
+ nleft = nbytes;
+ while( nleft > 0 )
+ {
+ nread = read( fd, ptr, nleft );
+ if( nread < 0 )
+ return( nread );
+ else if( nread == 0 )
+ break;
+
+ nleft -= nread;
+ ptr += nread;
+ }
+ return (nbytes - nleft);
+}
+
+
+/* -----------------------------------------------------------------------
+ Write n bytes of data to the socket
+
+ Loop until all the data is written.
+ ----------------------------------------------------------------------- */
+int writen( register int fd, register char *ptr, register int nbytes )
+{
+ int nleft,
+ nwritten;
+
+ nleft = nbytes;
+ while( nleft > 0 )
+ {
+ nwritten = write( fd, ptr, nleft );
+ if( nwritten <= 0 )
+ return( nwritten );
+
+ nleft -= nwritten;
+ ptr += nwritten;
+ }
+
+ return (nbytes - nleft );
+}
+
+
+/* -----------------------------------------------------------------------
+ A VERY inefficent read routine. I need to rewrite this to read as much
+ as possible and scan the buffer read...
+
+ But then how do you put back characters after the CR that you want to
+ read the next time it is called? Static holding previous spares maybe?
+ ----------------------------------------------------------------------- */
+int readline( register int fd, register char *ptr, register int maxlen )
+{
+ int n,
+ rc;
+ char c;
+
+ for( n = 1; n < maxlen; n++ )
+ {
+ if( ( rc = read( fd, &c, 1 ) ) == 1 )
+ {
+ *ptr++ = c;
+ if( c == '\n' )
+ break;
+ } else if( rc == 0 ) {
+ if( n == 1 )
+ return(0);
+ else
+ break;
+ } else {
+ return(-1);
+ }
+ }
+
+ *ptr = 0;
+ return(n);
+}
+
+
+/* -----------------------------------------------------------------------
+ Open a cddbd connection, return the fd
+
+ This handles the connection and initial login to the server,
+ returns error codes on failure.
+
+ Pass the server name and port
+ ----------------------------------------------------------------------- */
+int open_cddbd( char *server, int port )
+{
+ struct sockaddr_in tcp_srv_addr; /* Server's Internet socket addr. */
+ struct hostent tcp_host_info; /* from gethostbyname */
+ struct hostent *hp;
+ unsigned long inaddr;
+ int fd;
+
+
+ bzero( &tcp_srv_addr, sizeof( tcp_srv_addr ) );
+ tcp_srv_addr.sin_family = AF_INET;
+
+
+ /* Setup the port, exit if illegal passed */
+ if( port < 1 )
+ return(-2);
+
+ tcp_srv_addr.sin_port = htons( port );
+
+ /* Try the hostname as a dotted decimal first */
+ if( ( inaddr = inet_addr( server ) ) != INADDR_NONE )
+ {
+ bcopy((char *)&inaddr,(char *)&tcp_srv_addr.sin_addr,sizeof(inaddr));
+ tcp_host_info.h_name = NULL;
+ } else {
+ /* Not dotted decimal, try it as a name */
+ if( ( hp = gethostbyname( server ) ) == NULL )
+ {
+ return(-3);
+ }
+ tcp_host_info = *hp; /* Copy the structure */
+ bcopy( hp->h_addr, (char *) &tcp_srv_addr.sin_addr, hp->h_length );
+ }
+
+ /* Create the socket */
+ if( ( fd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
+ {
+ return(-4);
+ }
+
+ /* Connect to the server */
+ if( connect(fd,(struct sockaddr *)&tcp_srv_addr,sizeof(tcp_srv_addr)) < 0 )
+ {
+ close( fd );
+ return(-5);
+ }
+
+ return(fd);
+}
+
+/* -----------------------------------------------------------------------
+ Close down the connection to the server.
+
+ Send quit and close socket
+ ----------------------------------------------------------------------- */
+int close_cddbd( fd )
+{
+ char line[255];
+
+ writen( fd, "quit\n", strlen( "quit\n" ) );
+
+ /* Read the closing banner from the server */
+ if( readline( fd, line, 255 ) < 0 )
+ {
+ close( fd );
+ return(-2);
+ }
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ close( fd );
+ return(0);
+}
+
+
+/* -----------------------------------------------------------------------
+ Return the response code
+ ----------------------------------------------------------------------- */
+int cddbd_code( char *line )
+{
+ char *p;
+
+ p = strtok( line, " \n" );
+ return( atoi( p ) );
+}
+
+
+
+/* -----------------------------------------------------------------------
+ Watch for commands from the main process and execute connections to
+ the indicated cddbd internet database server.
+
+ Commands ... DB_*
+ Uses the cddbd_cmnd structure to pass the command, server and port.
+
+ Also need to read the sites listing, and motd.
+
+ Retrieve data for ID from server SERVER using PORT
+
+ returns a CDINFO structure with a status byte? How to keep it updated
+ without returning a full CDINFO every time?
+
+ As we progress thru the protocol, send back status updates to the parent
+ process.
+
+ ----------------------------------------------------------------------- */
+int cddbd( int control_fd )
+{
+ struct CDINFO cdinfo;
+ FILE *fp;
+ int cddbd_fd;
+ char line[255],
+ *tmpfile,
+ *p;
+ int x,
+ code;
+
+
+ for(;;)
+ {
+ /* Get the command from the parent process */
+ if( read( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd read error");
+ return(-1);
+ }
+
+ switch( cdinfo.cddbd_cmnd )
+ {
+ case DB_DIAG :
+ puts("cddbd diagnostic\n");
+ break;
+
+ /* Get the info on a CD from the server */
+ case DB_READ :
+
+#ifdef DEBUG1
+ g_print("Searching for 0x%08x at %s : %d\n", cdinfo.discid, cdinfo.server, cdinfo.port );
+#endif
+
+ if( (cddbd_fd = open_cddbd( cdinfo.server, cdinfo.port )) < 0 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the connection failed */
+ cdinfo.cddbd_stat = CDDBD_OPEN_ERR;
+ sprintf( cdinfo.line, "err=%d", cddbd_fd );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-1");
+ break;
+ }
+ break;
+ }
+
+ /* Tell the parent we are connected ok */
+ cdinfo.cddbd_stat = CDDBD_OPEN_OK;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-2");
+ break;
+ }
+
+ /* Read the opening banner from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-3");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 201 )
+ {
+ close_cddbd( cddbd_fd );
+ /* Tell the parent the connect failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-4");
+ break;
+ }
+ break;
+ }
+
+ /* Identify ourself to the server */
+ sprintf( line, "cddb hello %s %s XfreeCD %s\n", getenv("USER"),
+ getenv("HOSTNAME"),
+ VERSION );
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-5");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-6");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 200 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Send a Query to the server */
+ /* This can be quite! long (try NIN broken, 99 tracks) so break it up
+ into smaller chuncks
+ */
+ sprintf( line, "cddb query %08lx %d ", cdinfo.discid, cdinfo.tochdr.cdth_trk1 );
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ /* Write the first part of the query to the server */
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+ /* List of offsets, send it one offset at a time */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( line, "%ld ", cdinfo.track[x].frame_offset );
+
+#ifdef DEBUG1
+ g_print("[%d]%s", x, line );
+#endif
+
+ /* Write this next chunck of the request */
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+ }
+
+ /* End of the query with length in seconds */
+ sprintf(line, "%d\n", cdinfo.cd_length );
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server, get the coded response,
+ and the rest up to the '.'
+ */
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-9");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( (code > 211) || (code==202) )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-10");
+ break;
+ }
+ break;
+ }
+
+ /* Inexact match, get the list */
+ if( code == 211 )
+ {
+ /* Return the inexact match lines to the sparent process */
+ while( line[0] != '.' )
+ {
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-11");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+ /* Send the line to the parent */
+ cdinfo.cddbd_stat = CDDBD_INEX_LINE;
+ strncpy( cdinfo.line, line, 254 );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-11a");
+ break;
+ }
+ }
+
+ /* We are finished with the server */
+ close_cddbd( cddbd_fd );
+ break;
+ }
+
+ /* Exact match, get the category ?? and discid ?? */
+ if( code == 200 )
+ {
+ /* Tell the parent that we got a match */
+ cdinfo.cddbd_stat = CDDBD_MATCH_OK;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-11a");
+ break;
+ }
+
+ /* Get the category (code has already placed a null in line) */
+ p = strtok( NULL, " \n" );
+ if( cdinfo.category == NULL )
+ cdinfo.category = g_string_new( p );
+ else
+ cdinfo.category = g_string_assign( cdinfo.category, p );
+
+#ifdef DEBUG3
+ g_print("category = %s\n", cdinfo.category->str );
+#endif
+ /* We could also get the discid and title here... */
+ }
+
+
+ /* Send a read command to the server */
+ sprintf( line, "cddb read %s %08lx\n", cdinfo.category->str, cdinfo.discid );
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-12");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-13");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 210 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-14");
+ break;
+ }
+ break;
+ }
+
+ /*
+ Get the database file from the server.
+ Write the file into a temporary file to be read by cddb_read
+ */
+ if( ( tmpfile = tmpnam( NULL ) ) == NULL )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_TMPF_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-15");
+ break;
+ }
+ break;
+ }
+
+ if( ( fp = fopen( tmpfile, "wb" ) ) == NULL )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_FOPEN_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-16");
+ break;
+ }
+ break;
+ }
+
+ /* Tell the parent we are reading the database entry */
+ cdinfo.cddbd_stat = CDDBD_ENTRY_OK;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-14a");
+ break;
+ }
+
+ while( line[0] != '.' )
+ {
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-17");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+ /* Strip trailing CR -- kludge, may not be there... */
+ line[strlen(line)-2] = 0;
+ fprintf(fp, "%s\n", line);
+ }
+
+ fclose( fp );
+
+ /* We are finished with the server */
+ close_cddbd( cddbd_fd );
+
+
+ /* Read the file into the cdinfo structure, and write it to its
+ final location in the local database.
+ */
+ if( read_cddb_file( tmpfile, &cdinfo ) == 0 )
+ {
+ /* Make sure it is saved with our discid! */
+ cdinfo.discid = cddb_discid( &cdinfo );
+
+ if( write_cddb( &cdinfo, 1 ) == 0 )
+ cdinfo.cddbd_stat = CDDBD_DONE_OK;
+ else
+ cdinfo.cddbd_stat = CDDBD_DONE_ERR;
+ } else {
+ cdinfo.cddbd_stat = CDDBD_DONE_ERR;
+ }
+
+#ifdef DEBUG3
+ g_print("category = %s\n", cdinfo.category->str );
+#endif
+
+ /* Send the new structure back to the parent process */
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-18");
+ break;
+ }
+
+ /* Delete the temporary file */
+#ifdef DEBUG1
+ g_print( "tmpfile=%s\n", tmpfile );
+#else
+ unlink( tmpfile );
+#endif
+
+ break;
+
+ /* Get the MOTD from the current server */
+ case DB_MOTD :
+ if( (cddbd_fd = open_cddbd( cdinfo.server, cdinfo.port )) < 0 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the connection failed */
+ cdinfo.cddbd_stat = CDDBD_OPEN_ERR;
+ sprintf( cdinfo.line, "err=%d", cddbd_fd );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-1");
+ break;
+ }
+ break;
+ }
+
+ /* Read the opening banner from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-3");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 201 )
+ {
+ close_cddbd( cddbd_fd );
+ /* Tell the parent the connect failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-4");
+ break;
+ }
+ break;
+ }
+
+ /* Identify ourself to the server */
+ sprintf( line, "cddb hello %s %s XfreeCD %s\n", getenv("USER"),
+ getenv("HOSTNAME"),
+ VERSION );
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-5");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-6");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 200 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Tell the server we want the MOTD */
+ strcpy( line, "motd\n" );
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 210 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Tell the parent about the MOTD date and time */
+ cdinfo.cddbd_stat = CDDBD_MOTD_LINE;
+ strncpy( cdinfo.line, line, 254 );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+
+ /* Read the MOTD one line at a time */
+ while( line[0] != '.' )
+ {
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG10
+ g_print( "%s", line );
+#endif
+
+ /* Send the line to the parent */
+ cdinfo.cddbd_stat = CDDBD_MOTD_LINE;
+ strncpy( cdinfo.line, line, 254 );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ }
+
+ close_cddbd( cddbd_fd );
+
+ break;
+
+ /*
+ Read the list of sites from the server.
+ Only return sites that we can talk to (cddb, not html)
+ */
+ case DB_SITES :
+ if( (cddbd_fd = open_cddbd( cdinfo.server, cdinfo.port )) < 0 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the connection failed */
+ cdinfo.cddbd_stat = CDDBD_OPEN_ERR;
+ sprintf( cdinfo.line, "err=%d", cddbd_fd );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-1");
+ break;
+ }
+ break;
+ }
+
+ /* Read the opening banner from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-3");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 201 )
+ {
+ close_cddbd( cddbd_fd );
+ /* Tell the parent the connect failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-4");
+ break;
+ }
+ break;
+ }
+
+ /* Identify ourself to the server */
+ sprintf( line, "cddb hello %s %s XfreeCD %s\n", getenv("USER"),
+ getenv("HOSTNAME"),
+ VERSION );
+
+#ifdef DEBUG1
+ g_print("%s", line );
+#endif
+
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-5");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-6");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 200 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Tell the server we want the MOTD */
+ strcpy( line, "sites\n" );
+ if( writen( cddbd_fd, line, strlen( line ) ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the write failed */
+ cdinfo.cddbd_stat = CDDBD_WRITE_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG1
+ g_print( "%s", line );
+#endif
+
+ strncpy( cdinfo.line, line, 254 );
+ code = cddbd_code( line );
+ if( code > 210 )
+ {
+ close_cddbd( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = code;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-7");
+ break;
+ }
+ break;
+ }
+
+ /* Read the MOTD one line at a time */
+ while( line[0] != '.' )
+ {
+ /* Get the response from the server */
+ if( readline( cddbd_fd, line, 255 ) < 0 )
+ {
+ close( cddbd_fd );
+
+ /* Tell the parent the read failed */
+ cdinfo.cddbd_stat = CDDBD_READ_ERR;
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ break;
+ }
+
+#ifdef DEBUG10
+ g_print( "%s", line );
+#endif
+
+ /*
+ Here we should check the protocol level and parse the
+ response and only return sites that we can talk to.
+
+ For the moment, just return the lines to the parent
+ */
+
+ /* Send the line to the parent */
+ cdinfo.cddbd_stat = CDDBD_SITE_LINE;
+ strncpy( cdinfo.line, line, 254 );
+ if( write( control_fd, &cdinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror("cddbd write error-8");
+ break;
+ }
+ }
+
+ close_cddbd( cddbd_fd );
+ break;
+
+
+ case DB_QUIT :
+ return(0);
+ break;
+ }
+ }
+ return(0);
+}
diff --git a/cddbd.h b/cddbd.h
@@ -0,0 +1,37 @@
+/* ------------------------------------------------------------------------
+ cddbd include file for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ ------------------------------------------------------------------------ */
+#define DB_DIAG 0x00
+#define DB_READ 0x01
+#define DB_QUIT 0x02
+#define DB_MOTD 0x03
+#define DB_SITES 0x04
+#define DB_WRITE 0x05
+
+#define CDDBD_DONE_OK 0x00
+#define CDDBD_OPEN_ERR 0x01
+#define CDDBD_OPEN_OK 0x02
+#define CDDBD_READ_ERR 0x03
+#define CDDBD_WRITE_ERR 0x04
+#define CDDBD_TMPF_ERR 0x05
+#define CDDBD_FOPEN_ERR 0x06
+#define CDDBD_DONE_ERR 0x07
+#define CDDBD_MATCH_OK 0x08
+#define CDDBD_ENTRY_OK 0x09
+#define CDDBD_MOTD_LINE 0x0A
+#define CDDBD_SITE_LINE 0x0B
+#define CDDBD_INEX_LINE 0x0C
+#define CDDBD_MAX 0x10
+
+
+int start_cddbd( int * );
+
+int readn( register int, register char *, register int );
+int writen( register int, register char *, register int );
+int readline( register int, register char *, register int );
+
diff --git a/child_sync.c b/child_sync.c
@@ -0,0 +1,66 @@
+/* -----------------------------------------------------------------------
+ child process synchronization functions
+
+ part of XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ ==========================[ HISTORY ]==================================
+ 05/09/98 Moved these functions into this seperate file
+
+ ----------------------------------------------------------------------- */
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+
+
+/*
+ Synchronize with the parent process. Wait until we receive a 'P'
+ from the parent. Send a 'C' to the parent in response
+
+ Return a 0 if all went ok
+ Return a -1 if something went wrong
+*/
+int parent_sync( int fd )
+{
+ char c;
+
+ if( read( fd, &c, 1 ) != 1 )
+ return(-1);
+
+ if( write( fd, "C", 1 ) != 1 )
+ return(-1);
+
+ return(0);
+}
+
+
+
+/*
+ Synchronize with the child process. Send a 'P' to the child and wait
+ until a 'C' is received back.
+
+ Return a 0 if all went ok
+ Return a -1 if something went wrong
+*/
+int child_sync( int fd )
+{
+ char c;
+
+ if( write( fd, "P", 1 ) != 1 )
+ return(-1);
+
+ if( read( fd, &c, 1 ) != 1 )
+ return(-1);
+
+ if( c != 'C' )
+ return(-1);
+
+ return(0);
+}
diff --git a/child_sync.h b/child_sync.h
@@ -0,0 +1,10 @@
+/* ------------------------------------------------------------------------
+ child_sync include file for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ ------------------------------------------------------------------------ */
+int parent_sync( int fd );
+int child_sync( int fd );
diff --git a/xfreecd.c b/xfreecd.c
@@ -0,0 +1,5293 @@
+/* ---------------------------------------------------------------------
+
+ XfreeCD v0.7.8
+
+ Copyright (C) 1998 Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ 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
+ of the License, 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.
+
+ ============================[ HISTORY ]==============================
+ 12/02/98 Tracking down the crash on CDDB retrieveal bug that people
+ have been reporting. I've finally duplicated it myself
+ and narrowed it down to the code after printing the string
+ "Got the new data ok" and the call to local_cddb();
+ My theory is that display.plabel or display.progress don't
+ get set to a NULL when deleted, so my checks fail and
+ it tries to write to already freed memory.
+ Removed the progress update right before destroying the
+ progress box, no point if its going to get deleted!
+ I cannot be 100% positive, but I think this has fixed the
+ recall problem. Needs testing.
+
+ Another bug. If the progress box is killed, it never opens
+ again until the program is restarted. Fix this in next
+ version.
+
+
+ 07/05/98 Fixed intermittant bug with recalling CD info. There was
+ a spare strcat(tmpstr) in the request loop of offsets!
+ Depending on the state of memory this could really hose
+ the requests.
+
+ 06/26/98 Fixing Revision # for original submissions. Store them
+ locally as #0 so that the first send to the server will
+ increment it to #1 and the server will get #1.
+ Also added the VERSION number to the makefile to make
+ changing the version easier.
+
+ 06/22/98 I just noticed a problem. The Length of track #1 is wrong!
+ Seems to be stuck at 20:36. cd_control.c was waiting until
+ the track was at #2 before calculating the length. FIXED.
+
+ 06/20/98 TODO
+ 1. Fix EXT problems.
+ FIXED.
+ 2. Reread spec on blank tracks. It refused 'Track 6'
+ I shouldn't save any default strings to disk or send
+ them to the database. Only show them when updating
+ the clist. DONE. Sends a blank ttitle if none is
+ entered.
+
+ 3. Made change, but edit doesn't work and it complains
+ about lots of g_strings != NULL (means ==) that need
+ to be fixed (probably in the edit an edit copy). Make
+ sure the (blank) placeholders are not copied over unless
+ they are edited.
+ Writing of blank tracks doesn't work when the user has
+ deleted everything since it holds a string of zero
+ length.
+ FIXED. Check the length for 0.
+
+ 4. If the CD is paused when XfreeCD is run it doesn't
+ show the current track in the display. FIXED.
+
+ 5. If the CD is paused when XfreeCD is run and you show
+ the track names, it is blank. The database has not
+ been read yet. FIXED.
+
+ 6. Main Tracklist display is updated every second...
+ This is because some of the CDDB entries have a revision
+ of -1. I convert these into revision 1 now.
+
+ 06/19/98 Finishing up the extended data support. Adding free_cdinfo
+ to the appropriate places. Need to make sure its only called
+ when its needed and not in the update loop.
+ Changed Cancel button in edit window to Close
+
+ Shit. Nov I've broken it and I don't know how. Well, then
+ I'll just break it more. Changing all string usage to
+ dynamically allocated GString * from gtk.
+
+ Whew! Okay, I've converted all the static string storage
+ over to GString usage. It appears to be working okay for
+ the moment. Now to stress test it with all the CDDB
+ requirements.
+
+ Testing and making submissions to CDDB test server.
+ 1. Inexact doesn't save to disk with my ID, it saves it
+ with the downloaded id (because I canged the way it
+ is saved after being received!). Change in cddbd.c
+ so it save to local, reads it, changes discid and
+ saves it again <ICK>. FIXED.
+
+ 2. There is a problem when reading extended info. It
+ is writing a blank EXTD and EXT0 then another EXT0
+ and then up to the end-1 tracks. Fix it tomorrow.
+ FIXED. 1 - off error.
+
+ 06/16/98 Things that need fixing:
+ 1. 99 track CD isn't downloaded, causes an error and
+ it retries the server endlessly.
+ The track array in cdinfo was 1 short! I should use
+ 0-98 instead of 1-99 for all access.
+ FIXED.
+
+ 2. Server error caused by not sending all the track offsets
+ (buffer overflow). Fixed by sending it the offsets one
+ at a time. FIXED.
+
+ 3. Edit window coredumps when trying to edit 99 track
+ NIN-broken. FIXED. display structure had a [99] in it.
+
+ 4. Main tracklist window flickers every second...
+
+ 5. Need to process extended data, save to local database,
+ resend it to server when tracks change. Don't need
+ to edit it or display it (most CDs don't use it at all).
+ Reading works now.
+ a. Need to write it back to disk. DONE.
+ b. Need to free the used memory when a CD is ejected
+ DONE.
+
+ 6. Need to read the Revision # correctly
+ DONE.
+
+ 06/06/98 Fixed problem with changing categories. Once the new
+ category has successfully been written it erases the
+ old category.
+ While it is waiting to get new data from the server
+ it is displaying the old track data. Needs to be erased
+ from the cdinfo structure as well as the display.
+ FIXED.
+ I would like to make long title in the track window
+ wrap automatically, but the GTK label doesn't have
+ wrapping, so too bad.
+ Adding a Send to Server option. This just uses the
+ local mail binary (must be in the path). This should
+ work for everyone.
+ Works, Tested successfully.
+ Added support for multiple TITLEx lines. Tested, and it
+ works fine. Sent 5 test of long title and long track
+ names to the cddb-test server.
+ I just noticed a bug with the clist edit window. It
+ is resetting the vertical scroll bar when you select
+ a track that has to be scrolled down to. Probably
+ because I reload the clist when the trackname is
+ edited. FIXED.
+
+ 06/05/98 Trying to tidy up all the loose ends.
+ GTK cannot figure out the geometry of the window before
+ it is actually showing, so the negative geometry with
+ titlebar doean't work right. Hardcoded the window size
+ to 137x60
+
+ 06/02/98 Adding display of full string returned by server error.
+ Fixed close error, I was returning the wrong thing from
+ delete_event.
+
+ 06/01/98 Adding seperate Window Class names to the different
+ windows so that the user can individually control the
+ title bar, move/resize, etc. As suggested by Michael
+ J. Hammel
+
+ 05/31/98 Adding support for -geometry, apparently this has not
+ been added to GTK+ itself yet, so I have to do it
+ myself, using XParseGeometry. Works Great!
+ When geometry is passed a '- it needs to be relative
+ to the other corner of the window, not the upper
+ left. Fixed.
+
+ 05/28/98 Bug with eject on exit. It needs to send the current
+ state to the cd_control process when it reads the
+ config file. FIXED.
+ Need to add dropdown list for categories in edit. DONE.
+
+ 05/27/98 More finishing touches <G>.
+ Added selection of next track in the track window when
+ it goes to the next track. Looks nicer.
+ Fixed a bug with multiple DTITLE lines.
+
+ NEED TO DO:
+ x 1. Build local cddb directories sometime (either under
+ the setup menu, or the first time it writes,
+ or at compile time). The best would be when it
+ tries to write to a non-existant directory, create
+ the structure needed.
+ Works. It creates only the category directory
+ needed and the root cddb directory if it doesn't
+ exist yet. This way the categories aren't limited
+ to a static list.
+ x 2. Editing track names and saving to local database.
+ x 3. Sending edited track info to the database, or just
+ a new discid for the CD. Requires email. Use the
+ mail binary, but allow users to spec. it?
+ x 4. Allow user to select local .cddb support or internet
+ so they can build their own database in the cddb
+ format even if they have no inet connection.
+ 5. Clean up code. Add comments.
+ There is a bunch of duplicated code that can be
+ put into a subroutine with maybe 1 parameter
+ passed to it. The clist updating routines are
+ one.
+ x 6. Convert my usage of printf to g_print
+
+ 05/26/98 Finishing touches. Got the text window to work, thanks
+ to Owen. I had to add it to the notebook before
+ it is realized, otherwise it doesn't know who its
+ parent is (this is because text windows are changes as
+ you add to them).
+ Changed to main window to be a POPUP so that it has no
+ title bars. Keeps people from resizing it too!
+
+ 05/25/98 Problem with using device from main process -- we only
+ send 2 bytes to the cd_control process!! How am I going
+ to pass the new device? A 2 stage send? With the second
+ part being the new device?
+
+ Added CD_SET_DEVICE to cd_control.
+ Adding sending of it to read_config.
+ Switching cd device is now working!
+
+ Adding clear of display.tclist to no disc display points
+
+
+ 05/24/98 Adding support for inexact matches. CDDBD_INEX_LINE.
+ Inexact matching works!!! It saves it as the locally
+ calculated discid (recalculated just before the save).
+
+ x 1. Need to update the track list if its already open
+ when the new data is downloaded from the server or
+ read from disk.
+ x 2. Clear the track list and title when nodisc info is
+ available (on change, use Track #, etc).
+ x 3. Click on a track in the track list to jump to
+ that track directly.
+ no 5. CD changer support...
+ x 6. Volume update is slow, needs to be called on click.
+
+ I've decided not to include changer support. Too hard to
+ integrate into the current GUI. And there probably isn't
+ a great need.
+
+ 05/23/98 The Help/Setup dialog is partially working. It still
+ complains when making the help text box, why?
+ The startup sequence isn't working right. Not playing
+ after closing the tray, or even when already closed.
+
+ I also don't like the startup pause for checking for the
+ current status. That should be handled by a startup
+ state machine in the status callback. May help solve the
+ startup play problem. Fixed. Status returned was NO_STATUS
+ which I was checking for some reason.
+
+ Software selectable eject on exit with no play is now
+ working correctly.
+
+ Server refresh is working as far as updating the internal
+ list but the list in the setup dialog isn't working. Also
+ changes to cddb and device are cross-polinated! It is
+ happening at the realloc in the device_edit and cddb_edit
+ routines. Pointer copied over somehow? realloc wrong?
+ typical bug. Assignment to wrong place.
+
+ Some of th things left to do:
+ x 1. refresh server listing after update from internet
+ while dialog box is still open.
+ x 2. Status box showing the info while connected to the
+ server.
+ x 3. Implement Inexact match
+ x a. receive list of discids and titles from the server
+ Add a new command to cddbd return value
+ x b. Show a dialog box with the list of titles and have
+ the user pick one or Cancel.
+ x c. re-submit the query using the user selected discid
+ x 4. Saving to disk saves the wrong revision #, -1
+ o 5. Popup window when display is left clicked that shows
+ and allows editing of title and tracks.
+ showing title and track is working.
+ 6. Need to switch to using config.device and telling the
+ cd_control process about it when we change it.
+ 7. Submitting a new disc to the database (via email?)
+
+
+ 05/22/98 Need to finish the help/setup menu window.
+ x Device (defaults to /dev/cdrom)
+ x Server List
+ x cddb support
+ no changer support
+ x Eject at end?
+ x Startup behavior
+
+
+ Since I have the WM set to no titles and bars it makes it
+ hard to manipulate the setup screen! How about if I were
+ to extend the cdplayers window up or down (how to tell?)
+ and include the setup/server/tracklist data that way?
+ How do you delete things once you've packed them? Or can
+ you?
+
+
+ 05/21/98 Right click on display window and dragging now works
+ fine for moving the window around.
+
+
+ 05/20/98 Blink isn't working. Fixed. Multiple places calling
+ update_display. FIXED.
+ Also need to add window dragging if right clicked in
+ the display area.
+ Need to add parallel tracklist window popup if right
+ click on either of the track digits (or area).
+
+ At startup we need to know the current state of the
+ CD before we go off and send a play command to it. FIXED.
+ When it has to close the door to play it doesn't start
+ playing automatically.
+ When it tries to play with no CD in the drive it doesn't
+ recover when you insert a disc and hit play.
+ Because fd_cd is < 0. Check for this.
+
+ There is a problem with setting the play button graphic
+ and state at the point where it is sent to control.
+ it it fails then the state/button is wrong! But if I
+ wait until it succeeds/fails then response time is
+ greatly delayed... Added code to make sure the button
+ state matched the display state and to change it if it
+ differs. Seems to work nicely. Shows playup until it
+ fails and then switches to stopup. WORKS WELL.
+
+ Eject doesn't work with status doing an init. Do an
+ initial init when cd_control is started. FIXED.
+
+ Dragging now sortof works. It loses track of the cursor
+ if its moved too fast, and won't ungrab (ick!)
+
+ 05/19/98 Read/Write config now works, needs to still be tested
+ with multiple servers though.
+
+ 05/18/98 Adding configuration writing and reading
+
+ 05/17/98 Found a chunck of startup code that was double-copied,
+ The player actually plays, ejects, etc. OK.
+ The volume display needs to update immediatly as does
+ the time display update when the key is pressed. Eject
+ should eject even when no cd has been opened.
+ At startup we should?
+ Play automatically
+ Show no disc and do nothing until play pressed
+ Show total # of tracks and time remaining
+ All of these should be user selectable.
+ When a CD is opened for the first time (no discid stored
+ in cdinfo) we should query the database.
+ Clicking (right probably) on the display should pop up a
+ list of the tracks (editable).
+ There are some == NULL problems that need to be taken care
+ at starup, probably due to not realizing widgets first?
+ Blink isn't working right (doesn't blink)
+ Remaining on track counts up, not down!
+ Help/Setup dialog needs to be nicer looking, black to match
+ the look of the main box (how do I change the colors of
+ the dialogs?)
+
+ 05/16/98 Finishing up the cd control, adding get motd and get
+ server list from cddb.cddb.com
+ I really should add protocol support, but really, it
+ works fine like this...
+ OK, it 'looks like' the low level and server support is
+ now working okay. The code is a bit hard to read and
+ could probably use some macroing or other cleanups,
+ but I'll save that for later.
+
+ Now to start adding the GTK+ GUI stuff again and integrate
+ it with the data returning from the server and the CD
+ control process.
+
+ Well, the UI is sort of running. It complains a little,
+ and puts two 'NO DISC' pixmaps in the window, but it
+ starts at least!
+
+ 05/09/98 Added local cddb database support. It successfully reads
+ the disc info from the database.
+
+ 05/03/98 Restarted this whole project from the ground up. I am
+ writing the CD control and cddb support code first,
+ and then adding the GUI interface.
+
+ cd_control and cddb will be seperate processes forked
+ from the main process at startup.
+
+ -------------------------------------------------------------------- */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include "cd_control.h"
+#include "cddbd.h"
+#include "cddb.h"
+#include "xfreecd.h"
+#include "xpm_button.h"
+
+
+/* Include all the bitmaps used (in xpm format) */
+#include "bitmaps/ffup.xpm"
+#include "bitmaps/ffdn.xpm"
+#include "bitmaps/rewup.xpm"
+#include "bitmaps/rewdn.xpm"
+#include "bitmaps/sfwddn.xpm"
+#include "bitmaps/srevdn.xpm"
+#include "bitmaps/stopup.xpm"
+#include "bitmaps/playup.xpm"
+#include "bitmaps/playdn.xpm"
+#include "bitmaps/pauseup.xpm"
+#include "bitmaps/ejup.xpm"
+#include "bitmaps/ejdn.xpm"
+#include "bitmaps/exitup.xpm"
+#include "bitmaps/exitdn.xpm"
+#include "bitmaps/helpup.xpm"
+#include "bitmaps/helpdn.xpm"
+#include "bitmaps/rptup.xpm"
+#include "bitmaps/rptdn.xpm"
+#include "bitmaps/rptupact.xpm"
+#include "bitmaps/volup.xpm"
+#include "bitmaps/voldn.xpm"
+#include "bitmaps/null.xpm"
+#include "bitmaps/nodisc.xpm"
+#include "bitmaps/plstrkup.xpm"
+#include "bitmaps/plstrkdn.xpm"
+#include "bitmaps/mnstrkup.xpm"
+#include "bitmaps/mnstrkdn.xpm"
+#include "bitmaps/plscdup.xpm"
+#include "bitmaps/plscddn.xpm"
+#include "bitmaps/mnscdup.xpm"
+#include "bitmaps/mnscddn.xpm"
+#include "bitmaps/minus.xpm"
+#include "bitmaps/bar.xpm"
+#include "bitmaps/redbar.xpm"
+#include "bitmaps/nobar.xpm"
+#include "bitmaps/a0.xpm"
+#include "bitmaps/a1.xpm"
+#include "bitmaps/a2.xpm"
+#include "bitmaps/a3.xpm"
+#include "bitmaps/a4.xpm"
+#include "bitmaps/a5.xpm"
+#include "bitmaps/a6.xpm"
+#include "bitmaps/a7.xpm"
+#include "bitmaps/a8.xpm"
+#include "bitmaps/a9.xpm"
+#include "bitmaps/an.xpm"
+#include "bitmaps/b0.xpm"
+#include "bitmaps/b1.xpm"
+#include "bitmaps/b2.xpm"
+#include "bitmaps/b3.xpm"
+#include "bitmaps/b4.xpm"
+#include "bitmaps/b5.xpm"
+#include "bitmaps/b6.xpm"
+#include "bitmaps/b7.xpm"
+#include "bitmaps/b8.xpm"
+#include "bitmaps/b9.xpm"
+#include "bitmaps/bn.xpm"
+
+
+#undef DEBUG1
+#undef DEBUG2
+#undef DEBUG3
+#undef DEBUG4
+#undef DEBUG5
+#undef DEBUG6
+#undef DEBUG7
+#undef DEBUG8
+#undef DEBUG9
+#undef MAIL_DEBUG
+
+#define CDROM_NODISC 0
+#define CDROM_PLAYING 1
+#define CDROM_PAUSED 2
+
+#define TRACK_ELAPSED 0
+#define TRACK_REMAIN 1
+#define CD_ELAPSED 2
+#define CD_REMAIN 3
+
+
+#define HELP_TEXT( x ) gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, x, -1)
+#define COLOR_TEXT( x, y ) gtk_text_insert (GTK_TEXT (text), NULL, y, NULL, x, -1)
+
+int read_config( struct CONFIG * );
+int run_gtk();
+void free_config( struct CONFIG * );
+gint delete_event(GtkWidget *, gpointer);
+void destroy (GtkWidget *, gpointer);
+void cd_fd_read( gpointer, gint, GdkInputCondition);
+void cddbd_fd_read( gpointer, gint, GdkInputCondition);
+void ff_press(GtkWidget *, GdkEventButton *);
+void ff_release(GtkWidget *, GdkEventButton *);
+void rew_press(GtkWidget *, GdkEventButton *);
+void rew_release(GtkWidget *, GdkEventButton *);
+void play_press(GtkWidget *, GdkEventButton *);
+void play_release(GtkWidget *, GdkEventButton *);
+void eject_press(GtkWidget *, GdkEventButton *);
+void eject_release(GtkWidget *, GdkEventButton *);
+void ex_press(GtkWidget *, GdkEventButton *);
+void ex_release(GtkWidget *, GdkEventButton *);
+void help_press(GtkWidget *, GdkEventButton *);
+void help_release(GtkWidget *, GdkEventButton *);
+void tdisp_press(GtkWidget *, GdkEventButton *);
+void tdisp_release(GtkWidget *, GdkEventButton *);
+void rpt_press(GtkWidget *, GdkEventButton *);
+void rpt_release(GtkWidget *, GdkEventButton *);
+void vol_press(GtkWidget *, GdkEventButton *);
+void vol_release(GtkWidget *, GdkEventButton *);
+void set_vol( int amount );
+static int expose_display( GtkWidget *, GdkEventExpose *);
+gint update_cdrom( gpointer );
+void play_next();
+void play_previous();
+void pause_cdrom();
+void resume_cdrom();
+void play_track(int);
+void update_display();
+void stop_cdrom();
+void eject_cdrom();
+int cdrom_status();
+int write_config( struct CONFIG * );
+static void display_released (GtkWidget *);
+static void display_motion (GtkWidget *, GdkEventMotion *);
+static void display_pressed (GtkWidget *, GdkEventButton *);
+void wait_status();
+gint show_tracks();
+void set_eject( int );
+
+
+typedef struct _cursoroffset {gint x,y;} CursorOffset;
+
+/* Global Variables and structures */
+struct CDINFO cdinfo;
+struct CONFIG config;
+
+char cmnd[2];
+int cd_pid, cd_fd, /* Communications with the CD control */
+ cddbd_pid, cddbd_fd; /* Communications with cd database */
+gint gdk_cd_fd,
+ gdk_cddbd_fd;
+
+
+static GdkWindow *root_win = NULL;
+
+
+struct _pbutton ff, /* Fast Forward button */
+ rew, /* Rewind */
+ play, /* Play */
+ vol, /* Volume control */
+ eject, /* Elect */
+ tdisp, /* Time Display */
+ rpt, /* Repeat */
+ ex, /* Exit button */
+ help; /* Help */
+
+/* Display pixmaps and current disc status */
+struct _display
+{
+ GtkWidget *main_window; /* The main window */
+ GtkWidget *vbox; /* Vbox holding everything */
+ GtkWidget *wid; /* Widget for placing pixmaps */
+ GtkWidget *progress; /* CDDBD progress dialog window */
+ GtkWidget *plabel; /* Progress dialog label */
+ GtkWidget *twindow; /* Track Window pointer */
+ GtkWidget *tclist; /* List of tracks */
+ GtkWidget *ttitle; /* Track list title widget */
+ GtkWidget *tewindow; /* Track Edit Window */
+ GtkWidget *teentry; /* Track Entry Window */
+ GtkWidget *teclist; /* Clist for Track Edit */
+ GtkWidget *cb; /* ComboBox list of Servers */
+ GdkPixmap *image; /* image to paint onto */
+ GdkPixmap *null_pixmap; /* Blank with : */
+ GdkBitmap *null_mask;
+ GdkPixmap *nodisc_pixmap; /* NO DISC */
+ GdkBitmap *nodisc_mask;
+ GdkPixmap *minus_pixmap; /* Minus sign */
+ GdkBitmap *minus_mask;
+ GdkPixmap *playup_pixmap; /* Play in white */
+ GdkBitmap *playup_mask;
+ GdkPixmap *playdn_pixmap;
+ GdkBitmap *playdn_mask;
+ GdkPixmap *pauseup_pixmap; /* Pause bars in white */
+ GdkBitmap *pauseup_mask;
+ GdkPixmap *stopup_pixmap; /* play and pause in gray */
+ GdkBitmap *stopup_mask;
+ GdkPixmap *sfwddn_pixmap; /* Seek forward down button */
+ GdkBitmap *sfwddn_mask;
+ GdkPixmap *srevdn_pixmap; /* Seek reverse down button */
+ GdkBitmap *srevdn_mask;
+ GdkPixmap *rptupact_pixmap; /* repeat active (white) */
+ GdkBitmap *rptupact_mask;
+ GdkPixmap *plstrkup_pixmap; /* Elapsed track time */
+ GdkBitmap *plstrkup_mask;
+ GdkPixmap *plstrkdn_pixmap;
+ GdkBitmap *plstrkdn_mask;
+ GdkPixmap *mnstrkup_pixmap; /* Remaining track time */
+ GdkBitmap *mnstrkup_mask;
+ GdkPixmap *mnstrkdn_pixmap;
+ GdkBitmap *mnstrkdn_mask;
+ GdkPixmap *plscdup_pixmap; /* Elapsed CD time */
+ GdkBitmap *plscdup_mask;
+ GdkPixmap *plscddn_pixmap;
+ GdkBitmap *plscddn_mask;
+ GdkPixmap *mnscdup_pixmap; /* Remaining CD time */
+ GdkBitmap *mnscdup_mask;
+ GdkPixmap *mnscddn_pixmap;
+ GdkBitmap *mnscddn_mask;
+ GdkPixmap *bar_pixmap; /* Volume Bargraph */
+ GdkBitmap *bar_mask;
+ GdkPixmap *redbar_pixmap; /* Volume Bargraph */
+ GdkBitmap *redbar_mask;
+ GdkPixmap *nobar_pixmap; /* Volume Bargraph */
+ GdkBitmap *nobar_mask;
+
+ GdkPixmap *a_pixmap[11]; /* Large digits */
+ GdkBitmap *a_mask[11];
+ GdkPixmap *b_pixmap[11]; /* Small Digits */
+ GdkBitmap *b_mask[11];
+
+ GString *tmp_category; /* New Category entry */
+ GString *tmp_title; /* Title storage for edit_tracks*/
+ GString *tmp_track[99]; /* Song names for editing */
+ int tmp_row; /* Current row in tmp_track */
+
+ short startup; /* Startup State machine */
+ short cddb_lock; /* Lock cddb access while busy */
+ int status; /* Current player status
+ 0 = no disc in drive
+ 1 = stopped
+ 2 = playing
+ 3 = paused
+ */
+ int playbtn; /* State of play button, same as
+ above (use CDROM_ equates)
+ */
+ int time; /* Time mode */
+ int track, /* track # - 1 to end of CD */
+ minute,
+ second; /* Info for the display */
+ int repeat; /* Repeat status */
+} display;
+
+
+
+/* Signal handling code */
+static int caught_fatal_sig = 0;
+
+static void on_signal (int sig_num)
+{
+ if (caught_fatal_sig)
+/* raise (sig_num);*/
+ kill (getpid (), sig_num);
+ caught_fatal_sig = 1;
+
+ switch (sig_num)
+ {
+ case SIGHUP:
+ g_print("sighup caught\n");
+ gdk_exit(1);
+ break;
+ case SIGINT:
+ g_print("sigint caught\n");
+ gdk_exit(1);
+ break;
+ case SIGQUIT:
+ g_print("sigquit caught\n");
+ gdk_exit(1);
+ break;
+ case SIGABRT:
+ g_print("sigabrt caught\n");
+ gdk_exit(1);
+ break;
+ case SIGBUS:
+ g_print("sigbus caught\n");
+ destroy(NULL,NULL);
+ break;
+ case SIGSEGV:
+ g_print("sigsegv caught\n");
+ destroy(NULL,NULL);
+ break;
+ case SIGPIPE:
+ g_print("sigpipe caught\n");
+ gdk_exit(1);
+ break;
+ case SIGTERM:
+ g_print("sigterm caught\n");
+ gdk_exit(1);
+ break;
+ case SIGFPE:
+ g_print("sigfpe caught\n");
+ destroy(NULL,NULL);
+ break;
+ default:
+ g_print("unknown signal\n");
+ destroy(NULL,NULL);
+ break;
+ }
+}
+
+static void on_sig_child (int sig_num)
+{
+ int pid;
+ int status;
+
+ while (1)
+ {
+ pid = waitpid (WAIT_ANY, &status, WNOHANG);
+ if (pid <= 0)
+ break;
+ }
+}
+
+
+/* ------------------------------------------------------------------------
+ Where it all begins...
+ ------------------------------------------------------------------------ */
+int main( int argc, char *argv[] )
+{
+ int x,
+ x_ret,
+ y_ret,
+ w_ret,
+ h_ret,
+ stat=0;
+
+ bzero( &cdinfo, sizeof( struct CDINFO ) );
+
+ /* Start up the CD control process */
+ if( ( cd_fd = start_cd_control( &cd_pid ) ) < 0 )
+ {
+ perror("start_cd_control error");
+ exit(-1);
+ }
+
+ /* Start up the CDDBD control process */
+ if( ( cddbd_fd = start_cddbd( &cddbd_pid ) ) < 0 )
+ {
+ perror("start_cd_dbd error");
+ exit(-1);
+ }
+
+ /* Startup GTK */
+ gtk_init (&argc, &argv);
+
+ /* Tell gdk to watch the cd control and cddb pipes for reading */
+ gdk_cd_fd = gdk_input_add( cd_fd, GDK_INPUT_READ, cd_fd_read, 0 );
+ gdk_cddbd_fd = gdk_input_add( cddbd_fd, GDK_INPUT_READ, cddbd_fd_read, 0 );
+
+ /* Get the -geometry parameters */
+ for( x = 0; x < argc; x++ )
+ {
+ if( (strcasecmp( argv[x], "-geometry" ) == 0) || (strcasecmp( argv[x], "-geom" ) == 0) )
+ {
+ if( argv[x+1] != NULL )
+ {
+#ifdef DEBUG7
+ g_print("Passing %s to XParseGeometry\n", argv[x+1]);
+#endif
+
+ stat = XParseGeometry( argv[x+1], &x_ret, &y_ret, &w_ret, &h_ret );
+
+#ifdef DEBUG7
+ g_print("%d = XParseGeometry( s, %d, %d, %d, %d )\n",
+ stat, x_ret, y_ret, w_ret, h_ret );
+#endif
+ }
+ }
+ }
+
+ /* Handle some signals */
+ signal (SIGHUP, on_signal);
+ signal (SIGINT, on_signal);
+ signal (SIGQUIT, on_signal);
+ signal (SIGABRT, on_signal);
+ signal (SIGBUS, on_signal);
+ signal (SIGSEGV, on_signal);
+ signal (SIGPIPE, on_signal);
+ signal (SIGTERM, on_signal);
+ signal (SIGFPE, on_signal);
+
+ /* Handle child exits */
+ signal (SIGCHLD, on_sig_child);
+
+ run_gtk( stat, x_ret, y_ret );
+
+ gdk_input_remove( gdk_cd_fd );
+ gdk_input_remove( gdk_cddbd_fd );
+
+ /* Tell cd_control to quit */
+ cmnd[0] = CD_QUIT;
+ cmnd[1] = 0;
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ close( cd_fd );
+ exit(-1);
+ }
+
+ /* Tell cddbd to quit */
+ cdinfo.cddbd_cmnd = DB_QUIT;
+ if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO ) ) < 0 )
+ {
+ perror("write to cddbd error");
+ close( cd_fd );
+ close( cddbd_fd );
+ exit(-1);
+ }
+
+ close( cd_fd );
+ close( cddbd_fd );
+ return(0);
+}
+
+
+
+/* ----- subroutines follow ------ */
+
+
+void send_device()
+{
+ char buffer[80];
+
+ /* Send the CD device to the cd_control process */
+ buffer[0] = CD_SET_DEVICE;
+ buffer[1] = 0;
+ if( write( cd_fd, buffer, 2 ) < 0 )
+ {
+ perror("write to cd_control error - 1a");
+ }
+
+ sprintf( buffer, "%s\n", config.device );
+ if( write( cd_fd, buffer, strlen(buffer) ) < 0 )
+ {
+ perror("write to cd_control error - 1b");
+ }
+}
+
+
+/* ------------------------------------------------------------------------
+ Read the configuration file from $HOME/.xfreecdrc
+
+ Return -2 if a malloc fails
+ ------------------------------------------------------------------------ */
+int read_config( struct CONFIG *config )
+{
+ FILE *fp;
+ char fname[1024],
+ line[80],
+ *p;
+ struct SITE *sp, *sp2;
+
+ if( ( p = getenv( "HOME" ) ) == NULL )
+ {
+ g_print("Cannot find HOME enviornmental variable\n");
+ return(-1);
+ }
+
+ strncpy( fname, p, 1023 );
+ if( fname[strlen(fname)-1] != '/' )
+ strcat( fname, "/" );
+ strcat( fname, ".xfreecdrc" );
+
+ if( ( fp = fopen( fname, "rb" ) ) == NULL )
+ {
+ /* Setup reasonable defaults */
+
+ /* Setup for the default device (/dev/cdrom) */
+ if( ( config->device = (char *) malloc( strlen(DEFAULT_DEVICE)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( config->device, DEFAULT_DEVICE );
+
+ /* Get some memory for the path to the local database.
+ By default I put this in the user's home directory ~/.cddb
+ */
+ if( ( config->local_cddb = (char *) malloc( strlen(DEFAULT_CDDB_PATH)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( config->local_cddb, DEFAULT_CDDB_PATH );
+ strcpy( cdinfo.local_cddb, config->local_cddb );
+
+ /* Get some memory for the cddbd email address */
+ if( ( config->to_cddbd = (char *) malloc( strlen(DEFAULT_TO_CDDBD)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( config->to_cddbd, DEFAULT_TO_CDDBD );
+
+ /* Get some memory for the SITE */
+ if( ( config->server = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
+ {
+ return(-2);
+ }
+
+ bzero( config->server, sizeof(struct SITE) );
+
+ /* Fill in the site with cddb.cddb.com */
+ if( ( config->server->name = (char *) malloc( strlen(DEFAULT_SERVER)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( config->server->name, DEFAULT_SERVER );
+ config->server->port = 888;
+ config->server->next = NULL;
+
+ /* Set current server to the default */
+ if( ( config->current = (char *) malloc( strlen(DEFAULT_SERVER)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( config->current, DEFAULT_SERVER );
+
+ config->done_eject = 0; /* Eject when done playing */
+ config->exit_eject = 0; /* Eject on exit when not playing */
+ config->startup = 1; /* Play at startup */
+ config->cddb = 0;
+ config->changer = 0;
+ config->saved = 0; /* Needs to be saved */
+
+ /* Write this as the default */
+ return( write_config( config ) );
+ }
+
+ /* Process the lines in the file */
+ while( fgets( line, 80, fp ) != NULL )
+ {
+ if( (line[0] == '#') || (line[0] == '\n') )
+ continue;
+
+ p = strtok( line, "= \n" );
+ if( strcmp( p, "DEVICE" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ /* Get some memory for the device */
+ if( ( config->device = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ fclose( fp );
+ return(-2);
+ }
+ strcpy( config->device, p );
+
+ continue;
+ }
+
+ if( strcmp( p, "LOCAL_CDDB" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ /* Get some memory for the path to the local database.
+ */
+ if( ( config->local_cddb = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ fclose( fp );
+ return(-2);
+ }
+ strcpy( config->local_cddb, p );
+ strcpy( cdinfo.local_cddb, p );
+ continue;
+ }
+
+ if( strcmp( p, "TO_CDDBD" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ /* Get some memory for the path to the local database.
+ */
+ if( ( config->to_cddbd = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ fclose( fp );
+ return(-2);
+ }
+ strcpy( config->to_cddbd, p );
+ continue;
+ }
+
+ if( strcmp( p, "SERVER" ) == 0 )
+ {
+ p = strtok( NULL, "=: \n" );
+
+ /* Reserve some space for this server, add it to the end of the list */
+ if( ( sp = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
+ {
+ return(-2);
+ }
+ bzero( sp, sizeof(struct SITE) );
+
+ /* Reserve space for the name */
+ if( ( sp->name = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ return(-2);
+ }
+ strcpy( sp->name, p );
+
+ p = strtok( NULL, "=: \n" );
+ sp->port = atoi(p);
+ sp->next = NULL;
+
+ /* Is it the first in the list? */
+ if( config->server == NULL )
+ {
+ /* Yep, First in the list */
+ config->server = sp;
+ } else {
+ /* Walk to the end of the list (no cuts!!) */
+ sp2 = config->server;
+ while( sp2->next != NULL )
+ {
+ sp2 = sp2->next;
+ }
+
+ /* Add the new site to the end of the list */
+ sp2->next = sp;
+ }
+
+ continue;
+ }
+
+ if( strcmp( p, "CURRENT" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+
+ /* Get some memory for the current server name */
+ if( ( config->current = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ fclose( fp );
+ return(-2);
+ }
+ strcpy( config->current, p );
+ continue;
+ }
+
+ if( strcmp( p, "DONE_EJECT" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ config->done_eject = atoi( p );
+ continue;
+ }
+
+ if( strcmp( p, "EXIT_EJECT" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ config->exit_eject = atoi( p );
+ set_eject( config->exit_eject ); /* Tell the CD_CONTROL device about state */
+ continue;
+ }
+
+ if( strcmp( p, "STARTUP" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ config->startup = atoi( p );
+ continue;
+ }
+
+ if( strcmp( p, "CDDB" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ config->cddb = atoi( p );
+ continue;
+ }
+
+ if( strcmp( p, "CHANGER" ) == 0 )
+ {
+ p = strtok( NULL, "= \n" );
+ config->changer = atoi( p );
+ continue;
+ }
+
+
+ }
+ fclose( fp );
+
+
+ /*
+ I've added config->to_cddbd since last release, so if it wasn't read from the
+ rcfile give it a default value so people don't need to erase their old rcfile
+ */
+ if( config->to_cddbd == NULL )
+ {
+ if( ( config->to_cddbd = (char *) malloc( strlen( DEFAULT_TO_CDDBD ) + 1 ) ) == NULL )
+ {
+ fclose( fp );
+ return(-2);
+ }
+ strcpy( config->to_cddbd, DEFAULT_TO_CDDBD );
+ }
+
+ config->saved = 1; /* Just read it, same as saved file */
+
+ return 0;
+}
+
+
+/* ------------------------------------------------------------------------
+ Write the configuration file to $HOME/.xfreecdrc
+ ------------------------------------------------------------------------ */
+int write_config( struct CONFIG *config )
+{
+ FILE *fp;
+ char fname[1024],
+ *p;
+ struct SITE *sp;
+
+ /* Already saved, no need to save it again */
+ if( config->saved == 1 )
+ return(0);
+
+ if( ( p = getenv( "HOME" ) ) == NULL )
+ {
+ g_print("Cannot find HOME enviornmental variable\n");
+ return(-1);
+ }
+
+ strncpy( fname, p, 1023 );
+ if( fname[strlen(fname)-1] != '/' )
+ strcat( fname, "/" );
+ strcat( fname, ".xfreecdrc" );
+
+ if( ( fp = fopen( fname, "wb" ) ) == NULL )
+ {
+ return(-2);
+ }
+
+ fprintf( fp, "# XfreeCD v%s configuration file\n", VERSION );
+ fprintf( fp, "#\n");
+ fprintf( fp, "DEVICE=%s\n", config->device );
+ fprintf( fp, "LOCAL_CDDB=%s\n", config->local_cddb );
+ fprintf( fp, "TO_CDDBD=%s\n", config->to_cddbd );
+
+ /* Walk down the list of servers */
+ sp = config->server;
+ while( sp != NULL )
+ {
+ fprintf( fp, "SERVER=%s:%d\n", sp->name, sp->port);
+ sp = sp->next;
+ }
+
+ fprintf( fp, "CURRENT=%s\n", config->current );
+ fprintf( fp, "DONE_EJECT=%d\n", config->done_eject );
+ fprintf( fp, "EXIT_EJECT=%d\n", config->exit_eject );
+ fprintf( fp, "STARTUP=%d\n", config->startup );
+ fprintf( fp, "CDDB=%d\n", config->cddb );
+ fprintf( fp, "CHANGER=%d\n", config->changer );
+
+ fclose( fp );
+
+ config->saved = 1; /* config is now same as saved on disk */
+
+ return 0;
+}
+
+
+
+/* -----------------------------------------------------------------------
+ Free up the info used in the cdinfo structure
+ ----------------------------------------------------------------------- */
+void free_cdinfo()
+{
+ int x;
+
+#ifdef DEBUG8
+ g_print("Freeing any used extended data\n" );
+#endif
+
+ if( cdinfo.title != NULL )
+ {
+ g_string_free( cdinfo.title, 1 );
+ cdinfo.title = NULL;
+ }
+
+ if( cdinfo.category != NULL )
+ {
+ g_string_free( cdinfo.category, 1 );
+ cdinfo.category = NULL;
+ }
+
+ for( x = 0; x < 99; x++ )
+ {
+ if( cdinfo.name[x] != NULL )
+ {
+ g_string_free( cdinfo.name[x], 1 );
+ cdinfo.name[x] = NULL;
+ }
+ }
+
+
+ if( cdinfo.extd != NULL )
+ {
+ g_string_free( cdinfo.extd, 1 );
+ cdinfo.extd = NULL;
+ }
+
+ for( x = 0; x < 99; x++ )
+ {
+ if( cdinfo.extt[x] != NULL )
+ {
+ g_string_free( cdinfo.extt[x], 1 );
+ cdinfo.extt[x] = NULL;
+ }
+ }
+
+ /* Clean up the display's temporary storage */
+ if( display.tmp_category != NULL )
+ {
+ g_string_free( display.tmp_category, 1 );
+ display.tmp_category = NULL;
+ }
+
+ if( display.tmp_title != NULL )
+ {
+ g_string_free( display.tmp_title, 1 );
+ display.tmp_title = NULL;
+ }
+
+ for( x = 0; x < 99; x++ )
+ {
+ if( display.tmp_track[x] != NULL )
+ {
+ g_string_free( display.tmp_track[x], 1 );
+ display.tmp_track[x] = NULL;
+ }
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Free up any memory used by the config structure to store the paths
+ and site names.
+ ----------------------------------------------------------------------- */
+void free_config( struct CONFIG *config )
+{
+ struct SITE *sp;
+
+ /* Need to free up memory usage */
+ free( config->local_cddb );
+ free( config->device );
+ free( config->to_cddbd );
+
+ /* Walk the server list, freeing up memory */
+ while( config->server != NULL )
+ {
+ sp = config->server;
+ config->server = config->server->next;
+ free( sp->name );
+ free( sp );
+ }
+
+ /* Free up the dynamically allocated strings in cdinfo */
+ free_cdinfo();
+}
+
+
+
+/* -----------------------------------------------------------------------
+ Load the CD info from the local database or set defaults
+ ----------------------------------------------------------------------- */
+void local_cddb()
+{
+
+#ifdef DEBUG1
+ g_print("Loading local Database\n");
+#endif
+
+ /* No internet support, just try and read the local DB */
+ if( read_cddb( &cdinfo ) < 0 )
+ {
+
+#ifdef DEBUG1
+ g_print("Not found on disk, defaulting\n");
+#endif
+
+ /* Unknown title */
+ if( cdinfo.title == NULL )
+ cdinfo.title = g_string_new( "Unknown CD" );
+ else
+ cdinfo.title = g_string_assign( cdinfo.title, "Unknown CD" );
+
+ if( cdinfo.category == NULL )
+ cdinfo.category = g_string_new( "misc" );
+ else
+ cdinfo.category = g_string_assign( cdinfo.category, "misc" );
+
+ /* Set a default revision, so they can edit the list */
+ cdinfo.revision = 0;
+ }
+
+#ifdef DEBUG9
+ g_print("local_cddb() revision = %d\n", cdinfo.revision );
+#endif
+
+}
+
+
+/* -----------------------------------------------------------------------
+ The Main GTK setup routine
+ ----------------------------------------------------------------------- */
+int run_gtk( int stat, int x_ret, int y_ret )
+{
+ GtkWidget *window,
+ *button,
+ *hbox1,
+ *hbox2,
+ *table;
+ GtkTooltips *tips;
+ GtkStyle *style;
+ GdkColor tip_color;
+ GdkColormap *colormap;
+ gint timer;
+ CursorOffset *icon_pos;
+ int x_pos,
+ y_pos;
+
+ bzero( &config, sizeof( struct CONFIG ) );
+ bzero( &display, sizeof( struct _display ) );
+ bzero( &cdinfo, sizeof( struct CDINFO ) );
+
+ cdinfo.revision = -1; /* Initalize the revision number */
+
+ /* Copyright info, version info */
+ g_print("XfreeCD v%s\n", VERSION );
+ g_print("Copyright 1998 by Brian C. Lane\n<http://www.tatoosh.com/nexus>\n\n");
+
+ root_win = gdk_window_foreign_new (GDK_ROOT_WINDOW ());
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ display.main_window = window;
+ gtk_container_border_width( GTK_CONTAINER( window ), 0 );
+
+ /* Give it a name */
+ gtk_window_set_title (GTK_WINDOW (window), "XfreeCD");
+ gtk_window_set_wmclass(GTK_WINDOW(window), "XfreeCD", NULL );
+
+ /* when the window is given the "delete_event" signal (this is given
+ * by the window manager (usually the 'close' option, or on the
+ * titlebar), we ask it to call the delete_event () function
+ * as defined above. The data passed to the callback
+ * function is NULL and is ignored in the callback. */
+ gtk_signal_connect (GTK_OBJECT (window), "delete_event",
+ GTK_SIGNAL_FUNC (delete_event), NULL);
+
+ /* here we connect the "destroy" event to a signal handler.
+ * This event occurs when we call gtk_widget_destroy() on the window,
+ * or if we return 'FALSE' in the "delete_event" callback. */
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (destroy), NULL);
+
+ gtk_widget_realize(window);
+
+ /* Start up a new list of tool tips */
+ tips = gtk_tooltips_new();
+
+ /* Show the tooltips after the cursor has paused for 2 seconds */
+ gtk_tooltips_set_delay( tips, 2000 );
+
+ /*
+ * This section fills in all the data structures used by the xpm_button
+ * subroutine call to create buttons.
+ * Eventually this will be more automatic/cleaner/maybe a widget?
+ */
+
+ ff.up_xpm = (gchar *) ffup_xpm;
+ ff.dn_xpm = (gchar *) ffdn_xpm;
+ ff.press = &ff_press;
+ ff.release = &ff_release;
+
+ rew.up_xpm = (gchar *) rewup_xpm;
+ rew.dn_xpm = (gchar *) rewdn_xpm;
+ rew.press = &rew_press;
+ rew.release = &rew_release;
+
+ play.up_xpm = (gchar *) stopup_xpm;
+ play.dn_xpm = (gchar *) playdn_xpm;
+ play.press = &play_press;
+ play.release = &play_release;
+
+ eject.up_xpm = (gchar *) ejup_xpm;
+ eject.dn_xpm = (gchar *) ejdn_xpm;
+ eject.press = &eject_press;
+ eject.release = &eject_release;
+
+ ex.up_xpm = (gchar *) exitup_xpm;
+ ex.dn_xpm = (gchar *) exitdn_xpm;
+ ex.press = &ex_press;
+ ex.release = &ex_release;
+
+ help.up_xpm = (gchar *) helpup_xpm;
+ help.dn_xpm = (gchar *) helpdn_xpm;
+ help.press = &help_press;
+ help.release = &help_release;
+
+ tdisp.up_xpm = (gchar *) plstrkup_xpm;
+ tdisp.dn_xpm = (gchar *) plstrkdn_xpm;
+ tdisp.press = &tdisp_press;
+ tdisp.release = &tdisp_release;
+
+ rpt.up_xpm = (gchar *) rptup_xpm;
+ rpt.dn_xpm = (gchar *) rptdn_xpm;
+ rpt.press = &rpt_press;
+ rpt.release = &rpt_release;
+
+ vol.up_xpm = (gchar *) volup_xpm;
+ vol.dn_xpm = (gchar *) voldn_xpm;
+ vol.press = &vol_press;
+ vol.release = &vol_release;
+
+
+ /*
+ * The XfreeCD window consists of:
+ * one vertical box that contains:
+ * Two vertical boxes
+ * The top one with hbox of the display and right justified, a 3x2 table
+ * The bottom one with a hbox of 3 buttons
+ */
+
+ /* Make a vbox to hold our hboxes */
+ display.vbox = gtk_vbox_new( FALSE, 0 );
+ gtk_container_add( GTK_CONTAINER( window ), display.vbox );
+ gtk_widget_show( display.vbox );
+
+ /* Make a hbox to hold the bottom row of buttons REW/PLAY/FF */
+ hbox1 = gtk_hbox_new( FALSE, 0 );
+ gtk_box_pack_end( GTK_BOX( display.vbox ), hbox1, FALSE, FALSE, 0 );
+ gtk_widget_show( hbox1 );
+
+ /* Make a hbox to put the table into */
+ hbox2 = gtk_hbox_new( FALSE, 0 );
+ gtk_box_pack_start( GTK_BOX( display.vbox ), hbox2, FALSE, FALSE, 0 );
+ gtk_widget_show( hbox2 );
+
+ /* Make the xpm_buttons and add the tooltips for each one */
+ button = xpm_button( window, &rew );
+ gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Previous Track", "ContextHelp/buttons/Prev" );
+
+ button = xpm_button( window, &play );
+ gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Play/Pause", "ContextHelp/buttons/Play" );
+
+ button = xpm_button( window, &ff );
+ gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Next Track", "ContextHelp/buttons/Next" );
+
+ /* Make a table for the 6 control buttons */
+ table = gtk_table_new( 2, 3, TRUE );
+
+ /* Add the table to the end of the hbox (the display goes first) */
+ gtk_box_pack_end( GTK_BOX( hbox2 ), table, FALSE, FALSE, 0 );
+
+ /* Make sure it is showing */
+ gtk_widget_show( table );
+
+ button = xpm_button( window, &vol );
+ gtk_table_attach( GTK_TABLE( table ), button, 0, 1, 0, 1, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Volume", "ContextHelp/buttons/Volume" );
+
+ button = xpm_button( window, &tdisp );
+ gtk_table_attach( GTK_TABLE( table ), button, 1, 2, 0, 1, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Time Display", "ContextHelp/buttons/Time" );
+
+ button = xpm_button( window, &ex );
+ gtk_table_attach( GTK_TABLE( table ), button, 2, 3, 0, 1, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Exit", "ContextHelp/buttons/Exit" );
+
+ button = xpm_button( window, &rpt );
+ gtk_table_attach( GTK_TABLE( table ), button, 0, 1, 1, 2, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Repeat", "ContextHelp/buttons/Repeat" );
+
+ button = xpm_button( window, &eject );
+ gtk_table_attach( GTK_TABLE( table ), button, 1, 2, 1, 2, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Eject", "ContextHelp/buttons/Eject" );
+
+ button = xpm_button( window, &help );
+ gtk_table_attach( GTK_TABLE( table ), button, 2, 3, 1, 2, 0, 0, 0, 0 );
+ gtk_widget_show( button );
+ gtk_tooltips_set_tip( tips, button, "Help", "ContextHelp/buttons/Help" );
+
+
+ /* Get the window's style info */
+ style = gtk_widget_get_style( window );
+
+ /* Load all the other pixmaps (some are duplicates) */
+ display.null_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.null_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) null_xpm );
+
+ display.nodisc_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.nodisc_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) nodisc_xpm );
+
+ display.minus_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.minus_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) minus_xpm );
+
+ display.playup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.playup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) playup_xpm );
+
+ display.playdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.playdn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) playdn_xpm );
+
+ display.pauseup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.pauseup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) pauseup_xpm );
+
+ display.stopup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.stopup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) stopup_xpm );
+
+ display.sfwddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.sfwddn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) sfwddn_xpm );
+
+ display.srevdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.srevdn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) srevdn_xpm );
+
+ display.rptupact_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.rptupact_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) rptupact_xpm );
+
+ display.plstrkup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.plstrkup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) plstrkup_xpm );
+
+ display.plstrkdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.plstrkdn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) plstrkdn_xpm );
+
+ display.mnstrkup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.mnstrkup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) mnstrkup_xpm );
+
+ display.mnstrkdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.mnstrkdn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) mnstrkdn_xpm );
+
+ display.plscdup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.plscdup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) plscdup_xpm );
+
+ display.plscddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.plscddn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) plscddn_xpm );
+
+ display.mnscdup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.mnscdup_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) mnscdup_xpm );
+
+ display.mnscddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.mnscddn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) mnscddn_xpm );
+
+ display.bar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.bar_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) bar_xpm );
+
+ display.nobar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.nobar_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) nobar_xpm );
+
+ display.redbar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.redbar_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) redbar_xpm );
+
+ display.a_pixmap[0] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[0],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a0_xpm );
+
+ display.a_pixmap[1] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[1],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a1_xpm );
+
+ display.a_pixmap[2] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[2],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a2_xpm );
+
+ display.a_pixmap[3] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[3],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a3_xpm );
+
+ display.a_pixmap[4] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[4],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a4_xpm );
+
+ display.a_pixmap[5] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[5],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a5_xpm );
+
+ display.a_pixmap[6] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[6],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a6_xpm );
+
+ display.a_pixmap[7] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[7],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a7_xpm );
+
+ display.a_pixmap[8] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[8],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a8_xpm );
+
+ display.a_pixmap[9] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[9],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) a9_xpm );
+
+ display.a_pixmap[10] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.a_mask[10],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) an_xpm );
+
+
+ display.b_pixmap[0] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[0],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b0_xpm );
+
+ display.b_pixmap[1] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[1],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b1_xpm );
+
+ display.b_pixmap[2] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[2],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b2_xpm );
+
+ display.b_pixmap[3] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[3],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b3_xpm );
+
+ display.b_pixmap[4] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[4],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b4_xpm );
+
+ display.b_pixmap[5] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[5],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b5_xpm );
+
+ display.b_pixmap[6] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[6],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b6_xpm );
+
+ display.b_pixmap[7] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[7],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b7_xpm );
+
+ display.b_pixmap[8] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[8],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b8_xpm );
+
+ display.b_pixmap[9] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[9],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) b9_xpm );
+
+ display.b_pixmap[10] = gdk_pixmap_create_from_xpm_d( window->window,
+ &display.b_mask[10],
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) bn_xpm );
+
+ /*
+ * Create a drawing area for us to show the track and time
+ */
+ display.wid = gtk_drawing_area_new();
+ gtk_box_pack_start( GTK_BOX( hbox2 ), display.wid, FALSE, FALSE, 0 );
+ gtk_widget_show( display.wid );
+
+ /* Make sure it is redrawn when covered and uncovered by others */
+ gtk_signal_connect( GTK_OBJECT( display.wid ), "expose_event",
+ (GtkSignalFunc) expose_display, NULL );
+
+ gtk_drawing_area_size( GTK_DRAWING_AREA(display.wid), 92, 30 );
+ gtk_widget_set_events( display.wid, GDK_EXPOSURE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ GDK_BUTTON_PRESS_MASK);
+
+ /* gtk_widget_realize (display.wid);*/
+
+ gtk_signal_connect (GTK_OBJECT (display.wid), "button_press_event",
+ GTK_SIGNAL_FUNC (display_pressed),NULL);
+ gtk_signal_connect (GTK_OBJECT (display.wid), "button_release_event",
+ GTK_SIGNAL_FUNC (display_released),NULL);
+ gtk_signal_connect (GTK_OBJECT (display.wid), "motion_notify_event",
+ GTK_SIGNAL_FUNC (display_motion),NULL);
+
+ /* Data area to keep track of the cursor position for moving the window */
+ icon_pos = g_new (CursorOffset, 1);
+ gtk_object_set_user_data(GTK_OBJECT(display.wid), icon_pos);
+
+ x_pos = 0;
+ y_pos = 0;
+ /* Check the retun bits -- we want XValue & YValue */
+ if( stat & XValue )
+ {
+ /* Is the value negative (relative to right side?) */
+ if( stat & XNegative )
+ {
+ /* Make sure it returned a negative value */
+ if( x_ret < 0 )
+ x_pos = (gdk_screen_width()-137) + x_ret;
+ else
+ x_pos = (gdk_screen_width()-137) - x_ret;
+ } else {
+ x_pos = x_ret;
+ }
+ }
+
+ /* Did we get a Y position? */
+ if( stat & YValue )
+ {
+ /* Is the value negative (relative to bottom side?) */
+ if( stat & YNegative )
+ {
+ /* Make sure it returned a negative value */
+ if( y_ret < 0 )
+ y_pos = (gdk_screen_height()-60) + y_ret;
+ else
+ y_pos = (gdk_screen_height()-60) - y_ret;
+ } else {
+ y_pos = y_ret;
+ }
+ }
+
+#ifdef DEBUG7
+ g_print("Setting x=%d y=%d\n", x_pos, y_pos );
+#endif
+
+ /* Put the window where the user wants it (x_pos,y_pos) */
+ gtk_widget_set_uposition( window, x_pos, y_pos );
+
+ /* Setup the colors for the tooltip windows, postit yellow */
+ colormap = gdk_window_get_colormap (window->window);
+ tip_color.red = 61669;
+ tip_color.green = 59113;
+ tip_color.blue = 35979;
+ gdk_color_alloc (colormap, &tip_color);
+
+ /* Set the foreground/background of the tooltips */
+ gtk_tooltips_set_colors( tips, &tip_color, &window->style->fg[GTK_STATE_NORMAL] );
+
+ /* Showing the window last so everything pops up at once. */
+ gtk_widget_show (window);
+
+ /* Start the 1 second update timer */
+ timer = gtk_timeout_add( 1000, update_cdrom, NULL );
+
+ /*
+ Read some useful info from .xfreecdrc
+ Path to local database
+ server list
+ default server
+ */
+ if( read_config( &config ) < 0 )
+ {
+ g_print("read_config failed\n");
+ g_free( icon_pos );
+ exit(-1);
+ }
+
+ /* Send the device to the cd_control process */
+ send_device();
+
+ /* Tell cd_control about the exit on eject decision */
+ set_eject( config.exit_eject );
+
+ gtk_main ();
+
+ /* Turn off the 1 second timeout */
+ gtk_timeout_remove( timer );
+
+ g_free( icon_pos );
+
+ free_config( &config ); /* Free up config's memory usage */
+
+ return(0);
+}
+
+
+gint delete_event(GtkWidget *widget, gpointer data)
+{
+ /* if you return FALSE in the "delete_event" signal handler,
+ * GTK will emit the "destroy" signal. Returning TRUE means
+ * you don't want the window to be destroyed.
+ * This is useful for popping up 'are you sure you want to quit ?'
+ * type dialogs. */
+
+ /* Change TRUE to FALSE and the main window will be destroyed with
+ * a "delete_event". */
+
+ return (FALSE);
+}
+
+/* another callback */
+void destroy (GtkWidget *widget, gpointer data)
+{
+ gtk_main_quit ();
+}
+
+
+
+/* --------------------------------------------------------------
+ Copy the edited data over to cdinfo and write to local database
+ --------------------------------------------------------------- */
+void write_tracks()
+{
+ GString *old_category;
+ int x;
+ char *texts[3],
+ *p,
+ text1[255],
+ text2[255],
+ fname[1024],
+ tmp_fname[1024];
+
+
+ /* Sanity checks. We can't save or send if some of the important fields
+ are blank.
+ */
+ if( display.tmp_title == NULL )
+ return;
+
+ if( display.tmp_category == NULL )
+ return;
+
+ if( cdinfo.category == NULL )
+ return;
+
+ /* Save the old category so we can erase it if its changed */
+ old_category = g_string_new( cdinfo.category->str );
+
+ /* Copy the new title into the cdinfo structure */
+ cdinfo.title = g_string_assign( cdinfo.title, display.tmp_title->str );
+ g_string_free( display.tmp_title, 1 );
+ display.tmp_title = NULL;
+
+ /* Copy the track names if they exist and delete display storage */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ if( display.tmp_track[x] != NULL )
+ {
+ if( cdinfo.name[x] == NULL )
+ cdinfo.name[x] = g_string_new( display.tmp_track[x]->str );
+ else
+ cdinfo.name[x] = g_string_assign( cdinfo.name[x], display.tmp_track[x]->str );
+ g_string_free( display.tmp_track[x], 1 );
+ display.tmp_track[x] = NULL;
+ }
+ }
+
+ /* Copy the new categry name over */
+ cdinfo.category = g_string_assign( cdinfo.category, display.tmp_category->str );
+
+ /* Update the track window's info if its still open */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_freeze (GTK_CLIST (display.tclist));
+
+ /* Clear out the old list first */
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Set the new title */
+ if( cdinfo.title != NULL )
+ sprintf( text1, "%s", cdinfo.title->str );
+ else
+ strcpy( text1, "" );
+
+ gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
+
+ /* Add the track names */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( text1, "%d", x+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( cdinfo.name[x] != NULL )
+ texts[1] = cdinfo.name[x]->str;
+ else
+ texts[1] = "(blank)";
+
+ gtk_clist_append (GTK_CLIST (display.tclist), texts);
+ }
+ gtk_clist_thaw( GTK_CLIST( display.tclist ) );
+ gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
+ }
+
+ /* Save to database -- overwrite previous */
+ if( write_cddb( &cdinfo, 1 ) < 0 )
+ {
+ perror("write_cddb error");
+ } else {
+#ifdef DEBUG3
+ g_print("Wrote %08lx ok\n", cddb_discid( &cdinfo ) );
+#endif
+
+ /* The write was successful, we can erase the old category if it is different */
+ if( strcmp( cdinfo.category->str, old_category->str ) != 0 )
+ {
+ /* Build the path to the old entry */
+ /* Convert a leading ~ into the user's HOME directory */
+ if( cdinfo.local_cddb[0] == '~' )
+ {
+ /* Copy the reset of the path/filename to tmp_fname */
+ strncpy( tmp_fname, &cdinfo.local_cddb[1], 1023 );
+
+ if( ( p = (char *) getenv("HOME") ) == NULL )
+ {
+ return;
+ }
+ strncpy( fname, p, 1023 );
+
+ /* Make sure there is a slash inbetween the two */
+ if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
+ {
+ strcat( fname, "/" );
+ }
+
+ strncat( fname, tmp_fname, 1023-strlen( p ) );
+ } else {
+ strncpy( fname, cdinfo.local_cddb, 1023 );
+ }
+
+ if( fname[strlen(fname)-1] != '/')
+ strcat( fname, "/" );
+
+ /* Add the category name */
+ strcat( fname, old_category->str );
+ strcat( fname, "/" );
+ sprintf( tmp_fname, "%08lx", cdinfo.discid );
+ strcat( fname, tmp_fname );
+
+ /* Delete it */
+ unlink( fname );
+ }
+ }
+
+ g_string_free( old_category, 1 );
+}
+
+
+
+/* --------------------------------------------------------------
+ Send the cdinfo to the database
+
+ Increment the revision and save the new one to disk.
+ Use a system call to mail and cat
+ --------------------------------------------------------------- */
+void send_cddbd( GtkWidget *widget, GtkWidget *window)
+{
+ char *p,
+ tmp_fname[1024],
+ fname[1024],
+ buffer[1024];
+
+ /* Close the edit window */
+ gtk_widget_destroy( window );
+
+ /* Increment the revision number */
+ cdinfo.revision++;
+
+ /* Copy all the changes to the cdinfo structure */
+ write_tracks();
+
+ /* Make sure it has a title */
+ if( cdinfo.title != NULL )
+ {
+ /* Convert a leading ~ into the user's HOME directory */
+ if( cdinfo.local_cddb[0] == '~' )
+ {
+ /* Copy the reset of the path/filename to tmp_fname */
+ strncpy( tmp_fname, &cdinfo.local_cddb[1], 1023 );
+
+ if( ( p = (char *) getenv("HOME") ) == NULL )
+ {
+ return;
+ }
+ strncpy( fname, p, 1023 );
+
+ /* Make sure there is a slash inbetween the two */
+ if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
+ {
+ strcat( fname, "/" );
+ }
+
+ strncat( fname, tmp_fname, 1023-strlen( p ) );
+ } else {
+ strncpy( fname, cdinfo.local_cddb, 1023 );
+ }
+
+ if( fname[strlen(fname)-1] != '/')
+ strcat( fname, "/" );
+
+ /* Add the category name */
+ strcat( fname, cdinfo.category->str );
+ strcat( fname, "/" );
+ sprintf( tmp_fname, "%08lx", cdinfo.discid );
+ strcat( fname, tmp_fname );
+
+ sprintf( buffer, "cat %s | %s -s \"cddb %s %08lx\" %s", fname, MAIL_BINARY, cdinfo.category->str, cdinfo.discid, config.to_cddbd );
+
+#ifdef MAIL_DEBUG
+ g_print( "%s\n", buffer );
+#else
+ /* Make a system call */
+ system( buffer );
+#endif
+ }
+}
+
+
+/* --------------------------------------------------------------
+ Save the new cd data to the local cd database
+ --------------------------------------------------------------- */
+void save_tracks( GtkWidget *widget, GtkWidget *window)
+{
+ gtk_widget_destroy( window );
+
+ /* Update cdinfo and write the new data to the local database */
+ write_tracks();
+}
+
+
+/* ----------------------------------------------------------------------
+ The track entry data has changed. Copy it to a temporary location...
+ ---------------------------------------------------------------------- */
+void track_entry(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+ char *texts[3],
+ text1[255],
+ text2[255];
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ if( display.tmp_track[display.tmp_row] == NULL )
+ display.tmp_track[display.tmp_row] = g_string_new( entry_text );
+ else
+ display.tmp_track[display.tmp_row] = g_string_assign( display.tmp_track[display.tmp_row], entry_text );
+
+ /* Update the clist in the edit window */
+ if( display.tewindow != NULL )
+ {
+ gtk_clist_freeze (GTK_CLIST (display.teclist));
+
+ /* Remove the old entry first */
+ gtk_clist_remove( GTK_CLIST( display.teclist ), display.tmp_row );
+
+ /* Insert the new data */
+ sprintf( text1, "%d", display.tmp_row+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[display.tmp_row].length/60,cdinfo.track[display.tmp_row].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( display.tmp_track[display.tmp_row] != NULL )
+ texts[1] = display.tmp_track[display.tmp_row]->str;
+ else
+ texts[1] = "(blank)";
+ gtk_clist_insert (GTK_CLIST (display.teclist), display.tmp_row, texts);
+
+ /* Select the correct row in the clist */
+ gtk_clist_select_row( GTK_CLIST(display.teclist), display.tmp_row, -1 );
+
+ gtk_clist_thaw( GTK_CLIST( display.teclist ) );
+
+ }
+}
+
+
+/* -------------------------------------------------------------------
+ The Title entry data has changed, copy it to a temporary location
+ ------------------------------------------------------------------- */
+void title_entry(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ if( display.tmp_title == NULL )
+ display.tmp_title = g_string_new( entry_text );
+ else
+ display.tmp_title = g_string_assign( display.tmp_title, entry_text );
+}
+
+
+/* ----------------------------------------------------------------------
+ The user selected a track, copy its data into the edit widget
+ (display.teedit)
+ ---------------------------------------------------------------------- */
+void select_teclist (GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton * bevent)
+{
+
+#ifdef DEBUG9
+ g_print ("Selection: Track #%d by button %d\n", row, bevent ? bevent->button : 0 );
+#endif
+
+ if( bevent )
+ {
+ if( bevent->button == 1 )
+ {
+ display.tmp_row = row;
+ /* Copy the track name into the edit widget */
+ if( display.tmp_track[display.tmp_row] != NULL )
+ gtk_entry_set_text( GTK_ENTRY( display.teentry ), display.tmp_track[display.tmp_row]->str);
+ else
+ gtk_entry_set_text( GTK_ENTRY( display.teentry ), "(blank)" );
+ }
+ }
+}
+
+
+
+
+/* --------------------------------------------------------------------
+ If the user select a track in the tracklist window, play that
+ track now.
+ -------------------------------------------------------------------- */
+void select_tclist (GtkWidget *widget,
+ gint row,
+ gint column,
+ GdkEventButton * bevent)
+{
+
+#ifdef DEBUG9
+ g_print ("Selection: Track #%d by button %d\n", row, bevent ? bevent->button : 0 );
+#endif
+
+ if( bevent )
+ {
+ if( bevent->button == 1 )
+ {
+ play_track( row+1 );
+ }
+ }
+}
+
+
+
+
+/* -----------------------------------------------------------------------
+ Handle the button press events
+
+ Each button has a xx_press and xx_release event where they change their
+ pixmaps to show a up or down state.
+
+ The FF, REW, Eject button also have to update the state of the play
+ button since they can effect the state that the player is in. I'm sure
+ there is a better way to do this...
+ ----------------------------------------------------------------------- */
+
+
+/* -----------------------------------------------------------------------
+ Copy the selected item into the category
+ ----------------------------------------------------------------------- */
+static void menuitem_response (gchar *string)
+{
+ if( display.tmp_category == NULL )
+ display.tmp_category = g_string_new( string );
+ else
+ display.tmp_category = g_string_assign( display.tmp_category, string );
+}
+
+
+/* -----------------------------------------------------------------------
+ Start up the window for editing the track list.
+ Close the normal track list while doing this.
+ Save it to cddb format when close is clicked.
+ Don't save when Cancel is clicked.
+ ----------------------------------------------------------------------- */
+static void edit_tracks(GtkWidget *widget, GtkWidget *data)
+{
+ GtkWidget *vbox1,
+ *hbox1,
+ *button,
+ *edit,
+ *optionmenu,
+ *menu,
+ *menuitem;
+ int x,
+ curr_cat;
+ char *texts[3],
+ text1[255],
+ text2[255];
+ static char *titles[] =
+ {
+ "Track #",
+ "Title",
+ "Length"
+ };
+ static char *categories[] =
+ {
+ "blues",
+ "classical",
+ "country",
+ "data",
+ "folk",
+ "jazz",
+ "misc",
+ "newage",
+ "reggae",
+ "rock",
+ "soundtrack"
+ };
+
+
+ /* Don't allow edit if there's no disk */
+ if( display.status == CDROM_NODISC )
+ return;
+
+ if (!display.tewindow)
+ {
+ display.tewindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize (display.tewindow, 300, 400);
+
+ gtk_signal_connect (GTK_OBJECT (display.tewindow), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &display.tewindow);
+
+ gtk_window_set_title (GTK_WINDOW (display.tewindow), "Edit Track Info" );
+ gtk_container_border_width (GTK_CONTAINER (display.tewindow), 4);
+ gtk_window_set_wmclass(GTK_WINDOW(display.tewindow), "XfreeCDet", NULL );
+
+ /* create a vbox to hold the clist and buttons */
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (display.tewindow), vbox1);
+ gtk_widget_show (vbox1);
+
+ /* Make a hbox to hold discid and category dropdown */
+ hbox1 = gtk_hbox_new (FALSE, 0);
+ gtk_box_pack_start( GTK_BOX( vbox1 ), hbox1, FALSE, FALSE, 2 );
+ gtk_widget_show (hbox1);
+
+ /* Need to show the disc id and category */
+ /* Maybe category should be a drop-down listing of the standard categories? */
+ sprintf( text1, "Discid: 0x%08lx Category : ", cdinfo.discid );
+ button = gtk_label_new( text1 );
+ gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 2 );
+ gtk_widget_show( button );
+
+ /* Optionmenu for selecting the category */
+ optionmenu = gtk_option_menu_new();
+ menu = gtk_menu_new();
+
+ curr_cat = 0;
+ /* Add the categories */
+ for( x = 0; x < 11; x++ )
+ {
+ /* Set the current category */
+ if( cdinfo.category != NULL )
+ {
+ if( strcmp( cdinfo.category->str, categories[x] ) == 0 )
+ {
+ curr_cat = x;
+ if( display.tmp_category == NULL )
+ display.tmp_category = g_string_new( cdinfo.category->str );
+ else
+ display.tmp_category = g_string_assign( display.tmp_category, cdinfo.category->str );
+ }
+ } else {
+ display.tmp_category= g_string_new( categories[0] );
+ }
+ menuitem = gtk_menu_item_new_with_label( categories[x] );
+ gtk_menu_append (GTK_MENU (menu), menuitem);
+ gtk_signal_connect_object( GTK_OBJECT(menuitem), "activate",
+ GTK_SIGNAL_FUNC(menuitem_response), (gpointer) categories[x] );
+ gtk_widget_show (menuitem);
+ }
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
+ gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), curr_cat );
+ gtk_box_pack_start (GTK_BOX (hbox1), optionmenu, FALSE, FALSE, 0);
+ gtk_widget_show (optionmenu);
+
+
+
+ /* Entry Widget for the title of the CD */
+ edit = gtk_entry_new_with_max_length( 254 );
+ gtk_signal_connect(GTK_OBJECT(edit), "changed",
+ GTK_SIGNAL_FUNC(title_entry),
+ edit);
+
+ if( cdinfo.title != NULL )
+ gtk_entry_set_text( GTK_ENTRY( edit ), cdinfo.title->str );
+ gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
+ gtk_box_pack_start( GTK_BOX( vbox1 ), edit, FALSE, FALSE, 2 );
+ gtk_widget_show( edit );
+
+ /* Initalize the title */
+ if( cdinfo.title != NULL )
+ display.tmp_title = g_string_new( cdinfo.title->str );
+
+ /* Entry Widget for the track name-changed by selecting a diff. track */
+ display.teentry = gtk_entry_new_with_max_length( 254 );
+ gtk_box_pack_start( GTK_BOX( vbox1 ), display.teentry, FALSE, FALSE, 2 );
+ gtk_widget_show( display.teentry );
+
+ /* Create the clist */
+ display.teclist = gtk_clist_new_with_titles (3, titles);
+ gtk_clist_set_row_height (GTK_CLIST (display.teclist), 20);
+ gtk_clist_set_column_width (GTK_CLIST (display.teclist), 0, 45);
+ gtk_clist_set_column_width (GTK_CLIST (display.teclist), 1, 150);
+ gtk_clist_set_column_width (GTK_CLIST (display.teclist), 2, 45);
+ gtk_signal_connect (GTK_OBJECT (display.teclist),
+ "select_row",
+ (GtkSignalFunc) select_teclist,
+ NULL);
+#ifdef GOOBER
+ gtk_clist_set_selection_mode (GTK_CLIST (display.teclist), GTK_SELECTION_BROWSE);
+#endif
+ gtk_clist_set_policy (GTK_CLIST (display.teclist),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 0, GTK_JUSTIFY_CENTER);
+ gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 1, GTK_JUSTIFY_LEFT);
+ gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 2, GTK_JUSTIFY_LEFT);
+
+ gtk_clist_freeze (GTK_CLIST (display.teclist));
+
+ display.tmp_row = 0;
+ /* Add the track names */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( text1, "%d", x+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( cdinfo.name[x] != NULL )
+ texts[1] = cdinfo.name[x]->str;
+ else
+ texts[1] = "(blank)";
+ gtk_clist_append (GTK_CLIST (display.teclist), texts);
+
+ /* Initalize the temporary storage */
+ if( cdinfo.name[x] != NULL )
+ display.tmp_track[x] = g_string_new( cdinfo.name[x]->str );
+ else
+ display.tmp_track[x] = NULL;
+ }
+ gtk_clist_thaw( GTK_CLIST( display.teclist ) );
+
+ gtk_container_border_width (GTK_CONTAINER (display.teclist), 5);
+ gtk_box_pack_start (GTK_BOX (vbox1), display.teclist, TRUE, TRUE, 0);
+ gtk_widget_show (display.teclist);
+
+ /* Now we can update the track edit control, since the tclist is setup */
+ gtk_signal_connect(GTK_OBJECT(display.teentry), "changed",
+ GTK_SIGNAL_FUNC(track_entry),
+ display.teentry);
+ if( display.tmp_track[0] != NULL )
+ gtk_entry_set_text( GTK_ENTRY( display.teentry ), display.tmp_track[0]->str );
+ gtk_editable_select_region( GTK_EDITABLE( display.teentry ), 0, -1 );
+
+ button = gtk_button_new_with_label ("Send To Server");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(send_cddbd),
+ GTK_OBJECT (display.tewindow));
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Save");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(save_tracks),
+ GTK_OBJECT (display.tewindow));
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.tewindow));
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
+ gtk_widget_show (button);
+
+ gtk_widget_show( display.tewindow );
+ }
+}
+
+
+
+/*
+ Handle button press in the display window.
+ Determine if its a click for a track list or a drag to move the window
+*/
+static void display_pressed (GtkWidget *widget, GdkEventButton *event)
+{
+ CursorOffset *p;
+ GtkWidget *vbox1,
+ *button;
+ int x;
+ char *texts[3],
+ text1[255],
+ text2[255];
+ static char *titles[] =
+ {
+ "Track #",
+ "Title",
+ "Length"
+ };
+
+
+ /* ignore double and triple click */
+ if (event->type != GDK_BUTTON_PRESS)
+ return;
+
+ /* Right button is move window */
+ if( event->button == 3 )
+ {
+ if( ( p = gtk_object_get_user_data (GTK_OBJECT(widget)) ) != NULL )
+ {
+ p->x = (int) event->x;
+ p->y = (int) event->y;
+
+ gtk_grab_add (widget);
+ gdk_pointer_grab (widget->window, TRUE,
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_BUTTON_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK,
+ NULL, NULL, 0);
+ }
+ } else if( event->button == 1 ) {
+ /* Show the title and the tracks, using a clist */
+ if (!display.twindow)
+ {
+ display.twindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_usize (display.twindow, 300, 350);
+
+ gtk_signal_connect (GTK_OBJECT (display.twindow), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &display.twindow);
+
+ /* gtk_window_set_title (GTK_WINDOW (display.twindow), cdinfo.title); */
+ gtk_container_border_width (GTK_CONTAINER (display.twindow), 4);
+ gtk_window_set_wmclass(GTK_WINDOW(display.twindow), "XfreeCDt", NULL );
+
+ /* create a vbox to hold the clist and buttons */
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (display.twindow), vbox1);
+ gtk_widget_show (vbox1);
+
+ if( cdinfo.title != NULL )
+ sprintf( text1, "%s", cdinfo.title->str );
+ else
+ strcpy( text1, "" );
+ display.ttitle = gtk_label_new( text1 );
+ gtk_box_pack_start( GTK_BOX( vbox1 ), display.ttitle, FALSE, FALSE, 0 );
+ gtk_widget_show( display.ttitle );
+
+ /* Create the clist */
+ display.tclist = gtk_clist_new_with_titles (3, titles);
+ gtk_clist_set_row_height (GTK_CLIST (display.tclist), 20);
+ gtk_clist_set_column_width (GTK_CLIST (display.tclist), 0, 45);
+ gtk_clist_set_column_width (GTK_CLIST (display.tclist), 1, 150);
+ gtk_clist_set_column_width (GTK_CLIST (display.tclist), 2, 45);
+ gtk_signal_connect (GTK_OBJECT (display.tclist),
+ "select_row",
+ (GtkSignalFunc) select_tclist,
+ NULL);
+
+#ifdef GOOBER
+ gtk_clist_set_selection_mode (GTK_CLIST (display.tclist), GTK_SELECTION_BROWSE);
+#endif
+ gtk_clist_set_policy (GTK_CLIST (display.tclist),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 0, GTK_JUSTIFY_CENTER);
+ gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 1, GTK_JUSTIFY_LEFT);
+ gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 2, GTK_JUSTIFY_LEFT);
+
+ if( display.status != CDROM_NODISC )
+ {
+ gtk_clist_freeze (GTK_CLIST (display.tclist));
+ /* Add the track names */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( text1, "%d", x+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( cdinfo.name[x] != NULL )
+ texts[1] = cdinfo.name[x]->str;
+ else
+ texts[1] = "(blank)";
+ gtk_clist_append (GTK_CLIST (display.tclist), texts);
+ }
+ gtk_clist_thaw( GTK_CLIST( display.tclist ) );
+ }
+
+ gtk_container_border_width (GTK_CONTAINER (display.tclist), 5);
+ gtk_box_pack_start (GTK_BOX (vbox1), display.tclist, TRUE, TRUE, 0);
+ gtk_widget_show (display.tclist);
+
+ button = gtk_button_new_with_label ("Edit Info");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(edit_tracks),
+ GTK_OBJECT (display.twindow));
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
+ gtk_widget_show (button);
+
+ button = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.twindow));
+
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
+ /* GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); */
+ /* gtk_widget_grab_default (button); */
+ gtk_widget_show (button);
+ gtk_widget_show( display.twindow );
+ }
+ }
+}
+
+
+static void display_released (GtkWidget *widget)
+{
+ gtk_grab_remove (widget);
+ gdk_pointer_ungrab (0);
+}
+
+
+static void display_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+ gint xp, yp;
+ CursorOffset * p;
+ GdkModifierType mask;
+
+ if( ( p = gtk_object_get_user_data (GTK_OBJECT (widget)) ) != NULL )
+ {
+ /*
+ * Can't use event->x / event->y here
+ * because I need absolute coordinates.
+ */
+ gdk_window_get_pointer (root_win, &xp, &yp, &mask);
+ gtk_widget_set_uposition (display.main_window, xp - p->x, yp - p->y);
+ }
+}
+
+
+
+
+void ff_press (GtkWidget *widget, GdkEventButton *event)
+{
+ /* Left button pressed */
+ if( event->button == 1 )
+ {
+ /* Set the FF down image */
+ gtk_pixmap_set( GTK_PIXMAP( ff.wid ),
+ ff.image.dn_pixmap,
+ ff.image.dn_mask );
+
+ /* Tell the CD control to play the next track */
+ play_next();
+
+
+ /* Update the state of the play button -- Should be a subroutine */
+ if( (display.playbtn != CDROM_PLAYING) && (display.status != CDROM_NODISC))
+ {
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playup_pixmap,
+ display.playup_mask );
+ display.playbtn = CDROM_PLAYING;
+ }
+ }
+}
+
+
+void ff_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( (event->button == 1) )
+ {
+ /* Draw the button up image */
+ gtk_pixmap_set( GTK_PIXMAP( ff.wid ),
+ ff.image.up_pixmap,
+ ff.image.up_mask );
+ }
+}
+
+
+void rew_press (GtkWidget *widget, GdkEventButton *event)
+{
+ /* Left button pressed */
+ if( event->button == 1 )
+ {
+ /* Draw the down image for REW */
+ gtk_pixmap_set( GTK_PIXMAP( rew.wid ),
+ rew.image.dn_pixmap,
+ rew.image.dn_mask );
+
+ /* Tell the CD to play the previous track */
+ play_previous();
+
+
+ /* Update the state of the Play button -- should be a subroutine */
+ if( (display.playbtn != CDROM_PLAYING) && (display.status != CDROM_NODISC))
+ {
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playup_pixmap,
+ display.playup_mask );
+ display.playbtn = CDROM_PLAYING;
+ }
+
+ }
+}
+
+
+void rew_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( (event->button == 1) )
+ {
+ /* Draw the up image for the button */
+ gtk_pixmap_set( GTK_PIXMAP( rew.wid ),
+ rew.image.up_pixmap,
+ rew.image.up_mask );
+ }
+}
+
+
+void play_press (GtkWidget *widget, GdkEventButton *event)
+{
+ int x;
+
+ /* Left button press */
+ if( event->button == 1 )
+ {
+ /* Depending on the current state of the button, display the correct image */
+ switch( display.playbtn )
+ {
+ case CDROM_PLAYING: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playdn_pixmap,
+ display.playdn_mask );
+ pause_cdrom();
+ display.playbtn = CDROM_PAUSED;
+ break;
+
+ case CDROM_PAUSED: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playdn_pixmap,
+ display.playdn_mask );
+ resume_cdrom();
+ display.playbtn = CDROM_PLAYING;
+ break;
+
+ case CDROM_NODISC: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playdn_pixmap,
+ display.playdn_mask );
+
+ play_track( 1 );
+ update_display();
+ display.playbtn = CDROM_PLAYING;
+ break;
+ }
+ } else if( event->button ==3 ) {
+ /* Stop playing without ejecting the CD */
+ switch( display.playbtn )
+ {
+
+ case CDROM_PLAYING :
+ case CDROM_PAUSED :
+ stop_cdrom();
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playdn_pixmap,
+ display.playdn_mask );
+ update_display();
+ display.playbtn = CDROM_NODISC;
+
+ /* Clear out the track list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Clear the title */
+ gtk_label_set( GTK_LABEL( display.ttitle ), "" );
+ }
+
+ /* Erase the title and track names */
+ if( cdinfo.title != NULL )
+ {
+ g_string_free( cdinfo.title, 1 );
+ cdinfo.title = NULL;
+ }
+
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ if( cdinfo.name[x] != NULL )
+ {
+ g_string_free( cdinfo.name[x], 1 );
+ cdinfo.name[x] = NULL;
+ }
+ }
+
+ /* Free up dynamically allocated cdinfo strings */
+ free_cdinfo();
+ break;
+
+ case CDROM_NODISC :
+ break;
+ }
+ }
+}
+
+
+void play_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( (event->button == 1) || (event->button ==3) )
+ {
+ switch( display.playbtn )
+ {
+ case CDROM_PAUSED: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.pauseup_pixmap,
+ display.pauseup_mask );
+ break;
+
+ case CDROM_PLAYING: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playup_pixmap,
+ display.playup_mask );
+ break;
+
+ case CDROM_NODISC: gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.stopup_pixmap,
+ display.stopup_mask );
+ break;
+
+ }
+ }
+}
+
+
+void eject_press (GtkWidget *widget, GdkEventButton *event)
+{
+ int x;
+
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( eject.wid ),
+ eject.image.dn_pixmap,
+ eject.image.dn_mask );
+
+
+ /*
+ * If we are playing or paused then we will be stopped, so change
+ * the play button to the stop button
+ */
+ switch( display.status )
+ {
+ case CDROM_PAUSED :
+ case CDROM_PLAYING :
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.stopup_pixmap,
+ display.stopup_mask );
+ display.playbtn = CDROM_NODISC;
+ break;
+ }
+ eject_cdrom();
+ display.playbtn = CDROM_NODISC;
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.nodisc_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+
+ /* Clear out the track list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Clear the title */
+ gtk_label_set( GTK_LABEL( display.ttitle ), "" );
+ }
+
+
+ /* Erase the title and track names */
+ if( cdinfo.title != NULL )
+ {
+ g_string_free( cdinfo.title, 1 );
+ cdinfo.title = NULL;
+ }
+
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ if( cdinfo.name[x] != NULL )
+ {
+ g_string_free( cdinfo.name[x], 1 );
+ cdinfo.name[x] = NULL;
+ }
+ }
+
+ /* Free up the dynamically allocated cdinfo strings */
+ free_cdinfo();
+ }
+}
+
+
+void eject_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( eject.wid ),
+ eject.image.up_pixmap,
+ eject.image.up_mask );
+ }
+}
+
+
+gint ex_timer( gpointer data )
+{
+ gtk_timeout_remove( ex.timer );
+
+ gtk_main_quit();
+
+ return FALSE;
+}
+
+
+void ex_press (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( ex.wid ),
+ ex.image.dn_pixmap,
+ ex.image.dn_mask );
+
+ /* In 100mS execute the play_next function */
+ ex.timer = gtk_timeout_add( 500, ex_timer, NULL );
+
+ }
+}
+
+
+void ex_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( ex.wid ),
+ ex.image.up_pixmap,
+ ex.image.up_mask );
+ }
+}
+
+
+void
+destroy_window (GtkWidget *widget,
+ GtkWidget **window)
+{
+ *window = NULL;
+}
+
+
+void close_setup( GtkWidget *widget, GtkWidget *window)
+{
+ gtk_widget_destroy( window );
+
+ /* Write configuration */
+ write_config( &config );
+
+ /* Send the new device to the cd_control process */
+ send_device();
+}
+
+
+/* Toggle the state of the cddb button */
+static void toggle_cddb (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ config.cddb = GTK_TOGGLE_BUTTON(checkbutton)->active;
+
+#ifdef DEBUG3
+ g_print("Toggling cddb = %d\n", config.cddb );
+#endif
+
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+
+#ifdef CHANGER_SUPPORT
+/* Toggle the CD changer support button */
+static void toggle_changer (GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ config.changer = GTK_TOGGLE_BUTTON(checkbutton)->active;
+
+#ifdef DEBUG3
+ g_print("Toggling changer = %d\n", config.changer );
+#endif
+
+ config.saved = 0; /* Needs to be written to disk */
+}
+#endif
+
+
+/* Toggle the Eject when done playing */
+static void toggle_eject_done(GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ config.done_eject = GTK_TOGGLE_BUTTON(checkbutton)->active;
+
+#ifdef DEBUG3
+ g_print("Toggling done_eject = %d\n", config.done_eject );
+#endif
+
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+
+/* Toggle the Eject at Close */
+static void toggle_eject_exit(GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ config.exit_eject = GTK_TOGGLE_BUTTON(checkbutton)->active;
+
+#ifdef DEBUG3
+ g_print("Toggling exit_eject = %d\n", config.exit_eject );
+#endif
+
+ set_eject( config.exit_eject );
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+static void toggle_startup(GtkWidget *checkbutton,
+ GtkWidget *text)
+{
+ config.startup = GTK_TOGGLE_BUTTON(checkbutton)->active;
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+
+void cddb_servers()
+{
+
+ /* Placeholder for server listing/editing */
+}
+
+
+static void
+page_switch (GtkWidget *widget, GtkNotebookPage *page, gint page_num)
+{
+ GtkNotebookPage *oldpage;
+
+ oldpage = GTK_NOTEBOOK (widget)->cur_page;
+
+ if (page == oldpage)
+ return;
+
+}
+
+
+void device_entry(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ config.device = realloc( config.device, strlen(entry_text)+1);
+ strcpy( config.device, entry_text );
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+void cddb_entry(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ config.local_cddb = realloc( config.local_cddb, strlen(entry_text)+1);
+ strcpy( config.local_cddb, entry_text );
+ strcpy( cdinfo.local_cddb, entry_text );
+
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+void cddb_to_email(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ config.to_cddbd = realloc( config.to_cddbd, strlen(entry_text)+1);
+ strcpy( config.to_cddbd, entry_text );
+
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+
+void server_entry(GtkWidget *widget, GtkWidget *entry)
+{
+ gchar *entry_text;
+
+ entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
+
+ config.current = realloc( config.current, strlen(entry_text)+1);
+ strcpy( config.current, entry_text );
+ config.saved = 0; /* Needs to be written to disk */
+}
+
+
+void refresh_servers()
+{
+ char buffer[80];
+
+ /* Need to lock this routine until we complete the last request */
+ if( display.cddb_lock == 0 )
+ {
+ /* Get the server list from cddb.cddb.com - DB_SITES */
+ /* Tell it to print the diagnostic string */
+ cdinfo.cddbd_cmnd = DB_SITES;
+
+ /* Set the server to connect to */
+ strncpy( cdinfo.server, "cddb.cddb.com", 80 );
+ cdinfo.port = 888;
+ if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO ) ) < 0 )
+ {
+ perror("write to cddbd error");
+ }
+
+ display.cddb_lock = 1;
+
+ /* Start up a progress dialog */
+ display.progress = gtk_dialog_new ();
+
+ sprintf( buffer, "Connecting to %s:%d", cdinfo.server, cdinfo.port );
+
+ gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ &display.progress);
+
+ gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
+ gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
+ gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
+
+ display.plabel = gtk_label_new (buffer);
+ gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
+ display.plabel, FALSE, FALSE, 0);
+ gtk_widget_show( display.plabel );
+ gtk_widget_show( display.progress );
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Display the help/setup dialog box
+
+ All changed info is saved when this dialog is exited.
+ ----------------------------------------------------------------------- */
+gint help_timer( gpointer data )
+{
+ struct SITE *sp;
+ static GtkWidget *window = NULL;
+ GtkWidget *notebook,
+ *text,
+ *vbox1,
+ *vbox2,
+ *label,
+ *button,
+ *table,
+ *hscrollbar,
+ *vscrollbar,
+ *edit;
+ GList *cbitems = NULL;
+
+
+ gtk_timeout_remove( help.timer );
+ help.timer = 0;
+
+ /* Create the main window for the notebook */
+ if( !window )
+ {
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &window);
+
+ gtk_window_set_title (GTK_WINDOW (window), "notebook");
+ gtk_container_border_width (GTK_CONTAINER (window), 0);
+ gtk_window_set_title (GTK_WINDOW( window ), "XfreeCD Setup");
+ gtk_widget_set_usize (window, 250, 300);
+ gtk_window_set_wmclass(GTK_WINDOW(window), "XfreeCDs", NULL );
+
+ /* Create a vbox to hold the notebook */
+ vbox1 = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (window), vbox1);
+
+ /* Create the notebook */
+ notebook = gtk_notebook_new ();
+ gtk_signal_connect (GTK_OBJECT (notebook), "switch_page",
+ GTK_SIGNAL_FUNC (page_switch), NULL);
+ gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
+ gtk_box_pack_start (GTK_BOX (vbox1), notebook, TRUE, TRUE, 0);
+ gtk_container_border_width (GTK_CONTAINER (notebook), 10);
+ gtk_widget_realize (notebook);
+
+ /* Create the about page for the notebook -- Put a text window in it */
+ vbox2 = gtk_vbox_new( FALSE, 0 );
+ gtk_widget_show( vbox2 );
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
+ gtk_box_pack_start (GTK_BOX (vbox2), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ text = gtk_text_new (NULL, NULL);
+ gtk_text_set_editable (GTK_TEXT (text), FALSE);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (text);
+
+ hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
+ gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
+ GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
+ gtk_widget_show (hscrollbar);
+
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+ /* Create the label box for the About page */
+ label = gtk_label_new ("About");
+
+ /* Add it to the notebook */
+ gtk_notebook_append_page_menu (GTK_NOTEBOOK(notebook), vbox2, label, label );
+
+ gtk_text_freeze (GTK_TEXT (text));
+
+ gtk_widget_realize (text);
+
+ /* Add the actual text */
+ HELP_TEXT( "XfreeCD v0.7.8\n" );
+ HELP_TEXT( "Copyright 1998 Brian C. Lane\n" );
+ HELP_TEXT( "<nexus@tatoosh.com>\n" );
+ HELP_TEXT( "http://www.tatoosh.com/nexus\n\n" );
+
+ HELP_TEXT( "Click on the 'SETUP' tab to set the\n" );
+ HELP_TEXT( "CD device and some options:\n\n" );
+ HELP_TEXT( "AutoPlay will start playing the CD\n" );
+ HELP_TEXT( "when XfreeCD is started\n\n" );
+ HELP_TEXT( "Eject when done will eject the CD\n" );
+ HELP_TEXT( "when it is finished playing if repeat\n" );
+ HELP_TEXT( "is not turned on (the button on the\n" );
+ HELP_TEXT( "main panel).\n\n" );
+ HELP_TEXT( "Eject on Exit will eject the CD if it\n");
+ HELP_TEXT( "is not playing when you exit.\n\n" );
+ HELP_TEXT( "Click on the 'CDDB' tab to set up\n" );
+ HELP_TEXT( "the support for online cd database\n" );
+ HELP_TEXT( "support. The Local CDDB Path\n" );
+ HELP_TEXT( "should point to a directory where\n" );
+ HELP_TEXT( "you have read and write\n");
+ HELP_TEXT( "permission. This is where the\n" );
+ HELP_TEXT( "track lists for the CDs will be\n");
+ HELP_TEXT( "saved.\n\n");
+ HELP_TEXT( "The CDDB Submit email field is for\n" );
+ HELP_TEXT( "the email address of the CDDB\n");
+ HELP_TEXT( "server to submit CD information\n" );
+ HELP_TEXT( "to. The send button is located\n" );
+ HELP_TEXT( "in the Track Edit window. Please\n" );
+ HELP_TEXT( "only submit CDs that do not exist\n");
+ HELP_TEXT( "in the database and that have\n" );
+ HELP_TEXT( "complete title and track text\n" );
+ HELP_TEXT( "entered.\n\n" );
+ HELP_TEXT( "The CDDB server is the internet\n" );
+ HELP_TEXT( "site where requests for unknown\n" );
+ HELP_TEXT( "CDs will be sent to. Initially only\n" );
+ HELP_TEXT( "cddb.cddb.com is listed. Click\n");
+ HELP_TEXT( "Refresh Server List to download a\n");
+ HELP_TEXT( "list of the available servers.\n" );
+ HELP_TEXT( "Use the combobox to pick a server\n" );
+ HELP_TEXT( "close to you. If you unselect cddb\n" );
+ HELP_TEXT( "support the internet server will\n" );
+ HELP_TEXT( "not be queried, but the local\n" );
+ HELP_TEXT( "database will be used.\n\n" );
+ HELP_TEXT( "Right click & drag on the main\n");
+ HELP_TEXT( "display window will allow you to\n" );
+ HELP_TEXT( "move XfreeCD even when you\n");
+ HELP_TEXT( "have title bars and handles. Left\n");
+ HELP_TEXT( "Clicking on the display will open\n");
+ HELP_TEXT( "up the Track List window.\n\n" );
+ HELP_TEXT( "In the Track List window you can\n");
+ HELP_TEXT( "click on the title of a track and\n");
+ HELP_TEXT( "XfreeCD will jump directly to\n" );
+ HELP_TEXT( "that track and play it.\n\n" );
+ HELP_TEXT( "Right clicking on the play/pause\n");
+ HELP_TEXT( "button will stop playing and\n");
+ HELP_TEXT( "turn off the CD player.\n\n");
+ HELP_TEXT( "Volume is controlled by left/right\n");
+ HELP_TEXT( "clicking on the volume button.\n\n");
+ HELP_TEXT( "Thanks to Ti Kan and Steve Scherf\n" );
+ HELP_TEXT( "for creating CDDB.\n\n" );
+
+ gtk_text_thaw (GTK_TEXT (text));
+
+ /* Add the general setup page */
+ vbox2 = gtk_vbox_new( FALSE, 0 );
+ gtk_widget_show( vbox2 );
+
+ /* Add an editable field for the device */
+ label = gtk_label_new ("CDROM device:");
+ gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ edit = gtk_entry_new_with_max_length(20);
+ gtk_signal_connect(GTK_OBJECT(edit), "changed",
+ GTK_SIGNAL_FUNC(device_entry),
+ edit);
+ gtk_widget_show( edit );
+
+#ifdef DEBUG3
+ g_print("config.device = %s\n", config.device );
+#endif
+
+ if( config.device != NULL )
+ gtk_entry_set_text( GTK_ENTRY( edit ), config.device );
+ gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
+ gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
+ gtk_widget_show( edit );
+
+#ifdef DEBUG3
+ g_print("config.startup = %d\n", config.startup );
+#endif
+
+ /* Create a check button for Playing at Startup */
+ button = gtk_check_button_new_with_label("AutoPlay");
+ gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.startup == 1));
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( toggle_startup ), NULL );
+ gtk_widget_show( button );
+
+#ifdef DEBUG3
+ g_print("config.changer = %d\n", config.changer );
+#endif
+
+#ifdef CHANGER_SUPPORT
+ /* Create a check button for CD Changer */
+ button = gtk_check_button_new_with_label("CD Changer");
+ gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.changer == 1));
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( toggle_changer ), NULL );
+ gtk_widget_show( button );
+#endif
+
+#ifdef DEBUG3
+ g_print("config.done_eject = %d\n", config.done_eject );
+#endif
+
+ /* Create a check button for Eject on Close */
+ button = gtk_check_button_new_with_label("Eject when done");
+ gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.done_eject==1));
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( toggle_eject_done ), NULL );
+ gtk_widget_show( button );
+
+ /* Create a check button for Eject on Exit (and not playing) */
+ button = gtk_check_button_new_with_label("Eject on Exit");
+ gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.exit_eject==1));
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( toggle_eject_exit ), NULL );
+ gtk_widget_show( button );
+
+
+ /* Create the label box for the Setup page */
+ label = gtk_label_new ("Setup");
+
+ /* Add it to the notebook */
+ gtk_notebook_append_page_menu( GTK_NOTEBOOK( notebook ), vbox2, label, label );
+
+
+ /* Create the CDDB page for the notebook */
+ vbox2 = gtk_vbox_new( FALSE, 0 );
+ gtk_widget_show( vbox2 );
+
+ /* Path to the local cddb database */
+ label = gtk_label_new( "Local CDDB Path" );
+ gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Add an editable field for the local CDDBD database */
+ edit = gtk_entry_new_with_max_length( 1023 );
+ gtk_signal_connect(GTK_OBJECT(edit), "changed",
+ GTK_SIGNAL_FUNC(cddb_entry),
+ edit);
+ gtk_widget_show( edit );
+
+#ifdef DEBUG3
+ g_print("config.local_cddb = %s\n", config.local_cddb );
+#endif
+
+ if( config.local_cddb != NULL )
+ gtk_entry_set_text( GTK_ENTRY( edit ), config.local_cddb );
+ gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
+ gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
+ gtk_widget_show( edit );
+
+ /* Email address for submitting to the database */
+ label = gtk_label_new( "CDDB Submit Email" );
+ gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Add an editable field for the email address */
+ edit = gtk_entry_new_with_max_length( 1023 );
+ gtk_signal_connect(GTK_OBJECT(edit), "changed",
+ GTK_SIGNAL_FUNC(cddb_to_email),
+ edit);
+ gtk_widget_show( edit );
+
+#ifdef DEBUG3
+ g_print("config.local_cddb = %s\n", config.local_cddb );
+#endif
+
+ if( config.to_cddbd != NULL )
+ gtk_entry_set_text( GTK_ENTRY( edit ), config.to_cddbd );
+ gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
+ gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
+ gtk_widget_show( edit );
+
+
+#ifdef DEBUG3
+ g_print("config.cddb = %d\n", config.cddb );
+#endif
+
+ /* Create a check button for CDDB support */
+ button = gtk_check_button_new_with_label("CDDB Support");
+ gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.cddb == 1));
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( toggle_cddb ), NULL );
+ gtk_widget_show( button );
+
+
+ /* Create the list of servers, use a combobox for this */
+ label = gtk_label_new( "CDDB Server" );
+ gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
+ gtk_widget_show (label);
+
+ /* Walk the list */
+ cbitems = NULL;
+ sp = config.server;
+ while( sp != NULL )
+ {
+ cbitems = g_list_append(cbitems, sp->name);
+ sp = sp->next;
+ }
+
+ display.cb = gtk_combo_new ();
+ gtk_combo_set_popdown_strings (GTK_COMBO (display.cb), cbitems);
+
+ /* Here we should put the selected server into the entry box */
+ gtk_entry_set_text (GTK_ENTRY (GTK_COMBO(display.cb)->entry), config.current);
+
+ gtk_signal_connect(GTK_OBJECT(GTK_COMBO(display.cb)->entry), "changed",
+ GTK_SIGNAL_FUNC(server_entry),
+ GTK_COMBO(display.cb)->entry);
+ gtk_editable_select_region (GTK_EDITABLE (GTK_COMBO(display.cb)->entry),
+ 0, -1);
+ gtk_box_pack_start (GTK_BOX (vbox2), display.cb, FALSE, FALSE, 0);
+ gtk_widget_show (display.cb);
+
+
+ /* Add a button to refresh the list with */
+ button = gtk_button_new_with_label("Refresh Server List");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (refresh_servers),
+ GTK_OBJECT( window ));
+ gtk_box_pack_end (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+
+ /* Create the label box for the CDDB page */
+ label = gtk_label_new ("CDDB");
+
+ /* Add it to the notebook */
+ gtk_notebook_append_page_menu( GTK_NOTEBOOK( notebook ), vbox2, label, label );
+
+ /* Add the close button */
+ button = gtk_button_new_with_label ("close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (close_setup),
+ GTK_OBJECT( window ));
+ gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 0);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (button);
+ }
+
+ if (!GTK_WIDGET_VISIBLE (window))
+ gtk_widget_show_all (window);
+ else
+ gtk_widget_destroy( window );
+
+ return FALSE;
+}
+
+
+#ifdef GOOBER1
+void old_stuff()
+{
+
+ /* A useful frame with 3 radio buttons in it */
+
+ /* Create a frame with the Startup options as radio buttons */
+ frame = gtk_frame_new("Startup Action");
+ gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0);
+ gtk_widget_show(frame);
+
+ /* Add the general setup page */
+ vbox3 = gtk_vbox_new( FALSE, 0 );
+ gtk_container_add(GTK_CONTAINER(frame), vbox3);
+ gtk_container_border_width(GTK_CONTAINER(vbox3), 0);
+ gtk_widget_show( vbox3 );
+
+ /* create radio button for Play at startup */
+ button = gtk_radio_button_new_with_label( NULL, "Play CD");
+ if( config.startup == STARTUP_PLAY )
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
+ else
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( play_setup ), NULL );
+ gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_radio_button_new_with_label(
+ gtk_radio_button_group (GTK_RADIO_BUTTON (button)), "Show Tracks");
+ if( config.startup == STARTUP_TRACKS )
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
+ else
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( tracks_setup ), NULL );
+ gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+ button = gtk_radio_button_new_with_label(
+ gtk_radio_button_group (GTK_RADIO_BUTTON (button)), "No Disc");
+ if( config.startup == STARTUP_NODISC )
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
+ else
+ gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
+ gtk_signal_connect( GTK_OBJECT( button ), "toggled",
+ GTK_SIGNAL_FUNC( nothing_setup ), NULL );
+ gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
+ gtk_widget_show(button);
+
+
+
+
+
+
+
+
+
+ GtkWidget *hbox;
+ GtkWidget *button;
+ GtkWidget *check;
+ GtkWidget *separator;
+ GtkWidget *table;
+ GtkWidget *hscrollbar;
+ GtkWidget *vscrollbar;
+ GtkWidget *text;
+
+ gtk_container_border_width (GTK_CONTAINER (box2), 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
+ gtk_widget_show (box2);
+
+
+ table = gtk_table_new (2, 2, FALSE);
+ gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
+ gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
+ gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
+ gtk_widget_show (table);
+
+ text = gtk_text_new (NULL, NULL);
+ gtk_text_set_editable (GTK_TEXT (text), TRUE);
+ gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL,
+ GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (text);
+
+ /* hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
+ * gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
+ * GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
+ * gtk_widget_show (hscrollbar);
+ */
+
+ vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
+ gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
+ GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
+ gtk_widget_show (vscrollbar);
+
+
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "This program will play CDs using an attached ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "IDE or SoundBlaster CD player. It currently ", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "does not work with SCSI drives.\n\n", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "The latest version is available from my webpage.", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "Please email me with any suggestions, problems,", -1);
+ gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
+ "or comments you may have\n\n", -1);
+
+ hbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0);
+ gtk_widget_show (hbox);
+
+ /* check = gtk_check_button_new_with_label("Editable");
+ * gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
+ * gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ * GTK_SIGNAL_FUNC(text_toggle_editable), text);
+ * gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
+ * gtk_widget_show (check);
+ */
+
+ /* check = gtk_check_button_new_with_label("Wrap Words");
+ * gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
+ * gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ * GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
+ * gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
+ * gtk_widget_show (check);
+ */
+
+ /* Checkbox for www.cddb.com support */
+ check = gtk_check_button_new_with_label("www.cddb.com");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(about_toggle_cddb), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
+ gtk_widget_show (check);
+
+ /* Checkbox for CD Changer support */
+ check = gtk_check_button_new_with_label("CD changer");
+ gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
+ gtk_signal_connect (GTK_OBJECT(check), "toggled",
+ GTK_SIGNAL_FUNC(about_toggle_cddb), text);
+ gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
+ gtk_widget_show (check);
+
+ gtk_text_set_word_wrap(GTK_TEXT(text), TRUE );
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
+ gtk_widget_show (separator);
+
+
+ box2 = gtk_vbox_new (FALSE, 10);
+ gtk_container_border_width (GTK_CONTAINER (box2), 10);
+ gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
+ gtk_widget_show (box2);
+
+}
+#endif
+
+
+void help_press (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( help.wid ),
+ help.image.dn_pixmap,
+ help.image.dn_mask );
+
+ /* In XXXmS execute the help dialog window function */
+ help.timer = gtk_timeout_add( 500, help_timer, NULL );
+ }
+}
+
+
+void help_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( help.wid ),
+ help.image.up_pixmap,
+ help.image.up_mask );
+ }
+}
+
+
+void tdisp_press (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ /* Cycle through the time display options */
+ switch( display.time )
+ {
+ case TRACK_ELAPSED : display.time = TRACK_REMAIN;
+ gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.plstrkdn_pixmap,
+ display.plstrkdn_mask );
+ break;
+
+ case TRACK_REMAIN : display.time = CD_ELAPSED;
+ gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.mnstrkdn_pixmap,
+ display.mnstrkdn_mask );
+ break;
+
+ case CD_ELAPSED : display.time = CD_REMAIN;
+ gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.plscddn_pixmap,
+ display.plscddn_mask );
+ break;
+
+ case CD_REMAIN : display.time = TRACK_ELAPSED;
+ gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.mnscddn_pixmap,
+ display.mnscddn_mask );
+ break;
+ }
+ }
+}
+
+
+void tdisp_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ switch( display.time )
+ {
+ case TRACK_ELAPSED : gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.plstrkup_pixmap,
+ display.plstrkup_mask );
+ break;
+
+ case TRACK_REMAIN : gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.mnstrkup_pixmap,
+ display.mnstrkup_mask );
+ break;
+
+ case CD_ELAPSED : gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.plscdup_pixmap,
+ display.plscdup_mask );
+ break;
+
+ case CD_REMAIN : gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
+ display.mnscdup_pixmap,
+ display.mnscdup_mask );
+ break;
+ }
+ }
+}
+
+
+void rpt_press (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
+ rpt.image.dn_pixmap,
+ rpt.image.dn_mask );
+
+ if( display.repeat == 0 )
+ display.repeat = 1;
+ else
+ display.repeat = 0;
+ }
+}
+
+
+void rpt_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( event->button == 1 )
+ {
+ if( display.repeat == 0 )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
+ rpt.image.up_pixmap,
+ rpt.image.up_mask );
+ } else {
+ gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
+ display.rptupact_pixmap,
+ display.rptupact_mask );
+ }
+ }
+}
+
+
+void vol_press (GtkWidget *widget, GdkEventButton *event)
+{
+ switch( event->button )
+ {
+ case 1 : gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
+ vol.image.dn_pixmap,
+ vol.image.dn_mask );
+ set_vol( +10 );
+ update_display();
+ break;
+
+ case 3 : gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
+ vol.image.dn_pixmap,
+ vol.image.dn_mask );
+ set_vol( -10 );
+ update_display();
+ break;
+
+ }
+}
+
+void vol_release (GtkWidget *widget, GdkEventButton *event)
+{
+ if( (event->button == 1) || (event->button == 3) )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
+ vol.image.up_pixmap,
+ vol.image.up_mask );
+ }
+}
+
+/* -----------------------------------------------------------------------------------------
+ Show the track title listbox, allow editing of it. Hilight the current track. Show
+ total time for each track. Show discid for the CD. Allow sending to CDDB.
+ ----------------------------------------------------------------------------------------- */
+gint show_tracks( )
+{
+
+ return FALSE;
+}
+
+
+/* -----------------------------------------------------------------------
+ Redraw the uncovered part of the display
+ ----------------------------------------------------------------------- */
+static int expose_display( GtkWidget *widget, GdkEventExpose *event )
+{
+ switch( display.status )
+ {
+ case CDROM_PLAYING : gdk_draw_pixmap (widget->window,
+ widget->style->black_gc,
+ display.null_pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ break;
+
+ case CDROM_NODISC : gdk_draw_pixmap (widget->window,
+ widget->style->black_gc,
+ display.nodisc_pixmap,
+ event->area.x, event->area.y,
+ event->area.x, event->area.y,
+ event->area.width, event->area.height);
+ break;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+/* -----------------------------------------------------------------------
+ Update the cdrom status and the display
+
+ This routine is called once per second to check for the end of the CD
+ and to update the display.
+ ----------------------------------------------------------------------- */
+gint update_cdrom( gpointer data )
+{
+ /* Get the latest info into cdinfo structure */
+ cdrom_status();
+
+ return TRUE;
+}
+
+
+/* -----------------------------------------------------------------------
+ Draw the current track # onto the null_pixmap and then copy the
+ pixmap to the display
+ ----------------------------------------------------------------------- */
+void draw_track()
+{
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.a_pixmap[display.track/10],
+ 0, 0,
+ 2, 2, 11, 22 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.a_pixmap[display.track%10],
+ 0, 0,
+ 15, 2, 11, 22 );
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+}
+
+
+/* -----------------------------------------------------------------------
+ Draw the minute digits onto the null_pixmap and then copy the
+ pixmap to the display. Also draws the minus sign if needed.
+ ----------------------------------------------------------------------- */
+void draw_minute()
+{
+ /* Show or don't show the minus sign */
+ if((display.minute<0) || (display.second<0))
+ {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.minus_pixmap,
+ 0, 0,
+ 32, 10, 8, 16 );
+ } else {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 32, 10, 8, 16 );
+ }
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[abs(display.minute)/10],
+ 0, 0,
+ 42, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[abs(display.minute)%10],
+ 0, 0,
+ 52, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+}
+
+
+/* -----------------------------------------------------------------------
+ Draw the second digits onto the null_pixmap and then copy the
+ pixmap to the display.
+ ----------------------------------------------------------------------- */
+void draw_second()
+{
+ /* Show or don't show the minus sign */
+ if((display.minute<0) || (display.second<0))
+ {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.minus_pixmap,
+ 0, 0,
+ 32, 10, 8, 16 );
+ } else {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 32, 10, 8, 16 );
+ }
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[abs(display.second/10)],
+ 0, 0,
+ 68, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[abs(display.second%10)],
+ 0, 0,
+ 78, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+}
+
+
+/* -----------------------------------------------------------------------
+ Erase the minute digits and the minus sign. This is used by the blink
+ routine.
+ ----------------------------------------------------------------------- */
+void erase_minute()
+{
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 32, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 42, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 52, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+}
+
+
+/* -----------------------------------------------------------------------
+ Erase the second digits. This is used by the blink routine.
+ ----------------------------------------------------------------------- */
+void erase_second()
+{
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 68, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.b_pixmap[10],
+ 0, 0,
+ 78, 10, 8, 16 );
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+}
+
+
+/* -----------------------------------------------------------------------
+ Decide what to show on the display
+ If there is no disc, then show NO DISC bitmap
+ If playing then update the display digits
+ If paused then blink the time
+
+ It keeps track of the previous states of things so only the areas that
+ have changed get updated each time. An improvement would be to only
+ update the digits that change (instead of both minute or both second
+ digits), but it isn't that important.
+ ----------------------------------------------------------------------- */
+void update_display()
+{
+ static int curr_track=-1, /* Default to unknown value */
+ curr_minute=-1,
+ curr_second=-1,
+ curr_status=-1,
+ curr_volume=-1,
+ blinking=0,
+ tclist_track=-1; /* Track selected in tclist */
+
+ int x;
+
+ /*
+ * If the volume needs updating then we draw a bargraph, one bar for
+ * every 17 volume units. 15 bars maximum with the last 5 being red
+ */
+ if( curr_volume != cdinfo.volume )
+ {
+ for( x = 0; x < (cdinfo.volume / 17); x++ )
+ {
+ if( x < 10 )
+ {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.bar_pixmap,
+ 0, 0,
+ 30+(x*4), 1, 4, 7 );
+ } else {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.redbar_pixmap,
+ 0, 0,
+ 30+(x*4), 1, 4, 7 );
+ }
+ }
+ for( ; x < 15; x++ )
+ {
+ gdk_draw_pixmap( display.null_pixmap,
+ display.wid->style->black_gc,
+ display.nobar_pixmap,
+ 0, 0,
+ 30+(x*4), 1, 4, 7 );
+ }
+
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.null_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+
+ curr_volume = cdinfo.volume;
+ }
+
+ switch( display.time )
+ {
+ case TRACK_ELAPSED :
+ display.minute = cdinfo.sc.cdsc_reladdr.msf.minute;
+ display.second = cdinfo.sc.cdsc_reladdr.msf.second;
+ break;
+
+ case TRACK_REMAIN :
+ display.minute = 0 - ( (cdinfo.track[display.track-1].length/60) - cdinfo.sc.cdsc_reladdr.msf.minute);
+ display.second = 0 - ( (cdinfo.track[display.track-1].length%60) - cdinfo.sc.cdsc_reladdr.msf.second);
+ break;
+
+ case CD_ELAPSED :
+ display.minute = cdinfo.sc.cdsc_absaddr.msf.minute;
+ display.second = cdinfo.sc.cdsc_absaddr.msf.second;
+ break;
+
+ case CD_REMAIN :
+ display.minute = 0-cdinfo.cd_remaining/60;
+ display.second = 0-cdinfo.cd_remaining%60;
+ break;
+ }
+
+
+ switch( display.status )
+ {
+ case CDROM_PLAYING : if( display.track != curr_track )
+ {
+ draw_track();
+
+ curr_track = display.track;
+ }
+
+ if( display.minute != curr_minute )
+ {
+ draw_minute();
+
+ curr_minute = display.minute;
+ }
+
+ if( display.second != curr_second )
+ {
+ draw_second();
+
+ curr_second = display.second;
+ }
+ curr_status = display.status;
+
+ if( display.playbtn != CDROM_PLAYING )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.playup_pixmap,
+ display.playup_mask );
+
+ display.playbtn = CDROM_PLAYING;
+ }
+
+ /* If the track window is open, update selected track */
+ if( display.twindow != NULL )
+ {
+ if( tclist_track+1 != curr_track )
+ {
+ if( curr_track > 0 )
+ tclist_track = curr_track -1;
+ else
+ tclist_track = 0;
+ gtk_clist_select_row( GTK_CLIST(display.tclist), tclist_track, -1 );
+ }
+ }
+
+ break;
+
+ /* Flash the min/sec digits while paused */
+ case CDROM_PAUSED : if( display.track != curr_track )
+ {
+ draw_track();
+
+ curr_track = display.track;
+ }
+
+
+ if( blinking )
+ {
+ draw_second();
+ draw_minute();
+ blinking=0;
+ } else {
+ erase_second();
+ erase_minute();
+ curr_second = -1;
+ curr_minute = -1;
+ blinking=1;
+ }
+
+ if( display.playbtn != CDROM_PAUSED )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.pauseup_pixmap,
+ display.pauseup_mask );
+ display.playbtn = CDROM_PAUSED;
+ }
+ break;
+
+ /* Display NO DISC */
+ case CDROM_NODISC : if( curr_status != display.status )
+ {
+ gdk_draw_pixmap( display.wid->window,
+ display.wid->style->black_gc,
+ display.nodisc_pixmap,
+ 0, 0,
+ 0, 0, 92, 30 );
+
+ curr_status = display.status;
+ }
+
+ if( display.playbtn != CDROM_NODISC )
+ {
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.stopup_pixmap,
+ display.stopup_mask );
+ display.playbtn = CDROM_NODISC;
+ }
+
+ break;
+ }
+
+#ifdef DEBUG4
+ g_print( "update called. status = %d remain = %d\n", display.status, cdinfo.cd_remaining );
+ g_print( " curr new\n");
+ g_print( "track %03d : %03d\n", curr_track, display.track );
+ g_print( "minute %03d : %03d\n", curr_minute, display.minute );
+ g_print( "second %03d : %03d\n", curr_second, display.second );
+ g_print( "volume %03d : %03d\n", curr_volume, cdinfo.volume );
+#endif
+
+}
+
+
+/* ------------------------------------------------------------------------
+ Process data coming back from the cd_control process
+
+ This is a stateless process since it only returns a cdinfo structure
+ ------------------------------------------------------------------------ */
+void wait_status()
+{
+ struct CDINFO tmpinfo;
+ struct SITE *sp;
+ char buffer[80];
+ int x;
+ char *texts[3],
+ text1[255],
+ text2[255];
+
+
+ /* Read the new cdinfo structure from the cd_control process */
+ if( read( cd_fd, &tmpinfo, sizeof(struct CDINFO) ) < 0 )
+ perror("cdrom_status(read)");
+
+ /* Copy the useful bits from tmpinfo to cdinfo */
+ memcpy( &cdinfo.tochdr, &tmpinfo.tochdr, sizeof(tmpinfo.tochdr) );
+ memcpy( &cdinfo.leadout, &tmpinfo.leadout, sizeof(tmpinfo.leadout) );
+ memcpy( &cdinfo.volume, &tmpinfo.volume, sizeof(tmpinfo.volume) );
+
+ /* Copy track length, but not the track name over from the status */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ cdinfo.track[x].te = tmpinfo.track[x].te;
+ cdinfo.track[x].length = tmpinfo.track[x].length;
+ cdinfo.track[x].frame_offset = tmpinfo.track[x].frame_offset;
+ }
+
+ memcpy( &cdinfo.sc, &tmpinfo.sc, sizeof(tmpinfo.sc) );
+ memcpy( &cdinfo.ti, &tmpinfo.ti, sizeof(tmpinfo.ti) );
+ cdinfo.cd_length = tmpinfo.cd_length;
+ cdinfo.discid = tmpinfo.discid;
+
+ switch( cdinfo.sc.cdsc_audiostatus )
+ {
+ case CDROM_AUDIO_PLAY :
+ case CDROM_AUDIO_PAUSED :
+ if( cdinfo.sc.cdsc_audiostatus == CDROM_AUDIO_PLAY )
+ display.status = CDROM_PLAYING;
+ else
+ display.status = CDROM_PAUSED;
+
+ /* Update the currently playing track */
+ display.track = cdinfo.sc.cdsc_trk;
+
+ /* Update the number of seconds remaining on the CD */
+ cdinfo.cd_remaining = cdinfo.cd_length - ((cdinfo.sc.cdsc_absaddr.msf.minute*60)+cdinfo.sc.cdsc_absaddr.msf.second);
+
+ /* Do we need track data? Can we request it? */
+ if( (display.cddb_lock == 0) && (cdinfo.revision == -1))
+ {
+ if( config.cddb == 1 )
+ {
+#ifdef DEBUG9
+ g_print("reading cddb info %d\n", cdinfo.revision );
+#endif
+ /* Try and find a cddb entry in the local CD database */
+ if( read_cddb( &cdinfo ) < 0 )
+ {
+ /* Default server to query */
+ strcpy( cdinfo.server, "cddb.cddb.com" );
+ cdinfo.port = 888;
+
+ /* So here we query the default internet server */
+ if( config.current != NULL )
+ {
+ /* Use the current + 888 even if not found in the list */
+ strcpy( cdinfo.server, config.current );
+
+ /* Find the current server in the list */
+ sp = config.server;
+ while( sp != NULL )
+ {
+ if( strcmp( sp->name, config.current ) == 0 )
+ {
+ strcpy( cdinfo.server, sp->name );
+ cdinfo.port = sp->port;
+ sp = NULL;
+ }else {
+ sp = sp->next;
+ }
+ }
+ }
+ /* Send the command to the server - Starts a state machine */
+ cdinfo.cddbd_cmnd = DB_READ;
+ if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO) ) < 0 )
+ {
+ perror("write to cddbd_fd error");
+ if( cdinfo.revision < 0 )
+ cdinfo.revision = -2; /* Failed, Don't try again */
+ }
+ /* Lock the use of the internet database */
+ display.cddb_lock = 1;
+
+ /* !!!! This could probably be turned into a subroutine
+ !!!! */
+
+ /* Start up a progress dialog */
+ display.progress = gtk_dialog_new ();
+
+ sprintf(buffer, "Connecting to %s:%d", cdinfo.server,
+ cdinfo.port );
+
+ gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ &display.progress);
+
+ gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
+ gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
+ gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
+
+ display.plabel = gtk_label_new (buffer);
+ gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
+ display.plabel, FALSE, FALSE, 0);
+ gtk_widget_show( display.plabel );
+ gtk_widget_show( display.progress );
+ } else {
+ /* Read of local database successful */
+#ifdef DEBUG5
+ g_print("Title : %s\n", cdinfo.title->str );
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ g_print("Track %d : %s\n", x+1, cdinfo.name[x]->str );
+ }
+#endif
+ }
+ } else {
+ /* Read the data from the local database, or set defaults */
+ local_cddb();
+ }
+
+ /* If the track list window is open, update the list */
+ if( display.twindow != NULL )
+ {
+#ifdef DEBUG9
+ g_print("Updating Track Window revision=%d\n", cdinfo.revision);
+#endif
+
+ gtk_clist_freeze (GTK_CLIST (display.tclist));
+
+ /* Clear out the old list first */
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Set the new title */
+ if( cdinfo.title != NULL )
+ sprintf( text1, "%s", cdinfo.title->str );
+ else
+ strcpy( text1, "" );
+ gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
+
+ /* Add the track names */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( text1, "%d", x+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( cdinfo.name[x] != NULL )
+ texts[1] = cdinfo.name[x]->str;
+ else
+ texts[1] = "(blank)";
+ gtk_clist_append (GTK_CLIST (display.tclist), texts);
+ }
+ gtk_clist_thaw( GTK_CLIST( display.tclist ) );
+ gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
+ }
+ }
+ break;
+
+#ifdef OLD_WAY
+ case CDROM_AUDIO_PAUSED :
+ display.status = CDROM_PAUSED;
+ break;
+#endif
+
+ case CDROM_AUDIO_NO_STATUS :
+ /* Only clean things up once */
+ if( display.status != CDROM_NODISC )
+ {
+ /* Clear out the track list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Clear the title */
+ gtk_label_set( GTK_LABEL( display.ttitle ), "" );
+ }
+
+ /* Free dynamically allocated cdinfo strings */
+ free_cdinfo();
+
+ display.status = CDROM_NODISC;
+ /* Reset the revision # so next will recall from database*/
+ cdinfo.revision = -1;
+ }
+ break;
+
+ default:
+ /* Only cleanup once */
+ if( display.status != CDROM_NODISC )
+ {
+ /* Clear out the track list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Clear the title */
+ gtk_label_set( GTK_LABEL( display.ttitle ), "" );
+ }
+
+ /* Free dynamically allocated cdinfo strings */
+ free_cdinfo();
+
+ display.status = CDROM_NODISC;
+ cdinfo.revision = -1;
+ }
+ break;
+ }
+
+
+ /*
+ * If the CD is over then either restart it, or eject it, or show NO DISC
+ */
+ if( (cdinfo.cd_remaining == 0) && (display.status == CDROM_PLAYING) )
+ {
+ /* If repeat is on, start playing the cd over from the start */
+ if( display.repeat == 1 )
+ {
+ display.track = cdinfo.tochdr.cdth_trk0;
+ play_track( cdinfo.tochdr.cdth_trk0 );
+ } else {
+ /* Clear out the track list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Clear the title */
+ gtk_label_set( GTK_LABEL( display.ttitle ), "" );
+ }
+
+ /* Free up any dynamically allocated cdinfo strings */
+ free_cdinfo();
+
+ /* Eject when done Playing - only eject if the user has enabled this */
+ if( config.done_eject == 1 )
+ {
+ /* All done, eject it for the next disc! */
+ eject_cdrom();
+ } else {
+ stop_cdrom();
+ }
+
+ gtk_pixmap_set( GTK_PIXMAP( play.wid ),
+ display.stopup_pixmap,
+ display.stopup_mask );
+ display.playbtn = CDROM_NODISC;
+ }
+ }
+}
+
+
+/* -------------------------------------------------------------------------
+ Process data coming back from the cd_control process
+
+ This is a stateless process since it only returns a cdinfo structure
+ ------------------------------------------------------------------------- */
+void cd_fd_read( gpointer data, gint source, GdkInputCondition condition)
+{
+ wait_status();
+
+ if( display.startup == 0 )
+ {
+ if( config.startup == 1 )
+ {
+ /* If it is already playing, don't restart it */
+ if( ( cdinfo.sc.cdsc_audiostatus != CDROM_AUDIO_PLAY ) &&
+ (cdinfo.discid != 0 ) )
+ {
+#ifdef DEBUG6
+ g_print("Starting play of cd. audiostatus = %d\n", cdinfo.sc.cdsc_audiostatus );
+#endif
+ play_track( 1 );
+ }
+ }
+
+ /* Stop trying after the discid gets set by cd_control process */
+ if( cdinfo.discid != 0 )
+ display.startup = 1;
+ }
+
+ update_display();
+}
+
+
+/* ------------------------------------------------------------------------
+ this is the signal handler that gets called if GtkList
+ emits the "selection_changed" signal
+ ------------------------------------------------------------------------ */
+void inexact_selected( GtkWidget *gtklist, gpointer func_data)
+{
+ char *p,
+ buffer[80];
+ GList *dlist;
+ GtkObject *list_item;
+ gchar *item_data_string;
+
+
+ /* fetch the doubly linked list of selected items
+ * of the GtkList, remember to treat this as read-only!
+ */
+ dlist=GTK_LIST(gtklist)->selection;
+
+ /* if there are no selected items there is nothing more
+ * to do than just telling the user so
+ */
+ if (dlist)
+ {
+ list_item=GTK_OBJECT(dlist->data);
+ item_data_string=gtk_object_get_data(list_item,
+ "XfreeCDkey");
+ p = strtok( item_data_string, " \n" );
+ p = strtok( NULL, " \n" );
+
+#ifdef DEBUG3
+ g_print("Retrieving data for %s\n", p);
+#endif
+
+ /* Change the discid so the new request will work */
+ sscanf( p, "%lx", &cdinfo.discid );
+ cdinfo.cddbd_cmnd = DB_READ;
+ if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO) ) < 0 )
+ {
+ perror("write to cddbd_fd error");
+ if( cdinfo.revision < 0 )
+ cdinfo.revision = -2; /* Failed, Don't try again */
+ }
+ /* Lock use of the CDDB internet server */
+ display.cddb_lock = 1;
+
+ /* Delete the old progress box */
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+
+ /* Start up a new progress dialog */
+ display.progress = gtk_dialog_new ();
+
+ sprintf(buffer, "Connecting to %s:%d", cdinfo.server, cdinfo.port );
+
+ gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ &display.progress);
+
+ gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
+ gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
+ gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
+
+ display.plabel = gtk_label_new (buffer);
+ gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
+ display.plabel, FALSE, FALSE, 0);
+ gtk_widget_show( display.plabel );
+ gtk_widget_show( display.progress );
+ } else {
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+ }
+}
+
+
+
+
+/* -------------------------------------------------------------------------
+ Process data coming back from the cddbd process
+
+ This routine processes data from the pipe to the cddbd process. This gets
+ the track names and site list from the server.
+ ------------------------------------------------------------------------- */
+void cddbd_fd_read( gpointer data, gint source, GdkInputCondition condition)
+{
+ struct CDINFO tmpinfo;
+ int x;
+ static struct SITE *sp,
+ *sp_top=NULL,
+ *sp_end=NULL;
+ char *p, buffer[80];
+ gchar *string;
+ GtkWidget *button,
+ *scrolled_win,
+ *list,
+ *vbox1,
+ *label;
+ static GList *cbitems=NULL;
+ char *texts[3],
+ text1[255],
+ text2[255];
+
+
+ /* Read back the status structure */
+ if( read( cddbd_fd, &tmpinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
+ {
+ perror( "CDDBD status read" );
+ close( cd_fd );
+ close( cddbd_fd );
+ exit(-1);
+ }
+
+ switch( tmpinfo.cddbd_stat )
+ {
+ case CDDBD_OPEN_OK :
+#ifdef DEBUG3
+ g_print("Connected to %s ok\n", tmpinfo.server );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Connected to %s:%d", tmpinfo.server, tmpinfo.port);
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+ break;
+
+ case CDDBD_DONE_OK :
+ display.cddb_lock = 0;
+
+ /* Copy the info from the tmpinfo to cdinfo */
+#ifdef DEBUG3
+ g_print("Got the new data ok\n");
+ g_print("tmpinfo.title->str = 0x%X\n", tmpinfo.title->str );
+#endif
+
+#ifdef DUMB
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Retrieved %s", tmpinfo.title->str );
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+#endif
+
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+
+ /* Read the new data from the database */
+ local_cddb();
+
+ /* If the track list window is open, update the list */
+ if( display.twindow != NULL )
+ {
+ gtk_clist_freeze (GTK_CLIST (display.tclist));
+
+ /* Clear out the old list first */
+ gtk_clist_clear( GTK_CLIST( display.tclist ) );
+
+ /* Set the new title */
+ if( cdinfo.title != NULL )
+ sprintf( text1, "%s", cdinfo.title->str );
+ else
+ strcpy( text1, "" );
+ gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
+
+ /* Add the track names */
+ for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
+ {
+ sprintf( text1, "%d", x+1 );
+ sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
+ texts[0] = text1;
+ texts[2] = text2;
+ if( cdinfo.name[x] != NULL )
+ texts[1] = cdinfo.name[x]->str;
+ else
+ texts[1] = "(blank)";
+ gtk_clist_append (GTK_CLIST (display.tclist), texts);
+ }
+ gtk_clist_thaw( GTK_CLIST( display.tclist ) );
+ gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
+ }
+ break;
+
+ /* Process the line of text */
+ case CDDBD_MOTD_LINE :
+
+ if( tmpinfo.line[0] == '.' )
+ {
+ display.cddb_lock = 0;
+#ifdef DEBUG3
+ g_print("End of MOTD\n");
+#endif
+
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+
+ } else {
+#ifdef DEBUG3
+ /* Do something with the MOTD, for now print it */
+ g_print( "%s", tmpinfo.line );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
+ }
+ }
+ break;
+
+ /* Process the line of text */
+ case CDDBD_SITE_LINE :
+
+ if( tmpinfo.line[0] == '.' )
+ {
+ display.cddb_lock = 0;
+#ifdef DEBUG3
+ g_print("End of Site listing\n");
+#endif
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Retrieved Site Listing ok" );
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ if( sp_top != NULL )
+ {
+ /* Everything seems okay, erase the old list and add the new one */
+ while( config.server != NULL )
+ {
+ sp = config.server;
+ config.server = config.server->next;
+ free( sp->name );
+ free( sp );
+ }
+
+ /* Point config to the new list */
+ config.server = sp_top;
+ sp_top = sp_end = NULL;
+
+ config.saved = 0; /* Need to save this to disk */
+ write_config( &config ); /* Write new list to disk */
+ strncpy( cdinfo.device, config.device, 80 );
+
+ /* Need to let the site list in the dialog box be updated if it is
+ still being shown (user could close it before we finish).
+ */
+ if( display.cb != NULL )
+ {
+
+ /* Walk the list */
+ cbitems = NULL;
+ sp = config.server;
+ while( sp != NULL )
+ {
+ cbitems = g_list_append(cbitems, sp->name);
+ sp = sp->next;
+ }
+
+ gtk_combo_set_popdown_strings (GTK_COMBO (display.cb), cbitems);
+ cbitems = NULL;
+ }
+
+ }
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+
+ } else {
+ /* Do something with the SITE, for now print it */
+ /* Need to store in a temp. List until complete, then move to config */
+#ifdef DEBUG3
+ g_print( "%s", tmpinfo.line );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
+ }
+
+ /* Reserve some space for this server, add it to the end of the list */
+ if( ( sp = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
+ {
+ perror("malloc - 1");
+ }
+ bzero( sp, sizeof(struct SITE) );
+
+ p = strtok( tmpinfo.line, " \n" );
+
+ /* Reserve space for the name */
+ if( ( sp->name = (char *) malloc( strlen(p)+1 ) ) == NULL )
+ {
+ perror("malloc - 2");
+ }
+ strcpy( sp->name, p );
+
+ p = strtok( NULL, " \n" );
+ sp->port = atoi(p);
+ sp->next = NULL;
+
+ /* Is it the first in the list? */
+ if( sp_top == NULL )
+ {
+ /* Yep, First in the list */
+ sp_top = sp;
+ sp_end = sp;
+ } else {
+ /* No, add it to the end of the list */
+ sp_end->next = sp;
+ sp_end = sp;
+ }
+ }
+ break;
+
+
+ /* Process the line of text inexact matches */
+ case CDDBD_INEX_LINE :
+
+ if( tmpinfo.line[0] == '.' )
+ {
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("End of Inexact Match listing\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Retrieved inexact matches for discid 0x%08lx", cdinfo.discid );
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ /* Here we want to add a list of the sites to the progress box,
+ add a cancel button, and add a signal to use one of the inexact
+ matches in a new search.
+ */
+ if( cbitems != NULL )
+ {
+ /* Add a vbox to hold the list and cancel button */
+ vbox1 = gtk_vbox_new( FALSE, 0 );
+ gtk_box_pack_start( GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ vbox1, FALSE, TRUE, 1 );
+ gtk_widget_show( vbox1 );
+
+ scrolled_win = gtk_scrolled_window_new (NULL, NULL);
+ gtk_widget_set_usize( scrolled_win, 250, 100 );
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+ gtk_box_pack_start (GTK_BOX( vbox1 ), scrolled_win, FALSE, FALSE, 0);
+ gtk_widget_show (scrolled_win);
+
+ /* Put a list into the scrolling window */
+ list = gtk_list_new ();
+ gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_SINGLE);
+ gtk_container_add (GTK_CONTAINER (scrolled_win), list);
+ gtk_container_set_focus_vadjustment (GTK_CONTAINER (list),
+ gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_win)));
+ gtk_signal_connect(GTK_OBJECT(list),
+ "selection_changed",
+ GTK_SIGNAL_FUNC(inexact_selected),
+ NULL);
+ gtk_widget_show (list);
+
+ /* Add the list of inexact matches to the list from cbitems */
+ gtk_list_append_items( GTK_LIST( list ), cbitems );
+
+ button = gtk_button_new_with_label ("Cancel");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX( vbox1 ), button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ cbitems = NULL;
+ } else {
+ gtk_widget_destroy( display.progress );
+ display.plabel = NULL;
+ }
+ } else {
+ /* Need to store in a temp. List until we receive the last line */
+#ifdef DEBUG3
+ g_print( "%s", tmpinfo.line );
+#endif
+
+ /* Show the line in the progress box */
+ if( display.plabel != NULL )
+ {
+ gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
+ }
+
+ /* Add it to a list of sites */
+ /* Need to detect first line of this type and set cbitems to NULL, or else
+ set it to null when everything else is finished with it.
+ */
+ if( tmpinfo.line[strlen(tmpinfo.line)-1] == '\n' )
+ {
+ tmpinfo.line[strlen(tmpinfo.line)-1] = 0x00;
+ }
+
+ button=gtk_list_item_new();
+ label=gtk_label_new( tmpinfo.line );
+ gtk_misc_set_alignment (GTK_MISC(label), 0.0, 0.5);
+ gtk_container_add (GTK_CONTAINER (button), label);
+ gtk_widget_show (label);
+ gtk_widget_show(button);
+ gtk_label_get(GTK_LABEL(label), &string);
+ gtk_object_set_data(GTK_OBJECT(button),
+ "XfreeCDkey",
+ string);
+ cbitems = g_list_append(cbitems, button);
+
+ }
+ break;
+
+
+
+ case CDDBD_MATCH_OK :
+#ifdef DEBUG3
+ g_print("Got a match for 0x%08lx\n", tmpinfo.discid );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Retrieving data for id 0x%08lx", tmpinfo.discid);
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ break;
+
+ case CDDBD_ENTRY_OK :
+#ifdef DEBUG3
+ g_print("Reading CDDB Database entry from %s\n", tmpinfo.server );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Reading CDDB info from %s", tmpinfo.server);
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+ break;
+
+ case CDDBD_DONE_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+
+#ifdef DEBUG3
+ g_print("Error reading temporary file\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Error Reading temp. file");
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ case CDDBD_OPEN_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Failed to connect to %s\n", tmpinfo.server );
+ g_print("%s\n", tmpinfo.line );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Failed to connect to %s", tmpinfo.server );
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ case CDDBD_READ_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Socket read failed\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Socket Read Failed");
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ case CDDBD_WRITE_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Socket write failed\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Socket Write Failed");
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ case CDDBD_TMPF_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Error, cannot create a temporary filename\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Cannot Create temp. file");
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ case CDDBD_FOPEN_ERR :
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Error opening temporary file\n");
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf (buffer, "Error opening temp. file");
+ gtk_label_set (GTK_LABEL (display.plabel), buffer);
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+
+ default:
+ cdinfo.revision = -2;
+ display.cddb_lock = 0;
+
+#ifdef DEBUG3
+ g_print("Server returned %d\n", tmpinfo.cddbd_stat );
+#endif
+
+ if( display.plabel != NULL )
+ {
+ sprintf( buffer, "Server: %s", tmpinfo.line );
+ gtk_label_set (GTK_LABEL (display.plabel), buffer );
+ }
+
+ button = gtk_button_new_with_label ("OK");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC(gtk_widget_destroy),
+ GTK_OBJECT (display.progress));
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
+ button, TRUE, TRUE, 0);
+ gtk_widget_grab_default (button);
+ gtk_widget_show (button);
+
+ break;
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Get the current status of the cdrom and fill in the display info
+
+ return -1 = error writing
+ ----------------------------------------------------------------------- */
+int cdrom_status()
+{
+ /* Get the status of the cd */
+ cmnd[0] = CD_STATUS;
+ cmnd[1] = 0;
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ return(-1);
+ }
+
+ return 0;
+}
+
+
+/* -----------------------------------------------------------------------
+ Play a track
+ ----------------------------------------------------------------------- */
+void play_track( int track )
+{
+ /* Start Playing right away */
+ cmnd[0] = CD_PLAY;
+ cmnd[1] = track;
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Play the next track
+ ----------------------------------------------------------------------- */
+void play_next()
+{
+ cmnd[0] = CD_PLAY;
+ cmnd[1] = display.track == cdinfo.tochdr.cdth_trk1 ? display.track = cdinfo.tochdr.cdth_trk0 : ++display.track;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Play the previous track
+ ----------------------------------------------------------------------- */
+void play_previous()
+{
+ cmnd[0] = CD_PLAY;
+ cmnd[1] = display.track == cdinfo.tochdr.cdth_trk0 ? display.track = cdinfo.tochdr.cdth_trk1 : --display.track;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Eject the cd tray if we are currently playing, otherwise start playing.
+
+ This should really be seperated into 2 pieces of code for more positive
+ control over the cdrom.
+ ----------------------------------------------------------------------- */
+void eject_cdrom()
+{
+ cmnd[0] = CD_EJECT;
+ cmnd[1] = 0;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Pause the cdrom
+ ----------------------------------------------------------------------- */
+void pause_cdrom()
+{
+ cmnd[0] = CD_PAUSE;
+ cmnd[1] = 0;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Resume playing the cdrom
+ ----------------------------------------------------------------------- */
+void resume_cdrom()
+{
+ cmnd[0] = CD_RESUME;
+ cmnd[1] = 0;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Change the volume setting
+
+ amount is +/- amount to adjust by
+ ----------------------------------------------------------------------- */
+void set_vol( int amount )
+{
+ /* Limit the volume setting to 0 to 255 */
+ cdinfo.volume += amount;
+ cdinfo.volume = cdinfo.volume < 0 ? 0 : cdinfo.volume > 255 ? 255 : cdinfo.volume;
+
+ cmnd[0] = CD_VOLUME;
+ cmnd[1] = (unsigned char) cdinfo.volume;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Stop playing the cdrom
+ ----------------------------------------------------------------------- */
+void stop_cdrom()
+{
+ cmnd[0] = CD_STOP;
+ cmnd[1] = 0;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
+
+
+/* -----------------------------------------------------------------------
+ Set the Eject CD on exit state
+ ----------------------------------------------------------------------- */
+void set_eject( int state )
+{
+ cmnd[0] = CD_SET_EJECT;
+ cmnd[1] = state;
+
+ if( write( cd_fd, &cmnd, 2 ) < 0 )
+ {
+ perror("write to cd_control error");
+ }
+}
diff --git a/xfreecd.gif b/xfreecd.gif
Binary files differ.
diff --git a/xfreecd.h b/xfreecd.h
@@ -0,0 +1,34 @@
+/* ------------------------------------------------------------------------
+ xfreecd include file for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ nexus@tatoosh.com
+ http://www.tatoosh.com/nexus
+
+ ------------------------------------------------------------------------ */
+#define DEFAULT_CDDB_PATH "~/.cddb"
+#define DEFAULT_SERVER "cddb.cddb.com"
+#define DEFAULT_DEVICE "/dev/cdrom"
+#define DEFAULT_TO_CDDBD "xmcd-cddb@amb.org"
+#define MAIL_BINARY "mail"
+
+
+struct SITE {
+ char *name;
+ int port;
+ struct SITE *next;
+};
+
+struct CONFIG {
+ char *device; /* CD device to use */
+ char *local_cddb;
+ char *current; /* Current server name */
+ char *to_cddbd; /* Server to send to */
+ struct SITE *server;
+ short done_eject; /* Eject when play is finished */
+ short exit_eject; /* Eject on Exit when not playing */
+ short startup; /* What should we do at startup? See STARTUP_* */
+ short cddb; /* Is cddb support enabled? */
+ short changer; /* Is IDE CD changer support enabled? */
+ short saved; /* Saved state. 1 = saved, 0 = needs to be saved */
+};
diff --git a/xfreecd.lsm b/xfreecd.lsm
@@ -0,0 +1,21 @@
+
+Begin3
+Title: XfreeCD
+Version: 0.7.8
+Entered-date: 02DEC98
+Description: XfreeCD is a CD player for X written with GTK+
+ It supports CDDB database info, and can submit new CD
+ info to the CDDB database.
+Keywords: XfreeCD,CD player,Audio,CDROM,sound,X,GTK+
+Author: nexus@tatoosh.com (Brian Lane)
+Maintained-by: nexus@tatoosh.com (Brian Lane)
+Primary-site: ftp.tatoosh.com /linux
+ 669 xfreecd-0.7.8.lsm
+ 120464 xfreecd-0.7.8.tar.gz
+ 54867 xfreecd-0.7.8-1.i386.rpm
+ 121588 xfreecd-0.7.8-1.src.rpm
+Alternate-site: sunsite.unc.edu /pub/Linux/X11/utils
+Original-site:
+Platforms: GTK+ v1.0.2 or later
+Copying-policy: GPL
+End
diff --git a/xfreecd.sig b/xfreecd.sig
Binary files differ.
diff --git a/xfreecd.spec b/xfreecd.spec
@@ -0,0 +1,36 @@
+Summary: Xfreecd, a CD player with CDDB features
+Name: xfreecd
+Version: 0.7.8
+Release: 1
+Copyright: GPL
+Group: X11/Utilities
+Source: http://www.tatoosh.com/nexus/linux/xfreecd-0.7.8.tar.gz
+URL: http://www.tatoosh.com/nexus/xfreecd.shtml
+Packager: nexus@tatoosh.com
+Icon: xfreecd.gif
+Prefix: /usr/local
+Buildroot: /var/tmp/xfreecd-0.7.8
+Requires: libm.so.5
+
+%description
+ XfreeCD is a X windows program written using GTK+ that looks like the
+frontpanel of a cd player. It also supports the CDDB database of CD track
+information, and is certified for submitting new CD information to the
+database. At 137x90 it takes up a small amount of screen space.
+
+%prep
+%setup
+
+%build
+make
+
+%install
+install -d -m 755 -o 0 -g 0 $RPM_BUILD_ROOT/usr/local/bin/
+install -s -m 755 -o 0 -g 0 xfreecd $RPM_BUILD_ROOT/usr/local/bin/
+install -d -m 755 -o 0 -g 0 $RPM_BUILD_ROOT/etc/X11/wmconfig/
+install -m 644 -o 0 -g 0 xfreecd.wmconfig $RPM_BUILD_ROOT/etc/X11/wmconfig/xfreecd
+
+%files
+%doc COPYING README HISTORY xfreecd.xpm xfreecd.gif
+/usr/local/bin/xfreecd
+/etc/X11/wmconfig/xfreecd
diff --git a/xfreecd.wmconfig b/xfreecd.wmconfig
@@ -0,0 +1,4 @@
+xfreecd name "XFreeCD"
+xfreecd description "CD Player"
+xfreecd exec "xfreecd &"
+xfreecd group Utilities/Sound
diff --git a/xfreecd.xpm b/xfreecd.xpm
@@ -0,0 +1,341 @@
+/* XPM */
+static char * xfreecd_xpm[] = {
+"48 48 290 2",
+" c None",
+". c #50E5F8",
+"+ c #56E5F8",
+"@ c #61E6F8",
+"# c #65E5F8",
+"$ c #6BE5F8",
+"% c #6DE5F8",
+"& c #64E5F8",
+"* c #59E5F8",
+"= c #47E4F8",
+"- c #3FE4F8",
+"; c #3CE4F7",
+"> c #38E3F7",
+", c #35E3F7",
+"' c #24E1F7",
+") c #1FE1F7",
+"! c #1CE1F7",
+"~ c #19E1F7",
+"{ c #18E1F7",
+"] c #63E6F8",
+"^ c #71E6F8",
+"/ c #78E6F8",
+"( c #9DE7F8",
+"_ c #A3E8F8",
+": c #76E6F8",
+"< c #68E5F8",
+"[ c #5BE5F8",
+"} c #45E4F8",
+"| c #3EE4F8",
+"1 c #3AE4F7",
+"2 c #37E3F7",
+"3 c #33E3F7",
+"4 c #25E1F7",
+"5 c #1BE1F7",
+"6 c #8AE6F8",
+"7 c #8EE7F8",
+"8 c #9AE7F8",
+"9 c #A0E8F8",
+"0 c #99E7F8",
+"a c #8AE7F8",
+"b c #86E6F8",
+"c c #4BE5F8",
+"d c #41E4F8",
+"e c #20E1F7",
+"f c #A7E8F8",
+"g c #BEEBF9",
+"h c #C8EDF9",
+"i c #BCEBF9",
+"j c #A4E8F8",
+"k c #74E6F8",
+"l c #60E5F8",
+"m c #51E5F8",
+"n c #44E4F8",
+"o c #3DE4F7",
+"p c #39E3F7",
+"q c #1AE1F7",
+"r c #DFF0FA",
+"s c #EFF5FB",
+"t c #97E7F8",
+"u c #66E5F8",
+"v c #55E5F8",
+"w c #36E3F7",
+"x c #32E3F7",
+"y c #9FE8F8",
+"z c #C7EDF9",
+"A c #FFFEFE",
+"B c #9DE8F8",
+"C c #48E4F8",
+"D c #2EE2F7",
+"E c #31E3F7",
+"F c #2CE2F7",
+"G c #26E2F7",
+"H c #2FE2F7",
+"I c #2AE2F7",
+"J c #24E2F7",
+"K c #27E2F7",
+"L c #22E1F7",
+"M c #29E2F7",
+"N c #3FE4F7",
+"O c #34E3F7",
+"P c #2FE3F7",
+"Q c #2BE2F7",
+"R c #25E2F7",
+"S c #21E1F7",
+"T c #1DE1F7",
+"U c #22E2F7",
+"V c #1EE1F7",
+"W c #23E2F7",
+"X c #2DE2F7",
+"Y c #32E2F7",
+"Z c #30E3F7",
+"` c #28E2F7",
+" . c #30E2F7",
+".. c #45E4F7",
+"+. c #2CE1F7",
+"@. c #2BE1F7",
+"#. c #4AF1FA",
+"$. c #000000",
+"%. c #17DFF4",
+"&. c #17DFF5",
+"*. c #17E0F6",
+"=. c #14C7DA",
+"-. c #0A646D",
+";. c #0C747F",
+">. c #17DAF0",
+",. c #17DDF3",
+"'. c #17DEF4",
+"). c #32E1F5",
+"!. c #30E0F5",
+"~. c #1F281B",
+"{. c #17DDF2",
+"]. c #11AABC",
+"^. c #17DCF2",
+"/. c #14C4D8",
+"(. c #11ACBD",
+"_. c #77F5F9",
+":. c #2ADEF3",
+"<. c #1E8087",
+"[. c #150B00",
+"}. c #1A0E00",
+"|. c #17DBF1",
+"1. c #109CAB",
+"2. c #0F909F",
+"3. c #17D9EF",
+"4. c #11A9BA",
+"5. c #02181B",
+"6. c #074850",
+"7. c #0F92A0",
+"8. c #1D1000",
+"9. c #2297A0",
+"0. c #37DDF0",
+"a. c #21281B",
+"b. c #241500",
+"c. c #17D9EE",
+"d. c #16D5EA",
+"e. c #15CADD",
+"f. c #042F33",
+"g. c #16D6EB",
+"h. c #043034",
+"i. c #07484F",
+"j. c #0F909E",
+"k. c #1C0F00",
+"l. c #120A00",
+"m. c #33DAED",
+"n. c #180E00",
+"o. c #201200",
+"p. c #16D4E9",
+"q. c #16CFE3",
+"r. c #16D2E7",
+"s. c #07464D",
+"t. c #0E8D9B",
+"u. c #36D7E9",
+"v. c #372100",
+"w. c #16D1E5",
+"x. c #15C6D9",
+"y. c #010A0B",
+"z. c #15CDE1",
+"A. c #07454B",
+"B. c #16D0E4",
+"C. c #2ED2E4",
+"D. c #29D1E3",
+"E. c #142019",
+"F. c #1C1706",
+"G. c #091D13",
+"H. c #17CCDF",
+"I. c #15C8DB",
+"J. c #14C3D6",
+"K. c #04292D",
+"L. c #15C5D8",
+"M. c #074349",
+"N. c #15CADE",
+"O. c #12B3C5",
+"P. c #30CDDE",
+"Q. c #246462",
+"R. c #091D17",
+"S. c #1DCBDA",
+"T. c #0B6C76",
+"U. c #08525A",
+"V. c #14C1D4",
+"W. c #14BFD2",
+"X. c #042A2E",
+"Y. c #063F45",
+"Z. c #12AEBF",
+"`. c #042B30",
+" + c #064148",
+".+ c #11AEC0",
+"++ c #021618",
+"@+ c #02090B",
+"#+ c #09297B",
+"$+ c #16C1D4",
+"%+ c #0D7E8A",
+"&+ c #050100",
+"*+ c #1C7B81",
+"=+ c #08020D",
+"-+ c #06000B",
+";+ c #100C03",
+">+ c #13B7C9",
+",+ c #14BCCF",
+"'+ c #13BACC",
+")+ c #10A4B4",
+"!+ c #021517",
+"~+ c #031F22",
+"{+ c #11A8B9",
+"]+ c #063F46",
+"^+ c #0D7F8C",
+"/+ c #14BED1",
+"(+ c #14BDD0",
+"_+ c #000005",
+":+ c #171C05",
+"<+ c #101608",
+"[+ c #178282",
+"}+ c #114D41",
+"|+ c #070E00",
+"1+ c #040008",
+"2+ c #110E05",
+"3+ c #14BBCE",
+"4+ c #13B8CA",
+"5+ c #13B6C8",
+"6+ c #13B4C6",
+"7+ c #13B4C5",
+"8+ c #10A0AF",
+"9+ c #0D8693",
+"0+ c #11A2B1",
+"a+ c #13B9CB",
+"b+ c #10A5B5",
+"c+ c #0E919F",
+"d+ c #16B5C7",
+"e+ c #28BCC6",
+"f+ c #1CB7C3",
+"g+ c #1AB4C0",
+"h+ c #1AB2BE",
+"i+ c #19A7B0",
+"j+ c #12AFC1",
+"k+ c #13B5C7",
+"l+ c #13B3C4",
+"m+ c #12B0C1",
+"n+ c #12AFC0",
+"o+ c #13B2C3",
+"p+ c #22B6C4",
+"q+ c #1BB5C0",
+"r+ c #19B1BE",
+"s+ c #18AFBC",
+"t+ c #17ACB9",
+"u+ c #17ABB8",
+"v+ c #12ADBE",
+"w+ c #11A5B5",
+"x+ c #11A7B8",
+"y+ c #12B1C2",
+"z+ c #21B2C0",
+"A+ c #1DB3BD",
+"B+ c #1AB1BC",
+"C+ c #18AEBA",
+"D+ c #14AAB9",
+"E+ c #13A7B7",
+"F+ c #14A7B5",
+"G+ c #148A92",
+"H+ c #12ACBD",
+"I+ c #17ADBE",
+"J+ c #16ACBF",
+"K+ c #1CB3BC",
+"L+ c #1AAFBA",
+"M+ c #16ABB9",
+"N+ c #13A8B8",
+"O+ c #11A5B6",
+"P+ c #13A5B4",
+"Q+ c #16A5B2",
+"R+ c #0F95A3",
+"S+ c #11A6B6",
+"T+ c #21B0BE",
+"U+ c #1DB3BC",
+"V+ c #1AB0BB",
+"W+ c #18ADB9",
+"X+ c #17A8B4",
+"Y+ c #148083",
+"Z+ c #14C3D7",
+"`+ c #0F8E9B",
+" @ c #1DB2C3",
+".@ c #19B2BF",
+"+@ c #1BB2BD",
+"@@ c #19B0BC",
+"#@ c #18AEBB",
+"$@ c #18ACB8",
+"%@ c #1AA7AF",
+"&@ c #1AB3C7",
+"*@ c #1EB8C2",
+"=@ c #1AB3BF",
+"-@ c #1AB1BD",
+";@ c #1BB2BC",
+">@ c #1EB3BB",
+",@ c #0C717C",
+" ",
+" ",
+" ",
+" ",
+" . + @ # $ % $ & * . = - ; > , ' ) ! ~ { { { { { { { { { ",
+" ] ^ / ( _ ( : < [ . } | 1 2 3 4 ) 5 ~ { { { { { { { { ",
+" % 6 7 8 9 0 a b < * c d ; > , e 5 ~ { { { { { { { { ",
+" / 7 f g h i j a k l m n o p , ) q { { { { { { { { ",
+" 8 g r s r i t 8 u v = | 1 w x e q { { { { { { { { ",
+" y z s A s z B 9 < + C - 1 w x ~ { { { { { { { { ",
+" t i r s r i t 8 u v = | 1 w x D { { { { { { { { ",
+" j i z i j a k l m n o p , x D { { { { { { { ",
+" a t B t a b < * c d ; > , E F G { { { { { { ",
+" 8 9 8 k < [ . } | 1 2 3 H I J { { { { { ",
+" u < u l * . = - ; > , x F K L { { { { ",
+" v + v m c } - ; p w 3 D M J ) 5 { { { { ",
+" C = n d | N p 2 O P Q R S T ~ { { { ",
+" - | o ; 1 > w 3 P Q G U V 5 { { { { ",
+" 1 p > 2 , x P Q K W ) ! ~ { { { ",
+" w , , 3 E X I Y W ) ! ~ { { { { ",
+" x Z D Q ` ...+.! q { { { { { ",
+" F I ` G W e @.! ~ { { { { { { ",
+" G J U e ) T 5 ~ { { { { { { { { ",
+" ) V ! 5 ~ { { #.{ { { { { { { ",
+" 5 q ~ { { { { { { { { { { { { { ",
+" { { { { { { { { { { { { { { { ",
+" $.$.$. %.&.&.*.*.*.*.{ { { { { =.-.$.;.$.$. $.$.$. $.$. ",
+" $.$.$.$. >.,.'.'.'.&.&.&.&.&.).!.~.$.$.{.$.$. $.$.$. $.$.$. ",
+" $.$.$. ].^.^.,./.(./.(._.:.<.[.}.$.|. $. $.$.$. $.$.$.$. ",
+" $.$.$.$.$. $.$. $.$. 1.$.$. $.2.3.4.5.6.7.8.9.0.a.b.[.8.c.d. $.$.$. $.$.$.$. ",
+" $.$. $.$. $.$.e.f.$.$. $.$.g.h.$.i.j.k.l.m.n.o.l.k.d.p. $.$.$. $.$.$.$. ",
+" $.$. $.$. q.$.$.$. $.$.r.$.$.s.t.8.[.u.o.v.[.8.w.q.x. $.$.$. $.$.$.$. ",
+" $.$. $.$. e.$.$.y. z.$.$.A.q.B.C.D.E.[.F.G.H.e.I. $.$.$. $.$.$.$. ",
+" $.$. $.$. J./.$.$.K. L.$.$.M.N.N.O.P.Q.8.G.R.S./.T.U. $.$.$. $.$.$.$. ",
+" $.$. $.$. V.W.X.$.Y. $.Z.`.$. +/./. +/..+++@+#+$+%+&+*+ =+-+=+ ;+&+$. ",
+" $.$. $.$. >+,+'+)+K.!+~+$. {+X.!+]+]+^+/+(+{+X._+)+:+<+[+}+|+$.$.$.$.1+2+ ",
+" 3+4+5+6+7+8+9+ 0+a+b+c+'+'+a+4+>+5+d+e+f+g+h+i+ ",
+" j+a+k+l+m+n+n+m+ 6+k+k+k+6+6+7+l+o+p+q+r+s+t+u+ ",
+" 3+>+l+m+n+v+v+Z.w+ x+o+o+o+y+y+m+n+z+A+B+C+D+E+F+G+ ",
+" v+3+5+l+m+Z.H+H+H+v+ m+m+n+n+n+Z.I+J+K+L+M+N+O+P+Q+ ",
+" V.,+4+7+y+n+Z.v+Z.Z.R+ S+m+n+n+n+Z.T+H+U+V+W+D+E+F+X+Y+ ",
+" n+Z+/+'+5+7+o+y+m+m+y+y+ `+o+o+y+m+m+ @n+.@+@@@#@t+t+$@%@ ",
+" I.x.V.(+'+>+5+6+6+6+6+k+7. y+6+6+7+l+&@l+o+*@q+g+=@-@;@>@,@ ",
+" ",
+" ",
+" ",
+" ",
+" "};
diff --git a/xpm_button.c b/xpm_button.c
@@ -0,0 +1,67 @@
+/* -----------------------------------------------------------------------
+ xpm button routines for XfreeCD
+
+ Copyright 1998 by Brian C. Lane
+ ----------------------------------------------------------------------- */
+#include <gtk/gtk.h>
+#include "xpm_button.h"
+
+/* Create a button with 2 xpm pixmaps for pressed and unpressed states */
+GtkWidget *xpm_button( GtkWidget *parent, struct _pbutton *pb )
+{
+ GtkWidget *event_box;
+ GtkWidget *hbox;
+ GtkStyle *style;
+
+ /* get style of button.. I assume it's to get the background color.
+ * if someone knows the real reason, please enlighten me. */
+ style = gtk_widget_get_style( parent );
+
+ /* Make a hbox to hold the image */
+ hbox = gtk_hbox_new( FALSE, 0 );
+ gtk_container_border_width( GTK_CONTAINER( hbox ), 0 );
+
+ /* now on to the xpm stuff.. load xpm */
+ pb->image.up_pixmap = gdk_pixmap_create_from_xpm_d (parent->window,
+ &pb->image.up_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) pb->up_xpm);
+
+ pb->image.dn_pixmap = gdk_pixmap_create_from_xpm_d (parent->window,
+ &pb->image.dn_mask,
+ &style->bg[GTK_STATE_NORMAL],
+ (gchar **) pb->dn_xpm);
+
+ /* Start with the up image */
+ pb->wid = gtk_pixmap_new (pb->image.up_pixmap, pb->image.up_mask);
+
+ gtk_box_pack_start (GTK_BOX (hbox), pb->wid, FALSE, FALSE, 0);
+
+ gtk_widget_show(pb->wid);
+
+ /* Create the event box that will hold the image and callbacks */
+ event_box = gtk_event_box_new();
+/* gtk_container_add (GTK_CONTAINER(parent), event_box);
+ * gtk_widget_show (event_box);
+ */
+
+ /* Add the box containing the image to the button */
+ gtk_container_add (GTK_CONTAINER (event_box), hbox);
+
+ /* Make sure that the image is visible */
+ gtk_widget_show( hbox );
+
+ /* Set the clipping size (this should come from the pixmap somehow) */
+/* gtk_widget_set_usize (hbox, 29, 30); */
+
+ /* Add the callback functions */
+ gtk_widget_set_events( event_box, GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK );
+
+ gtk_signal_connect (GTK_OBJECT (event_box), "button_press_event",
+ GTK_SIGNAL_FUNC (pb->press), NULL );
+ gtk_signal_connect (GTK_OBJECT (event_box), "button_release_event",
+ GTK_SIGNAL_FUNC (pb->release), NULL );
+
+ return( event_box );
+}
diff --git a/xpm_button.h b/xpm_button.h
@@ -0,0 +1,24 @@
+/* XPM Pushbutton structures */
+
+struct _bstate
+{
+ GdkPixmap *up_pixmap;
+ GdkBitmap *up_mask;
+ GdkPixmap *dn_pixmap;
+ GdkBitmap *dn_mask;
+};
+
+
+struct _pbutton
+{
+ GtkWidget *wid; /* Button's Widget ID */
+ struct _bstate image; /* up and down images */
+ int timer;
+ void (*press) (GtkWidget *widget, GdkEventButton *event);
+ void (*release) (GtkWidget *widget, GdkEventButton *event);
+ void (*clicked) (GtkWidget *widget, GdkEventButton *event);
+ gchar *up_xpm;
+ gchar *dn_xpm;
+};
+
+GtkWidget *xpm_button( GtkWidget *parent, struct _pbutton *pb );