REM > CDIcons+
REM © James Bursa, 2000

ON ERROR ON ERROR OFF: ERROR 0, REPORT$ + "(" + STR$ERL + "/" + STR$ERR + ")"

CD_EMPTY = 0
CD_DATA = 1
CD_AUDIO = 2
MAX_TRACKS = 40

PROCcd_init
PROCwimp_init
PROCcddb_init
PROCwimp_update
PROCwimp_poll

END

REM********************************************************************************
REM CD handling
REM********************************************************************************

REM check how many drives are connected and initialise cd data

DEF PROCcd_init
LOCAL drive%, number%

SYS"CDFS_GetNumberOfDrives" TO cd_drives%
IF cd_drives% = 0 THEN ERROR 1, "CDIcons+ cannot detect any CD drives connected to this computer"

DIM cd_cb% 20, cd_b% 200
DIM cd_id%(cd_drives% - 1), cd_status%(cd_drives% - 1), cd_path$(cd_drives% - 1)

FOR drive% = 0 TO cd_drives% - 1
  cd_id%(drive%) = -1
NEXT

ENDPROC

REM********************************************************************************

REM convert a CDFS drive number to a CDFS control block in cd_cb%

DEF PROCcd_device(drive%)
LOCAL device%
SYS"CDFS_ConvertDriveToDevice", drive% TO , device%
cd_cb%!00 = device% AND 7
cd_cb%!04 = (device% >> 3) AND 3
cd_cb%!08 = (device% >> 5) AND 7
cd_cb%!12 = (device% >> 8) AND &ff
cd_cb%!16 = device% >> 16
ENDPROC

REM********************************************************************************

REM return a nearly unique number identifying the cd, 0 if none

DEF FNcd_id(drive%)
PROCcd_device(drive%)
SYS"XCD_EnquireTrack", 0, cd_b%,,,,,, cd_cb% TO ;f%
IF f% AND 1 THEN =0
SYS"CD_DiscUsed", 0, cd_b%,,,,,, cd_cb%
=cd_b%!00

REM********************************************************************************

REM find out the type of cd and get information about it

DEF PROCcd_update(drive%)
LOCAL f%

CASE cd_status%(drive%) OF
  WHEN CD_DATA:
    PROCclosedir(cd_path$(drive%))
    SYS"OS_CLI", "CDFS:Dismount " + MID$(cd_path$(drive%), 7, LENcd_path$(drive%) - 8)

  WHEN CD_AUDIO:
    PROCcddb_finished(drive%)

ENDCASE

PROCcd_device(drive%)
SYS"XCD_EnquireTrack", 1, cd_b%,,,,,, cd_cb% TO ;f%

CASE TRUE OF
  WHEN (f% AND 1) = 1:
    cd_status%(drive%) = CD_EMPTY

  WHEN (cd_b%?04 AND 1) = 1:
    cd_status%(drive%) = CD_DATA
    SYS"OS_FSControl", 37, "CDFS::" + STR$drive%, cd_b%, 0, 0, 200 TO ,, cd_path$(drive%)
    PROCopendir(cd_path$(drive%))

  OTHERWISE:
    cd_status%(drive%) = CD_AUDIO
    PROCcddb_query(drive%)

ENDCASE
ENDPROC

REM********************************************************************************

REM call a CD_ swi

DEF PROCcd_swi(drive%, swi%, r0%, r1%)
PROCcd_device(drive%)
SYS &41240 + swi%, r0%, r1%,,,,,, cd_cb%
ENDPROC

REM********************************************************************************

REM return a string suitable for displaying under the icon

DEF FNcd_title(drive%)
LOCAL title$

CASE cd_status%(drive%) OF
  WHEN CD_EMPTY: =STR$drive%
  WHEN CD_DATA : =MID$(cd_path$(drive%), 7, LENcd_path$(drive%) - 8)
  WHEN CD_AUDIO:
    title$ = FNcddb_title(drive%)
    IF title$ = "" THEN
      =STR$drive%
    ELSE
      =title$
    ENDIF
ENDCASE
=STR$drive%

REM********************************************************************************

REM return a string giving a suitable icon name for the drive

DEF FNcd_icon(drive%)
CASE cd_status%(drive%) OF
  WHEN CD_EMPTY:   ="nocddisc"
  WHEN CD_DATA:    ="cddisc"
  WHEN CD_AUDIO:   ="acddisc"
ENDCASE
="cddisc"

REM********************************************************************************

DEF PROCcd_eject(drive%)
PROCcd_swi(drive%, &7, 0, 0)
ENDPROC

REM********************************************************************************

DEF PROCcd_close(drive%)
PROCcd_swi(drive%, &1b, 0, 0)
ENDPROC

REM********************************************************************************

DEF PROCcd_stop(drive%)
PROCcd_swi(drive%, &12, 0, 0)
ENDPROC

REM********************************************************************************

DEF FNcd_tracks(drive%)
PROCcd_device(drive%)
SYS"CD_EnquireTrack", 0, cd_b%,,,,,, cd_cb%
=cd_b%?1

REM********************************************************************************

DEF PROCcd_play(drive%, track%)
PROCcd_device(drive%)
SYS"CD_PlayTrack", track%, 255,,,,,, cd_cb%
ENDPROC

REM********************************************************************************

REM return playing track, 0 if not playing

DEF FNcd_playingtrack(drive%)
LOCAL status%, address%, tracks%, last%
PROCcd_device(drive%)
SYS"CD_AudioStatus",,,,,,,, cd_cb% TO status%
IF status% <> 0 THEN =0
SYS"CD_EnquireAddress", 0,,,,,,, cd_cb% TO address%
SYS"CD_EnquireTrack", 0, cd_b%,,,,,, cd_cb%
tracks% = cd_b%?1
FOR track% = 1 TO tracks%
  SYS"CD_EnquireTrack", track%, cd_b%,,,,,, cd_cb%
  IF cd_b%!0 > address% THEN =track% - 1
NEXT
=tracks%

REM********************************************************************************
REM Wimp interaction
REM********************************************************************************

REM initialise with the wimp

DEF PROCwimp_init
LOCAL drive%, data%, handle%, track%

DIM b% 1023, menu% 28 + 24 * 4, tmenu% 28 + 24 * MAX_TRACKS

b%!00 = &51bc1
b%!04 = 0
SYS"Wimp_Initialise", 310, &4b534154, "CDIcons+", b% TO , task%

DIM iconbar_icon%(cd_drives% - 1), iconbar_data%(cd_drives% - 1)

FOR drive% = 0 TO cd_drives% - 1
  DIM data% 60
  iconbar_icon%(drive%) = -1
  iconbar_data%(drive%) = data%
NEXT

$(menu%) = "CDFS::?"
menu%!12 = &00070207
menu%!16 = 200
menu%!20 = 44
menu%!24 = 0

menu%!(28 + 0 * 24 + 00) = 0
menu%!(28 + 0 * 24 + 04) = -1
menu%!(28 + 0 * 24 + 08) = &07000011
$(menu% + 28 + 0 * 24 + 12) = "Dismount"

menu%!(28 + 1 * 24 + 00) = 0
menu%!(28 + 1 * 24 + 04) = -1
menu%!(28 + 1 * 24 + 08) = &07000011
$(menu% + 28 + 1 * 24 + 12) = "Eject"

menu%!(28 + 2 * 24 + 00) = 0
menu%!(28 + 2 * 24 + 04) = -1
menu%!(28 + 2 * 24 + 08) = &07000011
$(menu% + 28 + 2 * 24 + 12) = "Close"

menu%!(28 + 3 * 24 + 00) = &80
menu%!(28 + 3 * 24 + 04) = -1
menu%!(28 + 3 * 24 + 08) = &07000011
$(menu% + 28 + 3 * 24 + 12) = "Stop"

tmenu%!12 = &00070008
tmenu%!16 = 500
tmenu%!20 = 44
tmenu%!24 = 0

FOR track% = 0 TO MAX_TRACKS - 1
  tmenu%!(28 + track% * 24 + 04) = -1
NEXT

ENDPROC

REM********************************************************************************

REM handle wimp events

DEF PROCwimp_poll
LOCAL time%, reason%

WHILE 1
  SYS"OS_ReadMonotonicTime" TO time%
  SYS"Wimp_PollIdle", &3830, b%, time% + 50 TO reason%

  CASE reason% OF

    WHEN 0:
      PROCwimp_update

    WHEN 6:
      PROCwimp_click

    WHEN 9:
      PROCwimp_menu

    WHEN 17, 18:
      CASE b%!16 OF
        WHEN 0     : PROCquit
        WHEN &51bc1: PROCcddb_results
      ENDCASE

  ENDCASE

ENDWHILE
ENDPROC

REM********************************************************************************

REM check for cd changes and update icons

DEF PROCwimp_update
LOCAL drive%, id%

FOR drive% = 0 TO cd_drives% - 1
  id% = FNcd_id(drive%)
  IF id% <> cd_id%(drive%) THEN
    PROCcd_update(drive%)
    PROCwimp_updateicon(drive%)
    cd_id%(drive%) = id%
  ENDIF
NEXT
ENDPROC

REM********************************************************************************

REM handle mouse clicks

DEF PROCwimp_click
LOCAL drive%, path$

drive% = FNicon_to_drive(b%!16)

IF b%!08 = 2 THEN

  menu_drive% = drive%
  menu_type% = menu%
  $(menu%) = "CDFS::" + STR$drive%
  IF cd_status%(drive%) = CD_DATA THEN
    menu%!(28 + 08) = &07000011
  ELSE
    menu%!(28 + 08) = &07400011
  ENDIF
  SYS"Wimp_CreateMenu",, menu%, b%!00 - 64, 272

ELSE

  CASE cd_status%(drive%) OF
    WHEN CD_EMPTY:
      IF b%!08 = 4 THEN
        PROCcd_eject(drive%)
      ELSE
        PROCcd_close(drive%)
      ENDIF
    WHEN CD_DATA:
      PROCopendir(cd_path$(drive%))
    WHEN CD_AUDIO:
      PROCwimp_trackmenu(drive%)

  ENDCASE

ENDIF

ENDPROC

REM********************************************************************************

DEF PROCwimp_trackmenu(drive%)
LOCAL track%, title%, tracks%, playing%

menu_drive% = drive%
menu_type% = tmenu%

title% = FNcddb_titleat(drive%)
tracks% = FNcd_tracks(drive%)
IF tracks% > MAX_TRACKS THEN tracks% = MAX_TRACKS

IF title% = 0 THEN
  $(tmenu%) = "CDFS::" + STR$drive%
  FOR track% = 0 TO tracks% - 1
    tmenu%!(28 + track% * 24 + 00) = 0
    tmenu%!(28 + track% * 24 + 08) = &0b000011
    $(tmenu% + 28 + track% * 24 + 12) = STR$(track% + 1)
  NEXT
  tmenu%!(28 + (tracks% - 1) * 24 + 00) = 1<<7

ELSE
  tmenu%!00 = title%
  tmenu%!04 = -1
  tmenu%!08 = 0
  FOR track% = 0 TO tracks% - 1
    tmenu%!(28 + track% * 24 + 00) = 0
    tmenu%!(28 + track% * 24 + 08) = &0b000111
    tmenu%!(28 + track% * 24 + 12) = FNcddb_trackat(drive%, track% + 1)
    tmenu%!(28 + track% * 24 + 16) = -1
    tmenu%!(28 + track% * 24 + 20) = 0
  NEXT
  tmenu%!28 = 1<<8
  tmenu%!(28 + (tracks% - 1) * 24 + 00) += 1<<7

ENDIF

playing% = FNcd_playingtrack(drive%)
IF playing% > 0 THEN tmenu%!(28 + (playing% - 1) * 24 + 00) += 1

SYS"Wimp_CreateMenu",, tmenu%, b%!00 - 80, 96 + 44 * tracks%

ENDPROC

REM********************************************************************************

REM handle menu selections

DEF PROCwimp_menu
SYS"Wimp_GetPointerInfo",, b% + 800
IF menu_type% = menu% THEN
  CASE b%!00 OF
    WHEN 0:
      PROCclosedir(cd_path$(menu_drive%))
      SYS"OS_CLI", "CDFS:Dismount " + MID$(cd_path$(menu_drive%), 7, LENcd_path$(menu_drive%) - 8)
    WHEN 1: PROCcd_eject(menu_drive%)
    WHEN 2: PROCcd_close(menu_drive%)
    WHEN 3: PROCcd_stop(menu_drive%)
  ENDCASE
ELSE
  PROCcd_play(menu_drive%, b%!00 + 1)
ENDIF
IF b%!808 = 1 THEN
  CASE menu_type% OF
    WHEN menu% : SYS"Wimp_CreateMenu",, menu%
    WHEN tmenu%: PROCwimp_trackmenu(menu_drive%)
  ENDCASE
ENDIF
ENDPROC

REM********************************************************************************

DEF PROCwimp_updateicon(drive%)
LOCAL icon%
icon% = iconbar_icon%(drive%)
iconbar_icon%(drive%) = FNwimp_createicon(drive%, FNcd_title(drive%), FNcd_icon(drive%), iconbar_data%(drive%))
IF icon% <> -1 THEN
  PROCwimp_deleteicon(icon%)
ENDIF
ENDPROC

REM********************************************************************************

DEF FNwimp_createicon(number%, text$, sprite$, data%)
LOCAL handle%, width%
IF LENtext$ > 25 THEN text$ = LEFT$(text$, 24) + "Œ"
SYS"Wimp_TextOp", 1, text$, 0 TO width%
IF width% < 68 THEN width% = 68
$data% = text$
$(data% + 40) = "s" + sprite$
b%!800 = -5
b%!804 = 0
b%!808 = -16
b%!812 = width%
b%!816 = 80
b%!820 = &1700310b
b%!824 = data%
b%!828 = data% + 40
b%!832 = 40
SYS"Wimp_CreateIcon", &78000000 + number%, b% + 800 TO handle%
=handle%

REM********************************************************************************

DEF PROCwimp_deleteicon(handle%)
b%!800 = -2
b%!804 = handle%
SYS"Wimp_DeleteIcon",, b% + 800
ENDPROC

REM********************************************************************************
REM Reverse lookup functions
REM********************************************************************************

DEF FNicon_to_drive(icon%)
LOCAL drive%
FOR drive% = 0 TO cd_drives% - 1
  IF iconbar_icon%(drive%) = icon% THEN =drive%
NEXT
=0

REM********************************************************************************

DEF FNcddb_ref_to_drive(ref%)
LOCAL drive%
FOR drive% = 0 TO cd_drives% - 1
  IF cddb_ref%(drive%) = ref% THEN =drive%
NEXT
=0

REM********************************************************************************
REM Filer messages
REM********************************************************************************

DEF PROCopendir(dir$)
PROCfiler(&400, dir$)
ENDPROC

REM********************************************************************************

DEF PROCclosedir(dir$)
PROCfiler(&401, dir$)
ENDPROC

REM********************************************************************************

DEF PROCfiler(message%, dir$)
b%!800 = 28 + ((LENdir$ DIV 4) + 1) * 4
b%!812 = 0
b%!816 = message%
b%!820 = 37
b%!824 = 1
$(b% + 828) = dir$ + CHR$0
SYS"Wimp_SendMessage", 17, b% + 800, 0
ENDPROC

REM********************************************************************************
REM AcornCD CDDB server communication
REM********************************************************************************

REM initialise

DEF PROCcddb_init
DIM cddb_b% 40
DIM cddb_ref%(cd_drives% - 1), cddb_area%(cd_drives% - 1), cddb_areaid%(cd_drives% - 1)
ENDPROC

REM********************************************************************************

REM send a query to the server if possible

DEF PROCcddb_query(drive%)
LOCAL call%, found%, acorncd%, exists%

call% = 0
found% = 0
WHILE (call% >= 0) AND (found% = 0)
  SYS"TaskManager_EnumerateTasks", call%, cddb_b%, 16 TO call%
  IF FNstring(cddb_b%!04) = "AcornCD" THEN
    found% = 1
    acorncd% = cddb_b%!00
  ENDIF
ENDWHILE

IF found% = 0 THEN
  SYS"XOS_ReadVarVal", "AcornCD$Dir", cddb_b%, -1 TO ,, exists%
  IF exists% = 0 THEN ENDPROC
  SYS"Wimp_StartTask", "Run <AcornCD$Dir>.!Run" TO acorncd%
ENDIF

cddb_b%!00 = 28
cddb_b%!12 = 0
REM CDDB_Query
cddb_b%!16 = &51bc0
cddb_b%!20 = 1
cddb_b%!24 = drive%
SYS"Wimp_SendMessage", 17, cddb_b%, acorncd%
cddb_ref%(drive%) = cd_b%!08
cddb_area%(drive%) = 0
cddb_areaid%(drive%) = 0
ENDPROC

REM********************************************************************************

REM return the title of the cd if available

DEF FNcddb_title(drive%)
IF cddb_area%(drive%) = 0 THEN =""
=FNstring(cddb_area%(drive%)!04)

REM********************************************************************************

DEF FNcddb_titleat(drive%)
IF cddb_area%(drive%) = 0 THEN =0
=cddb_area%(drive%)!04

REM********************************************************************************

DEF FNcddb_trackat(drive%, track%)
=!(cddb_area%(drive%) + 24 + (track% - 1) * 8)

REM********************************************************************************

REM handle CDDB_QueryResults

DEF PROCcddb_results
LOCAL drive%, area%

drive% = FNcddb_ref_to_drive(b%!12)
CASE b%!20 OF
  WHEN 0, 3:
    cddb_area%(drive%) = b%!24
    cddb_areaid%(drive%) = b%!28
    PROCwimp_updateicon(drive%)

ENDCASE

ENDPROC

REM********************************************************************************

DEF PROCcddb_finished(drive%)
IF cddb_areaid%(drive%) = 0 THEN ENDPROC
cddb_b%!00 = 28
cddb_b%!12 = 0
REM CDDB_FreeArea
cddb_b%!16 = &51bc2
cddb_b%!20 = 0
cddb_b%!24 = cddb_areaid%(drive%)
SYS"Wimp_SendMessage", 17, cddb_b%, 0
cddb_area%(drive%) = 0
cddb_areaid%(drive%) = 0
ENDPROC

REM********************************************************************************
REM Useful functions
REM********************************************************************************

DEF FNstring(a%)
LOCAL s$
s$ = ""
WHILE ?a% > 31
  s$ += CHR$?a%
  a% += 1
ENDWHILE
=s$

REM********************************************************************************

DEF PROCquit
FOR drive% = 0 TO cd_drives% - 1
  PROCcddb_finished(drive%)
NEXT
SYS"Wimp_CloseDown", task%, &4b534154
END
ENDPROC

REM********************************************************************************