{player service} unit srv_player; interface uses windows,common,wat_api; // init/free procedures function ProcessPlayerLink:integer; procedure ClearPlayers; // miranda-style function for new player registering function ServicePlayer(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; // next 2 functions are public coz in watrack they going with GetAddonFileName funcion // which uses some miranda things function SetPlayerIcons(fname:pAnsiChar):integer; function LoadFromFile(fname:PAnsiChar):integer; function SendCommand(wParam:WPARAM;lParam:LPARAM;flags:integer):int_ptr; // options procedures procedure DefFillPlayerList (hwndList:HWND); procedure DefCheckPlayerList(hwndList:HWND); type MusEnumProc = function(param:PAnsiChar;lParam:LPARAM):bool;stdcall; function EnumPlayers(param:MusEnumProc;lParam:LPARAM):bool; function GetPlayerNote(name:PAnsiChar):pWideChar; function CheckAllPlayers(flags:integer;var status:integer; var PlayerChanged:bool):HWND; function GetActivePlayer:pPlayerCell; type pTmplCell = ^tTmplCell; tTmplCell = record p_class, p_text :PAnsiChar; p_class1, p_text1 :PAnsiChar; p_file :PAnsiChar; p_prefix :pWideChar; p_postfix:pWideChar; end; type pwPlayer = ^twPlayer; twPlayer = record This:pPlayerCell; Next:pwPlayer; end; const PlayerLink:pwPlayer=nil; implementation uses CommCtrl, shellapi, appcmdapi,winampapi,memini,syswin; type pPlyArray = ^tPlyArray; tPlyArray = array [0..10] of tPlayerCell; const StartSize = 32; Step = 8; buflen = 2048; const plyLink:pPlyArray=nil; PlyNum:integer=0; PlyMax:integer=0; //----- Support functions ----- procedure PreProcess; // BASS to start var i:integer; tmp:tPlayerCell; begin i:=1; while i<(PlyNum-1) do begin if (plyLink^[i].flags and WAT_OPT_FIRST)<>0 then begin tmp:=plyLink^[i]; move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*i); plyLink^[0]:=tmp; break; end; inc(i); end; if (plyLink^[0].flags and WAT_OPT_LAST)<>0 then begin tmp:=plyLink^[0]; move(plyLink^[1],plyLink^[0],SizeOf(tPlayerCell)*(PlyNum-1)); plyLink^[PlyNum-1]:=tmp; end; end; procedure PostProcess; // Winamp clone to the end var i,j:integer; tmp:tPlayerCell; begin i:=1; j:=PlyNum-1; while i0 then begin tmp:=plyLink^[i]; move(plyLink^[i+1],plyLink^[i],SizeOf(tPlayerCell)*(PlyNum-i-1)); plyLink^[PlyNum-1]:=tmp; i:=1; dec(j); continue; end; inc(i); end; end; function FindPlayer(desc:PAnsiChar):integer; var i:integer; begin if (desc<>nil) and (desc^<>#0) then begin i:=0; while i0) and (@param<>nil) then begin GetMem(tmp,PlyNum*SizeOf(tPlayerCell)); move(PlyLink^,tmp^,PlyNum*SizeOf(tPlayerCell)); i:=0; j:=PlyNum; repeat if not param(tmp^[i].Desc,lParam) then break; inc(i); until i=j; FreeMem(tmp); result:=true; end else result:=false; end; function GetPlayerNote(name:PAnsiChar):pWideChar; var i:integer; begin i:=FindPlayer(name); if i>=0 then result:=plyLink^[i].Notes else result:=nil; end; //----- Init/free ----- procedure ClearTemplate(tmpl:pTmplCell); begin with tmpl^ do begin mFreeMem(p_class); mFreeMem(p_text); mFreeMem(p_class1); mFreeMem(p_text1); mFreeMem(p_file); mFreeMem(p_prefix); mFreeMem(p_postfix); end; FreeMem(tmpl); end; function ServicePlayer(wParam:WPARAM;lParam:LPARAM):int_ptr;cdecl; var p:integer; i:integer; nl:pPlyArray; tmp:tPlayerCell; begin result:=WAT_RES_ERROR; if LoWord(wParam)=WAT_ACT_REGISTER then begin if pPlayerCell(lParam)^.Check=nil then exit; p:=FindPlayer(pPlayerCell(lParam)^.Desc); if (p=WAT_RES_NOTFOUND) or ((wParam and WAT_ACT_REPLACE)<>0) then begin if (p<>WAT_RES_NOTFOUND) and ((plyLink^[p].flags and WAT_OPT_ONLYONE)<>0) then exit; if p=WAT_RES_NOTFOUND then begin p:=PlyNum; result:=WAT_RES_OK; if PlyNum=PlyMax then // expand array when append begin if PlyMax=0 then PlyMax:=StartSize else inc(PlyMax,Step); GetMem(nl,PlyMax*SizeOf(tPlayerCell)); if plyLink<>nil then begin move(plyLink^,nl^,PlyNum*SizeOf(tPlayerCell)); FreeMem(plyLink); end; plyLink:=nl; end; FillChar(plyLink^[p],SizeOf(tPlayerCell),0); // doubling notes if (pPlayerCell(lParam)^.Notes<>nil) and ((pPlayerCell(lParam)^.flags and WAT_OPT_TEMPLATE)=0) then begin i:=(StrLenW(pPlayerCell(lParam)^.Notes)+1)*SizeOf(WideChar); GetMem(plyLink^[p].Notes,i); move(pPlayerCell(lParam)^.Notes^,plyLink^[p].Notes^,i); end else plyLink^[p].Notes:=pPlayerCell(lParam)^.Notes; // doubling description i:=StrLen(pPlayerCell(lParam)^.Desc)+1; GetMem(plyLink^[p].Desc,i); move(pPlayerCell(lParam)^.Desc^,plyLink^[p].Desc^,i); // doubling url if pPlayerCell(lParam)^.URL<>nil then begin with plyLink^[p] do begin i:=StrLen(pPlayerCell(lParam)^.URL)+1; GetMem(URL,i); move(pPlayerCell(lParam)^.URL^,URL^,i); end; end else plyLink^[p].URL:=nil; inc(PlyNum); end else // existing player begin if (plyLink^[p].flags and WAT_OPT_TEMPLATE)=0 then result:=int_ptr(plyLink^[p].Check) else begin // remove any info from templates result:=WAT_RES_OK; ClearTemplate(pTmplCell(plyLink^[p].Check)); end; end; // fill info with plyLink^[p] do begin flags:=pPlayerCell(lParam)^.flags; if URL<>nil then flags:=flags or WAT_OPT_HASURL; if pPlayerCell(lParam)^.Icon<>0 then begin if icon<>0 then DestroyIcon(icon); icon:=CopyIcon(pPlayerCell(lParam)^.Icon); end; Init :=pPlayerCell(lParam)^.Init; DeInit :=pPlayerCell(lParam)^.DeInit; Check :=pPlayerCell(lParam)^.Check; GetStatus:=pPlayerCell(lParam)^.GetStatus; GetName :=pPlayerCell(lParam)^.GetName; GetInfo :=pPlayerCell(lParam)^.GetInfo; Command :=pPlayerCell(lParam)^.Command; if Init<>nil then tInitProc(Init); end; // PreProcess; PostProcess; end; end else begin p:=FindPlayer(PAnsiChar(lParam)); if p<>WAT_RES_NOTFOUND then case LoWord(wParam) of WAT_ACT_UNREGISTER: begin dec(PlyNum); if plyLink^[p].DeInit<>nil then tDeInitProc(plyLink^[p].DeInit); FreeMem(plyLink^[p].Desc); if (plyLink^[p].flags and WAT_OPT_TEMPLATE)<>0 then ClearTemplate(pTmplCell(plyLink^[p].Check)); if p0 then result:=WAT_RES_DISABLED else result:=WAT_RES_ENABLED; end; WAT_ACT_SETACTIVE: begin if p>0 then begin tmp:=plyLink^[p]; move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*p); plyLink^[0]:=tmp; end; // PreProcess; // PostProcess; end; end; end; end; function ProcessPlayerLink:integer; var ptr:pwPlayer; begin ptr:=PlayerLink; result:=0; while ptr<>nil do begin ServicePlayer(WAT_ACT_REGISTER,lparam(ptr.This)); ptr:=ptr^.Next; inc(result); end; end; procedure ClearPlayers; begin if PlyNum>0 then begin repeat dec(PlyNum); with plyLink^[PlyNum] do begin if DeInit<>nil then tDeInitProc(DeInit); FreeMem(Desc); if URL<>nil then FreeMem(URL); if icon<>0 then DestroyIcon(icon); if (flags and WAT_OPT_TEMPLATE)<>0 then begin ClearTemplate(pTmplCell(Check)); mFreeMem(Notes); end else if Notes<>nil then FreeMem(Notes); end; until PlyNum=0; FreeMem(plyLink); end; end; function LoadFromFile(fname:PAnsiChar):integer; var buf:pAnsiChar; ptr:PAnsiChar; NumPlayers:integer; pcell:pTmplCell; rec:tPlayerCell; st,sec:pointer; begin result:=0; st:=OpenStorage(fname); if st=nil then exit; buf:=GetSectionList(st); ptr:=buf; NumPlayers:=0; while ptr^<>#0 do begin sec:=SearchSection(st,ptr); FillChar(rec,SizeOf(rec),0); GetMem(pcell,SizeOf(tTmplCell)); StrDup(pcell^.p_class ,GetParamSectionStr(sec,'class' )); StrDup(pcell^.p_text ,GetParamSectionStr(sec,'text' )); StrDup(pcell^.p_class1,GetParamSectionStr(sec,'class1')); StrDup(pcell^.p_text1 ,GetParamSectionStr(sec,'text1' )); StrDup(pcell^.p_file ,GetParamSectionStr(sec,'file' )); AnsiToWide(GetParamSectionStr(sec,'prefix' ),pcell^.p_prefix ); AnsiToWide(GetParamSectionStr(sec,'postfix'),pcell^.p_postfix); rec.URL :=GetParamSectionStr(sec,'url'); rec.Desc :=ptr; rec.flags:=GetParamSectionInt(sec,'flags') or WAT_OPT_TEMPLATE; rec.Check:=pointer(pcell); UTF8ToWide(GetParamSectionStr(sec,'notes'),rec.Notes); if ServicePlayer(WAT_ACT_REGISTER,lparam(@rec))=WAT_RES_ERROR then begin ClearTemplate(pcell); // mFreeMem(rec.URL); mFreeMem(rec.Notes); end else inc(NumPlayers); while ptr^<>#0 do inc(ptr); inc(ptr); end; FreeSectionList(buf); CloseStorage(st); result:=NumPlayers; end; function SetPlayerIcons(fname:pAnsiChar):integer; var i,j:integer; buf:array [0..255] of AnsiChar; p,pp:pAnsiChar; lhIcon:HICON; begin result:=LoadLibraryA(fname); if result<>0 then begin p:=StrCopyE(buf,'Player_'); i:=0; while i0 then begin if Icon<>0 then DestroyIcon(Icon); Icon:=lhIcon; end; end; inc(i); end; FreeLibrary(result); end; end; //----- options procedures ----- procedure DefFillPlayerList(hwndList:HWND); var item:LV_ITEMA; lvc:TLVCOLUMN; i,newItem:integer; il:HIMAGELIST; //!! begin FillChar(item,SizeOf(item),0); FillChar(lvc,SizeOf(lvc),0); ListView_SetExtendedListViewStyle(hwndList, LVS_EX_CHECKBOXES); lvc.mask:=LVCF_FMT or LVCF_WIDTH; lvc.fmt:=LVCFMT_LEFT; lvc.cx:=160; ListView_InsertColumn(hwndList,0,lvc); item.mask:=LVIF_TEXT or LVIF_IMAGE; //!! i:=0; il:=ImageList_Create(16,16,ILC_COLOR32 or ILC_MASK,0,1); //!! while i=0 then begin if (plyLink^[i].flags and WAT_OPT_DISABLED)=0 then ListView_SetCheckState(hwndList,newItem,TRUE); end; inc(i); end; ImageList_Destroy(SendMessage(hwndList,LVM_SETIMAGELIST,LVSIL_SMALL,il)); //!! // ListView_SetColumnWidth(hwndList,0,LVSCW_AUTOSIZE); end; procedure DefCheckPlayerList(hwndList:HWND); var i,j,k:integer; item:LV_ITEMA; szTemp:array [0..109] of AnsiChar; p:pPlayerCell; begin FillChar(item,SizeOf(item),0); item.mask :=LVIF_TEXT; item.pszText :=@szTemp; item.cchTextMax:=100; k:=ListView_GetItemCount(hwndList)-1; for i:=0 to k do begin item.iItem:=i; SendMessageA(hwndList,LVM_GETITEMA,0,lparam(@item)); j:=FindPlayer(item.pszText); if j<>WAT_RES_NOTFOUND then begin p:=@plyLink^[j]; if ListView_GetCheckState(hwndList,i)=0 then p^.flags:=p^.flags or WAT_OPT_DISABLED else p^.flags:=p^.flags and not WAT_OPT_DISABLED; end; end; end; //----- Active player search ----- function CheckTmpl(lwnd:HWND;cell:pTmplCell;flags:integer):HWND; var tmp,EXEName:PAnsiChar; lclass,ltext:PAnsiChar; ltmp,lcycle:boolean; begin lclass:=cell.p_class; ltext :=cell.p_text; lcycle:=false; repeat result:=lwnd; if (lclass<>nil) or (ltext<>nil) then repeat result:=FindWindowExA(0,result,lclass,ltext); if result=0 then break; // check filename if cell.p_file<>NIL then begin tmp:=Extract(GetEXEByWnd(result,EXEName),true); mFreeMem(EXEName); ltmp:=lstrcmpia(tmp,cell.p_file)=0; mFreeMem(tmp); if not ltmp then continue; end; exit; until false; // repeat for alternative window if lcycle then break; lclass:=cell.p_class1; ltext :=cell.p_text1; if (lclass=nil) and (ltext=nil) then break; lcycle:=not lcycle; until false; end; // find active player function CheckAllPlayers(flags:integer;var status:integer; var PlayerChanged:bool):HWND; const PrevPlayerName:PAnsiChar=nil; var wwnd,lwnd:HWND; tmp:tPlayerCell; fname:pWideChar; stat,act,oldstat,i,j:integer; begin result:=HWND(WAT_RES_NOTFOUND); i:=0; PlayerChanged:=true; PreProcess; oldstat:=-1; act:=-1; // for case when no any player enabled/registered stat:=WAT_PLS_UNKNOWN; wwnd:=0; while i0 then begin lwnd:=CheckTmpl(lwnd,plyLink^[i].Check,plyLink^[i].flags); // find "Winamp" window if (lwnd<>THANDLE(WAT_RES_NOTFOUND)) and (lwnd<>0) and ((plyLink^[i].flags and WAT_OPT_WINAMPAPI)<>0) then begin wwnd:=WinampFindWindow(lwnd); if wwnd<>0 then stat:=WinampGetStatus(wwnd); end; end // separate processing else begin with plyLink^[i] do begin lwnd:=tCheckProc(Check)(lwnd,flags); if (lwnd<>THANDLE(WAT_RES_NOTFOUND)) and (lwnd<>0) and (GetStatus<>nil) then stat:=tStatusProc(GetStatus)(lwnd); end; end; // player window found if (lwnd<>THANDLE(WAT_RES_NOTFOUND)) and (lwnd<>0) then begin if (stat=WAT_PLS_PLAYING) or ((flags and WAT_OPT_CHECKALL)=0) then begin act :=i; result:=lwnd; break; end else begin case stat of WAT_PLS_STOPPED: j:=00; WAT_PLS_UNKNOWN: j:=10; WAT_PLS_PAUSED : j:=20; else j:=00; end; if oldstat0 then break; until false; if (result<>HWND(WAT_RES_NOTFOUND)) and (result<>0) and ((stat=WAT_PLS_PLAYING) or ((flags and WAT_OPT_CHECKALL)=0)) then break; end; inc(i); end; // hmm, we found player if act>=0 then begin if result=1 then result:=0 //!! for example, mradio else if wwnd<>0 then result:=wwnd; if act>0 then // to first position begin tmp:=plyLink^[act]; move(plyLink^[0],plyLink^[1],SizeOf(tPlayerCell)*act); plyLink^[0]:=tmp; end; if PrevPlayerName=plyLink^[0].Desc then PlayerChanged:=false else begin PrevPlayerName:=plyLink^[0].Desc; // get player icon if didn't has it before if (plyLink^[0].icon=0) and (result>0) then begin if GetEXEByWnd(result,fname)<>nil then begin plyLink^[0].icon:=ExtractIconW(hInstance,fname,0); if plyLink^[0].icon=1 then plyLink^[0].icon:=0; mFreeMem(fname); end; end; end; status:=stat; end else begin PrevPlayerName:=nil; status:=WAT_PLS_NOTFOUND; end; PostProcess; end; //----- Send command to player ----- function TranslateToApp(code:integer):integer; begin case code of WAT_CTRL_PREV : result:=APPCOMMAND_MEDIA_PREVIOUSTRACK; WAT_CTRL_PLAY : begin if IsW2K then // Win2k+ only result:=APPCOMMAND_MEDIA_PLAY_PAUSE else result:=APPCOMMAND_MEDIA_PLAY; end; WAT_CTRL_PAUSE: result:=APPCOMMAND_MEDIA_PLAY_PAUSE; WAT_CTRL_STOP : result:=APPCOMMAND_MEDIA_STOP; WAT_CTRL_NEXT : result:=APPCOMMAND_MEDIA_NEXTTRACK; WAT_CTRL_VOLDN: result:=APPCOMMAND_VOLUME_DOWN; WAT_CTRL_VOLUP: result:=APPCOMMAND_VOLUME_UP; else result:=-1; end; end; function SendCommand(wParam:WPARAM;lParam:LPARAM;flags:integer):int_ptr; var dummy:bool; wnd:HWND; lstat:integer; begin result:=WAT_RES_ERROR; wnd:=CheckAllPlayers(flags,lstat,dummy); if wnd<>THANDLE(WAT_RES_NOTFOUND) then if plyLink^[0].Command<>nil then result:=tCommandProc(plyLink^[0].Command)(wnd,wParam,lParam) else if (plyLink^[0].flags and WAT_OPT_WINAMPAPI)<>0 then result:=WinampCommand(wnd,wParam+(lParam shl 16)) else if (flags and WAT_OPT_APPCOMMAND)<>0 then begin result:=TranslateToApp(wParam); if result>=0 then result:=SendMMCommand(wnd,result); end; end; end.