mtp (empty) → 0.1.1.1
raw patch · 9 files changed
+1891/−0 lines, 9 filesdep +basedep +filepathdep +unixsetup-changed
Dependencies added: base, filepath, unix
Files
- COPYING +504/−0
- ChangeLog +21/−0
- Foreign/Handle.hsc +27/−0
- MTP.hs +708/−0
- MTP/Foreign.hsc +507/−0
- MTP/Handle.hs +53/−0
- README.md +33/−0
- Setup.lhs +4/−0
- mtp.cabal +34/−0
+ COPYING view
@@ -0,0 +1,504 @@+ GNU LESSER GENERAL PUBLIC LICENSE+ Version 2.1, February 1999++ Copyright (C) 1991, 1999 Free Software Foundation, Inc.+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++[This is the first released version of the Lesser GPL. It also counts+ as the successor of the GNU Library Public License, version 2, hence+ the version number 2.1.]++ Preamble++ The licenses for most software are designed to take away your+freedom to share and change it. By contrast, the GNU General Public+Licenses are intended to guarantee your freedom to share and change+free software--to make sure the software is free for all its users.++ This license, the Lesser General Public License, applies to some+specially designated software packages--typically libraries--of the+Free Software Foundation and other authors who decide to use it. You+can use it too, but we suggest you first think carefully about whether+this license or the ordinary General Public License is the better+strategy to use in any particular case, based on the explanations below.++ When we speak of free software, we are referring to freedom of use,+not price. Our General Public Licenses are designed to make sure that+you have the freedom to distribute copies of free software (and charge+for this service if you wish); that you receive source code or can get+it if you want it; that you can change the software and use pieces of+it in new free programs; and that you are informed that you can do+these things.++ To protect your rights, we need to make restrictions that forbid+distributors to deny you these rights or to ask you to surrender these+rights. These restrictions translate to certain responsibilities for+you if you distribute copies of the library or if you modify it.++ For example, if you distribute copies of the library, whether gratis+or for a fee, you must give the recipients all the rights that we gave+you. You must make sure that they, too, receive or can get the source+code. If you link other code with the library, you must provide+complete object files to the recipients, so that they can relink them+with the library after making changes to the library and recompiling+it. And you must show them these terms so they know their rights.++ We protect your rights with a two-step method: (1) we copyright the+library, and (2) we offer you this license, which gives you legal+permission to copy, distribute and/or modify the library.++ To protect each distributor, we want to make it very clear that+there is no warranty for the free library. Also, if the library is+modified by someone else and passed on, the recipients should know+that what they have is not the original version, so that the original+author's reputation will not be affected by problems that might be+introduced by others.++ Finally, software patents pose a constant threat to the existence of+any free program. We wish to make sure that a company cannot+effectively restrict the users of a free program by obtaining a+restrictive license from a patent holder. Therefore, we insist that+any patent license obtained for a version of the library must be+consistent with the full freedom of use specified in this license.++ Most GNU software, including some libraries, is covered by the+ordinary GNU General Public License. This license, the GNU Lesser+General Public License, applies to certain designated libraries, and+is quite different from the ordinary General Public License. We use+this license for certain libraries in order to permit linking those+libraries into non-free programs.++ When a program is linked with a library, whether statically or using+a shared library, the combination of the two is legally speaking a+combined work, a derivative of the original library. The ordinary+General Public License therefore permits such linking only if the+entire combination fits its criteria of freedom. The Lesser General+Public License permits more lax criteria for linking other code with+the library.++ We call this license the "Lesser" General Public License because it+does Less to protect the user's freedom than the ordinary General+Public License. It also provides other free software developers Less+of an advantage over competing non-free programs. These disadvantages+are the reason we use the ordinary General Public License for many+libraries. However, the Lesser license provides advantages in certain+special circumstances.++ For example, on rare occasions, there may be a special need to+encourage the widest possible use of a certain library, so that it becomes+a de-facto standard. To achieve this, non-free programs must be+allowed to use the library. A more frequent case is that a free+library does the same job as widely used non-free libraries. In this+case, there is little to gain by limiting the free library to free+software only, so we use the Lesser General Public License.++ In other cases, permission to use a particular library in non-free+programs enables a greater number of people to use a large body of+free software. For example, permission to use the GNU C Library in+non-free programs enables many more people to use the whole GNU+operating system, as well as its variant, the GNU/Linux operating+system.++ Although the Lesser General Public License is Less protective of the+users' freedom, it does ensure that the user of a program that is+linked with the Library has the freedom and the wherewithal to run+that program using a modified version of the Library.++ The precise terms and conditions for copying, distribution and+modification follow. Pay close attention to the difference between a+"work based on the library" and a "work that uses the library". The+former contains code derived from the library, whereas the latter must+be combined with the library in order to run.++ GNU LESSER GENERAL PUBLIC LICENSE+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION++ 0. This License Agreement applies to any software library or other+program which contains a notice placed by the copyright holder or+other authorized party saying it may be distributed under the terms of+this Lesser General Public License (also called "this License").+Each licensee is addressed as "you".++ A "library" means a collection of software functions and/or data+prepared so as to be conveniently linked with application programs+(which use some of those functions and data) to form executables.++ The "Library", below, refers to any such software library or work+which has been distributed under these terms. A "work based on the+Library" means either the Library or any derivative work under+copyright law: that is to say, a work containing the Library or a+portion of it, either verbatim or with modifications and/or translated+straightforwardly into another language. (Hereinafter, translation is+included without limitation in the term "modification".)++ "Source code" for a work means the preferred form of the work for+making modifications to it. For a library, complete source code means+all the source code for all modules it contains, plus any associated+interface definition files, plus the scripts used to control compilation+and installation of the library.++ Activities other than copying, distribution and modification are not+covered by this License; they are outside its scope. The act of+running a program using the Library is not restricted, and output from+such a program is covered only if its contents constitute a work based+on the Library (independent of the use of the Library in a tool for+writing it). Whether that is true depends on what the Library does+and what the program that uses the Library does.++ 1. You may copy and distribute verbatim copies of the Library's+complete source code as you receive it, in any medium, provided that+you conspicuously and appropriately publish on each copy an+appropriate copyright notice and disclaimer of warranty; keep intact+all the notices that refer to this License and to the absence of any+warranty; and distribute a copy of this License along with the+Library.++ You may charge a fee for the physical act of transferring a copy,+and you may at your option offer warranty protection in exchange for a+fee.++ 2. You may modify your copy or copies of the Library or any portion+of it, thus forming a work based on the Library, and copy and+distribute such modifications or work under the terms of Section 1+above, provided that you also meet all of these conditions:++ a) The modified work must itself be a software library.++ b) You must cause the files modified to carry prominent notices+ stating that you changed the files and the date of any change.++ c) You must cause the whole of the work to be licensed at no+ charge to all third parties under the terms of this License.++ d) If a facility in the modified Library refers to a function or a+ table of data to be supplied by an application program that uses+ the facility, other than as an argument passed when the facility+ is invoked, then you must make a good faith effort to ensure that,+ in the event an application does not supply such function or+ table, the facility still operates, and performs whatever part of+ its purpose remains meaningful.++ (For example, a function in a library to compute square roots has+ a purpose that is entirely well-defined independent of the+ application. Therefore, Subsection 2d requires that any+ application-supplied function or table used by this function must+ be optional: if the application does not supply it, the square+ root function must still compute square roots.)++These requirements apply to the modified work as a whole. If+identifiable sections of that work are not derived from the Library,+and can be reasonably considered independent and separate works in+themselves, then this License, and its terms, do not apply to those+sections when you distribute them as separate works. But when you+distribute the same sections as part of a whole which is a work based+on the Library, the distribution of the whole must be on the terms of+this License, whose permissions for other licensees extend to the+entire whole, and thus to each and every part regardless of who wrote+it.++Thus, it is not the intent of this section to claim rights or contest+your rights to work written entirely by you; rather, the intent is to+exercise the right to control the distribution of derivative or+collective works based on the Library.++In addition, mere aggregation of another work not based on the Library+with the Library (or with a work based on the Library) on a volume of+a storage or distribution medium does not bring the other work under+the scope of this License.++ 3. You may opt to apply the terms of the ordinary GNU General Public+License instead of this License to a given copy of the Library. To do+this, you must alter all the notices that refer to this License, so+that they refer to the ordinary GNU General Public License, version 2,+instead of to this License. (If a newer version than version 2 of the+ordinary GNU General Public License has appeared, then you can specify+that version instead if you wish.) Do not make any other change in+these notices.++ Once this change is made in a given copy, it is irreversible for+that copy, so the ordinary GNU General Public License applies to all+subsequent copies and derivative works made from that copy.++ This option is useful when you wish to copy part of the code of+the Library into a program that is not a library.++ 4. You may copy and distribute the Library (or a portion or+derivative of it, under Section 2) in object code or executable form+under the terms of Sections 1 and 2 above provided that you accompany+it with the complete corresponding machine-readable source code, which+must be distributed under the terms of Sections 1 and 2 above on a+medium customarily used for software interchange.++ If distribution of object code is made by offering access to copy+from a designated place, then offering equivalent access to copy the+source code from the same place satisfies the requirement to+distribute the source code, even though third parties are not+compelled to copy the source along with the object code.++ 5. A program that contains no derivative of any portion of the+Library, but is designed to work with the Library by being compiled or+linked with it, is called a "work that uses the Library". Such a+work, in isolation, is not a derivative work of the Library, and+therefore falls outside the scope of this License.++ However, linking a "work that uses the Library" with the Library+creates an executable that is a derivative of the Library (because it+contains portions of the Library), rather than a "work that uses the+library". The executable is therefore covered by this License.+Section 6 states terms for distribution of such executables.++ When a "work that uses the Library" uses material from a header file+that is part of the Library, the object code for the work may be a+derivative work of the Library even though the source code is not.+Whether this is true is especially significant if the work can be+linked without the Library, or if the work is itself a library. The+threshold for this to be true is not precisely defined by law.++ If such an object file uses only numerical parameters, data+structure layouts and accessors, and small macros and small inline+functions (ten lines or less in length), then the use of the object+file is unrestricted, regardless of whether it is legally a derivative+work. (Executables containing this object code plus portions of the+Library will still fall under Section 6.)++ Otherwise, if the work is a derivative of the Library, you may+distribute the object code for the work under the terms of Section 6.+Any executables containing that work also fall under Section 6,+whether or not they are linked directly with the Library itself.++ 6. As an exception to the Sections above, you may also combine or+link a "work that uses the Library" with the Library to produce a+work containing portions of the Library, and distribute that work+under terms of your choice, provided that the terms permit+modification of the work for the customer's own use and reverse+engineering for debugging such modifications.++ You must give prominent notice with each copy of the work that the+Library is used in it and that the Library and its use are covered by+this License. You must supply a copy of this License. If the work+during execution displays copyright notices, you must include the+copyright notice for the Library among them, as well as a reference+directing the user to the copy of this License. Also, you must do one+of these things:++ a) Accompany the work with the complete corresponding+ machine-readable source code for the Library including whatever+ changes were used in the work (which must be distributed under+ Sections 1 and 2 above); and, if the work is an executable linked+ with the Library, with the complete machine-readable "work that+ uses the Library", as object code and/or source code, so that the+ user can modify the Library and then relink to produce a modified+ executable containing the modified Library. (It is understood+ that the user who changes the contents of definitions files in the+ Library will not necessarily be able to recompile the application+ to use the modified definitions.)++ b) Use a suitable shared library mechanism for linking with the+ Library. A suitable mechanism is one that (1) uses at run time a+ copy of the library already present on the user's computer system,+ rather than copying library functions into the executable, and (2)+ will operate properly with a modified version of the library, if+ the user installs one, as long as the modified version is+ interface-compatible with the version that the work was made with.++ c) Accompany the work with a written offer, valid for at+ least three years, to give the same user the materials+ specified in Subsection 6a, above, for a charge no more+ than the cost of performing this distribution.++ d) If distribution of the work is made by offering access to copy+ from a designated place, offer equivalent access to copy the above+ specified materials from the same place.++ e) Verify that the user has already received a copy of these+ materials or that you have already sent this user a copy.++ For an executable, the required form of the "work that uses the+Library" must include any data and utility programs needed for+reproducing the executable from it. However, as a special exception,+the materials to be distributed need not include anything that is+normally distributed (in either source or binary form) with the major+components (compiler, kernel, and so on) of the operating system on+which the executable runs, unless that component itself accompanies+the executable.++ It may happen that this requirement contradicts the license+restrictions of other proprietary libraries that do not normally+accompany the operating system. Such a contradiction means you cannot+use both them and the Library together in an executable that you+distribute.++ 7. You may place library facilities that are a work based on the+Library side-by-side in a single library together with other library+facilities not covered by this License, and distribute such a combined+library, provided that the separate distribution of the work based on+the Library and of the other library facilities is otherwise+permitted, and provided that you do these two things:++ a) Accompany the combined library with a copy of the same work+ based on the Library, uncombined with any other library+ facilities. This must be distributed under the terms of the+ Sections above.++ b) Give prominent notice with the combined library of the fact+ that part of it is a work based on the Library, and explaining+ where to find the accompanying uncombined form of the same work.++ 8. You may not copy, modify, sublicense, link with, or distribute+the Library except as expressly provided under this License. Any+attempt otherwise to copy, modify, sublicense, link with, or+distribute the Library is void, and will automatically terminate your+rights under this License. However, parties who have received copies,+or rights, from you under this License will not have their licenses+terminated so long as such parties remain in full compliance.++ 9. You are not required to accept this License, since you have not+signed it. However, nothing else grants you permission to modify or+distribute the Library or its derivative works. These actions are+prohibited by law if you do not accept this License. Therefore, by+modifying or distributing the Library (or any work based on the+Library), you indicate your acceptance of this License to do so, and+all its terms and conditions for copying, distributing or modifying+the Library or works based on it.++ 10. Each time you redistribute the Library (or any work based on the+Library), the recipient automatically receives a license from the+original licensor to copy, distribute, link with or modify the Library+subject to these terms and conditions. You may not impose any further+restrictions on the recipients' exercise of the rights granted herein.+You are not responsible for enforcing compliance by third parties with+this License.++ 11. If, as a consequence of a court judgment or allegation of patent+infringement or for any other reason (not limited to patent issues),+conditions are imposed on you (whether by court order, agreement or+otherwise) that contradict the conditions of this License, they do not+excuse you from the conditions of this License. If you cannot+distribute so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you+may not distribute the Library at all. For example, if a patent+license would not permit royalty-free redistribution of the Library by+all those who receive copies directly or indirectly through you, then+the only way you could satisfy both it and this License would be to+refrain entirely from distribution of the Library.++If any portion of this section is held invalid or unenforceable under any+particular circumstance, the balance of the section is intended to apply,+and the section as a whole is intended to apply in other circumstances.++It is not the purpose of this section to induce you to infringe any+patents or other property right claims or to contest validity of any+such claims; this section has the sole purpose of protecting the+integrity of the free software distribution system which is+implemented by public license practices. Many people have made+generous contributions to the wide range of software distributed+through that system in reliance on consistent application of that+system; it is up to the author/donor to decide if he or she is willing+to distribute software through any other system and a licensee cannot+impose that choice.++This section is intended to make thoroughly clear what is believed to+be a consequence of the rest of this License.++ 12. If the distribution and/or use of the Library is restricted in+certain countries either by patents or by copyrighted interfaces, the+original copyright holder who places the Library under this License may add+an explicit geographical distribution limitation excluding those countries,+so that distribution is permitted only in or among countries not thus+excluded. In such case, this License incorporates the limitation as if+written in the body of this License.++ 13. The Free Software Foundation may publish revised and/or new+versions of the Lesser General Public License from time to time.+Such new versions will be similar in spirit to the present version,+but may differ in detail to address new problems or concerns.++Each version is given a distinguishing version number. If the Library+specifies a version number of this License which applies to it and+"any later version", you have the option of following the terms and+conditions either of that version or of any later version published by+the Free Software Foundation. If the Library does not specify a+license version number, you may choose any version ever published by+the Free Software Foundation.++ 14. If you wish to incorporate parts of the Library into other free+programs whose distribution conditions are incompatible with these,+write to the author to ask for permission. For software which is+copyrighted by the Free Software Foundation, write to the Free+Software Foundation; we sometimes make exceptions for this. Our+decision will be guided by the two goals of preserving the free status+of all derivatives of our free software and of promoting the sharing+and reuse of software generally.++ NO WARRANTY++ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.++ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH+DAMAGES.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Libraries++ If you develop a new library, and you want it to be of the greatest+possible use to the public, we recommend making it free software that+everyone can redistribute and change. You can do so by permitting+redistribution under these terms (or, alternatively, under the terms of the+ordinary General Public License).++ To apply these terms, attach the following notices to the library. It is+safest to attach them to the start of each source file to most effectively+convey the exclusion of warranty; and each file should have at least the+"copyright" line and a pointer to where the full notice is found.++ <one line to give the library's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This library is free software; you can redistribute it and/or+ modify it under the terms of the GNU Lesser General Public+ License as published by the Free Software Foundation; either+ version 2.1 of the License, or (at your option) any later version.++ This library is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU+ Lesser General Public License for more details.++ You should have received a copy of the GNU Lesser General Public+ License along with this library; if not, write to the Free Software+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA++Also add information on how to contact you by electronic and paper mail.++You should also get your employer (if you work as a programmer) or your+school, if any, to sign a "copyright disclaimer" for the library, if+necessary. Here is a sample; alter the names:++ Yoyodyne, Inc., hereby disclaims all copyright interest in the+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.++ <signature of Ty Coon>, 1 April 1990+ Ty Coon, President of Vice++That's all there is to it!++
+ ChangeLog view
@@ -0,0 +1,21 @@+* version 0.1.1.1, 2010-03-13+ Internal:+ - Reimplemented MTPHandle, now supports closing+ - Check if handle is usable before using it++* version 0.1.1.0, 2010-03-10+ External:+ - Add binding to LIBMTP_Init+ - Add missing filetypes+ - Add findFileType+ Internal:+ - Modularise++* version 0.1.0.2, 2010-01-26+ - Cabal updates++* version 0.1.0.1, 2010-01-18+ - Fix build warnings reported by GHC 6.12++* version 0.1.0.0, 2009-12-25+ - First entry
+ Foreign/Handle.hsc view
@@ -0,0 +1,27 @@+{-# LANGUAGE ForeignFunctionInterface #-}++module Foreign.Handle (+ fdopen, fflush, fclose,+ handleToCFile+ ) where++import GHC.IO.Handle+import Foreign+import Foreign.C+import System.Posix.IO+import System.Posix.Types++foreign import ccall unsafe "stdio.h fdopen" fdopen :: Fd -> CString -> IO (Ptr CFile)++foreign import ccall unsafe "stdio.h fflush" fflush :: Ptr CFile -> IO ()++foreign import ccall unsafe "stdio.h fclose" fclose :: Ptr CFile -> IO ()++-- | Convert a Handle to a CFile.+-- Source <http://haskell.org/haskellwiki/The_Monad.Reader/Issue2/Bzlib2Binding>+handleToCFile :: Handle -> String -> IO (Ptr CFile)+handleToCFile h m = withCAString m $ \iomode -> do+ -- Create a duplicate so the original handle is kept open+ h' <- hDuplicate h+ fd <- handleToFd h'+ fdopen fd iomode
+ MTP.hs view
@@ -0,0 +1,708 @@+{-# LANGUAGE DeriveDataTypeable #-}++-- |+-- Module : MTP+-- Copyright : (c) Joachim Fasting 2010+-- License : LGPL+--+-- Maintainer : Joachim Fasting <joachim.fasting@gmail.com>+-- Stability : unstable+-- Portability : not portable+--+-- Bindings to libmtp. High-level interface.+--+-- Usage:+--+-- > import qualified MTP+--+-- > main = do+--+-- > MTP.init+--+-- > MTP.withFirstDevice MTP.getDeviceVersion++module MTP (+ -- * Types+ MTPHandle, Track(..), File(..), Folder(..), Playlist(..), FileType,+ MTPException(..),+ -- * Constants+ version,+ wav, mp3, wma, ogg, audible, mp4, undef_audio, wmv, avi, mpeg, asf, qt,+ undef_video, jpeg, jfif, tiff, bmp, gif, pict, png, vcalendar1,+ vcalendar2, vcard2, vcard3, windowsimageformat, winexec, text, html,+ firmware, aac, mediacard, flac, mp2, m4a, doc, xml, xls, ppt, mht, jp2,+ unknown,+ -- * Device management+ init, getFirstDevice, releaseDevice, resetDevice,+ withFirstDevice,+ getDeviceVersion, getManufacturerName, getModelName, getSerialNumber,+ getFriendlyName, getBatteryLevel, getSupportedFileTypes,+ -- * File management+ getFileListing,+ getFile, sendFile,+ hGetFile, hSendFile,+ setFileName,+ -- * Track management+ emptyTrack,+ doesTrackExist,+ getTrackListing,+ getTrack,+ sendTrack,+ hGetTrack, hSendTrack,+ updateTrack,+ getTrackMetadata,+ setTrackName,+ -- * Folder management+ createFolder, getFolderList, setFolderName,+ -- * Audio\/video playlist management+ getPlaylistList, getPlaylist, createPlaylist, updatePlaylist,+ setPlaylistName,+ -- * Object management+ deleteObject, setObjectName,+ -- * Extras+ findFileType+ ) where++import Foreign.Handle+import MTP.Foreign+import MTP.Handle++import Control.Exception+import Control.Monad+import Data.Maybe+import Data.Typeable+import Foreign+import Foreign.C+import Prelude hiding (init)+import System.FilePath+import System.IO++------------------------------------------------------------------------------+-- Types+------------------------------------------------------------------------------++-- | MTP exceptions.+data MTPException+ = NoDevice+ | StorageFull+ | ConnectionFailed+ | Cancelled+ | General String+ deriving (Eq, Show, Typeable)++instance Exception MTPException where++-- | File metadata.+data File = File+ { fileID :: Int+ , fileParentID :: Int+ , fileStorageID :: Int+ , fileName :: String+ , fileSize :: Integer+ , fileType :: FileType+ } deriving (Eq, Show)++-- | Track metadata.+data Track = Track+ { trackID :: Int+ , trackParentID :: Int+ , trackStorageID :: Int+ , trackTitle :: String+ , trackArtist :: String+ , trackComposer :: String+ , trackGenre :: String+ , trackAlbum :: String+ , trackDate :: String+ , trackFileName :: String+ , trackNumber :: Int+ , trackDuration :: Int+ , trackSamplerate :: Int+ , trackChannels :: Int+ , trackWavecodec :: Int+ , trackBitrate :: Int+ , trackBitrateType :: Int+ , trackRating :: Int+ , trackUseCount :: Int+ , trackFileSize :: Integer+ , trackFileType :: FileType+ } deriving (Eq, Show)++-- | Folder metadata.+data Folder = Folder+ { folderID :: Int+ , folderParentID :: Int+ , folderStorageID :: Int+ , folderName :: String+ , folderChild :: Maybe Folder+ }++-- | Playlist metadata.+data Playlist = Playlist+ { playlistID :: Int+ , playlistParentID :: Int+ , playlistStorageID :: Int+ , playlistName :: String+ , playlistTracks :: [Int]+ , playlistNoTracks :: Int+ } deriving (Eq, Show)++------------------------------------------------------------------------------+-- Device management+------------------------------------------------------------------------------++-- | Initialize MTP.+init :: IO ()+init = c_init++-- | Open a connection to the first available MTP device and run an+-- action, closing the connection afterwards.+withFirstDevice :: (MTPHandle -> IO a) -> IO a+withFirstDevice = bracket getFirstDevice releaseDevice++-- A helper that checks the error stack and throws an exception if there is an error.+checkError :: MTPHandle -> IO ()+checkError h = withMTPHandle h $ \devptr -> do+ e_ptr <- c_get_errorstack devptr+ unless (e_ptr == nullPtr) $ do+ et <- peek e_ptr+ es <- peekCString (et_errortext et)+ c_clear_errorstack devptr+ case ErrorCode (et_errornumber et) of+ x | x == general -> throw $ General es+ | x == noDevice -> throw NoDevice+ | x == storageFull -> throw StorageFull+ | x == connectionFailed -> throw ConnectionFailed+ | x == cancelled -> throw Cancelled+ | otherwise -> error $ "checkError: unhandled error number: " +++ show x++-- | Connect to the first available MTP device.+getFirstDevice :: IO MTPHandle+getFirstDevice = do+ devptr <- c_get_first_device+ if devptr == nullPtr+ then throw NoDevice+ else open devptr++-- XXX: we should free the ptr, but it causes a double free error, WHY, OH WHY+-- | Close connection to a MTP device. The handle is unusable after this.+releaseDevice :: MTPHandle -> IO ()+releaseDevice h = withMTPHandle h c_release_device >> close h++-- | Reset device.+resetDevice :: MTPHandle -> IO ()+resetDevice h = withMTPHandle h $ \devptr -> do+ r <- c_reset_device devptr+ unless (r == 0) (checkError h)++-- | Get the device manufacturer name.+getManufacturerName :: MTPHandle -> IO String+getManufacturerName h = withMTPHandle h $ \devptr ->+ peekCString =<< c_get_manufacturername devptr++-- | Get the device model name.+getModelName :: MTPHandle -> IO String+getModelName h = withMTPHandle h $ \devptr ->+ peekCString =<< c_get_modelname devptr++-- | Get the device serial number.+getSerialNumber :: MTPHandle -> IO String+getSerialNumber h = withMTPHandle h $ \devptr ->+ peekCString =<< c_get_serialnumber devptr++-- | Get the owner string aka. the \"friendly name\".+getFriendlyName :: MTPHandle -> IO String+getFriendlyName h = withMTPHandle h $ \devptr ->+ peekCString =<< c_get_friendlyname devptr++-- | Get device hardware and firmware version.+getDeviceVersion :: MTPHandle -> IO String+getDeviceVersion h = withMTPHandle h $ \ptr ->+ peekCString =<< c_get_deviceversion ptr++-- | Get battery level, maximum and current.+getBatteryLevel :: MTPHandle -> IO (Int, Int)+getBatteryLevel h = withMTPHandle h $ \devptr ->+ alloca $ \maxptr ->+ alloca $ \curptr -> do+ ret <- c_get_batterylevel devptr maxptr curptr+ unless (ret == 0) (checkError h)+ maxv <- peek maxptr+ curv <- peek curptr+ return (fromIntegral maxv, fromIntegral curv)++-- | Get a list of supported file types.+getSupportedFileTypes :: MTPHandle -> IO [FileType]+getSupportedFileTypes h = withMTPHandle h $ \devptr ->+ alloca $ \ft_ptr ->+ alloca $ \len_ptr -> do+ r <- c_get_supported_filetypes devptr ft_ptr len_ptr+ unless (r == 0) (checkError h)+ len <- peek len_ptr+ map FileType `fmap` peekArray (fromIntegral len) ft_ptr++------------------------------------------------------------------------------+-- File management+------------------------------------------------------------------------------++-- Marshall a Track into a Track_t pointer using temporary storage.+withFilePtr :: File -> (Ptr File_t -> IO a) -> IO a+withFilePtr f = bracket alloc free+ where+ alloc = do+ ptr <- malloc :: IO (Ptr File_t)+ ft <- marshall+ poke ptr ft+ return ptr+ marshall =+ withCAString (fileName f) $ \name_ptr ->+ return File_t { ft_item_id = fromIntegral (fileID f)+ , ft_parent_id = fromIntegral (fileParentID f)+ , ft_storage_id = fromIntegral (fileStorageID f)+ , ft_filename = name_ptr+ , ft_filesize = fromIntegral (fileSize f)+ , ft_filetype = unFileType (fileType f)+ , ft_next = nullPtr+ }++-- | Get a list of all files stored on the device.+getFileListing :: MTPHandle -> IO [File]+getFileListing h = withMTPHandle h $ \ptr ->+ toList [] =<< c_get_filelisting ptr nullPtr nullPtr+ where+ toList acc p =+ if p == nullPtr+ then return acc+ else do+ ft <- peek p+ fn <- convert ft+ free p+ toList (fn : acc) (ft_next ft)+ convert ft = do+ n <- peekCString (ft_filename ft)+ return File { fileID = fromIntegral (ft_item_id ft)+ , fileParentID = fromIntegral (ft_parent_id ft)+ , fileStorageID = fromIntegral (ft_storage_id ft)+ , fileName = n+ , fileSize = fromIntegral (ft_filesize ft)+ , fileType = FileType (ft_filetype ft)+ }++-- | Copy a file from the device to a local file.+getFile :: MTPHandle -> Int -> FilePath -> IO ()+getFile h i n =+ withMTPHandle h $ \devptr ->+ withCAString n $ \str_ptr -> do+ r <- c_get_file_to_file devptr (fromIntegral i) str_ptr nullPtr nullPtr+ unless (r == 0) (checkError h)++-- | Send a local file to the device.+sendFile :: MTPHandle -> FilePath -> IO ()+sendFile h n =+ withMTPHandle h $ \devptr ->+ withCAString n $ \str_ptr -> do+ r <- c_send_file_from_file devptr str_ptr nullPtr nullPtr+ unless (r == 0) (checkError h)++-- | Get a file from the device to a file handle.+hGetFile :: MTPHandle -> Int -> Handle -> IO ()+hGetFile h i fd = withMTPHandle h $ \devptr -> do+ oh <- handleToCFile fd "w"+ r <- c_get_file_to_file_descriptor devptr (fromIntegral i) oh nullPtr+ nullPtr+ fflush oh+ fclose oh+ unless (r == 0) (checkError h)++-- | Send a file to the device from a file handle.+hSendFile :: MTPHandle -> Handle -> File -> IO ()+hSendFile h fd f = withMTPHandle h $ \devptr ->+ withFilePtr f $ \file_ptr -> do+ ih <- handleToCFile fd "r"+ r <- c_send_file_from_file_descriptor devptr ih file_ptr nullPtr+ nullPtr+ fclose ih+ unless (r == 0) (checkError h)++-- | Rename a file on the device.+setFileName :: MTPHandle -> File -> String -> IO ()+setFileName h f n =+ withMTPHandle h $ \devptr ->+ withFilePtr f $ \file_ptr ->+ withCAString n $ \str_ptr -> do+ r <- c_set_file_name devptr file_ptr str_ptr+ unless (r == 0) (checkError h)++------------------------------------------------------------------------------+-- Track management+------------------------------------------------------------------------------++-- Marshall a Track into a Track_t pointer using temporary storage.+withTrackPtr :: Track -> (Ptr Track_t -> IO a) -> IO a+withTrackPtr t = bracket alloc free+ where+ alloc = do+ ptr <- malloc :: IO (Ptr Track_t)+ tt <- marshall+ poke ptr tt+ return ptr+ marshall =+ withCAString (trackTitle t) $ \title_ptr ->+ withCAString (trackArtist t) $ \artist_ptr ->+ withCAString (trackComposer t) $ \composer_ptr ->+ withCAString (trackGenre t) $ \genre_ptr ->+ withCAString (trackAlbum t) $ \album_ptr ->+ withCAString (trackDate t) $ \date_ptr ->+ withCAString (trackFileName t) $ \filename_ptr ->+ return Track_t+ { tt_item_id = fromIntegral (trackID t)+ , tt_parent_id = fromIntegral (trackParentID t)+ , tt_storage_id = fromIntegral (trackStorageID t)+ , tt_title = title_ptr+ , tt_artist = artist_ptr+ , tt_composer = composer_ptr+ , tt_genre = genre_ptr+ , tt_album = album_ptr+ , tt_date = date_ptr+ , tt_filename = filename_ptr+ , tt_tracknumber = fromIntegral (trackNumber t)+ , tt_duration = fromIntegral (trackDuration t)+ , tt_samplerate = fromIntegral (trackSamplerate t)+ , tt_nochannels = fromIntegral (trackChannels t)+ , tt_wavecodec = fromIntegral (trackWavecodec t)+ , tt_bitrate = fromIntegral (trackBitrate t)+ , tt_bitratetype = fromIntegral (trackBitrateType t)+ , tt_rating = fromIntegral (trackRating t)+ , tt_usecount = fromIntegral (trackUseCount t)+ , tt_filesize = fromIntegral (trackFileSize t)+ , tt_filetype = unFileType (trackFileType t)+ , tt_next = nullPtr+ }++peekTrack :: Ptr Track_t -> IO [Track]+peekTrack = go []+ where+ go acc p =+ if p == nullPtr+ then return acc+ else do+ tt <- peek p+ tn <- convert tt+ free p+ go (tn : acc) (tt_next tt)+ convert tt = do+ ti <- peekCString (tt_title tt)+ ar <- peekCString (tt_artist tt)+ cm <- peekCString (tt_composer tt)+ ge <- peekCString (tt_genre tt)+ al <- peekCString (tt_album tt)+ dt <- peekCString (tt_date tt)+ fn <- peekCString (tt_filename tt)+ return $! Track { trackID = fromIntegral (tt_item_id tt)+ , trackParentID = fromIntegral (tt_parent_id tt)+ , trackStorageID = fromIntegral (tt_storage_id tt)+ , trackTitle = ti+ , trackArtist = ar+ , trackComposer = cm+ , trackGenre = ge+ , trackAlbum = al+ , trackDate = dt+ , trackFileName = fn+ , trackNumber = fromIntegral (tt_tracknumber tt)+ , trackDuration = fromIntegral (tt_duration tt)+ , trackSamplerate = fromIntegral (tt_samplerate tt)+ , trackChannels = fromIntegral (tt_nochannels tt)+ , trackWavecodec = fromIntegral (tt_wavecodec tt)+ , trackBitrate = fromIntegral (tt_bitrate tt)+ , trackBitrateType = fromIntegral (tt_bitratetype tt)+ , trackRating = fromIntegral (tt_rating tt)+ , trackUseCount = fromIntegral (tt_usecount tt)+ , trackFileSize = fromIntegral (tt_filesize tt)+ , trackFileType = FileType (tt_filetype tt)+ }++-- | An empty track.+emptyTrack :: Track+emptyTrack =+ Track { trackAlbum = ""+ , trackArtist = ""+ , trackBitrate = 0+ , trackBitrateType = 0+ , trackComposer = ""+ , trackDate = ""+ , trackDuration = 0+ , trackFileName = ""+ , trackFileSize = 0+ , trackGenre = ""+ , trackFileType = unknown+ , trackID = 0+ , trackChannels = 0+ , trackParentID = 0+ , trackRating = 0+ , trackSamplerate = 0+ , trackStorageID = 0+ , trackTitle = ""+ , trackNumber = 0+ , trackUseCount = 0+ , trackWavecodec = 0+ }++-- | Test whether a track exists on the device.+doesTrackExist :: MTPHandle -> Int -> IO Bool+doesTrackExist h i = withMTPHandle h $ \devptr -> do+ exists <- c_track_exists devptr (fromIntegral i)+ return $ exists /= 0++-- | Get a list of all tracks stored on the device.+getTrackListing :: MTPHandle -> IO [Track]+getTrackListing h = withMTPHandle h $ \ptr ->+ peekTrack =<< c_get_tracklisting ptr nullPtr nullPtr++-- | Copy a track from the device to a local file.+getTrack :: MTPHandle -> Int -> FilePath -> IO ()+getTrack h i n = withMTPHandle h $ \devptr ->+ withCAString n $ \strptr -> do+ r <- c_get_track_to_file devptr (fromIntegral i) strptr nullPtr nullPtr+ unless (r == 0) (checkError h)++-- | Send a local track to the device, using the supplied metadata.+sendTrack :: MTPHandle -> FilePath -> Track -> IO ()+sendTrack h n t = withMTPHandle h $ \devptr ->+ withCAString n $ \strptr -> withTrackPtr t $ \tt_ptr -> do+ r <- c_send_track_from_file devptr strptr tt_ptr nullPtr nullPtr+ unless (r == 0) (checkError h)++-- | Copy a track from the device to a file handle.+hGetTrack :: MTPHandle -> Int -> Handle -> IO ()+hGetTrack h i fd = withMTPHandle h $ \devptr -> do+ oh <- handleToCFile fd "w"+ r <- c_get_track_to_file_descriptor devptr (fromIntegral i) oh nullPtr+ nullPtr+ fflush oh+ fclose oh+ unless (r == 0) (checkError h)++-- | Send a track to the device from a file handle.+hSendTrack :: MTPHandle -> Handle -> Track -> IO ()+hSendTrack h fd t = withMTPHandle h $ \devptr ->+ withTrackPtr t $ \track_ptr -> do+ ih <- handleToCFile fd "r"+ r <- c_send_track_from_file_descriptor devptr ih track_ptr nullPtr+ nullPtr+ fclose ih+ unless (r == 0) (checkError h)++-- | Update track metadata.+updateTrack :: MTPHandle -> Track -> IO ()+updateTrack h t = withMTPHandle h $ \devptr ->+ withTrackPtr t $ \tt_ptr -> do+ r <- c_update_track_metadata devptr tt_ptr+ unless (r == 0) (checkError h)++-- | Get metadata for a single track.+getTrackMetadata :: MTPHandle -> Int -> IO (Maybe Track)+getTrackMetadata h i = withMTPHandle h $ \devptr -> do+ r <- peekTrack =<< c_get_trackmetadata devptr (fromIntegral i)+ return $ listToMaybe r++-- | Rename a single track.+setTrackName :: MTPHandle -> Track -> String -> IO ()+setTrackName h t n = withMTPHandle h $ \devptr ->+ withTrackPtr t $ \track_ptr ->+ withCAString n $ \name_ptr -> do+ r <- c_set_track_name devptr track_ptr name_ptr+ unless (r == 0) (checkError h)++------------------------------------------------------------------------------+-- Folder management+------------------------------------------------------------------------------++peekFolder :: Ptr Folder_t -> IO [Folder]+peekFolder = go []+ where+ go acc p =+ if p == nullPtr+ then return acc+ else do+ fdt <- peek p+ fdn <- convert fdt+ free p+ go (fdn : acc) (fdt_sibling fdt)+ convert fdt = do+ name <- peekCString (fdt_name fdt)+ child <- if fdt_child fdt == nullPtr+ then return Nothing+ else peek (fdt_child fdt) >>= convert >>= return . Just+ return $! Folder+ { folderID = fromIntegral (fdt_folder_id fdt)+ , folderParentID = fromIntegral (fdt_parent_id fdt)+ , folderStorageID = fromIntegral (fdt_storage_id fdt)+ , folderName = name+ , folderChild = child+ }++-- XXX: should handle siblings and children+withFolderPtr :: Folder -> (Ptr Folder_t -> IO a) -> IO a+withFolderPtr f = bracket alloc free+ where+ alloc = do+ ptr <- malloc :: IO (Ptr Folder_t)+ fdt <- marshall+ poke ptr fdt+ return ptr+ marshall =+ withCAString (folderName f) $ \name_ptr ->+ return Folder_t+ { fdt_folder_id = fromIntegral (folderID f)+ , fdt_parent_id = fromIntegral (folderParentID f)+ , fdt_storage_id = fromIntegral (folderStorageID f)+ , fdt_name = name_ptr+ , fdt_sibling = nullPtr+ , fdt_child = nullPtr+ }++-- | Create a new folder.+createFolder :: MTPHandle+ -> String -- ^ Folder name+ -> Int -- ^ Parent ID+ -> Int -- ^ Storage ID+ -> IO Int -- ^ ID to new folder+createFolder h n pid sid = withMTPHandle h $ \devptr ->+ withCAString n $ \name_ptr -> do+ r <- c_create_folder devptr name_ptr (fromIntegral pid) (fromIntegral sid)+ when (r == 0) (checkError h)+ return $ fromIntegral r++-- | Get a list of all folders on the device.+getFolderList :: MTPHandle -> IO [Folder]+getFolderList h = withMTPHandle h $ \devptr ->+ peekFolder =<< c_folder_list devptr++-- | Rename a folder.+setFolderName :: MTPHandle -> Folder -> String -> IO ()+setFolderName h f n = withMTPHandle h $ \devptr ->+ withFolderPtr f $ \folder_ptr ->+ withCAString n $ \name_ptr -> do+ r <- c_set_folder_name devptr folder_ptr name_ptr+ unless (r == 0) (checkError h)++------------------------------------------------------------------------------+-- Playlist management+------------------------------------------------------------------------------++-- Marshall a Playlist into a Playlist_t pointer using temporary storage.+withPlaylistPtr :: Playlist -> (Ptr Playlist_t -> IO a) -> IO a+withPlaylistPtr pl = bracket alloc free+ where+ alloc = do+ ptr <- malloc :: IO (Ptr Playlist_t)+ pt <- marshall+ poke ptr pt+ return ptr+ marshall =+ withCAString (playlistName pl) $ \name_ptr ->+ withArray (map fromIntegral (playlistTracks pl)) $ \tracks_ptr ->+ return Playlist_t+ { pt_playlist_id = fromIntegral (playlistID pl)+ , pt_parent_id = fromIntegral (playlistParentID pl)+ , pt_storage_id = fromIntegral (playlistStorageID pl)+ , pt_name = name_ptr+ , pt_tracks = tracks_ptr+ , pt_no_tracks = fromIntegral (playlistNoTracks pl)+ , pt_next = nullPtr+ }++peekPlaylist :: Ptr Playlist_t -> IO [Playlist]+peekPlaylist = go []+ where+ go acc p =+ if p == nullPtr+ then return acc+ else do+ pt <- peek p+ pn <- convert pt+ free p+ go (pn : acc) (pt_next pt)+ convert pt = do+ name <- peekCString (pt_name pt)+ let no_tracks = fromIntegral (pt_no_tracks pt)+ tracks <- peekArray no_tracks (pt_tracks pt)+ return $! Playlist { playlistID = fromIntegral (pt_playlist_id pt)+ , playlistParentID = fromIntegral (pt_parent_id pt)+ , playlistStorageID = fromIntegral (pt_storage_id pt)+ , playlistName = name+ , playlistTracks = map fromIntegral tracks+ , playlistNoTracks = no_tracks }++-- | Get a list of playlists on the device.+getPlaylistList :: MTPHandle -> IO [Playlist]+getPlaylistList h = withMTPHandle h $ \devptr ->+ peekPlaylist =<< c_get_playlist_list devptr++-- | Get a single playlist by ID.+getPlaylist :: MTPHandle -> Int -> IO (Maybe Playlist)+getPlaylist h plid = withMTPHandle h $ \devptr -> do+ r <- peekPlaylist =<< c_get_playlist devptr (fromIntegral plid)+ return $ listToMaybe r++-- | Create a new playlist using the metadata supplied.+createPlaylist :: MTPHandle -> Playlist -> IO ()+createPlaylist h pl = withMTPHandle h $ \devptr ->+ withPlaylistPtr pl $ \plptr -> do+ r <- c_create_new_playlist devptr plptr+ unless (r == 0) (checkError h)++-- | Update an existing playlist.+updatePlaylist :: MTPHandle -> Playlist -> IO ()+updatePlaylist h pl = withMTPHandle h $ \devptr ->+ withPlaylistPtr pl $ \plptr -> do+ r <- c_update_playlist devptr plptr+ unless (r == 0) (checkError h)++-- | Rename an existing playlist. The expected name suffix is \".pla\".+setPlaylistName :: MTPHandle -> Playlist -> String -> IO ()+setPlaylistName h pl name = withMTPHandle h $ \devptr ->+ withPlaylistPtr pl $ \plptr ->+ withCAString name $ \nameptr -> do+ r <- c_set_playlist_name devptr plptr nameptr+ unless (r == 0) (checkError h)++------------------------------------------------------------------------------+-- Object management+------------------------------------------------------------------------------++-- | Delete a single file, track, playlist, folder or any other object.+deleteObject :: MTPHandle -> Int -> IO ()+deleteObject h i = withMTPHandle h $ \devptr -> do+ r <- c_delete_object devptr (fromIntegral i)+ unless (r == 0) (checkError h)++-- | Set the filename of any object.+setObjectName :: MTPHandle -> Int -> String -> IO ()+setObjectName h i n = withMTPHandle h $ \devptr ->+ withCAString n $ \name_ptr -> do+ r <- c_set_object_filename devptr (fromIntegral i) name_ptr+ unless (r == 0) (checkError h)++------------------------------------------------------------------------------+-- Extras+------------------------------------------------------------------------------++-- | Find the FileType for a given file name.+findFileType :: FilePath -> FileType+findFileType path =+ fromMaybe unknown (lookup (takeExtension path) tbl)+ where+ tbl = [(".wav", wav), (".mp3", mp3), (".wma", wma), (".ogg", ogg)+ ,(".aa", audible), (".mp4", mp4), (".wmv", wmv), (".avi", avi)+ ,(".mpg", mpeg), (".mpeg", mpeg), (".asf", asf), (".qt", qt)+ ,(".mov", qt), (".jpg", jpeg), (".jpeg", jpeg), (".jfif", jfif)+ ,(".tif", tiff), (".tiff", tiff), (".bmp", bmp), (".gif", gif)+ ,(".pict", pict), (".pct", pict), (".pic", pict), (".png", png)+ ,(".ics", vcalendar2), (".ical", vcalendar2), (".ifb", vcalendar2)+ ,(".icalendar", vcalendar2), (".vcard", vcard3), (".vcf", vcard3)+ ,(".wmf", windowsimageformat), (".exe", winexec), (".com", winexec)+ ,(".bat", winexec), (".dll", winexec), (".sys", winexec), (".txt", text)+ ,(".html", html), (".bin", firmware), (".aac", aac), (".flac", flac)+ ,(".mp2", mp2), (".m4a", m4a), (".doc", doc), (".xml", xml), (".xls", xls)+ ,(".ppt", ppt), (".mht", mht), (".jp2", jp2), (".jpx", jpx)]
+ MTP/Foreign.hsc view
@@ -0,0 +1,507 @@+{-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}++-- |+-- Module : MTP.Foreign+-- Copyright : (c) Joachim Fasting 2010+-- License : LGPL+--+-- Maintainer : Joachim Fasting <joachim.fasting@gmail.com>+-- Stability : unstable+-- Portability : not portable+--+-- Bindings to libmtp. Foreign interface.++module MTP.Foreign where++import Foreign+import Foreign.C++#include <libmtp.h>++#let alignment t = "%lu", (unsigned long)offsetof(struct {char x__; t(y__); }, y__)++------------------------------------------------------------------------------+-- Constants+------------------------------------------------------------------------------++-- | MTP library version.+version :: Integer+version = #const LIBMTP_VERSION_STRING++------------------------------------------------------------------------------+-- Types+------------------------------------------------------------------------------++-- | Filetype enumeration.+newtype FileType = FileType { unFileType :: CInt }+ deriving (Eq, Ord, Show)++#{enum FileType, FileType+ ,wav = LIBMTP_FILETYPE_WAV+ ,mp3 = LIBMTP_FILETYPE_MP3+ ,wma = LIBMTP_FILETYPE_WMA+ ,ogg = LIBMTP_FILETYPE_OGG+ ,audible = LIBMTP_FILETYPE_AUDIBLE+ ,mp4 = LIBMTP_FILETYPE_MP4+ ,undef_audio = LIBMTP_FILETYPE_UNDEF_AUDIO+ ,wmv = LIBMTP_FILETYPE_WMV+ ,avi = LIBMTP_FILETYPE_AVI+ ,mpeg = LIBMTP_FILETYPE_MPEG+ ,asf = LIBMTP_FILETYPE_ASF+ ,qt = LIBMTP_FILETYPE_QT+ ,undef_video = LIBMTP_FILETYPE_UNDEF_VIDEO+ ,jpeg = LIBMTP_FILETYPE_JPEG+ ,jfif = LIBMTP_FILETYPE_JFIF+ ,tiff = LIBMTP_FILETYPE_TIFF+ ,bmp = LIBMTP_FILETYPE_BMP+ ,gif = LIBMTP_FILETYPE_GIF+ ,pict = LIBMTP_FILETYPE_PICT+ ,png = LIBMTP_FILETYPE_PNG+ ,vcalendar1 = LIBMTP_FILETYPE_VCALENDAR1+ ,vcalendar2 = LIBMTP_FILETYPE_VCALENDAR2+ ,vcard2 = LIBMTP_FILETYPE_VCARD2+ ,vcard3 = LIBMTP_FILETYPE_VCARD3+ ,windowsimageformat = LIBMTP_FILETYPE_WINDOWSIMAGEFORMAT+ ,winexec = LIBMTP_FILETYPE_WINEXEC+ ,text = LIBMTP_FILETYPE_TEXT+ ,html = LIBMTP_FILETYPE_HTML+ ,firmware = LIBMTP_FILETYPE_FIRMWARE+ ,aac = LIBMTP_FILETYPE_AAC+ ,mediacard = LIBMTP_FILETYPE_MEDIACARD+ ,flac = LIBMTP_FILETYPE_FLAC+ ,mp2 = LIBMTP_FILETYPE_MP2+ ,m4a = LIBMTP_FILETYPE_M4A+ ,doc = LIBMTP_FILETYPE_DOC+ ,xml = LIBMTP_FILETYPE_XML+ ,xls = LIBMTP_FILETYPE_XLS+ ,ppt = LIBMTP_FILETYPE_PPT+ ,mht = LIBMTP_FILETYPE_MHT+ ,jp2 = LIBMTP_FILETYPE_JP2+ ,jpx = LIBMTP_FILETYPE_JPX+ ,album = LIBMTP_FILETYPE_ALBUM+ ,playlist = LIBMTP_FILETYPE_PLAYLIST+ ,unknown = LIBMTP_FILETYPE_UNKNOWN+ }++-- Error code enumeration.+newtype ErrorCode = ErrorCode { unErrorCode :: CInt }+ deriving (Eq, Show)++#{enum ErrorCode, ErrorCode+ ,general = LIBMTP_ERROR_GENERAL+ ,noDevice = LIBMTP_ERROR_NO_DEVICE_ATTACHED+ ,storageFull = LIBMTP_ERROR_STORAGE_FULL+ ,connectionFailed = LIBMTP_ERROR_CONNECTING+ ,cancelled = LIBMTP_ERROR_CANCELLED+ }++-- Opaque types, only passed around between C funcs, but never+-- dereferenced on the Haskell side.+data MTPDevice+data Data+data Callback++-- An intermediate structure for the LIBMTP_error_t struct.+data Error_t = Error_t+ { et_errornumber :: CInt+ , et_errortext :: CString+ , et_next :: Ptr Error_t+ }++instance Storable Error_t where+ sizeOf _ = #{size LIBMTP_error_t}+ alignment _ = #{alignment LIBMTP_error_t}+ peek ptr = do+ en <- #{peek LIBMTP_error_t, errornumber} ptr+ et <- #{peek LIBMTP_error_t, error_text} ptr+ next <- #{peek LIBMTP_error_t, next} ptr+ return $! Error_t { et_errornumber = en+ , et_errortext = et+ , et_next = next+ }++-- An intermediate structure for the LIBMTP_file_t struct.+-- We need this because LIBMPT_file_t is recursive.+data File_t = File_t+ { ft_item_id :: CInt+ , ft_parent_id :: CInt+ , ft_storage_id :: CInt+ , ft_filename :: CString+ , ft_filesize :: CInt+ , ft_filetype :: CInt+ , ft_next :: Ptr File_t+ }++instance Storable File_t where+ sizeOf _ = #{size LIBMTP_file_t}+ alignment _ = #{alignment LIBMTP_file_t}+ peek ptr = do+ fid <- #{peek LIBMTP_file_t, item_id} ptr+ pid <- #{peek LIBMTP_file_t, parent_id} ptr+ sid <- #{peek LIBMTP_file_t, storage_id} ptr+ name <- #{peek LIBMTP_file_t, filename} ptr+ size <- #{peek LIBMTP_file_t, filesize} ptr+ typ <- #{peek LIBMTP_file_t, filetype} ptr+ next <- #{peek LIBMTP_file_t, next} ptr+ return $! File_t fid pid sid name size typ next+ poke ptr ft = do+ #{poke LIBMTP_file_t, item_id} ptr (ft_item_id ft)+ #{poke LIBMTP_file_t, parent_id} ptr (ft_parent_id ft)+ #{poke LIBMTP_file_t, filename} ptr (ft_filename ft)+ #{poke LIBMTP_file_t, filesize} ptr (ft_filesize ft)+ #{poke LIBMTP_file_t, next} ptr (ft_next ft)++-- An intermediate structure for the LIBMTP_track_t struct.+-- We need this because LIBMTP_track_t is recursive.+data Track_t = Track_t+ { tt_item_id :: CInt+ , tt_parent_id :: CInt+ , tt_storage_id :: CInt+ , tt_title :: CString+ , tt_artist :: CString+ , tt_composer :: CString+ , tt_genre :: CString+ , tt_album :: CString+ , tt_date :: CString+ , tt_filename :: CString+ , tt_tracknumber :: CInt+ , tt_duration :: CInt+ , tt_samplerate :: CInt+ , tt_nochannels :: CInt+ , tt_wavecodec :: CInt+ , tt_bitrate :: CInt+ , tt_bitratetype :: CInt+ , tt_rating :: CInt+ , tt_usecount :: CInt+ , tt_filesize :: CInt+ , tt_filetype :: CInt+ , tt_next :: Ptr Track_t+ }++instance Storable Track_t where+ sizeOf _ = #{size LIBMTP_track_t}+ alignment _ = #{alignment LIBMTP_track_t}+ peek ptr = do+ iid <- #{peek LIBMTP_track_t, item_id} ptr+ pid <- #{peek LIBMTP_track_t, parent_id} ptr+ sid <- #{peek LIBMTP_track_t, storage_id} ptr+ title <- #{peek LIBMTP_track_t, title} ptr+ artist <- #{peek LIBMTP_track_t, artist} ptr+ composer <- #{peek LIBMTP_track_t, composer} ptr+ genre <- #{peek LIBMTP_track_t, genre} ptr+ alb <- #{peek LIBMTP_track_t, album} ptr+ date <- #{peek LIBMTP_track_t, date} ptr+ fname <- #{peek LIBMTP_track_t, filename} ptr+ trackno <- #{peek LIBMTP_track_t, tracknumber} ptr+ dur <- #{peek LIBMTP_track_t, duration} ptr+ srate <- #{peek LIBMTP_track_t, samplerate} ptr+ chans <- #{peek LIBMTP_track_t, nochannels} ptr+ codec <- #{peek LIBMTP_track_t, wavecodec} ptr+ brate <- #{peek LIBMTP_track_t, bitrate} ptr+ bratetype <- #{peek LIBMTP_track_t, bitratetype} ptr+ rating <- #{peek LIBMTP_track_t, rating} ptr+ count <- #{peek LIBMTP_track_t, usecount} ptr+ fsize <- #{peek LIBMTP_track_t, filesize} ptr+ ftype <- #{peek LIBMTP_track_t, filetype} ptr+ next <- #{peek LIBMTP_track_t, next} ptr+ return $! Track_t iid pid sid title artist composer genre alb+ date fname trackno dur srate chans codec brate+ bratetype rating count fsize ftype next+ poke ptr tt = do+ #{poke LIBMTP_track_t, item_id} ptr (tt_item_id tt)+ #{poke LIBMTP_track_t, parent_id} ptr (tt_parent_id tt)+ #{poke LIBMTP_track_t, storage_id} ptr (tt_storage_id tt)+ #{poke LIBMTP_track_t, title} ptr (tt_title tt)+ #{poke LIBMTP_track_t, artist} ptr (tt_artist tt)+ #{poke LIBMTP_track_t, composer} ptr (tt_composer tt)+ #{poke LIBMTP_track_t, genre} ptr (tt_genre tt)+ #{poke LIBMTP_track_t, album} ptr (tt_album tt)+ #{poke LIBMTP_track_t, date} ptr (tt_date tt)+ #{poke LIBMTP_track_t, filename} ptr (tt_filename tt)+ #{poke LIBMTP_track_t, tracknumber} ptr (tt_tracknumber tt)+ #{poke LIBMTP_track_t, duration} ptr (tt_duration tt)+ #{poke LIBMTP_track_t, samplerate} ptr (tt_samplerate tt)+ #{poke LIBMTP_track_t, nochannels} ptr (tt_nochannels tt)+ #{poke LIBMTP_track_t, wavecodec} ptr (tt_wavecodec tt)+ #{poke LIBMTP_track_t, bitrate} ptr (tt_bitrate tt)+ #{poke LIBMTP_track_t, bitratetype} ptr (tt_bitratetype tt)+ #{poke LIBMTP_track_t, rating} ptr (tt_rating tt)+ #{poke LIBMTP_track_t, usecount} ptr (tt_usecount tt)+ #{poke LIBMTP_track_t, filesize} ptr (tt_filesize tt)+ #{poke LIBMTP_track_t, filetype} ptr (tt_filetype tt)+ #{poke LIBMTP_track_t, next} ptr (tt_next tt)++-- An intermediate structure for the LIBMTP_folder_t struct.+-- We need this because LIBMTP_folder_t is recursive.+data Folder_t = Folder_t+ { fdt_folder_id :: CInt+ , fdt_parent_id :: CInt+ , fdt_storage_id :: CInt+ , fdt_name :: CString+ , fdt_sibling :: Ptr Folder_t+ , fdt_child :: Ptr Folder_t+ }++instance Storable Folder_t where+ sizeOf _ = #{size LIBMTP_folder_t}+ alignment _ = #{alignment LIBMTP_folder_t}+ peek ptr = do+ fid <- #{peek LIBMTP_folder_t, folder_id} ptr+ pid <- #{peek LIBMTP_folder_t, parent_id} ptr+ sid <- #{peek LIBMTP_folder_t, storage_id} ptr+ name <- #{peek LIBMTP_folder_t, name} ptr+ sibling <- #{peek LIBMTP_folder_t, sibling} ptr+ child <- #{peek LIBMTP_folder_t, child} ptr+ return $! Folder_t { fdt_folder_id = fid+ , fdt_parent_id = pid+ , fdt_storage_id = sid+ , fdt_name = name+ , fdt_sibling = sibling+ , fdt_child = child+ }+ poke ptr fdt = do+ #{poke LIBMTP_folder_t, folder_id} ptr (fdt_folder_id fdt)+ #{poke LIBMTP_folder_t, parent_id} ptr (fdt_parent_id fdt)+ #{poke LIBMTP_folder_t, storage_id} ptr (fdt_storage_id fdt)+ #{poke LIBMTP_folder_t, name} ptr (fdt_name fdt)+ #{poke LIBMTP_folder_t, sibling} ptr (fdt_sibling fdt)+ #{poke LIBMTP_folder_t, child} ptr (fdt_child fdt)++-- An intermediate structure for the LIBMTP_playlist_t struct.+-- We need this because LIBMTP_playlist_t is recursive.+data Playlist_t = Playlist_t+ { pt_playlist_id :: CInt+ , pt_parent_id :: CInt+ , pt_storage_id :: CInt+ , pt_name :: CString+ , pt_tracks :: Ptr CInt+ , pt_no_tracks :: CInt+ , pt_next :: Ptr Playlist_t+ }++instance Storable Playlist_t where+ sizeOf _ = #{size LIBMTP_playlist_t}+ alignment _ = #{alignment LIBMTP_playlist_t}+ peek ptr = do+ plid <- #{peek LIBMTP_playlist_t, playlist_id} ptr+ pid <- #{peek LIBMTP_playlist_t, parent_id} ptr+ sid <- #{peek LIBMTP_playlist_t, storage_id} ptr+ name <- #{peek LIBMTP_playlist_t, name} ptr+ tracks <- #{peek LIBMTP_playlist_t, tracks} ptr+ ntracks <- #{peek LIBMTP_playlist_t, no_tracks} ptr+ next <- #{peek LIBMTP_playlist_t, next} ptr+ return $ Playlist_t plid pid sid name tracks ntracks next+ poke ptr pt = do+ #{poke LIBMTP_playlist_t, playlist_id} ptr (pt_playlist_id pt)+ #{poke LIBMTP_playlist_t, parent_id} ptr (pt_parent_id pt)+ #{poke LIBMTP_playlist_t, storage_id} ptr (pt_storage_id pt)+ #{poke LIBMTP_playlist_t, name} ptr (pt_name pt)+ #{poke LIBMTP_playlist_t, tracks} ptr (pt_tracks pt)+ #{poke LIBMTP_playlist_t, no_tracks} ptr (pt_no_tracks pt)+ #{poke LIBMTP_playlist_t, next} ptr (pt_next pt)++------------------------------------------------------------------------------+-- Foreign imports+------------------------------------------------------------------------------++foreign import ccall unsafe "LIBMTP_Init" c_init :: IO ()++foreign import ccall unsafe "LIBMTP_Get_First_Device" c_get_first_device+ :: IO (Ptr MTPDevice)++foreign import ccall unsafe "LIBMTP_Release_Device" c_release_device+ :: Ptr MTPDevice -> IO ()++foreign import ccall unsafe "LIBMTP_Reset_Device" c_reset_device+ :: Ptr MTPDevice+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Errorstack" c_get_errorstack+ :: Ptr MTPDevice+ -> IO (Ptr Error_t)++foreign import ccall unsafe "LIBMTP_Clear_Errorstack" c_clear_errorstack+ :: Ptr MTPDevice+ -> IO ()++foreign import ccall unsafe "LIBMTP_Get_Manufacturername"+ c_get_manufacturername :: Ptr MTPDevice -> IO CString++foreign import ccall unsafe "LIBMTP_Get_Modelname"+ c_get_modelname :: Ptr MTPDevice -> IO CString++foreign import ccall unsafe "LIBMTP_Get_Serialnumber"+ c_get_serialnumber :: Ptr MTPDevice -> IO CString++foreign import ccall unsafe "LIBMTP_Get_Friendlyname"+ c_get_friendlyname :: Ptr MTPDevice -> IO CString++foreign import ccall unsafe "LIBMTP_Get_Deviceversion" c_get_deviceversion+ :: Ptr MTPDevice -> IO CString++foreign import ccall unsafe "LIBMTP_Get_Batterylevel" c_get_batterylevel+ :: Ptr MTPDevice+ -> Ptr CInt+ -> Ptr CInt+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Supported_Filetypes"+ c_get_supported_filetypes :: Ptr MTPDevice+ -> Ptr CInt+ -> Ptr CInt+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Filelisting_With_Callback" c_get_filelisting+ :: Ptr MTPDevice+ -> Ptr Callback+ -> Ptr Data+ -> IO (Ptr File_t)++foreign import ccall unsafe "LIBMTP_Track_Exists" c_track_exists+ :: Ptr MTPDevice+ -> CInt+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_File_To_File" c_get_file_to_file+ :: Ptr MTPDevice+ -> CInt+ -> CString+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Send_File_From_File" c_send_file_from_file+ :: Ptr MTPDevice+ -> CString+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_File_To_File_Descriptor"+ c_get_file_to_file_descriptor :: Ptr MTPDevice+ -> CInt+ -> Ptr CFile+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Send_File_From_File_Descriptor"+ c_send_file_from_file_descriptor :: Ptr MTPDevice+ -> Ptr CFile+ -> Ptr File_t+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Set_File_Name" c_set_file_name+ :: Ptr MTPDevice+ -> Ptr File_t+ -> CString+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Tracklisting_With_Callback" c_get_tracklisting+ :: Ptr MTPDevice+ -> Ptr Callback+ -> Ptr Data+ -> IO (Ptr Track_t)++foreign import ccall unsafe "LIBMTP_Get_Track_To_File" c_get_track_to_file+ :: Ptr MTPDevice+ -> CInt+ -> CString+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Send_Track_From_File" c_send_track_from_file+ :: Ptr MTPDevice+ -> CString+ -> (Ptr Track_t)+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Send_Track_From_File_Descriptor"+ c_send_track_from_file_descriptor :: Ptr MTPDevice+ -> Ptr CFile+ -> Ptr Track_t+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Track_To_File_Descriptor"+ c_get_track_to_file_descriptor :: Ptr MTPDevice+ -> CInt+ -> Ptr CFile+ -> Ptr Callback+ -> Ptr Data+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Trackmetadata"+ c_get_trackmetadata :: Ptr MTPDevice+ -> CInt+ -> IO (Ptr Track_t)++foreign import ccall unsafe "LIBMTP_Update_Track_Metadata"+ c_update_track_metadata :: Ptr MTPDevice+ -> Ptr Track_t+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Set_Track_Name" c_set_track_name+ :: Ptr MTPDevice+ -> Ptr Track_t+ -> CString+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Create_Folder" c_create_folder+ :: Ptr MTPDevice+ -> CString+ -> CInt+ -> CInt+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Folder_List" c_folder_list+ :: Ptr MTPDevice+ -> IO (Ptr Folder_t)++foreign import ccall unsafe "LIBMTP_Set_Folder_Name" c_set_folder_name+ :: Ptr MTPDevice+ -> Ptr Folder_t+ -> CString+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Get_Playlist_List"+ c_get_playlist_list :: Ptr MTPDevice+ -> IO (Ptr Playlist_t)++foreign import ccall unsafe "LIBMTP_Get_Playlist"+ c_get_playlist :: Ptr MTPDevice+ -> CInt+ -> IO (Ptr Playlist_t)++foreign import ccall unsafe "LIBMTP_Create_New_Playlist"+ c_create_new_playlist :: Ptr MTPDevice+ -> Ptr Playlist_t+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Update_Playlist"+ c_update_playlist :: Ptr MTPDevice+ -> Ptr Playlist_t+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Set_Playlist_Name"+ c_set_playlist_name :: Ptr MTPDevice+ -> Ptr Playlist_t+ -> CString+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Delete_Object"+ c_delete_object :: Ptr MTPDevice+ -> CInt+ -> IO CInt++foreign import ccall unsafe "LIBMTP_Set_Object_Filename"+ c_set_object_filename :: Ptr MTPDevice+ -> CInt+ -> CString+ -> IO CInt
+ MTP/Handle.hs view
@@ -0,0 +1,53 @@+-- |+-- Module : MTP.Handle+-- Copyright : (c) Joachim Fasting 2010+-- License : LGPL+--+-- Maintainer : Joachim Fasting <joachim.fasting@gmail.com>+-- Stability : unstable+-- Portability : not portable+--+-- Bindings to libmtp. Device handle.++module MTP.Handle (MTPHandle, isClosed, open, close, withMTPHandle) where++import MTP.Foreign (MTPDevice)++import Control.Concurrent.MVar+import Control.Monad+import Foreign++data HandleState = Closed | Open deriving Eq++data MTPHandle_ = MTPHandle_+ { haDev :: !(ForeignPtr MTPDevice)+ , haState :: HandleState+ }++-- | A handle to an MTP device connection.+data MTPHandle = MTPHandle (MVar MTPHandle_)++-- Open handle, given a device pointer.+open :: Ptr MTPDevice -> IO MTPHandle+open dptr = do+ -- XXX: newForeignPtr finalizerFree devptr causes a double free+ -- error when the finalizer is run.+ dev <- newForeignPtr_ dptr+ h_ <- newMVar $ MTPHandle_ dev Open+ return (MTPHandle h_)++-- Close handle.+close :: MTPHandle -> IO ()+close (MTPHandle hv) = modifyMVar_ hv (\x -> return x { haState = Closed })++-- Test whether the handle is closed.+isClosed :: MTPHandle -> IO Bool+isClosed (MTPHandle hv) = withMVar hv (\x -> return $ haState x == Closed)++-- A helper that lifts operations on MTPDevice into MTPHandle.+-- Throws error if the handle is unusable.+withMTPHandle :: MTPHandle -> (Ptr MTPDevice -> IO a) -> IO a+withMTPHandle h@(MTPHandle hv) f = do+ closed <- isClosed h+ when closed (fail "MTPHandle is closed")+ withMVar hv (\x -> withForeignPtr (haDev x) f)
+ README.md view
@@ -0,0 +1,33 @@+# About+A FFI binding to [libmtp], an implementation of the Media Transfer Protocol.++[libmtp]: http://libmtp.sourceforge.net++# Getting+* [Latest release]+* `git clone git://github.com/joachifm/hsmtp.git`++[Latest release]: http://hackage.haskell.org/package/mtp++# Building+The preferred method of building is using [cabal-install]:++ $ cd hsmtp+ $ cabal configure+ $ cabal build+ $ cabal install++[cabal-install]: http://hackage.haskell.org/package/cabal-install++# Usage++ > import qualified MTP+ > main = do+ > MTP.init+ > MTP.withFirstDevice MTP.getDeviceVersion++# Author+Joachim Fasting \<joachim.fasting@gmail.com\>++# Licence+LGPL version 2.1 (see COPYING)
+ Setup.lhs view
@@ -0,0 +1,4 @@+#!/usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMainWithHooks defaultUserHooks
+ mtp.cabal view
@@ -0,0 +1,34 @@+name: mtp+version: 0.1.1.1+license: LGPL+license-file: COPYING+copyright: Joachim Fasting 2010+author: Joachim Fasting+maintainer: Joachim Fasting <joachim.fasting@gmail.com>+category: Sound+synopsis: Bindings to libmtp+description:+ A FFI binding to libmtp, an implementation of the Media Transfer+ Protocol.+ .+ See <http://libmtp.sourceforge.net> for more information.+extra-source-files: ChangeLog, README.md+build-type: Simple+cabal-version: >= 1.6 && < 1.9++source-repository head+ type: git+ location: git://github.com/joachifm/hsmtp.git++library+ exposed-modules: MTP+ other-modules: Foreign.Handle,+ MTP.Foreign,+ MTP.Handle+ build-depends: base ==4.2.*, unix ==2.4.*, filepath ==1.1.*+ extensions: ForeignFunctionInterface, CPP, EmptyDataDecls,+ DeriveDataTypeable+ includes: libmtp.h+ extra-libraries: mtp+ ghc-options: -Wall+ ghc-prof-options: -prof -auto-all