xfreecd

Audio CD player for X
git clone https://www.brianlane.com/git/xfreecd
Log | Files | Refs | README | LICENSE

xfreecd.c (162771B)


      1 /* ---------------------------------------------------------------------
      2 
      3    XfreeCD v0.7.8
      4 
      5    Copyright (C) 1998 Brian C. Lane
      6    nexus@tatoosh.com
      7    http://www.tatoosh.com/nexus
      8 
      9    This program is free software; you can redistribute it and/or
     10    modify it under the terms of the GNU General Public License
     11    as published by the Free Software Foundation; either version 2
     12    of the License, or (at your option) any later version.
     13 
     14    This program is distributed in the hope that it will be useful,
     15    but WITHOUT ANY WARRANTY; without even the implied warranty of
     16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17    GNU General Public License for more details.
     18 
     19    You should have received a copy of the GNU General Public License
     20    along with this program; if not, write to the Free Software
     21    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     22 
     23    ============================[ HISTORY ]==============================
     24    12/02/98     Tracking down the crash on CDDB retrieveal bug that people
     25                 have been reporting. I've finally duplicated it myself
     26 		and narrowed it down to the code after printing the string
     27 		"Got the new data ok" and the call to local_cddb();
     28 		My theory is that display.plabel or display.progress don't
     29 		get set to a NULL when deleted, so my checks fail and
     30 		it tries to write to already freed memory.
     31 		Removed the progress update right before destroying the
     32 		progress box, no point if its going to get deleted!
     33 		I cannot be 100% positive, but I think this has fixed the
     34 		recall problem. Needs testing.
     35 
     36 		Another bug. If the progress box is killed, it never opens
     37 		again until the program is restarted. Fix this in next
     38 		version.
     39 
     40 
     41    07/05/98	Fixed intermittant bug with recalling CD info. There was
     42    	 	a spare strcat(tmpstr) in the request loop of offsets!
     43    	 	Depending on the state of memory this could really hose
     44    	 	the requests.
     45 
     46    06/26/98     Fixing Revision # for original submissions. Store them
     47                 locally as #0 so that the first send to the server will
     48 		increment it to #1 and the server will get #1.
     49 		Also added the VERSION number to the makefile to make
     50 		changing the version easier.
     51 
     52    06/22/98     I just noticed a problem. The Length of track #1 is wrong!
     53                 Seems to be stuck at 20:36. cd_control.c was waiting until
     54 		the track was at #2 before calculating the length. FIXED.
     55 
     56    06/20/98     TODO
     57                 1. Fix EXT problems.
     58 		   FIXED.
     59 		2. Reread spec on blank tracks. It refused 'Track 6'
     60 		   I shouldn't save any default strings to disk or send
     61 		   them to the database. Only show them when updating
     62 		   the clist. DONE. Sends a blank ttitle if none is
     63 		   entered.
     64 
     65 		3. Made change, but edit doesn't work and it complains
     66 		   about lots of g_strings != NULL (means ==) that need
     67 		   to be fixed (probably in the edit an edit copy). Make
     68 		   sure the (blank) placeholders are not copied over unless
     69 		   they are edited.
     70 		   Writing of blank tracks doesn't work when the user has
     71 		   deleted everything since it holds a string of zero
     72 		   length.
     73 		   FIXED. Check the length for 0.
     74 
     75 		4. If the CD is paused when XfreeCD is run it doesn't
     76 		   show the current track in the display. FIXED.
     77 
     78 		5. If the CD is paused when XfreeCD is run and you show
     79 		   the track names, it is blank. The database has not
     80 		   been read yet. FIXED.
     81 
     82 		6. Main Tracklist display is updated every second...
     83 		   This is because some of the CDDB entries have a revision
     84 		   of -1. I convert these into revision 1 now.
     85 
     86    06/19/98     Finishing up the extended data support. Adding free_cdinfo
     87                 to the appropriate places. Need to make sure its only called
     88 		when its needed and not in the update loop.
     89 		Changed Cancel button in edit window to Close
     90 
     91 		Shit. Nov I've broken it and I don't know how. Well, then
     92 		I'll just break it more. Changing all string usage to
     93 		dynamically allocated GString * from gtk.
     94 
     95 		Whew! Okay, I've converted all the static string storage
     96 		over to GString usage. It appears to be working okay for
     97 		the moment. Now to stress test it with all the CDDB
     98 		requirements.
     99 
    100 		Testing and making submissions to CDDB test server.
    101 		1. Inexact doesn't save to disk with my ID, it saves it
    102 		   with the downloaded id (because I canged the way it
    103 		   is saved after being received!). Change in cddbd.c
    104 		   so it save to local, reads it, changes discid and
    105 		   saves it again <ICK>. FIXED.
    106 
    107 		2. There is a problem when reading extended info. It
    108 		   is writing a blank EXTD and EXT0 then another EXT0
    109 		   and then up to the end-1 tracks. Fix it tomorrow.
    110 		   FIXED. 1 - off error.
    111 
    112    06/16/98     Things that need fixing:
    113                 1. 99 track CD isn't downloaded, causes an error and
    114 		   it retries the server endlessly.
    115 		   The track array in cdinfo was 1 short! I should use
    116 		   0-98 instead of 1-99 for all access.
    117 		   FIXED.
    118 
    119 		2. Server error caused by not sending all the track offsets
    120 		   (buffer overflow). Fixed by sending it the offsets one
    121 		   at a time. FIXED.
    122 
    123 		3. Edit window coredumps when trying to edit 99 track
    124 		   NIN-broken. FIXED. display structure had a [99] in it.
    125 		   
    126 		4. Main tracklist window flickers every second...
    127 
    128 		5. Need to process extended data, save to local database,
    129 		   resend it to server when tracks change. Don't need
    130 		   to edit it or display it (most CDs don't use it at all).
    131 		   Reading works now.
    132 		   a. Need to write it back to disk. DONE.
    133 		   b. Need to free the used memory when a CD is ejected
    134 		      DONE.
    135 
    136 		6. Need to read the Revision # correctly
    137 		   DONE.
    138 
    139    06/06/98     Fixed problem with changing categories. Once the new
    140                 category has successfully been written it erases the
    141 		old category.
    142 		While it is waiting to get new data from the server
    143 		it is displaying the old track data. Needs to be erased
    144 		from the cdinfo structure as well as the display.
    145 		FIXED.
    146 		I would like to make long title in the track window
    147 		wrap automatically, but the GTK label doesn't have
    148 		wrapping, so too bad.
    149 		Adding a Send to Server option. This just uses the
    150 		local mail binary (must be in the path). This should
    151 		work for everyone.
    152 		Works, Tested successfully.
    153 		Added support for multiple TITLEx lines. Tested, and it
    154 		works fine. Sent 5 test of long title and long track
    155 		names to the cddb-test server.
    156 		I just noticed a bug with the clist edit window. It
    157 		is resetting the vertical scroll bar when you select
    158                 a track that has to be scrolled down to. Probably
    159 		because I reload the clist when the trackname is
    160 		edited. FIXED.
    161 
    162    06/05/98     Trying to tidy up all the loose ends.
    163                 GTK cannot figure out the geometry of the window before
    164 		it is actually showing, so the negative geometry with
    165 		titlebar doean't work right. Hardcoded the window size
    166 		to 137x60
    167 
    168    06/02/98     Adding display of full string returned by server error.
    169                 Fixed close error, I was returning the wrong thing from
    170 		delete_event.
    171    
    172    06/01/98     Adding seperate Window Class names to the different
    173                 windows so that the user can individually control the
    174 		title bar, move/resize, etc. As suggested by Michael
    175 		J. Hammel
    176 
    177    05/31/98     Adding support for -geometry, apparently this has not
    178                 been added to GTK+ itself yet, so I have to do it
    179 		myself, using XParseGeometry. Works Great!
    180 		When geometry is passed a '- it needs to be relative
    181 		to the other corner of the window, not the upper
    182 		left. Fixed.
    183 
    184    05/28/98	Bug with eject on exit. It needs to send the current
    185    		state to the cd_control process when it reads the
    186    		config file. FIXED.
    187    		Need to add dropdown list for categories in edit. DONE.
    188 
    189    05/27/98     More finishing touches <G>.
    190                 Added selection of next track in the track window when
    191 		it goes to the next track. Looks nicer.
    192 		Fixed a bug with multiple DTITLE lines.
    193 
    194 		NEED TO DO:
    195 		x 1. Build local cddb directories sometime (either under
    196 		     the setup menu, or the first time it writes,
    197 		     or at compile time). The best would be when it
    198 		     tries to write to a non-existant directory, create
    199 		     the structure needed.
    200 		     Works. It creates only the category directory
    201 		     needed and the root cddb directory if it doesn't
    202 		     exist yet. This way the categories aren't limited
    203 		     to a static list.
    204 		x 2. Editing track names and saving to local database.
    205 		x 3. Sending edited track info to the database, or just
    206 		     a new discid for the CD. Requires email. Use the
    207 		     mail binary, but allow users to spec. it?
    208 		x 4. Allow user to select local .cddb support or internet
    209 		     so they can build their own database in the cddb
    210 		     format even if they have no inet connection.
    211 		  5. Clean up code. Add comments.
    212 		     There is a bunch of duplicated code that can be
    213 		     put into a subroutine with maybe 1 parameter
    214 		     passed to it. The clist updating routines are
    215 		     one.
    216 		x 6. Convert my usage of printf to g_print
    217 
    218    05/26/98     Finishing touches. Got the text window to work, thanks
    219                 to Owen. I had to add it to the notebook before
    220 		it is realized, otherwise it doesn't know who its
    221 		parent is (this is because text windows are changes as
    222 		you add to them).
    223 		Changed to main window to be a POPUP so that it has no
    224 		title bars. Keeps people from resizing it too!
    225 
    226    05/25/98     Problem with using device from main process -- we only
    227                 send 2 bytes to the cd_control process!! How am I going
    228 		to pass the new device? A 2 stage send? With the second
    229 		part being the new device?
    230 
    231 		Added CD_SET_DEVICE to cd_control.
    232 		Adding sending of it to read_config.
    233 		Switching cd device is now working!
    234 
    235 		Adding clear of display.tclist to no disc display points
    236 
    237 
    238    05/24/98     Adding support for inexact matches. CDDBD_INEX_LINE.
    239                 Inexact matching works!!! It saves it as the locally
    240 		calculated discid (recalculated just before the save).
    241 
    242 		x 1. Need to update the track list if its already open
    243 		     when the new data is downloaded from the server or
    244 		     read from disk.
    245 		x 2. Clear the track list and title when nodisc info is
    246 		     available (on change, use Track #, etc).
    247 		x 3. Click on a track in the track list to jump to
    248 		     that track directly.
    249 	       no  5. CD changer support...
    250 		x 6. Volume update is slow, needs to be called on click.
    251 
    252 		I've decided not to include changer support. Too hard to
    253 		integrate into the current GUI. And there probably isn't
    254 		a great need.
    255 
    256    05/23/98     The Help/Setup dialog is partially working. It still
    257                 complains when making the help text box, why?
    258 		The startup sequence isn't working right. Not playing
    259 		after closing the tray, or even when already closed.
    260 
    261 		I also don't like the startup pause for checking for the
    262 		current status. That should be handled by a startup
    263 		state machine in the status callback. May help solve the
    264 		startup play problem. Fixed. Status returned was NO_STATUS
    265 		which I was checking for some reason.
    266 
    267 		Software selectable eject on exit with no play is now
    268 		working correctly.
    269 
    270 		Server refresh is working as far as updating the internal
    271 		list but the list in the setup dialog isn't working. Also
    272 		changes to cddb and device are cross-polinated! It is
    273 		happening at the realloc in the device_edit and cddb_edit
    274 		routines. Pointer copied over somehow? realloc wrong?
    275 		typical bug. Assignment to wrong place.
    276 
    277 		Some of th things left to do:
    278 		x 1. refresh server listing after update from internet
    279 		     while dialog box is still open.
    280 		x 2. Status box showing the info while connected to the
    281 		     server.
    282 		x 3. Implement Inexact match
    283 		x    a. receive list of discids and titles from the server
    284 		        Add a new command to cddbd return value
    285 		x    b. Show a dialog box with the list of titles and have
    286 		        the user pick one or Cancel.
    287 		x    c. re-submit the query using the user selected discid
    288 		x 4. Saving to disk saves the wrong revision #, -1
    289 		o 5. Popup window when display is left clicked that shows
    290 		     and allows editing of title and tracks.
    291 		     showing title and track is working.
    292 		  6. Need to switch to using config.device and telling the
    293 		     cd_control process about it when we change it.
    294 		  7. Submitting a new disc to the database (via email?)
    295 
    296 
    297    05/22/98     Need to finish the help/setup menu window.
    298                 x Device (defaults to /dev/cdrom)
    299 		x Server List
    300 		x cddb support
    301 	       no changer support
    302 		x Eject at end?
    303 		x Startup behavior
    304 
    305 
    306 		Since I have the WM set to no titles and bars it makes it
    307 		hard to manipulate the setup screen! How about if I were
    308 		to extend the cdplayers window up or down (how to tell?)
    309 		and include the setup/server/tracklist data that way?
    310 		How do you delete things once you've packed them? Or can
    311 		you? 
    312 
    313 
    314    05/21/98     Right click on display window and dragging now works
    315                 fine for moving the window around.
    316 
    317 
    318    05/20/98     Blink isn't working. Fixed. Multiple places calling
    319                 update_display. FIXED.
    320 		Also need to add window dragging if right clicked in
    321 		the display area.
    322 		Need to add parallel tracklist window popup if right
    323 		click on either of the track digits (or area).
    324 
    325 		At startup we need to know the current state of the
    326 		CD before we go off and send a play command to it. FIXED.
    327 		When it has to close the door to play it doesn't start
    328 		playing automatically.
    329 		When it tries to play with no CD in the drive it doesn't
    330 		recover when you insert a disc and hit play.
    331 		Because fd_cd is < 0. Check for this.
    332 
    333 		There is a problem with setting the play button graphic
    334 		and state at the point where it is sent to control.
    335 		it it fails then the state/button is wrong! But if I
    336 		wait until it succeeds/fails then response time is
    337 		greatly delayed... Added code to make sure the button
    338 		state matched the display state and to change it if it
    339 		differs. Seems to work nicely. Shows playup until it
    340 		fails and then switches to stopup. WORKS WELL.
    341 
    342 		Eject doesn't work with status doing an init. Do an
    343 		initial init when cd_control is started. FIXED.
    344 
    345 		Dragging now sortof works. It loses track of the cursor
    346 		if its moved too fast, and won't ungrab (ick!)
    347 
    348    05/19/98     Read/Write config now works, needs to still be tested
    349                 with multiple servers though.
    350 
    351    05/18/98     Adding configuration writing and reading
    352 
    353    05/17/98     Found a chunck of startup code that was double-copied,
    354                 The player actually plays, ejects, etc. OK.
    355 		The volume display needs to update immediatly as does
    356 		the time display update when the key is pressed. Eject
    357 		should eject even when no cd has been opened.
    358 		At startup we should?
    359 		  Play automatically
    360 		  Show no disc and do nothing until play pressed
    361 		  Show total # of tracks and time remaining
    362 		All of these should be user selectable.
    363 		When a CD is opened for the first time (no discid stored
    364 		in cdinfo) we should query the database.
    365 		Clicking (right probably) on the display should pop up a
    366 		list of the tracks (editable).
    367 		There are some == NULL problems that need to be taken care
    368 		at starup, probably due to not realizing widgets first?
    369 		Blink isn't working right (doesn't blink)
    370 		Remaining on track counts up, not down!
    371 		Help/Setup dialog needs to be nicer looking, black to match
    372 		the look of the main box (how do I change the colors of
    373 		the dialogs?)
    374 
    375    05/16/98     Finishing up the cd control, adding get motd and get
    376                 server list from cddb.cddb.com
    377 		I really should add protocol support, but really, it
    378 		works fine like this...
    379 		OK, it 'looks like' the low level and server support is
    380 		now working okay. The code is a bit hard to read and
    381 		could probably use some macroing or other cleanups,
    382 		but I'll save that for later.
    383 
    384 		Now to start adding the GTK+ GUI stuff again and integrate
    385 		it with the data returning from the server and the CD
    386 		control process.
    387 
    388 		Well, the UI is sort of running. It complains a little,
    389 		and puts two 'NO DISC' pixmaps in the window, but it
    390 		starts at least!
    391 
    392    05/09/98     Added local cddb database support. It successfully reads
    393                 the disc info from the database.
    394 
    395    05/03/98     Restarted this whole project from the ground up. I am
    396                 writing the CD control and cddb support code first,
    397 		and then adding the GUI interface.
    398 
    399 		cd_control and cddb will be seperate processes forked
    400 		from the main process at startup.
    401 
    402    -------------------------------------------------------------------- */
    403 #include <stdio.h>
    404 #include <unistd.h>
    405 #include <string.h>
    406 #include <stdlib.h>
    407 #include <signal.h>
    408 #include <sys/types.h>
    409 #include <sys/wait.h>
    410 #include <gtk/gtk.h>
    411 #include <gdk/gdk.h>
    412 #include <gdk/gdkx.h>
    413 #include "cd_control.h"
    414 #include "cddbd.h"
    415 #include "cddb.h"
    416 #include "xfreecd.h"
    417 #include "xpm_button.h"
    418 
    419 
    420 /* Include all the bitmaps used (in xpm format) */
    421 #include "bitmaps/ffup.xpm"
    422 #include "bitmaps/ffdn.xpm"
    423 #include "bitmaps/rewup.xpm"
    424 #include "bitmaps/rewdn.xpm"
    425 #include "bitmaps/sfwddn.xpm"
    426 #include "bitmaps/srevdn.xpm"
    427 #include "bitmaps/stopup.xpm"
    428 #include "bitmaps/playup.xpm"
    429 #include "bitmaps/playdn.xpm"
    430 #include "bitmaps/pauseup.xpm"
    431 #include "bitmaps/ejup.xpm"
    432 #include "bitmaps/ejdn.xpm"
    433 #include "bitmaps/exitup.xpm"
    434 #include "bitmaps/exitdn.xpm"
    435 #include "bitmaps/helpup.xpm"
    436 #include "bitmaps/helpdn.xpm"
    437 #include "bitmaps/rptup.xpm"
    438 #include "bitmaps/rptdn.xpm"
    439 #include "bitmaps/rptupact.xpm"
    440 #include "bitmaps/volup.xpm"
    441 #include "bitmaps/voldn.xpm"
    442 #include "bitmaps/null.xpm"
    443 #include "bitmaps/nodisc.xpm"
    444 #include "bitmaps/plstrkup.xpm"
    445 #include "bitmaps/plstrkdn.xpm"
    446 #include "bitmaps/mnstrkup.xpm"
    447 #include "bitmaps/mnstrkdn.xpm"
    448 #include "bitmaps/plscdup.xpm"
    449 #include "bitmaps/plscddn.xpm"
    450 #include "bitmaps/mnscdup.xpm"
    451 #include "bitmaps/mnscddn.xpm"
    452 #include "bitmaps/minus.xpm"
    453 #include "bitmaps/bar.xpm"
    454 #include "bitmaps/redbar.xpm"
    455 #include "bitmaps/nobar.xpm"
    456 #include "bitmaps/a0.xpm"
    457 #include "bitmaps/a1.xpm"
    458 #include "bitmaps/a2.xpm"
    459 #include "bitmaps/a3.xpm"
    460 #include "bitmaps/a4.xpm"
    461 #include "bitmaps/a5.xpm"
    462 #include "bitmaps/a6.xpm"
    463 #include "bitmaps/a7.xpm"
    464 #include "bitmaps/a8.xpm"
    465 #include "bitmaps/a9.xpm"
    466 #include "bitmaps/an.xpm"
    467 #include "bitmaps/b0.xpm"
    468 #include "bitmaps/b1.xpm"
    469 #include "bitmaps/b2.xpm"
    470 #include "bitmaps/b3.xpm"
    471 #include "bitmaps/b4.xpm"
    472 #include "bitmaps/b5.xpm"
    473 #include "bitmaps/b6.xpm"
    474 #include "bitmaps/b7.xpm"
    475 #include "bitmaps/b8.xpm"
    476 #include "bitmaps/b9.xpm"
    477 #include "bitmaps/bn.xpm"
    478 
    479 
    480 #undef DEBUG1
    481 #undef DEBUG2
    482 #undef DEBUG3
    483 #undef DEBUG4
    484 #undef DEBUG5
    485 #undef DEBUG6
    486 #undef DEBUG7
    487 #undef DEBUG8
    488 #undef DEBUG9
    489 #undef MAIL_DEBUG
    490 
    491 #define	CDROM_NODISC	0
    492 #define CDROM_PLAYING	1
    493 #define CDROM_PAUSED	2
    494 
    495 #define TRACK_ELAPSED	0
    496 #define TRACK_REMAIN	1
    497 #define CD_ELAPSED	2
    498 #define CD_REMAIN	3
    499 
    500 
    501 #define HELP_TEXT( x )       gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, x, -1)
    502 #define COLOR_TEXT( x, y )   gtk_text_insert (GTK_TEXT (text), NULL, y, NULL, x, -1)
    503 
    504 int read_config( struct CONFIG * );
    505 int run_gtk();
    506 void free_config( struct CONFIG * );
    507 gint delete_event(GtkWidget *, gpointer);
    508 void destroy (GtkWidget *, gpointer);
    509 void cd_fd_read( gpointer, gint, GdkInputCondition);
    510 void cddbd_fd_read( gpointer, gint, GdkInputCondition);
    511 void ff_press(GtkWidget *, GdkEventButton *);
    512 void ff_release(GtkWidget *, GdkEventButton *);
    513 void rew_press(GtkWidget *, GdkEventButton *);
    514 void rew_release(GtkWidget *, GdkEventButton *);
    515 void play_press(GtkWidget *, GdkEventButton *);
    516 void play_release(GtkWidget *, GdkEventButton *);
    517 void eject_press(GtkWidget *, GdkEventButton *);
    518 void eject_release(GtkWidget *, GdkEventButton *);
    519 void ex_press(GtkWidget *, GdkEventButton *);
    520 void ex_release(GtkWidget *, GdkEventButton *);
    521 void help_press(GtkWidget *, GdkEventButton *);
    522 void help_release(GtkWidget *, GdkEventButton *);
    523 void tdisp_press(GtkWidget *, GdkEventButton *);
    524 void tdisp_release(GtkWidget *, GdkEventButton *);
    525 void rpt_press(GtkWidget *, GdkEventButton *);
    526 void rpt_release(GtkWidget *, GdkEventButton *);
    527 void vol_press(GtkWidget *, GdkEventButton *);
    528 void vol_release(GtkWidget *, GdkEventButton *);
    529 void set_vol( int amount );
    530 static int expose_display( GtkWidget *, GdkEventExpose *);
    531 gint update_cdrom( gpointer );
    532 void play_next();
    533 void play_previous();
    534 void pause_cdrom();
    535 void resume_cdrom();
    536 void play_track(int);
    537 void update_display();
    538 void stop_cdrom();
    539 void eject_cdrom();
    540 int cdrom_status();
    541 int write_config( struct CONFIG * );
    542 static void display_released (GtkWidget *);
    543 static void display_motion (GtkWidget *, GdkEventMotion *);
    544 static void display_pressed (GtkWidget *, GdkEventButton *);
    545 void wait_status();
    546 gint show_tracks();
    547 void set_eject( int );
    548 
    549 
    550 typedef struct _cursoroffset {gint x,y;} CursorOffset;
    551 
    552 /* Global Variables and structures */
    553 struct CDINFO cdinfo;
    554 struct CONFIG config;
    555  
    556 char          cmnd[2];
    557 int           cd_pid, cd_fd,            /* Communications with the CD control */
    558               cddbd_pid, cddbd_fd;      /* Communications with cd database */
    559 gint          gdk_cd_fd,
    560               gdk_cddbd_fd;
    561 
    562 
    563 static GdkWindow *root_win = NULL;
    564 
    565 
    566 struct _pbutton	ff,			/* Fast Forward button		*/
    567 		rew,			/* Rewind			*/
    568 		play,			/* Play				*/
    569 		vol,			/* Volume control		*/
    570 		eject,			/* Elect			*/
    571 		tdisp,			/* Time Display			*/
    572 		rpt,			/* Repeat			*/
    573 		ex,			/* Exit button			*/
    574 		help;			/* Help				*/
    575 
    576 /* Display pixmaps and current disc status */
    577 struct _display 
    578 {
    579   GtkWidget	*main_window;           /* The main window              */
    580   GtkWidget     *vbox;                  /* Vbox holding everything      */
    581   GtkWidget	*wid;			/* Widget for placing pixmaps	*/
    582   GtkWidget     *progress;              /* CDDBD progress dialog window */
    583   GtkWidget     *plabel;                /* Progress dialog label        */
    584   GtkWidget     *twindow;               /* Track Window pointer         */
    585   GtkWidget     *tclist;                /* List of tracks               */               
    586   GtkWidget     *ttitle;                /* Track list title widget      */
    587   GtkWidget     *tewindow;              /* Track Edit Window            */
    588   GtkWidget     *teentry;               /* Track Entry Window           */
    589   GtkWidget     *teclist;               /* Clist for Track Edit         */
    590   GtkWidget     *cb;                    /* ComboBox list of Servers     */
    591   GdkPixmap	*image;			/* image to paint onto		*/
    592   GdkPixmap	*null_pixmap;		/* Blank with : 		*/
    593   GdkBitmap	*null_mask;
    594   GdkPixmap	*nodisc_pixmap;		/* NO DISC			*/
    595   GdkBitmap	*nodisc_mask;
    596   GdkPixmap	*minus_pixmap;		/* Minus sign			*/
    597   GdkBitmap	*minus_mask;
    598   GdkPixmap	*playup_pixmap;		/* Play in white		*/
    599   GdkBitmap	*playup_mask;
    600   GdkPixmap	*playdn_pixmap;
    601   GdkBitmap	*playdn_mask;
    602   GdkPixmap	*pauseup_pixmap;	/* Pause bars in white		*/
    603   GdkBitmap	*pauseup_mask;
    604   GdkPixmap	*stopup_pixmap;		/* play and pause in gray	*/
    605   GdkBitmap	*stopup_mask;
    606   GdkPixmap     *sfwddn_pixmap;         /* Seek forward down button     */
    607   GdkBitmap     *sfwddn_mask;
    608   GdkPixmap     *srevdn_pixmap;         /* Seek reverse down button     */
    609   GdkBitmap     *srevdn_mask;
    610   GdkPixmap	*rptupact_pixmap;	/* repeat active (white)	*/
    611   GdkBitmap	*rptupact_mask;
    612   GdkPixmap	*plstrkup_pixmap;	/* Elapsed track time		*/
    613   GdkBitmap	*plstrkup_mask;
    614   GdkPixmap	*plstrkdn_pixmap;
    615   GdkBitmap	*plstrkdn_mask;
    616   GdkPixmap	*mnstrkup_pixmap;	/* Remaining track time		*/
    617   GdkBitmap	*mnstrkup_mask;
    618   GdkPixmap	*mnstrkdn_pixmap;
    619   GdkBitmap	*mnstrkdn_mask;
    620   GdkPixmap	*plscdup_pixmap;	/* Elapsed CD time		*/
    621   GdkBitmap	*plscdup_mask;
    622   GdkPixmap	*plscddn_pixmap;
    623   GdkBitmap	*plscddn_mask;
    624   GdkPixmap	*mnscdup_pixmap;	/* Remaining CD time		*/
    625   GdkBitmap	*mnscdup_mask;
    626   GdkPixmap	*mnscddn_pixmap;
    627   GdkBitmap	*mnscddn_mask;
    628   GdkPixmap	*bar_pixmap;		/* Volume Bargraph		*/
    629   GdkBitmap	*bar_mask;
    630   GdkPixmap	*redbar_pixmap;		/* Volume Bargraph		*/
    631   GdkBitmap	*redbar_mask;
    632   GdkPixmap	*nobar_pixmap;		/* Volume Bargraph		*/
    633   GdkBitmap	*nobar_mask;
    634 
    635   GdkPixmap	*a_pixmap[11];		/* Large digits			*/
    636   GdkBitmap	*a_mask[11];
    637   GdkPixmap	*b_pixmap[11];		/* Small Digits			*/
    638   GdkBitmap	*b_mask[11];
    639 
    640   GString       *tmp_category;          /* New Category entry */
    641   GString       *tmp_title;             /* Title storage for edit_tracks*/
    642   GString       *tmp_track[99];         /* Song names for editing       */
    643   int           tmp_row;                /* Current row in tmp_track     */
    644 
    645   short         startup;                /* Startup State machine        */
    646   short         cddb_lock;              /* Lock cddb access while busy  */
    647   int		status;			/* Current player status
    648   					   0 = no disc in drive
    649   					   1 = stopped
    650   					   2 = playing
    651   					   3 = paused
    652   					 */
    653   int		playbtn;		/* State of play button, same as
    654   					   above (use CDROM_ equates)
    655   					 */
    656   int		time;			/* Time mode			*/
    657   int		track,                  /* track # - 1 to end of CD     */
    658                 minute,
    659                 second;                 /* Info for the display		*/
    660   int		repeat;			/* Repeat status		*/
    661 } display;
    662 
    663 
    664 
    665 /* Signal handling code */
    666 static int caught_fatal_sig = 0;
    667 
    668 static void on_signal (int sig_num)
    669 {
    670   if (caught_fatal_sig)
    671 /*    raise (sig_num);*/
    672     kill (getpid (), sig_num);
    673   caught_fatal_sig = 1;
    674 
    675   switch (sig_num)
    676     {
    677     case SIGHUP:
    678       g_print("sighup caught\n");
    679       gdk_exit(1);
    680       break;
    681     case SIGINT:
    682       g_print("sigint caught\n");
    683       gdk_exit(1);
    684       break;
    685     case SIGQUIT:
    686       g_print("sigquit caught\n");
    687       gdk_exit(1);
    688       break;
    689    case SIGABRT:
    690       g_print("sigabrt caught\n");
    691       gdk_exit(1);
    692       break;
    693     case SIGBUS:
    694       g_print("sigbus caught\n");
    695       destroy(NULL,NULL);
    696       break;
    697     case SIGSEGV:
    698       g_print("sigsegv caught\n");
    699       destroy(NULL,NULL);
    700       break;
    701     case SIGPIPE:
    702       g_print("sigpipe caught\n");
    703       gdk_exit(1);
    704       break;
    705     case SIGTERM:
    706       g_print("sigterm caught\n");
    707       gdk_exit(1);
    708       break;
    709     case SIGFPE:
    710       g_print("sigfpe caught\n");
    711       destroy(NULL,NULL);
    712       break;
    713     default:
    714       g_print("unknown signal\n");
    715       destroy(NULL,NULL);
    716       break;
    717     }
    718 }
    719 
    720 static void on_sig_child (int sig_num)
    721 {
    722   int pid;
    723   int status;
    724 
    725   while (1)
    726     {
    727       pid = waitpid (WAIT_ANY, &status, WNOHANG);
    728       if (pid <= 0)
    729         break;
    730     }
    731 }
    732 
    733 
    734 /* ------------------------------------------------------------------------
    735    Where it all begins...
    736    ------------------------------------------------------------------------ */
    737 int main( int argc, char *argv[] )
    738 {
    739   int    x,
    740          x_ret,
    741          y_ret,
    742          w_ret,
    743          h_ret,
    744          stat=0;
    745 
    746   bzero( &cdinfo, sizeof( struct CDINFO ) );
    747 
    748   /* Start up the CD control process */
    749   if( ( cd_fd = start_cd_control( &cd_pid ) ) < 0 )
    750     {
    751       perror("start_cd_control error");
    752       exit(-1);
    753     }
    754 
    755   /* Start up the CDDBD control process */
    756   if( ( cddbd_fd = start_cddbd( &cddbd_pid ) ) < 0 )
    757     {
    758       perror("start_cd_dbd error");
    759       exit(-1);
    760     }
    761 
    762   /* Startup GTK */
    763   gtk_init (&argc, &argv);
    764 
    765   /* Tell gdk to watch the cd control and cddb pipes for reading */
    766   gdk_cd_fd = gdk_input_add( cd_fd, GDK_INPUT_READ, cd_fd_read, 0 );
    767   gdk_cddbd_fd = gdk_input_add( cddbd_fd, GDK_INPUT_READ, cddbd_fd_read, 0 );
    768 
    769   /* Get the -geometry parameters */
    770   for( x = 0; x < argc; x++ )
    771     {
    772       if( (strcasecmp( argv[x], "-geometry" ) == 0) || (strcasecmp( argv[x], "-geom" ) == 0) )
    773 	{
    774 	if( argv[x+1] != NULL )
    775 	  {
    776 #ifdef DEBUG7
    777 	    g_print("Passing %s to XParseGeometry\n", argv[x+1]);
    778 #endif
    779 
    780 	    stat = XParseGeometry( argv[x+1], &x_ret, &y_ret, &w_ret, &h_ret );
    781 
    782 #ifdef DEBUG7
    783 	    g_print("%d = XParseGeometry( s, %d, %d, %d, %d )\n",
    784                     stat, x_ret, y_ret, w_ret, h_ret );
    785 #endif
    786 	  }
    787 	}
    788     }
    789 
    790  /* Handle some signals */
    791   signal (SIGHUP, on_signal);
    792   signal (SIGINT, on_signal);
    793   signal (SIGQUIT, on_signal);
    794   signal (SIGABRT, on_signal);
    795   signal (SIGBUS, on_signal);
    796   signal (SIGSEGV, on_signal);
    797   signal (SIGPIPE, on_signal);
    798   signal (SIGTERM, on_signal);
    799   signal (SIGFPE, on_signal);
    800   
    801   /* Handle child exits */
    802   signal (SIGCHLD, on_sig_child);
    803 
    804   run_gtk( stat, x_ret, y_ret );
    805 
    806   gdk_input_remove( gdk_cd_fd );
    807   gdk_input_remove( gdk_cddbd_fd );
    808 
    809   /* Tell cd_control to quit */
    810   cmnd[0] = CD_QUIT;
    811   cmnd[1] = 0;
    812   if( write( cd_fd, &cmnd, 2 ) < 0 )
    813     {
    814       perror("write to cd_control error");
    815       close( cd_fd );
    816       exit(-1);
    817     }
    818 
    819   /* Tell cddbd to quit */
    820   cdinfo.cddbd_cmnd = DB_QUIT;
    821   if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO ) ) < 0 )
    822     {
    823       perror("write to cddbd error");
    824       close( cd_fd );
    825       close( cddbd_fd );
    826       exit(-1);
    827     }
    828 
    829   close( cd_fd );
    830   close( cddbd_fd );
    831   return(0);
    832 }
    833 
    834 
    835 
    836 /* ----- subroutines follow ------ */
    837 
    838 
    839 void send_device()
    840 {
    841   char  buffer[80];
    842 
    843   /* Send the CD device to the cd_control process */
    844   buffer[0] = CD_SET_DEVICE;
    845   buffer[1] = 0;
    846   if( write( cd_fd, buffer, 2 ) < 0 )
    847     {
    848       perror("write to cd_control error - 1a");
    849     }
    850 
    851   sprintf( buffer, "%s\n", config.device );
    852   if( write( cd_fd, buffer, strlen(buffer) ) < 0 )
    853     {
    854       perror("write to cd_control error - 1b");
    855     }
    856 }
    857 
    858 
    859 /* ------------------------------------------------------------------------
    860    Read the configuration file from $HOME/.xfreecdrc
    861 
    862    Return -2 if a malloc fails
    863    ------------------------------------------------------------------------ */
    864 int read_config( struct CONFIG *config )
    865 {
    866   FILE  *fp;
    867   char  fname[1024],
    868         line[80],
    869         *p;
    870   struct SITE *sp, *sp2;
    871 
    872   if( ( p = getenv( "HOME" ) ) == NULL )
    873     {
    874       g_print("Cannot find HOME enviornmental variable\n");
    875       return(-1);
    876     }
    877 
    878   strncpy( fname, p, 1023 );
    879   if( fname[strlen(fname)-1] != '/' )
    880     strcat( fname, "/" );
    881   strcat( fname, ".xfreecdrc" );
    882 
    883   if( ( fp = fopen( fname, "rb" ) ) == NULL )
    884     {
    885       /* Setup reasonable defaults */
    886 
    887       /* Setup for the default device (/dev/cdrom) */
    888       if( ( config->device = (char *) malloc( strlen(DEFAULT_DEVICE)+1 ) ) == NULL )
    889 	{
    890 	  return(-2);
    891 	}
    892       strcpy( config->device, DEFAULT_DEVICE );
    893 
    894       /* Get some memory for the path to the local database.
    895 	 By default I put this in the user's home directory ~/.cddb
    896       */
    897       if( ( config->local_cddb = (char *) malloc( strlen(DEFAULT_CDDB_PATH)+1 ) ) == NULL )
    898 	{
    899 	  return(-2);
    900 	}
    901       strcpy( config->local_cddb, DEFAULT_CDDB_PATH );
    902       strcpy( cdinfo.local_cddb, config->local_cddb );
    903 
    904      /* Get some memory for the cddbd email address */
    905       if( ( config->to_cddbd = (char *) malloc( strlen(DEFAULT_TO_CDDBD)+1 ) ) == NULL )
    906 	{
    907 	  return(-2);
    908 	}
    909       strcpy( config->to_cddbd, DEFAULT_TO_CDDBD );
    910 
    911       /* Get some memory for the SITE */
    912       if( ( config->server = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
    913 	{
    914 	  return(-2);
    915 	}
    916 
    917       bzero( config->server, sizeof(struct SITE) );
    918       
    919        /* Fill in the site with cddb.cddb.com */
    920       if( ( config->server->name = (char *) malloc( strlen(DEFAULT_SERVER)+1 ) ) == NULL )
    921 	{
    922 	  return(-2);
    923 	}
    924       strcpy( config->server->name, DEFAULT_SERVER );
    925       config->server->port = 888;
    926       config->server->next = NULL;
    927 
    928       /* Set current server to the default */
    929       if( ( config->current = (char *) malloc( strlen(DEFAULT_SERVER)+1 ) ) == NULL )
    930 	{
    931 	  return(-2);
    932 	}
    933       strcpy( config->current, DEFAULT_SERVER );
    934 
    935       config->done_eject = 0;          /* Eject when done playing */
    936       config->exit_eject = 0;          /* Eject on exit when not playing */
    937       config->startup = 1;             /* Play at startup */
    938       config->cddb = 0;
    939       config->changer = 0;
    940       config->saved = 0;               /* Needs to be saved */
    941 
    942       /* Write this as the default */
    943       return( write_config( config ) );
    944     }
    945 
    946   /* Process the lines in the file */
    947   while( fgets( line, 80, fp ) != NULL )
    948     {
    949       if( (line[0] == '#') || (line[0] == '\n') )
    950 	continue;
    951 
    952       p = strtok( line, "= \n" );
    953       if( strcmp( p, "DEVICE" ) == 0 )
    954 	{
    955 	  p = strtok( NULL, "= \n" );
    956 	  /* Get some memory for the device */
    957 	  if( ( config->device = (char *) malloc( strlen(p)+1 ) ) == NULL )
    958 	    {
    959 	      fclose( fp );
    960 	      return(-2);
    961 	    }
    962 	  strcpy( config->device, p );
    963 
    964 	  continue;
    965 	}
    966 
    967       if( strcmp( p, "LOCAL_CDDB" ) == 0 )
    968 	{
    969 	  p = strtok( NULL, "= \n" );
    970 	  /* Get some memory for the path to the local database.
    971 	  */
    972 	  if( ( config->local_cddb = (char *) malloc( strlen(p)+1 ) ) == NULL )
    973 	    {
    974 	      fclose( fp );
    975 	      return(-2);
    976 	    }
    977 	  strcpy( config->local_cddb, p );
    978 	  strcpy(  cdinfo.local_cddb, p );
    979 	  continue;
    980 	}
    981 
    982       if( strcmp( p, "TO_CDDBD" ) == 0 )
    983 	{
    984 	  p = strtok( NULL, "= \n" );
    985 	  /* Get some memory for the path to the local database.
    986 	  */
    987 	  if( ( config->to_cddbd = (char *) malloc( strlen(p)+1 ) ) == NULL )
    988 	    {
    989 	      fclose( fp );
    990 	      return(-2);
    991 	    }
    992 	  strcpy( config->to_cddbd, p );
    993 	  continue;
    994 	}
    995 
    996       if( strcmp( p, "SERVER" ) == 0 )
    997 	{
    998 	  p = strtok( NULL, "=: \n" );
    999 
   1000 	  /* Reserve some space for this server, add it to the end of the list */
   1001 	  if( ( sp = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
   1002 	    {
   1003 	      return(-2);
   1004 	    }
   1005 	  bzero( sp, sizeof(struct SITE) );
   1006       
   1007 	  /* Reserve space for the name */
   1008 	  if( ( sp->name = (char *) malloc( strlen(p)+1 ) ) == NULL )
   1009 	    {
   1010 	      return(-2);
   1011 	    }
   1012 	  strcpy( sp->name, p );
   1013 
   1014 	  p = strtok( NULL, "=: \n" );
   1015 	  sp->port = atoi(p);
   1016 	  sp->next = NULL;
   1017 
   1018 	  /* Is it the first in the list? */
   1019 	  if( config->server == NULL )
   1020 	    {
   1021 	      /* Yep, First in the list */
   1022 	      config->server = sp;
   1023 	    } else {
   1024 	      /* Walk to the end of the list (no cuts!!) */
   1025 	      sp2 = config->server;
   1026 	      while( sp2->next != NULL )
   1027 		{
   1028 		  sp2 = sp2->next;
   1029 		}
   1030 
   1031 	      /* Add the new site to the end of the list */
   1032 	      sp2->next = sp;
   1033 	    }
   1034 
   1035 	  continue;
   1036 	}
   1037 
   1038       if( strcmp( p, "CURRENT" ) == 0 )
   1039 	{
   1040 	  p = strtok( NULL, "= \n" );
   1041 
   1042 	  /* Get some memory for the current server name */
   1043 	  if( ( config->current = (char *) malloc( strlen(p)+1 ) ) == NULL )
   1044 	    {
   1045 	      fclose( fp );
   1046 	      return(-2);
   1047 	    }
   1048 	  strcpy( config->current, p );
   1049 	  continue;
   1050 	}
   1051 
   1052       if( strcmp( p, "DONE_EJECT" ) == 0 )
   1053 	{
   1054 	  p = strtok( NULL, "= \n" );
   1055 	  config->done_eject = atoi( p );
   1056 	  continue;
   1057 	}
   1058 
   1059       if( strcmp( p, "EXIT_EJECT" ) == 0 )
   1060 	{
   1061 	  p = strtok( NULL, "= \n" );
   1062 	  config->exit_eject = atoi( p );
   1063 	  set_eject( config->exit_eject );          /* Tell the CD_CONTROL device about state */
   1064 	  continue;
   1065 	}
   1066 
   1067       if( strcmp( p, "STARTUP" ) == 0 )
   1068 	{
   1069 	  p = strtok( NULL, "= \n" );
   1070 	  config->startup = atoi( p );
   1071 	  continue;
   1072 	}
   1073 
   1074       if( strcmp( p, "CDDB" ) == 0 )
   1075 	{
   1076 	  p = strtok( NULL, "= \n" );
   1077 	  config->cddb = atoi( p );
   1078 	  continue;
   1079 	}
   1080 
   1081       if( strcmp( p, "CHANGER" ) == 0 )
   1082 	{
   1083 	  p = strtok( NULL, "= \n" );
   1084 	  config->changer = atoi( p );
   1085 	  continue;
   1086 	}
   1087 
   1088 
   1089     }
   1090   fclose( fp );
   1091 
   1092 
   1093   /* 
   1094      I've added config->to_cddbd since last release, so if it wasn't read from the
   1095      rcfile give it a default value so people don't need to erase their old rcfile
   1096   */
   1097   if( config->to_cddbd == NULL )
   1098     {
   1099       if( ( config->to_cddbd = (char *) malloc( strlen( DEFAULT_TO_CDDBD ) + 1 ) ) == NULL )
   1100 	{
   1101 	  fclose( fp );
   1102 	  return(-2);
   1103 	}
   1104       strcpy( config->to_cddbd, DEFAULT_TO_CDDBD );
   1105     }
   1106   
   1107   config->saved = 1;            /* Just read it, same as saved file */
   1108 
   1109   return 0;
   1110 }
   1111 
   1112 
   1113 /* ------------------------------------------------------------------------
   1114    Write the configuration file to $HOME/.xfreecdrc
   1115    ------------------------------------------------------------------------ */
   1116 int write_config( struct CONFIG *config )
   1117 {
   1118   FILE  *fp;
   1119   char  fname[1024],
   1120         *p;
   1121   struct SITE  *sp;
   1122 
   1123   /* Already saved, no need to save it again */
   1124   if( config->saved == 1 )
   1125     return(0);
   1126 
   1127   if( ( p = getenv( "HOME" ) ) == NULL )
   1128     {
   1129       g_print("Cannot find HOME enviornmental variable\n");
   1130       return(-1);
   1131     }
   1132 
   1133   strncpy( fname, p, 1023 );
   1134   if( fname[strlen(fname)-1] != '/' )
   1135     strcat( fname, "/" );
   1136   strcat( fname, ".xfreecdrc" );
   1137 
   1138   if( ( fp = fopen( fname, "wb" ) ) == NULL )
   1139     {
   1140       return(-2);
   1141     }
   1142 
   1143   fprintf( fp, "# XfreeCD v%s configuration file\n", VERSION );
   1144   fprintf( fp, "#\n");
   1145   fprintf( fp, "DEVICE=%s\n", config->device );
   1146   fprintf( fp, "LOCAL_CDDB=%s\n", config->local_cddb );
   1147   fprintf( fp, "TO_CDDBD=%s\n", config->to_cddbd );
   1148 
   1149   /* Walk down the list of servers */
   1150   sp = config->server;
   1151   while( sp != NULL )
   1152     {
   1153       fprintf( fp, "SERVER=%s:%d\n", sp->name, sp->port);
   1154       sp = sp->next;
   1155     }
   1156 
   1157   fprintf( fp, "CURRENT=%s\n", config->current );
   1158   fprintf( fp, "DONE_EJECT=%d\n", config->done_eject );
   1159   fprintf( fp, "EXIT_EJECT=%d\n", config->exit_eject );
   1160   fprintf( fp, "STARTUP=%d\n", config->startup );
   1161   fprintf( fp, "CDDB=%d\n", config->cddb );
   1162   fprintf( fp, "CHANGER=%d\n", config->changer );
   1163 
   1164   fclose( fp );
   1165 
   1166   config->saved = 1;            /* config is now same as saved on disk */
   1167 
   1168   return 0;
   1169 }
   1170 
   1171 
   1172 
   1173 /* -----------------------------------------------------------------------
   1174    Free up the info used in the cdinfo structure
   1175    ----------------------------------------------------------------------- */
   1176 void free_cdinfo()
   1177 {
   1178   int  x;
   1179 
   1180 #ifdef DEBUG8
   1181   g_print("Freeing any used extended data\n" );
   1182 #endif
   1183 
   1184   if( cdinfo.title != NULL )
   1185     {
   1186       g_string_free( cdinfo.title, 1 );
   1187       cdinfo.title = NULL;
   1188     }
   1189 
   1190   if( cdinfo.category != NULL )
   1191     {
   1192       g_string_free( cdinfo.category, 1 );
   1193       cdinfo.category = NULL;
   1194     }
   1195 
   1196   for( x = 0; x < 99; x++ )
   1197     {
   1198       if( cdinfo.name[x] != NULL )
   1199 	{
   1200 	  g_string_free( cdinfo.name[x], 1 );
   1201 	  cdinfo.name[x] = NULL;
   1202 	}
   1203     }
   1204 
   1205 
   1206   if( cdinfo.extd != NULL )
   1207     {
   1208       g_string_free( cdinfo.extd, 1 );
   1209       cdinfo.extd = NULL;
   1210     }
   1211 
   1212   for( x = 0; x < 99; x++ )
   1213     {
   1214       if( cdinfo.extt[x] != NULL )
   1215 	{
   1216 	  g_string_free( cdinfo.extt[x], 1 );
   1217 	  cdinfo.extt[x] = NULL;
   1218 	}
   1219     }
   1220 
   1221   /* Clean up the display's temporary storage */
   1222   if( display.tmp_category != NULL )
   1223     {
   1224       g_string_free( display.tmp_category, 1 );
   1225       display.tmp_category = NULL;
   1226     }
   1227 
   1228   if( display.tmp_title != NULL )
   1229     {
   1230       g_string_free( display.tmp_title, 1 );
   1231       display.tmp_title = NULL;
   1232     }
   1233 
   1234   for( x = 0; x < 99; x++ )
   1235     {
   1236       if( display.tmp_track[x] != NULL )
   1237 	{
   1238 	  g_string_free( display.tmp_track[x], 1 );
   1239 	  display.tmp_track[x] = NULL;
   1240 	}
   1241     }  
   1242 }
   1243 
   1244 
   1245 /* -----------------------------------------------------------------------
   1246    Free up any memory used by the config structure to store the paths
   1247    and site names.
   1248    ----------------------------------------------------------------------- */
   1249 void free_config( struct CONFIG *config )
   1250 {
   1251   struct SITE *sp;
   1252 
   1253   /* Need to free up memory usage */
   1254   free( config->local_cddb );
   1255   free( config->device );
   1256   free( config->to_cddbd );
   1257 
   1258   /* Walk the server list, freeing up memory */
   1259   while( config->server != NULL )
   1260     {
   1261       sp = config->server;
   1262       config->server = config->server->next;
   1263       free( sp->name );
   1264       free( sp );
   1265     }
   1266 
   1267   /* Free up the dynamically allocated strings in cdinfo */
   1268   free_cdinfo();
   1269 }
   1270 
   1271 
   1272 
   1273 /* -----------------------------------------------------------------------
   1274    Load the CD info from the local database or set defaults
   1275    ----------------------------------------------------------------------- */
   1276 void local_cddb()
   1277 {
   1278 
   1279 #ifdef DEBUG1
   1280   g_print("Loading local Database\n");
   1281 #endif
   1282 
   1283   /* No internet support, just try and read the local DB */
   1284   if( read_cddb( &cdinfo ) < 0 )
   1285     {
   1286 
   1287 #ifdef DEBUG1
   1288       g_print("Not found on disk, defaulting\n");
   1289 #endif
   1290 
   1291       /* Unknown title */
   1292       if( cdinfo.title == NULL )
   1293 	cdinfo.title = g_string_new( "Unknown CD" );
   1294       else
   1295 	cdinfo.title = g_string_assign( cdinfo.title, "Unknown CD" );
   1296 
   1297       if( cdinfo.category == NULL )
   1298 	cdinfo.category = g_string_new( "misc" );
   1299       else
   1300 	cdinfo.category = g_string_assign( cdinfo.category, "misc" );
   1301 
   1302       /* Set a default revision, so they can edit the list */
   1303       cdinfo.revision = 0;
   1304     }	      
   1305 
   1306 #ifdef DEBUG9
   1307   g_print("local_cddb() revision = %d\n", cdinfo.revision );
   1308 #endif
   1309 
   1310 }
   1311 
   1312 
   1313 /* -----------------------------------------------------------------------
   1314    The Main GTK setup routine
   1315    ----------------------------------------------------------------------- */
   1316 int run_gtk( int stat, int x_ret, int y_ret )
   1317 {
   1318   GtkWidget	*window,
   1319                 *button,
   1320   	        *hbox1,
   1321   		*hbox2,
   1322         	*table;
   1323   GtkTooltips	*tips;
   1324   GtkStyle	*style;
   1325   GdkColor	tip_color;
   1326   GdkColormap	*colormap;      
   1327   gint          timer;
   1328   CursorOffset  *icon_pos;
   1329   int           x_pos, 
   1330                 y_pos;
   1331 
   1332   bzero( &config, sizeof( struct CONFIG ) );
   1333   bzero( &display, sizeof( struct _display ) );
   1334   bzero( &cdinfo, sizeof( struct CDINFO ) );
   1335 
   1336   cdinfo.revision = -1;                /* Initalize the revision number */
   1337 
   1338   /* Copyright info, version info */
   1339   g_print("XfreeCD v%s\n", VERSION );
   1340   g_print("Copyright 1998 by Brian C. Lane\n<http://www.tatoosh.com/nexus>\n\n");
   1341 
   1342   root_win = gdk_window_foreign_new (GDK_ROOT_WINDOW ());
   1343 
   1344   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   1345   display.main_window = window;
   1346   gtk_container_border_width( GTK_CONTAINER( window ), 0 );
   1347 
   1348   /* Give it a name */
   1349   gtk_window_set_title (GTK_WINDOW (window), "XfreeCD");
   1350   gtk_window_set_wmclass(GTK_WINDOW(window), "XfreeCD", NULL );
   1351 
   1352   /* when the window is given the "delete_event" signal (this is given
   1353    * by the window manager (usually the 'close' option, or on the
   1354    * titlebar), we ask it to call the delete_event () function
   1355    * as defined above.  The data passed to the callback
   1356    * function is NULL and is ignored in the callback. */
   1357   gtk_signal_connect (GTK_OBJECT (window), "delete_event",
   1358 		      GTK_SIGNAL_FUNC (delete_event), NULL);
   1359   
   1360   /* here we connect the "destroy" event to a signal handler.  
   1361    * This event occurs when we call gtk_widget_destroy() on the window,
   1362    * or if we return 'FALSE' in the "delete_event" callback. */
   1363   gtk_signal_connect (GTK_OBJECT (window), "destroy",
   1364 		      GTK_SIGNAL_FUNC (destroy), NULL);
   1365 
   1366   gtk_widget_realize(window);
   1367 
   1368   /* Start up a new list of tool tips */
   1369   tips = gtk_tooltips_new();
   1370 
   1371   /* Show the tooltips after the cursor has paused for 2 seconds */
   1372   gtk_tooltips_set_delay( tips, 2000 );
   1373 
   1374   /*
   1375    * This section fills in all the data structures used by the xpm_button
   1376    * subroutine call to create buttons.
   1377    * Eventually this will be more automatic/cleaner/maybe a widget?
   1378    */
   1379    
   1380   ff.up_xpm = (gchar *) ffup_xpm;
   1381   ff.dn_xpm = (gchar *) ffdn_xpm;
   1382   ff.press = &ff_press;
   1383   ff.release = &ff_release;
   1384 
   1385   rew.up_xpm = (gchar *) rewup_xpm;
   1386   rew.dn_xpm = (gchar *) rewdn_xpm;
   1387   rew.press = &rew_press;
   1388   rew.release = &rew_release;
   1389   
   1390   play.up_xpm = (gchar *) stopup_xpm;
   1391   play.dn_xpm = (gchar *) playdn_xpm;
   1392   play.press = &play_press;
   1393   play.release = &play_release;
   1394   
   1395   eject.up_xpm = (gchar *) ejup_xpm;
   1396   eject.dn_xpm = (gchar *) ejdn_xpm;
   1397   eject.press = &eject_press;
   1398   eject.release = &eject_release;
   1399 
   1400   ex.up_xpm = (gchar *) exitup_xpm;
   1401   ex.dn_xpm = (gchar *) exitdn_xpm;
   1402   ex.press = &ex_press;
   1403   ex.release = &ex_release;
   1404   
   1405   help.up_xpm = (gchar *) helpup_xpm;
   1406   help.dn_xpm = (gchar *) helpdn_xpm;
   1407   help.press = &help_press;
   1408   help.release = &help_release;
   1409 
   1410   tdisp.up_xpm = (gchar *) plstrkup_xpm;
   1411   tdisp.dn_xpm = (gchar *) plstrkdn_xpm;
   1412   tdisp.press = &tdisp_press;
   1413   tdisp.release = &tdisp_release;
   1414   
   1415   rpt.up_xpm = (gchar *) rptup_xpm;
   1416   rpt.dn_xpm = (gchar *) rptdn_xpm;
   1417   rpt.press = &rpt_press;
   1418   rpt.release = &rpt_release;
   1419   
   1420   vol.up_xpm = (gchar *) volup_xpm;
   1421   vol.dn_xpm = (gchar *) voldn_xpm;
   1422   vol.press = &vol_press;
   1423   vol.release = &vol_release;
   1424   
   1425   
   1426   /* 
   1427    *  The XfreeCD window consists of:
   1428    *    one vertical box that contains:
   1429    *    Two vertical boxes
   1430    *    The top one with hbox of the display and right justified, a 3x2 table
   1431    *    The bottom one with a hbox of 3 buttons
   1432    */
   1433 
   1434   /* Make a vbox to hold our hboxes */
   1435   display.vbox = gtk_vbox_new( FALSE, 0 );
   1436   gtk_container_add( GTK_CONTAINER( window ), display.vbox );
   1437   gtk_widget_show( display.vbox );
   1438 
   1439   /* Make a hbox to hold the bottom row of buttons REW/PLAY/FF */    
   1440   hbox1 = gtk_hbox_new( FALSE, 0 );
   1441   gtk_box_pack_end( GTK_BOX( display.vbox ), hbox1, FALSE, FALSE, 0 );
   1442   gtk_widget_show( hbox1 );
   1443 
   1444   /* Make a hbox to put the table into */
   1445   hbox2 = gtk_hbox_new( FALSE, 0 );
   1446   gtk_box_pack_start( GTK_BOX( display.vbox ), hbox2, FALSE, FALSE, 0 );
   1447   gtk_widget_show( hbox2 );  
   1448 
   1449   /* Make the xpm_buttons and add the tooltips for each one */
   1450   button = xpm_button( window, &rew );
   1451   gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
   1452   gtk_widget_show( button );
   1453   gtk_tooltips_set_tip( tips, button, "Previous Track", "ContextHelp/buttons/Prev" );
   1454 
   1455   button = xpm_button( window, &play );
   1456   gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
   1457   gtk_widget_show( button );
   1458   gtk_tooltips_set_tip( tips, button, "Play/Pause", "ContextHelp/buttons/Play" );
   1459 
   1460   button = xpm_button( window, &ff );
   1461   gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 0 );
   1462   gtk_widget_show( button );
   1463   gtk_tooltips_set_tip( tips, button, "Next Track", "ContextHelp/buttons/Next" );
   1464   
   1465   /* Make a table for the 6 control buttons */
   1466   table = gtk_table_new( 2, 3, TRUE );
   1467 
   1468   /* Add the table to the end of the hbox (the display goes first) */
   1469   gtk_box_pack_end( GTK_BOX( hbox2 ), table, FALSE, FALSE, 0 );
   1470 
   1471   /* Make sure it is showing */
   1472   gtk_widget_show( table );
   1473   
   1474   button = xpm_button( window, &vol );
   1475   gtk_table_attach( GTK_TABLE( table ), button, 0, 1, 0, 1, 0, 0, 0, 0 );
   1476   gtk_widget_show( button );
   1477   gtk_tooltips_set_tip( tips, button, "Volume", "ContextHelp/buttons/Volume" );
   1478   
   1479   button = xpm_button( window, &tdisp );
   1480   gtk_table_attach( GTK_TABLE( table ), button, 1, 2, 0, 1, 0, 0, 0, 0 );
   1481   gtk_widget_show( button );
   1482   gtk_tooltips_set_tip( tips, button, "Time Display", "ContextHelp/buttons/Time" );
   1483   
   1484   button = xpm_button( window, &ex );
   1485   gtk_table_attach( GTK_TABLE( table ), button, 2, 3, 0, 1, 0, 0, 0, 0 );
   1486   gtk_widget_show( button );
   1487   gtk_tooltips_set_tip( tips, button, "Exit", "ContextHelp/buttons/Exit" );
   1488   
   1489   button = xpm_button( window, &rpt );
   1490   gtk_table_attach( GTK_TABLE( table ), button, 0, 1, 1, 2, 0, 0, 0, 0 );
   1491   gtk_widget_show( button );
   1492   gtk_tooltips_set_tip( tips, button, "Repeat", "ContextHelp/buttons/Repeat" );
   1493   
   1494   button = xpm_button( window, &eject );
   1495   gtk_table_attach( GTK_TABLE( table ), button, 1, 2, 1, 2, 0, 0, 0, 0 );
   1496   gtk_widget_show( button );
   1497   gtk_tooltips_set_tip( tips, button, "Eject", "ContextHelp/buttons/Eject" );
   1498   
   1499   button = xpm_button( window, &help );
   1500   gtk_table_attach( GTK_TABLE( table ), button, 2, 3, 1, 2, 0, 0, 0, 0 );
   1501   gtk_widget_show( button );
   1502   gtk_tooltips_set_tip( tips, button, "Help", "ContextHelp/buttons/Help" );
   1503   
   1504 
   1505   /* Get the window's style info */
   1506   style = gtk_widget_get_style( window );
   1507 
   1508   /* Load all the other pixmaps (some are duplicates) */
   1509   display.null_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1510   						    &display.null_mask,
   1511   						    &style->bg[GTK_STATE_NORMAL],
   1512   						    (gchar **) null_xpm );
   1513   
   1514   display.nodisc_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1515   						    &display.nodisc_mask,
   1516   						    &style->bg[GTK_STATE_NORMAL],
   1517   						    (gchar **) nodisc_xpm );
   1518   
   1519   display.minus_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1520   						    &display.minus_mask,
   1521   						    &style->bg[GTK_STATE_NORMAL],
   1522   						    (gchar **) minus_xpm );
   1523 
   1524   display.playup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1525   						    &display.playup_mask,
   1526   						    &style->bg[GTK_STATE_NORMAL],
   1527   						    (gchar **) playup_xpm );
   1528 
   1529   display.playdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1530   						    &display.playdn_mask,
   1531   						    &style->bg[GTK_STATE_NORMAL],
   1532   						    (gchar **) playdn_xpm );
   1533 
   1534   display.pauseup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1535   						    &display.pauseup_mask,
   1536   						    &style->bg[GTK_STATE_NORMAL],
   1537   						    (gchar **) pauseup_xpm );
   1538 
   1539   display.stopup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1540   						    &display.stopup_mask,
   1541   						    &style->bg[GTK_STATE_NORMAL],
   1542   						    (gchar **) stopup_xpm );
   1543 
   1544   display.sfwddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1545   						    &display.sfwddn_mask,
   1546   						    &style->bg[GTK_STATE_NORMAL],
   1547   						    (gchar **) sfwddn_xpm );
   1548 
   1549   display.srevdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1550   						    &display.srevdn_mask,
   1551   						    &style->bg[GTK_STATE_NORMAL],
   1552   						    (gchar **) srevdn_xpm );
   1553 
   1554   display.rptupact_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1555   						    &display.rptupact_mask,
   1556   						    &style->bg[GTK_STATE_NORMAL],
   1557   						    (gchar **) rptupact_xpm );
   1558 
   1559   display.plstrkup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1560   						    &display.plstrkup_mask,
   1561   						    &style->bg[GTK_STATE_NORMAL],
   1562   						    (gchar **) plstrkup_xpm );
   1563 
   1564   display.plstrkdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1565   						    &display.plstrkdn_mask,
   1566   						    &style->bg[GTK_STATE_NORMAL],
   1567   						    (gchar **) plstrkdn_xpm );
   1568 
   1569   display.mnstrkup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1570   						    &display.mnstrkup_mask,
   1571   						    &style->bg[GTK_STATE_NORMAL],
   1572   						    (gchar **) mnstrkup_xpm );
   1573 
   1574   display.mnstrkdn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1575   						    &display.mnstrkdn_mask,
   1576   						    &style->bg[GTK_STATE_NORMAL],
   1577   						    (gchar **) mnstrkdn_xpm );
   1578 
   1579   display.plscdup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1580   						    &display.plscdup_mask,
   1581   						    &style->bg[GTK_STATE_NORMAL],
   1582   						    (gchar **) plscdup_xpm );
   1583 
   1584   display.plscddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1585   						    &display.plscddn_mask,
   1586   						    &style->bg[GTK_STATE_NORMAL],
   1587   						    (gchar **) plscddn_xpm );
   1588 
   1589   display.mnscdup_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1590   						    &display.mnscdup_mask,
   1591   						    &style->bg[GTK_STATE_NORMAL],
   1592   						    (gchar **) mnscdup_xpm );
   1593 
   1594   display.mnscddn_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1595   						    &display.mnscddn_mask,
   1596   						    &style->bg[GTK_STATE_NORMAL],
   1597   						    (gchar **) mnscddn_xpm );
   1598 
   1599   display.bar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1600   						    &display.bar_mask,
   1601   						    &style->bg[GTK_STATE_NORMAL],
   1602   						    (gchar **) bar_xpm );
   1603 
   1604   display.nobar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1605   						    &display.nobar_mask,
   1606   						    &style->bg[GTK_STATE_NORMAL],
   1607   						    (gchar **) nobar_xpm );
   1608 
   1609   display.redbar_pixmap = gdk_pixmap_create_from_xpm_d( window->window,
   1610   						    &display.redbar_mask,
   1611   						    &style->bg[GTK_STATE_NORMAL],
   1612   						    (gchar **) redbar_xpm );
   1613 
   1614   display.a_pixmap[0] = gdk_pixmap_create_from_xpm_d( window->window,
   1615   						    &display.a_mask[0],
   1616   						    &style->bg[GTK_STATE_NORMAL],
   1617   						    (gchar **) a0_xpm );
   1618  
   1619   display.a_pixmap[1] = gdk_pixmap_create_from_xpm_d( window->window,
   1620   						    &display.a_mask[1],
   1621   						    &style->bg[GTK_STATE_NORMAL],
   1622   						    (gchar **) a1_xpm );
   1623  
   1624   display.a_pixmap[2] = gdk_pixmap_create_from_xpm_d( window->window,
   1625   						    &display.a_mask[2],
   1626   						    &style->bg[GTK_STATE_NORMAL],
   1627   						    (gchar **) a2_xpm );
   1628  
   1629   display.a_pixmap[3] = gdk_pixmap_create_from_xpm_d( window->window,
   1630   						    &display.a_mask[3],
   1631   						    &style->bg[GTK_STATE_NORMAL],
   1632   						    (gchar **) a3_xpm );
   1633  
   1634   display.a_pixmap[4] = gdk_pixmap_create_from_xpm_d( window->window,
   1635   						    &display.a_mask[4],
   1636   						    &style->bg[GTK_STATE_NORMAL],
   1637   						    (gchar **) a4_xpm );
   1638  
   1639   display.a_pixmap[5] = gdk_pixmap_create_from_xpm_d( window->window,
   1640   						    &display.a_mask[5],
   1641   						    &style->bg[GTK_STATE_NORMAL],
   1642   						    (gchar **) a5_xpm );
   1643  
   1644   display.a_pixmap[6] = gdk_pixmap_create_from_xpm_d( window->window,
   1645   						    &display.a_mask[6],
   1646   						    &style->bg[GTK_STATE_NORMAL],
   1647   						    (gchar **) a6_xpm );
   1648  
   1649   display.a_pixmap[7] = gdk_pixmap_create_from_xpm_d( window->window,
   1650   						    &display.a_mask[7],
   1651   						    &style->bg[GTK_STATE_NORMAL],
   1652   						    (gchar **) a7_xpm );
   1653  
   1654   display.a_pixmap[8] = gdk_pixmap_create_from_xpm_d( window->window,
   1655   						    &display.a_mask[8],
   1656   						    &style->bg[GTK_STATE_NORMAL],
   1657   						    (gchar **) a8_xpm );
   1658  
   1659   display.a_pixmap[9] = gdk_pixmap_create_from_xpm_d( window->window,
   1660   						    &display.a_mask[9],
   1661   						    &style->bg[GTK_STATE_NORMAL],
   1662   						    (gchar **) a9_xpm );
   1663  
   1664   display.a_pixmap[10] = gdk_pixmap_create_from_xpm_d( window->window,
   1665   		 				    &display.a_mask[10],
   1666   		 				    &style->bg[GTK_STATE_NORMAL],
   1667   						    (gchar **) an_xpm );
   1668  
   1669  
   1670   display.b_pixmap[0] = gdk_pixmap_create_from_xpm_d( window->window,
   1671   						    &display.b_mask[0],
   1672   						    &style->bg[GTK_STATE_NORMAL],
   1673   						    (gchar **) b0_xpm );
   1674 
   1675   display.b_pixmap[1] = gdk_pixmap_create_from_xpm_d( window->window,
   1676   						    &display.b_mask[1],
   1677   						    &style->bg[GTK_STATE_NORMAL],
   1678   						    (gchar **) b1_xpm );
   1679 
   1680   display.b_pixmap[2] = gdk_pixmap_create_from_xpm_d( window->window,
   1681   						    &display.b_mask[2],
   1682   						    &style->bg[GTK_STATE_NORMAL],
   1683   						    (gchar **) b2_xpm );
   1684 
   1685   display.b_pixmap[3] = gdk_pixmap_create_from_xpm_d( window->window,
   1686   						    &display.b_mask[3],
   1687   						    &style->bg[GTK_STATE_NORMAL],
   1688   						    (gchar **) b3_xpm );
   1689 
   1690   display.b_pixmap[4] = gdk_pixmap_create_from_xpm_d( window->window,
   1691   						    &display.b_mask[4],
   1692   						    &style->bg[GTK_STATE_NORMAL],
   1693   						    (gchar **) b4_xpm );
   1694 
   1695   display.b_pixmap[5] = gdk_pixmap_create_from_xpm_d( window->window,
   1696   						    &display.b_mask[5],
   1697   						    &style->bg[GTK_STATE_NORMAL],
   1698   						    (gchar **) b5_xpm );
   1699 
   1700   display.b_pixmap[6] = gdk_pixmap_create_from_xpm_d( window->window,
   1701   						    &display.b_mask[6],
   1702   						    &style->bg[GTK_STATE_NORMAL],
   1703   						    (gchar **) b6_xpm );
   1704 
   1705   display.b_pixmap[7] = gdk_pixmap_create_from_xpm_d( window->window,
   1706   						    &display.b_mask[7],
   1707   						    &style->bg[GTK_STATE_NORMAL],
   1708   						    (gchar **) b7_xpm );
   1709 
   1710   display.b_pixmap[8] = gdk_pixmap_create_from_xpm_d( window->window,
   1711   						    &display.b_mask[8],
   1712   						    &style->bg[GTK_STATE_NORMAL],
   1713   						    (gchar **) b8_xpm );
   1714 
   1715   display.b_pixmap[9] = gdk_pixmap_create_from_xpm_d( window->window,
   1716   						    &display.b_mask[9],
   1717   						    &style->bg[GTK_STATE_NORMAL],
   1718   						    (gchar **) b9_xpm );
   1719 
   1720   display.b_pixmap[10] = gdk_pixmap_create_from_xpm_d( window->window,
   1721    						    &display.b_mask[10],
   1722   						    &style->bg[GTK_STATE_NORMAL],
   1723   						    (gchar **) bn_xpm );
   1724 
   1725   /*
   1726    * Create a drawing area for us to show the track and time
   1727    */
   1728   display.wid = gtk_drawing_area_new();
   1729   gtk_box_pack_start( GTK_BOX( hbox2 ), display.wid, FALSE, FALSE, 0 );
   1730   gtk_widget_show( display.wid );
   1731 
   1732   /* Make sure it is redrawn when covered and uncovered by others */  
   1733   gtk_signal_connect( GTK_OBJECT( display.wid ), "expose_event",
   1734   		      (GtkSignalFunc) expose_display, NULL );
   1735 
   1736   gtk_drawing_area_size( GTK_DRAWING_AREA(display.wid), 92, 30 );
   1737   gtk_widget_set_events( display.wid, GDK_EXPOSURE_MASK | 
   1738                          GDK_BUTTON_MOTION_MASK |
   1739                          GDK_POINTER_MOTION_HINT_MASK |
   1740                          GDK_BUTTON_PRESS_MASK);
   1741 
   1742   /*  gtk_widget_realize (display.wid);*/
   1743 
   1744   gtk_signal_connect (GTK_OBJECT (display.wid), "button_press_event",
   1745                       GTK_SIGNAL_FUNC (display_pressed),NULL);
   1746   gtk_signal_connect (GTK_OBJECT (display.wid), "button_release_event",
   1747                       GTK_SIGNAL_FUNC (display_released),NULL);
   1748   gtk_signal_connect (GTK_OBJECT (display.wid), "motion_notify_event",
   1749                       GTK_SIGNAL_FUNC (display_motion),NULL);
   1750 
   1751   /* Data area to keep track of the cursor position for moving the window */
   1752   icon_pos = g_new (CursorOffset, 1);
   1753   gtk_object_set_user_data(GTK_OBJECT(display.wid), icon_pos);
   1754 
   1755   x_pos = 0;
   1756   y_pos = 0;
   1757   /* Check the retun bits -- we want XValue & YValue */
   1758   if( stat & XValue )
   1759     {
   1760       /* Is the value negative (relative to right side?) */
   1761       if( stat & XNegative )
   1762 	{
   1763 	  /* Make sure it returned a negative value */
   1764 	  if( x_ret < 0 )
   1765 	    x_pos = (gdk_screen_width()-137) + x_ret;
   1766 	  else
   1767 	    x_pos = (gdk_screen_width()-137) - x_ret;
   1768 	} else {
   1769 	  x_pos = x_ret;
   1770 	}
   1771     }
   1772 
   1773   /* Did we get a Y position? */
   1774   if( stat & YValue )
   1775     {
   1776       /* Is the value negative (relative to bottom side?) */
   1777       if( stat & YNegative )
   1778 	{
   1779 	  /* Make sure it returned a negative value */
   1780 	  if( y_ret < 0 )
   1781 	    y_pos = (gdk_screen_height()-60) + y_ret;
   1782 	  else
   1783 	    y_pos = (gdk_screen_height()-60) - y_ret;
   1784 	} else {
   1785 	  y_pos = y_ret;
   1786 	}
   1787     }
   1788 
   1789 #ifdef DEBUG7
   1790   g_print("Setting x=%d  y=%d\n", x_pos, y_pos );
   1791 #endif
   1792 
   1793   /* Put the window where the user wants it (x_pos,y_pos) */
   1794   gtk_widget_set_uposition( window, x_pos, y_pos );
   1795 
   1796   /* Setup the colors for the tooltip windows, postit yellow */
   1797   colormap = gdk_window_get_colormap (window->window);
   1798   tip_color.red = 61669;
   1799   tip_color.green = 59113;
   1800   tip_color.blue = 35979;
   1801   gdk_color_alloc (colormap, &tip_color);
   1802 
   1803   /* Set the foreground/background of the tooltips */
   1804   gtk_tooltips_set_colors( tips, &tip_color, &window->style->fg[GTK_STATE_NORMAL] );
   1805 
   1806   /* Showing the window last so everything pops up at once. */
   1807   gtk_widget_show (window);
   1808 
   1809   /* Start the 1 second update timer */
   1810   timer = gtk_timeout_add( 1000, update_cdrom, NULL );
   1811 
   1812   /*
   1813      Read some useful info from .xfreecdrc
   1814      Path to local database
   1815      server list
   1816      default server
   1817   */
   1818   if( read_config( &config ) < 0 )
   1819     {
   1820       g_print("read_config failed\n");
   1821       g_free( icon_pos );
   1822       exit(-1);
   1823     }
   1824 
   1825   /* Send the device to the cd_control process */
   1826   send_device();
   1827 
   1828   /* Tell cd_control about the exit on eject decision */
   1829   set_eject( config.exit_eject );
   1830 
   1831   gtk_main ();
   1832 
   1833   /* Turn off the 1 second timeout */
   1834   gtk_timeout_remove( timer );
   1835 
   1836   g_free( icon_pos );
   1837 
   1838   free_config( &config );        /* Free up config's memory usage */
   1839 
   1840   return(0);
   1841 }
   1842 
   1843 
   1844 gint delete_event(GtkWidget *widget, gpointer data)
   1845 {
   1846     /* if you return FALSE in the "delete_event" signal handler,
   1847      * GTK will emit the "destroy" signal.  Returning TRUE means
   1848      * you don't want the window to be destroyed.
   1849      * This is useful for popping up 'are you sure you want to quit ?'
   1850      * type dialogs. */
   1851 
   1852     /* Change TRUE to FALSE and the main window will be destroyed with
   1853      * a "delete_event". */
   1854 
   1855     return (FALSE);
   1856 }
   1857 
   1858 /* another callback */
   1859 void destroy (GtkWidget *widget, gpointer data)
   1860 {
   1861   gtk_main_quit ();
   1862 }
   1863 
   1864 
   1865 
   1866 /* --------------------------------------------------------------
   1867    Copy the edited data over to cdinfo and write to local database
   1868    --------------------------------------------------------------- */
   1869 void write_tracks()
   1870 {
   1871   GString      *old_category;
   1872   int  x;
   1873   char         *texts[3],
   1874                *p,
   1875                text1[255],
   1876                text2[255],
   1877                fname[1024],
   1878                tmp_fname[1024];
   1879 
   1880 
   1881   /* Sanity checks. We can't save or send if some of the important fields
   1882      are blank.
   1883   */
   1884   if( display.tmp_title == NULL )
   1885     return;
   1886 
   1887   if( display.tmp_category == NULL )
   1888     return;
   1889 
   1890   if( cdinfo.category == NULL )
   1891     return;
   1892 
   1893   /* Save the old category so we can erase it if its changed */
   1894   old_category = g_string_new( cdinfo.category->str );
   1895   
   1896   /* Copy the new title into the cdinfo structure */
   1897   cdinfo.title = g_string_assign( cdinfo.title, display.tmp_title->str );
   1898   g_string_free( display.tmp_title, 1 );
   1899   display.tmp_title = NULL;
   1900 
   1901   /* Copy the track names if they exist and delete display storage */
   1902   for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   1903     {
   1904       if( display.tmp_track[x] != NULL )
   1905 	{
   1906 	  if( cdinfo.name[x] == NULL )
   1907 	    cdinfo.name[x] = g_string_new( display.tmp_track[x]->str );
   1908 	  else
   1909 	    cdinfo.name[x] = g_string_assign( cdinfo.name[x], display.tmp_track[x]->str );
   1910 	  g_string_free( display.tmp_track[x], 1 );
   1911 	  display.tmp_track[x] = NULL;
   1912 	}
   1913     }
   1914 
   1915   /* Copy the new categry name over */
   1916   cdinfo.category = g_string_assign( cdinfo.category, display.tmp_category->str );
   1917 
   1918   /* Update the track window's info if its still open */
   1919   if( display.twindow != NULL )
   1920     {
   1921       gtk_clist_freeze (GTK_CLIST (display.tclist));
   1922 
   1923       /* Clear out the old list first */
   1924       gtk_clist_clear( GTK_CLIST( display.tclist ) );
   1925 
   1926       /* Set the new title */
   1927       if( cdinfo.title != NULL )
   1928 	sprintf( text1, "%s", cdinfo.title->str );
   1929       else
   1930 	strcpy( text1, "" );
   1931 
   1932       gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
   1933 
   1934       /* Add the track names */
   1935       for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   1936 	{
   1937 	  sprintf( text1, "%d", x+1 );
   1938 	  sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
   1939 	  texts[0] = text1;
   1940 	  texts[2] = text2;
   1941 	  if( cdinfo.name[x] != NULL )
   1942 	    texts[1] = cdinfo.name[x]->str;
   1943 	  else
   1944 	    texts[1] = "(blank)";
   1945 
   1946 	  gtk_clist_append (GTK_CLIST (display.tclist), texts);
   1947 	}
   1948       gtk_clist_thaw( GTK_CLIST( display.tclist ) );
   1949       gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
   1950     }
   1951 
   1952   /* Save to database -- overwrite previous */
   1953   if( write_cddb( &cdinfo, 1 ) < 0 )
   1954     {
   1955       perror("write_cddb error");
   1956     } else {
   1957 #ifdef DEBUG3
   1958       g_print("Wrote %08lx ok\n", cddb_discid( &cdinfo ) );
   1959 #endif
   1960 
   1961       /* The write was successful, we can erase the old category if it is different */
   1962       if( strcmp( cdinfo.category->str, old_category->str ) != 0 )
   1963 	{
   1964 	  /* Build the path to the old entry */
   1965 	  /* Convert a leading ~ into the user's HOME directory */
   1966 	  if( cdinfo.local_cddb[0] == '~' )
   1967 	    {
   1968 	      /* Copy the reset of the path/filename to tmp_fname */
   1969 	      strncpy( tmp_fname, &cdinfo.local_cddb[1], 1023 );
   1970 
   1971 	      if( ( p = (char *) getenv("HOME") ) == NULL )
   1972 		{
   1973 		  return;
   1974 		}
   1975 	      strncpy( fname, p, 1023 );
   1976 
   1977 	      /* Make sure there is a slash inbetween the two */
   1978 	      if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
   1979 		{
   1980 		  strcat( fname, "/" );
   1981 		}
   1982 
   1983 	      strncat( fname, tmp_fname, 1023-strlen( p ) );
   1984 	    } else {
   1985 	      strncpy( fname, cdinfo.local_cddb, 1023 );
   1986 	    }
   1987 
   1988 	  if( fname[strlen(fname)-1] != '/')
   1989 	    strcat( fname, "/" );
   1990 
   1991 	  /* Add the category name */
   1992 	  strcat( fname, old_category->str );
   1993 	  strcat( fname, "/" );
   1994 	  sprintf( tmp_fname, "%08lx", cdinfo.discid );
   1995 	  strcat( fname, tmp_fname );
   1996 
   1997 	  /* Delete it */
   1998 	  unlink( fname );
   1999 	}
   2000     }
   2001 
   2002   g_string_free( old_category, 1 );
   2003 }
   2004 
   2005 
   2006 
   2007 /* --------------------------------------------------------------
   2008    Send the cdinfo to the database
   2009 
   2010    Increment the revision and save the new one to disk.
   2011    Use a system call to mail and cat
   2012    --------------------------------------------------------------- */
   2013 void send_cddbd( GtkWidget  *widget, GtkWidget *window)
   2014 {
   2015   char  *p,
   2016         tmp_fname[1024],
   2017         fname[1024],
   2018         buffer[1024];
   2019 
   2020   /* Close the edit window */
   2021   gtk_widget_destroy( window );
   2022 
   2023   /* Increment the revision number */
   2024   cdinfo.revision++;
   2025 
   2026   /* Copy all the changes to the cdinfo structure */
   2027   write_tracks();
   2028 
   2029   /* Make sure it has a title */
   2030   if( cdinfo.title != NULL )
   2031     {
   2032       /* Convert a leading ~ into the user's HOME directory */
   2033       if( cdinfo.local_cddb[0] == '~' )
   2034 	{
   2035 	  /* Copy the reset of the path/filename to tmp_fname */
   2036 	  strncpy( tmp_fname, &cdinfo.local_cddb[1], 1023 );
   2037 	
   2038 	  if( ( p = (char *) getenv("HOME") ) == NULL )
   2039 	    {
   2040 	      return;
   2041 	    }
   2042 	  strncpy( fname, p, 1023 );
   2043 
   2044 	  /* Make sure there is a slash inbetween the two */
   2045 	  if( (fname[strlen(fname)-1] != '/') && (tmp_fname[0] != '/') )
   2046 	    {
   2047 	      strcat( fname, "/" );
   2048 	    }
   2049 
   2050 	  strncat( fname, tmp_fname, 1023-strlen( p ) );
   2051 	} else {
   2052 	  strncpy( fname, cdinfo.local_cddb, 1023 );
   2053 	}
   2054 
   2055       if( fname[strlen(fname)-1] != '/')
   2056 	strcat( fname, "/" );
   2057 
   2058       /* Add the category name */
   2059       strcat( fname, cdinfo.category->str );
   2060       strcat( fname, "/" );
   2061       sprintf( tmp_fname, "%08lx", cdinfo.discid );
   2062       strcat( fname, tmp_fname );
   2063 
   2064       sprintf( buffer, "cat %s | %s -s \"cddb %s %08lx\" %s", fname, MAIL_BINARY, cdinfo.category->str, cdinfo.discid, config.to_cddbd );
   2065 
   2066 #ifdef MAIL_DEBUG
   2067       g_print( "%s\n", buffer );
   2068 #else      
   2069       /* Make a system call */
   2070       system( buffer );
   2071 #endif
   2072     }
   2073 }
   2074 
   2075 
   2076 /* --------------------------------------------------------------
   2077    Save the new cd data to the local cd database
   2078   --------------------------------------------------------------- */
   2079 void save_tracks( GtkWidget  *widget, GtkWidget *window)
   2080 {
   2081   gtk_widget_destroy( window );
   2082 
   2083   /* Update cdinfo and write the new data to the local database */
   2084   write_tracks();
   2085 }
   2086 
   2087 
   2088 /* ----------------------------------------------------------------------
   2089    The track entry data has changed. Copy it to a temporary location...
   2090    ---------------------------------------------------------------------- */
   2091 void track_entry(GtkWidget *widget, GtkWidget *entry)
   2092 {
   2093   gchar *entry_text;
   2094   char         *texts[3],
   2095                text1[255],
   2096                text2[255];
   2097 
   2098   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   2099 
   2100   if( display.tmp_track[display.tmp_row] == NULL )
   2101     display.tmp_track[display.tmp_row] = g_string_new( entry_text );
   2102   else
   2103     display.tmp_track[display.tmp_row] = g_string_assign( display.tmp_track[display.tmp_row], entry_text );
   2104 
   2105   /* Update the clist in the edit window */
   2106   if( display.tewindow != NULL )
   2107     {
   2108       gtk_clist_freeze (GTK_CLIST (display.teclist));
   2109       
   2110       /* Remove the old entry first */
   2111       gtk_clist_remove( GTK_CLIST( display.teclist ), display.tmp_row );
   2112 
   2113       /* Insert the new data */
   2114       sprintf( text1, "%d", display.tmp_row+1 );
   2115       sprintf( text2, "%d:%02d",cdinfo.track[display.tmp_row].length/60,cdinfo.track[display.tmp_row].length%60 );
   2116       texts[0] = text1;
   2117       texts[2] = text2;
   2118       if( display.tmp_track[display.tmp_row] != NULL )
   2119 	texts[1] = display.tmp_track[display.tmp_row]->str;
   2120       else
   2121 	texts[1] = "(blank)";
   2122       gtk_clist_insert (GTK_CLIST (display.teclist), display.tmp_row, texts);
   2123 
   2124       /* Select the correct row in the clist */
   2125       gtk_clist_select_row( GTK_CLIST(display.teclist), display.tmp_row, -1 );
   2126 
   2127       gtk_clist_thaw( GTK_CLIST( display.teclist ) );
   2128 
   2129     }
   2130 }
   2131 
   2132 
   2133 /* -------------------------------------------------------------------
   2134    The Title entry data has changed, copy it to a temporary location
   2135    ------------------------------------------------------------------- */
   2136 void title_entry(GtkWidget *widget, GtkWidget *entry)
   2137 {
   2138   gchar *entry_text;
   2139 
   2140   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   2141 
   2142   if( display.tmp_title == NULL )
   2143     display.tmp_title = g_string_new( entry_text );
   2144   else
   2145     display.tmp_title = g_string_assign( display.tmp_title, entry_text );
   2146 }
   2147 
   2148 
   2149 /* ----------------------------------------------------------------------
   2150    The user selected a track, copy its data into the edit widget
   2151    (display.teedit)
   2152    ---------------------------------------------------------------------- */
   2153 void select_teclist (GtkWidget *widget,
   2154 		     gint row,
   2155 		     gint column,
   2156 		     GdkEventButton * bevent)
   2157 {
   2158 
   2159 #ifdef DEBUG9
   2160   g_print ("Selection: Track #%d by button %d\n", row, bevent ? bevent->button : 0 );
   2161 #endif
   2162 
   2163   if( bevent )
   2164     {
   2165       if( bevent->button == 1 )
   2166 	{
   2167 	  display.tmp_row = row;
   2168 	  /* Copy the track name into the edit widget */
   2169 	  if( display.tmp_track[display.tmp_row] != NULL )
   2170 	    gtk_entry_set_text( GTK_ENTRY( display.teentry ), display.tmp_track[display.tmp_row]->str);
   2171 	  else
   2172 	    gtk_entry_set_text( GTK_ENTRY( display.teentry ), "(blank)" );
   2173 	}
   2174     }
   2175 }
   2176 
   2177 
   2178 
   2179 
   2180 /* --------------------------------------------------------------------
   2181    If the user select a track in the tracklist window, play that
   2182    track now.
   2183    -------------------------------------------------------------------- */
   2184 void select_tclist (GtkWidget *widget,
   2185               gint row,
   2186               gint column,
   2187               GdkEventButton * bevent)
   2188 {
   2189 
   2190 #ifdef DEBUG9
   2191   g_print ("Selection: Track #%d by button %d\n", row, bevent ? bevent->button : 0 );
   2192 #endif
   2193 
   2194   if( bevent )
   2195     {
   2196       if( bevent->button == 1 )
   2197 	{
   2198 	  play_track( row+1 );      
   2199 	}
   2200     }
   2201 }
   2202 
   2203 
   2204 
   2205 
   2206 /* -----------------------------------------------------------------------
   2207    Handle the button press events
   2208 
   2209    Each button has a xx_press and xx_release event where they change their
   2210    pixmaps to show a up or down state.
   2211 
   2212    The FF, REW, Eject button also have to update the state of the play
   2213    button since they can effect the state that the player is in. I'm sure
   2214    there is a better way to do this...
   2215    ----------------------------------------------------------------------- */
   2216 
   2217 
   2218 /* -----------------------------------------------------------------------
   2219    Copy the selected item into the category
   2220    ----------------------------------------------------------------------- */
   2221 static void menuitem_response (gchar *string)
   2222 {
   2223   if( display.tmp_category == NULL )
   2224     display.tmp_category = g_string_new( string );
   2225   else
   2226     display.tmp_category = g_string_assign( display.tmp_category, string );
   2227 }
   2228 
   2229 
   2230 /* -----------------------------------------------------------------------
   2231    Start up the window for editing the track list.
   2232    Close the normal track list while doing this.
   2233    Save it to cddb format when close is clicked.
   2234    Don't save when Cancel is clicked.
   2235    ----------------------------------------------------------------------- */
   2236 static void edit_tracks(GtkWidget *widget, GtkWidget *data)
   2237 {
   2238   GtkWidget    *vbox1,
   2239                *hbox1,
   2240                *button,
   2241                *edit,
   2242                *optionmenu,
   2243                *menu,
   2244                *menuitem;
   2245   int          x,
   2246                curr_cat;
   2247   char         *texts[3],
   2248                text1[255],
   2249                text2[255];
   2250   static char  *titles[] =
   2251   {
   2252     "Track #",
   2253     "Title",
   2254     "Length"
   2255   };
   2256   static char  *categories[] =
   2257   {
   2258     "blues",
   2259     "classical",
   2260     "country",
   2261     "data",
   2262     "folk",
   2263     "jazz",
   2264     "misc",
   2265     "newage",
   2266     "reggae",
   2267     "rock",
   2268     "soundtrack"
   2269   };
   2270 
   2271 
   2272   /* Don't allow edit if there's no disk */
   2273   if( display.status == CDROM_NODISC )
   2274     return;
   2275 
   2276   if (!display.tewindow)
   2277     {
   2278       display.tewindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   2279       gtk_widget_set_usize (display.tewindow, 300, 400);
   2280       
   2281       gtk_signal_connect (GTK_OBJECT (display.tewindow), "destroy",
   2282 			  GTK_SIGNAL_FUNC(gtk_widget_destroyed),
   2283 			  &display.tewindow);
   2284 
   2285       gtk_window_set_title (GTK_WINDOW (display.tewindow), "Edit Track Info" );
   2286       gtk_container_border_width (GTK_CONTAINER (display.tewindow), 4);
   2287       gtk_window_set_wmclass(GTK_WINDOW(display.tewindow), "XfreeCDet", NULL );
   2288 
   2289       /* create a vbox to hold the clist and buttons */
   2290       vbox1 = gtk_vbox_new (FALSE, 0);
   2291       gtk_container_add (GTK_CONTAINER (display.tewindow), vbox1);
   2292       gtk_widget_show (vbox1);
   2293 
   2294       /* Make a hbox to hold discid and category dropdown */
   2295       hbox1 = gtk_hbox_new (FALSE, 0);
   2296       gtk_box_pack_start( GTK_BOX( vbox1 ), hbox1, FALSE, FALSE, 2 );
   2297       gtk_widget_show (hbox1);
   2298 
   2299       /* Need to show the disc id and category */
   2300       /* Maybe category should be a drop-down listing of the standard categories? */
   2301       sprintf( text1, "Discid: 0x%08lx   Category : ", cdinfo.discid );
   2302       button = gtk_label_new( text1 );
   2303       gtk_box_pack_start( GTK_BOX( hbox1 ), button, FALSE, FALSE, 2 );
   2304       gtk_widget_show( button );
   2305 
   2306       /* Optionmenu for selecting the category */
   2307       optionmenu = gtk_option_menu_new();
   2308       menu = gtk_menu_new();
   2309 
   2310       curr_cat = 0;
   2311       /* Add the categories */
   2312       for( x = 0; x < 11; x++ )
   2313 	{
   2314 	  /* Set the current category */
   2315 	  if( cdinfo.category != NULL )
   2316 	    {
   2317 	      if( strcmp( cdinfo.category->str, categories[x] ) == 0 )
   2318 		{
   2319 		  curr_cat = x;
   2320 		  if( display.tmp_category == NULL )
   2321 		    display.tmp_category = g_string_new( cdinfo.category->str );
   2322 		  else
   2323 		    display.tmp_category = g_string_assign( display.tmp_category, cdinfo.category->str );
   2324 		}
   2325 	    } else {
   2326 	      display.tmp_category= g_string_new( categories[0] );
   2327 	    }
   2328 	  menuitem = gtk_menu_item_new_with_label( categories[x] );
   2329 	  gtk_menu_append (GTK_MENU (menu), menuitem);
   2330 	  gtk_signal_connect_object( GTK_OBJECT(menuitem), "activate",
   2331 	      GTK_SIGNAL_FUNC(menuitem_response), (gpointer) categories[x] );
   2332 	  gtk_widget_show (menuitem);
   2333 	}
   2334       gtk_option_menu_set_menu (GTK_OPTION_MENU (optionmenu), menu);
   2335       gtk_option_menu_set_history (GTK_OPTION_MENU (optionmenu), curr_cat );
   2336       gtk_box_pack_start (GTK_BOX (hbox1), optionmenu, FALSE, FALSE, 0);
   2337       gtk_widget_show (optionmenu);
   2338 
   2339 
   2340 
   2341       /* Entry Widget for the title of the CD */
   2342       edit = gtk_entry_new_with_max_length( 254 );
   2343       gtk_signal_connect(GTK_OBJECT(edit), "changed",
   2344 			 GTK_SIGNAL_FUNC(title_entry),
   2345 			 edit);
   2346 
   2347       if( cdinfo.title != NULL )
   2348 	gtk_entry_set_text( GTK_ENTRY( edit ), cdinfo.title->str );
   2349       gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
   2350       gtk_box_pack_start( GTK_BOX( vbox1 ), edit, FALSE, FALSE, 2 );
   2351       gtk_widget_show( edit );
   2352 
   2353       /* Initalize the title */
   2354       if( cdinfo.title != NULL )
   2355 	display.tmp_title = g_string_new( cdinfo.title->str );
   2356 
   2357       /* Entry Widget for the track name-changed by selecting a diff. track */
   2358       display.teentry = gtk_entry_new_with_max_length( 254 );
   2359       gtk_box_pack_start( GTK_BOX( vbox1 ), display.teentry, FALSE, FALSE, 2 );
   2360       gtk_widget_show( display.teentry );
   2361 
   2362       /* Create the clist */
   2363       display.teclist = gtk_clist_new_with_titles (3, titles);
   2364       gtk_clist_set_row_height (GTK_CLIST (display.teclist), 20);
   2365       gtk_clist_set_column_width (GTK_CLIST (display.teclist), 0, 45);
   2366       gtk_clist_set_column_width (GTK_CLIST (display.teclist), 1, 150);
   2367       gtk_clist_set_column_width (GTK_CLIST (display.teclist), 2, 45);
   2368       gtk_signal_connect (GTK_OBJECT (display.teclist),
   2369 			  "select_row",
   2370 			  (GtkSignalFunc) select_teclist,
   2371 			  NULL);
   2372 #ifdef GOOBER
   2373       gtk_clist_set_selection_mode (GTK_CLIST (display.teclist), GTK_SELECTION_BROWSE);
   2374 #endif
   2375       gtk_clist_set_policy (GTK_CLIST (display.teclist),
   2376 			    GTK_POLICY_AUTOMATIC,
   2377 			    GTK_POLICY_AUTOMATIC);
   2378 
   2379       gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 0, GTK_JUSTIFY_CENTER);
   2380       gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 1, GTK_JUSTIFY_LEFT);
   2381       gtk_clist_set_column_justification (GTK_CLIST (display.teclist), 2, GTK_JUSTIFY_LEFT);
   2382 	  
   2383       gtk_clist_freeze (GTK_CLIST (display.teclist));
   2384 
   2385       display.tmp_row = 0;
   2386       /* Add the track names */
   2387       for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   2388 	{
   2389 	  sprintf( text1, "%d", x+1 );
   2390 	  sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
   2391 	  texts[0] = text1;
   2392 	  texts[2] = text2;
   2393 	  if( cdinfo.name[x] != NULL )
   2394 	    texts[1] = cdinfo.name[x]->str;
   2395 	  else
   2396 	    texts[1] = "(blank)";
   2397 	  gtk_clist_append (GTK_CLIST (display.teclist), texts);
   2398 
   2399 	  /* Initalize the temporary storage */
   2400 	  if( cdinfo.name[x] != NULL )
   2401 	    display.tmp_track[x] = g_string_new( cdinfo.name[x]->str );
   2402 	  else
   2403 	    display.tmp_track[x] = NULL;
   2404 	}
   2405       gtk_clist_thaw( GTK_CLIST( display.teclist ) );
   2406 	  
   2407       gtk_container_border_width (GTK_CONTAINER (display.teclist), 5);
   2408       gtk_box_pack_start (GTK_BOX (vbox1), display.teclist, TRUE, TRUE, 0);
   2409       gtk_widget_show (display.teclist);
   2410 
   2411       /* Now we can update the track edit control, since the tclist is setup */
   2412       gtk_signal_connect(GTK_OBJECT(display.teentry), "changed",
   2413 			 GTK_SIGNAL_FUNC(track_entry),
   2414 			 display.teentry);
   2415       if( display.tmp_track[0] != NULL )
   2416 	gtk_entry_set_text( GTK_ENTRY( display.teentry ), display.tmp_track[0]->str );
   2417       gtk_editable_select_region( GTK_EDITABLE( display.teentry ), 0, -1 );
   2418 
   2419       button = gtk_button_new_with_label ("Send To Server");
   2420       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   2421 				 GTK_SIGNAL_FUNC(send_cddbd),
   2422 				 GTK_OBJECT (display.tewindow));
   2423       gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
   2424       gtk_widget_show (button);
   2425 
   2426       button = gtk_button_new_with_label ("Save");
   2427       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   2428 				 GTK_SIGNAL_FUNC(save_tracks),
   2429 				 GTK_OBJECT (display.tewindow));
   2430       gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
   2431       gtk_widget_show (button);
   2432 
   2433       button = gtk_button_new_with_label ("Close");
   2434       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   2435 				 GTK_SIGNAL_FUNC(gtk_widget_destroy),
   2436 				 GTK_OBJECT (display.tewindow));
   2437       gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
   2438       gtk_widget_show (button);
   2439 
   2440       gtk_widget_show( display.tewindow );
   2441     }
   2442 }
   2443 
   2444 
   2445 
   2446 /*
   2447    Handle button press in the display window.
   2448    Determine if its a click for a track list or a drag to move the window
   2449 */
   2450 static void display_pressed (GtkWidget *widget, GdkEventButton *event)
   2451 {
   2452   CursorOffset *p;
   2453   GtkWidget    *vbox1,
   2454                *button;
   2455   int          x;
   2456   char         *texts[3],
   2457                text1[255],
   2458                text2[255];
   2459   static char  *titles[] =
   2460   {
   2461     "Track #",
   2462     "Title",
   2463     "Length"
   2464   };
   2465 
   2466 
   2467   /* ignore double and triple click */
   2468   if (event->type != GDK_BUTTON_PRESS)
   2469     return;
   2470 
   2471   /* Right button is move window */
   2472   if( event->button == 3 )
   2473     {
   2474       if( ( p = gtk_object_get_user_data (GTK_OBJECT(widget)) ) != NULL )
   2475 	{
   2476 	  p->x = (int) event->x;
   2477 	  p->y = (int) event->y;
   2478 
   2479 	  gtk_grab_add (widget);
   2480 	  gdk_pointer_grab (widget->window, TRUE,
   2481 			    GDK_BUTTON_RELEASE_MASK |
   2482 			    GDK_BUTTON_MOTION_MASK |
   2483 			    GDK_POINTER_MOTION_HINT_MASK,
   2484 			    NULL, NULL, 0);
   2485 	}
   2486     } else if( event->button == 1 ) {
   2487       /* Show the title and the tracks, using a clist */
   2488       if (!display.twindow)
   2489 	{
   2490 	  display.twindow = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   2491 	  gtk_widget_set_usize (display.twindow, 300, 350);
   2492 
   2493 	  gtk_signal_connect (GTK_OBJECT (display.twindow), "destroy",
   2494 			      GTK_SIGNAL_FUNC(gtk_widget_destroyed),
   2495 			      &display.twindow);
   2496 
   2497 	  /*	  gtk_window_set_title (GTK_WINDOW (display.twindow), cdinfo.title); */
   2498 	  gtk_container_border_width (GTK_CONTAINER (display.twindow), 4);
   2499 	  gtk_window_set_wmclass(GTK_WINDOW(display.twindow), "XfreeCDt", NULL );
   2500 
   2501 	  /* create a vbox to hold the clist and buttons */
   2502 	  vbox1 = gtk_vbox_new (FALSE, 0);
   2503 	  gtk_container_add (GTK_CONTAINER (display.twindow), vbox1);
   2504 	  gtk_widget_show (vbox1);
   2505 
   2506 	  if( cdinfo.title != NULL )
   2507 	    sprintf( text1, "%s", cdinfo.title->str );
   2508 	  else
   2509 	    strcpy( text1, "" );
   2510 	  display.ttitle = gtk_label_new( text1 );
   2511 	  gtk_box_pack_start( GTK_BOX( vbox1 ), display.ttitle, FALSE, FALSE, 0 );
   2512 	  gtk_widget_show( display.ttitle );
   2513 
   2514 	  /* Create the clist */
   2515 	  display.tclist = gtk_clist_new_with_titles (3, titles);
   2516 	  gtk_clist_set_row_height (GTK_CLIST (display.tclist), 20);
   2517 	  gtk_clist_set_column_width (GTK_CLIST (display.tclist), 0, 45);
   2518 	  gtk_clist_set_column_width (GTK_CLIST (display.tclist), 1, 150);
   2519 	  gtk_clist_set_column_width (GTK_CLIST (display.tclist), 2, 45);
   2520 	  gtk_signal_connect (GTK_OBJECT (display.tclist),
   2521 			      "select_row",
   2522 			      (GtkSignalFunc) select_tclist,
   2523 			      NULL);
   2524 
   2525 #ifdef GOOBER
   2526 	  gtk_clist_set_selection_mode (GTK_CLIST (display.tclist), GTK_SELECTION_BROWSE);
   2527 #endif
   2528 	  gtk_clist_set_policy (GTK_CLIST (display.tclist),
   2529 				GTK_POLICY_AUTOMATIC,
   2530 				GTK_POLICY_AUTOMATIC);
   2531 
   2532 	  gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 0, GTK_JUSTIFY_CENTER);
   2533 	  gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 1, GTK_JUSTIFY_LEFT);
   2534 	  gtk_clist_set_column_justification (GTK_CLIST (display.tclist), 2, GTK_JUSTIFY_LEFT);
   2535 
   2536 	  if( display.status != CDROM_NODISC )
   2537 	    {
   2538 	      gtk_clist_freeze (GTK_CLIST (display.tclist));
   2539 	      /* Add the track names */
   2540 	      for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   2541 		{
   2542 		  sprintf( text1, "%d", x+1 );
   2543 		  sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
   2544 		  texts[0] = text1;
   2545 		  texts[2] = text2;
   2546 		  if( cdinfo.name[x] != NULL )
   2547 		    texts[1] = cdinfo.name[x]->str;
   2548 		  else
   2549 		    texts[1] = "(blank)";
   2550 		  gtk_clist_append (GTK_CLIST (display.tclist), texts);
   2551 		}
   2552 	      gtk_clist_thaw( GTK_CLIST( display.tclist ) );
   2553 	    }
   2554 
   2555 	  gtk_container_border_width (GTK_CONTAINER (display.tclist), 5);
   2556 	  gtk_box_pack_start (GTK_BOX (vbox1), display.tclist, TRUE, TRUE, 0);
   2557 	  gtk_widget_show (display.tclist);
   2558 
   2559 	  button = gtk_button_new_with_label ("Edit Info");
   2560 	  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   2561 				     GTK_SIGNAL_FUNC(edit_tracks),
   2562 				     GTK_OBJECT (display.twindow));
   2563 	  gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
   2564 	  gtk_widget_show (button);
   2565 
   2566 	  button = gtk_button_new_with_label ("Close");
   2567 	  gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   2568 				     GTK_SIGNAL_FUNC(gtk_widget_destroy),
   2569 				     GTK_OBJECT (display.twindow));
   2570 
   2571 	  gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 2);
   2572 	  /*	  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT); */
   2573 	  /*	  gtk_widget_grab_default (button); */
   2574 	  gtk_widget_show (button);
   2575 	  gtk_widget_show( display.twindow );
   2576 	}
   2577     }
   2578 }
   2579 
   2580 
   2581 static void display_released (GtkWidget *widget)
   2582 {
   2583   gtk_grab_remove (widget);
   2584   gdk_pointer_ungrab (0);
   2585 }
   2586 
   2587 
   2588 static void display_motion (GtkWidget *widget, GdkEventMotion *event)
   2589 {
   2590   gint xp, yp;
   2591   CursorOffset * p;
   2592   GdkModifierType mask;
   2593 
   2594   if( ( p = gtk_object_get_user_data (GTK_OBJECT (widget)) ) != NULL )
   2595     {
   2596       /*
   2597        * Can't use event->x / event->y here 
   2598        * because I need absolute coordinates.
   2599        */
   2600       gdk_window_get_pointer (root_win, &xp, &yp, &mask);
   2601       gtk_widget_set_uposition (display.main_window, xp  - p->x, yp  - p->y);
   2602     }
   2603 }
   2604 
   2605 
   2606 
   2607 
   2608 void ff_press (GtkWidget *widget, GdkEventButton *event)
   2609 {
   2610   /* Left button pressed */
   2611   if( event->button == 1 )
   2612   {
   2613     /* Set the FF down image */
   2614     gtk_pixmap_set( GTK_PIXMAP( ff.wid ),
   2615                     ff.image.dn_pixmap,
   2616                     ff.image.dn_mask );
   2617 
   2618     /* Tell the CD control to play the next track */
   2619     play_next();
   2620      
   2621 
   2622     /* Update the state of the play button -- Should be a subroutine */
   2623     if( (display.playbtn != CDROM_PLAYING) && (display.status != CDROM_NODISC))
   2624       {
   2625      	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2626      			display.playup_pixmap,
   2627      			display.playup_mask );
   2628      	display.playbtn = CDROM_PLAYING;
   2629       }
   2630   }
   2631 }
   2632 
   2633 
   2634 void ff_release (GtkWidget *widget, GdkEventButton *event)
   2635 {
   2636   if( (event->button == 1) )
   2637   {
   2638     /* Draw the button up image */
   2639     gtk_pixmap_set( GTK_PIXMAP( ff.wid ),
   2640                     ff.image.up_pixmap,
   2641                     ff.image.up_mask );
   2642   }
   2643 }
   2644 
   2645 
   2646 void rew_press (GtkWidget *widget, GdkEventButton *event)                              
   2647 {
   2648   /* Left button pressed */
   2649   if( event->button == 1 )
   2650   {
   2651     /* Draw the down image for REW */
   2652     gtk_pixmap_set( GTK_PIXMAP( rew.wid ),
   2653                     rew.image.dn_pixmap,
   2654                     rew.image.dn_mask );
   2655 
   2656     /* Tell the CD to play the previous track */
   2657     play_previous();
   2658     
   2659 
   2660     /* Update the state of the Play button -- should be a subroutine */
   2661     if( (display.playbtn != CDROM_PLAYING) && (display.status != CDROM_NODISC))
   2662       {
   2663      	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2664      			display.playup_pixmap,
   2665      			display.playup_mask );
   2666      	display.playbtn = CDROM_PLAYING;
   2667       }
   2668      
   2669   }
   2670 }
   2671 
   2672 
   2673 void rew_release (GtkWidget *widget, GdkEventButton *event)
   2674 {
   2675   if( (event->button == 1) )
   2676   {
   2677     /* Draw the up image for the button */
   2678     gtk_pixmap_set( GTK_PIXMAP( rew.wid ),
   2679                     rew.image.up_pixmap,
   2680                     rew.image.up_mask );
   2681   }
   2682 }
   2683 
   2684 
   2685 void play_press (GtkWidget *widget, GdkEventButton *event)                              
   2686 {
   2687   int x;
   2688 
   2689   /* Left button press */
   2690   if( event->button == 1 )
   2691   {
   2692     /* Depending on the current state of the button, display the correct image */
   2693     switch( display.playbtn )
   2694     {
   2695       case CDROM_PLAYING:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2696                     				display.playdn_pixmap,
   2697                     				display.playdn_mask );
   2698                                 pause_cdrom();
   2699 				display.playbtn = CDROM_PAUSED;
   2700       				break;
   2701 
   2702       case CDROM_PAUSED:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2703                     				display.playdn_pixmap,
   2704                     				display.playdn_mask );
   2705                                 resume_cdrom();
   2706 				display.playbtn = CDROM_PLAYING;
   2707       				break;
   2708 
   2709       case CDROM_NODISC:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2710                     				display.playdn_pixmap,
   2711                     				display.playdn_mask );
   2712 
   2713                                 play_track( 1 );
   2714 				update_display();
   2715 				display.playbtn = CDROM_PLAYING;
   2716 				break;
   2717     }    
   2718   } else if( event->button ==3 ) {
   2719     /* Stop playing without ejecting the CD */
   2720     switch( display.playbtn )
   2721       {
   2722 
   2723       case CDROM_PLAYING :	
   2724       case CDROM_PAUSED :
   2725 	stop_cdrom();
   2726 	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2727 			display.playdn_pixmap,
   2728 			display.playdn_mask );
   2729 	update_display();
   2730 	display.playbtn = CDROM_NODISC;
   2731 
   2732 	/* Clear out the track list */
   2733 	if( display.twindow != NULL )
   2734 	  {
   2735 	  gtk_clist_clear( GTK_CLIST( display.tclist ) );
   2736 
   2737 	  /* Clear the title */
   2738 	  gtk_label_set( GTK_LABEL( display.ttitle ), "" );
   2739 	  }
   2740 
   2741 	/* Erase the title and track names */
   2742 	if( cdinfo.title != NULL )
   2743 	  {
   2744 	    g_string_free( cdinfo.title, 1 );
   2745 	    cdinfo.title = NULL;
   2746 	  }
   2747 
   2748 	for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   2749 	  {
   2750 	    if( cdinfo.name[x] != NULL )
   2751 	      {
   2752 		g_string_free( cdinfo.name[x], 1 );
   2753 		cdinfo.name[x] = NULL;
   2754 	      }
   2755 	  }
   2756 
   2757 	/* Free up dynamically allocated cdinfo strings */
   2758 	free_cdinfo();
   2759 	break;
   2760 
   2761       case CDROM_NODISC :
   2762 	break;
   2763     }
   2764   }
   2765 }
   2766 
   2767 
   2768 void play_release (GtkWidget *widget, GdkEventButton *event)
   2769 {
   2770   if( (event->button == 1) || (event->button ==3) )
   2771   {
   2772     switch( display.playbtn )
   2773     {
   2774       case CDROM_PAUSED:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2775                     				display.pauseup_pixmap,
   2776                     				display.pauseup_mask );
   2777     				break;
   2778 
   2779       case CDROM_PLAYING:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2780                     				display.playup_pixmap,
   2781                     				display.playup_mask );
   2782     				break;
   2783 
   2784       case CDROM_NODISC:	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2785                     				display.stopup_pixmap,
   2786                     				display.stopup_mask );
   2787     				break;
   2788 
   2789     }
   2790   }
   2791 }
   2792 
   2793 
   2794 void eject_press (GtkWidget *widget, GdkEventButton *event)                              
   2795 {
   2796   int x;
   2797 
   2798   if( event->button == 1 )
   2799   {
   2800     gtk_pixmap_set( GTK_PIXMAP( eject.wid ),
   2801                     eject.image.dn_pixmap,
   2802                     eject.image.dn_mask );
   2803 
   2804 
   2805     /*
   2806      * If we are playing or paused then we will be stopped, so change
   2807      * the play button to the stop button
   2808      */
   2809     switch( display.status )
   2810       {
   2811       case CDROM_PAUSED :
   2812       case CDROM_PLAYING :
   2813 	gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   2814 			display.stopup_pixmap,
   2815 			display.stopup_mask );
   2816 	display.playbtn = CDROM_NODISC;
   2817 	break;
   2818       }
   2819     eject_cdrom();
   2820     display.playbtn = CDROM_NODISC;
   2821     gdk_draw_pixmap( display.wid->window,
   2822 		     display.wid->style->black_gc,
   2823 		     display.nodisc_pixmap,
   2824 		     0, 0,
   2825 		     0, 0, 92, 30 );
   2826 
   2827     /* Clear out the track list */
   2828     if( display.twindow != NULL )
   2829       {
   2830 	gtk_clist_clear( GTK_CLIST( display.tclist ) );
   2831 
   2832 	/* Clear the title */
   2833 	gtk_label_set( GTK_LABEL( display.ttitle ), "" );
   2834       }
   2835 
   2836 
   2837     /* Erase the title and track names */
   2838     if( cdinfo.title != NULL )
   2839       {
   2840 	g_string_free( cdinfo.title, 1 );
   2841 	cdinfo.title = NULL;
   2842       }
   2843 
   2844     for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   2845       {
   2846 	if( cdinfo.name[x] != NULL )
   2847 	  {
   2848 	    g_string_free( cdinfo.name[x], 1 );
   2849 	    cdinfo.name[x] = NULL;
   2850 	  }
   2851       }
   2852 
   2853     /* Free up the dynamically allocated cdinfo strings */
   2854     free_cdinfo();
   2855   }
   2856 }
   2857 
   2858 
   2859 void eject_release (GtkWidget *widget, GdkEventButton *event)
   2860 {
   2861   if( event->button == 1 )
   2862   {
   2863     gtk_pixmap_set( GTK_PIXMAP( eject.wid ),
   2864                     eject.image.up_pixmap,
   2865                     eject.image.up_mask );
   2866   }
   2867 }
   2868 
   2869 
   2870 gint ex_timer( gpointer data )
   2871 {
   2872   gtk_timeout_remove( ex.timer );
   2873 
   2874   gtk_main_quit();
   2875 
   2876   return FALSE;
   2877 }
   2878 
   2879 
   2880 void ex_press (GtkWidget *widget, GdkEventButton *event)                              
   2881 {
   2882   if( event->button == 1 )
   2883   {
   2884     gtk_pixmap_set( GTK_PIXMAP( ex.wid ),
   2885                     ex.image.dn_pixmap,
   2886                     ex.image.dn_mask );
   2887 
   2888     /* In 100mS execute the play_next function */
   2889     ex.timer = gtk_timeout_add( 500, ex_timer, NULL );
   2890 
   2891   }
   2892 }
   2893 
   2894 
   2895 void ex_release (GtkWidget *widget, GdkEventButton *event)
   2896 {
   2897   if( event->button == 1 )
   2898   {
   2899     gtk_pixmap_set( GTK_PIXMAP( ex.wid ),
   2900                     ex.image.up_pixmap,
   2901                     ex.image.up_mask );
   2902   }
   2903 }
   2904 
   2905 
   2906 void
   2907 destroy_window (GtkWidget  *widget,
   2908                 GtkWidget **window)
   2909 {
   2910   *window = NULL;
   2911 }
   2912 
   2913 
   2914 void close_setup( GtkWidget  *widget, GtkWidget *window)
   2915 {
   2916   gtk_widget_destroy( window );
   2917 
   2918   /* Write configuration */
   2919   write_config( &config );
   2920 
   2921   /* Send the new device to the cd_control process */
   2922   send_device();
   2923 }
   2924 
   2925 
   2926 /* Toggle the state of the cddb button */
   2927 static void toggle_cddb (GtkWidget *checkbutton,
   2928 		       GtkWidget *text)
   2929 {
   2930   config.cddb = GTK_TOGGLE_BUTTON(checkbutton)->active;
   2931 
   2932 #ifdef DEBUG3
   2933   g_print("Toggling cddb = %d\n", config.cddb );
   2934 #endif
   2935 
   2936   config.saved = 0;            /* Needs to be written to disk */
   2937 }
   2938 
   2939 
   2940 #ifdef CHANGER_SUPPORT
   2941 /* Toggle the CD changer support button */
   2942 static void toggle_changer (GtkWidget *checkbutton,
   2943 		       GtkWidget *text)
   2944 {
   2945   config.changer = GTK_TOGGLE_BUTTON(checkbutton)->active;
   2946 
   2947 #ifdef DEBUG3
   2948   g_print("Toggling changer = %d\n", config.changer );
   2949 #endif
   2950 
   2951   config.saved = 0;            /* Needs to be written to disk */
   2952 }
   2953 #endif
   2954 
   2955 
   2956 /* Toggle the Eject when done playing */
   2957 static void toggle_eject_done(GtkWidget *checkbutton,
   2958 		       GtkWidget *text)
   2959 {
   2960   config.done_eject = GTK_TOGGLE_BUTTON(checkbutton)->active;
   2961 
   2962 #ifdef DEBUG3
   2963   g_print("Toggling done_eject = %d\n", config.done_eject );
   2964 #endif
   2965 
   2966   config.saved = 0;            /* Needs to be written to disk */
   2967 }
   2968 
   2969 
   2970 /* Toggle the Eject at Close */
   2971 static void toggle_eject_exit(GtkWidget *checkbutton,
   2972 		       GtkWidget *text)
   2973 {
   2974   config.exit_eject = GTK_TOGGLE_BUTTON(checkbutton)->active;
   2975 
   2976 #ifdef DEBUG3
   2977   g_print("Toggling exit_eject = %d\n", config.exit_eject );
   2978 #endif
   2979 
   2980   set_eject( config.exit_eject );
   2981   config.saved = 0;            /* Needs to be written to disk */
   2982 }
   2983 
   2984 static void toggle_startup(GtkWidget *checkbutton,
   2985 		       GtkWidget *text)
   2986 {
   2987   config.startup = GTK_TOGGLE_BUTTON(checkbutton)->active;
   2988   config.saved = 0;            /* Needs to be written to disk */
   2989 }
   2990 
   2991 
   2992 void cddb_servers()
   2993 {
   2994 
   2995   /* Placeholder for server listing/editing */
   2996 }
   2997 
   2998 
   2999 static void
   3000 page_switch (GtkWidget *widget, GtkNotebookPage *page, gint page_num)
   3001 {
   3002   GtkNotebookPage *oldpage;
   3003 
   3004   oldpage = GTK_NOTEBOOK (widget)->cur_page;
   3005 
   3006   if (page == oldpage)
   3007     return;
   3008 
   3009 }
   3010 
   3011 
   3012 void device_entry(GtkWidget *widget, GtkWidget *entry)
   3013 {
   3014   gchar *entry_text;
   3015 
   3016   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   3017 
   3018   config.device = realloc( config.device, strlen(entry_text)+1);
   3019   strcpy( config.device, entry_text );
   3020   config.saved = 0;            /* Needs to be written to disk */
   3021 }
   3022 
   3023 void cddb_entry(GtkWidget *widget, GtkWidget *entry)
   3024 {
   3025   gchar *entry_text;
   3026 
   3027   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   3028 
   3029   config.local_cddb = realloc( config.local_cddb, strlen(entry_text)+1);
   3030   strcpy( config.local_cddb, entry_text );
   3031   strcpy( cdinfo.local_cddb, entry_text );
   3032 
   3033   config.saved = 0;            /* Needs to be written to disk */
   3034 }
   3035 
   3036 void cddb_to_email(GtkWidget *widget, GtkWidget *entry)
   3037 {
   3038   gchar *entry_text;
   3039 
   3040   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   3041 
   3042   config.to_cddbd = realloc( config.to_cddbd, strlen(entry_text)+1);
   3043   strcpy( config.to_cddbd, entry_text );
   3044 
   3045   config.saved = 0;            /* Needs to be written to disk */
   3046 }
   3047 
   3048 
   3049 void server_entry(GtkWidget *widget, GtkWidget *entry)
   3050 {
   3051   gchar *entry_text;
   3052 
   3053   entry_text = gtk_entry_get_text(GTK_ENTRY(entry));
   3054 
   3055   config.current = realloc( config.current, strlen(entry_text)+1);
   3056   strcpy( config.current, entry_text );
   3057   config.saved = 0;            /* Needs to be written to disk */
   3058 }
   3059 
   3060 
   3061 void refresh_servers()
   3062 {
   3063   char buffer[80];
   3064 
   3065   /* Need to lock this routine until we complete the last request */
   3066   if( display.cddb_lock == 0 )
   3067     {
   3068       /* Get the server list from cddb.cddb.com - DB_SITES */
   3069       /* Tell it to print the diagnostic string */
   3070       cdinfo.cddbd_cmnd = DB_SITES;
   3071 
   3072       /* Set the server to connect to */
   3073       strncpy( cdinfo.server, "cddb.cddb.com", 80 );
   3074       cdinfo.port = 888;
   3075       if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO ) ) < 0 )
   3076 	{
   3077 	  perror("write to cddbd error");
   3078 	}
   3079 
   3080       display.cddb_lock = 1;
   3081      
   3082       /* Start up a progress dialog */
   3083       display.progress = gtk_dialog_new ();
   3084 
   3085       sprintf( buffer, "Connecting to %s:%d", cdinfo.server, cdinfo.port );
   3086 
   3087       gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
   3088                           GTK_SIGNAL_FUNC(gtk_widget_destroy),
   3089                           &display.progress);
   3090 
   3091       gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
   3092       gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
   3093       gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
   3094 
   3095       display.plabel = gtk_label_new (buffer);
   3096       gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
   3097       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
   3098                           display.plabel, FALSE, FALSE, 0);
   3099       gtk_widget_show( display.plabel );
   3100       gtk_widget_show( display.progress );
   3101     }
   3102 }
   3103 
   3104 
   3105 /* -----------------------------------------------------------------------
   3106    Display the help/setup dialog box
   3107 
   3108    All changed info is saved when this dialog is exited.
   3109    ----------------------------------------------------------------------- */
   3110 gint help_timer( gpointer data )
   3111 {
   3112   struct SITE       *sp;
   3113   static GtkWidget  *window = NULL;
   3114   GtkWidget         *notebook,
   3115                     *text,
   3116                     *vbox1,
   3117                     *vbox2,
   3118                     *label,
   3119                     *button,
   3120                     *table,
   3121                     *hscrollbar,
   3122                     *vscrollbar,
   3123                     *edit;
   3124   GList             *cbitems = NULL;
   3125 
   3126 
   3127   gtk_timeout_remove( help.timer );   
   3128   help.timer = 0;
   3129 
   3130   /* Create the main window for the notebook */
   3131   if( !window )
   3132     {
   3133       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
   3134       gtk_signal_connect (GTK_OBJECT (window), "destroy",
   3135                           GTK_SIGNAL_FUNC(gtk_widget_destroyed),
   3136                           &window);
   3137 
   3138       gtk_window_set_title (GTK_WINDOW (window), "notebook");
   3139       gtk_container_border_width (GTK_CONTAINER (window), 0);
   3140       gtk_window_set_title (GTK_WINDOW( window ), "XfreeCD Setup");
   3141       gtk_widget_set_usize (window, 250, 300);
   3142       gtk_window_set_wmclass(GTK_WINDOW(window), "XfreeCDs", NULL );
   3143  
   3144       /* Create a vbox to hold the notebook */
   3145       vbox1 = gtk_vbox_new (FALSE, 0);
   3146       gtk_container_add (GTK_CONTAINER (window), vbox1);
   3147 
   3148       /* Create the notebook */
   3149       notebook = gtk_notebook_new ();
   3150       gtk_signal_connect (GTK_OBJECT (notebook), "switch_page",
   3151                           GTK_SIGNAL_FUNC (page_switch), NULL);
   3152       gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
   3153       gtk_box_pack_start (GTK_BOX (vbox1), notebook, TRUE, TRUE, 0);
   3154       gtk_container_border_width (GTK_CONTAINER (notebook), 10);
   3155       gtk_widget_realize (notebook);
   3156 
   3157       /* Create the about page for the notebook -- Put a text window in it */
   3158       vbox2 = gtk_vbox_new( FALSE, 0 );
   3159       gtk_widget_show( vbox2 );
   3160 
   3161       table = gtk_table_new (2, 2, FALSE);
   3162       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
   3163       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
   3164       gtk_box_pack_start (GTK_BOX (vbox2), table, TRUE, TRUE, 0);
   3165       gtk_widget_show (table);
   3166 
   3167       text = gtk_text_new (NULL, NULL);
   3168       gtk_text_set_editable (GTK_TEXT (text), FALSE);
   3169       gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
   3170                         GTK_EXPAND | GTK_SHRINK | GTK_FILL,
   3171                         GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
   3172       gtk_widget_show (text);
   3173 
   3174       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
   3175       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
   3176                         GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
   3177       gtk_widget_show (hscrollbar);
   3178 
   3179       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
   3180       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
   3181                         GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
   3182       gtk_widget_show (vscrollbar);
   3183 
   3184       /* Create the label box for the About page */
   3185       label = gtk_label_new ("About");
   3186       
   3187       /* Add it to the notebook */
   3188       gtk_notebook_append_page_menu (GTK_NOTEBOOK(notebook), vbox2, label, label  );
   3189 
   3190       gtk_text_freeze (GTK_TEXT (text));
   3191 
   3192       gtk_widget_realize (text);
   3193 
   3194       /* Add the actual text */
   3195       HELP_TEXT( "XfreeCD v0.7.8\n" );
   3196       HELP_TEXT( "Copyright 1998 Brian C. Lane\n" );
   3197       HELP_TEXT( "<nexus@tatoosh.com>\n" );
   3198       HELP_TEXT( "http://www.tatoosh.com/nexus\n\n" );
   3199 
   3200       HELP_TEXT( "Click on the 'SETUP' tab to set the\n" );
   3201       HELP_TEXT( "CD device and some options:\n\n" );
   3202       HELP_TEXT( "AutoPlay will start playing the CD\n" );
   3203       HELP_TEXT( "when XfreeCD is started\n\n" );
   3204       HELP_TEXT( "Eject when done will eject the CD\n" );
   3205       HELP_TEXT( "when it is finished playing if repeat\n" );
   3206       HELP_TEXT( "is not turned on (the button on the\n" );
   3207       HELP_TEXT( "main panel).\n\n" );
   3208       HELP_TEXT( "Eject on Exit will eject the CD if it\n");
   3209       HELP_TEXT( "is not playing when you exit.\n\n" );
   3210       HELP_TEXT( "Click on the 'CDDB' tab to set up\n" );
   3211       HELP_TEXT( "the support for online cd database\n" );
   3212       HELP_TEXT( "support. The Local CDDB Path\n" );
   3213       HELP_TEXT( "should point to a directory where\n" );
   3214       HELP_TEXT( "you have read and write\n");
   3215       HELP_TEXT( "permission. This is where the\n" );
   3216       HELP_TEXT( "track lists for the CDs will be\n");
   3217       HELP_TEXT( "saved.\n\n");
   3218       HELP_TEXT( "The CDDB Submit email field is for\n" );
   3219       HELP_TEXT( "the email address of the CDDB\n");
   3220       HELP_TEXT( "server to submit CD information\n" );
   3221       HELP_TEXT( "to. The send button is located\n" );
   3222       HELP_TEXT( "in the Track Edit window. Please\n" );
   3223       HELP_TEXT( "only submit CDs that do not exist\n");
   3224       HELP_TEXT( "in the database and that have\n" );
   3225       HELP_TEXT( "complete title and track text\n" );
   3226       HELP_TEXT( "entered.\n\n" );
   3227       HELP_TEXT( "The CDDB server is the internet\n" );
   3228       HELP_TEXT( "site where requests for unknown\n" );
   3229       HELP_TEXT( "CDs will be sent to. Initially only\n" );
   3230       HELP_TEXT( "cddb.cddb.com is listed. Click\n");
   3231       HELP_TEXT( "Refresh Server List to download a\n");
   3232       HELP_TEXT( "list of the available servers.\n" );
   3233       HELP_TEXT( "Use the combobox to pick a server\n" );
   3234       HELP_TEXT( "close to you. If you unselect cddb\n" );
   3235       HELP_TEXT( "support the internet server will\n" );
   3236       HELP_TEXT( "not be queried, but the local\n" );
   3237       HELP_TEXT( "database will be used.\n\n" );
   3238       HELP_TEXT( "Right click & drag on the main\n");
   3239       HELP_TEXT( "display window will allow you to\n" );
   3240       HELP_TEXT( "move XfreeCD even when you\n");
   3241       HELP_TEXT( "have title bars and handles. Left\n");
   3242       HELP_TEXT( "Clicking on the display will open\n");
   3243       HELP_TEXT( "up the Track List window.\n\n" );
   3244       HELP_TEXT( "In the Track List window you can\n");
   3245       HELP_TEXT( "click on the title of a track and\n");
   3246       HELP_TEXT( "XfreeCD will jump directly to\n" );
   3247       HELP_TEXT( "that track and play it.\n\n" );
   3248       HELP_TEXT( "Right clicking on the play/pause\n");
   3249       HELP_TEXT( "button will stop playing and\n");
   3250       HELP_TEXT( "turn off the CD player.\n\n");
   3251       HELP_TEXT( "Volume is controlled by left/right\n");
   3252       HELP_TEXT( "clicking on the volume button.\n\n");
   3253       HELP_TEXT( "Thanks to Ti Kan and Steve Scherf\n" );
   3254       HELP_TEXT( "for creating CDDB.\n\n" );
   3255 
   3256       gtk_text_thaw (GTK_TEXT (text));
   3257 
   3258       /* Add the general setup page */
   3259       vbox2 = gtk_vbox_new( FALSE, 0 );
   3260       gtk_widget_show( vbox2 );
   3261 
   3262       /* Add an editable field for the device */
   3263       label = gtk_label_new ("CDROM device:");
   3264       gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
   3265       gtk_widget_show (label);
   3266 
   3267       edit = gtk_entry_new_with_max_length(20);
   3268       gtk_signal_connect(GTK_OBJECT(edit), "changed",
   3269 			 GTK_SIGNAL_FUNC(device_entry),
   3270 			 edit);
   3271       gtk_widget_show( edit );
   3272 
   3273 #ifdef DEBUG3
   3274       g_print("config.device = %s\n", config.device );
   3275 #endif
   3276 
   3277       if( config.device != NULL )
   3278 	gtk_entry_set_text( GTK_ENTRY( edit ), config.device );
   3279       gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
   3280       gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
   3281       gtk_widget_show( edit );
   3282 
   3283 #ifdef DEBUG3
   3284       g_print("config.startup = %d\n", config.startup );
   3285 #endif
   3286       
   3287       /* Create a check button for Playing at Startup */
   3288       button = gtk_check_button_new_with_label("AutoPlay");
   3289       gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
   3290       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.startup == 1));
   3291       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3292 			  GTK_SIGNAL_FUNC( toggle_startup ), NULL );
   3293       gtk_widget_show( button );
   3294 
   3295 #ifdef DEBUG3
   3296       g_print("config.changer = %d\n", config.changer );
   3297 #endif
   3298 
   3299 #ifdef CHANGER_SUPPORT
   3300       /* Create a check button for CD Changer */
   3301       button = gtk_check_button_new_with_label("CD Changer");
   3302       gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
   3303       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.changer == 1));
   3304       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3305 			  GTK_SIGNAL_FUNC( toggle_changer ), NULL );
   3306       gtk_widget_show( button );
   3307 #endif
   3308 
   3309 #ifdef DEBUG3
   3310       g_print("config.done_eject = %d\n", config.done_eject );
   3311 #endif
   3312 
   3313       /* Create a check button for Eject on Close */
   3314       button = gtk_check_button_new_with_label("Eject when done");
   3315       gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
   3316       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.done_eject==1));
   3317       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3318 			  GTK_SIGNAL_FUNC( toggle_eject_done ), NULL );
   3319       gtk_widget_show( button );
   3320 
   3321       /* Create a check button for Eject on Exit (and not playing) */
   3322       button = gtk_check_button_new_with_label("Eject on Exit");
   3323       gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
   3324       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.exit_eject==1));
   3325       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3326 			  GTK_SIGNAL_FUNC( toggle_eject_exit ), NULL );
   3327       gtk_widget_show( button );
   3328 
   3329       
   3330       /* Create the label box for the Setup page */
   3331       label = gtk_label_new ("Setup");
   3332       
   3333       /* Add it to the notebook */
   3334       gtk_notebook_append_page_menu( GTK_NOTEBOOK( notebook ), vbox2, label, label );
   3335 
   3336 
   3337       /* Create the CDDB page for the notebook */
   3338       vbox2 = gtk_vbox_new( FALSE, 0 );
   3339       gtk_widget_show( vbox2 );
   3340 
   3341        /* Path to the local cddb database */
   3342       label = gtk_label_new( "Local CDDB Path" );
   3343       gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
   3344       gtk_widget_show (label);
   3345 
   3346      /* Add an editable field for the local CDDBD database */
   3347       edit = gtk_entry_new_with_max_length( 1023 );
   3348       gtk_signal_connect(GTK_OBJECT(edit), "changed",
   3349 			 GTK_SIGNAL_FUNC(cddb_entry),
   3350 			 edit);
   3351       gtk_widget_show( edit );
   3352 
   3353 #ifdef DEBUG3
   3354       g_print("config.local_cddb = %s\n", config.local_cddb );
   3355 #endif
   3356 
   3357       if( config.local_cddb != NULL )
   3358 	gtk_entry_set_text( GTK_ENTRY( edit ), config.local_cddb );
   3359       gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
   3360       gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
   3361       gtk_widget_show( edit );
   3362 
   3363        /* Email address for submitting to the database */
   3364       label = gtk_label_new( "CDDB Submit Email" );
   3365       gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
   3366       gtk_widget_show (label);
   3367 
   3368       /* Add an editable field for the email address */
   3369       edit = gtk_entry_new_with_max_length( 1023 );
   3370       gtk_signal_connect(GTK_OBJECT(edit), "changed",
   3371 			 GTK_SIGNAL_FUNC(cddb_to_email),
   3372 			 edit);
   3373       gtk_widget_show( edit );
   3374 
   3375 #ifdef DEBUG3
   3376       g_print("config.local_cddb = %s\n", config.local_cddb );
   3377 #endif
   3378 
   3379       if( config.to_cddbd != NULL )
   3380 	gtk_entry_set_text( GTK_ENTRY( edit ), config.to_cddbd );
   3381       gtk_editable_select_region( GTK_EDITABLE( edit ), 0, -1 );
   3382       gtk_box_pack_start( GTK_BOX( vbox2 ), edit, FALSE, FALSE, 0 );
   3383       gtk_widget_show( edit );
   3384 
   3385 
   3386 #ifdef DEBUG3
   3387       g_print("config.cddb = %d\n", config.cddb );
   3388 #endif
   3389 
   3390       /* Create a check button for CDDB support */
   3391       button = gtk_check_button_new_with_label("CDDB Support");
   3392       gtk_box_pack_start( GTK_BOX( vbox2 ), button, FALSE, FALSE, 0 );
   3393       gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button), (config.cddb == 1));
   3394       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3395 			  GTK_SIGNAL_FUNC( toggle_cddb ), NULL );
   3396       gtk_widget_show( button );
   3397 
   3398 
   3399       /* Create the list of servers, use a combobox for this */
   3400       label = gtk_label_new( "CDDB Server" );
   3401       gtk_box_pack_start (GTK_BOX (vbox2), label, FALSE, FALSE, 0);
   3402       gtk_widget_show (label);
   3403 
   3404       /* Walk the list */
   3405       cbitems = NULL;
   3406       sp = config.server;
   3407       while( sp != NULL )
   3408 	{
   3409 	  cbitems = g_list_append(cbitems, sp->name);
   3410 	  sp = sp->next;
   3411 	}
   3412       
   3413       display.cb = gtk_combo_new ();
   3414       gtk_combo_set_popdown_strings (GTK_COMBO (display.cb), cbitems);
   3415 
   3416       /* Here we should put the selected server into the entry box */
   3417       gtk_entry_set_text (GTK_ENTRY (GTK_COMBO(display.cb)->entry), config.current);
   3418 
   3419       gtk_signal_connect(GTK_OBJECT(GTK_COMBO(display.cb)->entry), "changed",
   3420 			 GTK_SIGNAL_FUNC(server_entry),
   3421 			 GTK_COMBO(display.cb)->entry);
   3422       gtk_editable_select_region (GTK_EDITABLE (GTK_COMBO(display.cb)->entry),
   3423                                   0, -1);
   3424       gtk_box_pack_start (GTK_BOX (vbox2), display.cb, FALSE, FALSE, 0);
   3425       gtk_widget_show (display.cb);
   3426 
   3427 
   3428       /* Add a button to refresh the list with */
   3429       button = gtk_button_new_with_label("Refresh Server List");
   3430       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   3431                                  GTK_SIGNAL_FUNC (refresh_servers),
   3432                                  GTK_OBJECT( window ));
   3433       gtk_box_pack_end (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
   3434 
   3435       /* Create the label box for the CDDB page */
   3436       label = gtk_label_new ("CDDB");
   3437       
   3438       /* Add it to the notebook */
   3439       gtk_notebook_append_page_menu( GTK_NOTEBOOK( notebook ), vbox2, label, label );
   3440 
   3441       /* Add the close button */
   3442       button = gtk_button_new_with_label ("close");
   3443       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   3444                                  GTK_SIGNAL_FUNC (close_setup),
   3445                                  GTK_OBJECT( window ));
   3446       gtk_box_pack_start (GTK_BOX (vbox1), button, FALSE, FALSE, 0);
   3447       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   3448       gtk_widget_grab_default (button);
   3449     }
   3450 
   3451   if (!GTK_WIDGET_VISIBLE (window))
   3452     gtk_widget_show_all (window);
   3453   else
   3454     gtk_widget_destroy( window );
   3455 
   3456   return FALSE;
   3457 }
   3458 
   3459 
   3460 #ifdef GOOBER1
   3461 void old_stuff()
   3462 {
   3463 
   3464   /* A useful frame with 3 radio buttons in it */
   3465 
   3466       /* Create a frame with the Startup options as radio buttons */
   3467       frame = gtk_frame_new("Startup Action");
   3468       gtk_box_pack_start(GTK_BOX(vbox2), frame, FALSE, FALSE, 0);
   3469       gtk_widget_show(frame);
   3470 
   3471       /* Add the general setup page */
   3472       vbox3 = gtk_vbox_new( FALSE, 0 );
   3473       gtk_container_add(GTK_CONTAINER(frame), vbox3);
   3474       gtk_container_border_width(GTK_CONTAINER(vbox3), 0); 
   3475       gtk_widget_show( vbox3 );
   3476 
   3477       /* create radio button for Play at startup */
   3478       button = gtk_radio_button_new_with_label( NULL, "Play CD");
   3479       if( config.startup == STARTUP_PLAY )
   3480 	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
   3481       else
   3482  	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
   3483       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3484 			  GTK_SIGNAL_FUNC( play_setup ), NULL );
   3485       gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
   3486       gtk_widget_show(button);
   3487  
   3488       button = gtk_radio_button_new_with_label(
   3489 	      gtk_radio_button_group (GTK_RADIO_BUTTON (button)), "Show Tracks");
   3490       if( config.startup == STARTUP_TRACKS )
   3491 	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
   3492       else
   3493  	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
   3494       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3495 			  GTK_SIGNAL_FUNC( tracks_setup ), NULL );
   3496       gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
   3497       gtk_widget_show(button);
   3498  
   3499       button = gtk_radio_button_new_with_label(
   3500 	      gtk_radio_button_group (GTK_RADIO_BUTTON (button)), "No Disc");
   3501       if( config.startup == STARTUP_NODISC )
   3502 	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), TRUE);
   3503       else
   3504  	gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (button), FALSE);
   3505       gtk_signal_connect( GTK_OBJECT( button ), "toggled",
   3506 			  GTK_SIGNAL_FUNC( nothing_setup ), NULL );
   3507       gtk_box_pack_start(GTK_BOX(vbox3), button, FALSE, FALSE, 0);
   3508       gtk_widget_show(button);
   3509  
   3510 
   3511 
   3512 
   3513 
   3514 
   3515 
   3516 
   3517 
   3518   GtkWidget *hbox;
   3519   GtkWidget *button;
   3520   GtkWidget *check;
   3521   GtkWidget *separator;
   3522   GtkWidget *table;
   3523   GtkWidget *hscrollbar;
   3524   GtkWidget *vscrollbar;
   3525   GtkWidget *text;
   3526 
   3527     gtk_container_border_width (GTK_CONTAINER (box2), 10);
   3528     gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
   3529     gtk_widget_show (box2);
   3530 
   3531 
   3532     table = gtk_table_new (2, 2, FALSE);
   3533     gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
   3534     gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
   3535     gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
   3536     gtk_widget_show (table);
   3537 
   3538     text = gtk_text_new (NULL, NULL);
   3539     gtk_text_set_editable (GTK_TEXT (text), TRUE);
   3540     gtk_table_attach (GTK_TABLE (table), text, 0, 1, 0, 1,
   3541 		      GTK_EXPAND | GTK_SHRINK | GTK_FILL,
   3542 		      GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
   3543     gtk_widget_show (text);
   3544 
   3545     /*      hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
   3546      *      gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
   3547      *			GTK_EXPAND | GTK_FILL | GTK_SHRINK, GTK_FILL, 0, 0);
   3548      *      gtk_widget_show (hscrollbar);
   3549      */
   3550 
   3551     vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
   3552     gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
   3553 		      GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
   3554     gtk_widget_show (vscrollbar);
   3555     
   3556 
   3557     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3558                      "This program will play CDs using an attached ", -1);
   3559     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3560                      "IDE or SoundBlaster CD player. It currently ", -1);
   3561     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3562                      "does not work with SCSI drives.\n\n", -1);
   3563     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3564                      "The latest version is available from my webpage.", -1);
   3565     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3566                      "Please email me with any suggestions, problems,", -1);
   3567     gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
   3568                      "or comments you may have\n\n", -1);
   3569      
   3570     hbox = gtk_hbutton_box_new ();
   3571     gtk_box_pack_start (GTK_BOX (box2), hbox, FALSE, FALSE, 0);
   3572     gtk_widget_show (hbox);
   3573 
   3574     /*      check = gtk_check_button_new_with_label("Editable");
   3575      *      gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, FALSE, 0);
   3576      *      gtk_signal_connect (GTK_OBJECT(check), "toggled",
   3577      *			  GTK_SIGNAL_FUNC(text_toggle_editable), text);
   3578      *      gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), TRUE);
   3579      *      gtk_widget_show (check);
   3580      */
   3581 
   3582     /*      check = gtk_check_button_new_with_label("Wrap Words");
   3583      *      gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
   3584      *      gtk_signal_connect (GTK_OBJECT(check), "toggled",
   3585      *			  GTK_SIGNAL_FUNC(text_toggle_word_wrap), text);
   3586      *      gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
   3587      *      gtk_widget_show (check);
   3588      */
   3589 
   3590     /* Checkbox for www.cddb.com support */
   3591     check = gtk_check_button_new_with_label("www.cddb.com");
   3592     gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
   3593     gtk_signal_connect (GTK_OBJECT(check), "toggled",
   3594 			GTK_SIGNAL_FUNC(about_toggle_cddb), text);
   3595     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
   3596     gtk_widget_show (check);
   3597 
   3598     /* Checkbox for CD Changer support */
   3599     check = gtk_check_button_new_with_label("CD changer");
   3600     gtk_box_pack_start (GTK_BOX (hbox), check, FALSE, TRUE, 0);
   3601     gtk_signal_connect (GTK_OBJECT(check), "toggled",
   3602 			GTK_SIGNAL_FUNC(about_toggle_cddb), text);
   3603     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(check), FALSE);
   3604     gtk_widget_show (check);
   3605 
   3606     gtk_text_set_word_wrap(GTK_TEXT(text), TRUE );
   3607     
   3608     separator = gtk_hseparator_new ();
   3609     gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
   3610     gtk_widget_show (separator);
   3611 
   3612 
   3613     box2 = gtk_vbox_new (FALSE, 10);
   3614     gtk_container_border_width (GTK_CONTAINER (box2), 10);
   3615     gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
   3616     gtk_widget_show (box2);
   3617 
   3618 }
   3619 #endif
   3620 
   3621 
   3622 void help_press (GtkWidget *widget, GdkEventButton *event)                              
   3623 {
   3624   if( event->button == 1 )
   3625   {
   3626     gtk_pixmap_set( GTK_PIXMAP( help.wid ),
   3627                     help.image.dn_pixmap,
   3628                     help.image.dn_mask );
   3629 
   3630     /* In XXXmS execute the help dialog window function */
   3631     help.timer = gtk_timeout_add( 500, help_timer, NULL );
   3632   }
   3633 }
   3634 
   3635 
   3636 void help_release (GtkWidget *widget, GdkEventButton *event)
   3637 {
   3638   if( event->button == 1 )
   3639   {
   3640     gtk_pixmap_set( GTK_PIXMAP( help.wid ),
   3641                     help.image.up_pixmap,
   3642                     help.image.up_mask );
   3643   }
   3644 }
   3645 
   3646 
   3647 void tdisp_press (GtkWidget *widget, GdkEventButton *event)                              
   3648 {
   3649   if( event->button == 1 )
   3650   {
   3651     /* Cycle through the time display options */
   3652     switch( display.time )
   3653     {
   3654       case TRACK_ELAPSED :	display.time = TRACK_REMAIN;
   3655       				gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3656       						display.plstrkdn_pixmap,
   3657       						display.plstrkdn_mask );
   3658       				break;
   3659       				
   3660       case TRACK_REMAIN :	display.time = CD_ELAPSED;
   3661       				gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3662       						display.mnstrkdn_pixmap,
   3663       						display.mnstrkdn_mask );
   3664       				break;
   3665       				
   3666       case CD_ELAPSED :		display.time = CD_REMAIN;
   3667       				gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3668       						display.plscddn_pixmap,
   3669       						display.plscddn_mask );
   3670       				break;
   3671       				
   3672       case CD_REMAIN :		display.time = TRACK_ELAPSED;
   3673       				gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3674       						display.mnscddn_pixmap,
   3675       						display.mnscddn_mask );
   3676       				break;
   3677     }
   3678   }
   3679 }
   3680 
   3681 
   3682 void tdisp_release (GtkWidget *widget, GdkEventButton *event)
   3683 {
   3684   if( event->button == 1 )
   3685   {
   3686     switch( display.time )
   3687     {
   3688       case TRACK_ELAPSED :	gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3689       						display.plstrkup_pixmap,
   3690       						display.plstrkup_mask );
   3691       				break;
   3692       				
   3693       case TRACK_REMAIN :	gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3694       						display.mnstrkup_pixmap,
   3695       						display.mnstrkup_mask );
   3696       				break;
   3697       				
   3698       case CD_ELAPSED :		gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3699       						display.plscdup_pixmap,
   3700       						display.plscdup_mask );
   3701       				break;
   3702       				
   3703       case CD_REMAIN :		gtk_pixmap_set( GTK_PIXMAP( tdisp.wid ),
   3704       						display.mnscdup_pixmap,
   3705       						display.mnscdup_mask );
   3706       				break;
   3707     }
   3708   }
   3709 }
   3710 
   3711 
   3712 void rpt_press (GtkWidget *widget, GdkEventButton *event)                              
   3713 {
   3714   if( event->button == 1 )
   3715   {
   3716     gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
   3717                     rpt.image.dn_pixmap,
   3718                     rpt.image.dn_mask );
   3719 
   3720     if( display.repeat == 0 )
   3721       display.repeat = 1;
   3722     else
   3723       display.repeat = 0;
   3724   }
   3725 }
   3726 
   3727 
   3728 void rpt_release (GtkWidget *widget, GdkEventButton *event)
   3729 {
   3730   if( event->button == 1 )
   3731   {
   3732     if( display.repeat == 0 )
   3733     {
   3734       gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
   3735                       rpt.image.up_pixmap,
   3736                       rpt.image.up_mask );
   3737     } else {
   3738       gtk_pixmap_set( GTK_PIXMAP( rpt.wid ),
   3739                       display.rptupact_pixmap,
   3740                       display.rptupact_mask );
   3741     }
   3742   }
   3743 }
   3744 
   3745 
   3746 void vol_press (GtkWidget *widget, GdkEventButton *event)                              
   3747 {
   3748   switch( event->button )
   3749   {
   3750     case 1 :	gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
   3751                    	        vol.image.dn_pixmap,
   3752                     		vol.image.dn_mask );
   3753     		set_vol( +10 );
   3754 		update_display();
   3755     		break;
   3756     		
   3757     case 3 :	gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
   3758                    	        vol.image.dn_pixmap,
   3759                     		vol.image.dn_mask );
   3760 		set_vol( -10 );
   3761 		update_display();
   3762     		break;
   3763     	
   3764   }
   3765 }
   3766 
   3767 void vol_release (GtkWidget *widget, GdkEventButton *event)
   3768 {
   3769   if( (event->button == 1) || (event->button == 3) )
   3770   {
   3771     gtk_pixmap_set( GTK_PIXMAP( vol.wid ),
   3772                     vol.image.up_pixmap,
   3773                     vol.image.up_mask );
   3774   }
   3775 }
   3776 
   3777 /* -----------------------------------------------------------------------------------------
   3778    Show the track title listbox, allow editing of it. Hilight the current track. Show
   3779    total time for each track. Show discid for the CD. Allow sending to CDDB.
   3780    ----------------------------------------------------------------------------------------- */
   3781 gint show_tracks( )
   3782 {
   3783 
   3784   return FALSE;
   3785 }
   3786 
   3787 
   3788 /* -----------------------------------------------------------------------
   3789    Redraw the uncovered part of the display
   3790    ----------------------------------------------------------------------- */
   3791 static int expose_display( GtkWidget *widget, GdkEventExpose *event )
   3792 {
   3793   switch( display.status )
   3794   {
   3795     case CDROM_PLAYING :	gdk_draw_pixmap (widget->window,
   3796                      				 widget->style->black_gc,
   3797                      				 display.null_pixmap, 
   3798                      				 event->area.x, event->area.y,
   3799                      				 event->area.x, event->area.y,
   3800                      				 event->area.width, event->area.height);
   3801   				break;
   3802 
   3803     case CDROM_NODISC :		gdk_draw_pixmap (widget->window,
   3804                      				 widget->style->black_gc,
   3805                      				 display.nodisc_pixmap, 
   3806                      				 event->area.x, event->area.y,
   3807                      				 event->area.x, event->area.y,
   3808                      				 event->area.width, event->area.height);
   3809   				break;
   3810   }
   3811                                                                                                        
   3812   return FALSE;
   3813 }
   3814 
   3815 
   3816 
   3817 
   3818 
   3819 /* -----------------------------------------------------------------------
   3820    Update the cdrom status and the display
   3821    
   3822    This routine is called once per second to check for the end of the CD
   3823    and to update the display.
   3824    ----------------------------------------------------------------------- */
   3825 gint update_cdrom( gpointer data )
   3826 {
   3827   /* Get the latest info into cdinfo structure */
   3828   cdrom_status();
   3829   
   3830   return TRUE;
   3831 }
   3832 
   3833 
   3834 /* -----------------------------------------------------------------------
   3835    Draw the current track # onto the null_pixmap and then copy the
   3836    pixmap to the display
   3837    ----------------------------------------------------------------------- */
   3838 void draw_track()
   3839 {
   3840   gdk_draw_pixmap( display.null_pixmap,
   3841  		   display.wid->style->black_gc,
   3842 		   display.a_pixmap[display.track/10],
   3843                    0, 0,
   3844                    2, 2, 11, 22 );
   3845 
   3846   gdk_draw_pixmap( display.null_pixmap,
   3847                    display.wid->style->black_gc,
   3848                    display.a_pixmap[display.track%10],
   3849                    0, 0,
   3850                    15, 2, 11, 22 );
   3851 
   3852   gdk_draw_pixmap( display.wid->window,
   3853                    display.wid->style->black_gc,
   3854                    display.null_pixmap,
   3855                    0, 0,
   3856                    0, 0, 92, 30 );
   3857 }		  
   3858 
   3859 
   3860 /* -----------------------------------------------------------------------
   3861    Draw the minute digits onto the null_pixmap and then copy the
   3862    pixmap to the display. Also draws the minus sign if needed.
   3863    ----------------------------------------------------------------------- */
   3864 void draw_minute()
   3865 {
   3866   /* Show or don't show the minus sign */
   3867   if((display.minute<0) || (display.second<0))
   3868     {
   3869       gdk_draw_pixmap( display.null_pixmap,
   3870 		       display.wid->style->black_gc,
   3871 		       display.minus_pixmap,
   3872 		       0, 0,
   3873 		       32, 10, 8, 16 );
   3874     } else {
   3875       gdk_draw_pixmap( display.null_pixmap,
   3876 		       display.wid->style->black_gc,
   3877 		       display.b_pixmap[10],
   3878 		       0, 0,
   3879 		       32, 10, 8, 16 );
   3880     }
   3881 
   3882   gdk_draw_pixmap( display.null_pixmap,
   3883                    display.wid->style->black_gc,
   3884                    display.b_pixmap[abs(display.minute)/10],
   3885                    0, 0,
   3886                    42, 10, 8, 16 );
   3887 
   3888   gdk_draw_pixmap( display.null_pixmap,
   3889                    display.wid->style->black_gc,
   3890                    display.b_pixmap[abs(display.minute)%10],
   3891                    0, 0,
   3892                    52, 10, 8, 16 );
   3893 
   3894   gdk_draw_pixmap( display.wid->window,
   3895                    display.wid->style->black_gc,
   3896                    display.null_pixmap,
   3897                    0, 0,
   3898                    0, 0, 92, 30 );
   3899 }
   3900 
   3901 
   3902 /* -----------------------------------------------------------------------
   3903    Draw the second digits onto the null_pixmap and then copy the
   3904    pixmap to the display.
   3905    ----------------------------------------------------------------------- */
   3906 void draw_second()
   3907 {
   3908   /* Show or don't show the minus sign */
   3909   if((display.minute<0) || (display.second<0))
   3910     {
   3911       gdk_draw_pixmap( display.null_pixmap,
   3912 		       display.wid->style->black_gc,
   3913 		       display.minus_pixmap,
   3914 		       0, 0,
   3915 		       32, 10, 8, 16 );
   3916     } else {
   3917       gdk_draw_pixmap( display.null_pixmap,
   3918 		       display.wid->style->black_gc,
   3919 		       display.b_pixmap[10],
   3920 		       0, 0,
   3921 		       32, 10, 8, 16 );
   3922     }
   3923 
   3924   gdk_draw_pixmap( display.null_pixmap,
   3925                    display.wid->style->black_gc,
   3926                    display.b_pixmap[abs(display.second/10)],
   3927                    0, 0,
   3928                    68, 10, 8, 16 );
   3929 
   3930   gdk_draw_pixmap( display.null_pixmap,
   3931                    display.wid->style->black_gc,
   3932                    display.b_pixmap[abs(display.second%10)],
   3933                    0, 0,
   3934                    78, 10, 8, 16 );
   3935 
   3936   gdk_draw_pixmap( display.wid->window,
   3937                    display.wid->style->black_gc,
   3938                    display.null_pixmap,
   3939                    0, 0,
   3940                    0, 0, 92, 30 );
   3941 }
   3942 
   3943 
   3944 /* -----------------------------------------------------------------------
   3945    Erase the minute digits and the minus sign. This is used by the blink
   3946    routine.
   3947    ----------------------------------------------------------------------- */
   3948 void erase_minute()
   3949 {				  
   3950   gdk_draw_pixmap( display.null_pixmap,
   3951                    display.wid->style->black_gc,
   3952                    display.b_pixmap[10],
   3953                    0, 0,
   3954                    32, 10, 8, 16 );
   3955 
   3956   gdk_draw_pixmap( display.null_pixmap,
   3957                    display.wid->style->black_gc,
   3958                    display.b_pixmap[10],
   3959                    0, 0,
   3960                    42, 10, 8, 16 );
   3961 
   3962   gdk_draw_pixmap( display.null_pixmap,
   3963                    display.wid->style->black_gc,
   3964                    display.b_pixmap[10],
   3965                    0, 0,
   3966                    52, 10, 8, 16 );
   3967 
   3968   gdk_draw_pixmap( display.wid->window,
   3969                    display.wid->style->black_gc,
   3970                    display.null_pixmap,
   3971                    0, 0,
   3972                    0, 0, 92, 30 );
   3973 }
   3974 
   3975 
   3976 /* -----------------------------------------------------------------------
   3977    Erase the second digits. This is used by the blink routine.
   3978    ----------------------------------------------------------------------- */
   3979 void erase_second()
   3980 {
   3981   gdk_draw_pixmap( display.null_pixmap,
   3982                    display.wid->style->black_gc,
   3983                    display.b_pixmap[10],
   3984                    0, 0,
   3985                    68, 10, 8, 16 );
   3986 
   3987   gdk_draw_pixmap( display.null_pixmap,
   3988                    display.wid->style->black_gc,
   3989                    display.b_pixmap[10],
   3990                    0, 0,
   3991                    78, 10, 8, 16 );
   3992 
   3993   gdk_draw_pixmap( display.wid->window,
   3994                    display.wid->style->black_gc,
   3995                    display.null_pixmap,
   3996                    0, 0,
   3997                    0, 0, 92, 30 );
   3998 }
   3999 
   4000 
   4001 /* -----------------------------------------------------------------------
   4002    Decide what to show on the display
   4003    If there is no disc, then show NO DISC bitmap
   4004    If playing then update the display digits
   4005    If paused then blink the time
   4006    
   4007    It keeps track of the previous states of things so only the areas that
   4008    have changed get updated each time. An improvement would be to only
   4009    update the digits that change (instead of both minute or both second
   4010    digits), but it isn't that important.
   4011    ----------------------------------------------------------------------- */
   4012 void update_display()
   4013 {
   4014   static int	curr_track=-1,		/* Default to unknown value	*/
   4015   		curr_minute=-1,
   4016   		curr_second=-1,
   4017   		curr_status=-1,
   4018                 curr_volume=-1,
   4019   		blinking=0,
   4020                 tclist_track=-1;        /* Track selected in tclist */ 
   4021                 
   4022   int		x;
   4023 
   4024   /*
   4025    * If the volume needs updating then we draw a bargraph, one bar for
   4026    *  every 17 volume units. 15 bars maximum with the last 5 being red
   4027    */
   4028   if( curr_volume != cdinfo.volume )
   4029   {
   4030     for( x = 0; x < (cdinfo.volume / 17); x++ )
   4031     {
   4032       if( x < 10 )
   4033       {
   4034         gdk_draw_pixmap( display.null_pixmap,
   4035                          display.wid->style->black_gc,
   4036                          display.bar_pixmap,
   4037                          0, 0,
   4038                          30+(x*4), 1, 4, 7 );
   4039       } else {
   4040         gdk_draw_pixmap( display.null_pixmap,
   4041                          display.wid->style->black_gc,
   4042                          display.redbar_pixmap,
   4043                          0, 0,
   4044                          30+(x*4), 1, 4, 7 );
   4045       }
   4046     }
   4047     for( ; x < 15; x++ )
   4048     {
   4049       gdk_draw_pixmap( display.null_pixmap,
   4050                        display.wid->style->black_gc,
   4051                        display.nobar_pixmap,
   4052                        0, 0,
   4053                        30+(x*4), 1, 4, 7 );
   4054     }
   4055 
   4056     gdk_draw_pixmap( display.wid->window,
   4057                      display.wid->style->black_gc,
   4058                      display.null_pixmap,
   4059                      0, 0,
   4060                      0, 0, 92, 30 );
   4061 
   4062     curr_volume = cdinfo.volume;
   4063   }
   4064 
   4065   switch( display.time )
   4066     {
   4067     case TRACK_ELAPSED :
   4068       display.minute = cdinfo.sc.cdsc_reladdr.msf.minute;
   4069       display.second = cdinfo.sc.cdsc_reladdr.msf.second;
   4070       break;
   4071 
   4072     case TRACK_REMAIN :
   4073       display.minute = 0 - ( (cdinfo.track[display.track-1].length/60) - cdinfo.sc.cdsc_reladdr.msf.minute);
   4074       display.second = 0 - ( (cdinfo.track[display.track-1].length%60) - cdinfo.sc.cdsc_reladdr.msf.second);
   4075       break;
   4076       
   4077     case CD_ELAPSED : 
   4078       display.minute = cdinfo.sc.cdsc_absaddr.msf.minute;
   4079       display.second = cdinfo.sc.cdsc_absaddr.msf.second;
   4080       break;
   4081       
   4082     case CD_REMAIN :
   4083       display.minute = 0-cdinfo.cd_remaining/60;
   4084       display.second = 0-cdinfo.cd_remaining%60;
   4085       break;  
   4086     }
   4087 
   4088 
   4089   switch( display.status )
   4090   {
   4091     case CDROM_PLAYING :       	if( display.track != curr_track )
   4092     				{
   4093     				  draw_track();
   4094     				  
   4095   				  curr_track = display.track;
   4096     				}
   4097     
   4098     				if( display.minute != curr_minute )
   4099     				{
   4100 				  draw_minute();
   4101   				  
   4102     				  curr_minute = display.minute;
   4103     				}
   4104     
   4105     				if( display.second != curr_second )
   4106     				{
   4107 				  draw_second();
   4108 				  
   4109    				  curr_second = display.second;
   4110    				}
   4111 				curr_status = display.status;
   4112 
   4113 				if( display.playbtn != CDROM_PLAYING )
   4114 				  {
   4115 				    gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   4116 						    display.playup_pixmap,
   4117 						    display.playup_mask );
   4118 
   4119 				    display.playbtn = CDROM_PLAYING;
   4120 				  }
   4121 
   4122 				/* If the track window is open, update selected track */
   4123 				if( display.twindow != NULL )
   4124 				  {
   4125 				    if( tclist_track+1 != curr_track )
   4126 				      {
   4127 					if( curr_track > 0 )
   4128 					  tclist_track = curr_track -1;
   4129 					else
   4130 					  tclist_track = 0;
   4131 					gtk_clist_select_row( GTK_CLIST(display.tclist), tclist_track, -1 );
   4132 				      }
   4133 				  }
   4134 				
   4135    				break;
   4136    
   4137     /* Flash the min/sec digits while paused */
   4138     case CDROM_PAUSED :		if( display.track != curr_track )
   4139                                 {
   4140     				  draw_track();
   4141     				  
   4142   				  curr_track = display.track;
   4143     				}
   4144     
   4145 
   4146                                 if( blinking )
   4147     				{
   4148 				  draw_second();
   4149     				  draw_minute();
   4150     				  blinking=0;  
   4151     				} else {
   4152     				  erase_second();
   4153     				  erase_minute();
   4154     				  curr_second = -1;
   4155     				  curr_minute = -1;
   4156     				  blinking=1;
   4157     				}
   4158 
   4159                                 if( display.playbtn != CDROM_PAUSED )
   4160 				  {
   4161 				    gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   4162 						    display.pauseup_pixmap,
   4163 						    display.pauseup_mask );
   4164 				    display.playbtn = CDROM_PAUSED;
   4165 				  }
   4166     				break; 
   4167     
   4168     				/* Display NO DISC */
   4169     case CDROM_NODISC :		if( curr_status != display.status )
   4170     				{
   4171     				  gdk_draw_pixmap( display.wid->window,
   4172     				  		   display.wid->style->black_gc,
   4173     						   display.nodisc_pixmap,
   4174     						   0, 0,
   4175     						   0, 0, 92, 30 );
   4176 
   4177     				  curr_status = display.status;
   4178     				}
   4179 
   4180                                 if( display.playbtn != CDROM_NODISC )
   4181 				  {
   4182 				    gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   4183 						    display.stopup_pixmap,
   4184 						    display.stopup_mask );
   4185 				    display.playbtn = CDROM_NODISC;
   4186 				  }
   4187 
   4188 				break;
   4189   }
   4190 
   4191 #ifdef DEBUG4
   4192   g_print( "update called. status = %d  remain = %d\n", display.status, cdinfo.cd_remaining );
   4193   g_print( "        curr  new\n");
   4194   g_print( "track   %03d : %03d\n", curr_track, display.track );
   4195   g_print( "minute  %03d : %03d\n", curr_minute, display.minute );
   4196   g_print( "second  %03d : %03d\n", curr_second, display.second );
   4197   g_print( "volume  %03d : %03d\n", curr_volume, cdinfo.volume );
   4198 #endif
   4199 
   4200 }
   4201 
   4202 
   4203 /* ------------------------------------------------------------------------
   4204    Process data coming back from the cd_control process
   4205 
   4206    This is a stateless process since it only returns a cdinfo structure
   4207    ------------------------------------------------------------------------ */
   4208 void wait_status()
   4209 {
   4210   struct CDINFO tmpinfo;
   4211   struct SITE   *sp;
   4212   char          buffer[80];
   4213   int           x;
   4214   char         *texts[3],
   4215                text1[255],
   4216                text2[255];
   4217 
   4218 
   4219   /* Read the new cdinfo structure from the cd_control process */
   4220   if( read( cd_fd, &tmpinfo, sizeof(struct CDINFO) ) < 0 )
   4221     perror("cdrom_status(read)");
   4222 
   4223   /* Copy the useful bits from tmpinfo  to cdinfo */
   4224   memcpy( &cdinfo.tochdr, &tmpinfo.tochdr, sizeof(tmpinfo.tochdr) );
   4225   memcpy( &cdinfo.leadout, &tmpinfo.leadout, sizeof(tmpinfo.leadout) );
   4226   memcpy( &cdinfo.volume, &tmpinfo.volume, sizeof(tmpinfo.volume) );
   4227 
   4228   /* Copy track length, but not the track name over from the status */
   4229   for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   4230     {
   4231       cdinfo.track[x].te = tmpinfo.track[x].te;
   4232       cdinfo.track[x].length = tmpinfo.track[x].length;
   4233       cdinfo.track[x].frame_offset = tmpinfo.track[x].frame_offset;
   4234     }
   4235 
   4236   memcpy( &cdinfo.sc, &tmpinfo.sc, sizeof(tmpinfo.sc) );
   4237   memcpy( &cdinfo.ti, &tmpinfo.ti, sizeof(tmpinfo.ti) );
   4238   cdinfo.cd_length = tmpinfo.cd_length;
   4239   cdinfo.discid = tmpinfo.discid;
   4240 
   4241   switch( cdinfo.sc.cdsc_audiostatus )
   4242     {
   4243     case CDROM_AUDIO_PLAY :
   4244     case CDROM_AUDIO_PAUSED :
   4245       if( cdinfo.sc.cdsc_audiostatus == CDROM_AUDIO_PLAY )
   4246 	display.status = CDROM_PLAYING;
   4247       else
   4248 	display.status = CDROM_PAUSED;
   4249 
   4250       /* Update the currently playing track */
   4251       display.track = cdinfo.sc.cdsc_trk;
   4252 
   4253       /* Update the number of seconds remaining on the CD */
   4254       cdinfo.cd_remaining = cdinfo.cd_length - ((cdinfo.sc.cdsc_absaddr.msf.minute*60)+cdinfo.sc.cdsc_absaddr.msf.second);
   4255 
   4256       /* Do we need track data? Can we request it? */
   4257       if( (display.cddb_lock == 0) && (cdinfo.revision == -1))
   4258 	{
   4259 	  if( config.cddb == 1 )
   4260 	    {   
   4261 #ifdef DEBUG9
   4262 	      g_print("reading cddb info %d\n", cdinfo.revision );
   4263 #endif
   4264 	      /* Try and find a cddb entry in the local CD database */
   4265 	      if( read_cddb( &cdinfo ) < 0 )
   4266 		{
   4267 		  /* Default server to query */
   4268 		  strcpy( cdinfo.server, "cddb.cddb.com" );
   4269 		  cdinfo.port = 888;
   4270 
   4271 		  /* So here we query the default internet server */
   4272 		  if( config.current != NULL )
   4273 		    {
   4274 		      /* Use the current + 888 even if not found in the list */
   4275 		      strcpy( cdinfo.server, config.current );
   4276 
   4277 		      /* Find the current server in the list */
   4278 		      sp = config.server;
   4279 		      while( sp != NULL )
   4280 			{
   4281 			  if( strcmp( sp->name, config.current ) == 0 )
   4282 			    {
   4283 			      strcpy( cdinfo.server, sp->name );
   4284 			      cdinfo.port = sp->port;
   4285 			      sp = NULL;
   4286 			    }else {
   4287 			      sp = sp->next;
   4288 			    }
   4289 			}
   4290 		    }
   4291 		  /* Send the command to the server - Starts a state machine */
   4292 		  cdinfo.cddbd_cmnd = DB_READ;
   4293 		  if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO) ) < 0 )
   4294 		    {
   4295 		      perror("write to cddbd_fd error");
   4296 		      if( cdinfo.revision < 0 )
   4297 			cdinfo.revision = -2;     /* Failed, Don't try again */
   4298 		    }
   4299 		  /* Lock the use of the internet database */
   4300 		  display.cddb_lock = 1;
   4301 
   4302 		  /* !!!! This could probably be turned into a subroutine
   4303 		     !!!! */
   4304 
   4305 		  /* Start up a progress dialog */
   4306 		  display.progress = gtk_dialog_new ();
   4307 		  
   4308 		  sprintf(buffer, "Connecting to %s:%d", cdinfo.server,
   4309 			                                 cdinfo.port );
   4310 
   4311 		  gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
   4312 				      GTK_SIGNAL_FUNC(gtk_widget_destroy),
   4313 				      &display.progress);
   4314 
   4315 		  gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
   4316 		  gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
   4317 		  gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
   4318 
   4319 		  display.plabel = gtk_label_new (buffer);
   4320 		  gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
   4321 		  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
   4322 				      display.plabel, FALSE, FALSE, 0);
   4323 		  gtk_widget_show( display.plabel );
   4324 		  gtk_widget_show( display.progress );
   4325 		} else {
   4326 		  /* Read of local database successful */
   4327 #ifdef DEBUG5
   4328 		  g_print("Title : %s\n", cdinfo.title->str );
   4329 		  for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   4330 		    {
   4331 		      g_print("Track %d : %s\n", x+1, cdinfo.name[x]->str );
   4332 		    }
   4333 #endif
   4334 		}
   4335 	    } else {
   4336 	      /* Read the data from the local database, or set defaults */
   4337 	      local_cddb();
   4338 	    }
   4339 
   4340 	  /* If the track list window is open, update the list */
   4341 	  if( display.twindow != NULL )
   4342 	    {
   4343 #ifdef DEBUG9
   4344 	      g_print("Updating Track Window revision=%d\n", cdinfo.revision);
   4345 #endif
   4346 
   4347 	      gtk_clist_freeze (GTK_CLIST (display.tclist));
   4348 	      
   4349 	      /* Clear out the old list first */
   4350 	      gtk_clist_clear( GTK_CLIST( display.tclist ) );
   4351 	      
   4352 	      /* Set the new title */
   4353 	      if( cdinfo.title != NULL )
   4354 		sprintf( text1, "%s", cdinfo.title->str );
   4355 	      else
   4356 		strcpy( text1, "" );
   4357 	      gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
   4358 	      
   4359 	      /* Add the track names */
   4360 	      for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   4361 		{
   4362 		  sprintf( text1, "%d", x+1 );
   4363 		  sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
   4364 		  texts[0] = text1;
   4365 		  texts[2] = text2;
   4366 		  if( cdinfo.name[x] != NULL )
   4367 		    texts[1] = cdinfo.name[x]->str;
   4368 		  else
   4369 		    texts[1] = "(blank)";
   4370 		  gtk_clist_append (GTK_CLIST (display.tclist), texts);
   4371 		}
   4372 	      gtk_clist_thaw( GTK_CLIST( display.tclist ) );
   4373 	      gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
   4374 	    }
   4375 	}
   4376       break;
   4377 
   4378 #ifdef OLD_WAY					
   4379       case CDROM_AUDIO_PAUSED :
   4380       display.status = CDROM_PAUSED;
   4381       break;
   4382 #endif
   4383 
   4384     case CDROM_AUDIO_NO_STATUS :
   4385       /* Only clean things up once */
   4386       if( display.status != CDROM_NODISC )
   4387 	{
   4388 	  /* Clear out the track list */
   4389 	  if( display.twindow != NULL )
   4390 	    {
   4391 	      gtk_clist_clear( GTK_CLIST( display.tclist ) );
   4392 	      
   4393 	      /* Clear the title */
   4394 	      gtk_label_set( GTK_LABEL( display.ttitle ), "" );
   4395 	    }
   4396 
   4397 	  /* Free dynamically allocated cdinfo strings */
   4398 	  free_cdinfo();
   4399 
   4400 	  display.status = CDROM_NODISC;
   4401 	  /* Reset the revision # so next will recall from database*/
   4402 	  cdinfo.revision = -1;
   4403 	}
   4404       break;
   4405 
   4406     default:
   4407       /* Only cleanup once */
   4408       if( display.status != CDROM_NODISC )
   4409 	{
   4410 	  /* Clear out the track list */
   4411 	  if( display.twindow != NULL )
   4412 	    {
   4413 	      gtk_clist_clear( GTK_CLIST( display.tclist ) );
   4414 
   4415 	      /* Clear the title */
   4416 	      gtk_label_set( GTK_LABEL( display.ttitle ), "" );
   4417 	    }
   4418 
   4419 	  /* Free dynamically allocated cdinfo strings */
   4420 	  free_cdinfo();
   4421 
   4422 	  display.status = CDROM_NODISC;
   4423 	  cdinfo.revision = -1;
   4424 	}
   4425       break;
   4426   }  
   4427 
   4428 
   4429   /*
   4430    * If the CD is over then either restart it, or eject it, or show NO DISC
   4431    */
   4432   if( (cdinfo.cd_remaining == 0) && (display.status == CDROM_PLAYING) )
   4433     {
   4434       /* If repeat is on, start playing the cd over from the start */
   4435       if( display.repeat == 1 )
   4436 	{
   4437 	  display.track = cdinfo.tochdr.cdth_trk0;
   4438 	  play_track( cdinfo.tochdr.cdth_trk0 );
   4439 	} else {
   4440 	  /* Clear out the track list */
   4441 	  if( display.twindow != NULL )
   4442 	    {
   4443 	      gtk_clist_clear( GTK_CLIST( display.tclist ) );
   4444 
   4445 	      /* Clear the title */
   4446 	      gtk_label_set( GTK_LABEL( display.ttitle ), "" );
   4447 	    }
   4448 
   4449 	  /* Free up any dynamically allocated cdinfo strings */
   4450 	  free_cdinfo();
   4451 
   4452 	  /* Eject when done Playing - only eject if the user has enabled this */
   4453 	  if( config.done_eject == 1 )
   4454 	    {
   4455 	      /* All done, eject it for the next disc! */
   4456 	      eject_cdrom();
   4457 	    } else {
   4458 	      stop_cdrom();
   4459 	    }
   4460 
   4461 	  gtk_pixmap_set( GTK_PIXMAP( play.wid ),
   4462 			  display.stopup_pixmap,
   4463 			  display.stopup_mask );
   4464 	  display.playbtn = CDROM_NODISC;
   4465 	}
   4466     }
   4467 }
   4468 
   4469 
   4470 /* -------------------------------------------------------------------------
   4471    Process data coming back from the cd_control process
   4472 
   4473    This is a stateless process since it only returns a cdinfo structure
   4474    ------------------------------------------------------------------------- */
   4475 void cd_fd_read( gpointer data, gint source, GdkInputCondition condition)
   4476 {
   4477   wait_status();
   4478 
   4479   if( display.startup == 0 )
   4480     {
   4481       if( config.startup == 1 )
   4482 	{
   4483 	  /* If it is already playing, don't restart it */
   4484 	  if( ( cdinfo.sc.cdsc_audiostatus != CDROM_AUDIO_PLAY ) &&
   4485 	      (cdinfo.discid != 0 ) )
   4486 	    {
   4487 #ifdef DEBUG6
   4488 	      g_print("Starting play of cd. audiostatus = %d\n", cdinfo.sc.cdsc_audiostatus );
   4489 #endif
   4490 	      play_track( 1 );
   4491 	    }
   4492 	}
   4493 
   4494       /* Stop trying after the discid gets set by cd_control process */
   4495       if( cdinfo.discid != 0 )
   4496 	display.startup = 1;
   4497     }
   4498 
   4499   update_display();
   4500 }
   4501 
   4502 
   4503 /* ------------------------------------------------------------------------
   4504    this is the signal handler that gets called if GtkList
   4505    emits the "selection_changed" signal
   4506    ------------------------------------------------------------------------ */
   4507 void inexact_selected( GtkWidget *gtklist, gpointer func_data)
   4508 {
   4509   char      *p,
   4510             buffer[80];
   4511   GList     *dlist;
   4512   GtkObject *list_item;
   4513   gchar     *item_data_string;
   4514 
   4515 
   4516   /* fetch the doubly linked list of selected items
   4517    * of the GtkList, remember to treat this as read-only!
   4518    */
   4519   dlist=GTK_LIST(gtklist)->selection;
   4520 
   4521   /* if there are no selected items there is nothing more
   4522    * to do than just telling the user so
   4523    */
   4524   if (dlist)
   4525     {
   4526       list_item=GTK_OBJECT(dlist->data);
   4527       item_data_string=gtk_object_get_data(list_item,
   4528 					   "XfreeCDkey");
   4529       p = strtok( item_data_string, " \n" );
   4530       p = strtok( NULL, " \n" );
   4531 
   4532 #ifdef DEBUG3
   4533       g_print("Retrieving data for %s\n", p);
   4534 #endif
   4535 
   4536       /* Change the discid so the new request will work */
   4537       sscanf( p, "%lx", &cdinfo.discid );
   4538       cdinfo.cddbd_cmnd = DB_READ;
   4539       if( write( cddbd_fd, &cdinfo, sizeof( struct CDINFO) ) < 0 )
   4540 	{
   4541 	  perror("write to cddbd_fd error");
   4542 	  if( cdinfo.revision < 0 )
   4543 	    cdinfo.revision = -2;     /* Failed, Don't try again */
   4544 	}
   4545       /* Lock use of the CDDB internet server */
   4546       display.cddb_lock = 1;
   4547 
   4548       /* Delete the old progress box */
   4549       gtk_widget_destroy( display.progress );
   4550       display.plabel = NULL;
   4551 
   4552       /* Start up a new progress dialog */
   4553       display.progress = gtk_dialog_new ();
   4554 
   4555       sprintf(buffer, "Connecting to %s:%d", cdinfo.server, cdinfo.port );
   4556 
   4557       gtk_signal_connect( GTK_OBJECT( display.progress ), "destroy",
   4558 			  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   4559 			  &display.progress);
   4560 
   4561       gtk_window_set_title (GTK_WINDOW (display.progress), "CDDBD Status");
   4562       gtk_container_border_width (GTK_CONTAINER (display.progress), 0);
   4563       gtk_window_set_wmclass(GTK_WINDOW(display.progress), "XfreeCDp", NULL );
   4564 
   4565       display.plabel = gtk_label_new (buffer);
   4566       gtk_misc_set_padding (GTK_MISC (display.plabel), 10, 10);
   4567       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->vbox),
   4568 			  display.plabel, FALSE, FALSE, 0);
   4569       gtk_widget_show( display.plabel );
   4570       gtk_widget_show( display.progress );
   4571     } else {
   4572       gtk_widget_destroy( display.progress );
   4573       display.plabel = NULL;
   4574     }
   4575 }
   4576 
   4577 
   4578 
   4579 
   4580 /* -------------------------------------------------------------------------
   4581    Process data coming back from the cddbd process
   4582 
   4583    This routine processes data from the pipe to the cddbd process. This gets
   4584    the track names and site list from the server.
   4585    ------------------------------------------------------------------------- */
   4586 void cddbd_fd_read( gpointer data, gint source, GdkInputCondition condition)
   4587 {
   4588   struct CDINFO tmpinfo;
   4589   int           x;
   4590   static struct SITE   *sp,
   4591                        *sp_top=NULL,
   4592                        *sp_end=NULL;
   4593   char          *p, buffer[80];
   4594   gchar         *string;
   4595   GtkWidget     *button,
   4596                 *scrolled_win,
   4597                 *list,
   4598                 *vbox1,
   4599                 *label;
   4600   static GList  *cbitems=NULL;
   4601   char         *texts[3],
   4602                text1[255],
   4603                text2[255];
   4604 
   4605 
   4606   /* Read back the status structure */
   4607   if( read( cddbd_fd, &tmpinfo, sizeof( struct CDINFO ) ) != sizeof( struct CDINFO ) )
   4608     {
   4609       perror( "CDDBD status read" );
   4610       close( cd_fd );
   4611       close( cddbd_fd );
   4612       exit(-1);
   4613     }
   4614 
   4615   switch( tmpinfo.cddbd_stat )
   4616     {
   4617     case CDDBD_OPEN_OK :
   4618 #ifdef DEBUG3
   4619       g_print("Connected to %s ok\n", tmpinfo.server );
   4620 #endif
   4621 
   4622       if( display.plabel != NULL )
   4623 	{
   4624 	  sprintf (buffer, "Connected to %s:%d", tmpinfo.server, tmpinfo.port);
   4625 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4626 	}
   4627       break;
   4628 
   4629     case CDDBD_DONE_OK :
   4630       display.cddb_lock = 0;
   4631 
   4632       /* Copy the info from the tmpinfo to cdinfo */
   4633 #ifdef DEBUG3
   4634       g_print("Got the new data ok\n");
   4635       g_print("tmpinfo.title->str = 0x%X\n", tmpinfo.title->str );
   4636 #endif
   4637 
   4638 #ifdef DUMB
   4639       if( display.plabel != NULL )
   4640 	{
   4641 	  sprintf (buffer, "Retrieved %s", tmpinfo.title->str );
   4642 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4643 	}
   4644 #endif
   4645 
   4646       gtk_widget_destroy( display.progress );
   4647       display.plabel = NULL;
   4648 
   4649       /* Read the new data from the database */
   4650       local_cddb();
   4651 
   4652       /* If the track list window is open, update the list */
   4653       if( display.twindow != NULL )
   4654 	{
   4655 	  gtk_clist_freeze (GTK_CLIST (display.tclist));
   4656 	  
   4657 	  /* Clear out the old list first */
   4658 	  gtk_clist_clear( GTK_CLIST( display.tclist ) );
   4659 
   4660 	  /* Set the new title */
   4661 	  if( cdinfo.title != NULL )
   4662 	    sprintf( text1, "%s", cdinfo.title->str );
   4663 	  else
   4664 	    strcpy( text1, "" );
   4665 	  gtk_label_set( GTK_LABEL( display.ttitle ), text1 );
   4666 
   4667 	  /* Add the track names */
   4668 	  for( x = 0; x < cdinfo.tochdr.cdth_trk1; x++ )
   4669 	    {
   4670 	      sprintf( text1, "%d", x+1 );
   4671 	      sprintf( text2, "%d:%02d",cdinfo.track[x].length/60,cdinfo.track[x].length%60 );
   4672 	      texts[0] = text1;
   4673 	      texts[2] = text2;
   4674 	      if( cdinfo.name[x] != NULL )
   4675 		texts[1] = cdinfo.name[x]->str;
   4676 	      else
   4677 		texts[1] = "(blank)";
   4678 	      gtk_clist_append (GTK_CLIST (display.tclist), texts);
   4679 	    }
   4680 	  gtk_clist_thaw( GTK_CLIST( display.tclist ) );
   4681 	  gtk_clist_select_row( GTK_CLIST(display.tclist), display.track-1, -1 );
   4682 	}
   4683       break;
   4684       
   4685       /* Process the line of text */
   4686     case CDDBD_MOTD_LINE :
   4687       
   4688       if( tmpinfo.line[0] == '.' )
   4689 	{
   4690 	  display.cddb_lock = 0;
   4691 #ifdef DEBUG3
   4692 	  g_print("End of MOTD\n");
   4693 #endif
   4694 
   4695 	  gtk_widget_destroy( display.progress );
   4696 	  display.plabel = NULL;
   4697 
   4698 	} else {
   4699 #ifdef DEBUG3
   4700 	  /* Do something with the MOTD, for now print it */
   4701 	  g_print( "%s", tmpinfo.line );
   4702 #endif
   4703 
   4704 	  if( display.plabel != NULL )
   4705 	    {
   4706 	      gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
   4707 	    }
   4708 	}
   4709       break;
   4710 
   4711       /* Process the line of text */
   4712     case CDDBD_SITE_LINE :
   4713       
   4714       if( tmpinfo.line[0] == '.' )
   4715 	{
   4716 	  display.cddb_lock = 0;
   4717 #ifdef DEBUG3
   4718 	  g_print("End of Site listing\n");
   4719 #endif
   4720 	  if( display.plabel != NULL )
   4721 	    {
   4722 	      sprintf (buffer, "Retrieved Site Listing ok" );
   4723 	      gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4724 	    }
   4725 
   4726 	  if( sp_top != NULL )
   4727 	    {
   4728 	      /* Everything seems okay, erase the old list and add the new one */
   4729 	      while( config.server != NULL )
   4730 		{
   4731 		  sp = config.server;
   4732 		  config.server = config.server->next;
   4733 		  free( sp->name );
   4734 		  free( sp );
   4735 		}
   4736 
   4737 	      /* Point config to the new list */
   4738 	      config.server = sp_top;
   4739 	      sp_top = sp_end = NULL;
   4740 
   4741 	      config.saved = 0;             /* Need to save this to disk */
   4742 	      write_config( &config );      /* Write new list to disk */
   4743 	      strncpy( cdinfo.device, config.device, 80 );
   4744 
   4745 	      /* Need to let the site list in the dialog box be updated if it is
   4746 		 still being shown (user could close it before we finish).
   4747 	      */
   4748 	      if( display.cb != NULL )
   4749 		{
   4750 
   4751 		  /* Walk the list */
   4752 		  cbitems = NULL;
   4753 		  sp = config.server;
   4754 		  while( sp != NULL )
   4755 		    {
   4756 		      cbitems = g_list_append(cbitems, sp->name);
   4757 		      sp = sp->next;
   4758 		    }
   4759       
   4760 		  gtk_combo_set_popdown_strings (GTK_COMBO (display.cb), cbitems);
   4761 		  cbitems = NULL;
   4762 		}
   4763 
   4764 	    }
   4765 	  gtk_widget_destroy( display.progress );
   4766 	  display.plabel = NULL;
   4767 
   4768 	} else {
   4769 	  /* Do something with the SITE, for now print it */
   4770 	  /* Need to store in a temp. List until complete, then move to config */
   4771 #ifdef DEBUG3
   4772 	  g_print( "%s", tmpinfo.line );
   4773 #endif
   4774 
   4775 	  if( display.plabel != NULL )
   4776 	    {
   4777 	      gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
   4778 	    }
   4779 
   4780 	  /* Reserve some space for this server, add it to the end of the list */
   4781 	  if( ( sp = (struct SITE *) malloc( sizeof(struct SITE) ) ) == NULL )
   4782 	    {
   4783 	      perror("malloc - 1");
   4784 	    }
   4785 	  bzero( sp, sizeof(struct SITE) );
   4786       
   4787 	  p = strtok( tmpinfo.line, " \n" );
   4788 
   4789 	  /* Reserve space for the name */
   4790 	  if( ( sp->name = (char *) malloc( strlen(p)+1 ) ) == NULL )
   4791 	    {
   4792 	      perror("malloc - 2");
   4793 	    }
   4794 	  strcpy( sp->name, p );
   4795 
   4796 	  p = strtok( NULL, " \n" );
   4797 	  sp->port = atoi(p);
   4798 	  sp->next = NULL;
   4799 
   4800 	  /* Is it the first in the list? */
   4801 	  if( sp_top == NULL )
   4802 	    {
   4803 	      /* Yep, First in the list */
   4804 	      sp_top = sp;
   4805 	      sp_end = sp;
   4806 	    } else {
   4807 	      /* No, add it to the end of the list */
   4808 	      sp_end->next = sp;
   4809 	      sp_end = sp;
   4810 	    }
   4811 	}
   4812       break;
   4813 
   4814 
   4815       /* Process the line of text inexact matches */
   4816     case CDDBD_INEX_LINE :
   4817       
   4818       if( tmpinfo.line[0] == '.' )
   4819 	{
   4820 	  cdinfo.revision = -2;
   4821 	  display.cddb_lock = 0;
   4822 
   4823 #ifdef DEBUG3
   4824 	  g_print("End of Inexact Match listing\n");
   4825 #endif
   4826 
   4827 	  if( display.plabel != NULL )
   4828 	    {
   4829 	      sprintf (buffer, "Retrieved inexact matches for discid 0x%08lx", cdinfo.discid );
   4830 	      gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4831 	    }
   4832 
   4833 	  /* Here we want to add a list of the sites to the progress box,
   4834 	     add a cancel button, and add a signal to use one of the inexact
   4835 	     matches in a new search.
   4836 	  */
   4837 	  if( cbitems != NULL )
   4838 	    {
   4839 	      /* Add a vbox to hold the list and cancel button */
   4840 	      vbox1 = gtk_vbox_new( FALSE, 0 );
   4841 	      gtk_box_pack_start( GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   4842 				  vbox1, FALSE, TRUE, 1 );
   4843 	      gtk_widget_show( vbox1 );
   4844 	      
   4845 	      scrolled_win = gtk_scrolled_window_new (NULL, NULL);
   4846 	      gtk_widget_set_usize( scrolled_win, 250, 100 );
   4847 	      gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
   4848 					      GTK_POLICY_AUTOMATIC,
   4849 					      GTK_POLICY_AUTOMATIC);
   4850 	      gtk_box_pack_start (GTK_BOX( vbox1 ), scrolled_win, FALSE, FALSE, 0);
   4851 	      gtk_widget_show (scrolled_win);
   4852 
   4853 	      /* Put a list into the scrolling window */
   4854 	      list = gtk_list_new ();
   4855 	      gtk_list_set_selection_mode (GTK_LIST (list), GTK_SELECTION_SINGLE);
   4856 	      gtk_container_add (GTK_CONTAINER (scrolled_win), list);
   4857 	      gtk_container_set_focus_vadjustment (GTK_CONTAINER (list),
   4858 	      	   gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (scrolled_win)));
   4859 	      gtk_signal_connect(GTK_OBJECT(list),
   4860 				 "selection_changed",
   4861 				 GTK_SIGNAL_FUNC(inexact_selected),
   4862 				 NULL);
   4863 	      gtk_widget_show (list);
   4864 
   4865 	      /* Add the list of inexact matches to the list from cbitems */
   4866 	      gtk_list_append_items( GTK_LIST( list ), cbitems );
   4867 
   4868 	      button = gtk_button_new_with_label ("Cancel");
   4869 	      gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   4870 					 GTK_SIGNAL_FUNC(gtk_widget_destroy),
   4871 					 GTK_OBJECT (display.progress));
   4872 	      GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   4873 	      gtk_box_pack_start (GTK_BOX( vbox1 ), button, TRUE, TRUE, 0);
   4874 	      gtk_widget_grab_default (button);
   4875 	      gtk_widget_show (button);
   4876 	      
   4877 	      cbitems = NULL;
   4878 	    } else {
   4879 	      gtk_widget_destroy( display.progress );
   4880 	      display.plabel = NULL;
   4881 	    }
   4882 	} else {
   4883 	  /* Need to store in a temp. List until we receive the last line */
   4884 #ifdef DEBUG3
   4885 	  g_print( "%s", tmpinfo.line );
   4886 #endif
   4887 
   4888 	  /* Show the line in the progress box */
   4889 	  if( display.plabel != NULL )
   4890 	    {
   4891 	      gtk_label_set (GTK_LABEL (display.plabel), tmpinfo.line);
   4892 	    }
   4893 
   4894 	  /* Add it to a list of sites */
   4895 	  /* Need to detect first line of this type and set cbitems to NULL, or else
   4896 	     set it to null when everything else is finished with it.
   4897 	  */
   4898 	  if( tmpinfo.line[strlen(tmpinfo.line)-1] == '\n' )
   4899 	    {
   4900 	      tmpinfo.line[strlen(tmpinfo.line)-1] = 0x00;
   4901 	    }
   4902 
   4903 	  button=gtk_list_item_new();
   4904 	  label=gtk_label_new( tmpinfo.line );
   4905 	  gtk_misc_set_alignment (GTK_MISC(label), 0.0, 0.5);
   4906 	  gtk_container_add (GTK_CONTAINER (button), label);
   4907 	  gtk_widget_show (label);
   4908 	  gtk_widget_show(button);
   4909 	  gtk_label_get(GTK_LABEL(label), &string);
   4910 	  gtk_object_set_data(GTK_OBJECT(button),
   4911 			      "XfreeCDkey",
   4912 			      string);
   4913 	  cbitems = g_list_append(cbitems, button);
   4914 
   4915 	}
   4916       break;
   4917 
   4918 
   4919 
   4920     case CDDBD_MATCH_OK :
   4921 #ifdef DEBUG3
   4922       g_print("Got a match for 0x%08lx\n", tmpinfo.discid );
   4923 #endif
   4924 
   4925       if( display.plabel != NULL )
   4926 	{
   4927 	  sprintf (buffer, "Retrieving data for id 0x%08lx", tmpinfo.discid);
   4928 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4929 	}
   4930 
   4931       break;
   4932       
   4933     case CDDBD_ENTRY_OK :
   4934 #ifdef DEBUG3
   4935       g_print("Reading CDDB Database entry from %s\n", tmpinfo.server );
   4936 #endif
   4937 
   4938       if( display.plabel != NULL )
   4939 	{
   4940 	  sprintf (buffer, "Reading CDDB info from %s", tmpinfo.server);
   4941 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4942 	}
   4943       break;
   4944       
   4945     case CDDBD_DONE_ERR :
   4946       cdinfo.revision = -2;
   4947       display.cddb_lock = 0;
   4948 
   4949 
   4950 #ifdef DEBUG3
   4951       g_print("Error reading temporary file\n");
   4952 #endif
   4953 
   4954       if( display.plabel != NULL )
   4955 	{
   4956 	  sprintf (buffer, "Error Reading temp. file");
   4957 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4958 	}
   4959 
   4960       button = gtk_button_new_with_label ("OK");
   4961       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   4962                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   4963                                  GTK_OBJECT (display.progress));
   4964       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   4965       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   4966                           button, TRUE, TRUE, 0);
   4967       gtk_widget_grab_default (button);
   4968       gtk_widget_show (button);
   4969 
   4970       break;
   4971       
   4972     case CDDBD_OPEN_ERR :
   4973       cdinfo.revision = -2;
   4974       display.cddb_lock = 0;
   4975 
   4976 #ifdef DEBUG3
   4977       g_print("Failed to connect to %s\n", tmpinfo.server );
   4978       g_print("%s\n", tmpinfo.line );
   4979 #endif
   4980 
   4981       if( display.plabel != NULL )
   4982 	{
   4983 	  sprintf (buffer, "Failed to connect to %s", tmpinfo.server );
   4984 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   4985 	}
   4986 
   4987       button = gtk_button_new_with_label ("OK");
   4988       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   4989                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   4990                                  GTK_OBJECT (display.progress));
   4991       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   4992       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   4993                           button, TRUE, TRUE, 0);
   4994       gtk_widget_grab_default (button);
   4995       gtk_widget_show (button);
   4996 
   4997       break;
   4998       
   4999     case CDDBD_READ_ERR :
   5000       cdinfo.revision = -2;
   5001       display.cddb_lock = 0;
   5002 
   5003 #ifdef DEBUG3
   5004       g_print("Socket read failed\n");
   5005 #endif
   5006 
   5007       if( display.plabel != NULL )
   5008 	{
   5009 	  sprintf (buffer, "Socket Read Failed");
   5010 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   5011 	}
   5012 
   5013       button = gtk_button_new_with_label ("OK");
   5014       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   5015                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   5016                                  GTK_OBJECT (display.progress));
   5017       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   5018       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   5019                           button, TRUE, TRUE, 0);
   5020       gtk_widget_grab_default (button);
   5021       gtk_widget_show (button);
   5022 
   5023       break;
   5024       
   5025     case CDDBD_WRITE_ERR :
   5026       cdinfo.revision = -2;
   5027       display.cddb_lock = 0;
   5028 
   5029 #ifdef DEBUG3
   5030       g_print("Socket write failed\n");
   5031 #endif
   5032 
   5033       if( display.plabel != NULL )
   5034 	{
   5035 	  sprintf (buffer, "Socket Write Failed");
   5036 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   5037 	}
   5038 
   5039       button = gtk_button_new_with_label ("OK");
   5040       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   5041                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   5042                                  GTK_OBJECT (display.progress));
   5043       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   5044       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   5045                           button, TRUE, TRUE, 0);
   5046       gtk_widget_grab_default (button);
   5047       gtk_widget_show (button);
   5048 
   5049       break;
   5050       
   5051     case CDDBD_TMPF_ERR :
   5052       cdinfo.revision = -2;
   5053       display.cddb_lock = 0;
   5054 
   5055 #ifdef DEBUG3
   5056       g_print("Error, cannot create a temporary filename\n");
   5057 #endif
   5058 
   5059       if( display.plabel != NULL )
   5060 	{
   5061 	  sprintf (buffer, "Cannot Create temp. file");
   5062 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   5063 	}
   5064 
   5065       button = gtk_button_new_with_label ("OK");
   5066       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   5067                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   5068                                  GTK_OBJECT (display.progress));
   5069       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   5070       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   5071                           button, TRUE, TRUE, 0);
   5072       gtk_widget_grab_default (button);
   5073       gtk_widget_show (button);
   5074 
   5075       break;
   5076       
   5077     case CDDBD_FOPEN_ERR :
   5078       cdinfo.revision = -2;
   5079       display.cddb_lock = 0;
   5080 
   5081 #ifdef DEBUG3
   5082       g_print("Error opening temporary file\n");
   5083 #endif
   5084 
   5085       if( display.plabel != NULL )
   5086 	{
   5087 	  sprintf (buffer, "Error opening temp. file");
   5088 	  gtk_label_set (GTK_LABEL (display.plabel), buffer);
   5089 	}
   5090 
   5091       button = gtk_button_new_with_label ("OK");
   5092       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   5093                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   5094                                  GTK_OBJECT (display.progress));
   5095       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   5096       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   5097                           button, TRUE, TRUE, 0);
   5098       gtk_widget_grab_default (button);
   5099       gtk_widget_show (button);
   5100 
   5101       break;
   5102       
   5103     default:
   5104       cdinfo.revision = -2;
   5105       display.cddb_lock = 0;
   5106 
   5107 #ifdef DEBUG3
   5108       g_print("Server returned %d\n", tmpinfo.cddbd_stat );
   5109 #endif
   5110 
   5111       if( display.plabel != NULL )
   5112 	{
   5113 	  sprintf( buffer, "Server: %s", tmpinfo.line );
   5114 	  gtk_label_set (GTK_LABEL (display.plabel), buffer );
   5115 	}
   5116 
   5117       button = gtk_button_new_with_label ("OK");
   5118       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
   5119                                  GTK_SIGNAL_FUNC(gtk_widget_destroy),
   5120                                  GTK_OBJECT (display.progress));
   5121       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
   5122       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (display.progress)->action_area),
   5123                           button, TRUE, TRUE, 0);
   5124       gtk_widget_grab_default (button);
   5125       gtk_widget_show (button);
   5126 
   5127       break;
   5128     }
   5129 }
   5130 
   5131 
   5132 /* -----------------------------------------------------------------------
   5133    Get the current status of the cdrom and fill in the display info
   5134 
   5135    return -1 = error writing
   5136    ----------------------------------------------------------------------- */
   5137 int cdrom_status()
   5138 {
   5139   /* Get the status of the cd */
   5140   cmnd[0] = CD_STATUS;
   5141   cmnd[1] = 0;
   5142   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5143     {
   5144       perror("write to cd_control error");
   5145       return(-1);
   5146     }
   5147 
   5148   return 0;
   5149 }
   5150 
   5151 
   5152 /* -----------------------------------------------------------------------
   5153    Play a track
   5154    ----------------------------------------------------------------------- */
   5155 void play_track( int track )
   5156 {
   5157   /* Start Playing right away */
   5158   cmnd[0] = CD_PLAY;
   5159   cmnd[1] = track;
   5160   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5161     {
   5162       perror("write to cd_control error");
   5163     }
   5164 }
   5165 
   5166 
   5167 /* -----------------------------------------------------------------------
   5168    Play the next track
   5169    ----------------------------------------------------------------------- */
   5170 void play_next()
   5171 {
   5172   cmnd[0] = CD_PLAY;
   5173   cmnd[1] = display.track == cdinfo.tochdr.cdth_trk1 ? display.track = cdinfo.tochdr.cdth_trk0 : ++display.track;
   5174 
   5175   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5176     {
   5177       perror("write to cd_control error");
   5178     }
   5179 }
   5180 
   5181 
   5182 /* -----------------------------------------------------------------------
   5183    Play the previous track
   5184    ----------------------------------------------------------------------- */
   5185 void play_previous()
   5186 {
   5187   cmnd[0] = CD_PLAY;
   5188   cmnd[1] = display.track == cdinfo.tochdr.cdth_trk0 ? display.track = cdinfo.tochdr.cdth_trk1 : --display.track;
   5189 
   5190   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5191     {
   5192       perror("write to cd_control error");
   5193     }
   5194 }
   5195 
   5196 
   5197 /* -----------------------------------------------------------------------
   5198    Eject the cd tray if we are currently playing, otherwise start playing.
   5199    
   5200    This should really be seperated into 2 pieces of code for more positive
   5201    control over the cdrom.
   5202    ----------------------------------------------------------------------- */
   5203 void eject_cdrom()
   5204 {
   5205   cmnd[0] = CD_EJECT;
   5206   cmnd[1] = 0;
   5207 
   5208   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5209     {
   5210       perror("write to cd_control error");
   5211     }
   5212 }
   5213 
   5214 
   5215 /* -----------------------------------------------------------------------
   5216    Pause the cdrom
   5217    ----------------------------------------------------------------------- */
   5218 void pause_cdrom()
   5219 {
   5220   cmnd[0] = CD_PAUSE;
   5221   cmnd[1] = 0;  
   5222 
   5223   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5224     {
   5225       perror("write to cd_control error");
   5226     }
   5227 }
   5228 
   5229 
   5230 /* -----------------------------------------------------------------------
   5231    Resume playing the cdrom
   5232    ----------------------------------------------------------------------- */
   5233 void resume_cdrom()
   5234 {
   5235   cmnd[0] = CD_RESUME;
   5236   cmnd[1] = 0;
   5237 
   5238   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5239     {
   5240       perror("write to cd_control error");
   5241     }
   5242 }
   5243 
   5244 
   5245 /* -----------------------------------------------------------------------
   5246    Change the volume setting
   5247 
   5248    amount is +/- amount to adjust by
   5249    ----------------------------------------------------------------------- */
   5250 void set_vol( int amount )
   5251 {
   5252   /* Limit the volume setting to 0 to 255 */
   5253   cdinfo.volume += amount;
   5254   cdinfo.volume = cdinfo.volume < 0 ? 0 : cdinfo.volume > 255 ? 255 : cdinfo.volume;
   5255 
   5256   cmnd[0] = CD_VOLUME;
   5257   cmnd[1] = (unsigned char) cdinfo.volume;
   5258 
   5259   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5260     {
   5261       perror("write to cd_control error");
   5262     }
   5263 }
   5264 
   5265 
   5266 /* -----------------------------------------------------------------------
   5267    Stop playing the cdrom
   5268    ----------------------------------------------------------------------- */
   5269 void stop_cdrom()
   5270 {
   5271   cmnd[0] = CD_STOP;
   5272   cmnd[1] = 0;
   5273 
   5274   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5275     {
   5276       perror("write to cd_control error");
   5277     }
   5278 }
   5279 
   5280 
   5281 /* -----------------------------------------------------------------------
   5282    Set the Eject CD on exit state
   5283    ----------------------------------------------------------------------- */
   5284 void set_eject( int state )
   5285 {
   5286   cmnd[0] = CD_SET_EJECT;
   5287   cmnd[1] = state;
   5288 
   5289   if( write( cd_fd, &cmnd, 2 ) < 0 )
   5290     {
   5291       perror("write to cd_control error");
   5292     }
   5293 }