pulseview-0.2.0/COPYING000600 001750 001750 00000104513 12332253627 014232 0ustar00uweuwe000000 000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If 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 convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . pulseview-0.2.0/icons/trigger-rising.svg000600 001750 001750 00000001754 12332253627 017772 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/zoom-out.png000600 001750 001750 00000001213 12332253627 016602 0ustar00uweuwe000000 000000 PNG  IHDRj PLTExxx>>6992pqnMNKLMJhifwytgheZ]X鲳XZVwytz}w{}xD+tRNS !$$%()FVIDATxڕ][Av}PBJZ !ZFv[(֥t,5Kؒ^!gէ@G|07|̑hBnp h@&6u5cP5ȕe۪ޣ^V w֢Y=4E*BN]H#/ J \?̟{`,{+eOw7xp'r=Bs(NU;AIENDB`pulseview-0.2.0/icons/status-grey.svg000600 001750 001750 00000002147 12332253627 017322 0ustar00uweuwe000000 000000 pulseview-0.2.0/icons/decoder-delete.svg000600 001750 001750 00000001651 12332253627 017677 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/sigrok-logo-notext.png000600 001750 001750 00000007306 12332253627 020575 0ustar00uweuwe000000 000000 PNG  IHDR00WsRGBbKGDC pHYs  YIDAThZkpUUyWnn$@ G% m;% ]S 6@[ilLMVL{LJNCc$$ !skI$igW{kksAD#܀f#"fz~駈H4ј1?\"TTT< o׏N@(~EE#fP '4EQ,4Ѥq8x`(63ِg2X"lݺus}tTVVN1#l@eeԂnvܹ9[n]z~O13Vx<4 2"t3y>HXe(#"999'O:5 Nr8N+SNe]>u䜜brC7znA(eѢE/c%z!\) ;\I6;>\TTT fMI0 6PryڴiW7o 1&}ظq;eeeKwjihh1dn۟PCCCNVVVaʖlܸNCygMvec4`&m.mnS"M`ݻw0, #"޽{Eqq&ٹ,.QoЅŃ";|Szh'VrE" mmm۶m{r /^/|N=|f̫oOZ,ϟ_YZZZlЬs455vOQa/ MӠ8}/֯_vO4559Yϯi|{.3e-y"j*++W10hDѱ\e&7Ԑ6_c޽nw]D75]z%fz-qhɩݵs4{/w}!œ93@;w-ɩlafzj& mooOFUWW{4,L$dW1M@]vOyyjc]D$Xf<5b׈H,q@x k׮@4k]^PSS@Suu!fX撒LK<زeK; e?""듫O:X'zeGDD?AΚ."0@[l)Vka 0wtٲee}p(rΑ{"37jV|-CD}+^WzH̥Yo=Aq|J }Lvn cEs-[,//S"~ȀF4x<I>i_SӥjDĨq:0Dv>=^_ NSIZ=sҤIz<Jhy… w-@5Wl."r\3uOگI4yښvzU츇]]z^w8#hS(?c+щWrdp>sR-W׭{znyM%yFN8N/yS';*.tp@^GuOrp<(*ڤA-j|Q΢`, _S#m5`+TQלz \!C8*Wp{y"(1V`ѺW|내ԙ\l@ ,!=XZ[guZ+ 2~Gt8UB/t&`ӊA=U.z!h!0ADDQH q5ҍpPAjf\ .,;o lʢ(J(Bf2d ېd4@QA$ 0]d3mH))B$I ">Me]Qbr!D( :b1x<@/dJB4"U݂?#qaZq9ɓs gs!V  E*t]G\  7o7A:{퍀3 ?Q`@yn׹Bm״.y4`"! xѸ%{13f rO# uˌŎ<#Sҭ ɓzKtrN1%oYdEX(@.+;]4(v,L$YeD ( oA:DQd痢K1"ӖJ\%WC:42tvbV$1b?&IvUE"D9JϓP_[wcd_ՇH$[2aƗ#.6w=8z({~=P,lIENDB`pulseview-0.2.0/icons/zoom-original.png000600 001750 001750 00000001376 12332253627 017611 0ustar00uweuwe000000 000000 PNG  IHDRj JPLTExxx>>6992pqnMNKLMJhifwytgheZ]X鲳XZVjmhuwrz}w|s68+tRNS !$$%()FV8IDATxڕ[WaQT:)%I1"1ЌSw۶^\l,v8[zSawgDS4r٣M>hE oznowןpsZru{p )h4rPX )Zʂ 2N9JSރ$1*QPGi (7Mlv9Q")J\Fe9HVA:JȲΓne^;YLGpuw{>Y]40o^Y۲ml%#9M:78\gbȘ4hIENDB`pulseview-0.2.0/icons/trigger-low.svg000600 001750 001750 00000001743 12332253627 017276 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/document-open.png000600 001750 001750 00000001627 12332253627 017577 0ustar00uweuwe000000 000000 PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8OhU?ofv6Y"h6 EP=GdLsVxEAD̩g(Bjm͡lCI7ݝy{nwӨI1}˲R9sp&4MۻKg~snb,.~q|雥)`ι 63(rTPUN^<ʅIӴ?03TU'".]41'8sw\|`jTUpΑ)W5@FDx)wP!:-H"0r /T5h2cD*B.XDbW'܎_Y뵗;vg,hB|JQtwf3#rcs9׾|ͣClV{zbKGd[6XDKcffV_P{kQua+Uu0H؏ԠEW#:f a:^p# @P@nP#/F7B^ïrP{Q&;+1UD{[zEGy+GT'W"`lw+Z[}?l\;NQYм k1 PVv88n<l$:XDbj[bl6ύz`(@0Sm_#8y寫׫F=+̤Xrݫ Opa,2=eY\n0yT6WwaIENDB`pulseview-0.2.0/icons/sigrok-logo-notext.ico000600 001750 001750 00000022676 12332253627 020572 0ustar00uweuwe000000 000000 00%(0` $^]]  A@@p$. 0:.99G 4> 4:ISDL /78>?I>J 3;2>@NGW=KCJ*))nQpuhehikp|{r=t 7boUFLe_eilw\|wp|V^_8p$v\_`tkqwy|d{plcef7o{379${Zbb{ynpobT]jj}u{{~~|veVZebpdhdgr{{tcePXZb_}{c`B[kjmnsmnmmmnkjnwyk_:|c ѐ}oawXjzPfvSeuYi{^nj`Vr~l]Цl` K`Ft4~VK.2 "[H({-D -m[3>ĺ`, sdY}s%D*sf޻A7-[& y#vAմA,"3ߢ&(缅uS͘u;???zz|0-؞=X/⚚F <Q9U4kC ;i@ LtZ9/S{5%1/nS^h++/dd`Sqm*m?֔M~%o姡ىJJJ++-aY˵S(#xButx/NY}䞞BBB((* @IropmubUumqO րܐ)))GGGvwSڶ}ڷ|>ۿ۟6vf}}pulseview-0.2.0/icons/status-green.svg000600 001750 001750 00000002074 12332253627 017453 0ustar00uweuwe000000 000000 pulseview-0.2.0/icons/decoder-hidden.svg000600 001750 001750 00000001712 12332253627 017666 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/document-save-as.png000600 001750 001750 00000001511 12332253627 020165 0ustar00uweuwe000000 000000 PNG  IHDRj PLTEtMlnpkqUWSnTnpkx\lWUWSnpkzdjZnpkxewlnpknpk|mnpkfhdlw`}npgid[zrnpk^|ymWx}Tw[~MrQySyInMy?lBnbAl?jBpgDxJt?rG}8g;l_?HSl H*3<h"OЈ=XIENDB`pulseview-0.2.0/icons/status-red.svg000600 001750 001750 00000002071 12332253627 017122 0ustar00uweuwe000000 000000 pulseview-0.2.0/icons/decoder-shown.svg000600 001750 001750 00000003736 12332253627 017601 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/configure.png000600 001750 001750 00000002177 12332253627 017004 0ustar00uweuwe000000 000000 PNG  IHDRĴl;bKGD pHYs  d_tIME  %yQI IDAT8˥Lu_q)ࡖriV  eijX ۓQ֚c--s&i-ؔg;C%v8.@;?Am}g{>Ͼk~?DB=WvBhGiMBhŀ/JA/ Q6>=۷<%ԜS`I 0`}=222"UEB;1/gMNyKe!8íV+(] -F`ILḺ7c}޲e 6mDT$I?3yE%(jњ%;\!~?iZdJ`kut6DۧeB`!DJ||o/4=SHo}r:Հj+72E'ڣC֟m+\_hƲ$Z[[isfpSZUu; og|{#g8Z9qTSE7%\p+C^JMr.Ե%Ef5|8tdn^IVQ#u>,H+ZjEPH4($ ypf#>.;7 ܨ`L~?cO?!ʲʇW#I҂pC$kVqvɳpnOߟ-2+V=Ņ=Eʐ+,{gsE=^Kpnuߤ_P__ng~<@ee%#V<#eOSZI z؞+ ZwXt:NXe̩i466B0wҘU5FNV.$ 0<11뱱1TU{7cɶX_nq~)Ѿ6%b&jUg``Ś;zMMѯg,PS%:Ps魇 uBb#1 JQb]b*IENDB`pulseview-0.2.0/icons/trigger-change.svg000600 001750 001750 00000004401 12332253627 017714 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/trigger-none.svg000600 001750 001750 00000001602 12332253627 017426 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/zoom-fit.png000600 001750 001750 00000001331 12332253627 016556 0ustar00uweuwe000000 000000 PNG  IHDRj /PLTExxx>>6992pqnMNKLMJhifwytgheZ]X鲳XZVikfoqluwrz}w{~x6tRNS !$$%():FVbl+#IDATxڕ[SPaNvV+S+uՀC1eŚoދ[$2WSE |_OO:峝==0ө{ss; T:(@2AeBع;@*P!.܆IZ|28P ianZXMʬ(J1S04.:暩sf sy`h';1(cNGf4ro|O!*<+_(p qW8]] p^Yۊm{b奠 (< < d\}CIENDB`pulseview-0.2.0/icons/trigger-high.svg000600 001750 001750 00000001743 12332253627 017414 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/application-exit.png000600 001750 001750 00000002074 12332253627 020271 0ustar00uweuwe000000 000000 PNG  IHDRĴl;sBIT|d pHYs B(xtEXtSoftwarewww.inkscape.org<IDAT8kU?ޙ;~yM>BcL!U h "-j1Xt7!BqS]\%JhZ\4R^_&{ z03{Z @N `[=%cJ)4=0 q]}syk@}/0\ *ZZ1c RJr#l k|ןKi)x֚0 8I5CCC(PJNgH}S>Zk("Iڹ7yL!$B4 Z{=`5@H)Zrm~`8>y^ǵֈ!}{!nU,R q c"<=}#Gcw&399imIs[kWzJH*USPI^BS0t <Ì=RoYX{{Ug2fff{.Ǐ)ss0<: XP $ "db!D'NٳgJ%R^}UlT*$ 2"u$> ֢Z˃948 rK$&m8R,o=G*ns؇a4[?Nī",Fi40R=G,bw牃fdWYImzIIENDB`pulseview-0.2.0/icons/zoom-in.png000600 001750 001750 00000001327 12332253627 016407 0ustar00uweuwe000000 000000 PNG  IHDRj 5PLTExxx>>6992pqnMNKLMJhifwytgheZ]X貳UWSXZVac^lniwytz}w{}x_%+tRNS !$$%()FV&IDATxڕY[@aK+۬r˲,7\pG"DC:>潞cPBw";Clq 9+_(zdd=FV~)\VnDi4~ 4IprdfC\3S,t`Hh=*>UH.RJ%+KuUYIfز(bIJOj[,A7ѫm4\Wɗ_s2| j=?g 1ߩwq"9r8eaf;]@bIENDB`pulseview-0.2.0/icons/trigger-falling.svg000600 001750 001750 00000001760 12332253627 020110 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/icons/probes.svg000600 001750 001750 00000005035 12332253627 016324 0ustar00uweuwe000000 000000 image/svg+xml pulseview-0.2.0/config.h.in000600 001750 001750 00000002357 12332253627 015225 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Alexandru Gagniuc * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _PULSEVIEW_CONFIG_H #define _PULSEVIEW_CONFIG_H /* Application details */ #define PV_TITLE "@PV_TITLE@" #define PV_DESCRIPTION "@PV_DESCRIPTION@" #define PV_BIN_NAME "@PROJECT_NAME@" /* Pulseview version information */ #define PV_VERSION_MAJOR @PV_VERSION_MAJOR@ #define PV_VERSION_MINOR @PV_VERSION_MINOR@ #define PV_VERSION_MICRO @PV_VERSION_MICRO@ #define PV_VERSION_STRING "@PV_VERSION_STRING@" /* Platform properties */ #cmakedefine HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS #endif pulseview-0.2.0/main.cpp000600 001750 001750 00000007765 12332253627 014642 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ENABLE_DECODE #include /* First, so we avoid a _POSIX_C_SOURCE warning. */ #endif #include #include #include #include #include #ifdef ENABLE_SIGNALS #include "signalhandler.h" #endif #include "pv/devicemanager.h" #include "pv/mainwindow.h" #include "config.h" #ifdef _WIN32 // The static qsvg lib is required for SVG graphics/icons (on Windows). #include Q_IMPORT_PLUGIN(qsvg) #endif void usage() { fprintf(stdout, "Usage:\n" " %s [OPTION…] [FILE] — %s\n" "\n" "Help Options:\n" " -l, --loglevel Set libsigrok/libsigrokdecode loglevel\n" " -V, --version Show release version\n" " -h, -?, --help Show help option\n" "\n", PV_BIN_NAME, PV_DESCRIPTION); } int main(int argc, char *argv[]) { int ret = 0; struct sr_context *sr_ctx = NULL; const char *open_file = NULL; QApplication a(argc, argv); // Set some application metadata QApplication::setApplicationVersion(PV_VERSION_STRING); QApplication::setApplicationName("PulseView"); QApplication::setOrganizationDomain("http://www.sigrok.org"); // Parse arguments while (1) { static const struct option long_options[] = { {"loglevel", required_argument, 0, 'l'}, {"version", no_argument, 0, 'V'}, {"help", no_argument, 0, 'h'}, {0, 0, 0, 0} }; const int c = getopt_long(argc, argv, "l:Vh?", long_options, NULL); if (c == -1) break; switch (c) { case 'l': { const int loglevel = atoi(optarg); sr_log_loglevel_set(loglevel); #ifdef ENABLE_DECODE srd_log_loglevel_set(loglevel); #endif break; } case 'V': // Print version info fprintf(stdout, "%s %s\n", PV_TITLE, PV_VERSION_STRING); return 0; case 'h': case '?': usage(); return 0; } } if (argc - optind > 1) { fprintf(stderr, "Only one file can be openened.\n"); return 1; } else if (argc - optind == 1) open_file = argv[argc - 1]; // Initialise libsigrok if (sr_init(&sr_ctx) != SR_OK) { qDebug() << "ERROR: libsigrok init failed."; return 1; } do { #ifdef ENABLE_DECODE // Initialise libsigrokdecode if (srd_init(NULL) != SRD_OK) { qDebug() << "ERROR: libsigrokdecode init failed."; break; } // Load the protocol decoders srd_decoder_load_all(); #endif try { // Create the device manager, initialise the drivers pv::DeviceManager device_manager(sr_ctx); // Initialise the main window pv::MainWindow w(device_manager, open_file); w.show(); #ifdef ENABLE_SIGNALS if(SignalHandler::prepare_signals()) { SignalHandler *const handler = new SignalHandler(&w); QObject::connect(handler, SIGNAL(int_received()), &w, SLOT(close())); QObject::connect(handler, SIGNAL(term_received()), &w, SLOT(close())); } else { qWarning() << "Could not prepare signal handler."; } #endif // Run the application ret = a.exec(); } catch(std::exception e) { qDebug() << e.what(); } #ifdef ENABLE_DECODE // Destroy libsigrokdecode srd_exit(); #endif } while (0); // Destroy libsigrok if (sr_ctx) sr_exit(sr_ctx); return ret; } pulseview-0.2.0/CMake/memaccess.cmake000600 001750 001750 00000002376 12332253627 017125 0ustar00uweuwe000000 000000 ## ## This file is part of the PulseView project. ## ## Copyright (C) 2014 Marcus Comstedt ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## include(CheckCSourceRuns) function(memaccess_check_unaligned_le _var) if(NOT CMAKE_CROSSCOMPILING) CHECK_C_SOURCE_RUNS(" #include int main() { int i; union { uint64_t u64; uint8_t u8[16]; } d; uint64_t v; for (i=0; i<16; i++) d.u8[i] = i; v = *(uint64_t *)(d.u8+1); if (v != 0x0807060504030201ULL) return 1; return 0; }" ${_var}) endif() if(CMAKE_CROSSCOMPILING) message(STATUS "Cross compiling - using portable code for memory access") endif() endfunction() pulseview-0.2.0/CMake/cotire.cmake000600 001750 001750 00000404461 12332253627 016453 0ustar00uweuwe000000 000000 # - cotire (compile time reducer) # # See the cotire manual for usage hints. # #============================================================================= # Copyright 2012-2013 Sascha Kratky # # Permission is hereby granted, free of charge, to any person # obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without # restriction, including without limitation the rights to use, # copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following # conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES # OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. #============================================================================= if(__COTIRE_INCLUDED) return() endif() set(__COTIRE_INCLUDED TRUE) # call cmake_minimum_required, but prevent modification of the CMake policy stack in include mode # cmake_minimum_required also sets the policy version as a side effect, which we have to avoid if (NOT CMAKE_SCRIPT_MODE_FILE) cmake_policy(PUSH) endif() # we need the CMake variables CMAKE_SCRIPT_MODE_FILE and CMAKE_ARGV available since 2.8.5 # we need APPEND_STRING option for set_property available since 2.8.6 cmake_minimum_required(VERSION 2.8.6) if (NOT CMAKE_SCRIPT_MODE_FILE) cmake_policy(POP) endif() set (COTIRE_CMAKE_MODULE_FILE "${CMAKE_CURRENT_LIST_FILE}") set (COTIRE_CMAKE_MODULE_VERSION "1.4.1") include(CMakeParseArguments) include(ProcessorCount) function (cotire_determine_compiler_version _language _versionPrefix) if (NOT ${_versionPrefix}_VERSION) # use CMake's predefined compiler version variable (available since CMake 2.8.8) if (DEFINED CMAKE_${_language}_COMPILER_VERSION) set (${_versionPrefix}_VERSION "${CMAKE_${_language}_COMPILER_VERSION}") elseif (WIN32) # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared unset (ENV{VS_UNICODE_OUTPUT}) string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} ERROR_VARIABLE _versionLine OUTPUT_QUIET TIMEOUT 10) string (REGEX REPLACE ".*Version *([0-9]+(\\.[0-9]+)*).*" "\\1" ${_versionPrefix}_VERSION "${_versionLine}") else() # assume GCC like command line interface string (STRIP "${CMAKE_${_language}_COMPILER_ARG1}" _compilerArg1) execute_process (COMMAND ${CMAKE_${_language}_COMPILER} ${_compilerArg1} "-dumpversion" OUTPUT_VARIABLE ${_versionPrefix}_VERSION RESULT_VARIABLE _result OUTPUT_STRIP_TRAILING_WHITESPACE TIMEOUT 10) if (_result) set (${_versionPrefix}_VERSION "") endif() endif() if (${_versionPrefix}_VERSION) set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" CACHE INTERNAL "${_language} compiler version") endif() set (${_versionPrefix}_VERSION "${${_versionPrefix}_VERSION}" PARENT_SCOPE) if (COTIRE_DEBUG) message (STATUS "${CMAKE_${_language}_COMPILER} version ${${_versionPrefix}_VERSION}") endif() endif() endfunction() function (cotire_get_source_file_extension _sourceFile _extVar) # get_filename_component returns extension from first occurrence of . in file name # this function computes the extension from last occurrence of . in file name string (FIND "${_sourceFile}" "." _index REVERSE) if (_index GREATER -1) math (EXPR _index "${_index} + 1") string (SUBSTRING "${_sourceFile}" ${_index} -1 _sourceExt) else() set (_sourceExt "") endif() set (${_extVar} "${_sourceExt}" PARENT_SCOPE) endfunction() macro (cotire_check_is_path_relative_to _path _isRelativeVar) set (${_isRelativeVar} FALSE) if (IS_ABSOLUTE "${_path}") foreach (_dir ${ARGN}) file (RELATIVE_PATH _relPath "${_dir}" "${_path}") if (NOT _relPath OR (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.")) set (${_isRelativeVar} TRUE) break() endif() endforeach() endif() endmacro() function (cotire_filter_language_source_files _language _sourceFilesVar _excludedSourceFilesVar _cotiredSourceFilesVar) set (_sourceFiles "") set (_excludedSourceFiles "") set (_cotiredSourceFiles "") if (CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) set (_languageExtensions "${CMAKE_${_language}_SOURCE_FILE_EXTENSIONS}") else() set (_languageExtensions "") endif() if (CMAKE_${_language}_IGNORE_EXTENSIONS) set (_ignoreExtensions "${CMAKE_${_language}_IGNORE_EXTENSIONS}") else() set (_ignoreExtensions "") endif() if (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS) set (_excludeExtensions "${COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS}") else() set (_excludeExtensions "") endif() if (COTIRE_DEBUG) message (STATUS "${_language} source file extensions: ${_languageExtensions}") message (STATUS "${_language} ignore extensions: ${_ignoreExtensions}") message (STATUS "${_language} exclude extensions: ${_excludeExtensions}") endif() foreach (_sourceFile ${ARGN}) get_source_file_property(_sourceIsHeaderOnly "${_sourceFile}" HEADER_FILE_ONLY) get_source_file_property(_sourceIsExternal "${_sourceFile}" EXTERNAL_OBJECT) get_source_file_property(_sourceIsSymbolic "${_sourceFile}" SYMBOLIC) get_source_file_property(_sourceLanguage "${_sourceFile}" LANGUAGE) set (_sourceIsFiltered FALSE) if (NOT _sourceIsHeaderOnly AND NOT _sourceIsExternal AND NOT _sourceIsSymbolic) cotire_get_source_file_extension("${_sourceFile}" _sourceExt) if (_sourceExt) list (FIND _ignoreExtensions "${_sourceExt}" _ignoreIndex) if (_ignoreIndex LESS 0) list (FIND _excludeExtensions "${_sourceExt}" _excludeIndex) if (_excludeIndex GREATER -1) list (APPEND _excludedSourceFiles "${_sourceFile}") else() list (FIND _languageExtensions "${_sourceExt}" _sourceIndex) if (_sourceIndex GREATER -1) set (_sourceIsFiltered TRUE) elseif ("${_sourceLanguage}" STREQUAL "${_language}") # add to excluded sources, if file is not ignored and has correct language without having the correct extension list (APPEND _excludedSourceFiles "${_sourceFile}") endif() endif() endif() endif() endif() if (COTIRE_DEBUG) message (STATUS "${_sourceFile} filtered=${_sourceIsFiltered} language=${_sourceLanguage} header=${_sourceIsHeaderOnly}") endif() if (_sourceIsFiltered) get_source_file_property(_sourceIsExcluded "${_sourceFile}" COTIRE_EXCLUDED) get_source_file_property(_sourceIsCotired "${_sourceFile}" COTIRE_TARGET) get_source_file_property(_sourceCompileFlags "${_sourceFile}" COMPILE_FLAGS) if (COTIRE_DEBUG) message (STATUS "${_sourceFile} excluded=${_sourceIsExcluded} cotired=${_sourceIsCotired}") endif() if (_sourceIsCotired) list (APPEND _cotiredSourceFiles "${_sourceFile}") elseif (_sourceIsExcluded OR _sourceCompileFlags) list (APPEND _excludedSourceFiles "${_sourceFile}") else() list (APPEND _sourceFiles "${_sourceFile}") endif() endif() endforeach() if (COTIRE_DEBUG) message (STATUS "All: ${ARGN}") message (STATUS "${_language}: ${_sourceFiles}") message (STATUS "Excluded: ${_excludedSourceFiles}") message (STATUS "Cotired: ${_cotiredSourceFiles}") endif() set (${_sourceFilesVar} ${_sourceFiles} PARENT_SCOPE) set (${_excludedSourceFilesVar} ${_excludedSourceFiles} PARENT_SCOPE) set (${_cotiredSourceFilesVar} ${_cotiredSourceFiles} PARENT_SCOPE) endfunction() function (cotire_get_objects_with_property_on _filteredObjectsVar _property _type) set (_filteredObjects "") foreach (_object ${ARGN}) get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) if (_isSet) get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) if (_propertyValue) list (APPEND _filteredObjects "${_object}") endif() endif() endforeach() set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) endfunction() function (cotire_get_objects_with_property_off _filteredObjectsVar _property _type) set (_filteredObjects "") foreach (_object ${ARGN}) get_property(_isSet ${_type} "${_object}" PROPERTY ${_property} SET) if (_isSet) get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) if (NOT _propertyValue) list (APPEND _filteredObjects "${_object}") endif() endif() endforeach() set (${_filteredObjectsVar} ${_filteredObjects} PARENT_SCOPE) endfunction() function (cotire_get_source_file_property_values _valuesVar _property) set (_values "") foreach (_sourceFile ${ARGN}) get_source_file_property(_propertyValue "${_sourceFile}" ${_property}) if (_propertyValue) list (APPEND _values "${_propertyValue}") endif() endforeach() set (${_valuesVar} ${_values} PARENT_SCOPE) endfunction() function (cotrie_resolve_config_properites _configurations _propertiesVar) set (_properties "") foreach (_property ${ARGN}) if ("${_property}" MATCHES "") foreach (_config ${_configurations}) string (TOUPPER "${_config}" _upperConfig) string (REPLACE "" "${_upperConfig}" _configProperty "${_property}") list (APPEND _properties ${_configProperty}) endforeach() else() list (APPEND _properties ${_property}) endif() endforeach() set (${_propertiesVar} ${_properties} PARENT_SCOPE) endfunction() function (cotrie_copy_set_properites _configurations _type _source _target) cotrie_resolve_config_properites("${_configurations}" _properties ${ARGN}) foreach (_property ${_properties}) get_property(_isSet ${_type} ${_source} PROPERTY ${_property} SET) if (_isSet) get_property(_propertyValue ${_type} ${_source} PROPERTY ${_property}) set_property(${_type} ${_target} PROPERTY ${_property} "${_propertyValue}") endif() endforeach() endfunction() function (cotire_filter_compile_flags _language _flagFilter _matchedOptionsVar _unmatchedOptionsVar) if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") set (_flagPrefix "[/-]") else() set (_flagPrefix "--?") endif() set (_optionFlag "") set (_matchedOptions "") set (_unmatchedOptions "") foreach (_compileFlag ${ARGN}) if (_compileFlag) if (_optionFlag AND NOT "${_compileFlag}" MATCHES "^${_flagPrefix}") # option with separate argument list (APPEND _matchedOptions "${_compileFlag}") set (_optionFlag "") elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})$") # remember option set (_optionFlag "${CMAKE_MATCH_2}") elseif ("${_compileFlag}" MATCHES "^(${_flagPrefix})(${_flagFilter})(.+)$") # option with joined argument list (APPEND _matchedOptions "${CMAKE_MATCH_3}") set (_optionFlag "") else() # flush remembered option if (_optionFlag) list (APPEND _matchedOptions "${_optionFlag}") set (_optionFlag "") endif() # add to unfiltered options list (APPEND _unmatchedOptions "${_compileFlag}") endif() endif() endforeach() if (_optionFlag) list (APPEND _matchedOptions "${_optionFlag}") endif() if (COTIRE_DEBUG) message (STATUS "Filter ${_flagFilter}") if (_matchedOptions) message (STATUS "Matched ${_matchedOptions}") endif() if (_unmatchedOptions) message (STATUS "Unmatched ${_unmatchedOptions}") endif() endif() set (${_matchedOptionsVar} ${_matchedOptions} PARENT_SCOPE) set (${_unmatchedOptionsVar} ${_unmatchedOptions} PARENT_SCOPE) endfunction() function (cotire_get_target_compile_flags _config _language _directory _target _flagsVar) string (TOUPPER "${_config}" _upperConfig) # collect options from CMake language variables set (_compileFlags "") if (CMAKE_${_language}_FLAGS) set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS}") endif() if (CMAKE_${_language}_FLAGS_${_upperConfig}) set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_FLAGS_${_upperConfig}}") endif() if (_target) # add option from CMake target type variable get_target_property(_targetType ${_target} TYPE) if (POLICY CMP0018) # handle POSITION_INDEPENDENT_CODE property introduced with CMake 2.8.9 if policy CMP0018 is turned on cmake_policy(GET CMP0018 _PIC_Policy) else() # default to old behavior set (_PIC_Policy "OLD") endif() if (COTIRE_DEBUG) message(STATUS "CMP0018=${_PIC_Policy}") endif() if (_PIC_Policy STREQUAL "NEW") # NEW behavior: honor the POSITION_INDEPENDENT_CODE target property get_target_property(_targetPIC ${_target} POSITION_INDEPENDENT_CODE) if (_targetPIC) if (_targetType STREQUAL "EXECUTABLE" AND CMAKE_${_language}_COMPILE_OPTIONS_PIE) set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIE}") elseif (CMAKE_${_language}_COMPILE_OPTIONS_PIC) set (_compileFlags "${_compileFlags} ${CMAKE_${_language}_COMPILE_OPTIONS_PIC}") endif() endif() else() # OLD behavior or policy not set: use the value of CMAKE_SHARED_LIBRARY__FLAGS if (_targetType STREQUAL "MODULE_LIBRARY") # flags variable for module library uses different name SHARED_MODULE # (e.g., CMAKE_SHARED_MODULE_C_FLAGS) set (_targetType SHARED_MODULE) endif() if (CMAKE_${_targetType}_${_language}_FLAGS) set (_compileFlags "${_compileFlags} ${CMAKE_${_targetType}_${_language}_FLAGS}") endif() endif() endif() if (_directory) # add_definitions may have been used to add flags to the compiler command get_directory_property(_dirDefinitions DIRECTORY "${_directory}" DEFINITIONS) if (_dirDefinitions) set (_compileFlags "${_compileFlags} ${_dirDefinitions}") endif() endif() if (_target) # add target compile options get_target_property(_targetflags ${_target} COMPILE_FLAGS) if (_targetflags) set (_compileFlags "${_compileFlags} ${_targetflags}") endif() endif() if (UNIX) separate_arguments(_compileFlags UNIX_COMMAND "${_compileFlags}") elseif(WIN32) separate_arguments(_compileFlags WINDOWS_COMMAND "${_compileFlags}") else() separate_arguments(_compileFlags) endif() # platform specific flags if (APPLE) get_target_property(_architectures ${_target} OSX_ARCHITECTURES_${_upperConfig}) if (NOT _architectures) get_target_property(_architectures ${_target} OSX_ARCHITECTURES) endif() foreach (_arch ${_architectures}) list (APPEND _compileFlags "-arch" "${_arch}") endforeach() if (CMAKE_OSX_SYSROOT AND CMAKE_OSX_SYSROOT_DEFAULT AND CMAKE_${_language}_HAS_ISYSROOT) if (NOT "${CMAKE_OSX_SYSROOT}" STREQUAL "${CMAKE_OSX_SYSROOT_DEFAULT}") list (APPEND _compileFlags "-isysroot" "${CMAKE_OSX_SYSROOT}") endif() endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG) list (APPEND _compileFlags "${CMAKE_${_language}_OSX_DEPLOYMENT_TARGET_FLAG}${CMAKE_OSX_DEPLOYMENT_TARGET}") endif() endif() if (COTIRE_DEBUG AND _compileFlags) message (STATUS "Target ${_target} compile flags ${_compileFlags}") endif() set (${_flagsVar} ${_compileFlags} PARENT_SCOPE) endfunction() function (cotire_get_target_include_directories _config _language _targetSourceDir _targetBinaryDir _target _includeDirsVar) set (_includeDirs "") # default include dirs if (CMAKE_INCLUDE_CURRENT_DIR) list (APPEND _includeDirs "${_targetBinaryDir}") list (APPEND _includeDirs "${_targetSourceDir}") endif() # parse additional include directories from target compile flags set (_targetFlags "") cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) cotire_filter_compile_flags("${_language}" "I" _dirs _ignore ${_targetFlags}) if (_dirs) list (APPEND _includeDirs ${_dirs}) endif() # target include directories get_directory_property(_dirs DIRECTORY "${_targetSourceDir}" INCLUDE_DIRECTORIES) if (_target) get_target_property(_targetDirs ${_target} INCLUDE_DIRECTORIES) if (_targetDirs) list (APPEND _dirs ${_targetDirs}) list (REMOVE_DUPLICATES _dirs) endif() endif() list (LENGTH _includeDirs _projectInsertIndex) foreach (_dir ${_dirs}) if (CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE) cotire_check_is_path_relative_to("${_dir}" _isRelative "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}") if (_isRelative) list (LENGTH _includeDirs _len) if (_len EQUAL _projectInsertIndex) list (APPEND _includeDirs "${_dir}") else() list (INSERT _includeDirs _projectInsertIndex "${_dir}") endif() math (EXPR _projectInsertIndex "${_projectInsertIndex} + 1") else() list (APPEND _includeDirs "${_dir}") endif() else() list (APPEND _includeDirs "${_dir}") endif() endforeach() list (REMOVE_DUPLICATES _includeDirs) if (CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES) list (REMOVE_ITEM _includeDirs ${CMAKE_${_language}_IMPLICIT_INCLUDE_DIRECTORIES}) endif() if (COTIRE_DEBUG AND _includeDirs) message (STATUS "Target ${_target} include dirs ${_includeDirs}") endif() set (${_includeDirsVar} ${_includeDirs} PARENT_SCOPE) endfunction() macro (cotire_make_C_identifier _identifierVar _str) # mimic CMake SystemTools::MakeCindentifier behavior if ("${_str}" MATCHES "^[0-9].+$") set (_str "_${str}") endif() string (REGEX REPLACE "[^a-zA-Z0-9]" "_" ${_identifierVar} "${_str}") endmacro() function (cotire_get_target_export_symbol _target _exportSymbolVar) set (_exportSymbol "") get_target_property(_targetType ${_target} TYPE) get_target_property(_enableExports ${_target} ENABLE_EXPORTS) if (_targetType MATCHES "(SHARED|MODULE)_LIBRARY" OR (_targetType STREQUAL "EXECUTABLE" AND _enableExports)) get_target_property(_exportSymbol ${_target} DEFINE_SYMBOL) if (NOT _exportSymbol) set (_exportSymbol "${_target}_EXPORTS") endif() cotire_make_C_identifier(_exportSymbol "${_exportSymbol}") endif() set (${_exportSymbolVar} ${_exportSymbol} PARENT_SCOPE) endfunction() function (cotire_get_target_compile_definitions _config _language _directory _target _definitionsVar) string (TOUPPER "${_config}" _upperConfig) set (_configDefinitions "") # CMAKE_INTDIR for multi-configuration build systems if (NOT "${CMAKE_CFG_INTDIR}" STREQUAL ".") list (APPEND _configDefinitions "CMAKE_INTDIR=\"${_config}\"") endif() # target export define symbol cotire_get_target_export_symbol("${_target}" _defineSymbol) if (_defineSymbol) list (APPEND _configDefinitions "${_defineSymbol}") endif() # directory compile definitions get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS) if (_definitions) list (APPEND _configDefinitions ${_definitions}) endif() get_directory_property(_definitions DIRECTORY "${_directory}" COMPILE_DEFINITIONS_${_upperConfig}) if (_definitions) list (APPEND _configDefinitions ${_definitions}) endif() # target compile definitions get_target_property(_definitions ${_target} COMPILE_DEFINITIONS) if (_definitions) list (APPEND _configDefinitions ${_definitions}) endif() get_target_property(_definitions ${_target} COMPILE_DEFINITIONS_${_upperConfig}) if (_definitions) list (APPEND _configDefinitions ${_definitions}) endif() # parse additional compile definitions from target compile flags # and don't look at directory compile definitions, which we already handled set (_targetFlags "") cotire_get_target_compile_flags("${_config}" "${_language}" "" "${_target}" _targetFlags) cotire_filter_compile_flags("${_language}" "D" _definitions _ignore ${_targetFlags}) if (_definitions) list (APPEND _configDefinitions ${_definitions}) endif() list (REMOVE_DUPLICATES _configDefinitions) if (COTIRE_DEBUG AND _configDefinitions) message (STATUS "Target ${_target} compile definitions ${_configDefinitions}") endif() set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) endfunction() function (cotire_get_target_compiler_flags _config _language _directory _target _compilerFlagsVar) # parse target compile flags omitting compile definitions and include directives set (_targetFlags "") cotire_get_target_compile_flags("${_config}" "${_language}" "${_directory}" "${_target}" _targetFlags) set (_compilerFlags "") cotire_filter_compile_flags("${_language}" "[ID]" _ignore _compilerFlags ${_targetFlags}) if (COTIRE_DEBUG AND _compilerFlags) message (STATUS "Target ${_target} compiler flags ${_compilerFlags}") endif() set (${_compilerFlagsVar} ${_compilerFlags} PARENT_SCOPE) endfunction() function (cotire_add_sys_root_paths _pathsVar) if (APPLE) if (CMAKE_OSX_SYSROOT AND CMAKE_${_language}_HAS_ISYSROOT) foreach (_path IN LISTS ${_pathsVar}) if (IS_ABSOLUTE "${_path}") get_filename_component(_path "${CMAKE_OSX_SYSROOT}/${_path}" ABSOLUTE) if (EXISTS "${_path}") list (APPEND ${_pathsVar} "${_path}") endif() endif() endforeach() endif() endif() set (${_pathsVar} ${${_pathsVar}} PARENT_SCOPE) if (COTIRE_DEBUG) message (STATUS "${_pathsVar}=${${_pathsVar}}") endif() endfunction() function (cotire_get_source_extra_properties _sourceFile _pattern _resultVar) set (_extraProperties ${ARGN}) set (_result "") if (_extraProperties) list (FIND _extraProperties "${_sourceFile}" _index) if (_index GREATER -1) math (EXPR _index "${_index} + 1") list (LENGTH _extraProperties _len) math (EXPR _len "${_len} - 1") foreach (_index RANGE ${_index} ${_len}) list (GET _extraProperties ${_index} _value) if ("${_value}" MATCHES "${_pattern}") list (APPEND _result "${_value}") else() break() endif() endforeach() endif() endif() set (${_resultVar} ${_result} PARENT_SCOPE) endfunction() function (cotire_get_source_compile_definitions _config _language _sourceFile _definitionsVar) set (_compileDefinitions "") if (NOT CMAKE_SCRIPT_MODE_FILE) string (TOUPPER "${_config}" _upperConfig) get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS) if (_definitions) list (APPEND _compileDefinitions ${_definitions}) endif() get_source_file_property(_definitions "${_sourceFile}" COMPILE_DEFINITIONS_${_upperConfig}) if (_definitions) list (APPEND _compileDefinitions ${_definitions}) endif() endif() cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+(=.*)?$" _definitions ${ARGN}) if (_definitions) list (APPEND _compileDefinitions ${_definitions}) endif() if (COTIRE_DEBUG AND _compileDefinitions) message (STATUS "Source ${_sourceFile} compile definitions ${_compileDefinitions}") endif() set (${_definitionsVar} ${_compileDefinitions} PARENT_SCOPE) endfunction() function (cotire_get_source_files_compile_definitions _config _language _definitionsVar) set (_configDefinitions "") foreach (_sourceFile ${ARGN}) cotire_get_source_compile_definitions("${_config}" "${_language}" "${_sourceFile}" _sourceDefinitions) if (_sourceDefinitions) list (APPEND _configDefinitions "${_sourceFile}" ${_sourceDefinitions} "-") endif() endforeach() set (${_definitionsVar} ${_configDefinitions} PARENT_SCOPE) endfunction() function (cotire_get_source_undefs _sourceFile _property _sourceUndefsVar) set (_sourceUndefs "") if (NOT CMAKE_SCRIPT_MODE_FILE) get_source_file_property(_undefs "${_sourceFile}" ${_property}) if (_undefs) list (APPEND _sourceUndefs ${_undefs}) endif() endif() cotire_get_source_extra_properties("${_sourceFile}" "^[a-zA-Z0-9_]+$" _undefs ${ARGN}) if (_undefs) list (APPEND _sourceUndefs ${_undefs}) endif() if (COTIRE_DEBUG AND _sourceUndefs) message (STATUS "Source ${_sourceFile} ${_property} undefs ${_sourceUndefs}") endif() set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) endfunction() function (cotire_get_source_files_undefs _property _sourceUndefsVar) set (_sourceUndefs "") foreach (_sourceFile ${ARGN}) cotire_get_source_undefs("${_sourceFile}" ${_property} _undefs) if (_undefs) list (APPEND _sourceUndefs "${_sourceFile}" ${_undefs} "-") endif() endforeach() set (${_sourceUndefsVar} ${_sourceUndefs} PARENT_SCOPE) endfunction() macro (cotire_set_cmd_to_prologue _cmdVar) set (${_cmdVar} "${CMAKE_COMMAND}") if (COTIRE_DEBUG) list (APPEND ${_cmdVar} "--warn-uninitialized") endif() list (APPEND ${_cmdVar} "-DCOTIRE_BUILD_TYPE:STRING=$") if (COTIRE_VERBOSE) list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=ON") elseif("${CMAKE_GENERATOR}" MATCHES "Makefiles") list (APPEND ${_cmdVar} "-DCOTIRE_VERBOSE:BOOL=$(VERBOSE)") endif() endmacro() function (cotire_init_compile_cmd _cmdVar _language _compilerExe _compilerArg1) if (NOT _compilerExe) set (_compilerExe "${CMAKE_${_language}_COMPILER}") endif() if (NOT _compilerArg1) set (_compilerArg1 ${CMAKE_${_language}_COMPILER_ARG1}) endif() string (STRIP "${_compilerArg1}" _compilerArg1) set (${_cmdVar} "${_compilerExe}" ${_compilerArg1} PARENT_SCOPE) endfunction() macro (cotire_add_definitions_to_cmd _cmdVar _language) foreach (_definition ${ARGN}) if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") list (APPEND ${_cmdVar} "/D${_definition}") else() list (APPEND ${_cmdVar} "-D${_definition}") endif() endforeach() endmacro() macro (cotire_add_includes_to_cmd _cmdVar _language) foreach (_include ${ARGN}) if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") file (TO_NATIVE_PATH "${_include}" _include) list (APPEND ${_cmdVar} "/I${_include}") else() list (APPEND ${_cmdVar} "-I${_include}") endif() endforeach() endmacro() macro (cotire_add_compile_flags_to_cmd _cmdVar) foreach (_flag ${ARGN}) list (APPEND ${_cmdVar} "${_flag}") endforeach() endmacro() function (cotire_check_file_up_to_date _fileIsUpToDateVar _file) set (${_fileIsUpToDateVar} FALSE PARENT_SCOPE) set (_triggerFile "") foreach (_dependencyFile ${ARGN}) if (EXISTS "${_dependencyFile}" AND "${_dependencyFile}" IS_NEWER_THAN "${_file}") set (_triggerFile "${_dependencyFile}") break() endif() endforeach() get_filename_component(_fileName "${_file}" NAME) if (EXISTS "${_file}") if (_triggerFile) if (COTIRE_VERBOSE) message (STATUS "${_fileName} update triggered by ${_triggerFile} change.") endif() else() if (COTIRE_VERBOSE) message (STATUS "${_fileName} is up-to-date.") endif() set (${_fileIsUpToDateVar} TRUE PARENT_SCOPE) endif() else() if (COTIRE_VERBOSE) message (STATUS "${_fileName} does not exist yet.") endif() endif() endfunction() macro (cotire_find_closest_relative_path _headerFile _includeDirs _relPathVar) set (${_relPathVar} "") foreach (_includeDir ${_includeDirs}) if (IS_DIRECTORY "${_includeDir}") file (RELATIVE_PATH _relPath "${_includeDir}" "${_headerFile}") if (NOT IS_ABSOLUTE "${_relPath}" AND NOT "${_relPath}" MATCHES "^\\.\\.") string (LENGTH "${${_relPathVar}}" _closestLen) string (LENGTH "${_relPath}" _relLen) if (_closestLen EQUAL 0 OR _relLen LESS _closestLen) set (${_relPathVar} "${_relPath}") endif() endif() elseif ("${_includeDir}" STREQUAL "${_headerFile}") # if path matches exactly, return short non-empty string set (${_relPathVar} "1") break() endif() endforeach() endmacro() macro (cotire_check_header_file_location _headerFile _insideIncudeDirs _outsideIncudeDirs _headerIsInside) # check header path against ignored and honored include directories cotire_find_closest_relative_path("${_headerFile}" "${_insideIncudeDirs}" _insideRelPath) if (_insideRelPath) # header is inside, but could be become outside if there is a shorter outside match cotire_find_closest_relative_path("${_headerFile}" "${_outsideIncudeDirs}" _outsideRelPath) if (_outsideRelPath) string (LENGTH "${_insideRelPath}" _insideRelPathLen) string (LENGTH "${_outsideRelPath}" _outsideRelPathLen) if (_outsideRelPathLen LESS _insideRelPathLen) set (${_headerIsInside} FALSE) else() set (${_headerIsInside} TRUE) endif() else() set (${_headerIsInside} TRUE) endif() else() # header is outside set (${_headerIsInside} FALSE) endif() endmacro() macro (cotire_check_ignore_header_file_path _headerFile _headerIsIgnoredVar) if (NOT EXISTS "${_headerFile}") set (${_headerIsIgnoredVar} TRUE) elseif (IS_DIRECTORY "${_headerFile}") set (${_headerIsIgnoredVar} TRUE) elseif ("${_headerFile}" MATCHES "\\.\\.|[_-]fixed" AND "${_headerFile}" MATCHES "\\.h$") # heuristic: ignore C headers with embedded parent directory references or "-fixed" or "_fixed" in path # these often stem from using GCC #include_next tricks, which may break the precompiled header compilation # with the error message "error: no include path in which to search for header.h" set (${_headerIsIgnoredVar} TRUE) else() set (${_headerIsIgnoredVar} FALSE) endif() endmacro() macro (cotire_check_ignore_header_file_ext _headerFile _ignoreExtensionsVar _headerIsIgnoredVar) # check header file extension cotire_get_source_file_extension("${_headerFile}" _headerFileExt) set (${_headerIsIgnoredVar} FALSE) if (_headerFileExt) list (FIND ${_ignoreExtensionsVar} "${_headerFileExt}" _index) if (_index GREATER -1) set (${_headerIsIgnoredVar} TRUE) endif() endif() endmacro() macro (cotire_parse_line _line _headerFileVar _headerDepthVar) if (MSVC) # cl.exe /showIncludes output looks different depending on the language pack used, e.g.: # English: "Note: including file: C:\directory\file" # German: "Hinweis: Einlesen der Datei: C:\directory\file" # We use a very general regular expression, relying on the presence of the : characters if ("${_line}" MATCHES ":( +)([^:]+:[^:]+)$") # Visual Studio compiler output string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" ABSOLUTE) else() set (${_headerFileVar} "") set (${_headerDepthVar} 0) endif() else() if ("${_line}" MATCHES "^(\\.+) (.*)$") # GCC like output string (LENGTH "${CMAKE_MATCH_1}" ${_headerDepthVar}) if (IS_ABSOLUTE "${CMAKE_MATCH_2}") set (${_headerFileVar} "${CMAKE_MATCH_2}") else() get_filename_component(${_headerFileVar} "${CMAKE_MATCH_2}" REALPATH) endif() else() set (${_headerFileVar} "") set (${_headerDepthVar} 0) endif() endif() endmacro() function (cotire_parse_includes _language _scanOutput _ignoredIncudeDirs _honoredIncudeDirs _ignoredExtensions _selectedIncludesVar _unparsedLinesVar) if (WIN32) # prevent CMake macro invocation errors due to backslash characters in Windows paths string (REPLACE "\\" "/" _scanOutput "${_scanOutput}") endif() # canonize slashes string (REPLACE "//" "/" _scanOutput "${_scanOutput}") # prevent semicolon from being interpreted as a line separator string (REPLACE ";" "\\;" _scanOutput "${_scanOutput}") # then separate lines string (REGEX REPLACE "\n" ";" _scanOutput "${_scanOutput}") list (LENGTH _scanOutput _len) # remove duplicate lines to speed up parsing list (REMOVE_DUPLICATES _scanOutput) list (LENGTH _scanOutput _uniqueLen) if (COTIRE_VERBOSE) message (STATUS "Scanning ${_uniqueLen} unique lines of ${_len} for includes") if (_ignoredExtensions) message (STATUS "Ignored extensions: ${_ignoredExtensions}") endif() if (_ignoredIncudeDirs) message (STATUS "Ignored paths: ${_ignoredIncudeDirs}") endif() if (_honoredIncudeDirs) message (STATUS "Included paths: ${_honoredIncudeDirs}") endif() endif() set (_sourceFiles ${ARGN}) set (_selectedIncludes "") set (_unparsedLines "") # stack keeps track of inside/outside project status of processed header files set (_headerIsInsideStack "") foreach (_line IN LISTS _scanOutput) if (_line) cotire_parse_line("${_line}" _headerFile _headerDepth) if (_headerFile) cotire_check_header_file_location("${_headerFile}" "${_ignoredIncudeDirs}" "${_honoredIncudeDirs}" _headerIsInside) if (COTIRE_DEBUG) message (STATUS "${_headerDepth}: ${_headerFile} ${_headerIsInside}") endif() # update stack list (LENGTH _headerIsInsideStack _stackLen) if (_headerDepth GREATER _stackLen) math (EXPR _stackLen "${_stackLen} + 1") foreach (_index RANGE ${_stackLen} ${_headerDepth}) list (APPEND _headerIsInsideStack ${_headerIsInside}) endforeach() else() foreach (_index RANGE ${_headerDepth} ${_stackLen}) list (REMOVE_AT _headerIsInsideStack -1) endforeach() list (APPEND _headerIsInsideStack ${_headerIsInside}) endif() if (COTIRE_DEBUG) message (STATUS "${_headerIsInsideStack}") endif() # header is a candidate if it is outside project if (NOT _headerIsInside) # get parent header file's inside/outside status if (_headerDepth GREATER 1) math (EXPR _index "${_headerDepth} - 2") list (GET _headerIsInsideStack ${_index} _parentHeaderIsInside) else() set (_parentHeaderIsInside TRUE) endif() # select header file if parent header file is inside project # (e.g., a project header file that includes a standard header file) if (_parentHeaderIsInside) cotire_check_ignore_header_file_path("${_headerFile}" _headerIsIgnored) if (NOT _headerIsIgnored) cotire_check_ignore_header_file_ext("${_headerFile}" _ignoredExtensions _headerIsIgnored) if (NOT _headerIsIgnored) list (APPEND _selectedIncludes "${_headerFile}") else() # fix header's inside status on stack, it is ignored by extension now list (REMOVE_AT _headerIsInsideStack -1) list (APPEND _headerIsInsideStack TRUE) endif() endif() if (COTIRE_DEBUG) message (STATUS "${_headerFile} ${_ignoredExtensions} ${_headerIsIgnored}") endif() endif() endif() else() if (MSVC) # for cl.exe do not keep unparsed lines which solely consist of a source file name string (FIND "${_sourceFiles}" "${_line}" _index) if (_index LESS 0) list (APPEND _unparsedLines "${_line}") endif() else() list (APPEND _unparsedLines "${_line}") endif() endif() endif() endforeach() list (REMOVE_DUPLICATES _selectedIncludes) set (${_selectedIncludesVar} ${_selectedIncludes} PARENT_SCOPE) set (${_unparsedLinesVar} ${_unparsedLines} PARENT_SCOPE) endfunction() function (cotire_scan_includes _includesVar) set(_options "") set(_oneValueArgs COMPILER_ID COMPILER_EXECUTABLE COMPILER_VERSION LANGUAGE UNPARSED_LINES) set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) if (NOT _option_LANGUAGE) set (_option_LANGUAGE "CXX") endif() if (NOT _option_COMPILER_ID) set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") endif() set (_cmd "${_option_COMPILER_EXECUTABLE}" ${_option_COMPILER_ARG1}) cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES}) cotire_add_makedep_flags("${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" _cmd) # only consider existing source files for scanning set (_existingSourceFiles "") foreach (_sourceFile ${_sourceFiles}) if (EXISTS "${_sourceFile}") list (APPEND _existingSourceFiles "${_sourceFile}") endif() endforeach() if (NOT _existingSourceFiles) set (${_includesVar} "" PARENT_SCOPE) return() endif() list (APPEND _cmd ${_existingSourceFiles}) if (COTIRE_VERBOSE) message (STATUS "execute_process: ${_cmd}") endif() if (_option_COMPILER_ID MATCHES "MSVC") if (COTIRE_DEBUG) message (STATUS "clearing VS_UNICODE_OUTPUT") endif() # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared unset (ENV{VS_UNICODE_OUTPUT}) endif() execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result OUTPUT_QUIET ERROR_VARIABLE _output) if (_result) message (STATUS "Result ${_result} scanning includes of ${_existingSourceFiles}.") endif() cotire_parse_includes( "${_option_LANGUAGE}" "${_output}" "${_option_IGNORE_PATH}" "${_option_INCLUDE_PATH}" "${_option_IGNORE_EXTENSIONS}" _includes _unparsedLines ${_sourceFiles}) set (${_includesVar} ${_includes} PARENT_SCOPE) if (_option_UNPARSED_LINES) set (${_option_UNPARSED_LINES} ${_unparsedLines} PARENT_SCOPE) endif() endfunction() macro (cotire_append_undefs _contentsVar) set (_undefs ${ARGN}) if (_undefs) list (REMOVE_DUPLICATES _undefs) foreach (_definition ${_undefs}) list (APPEND ${_contentsVar} "#undef ${_definition}") endforeach() endif() endmacro() macro (cotire_comment_str _language _commentText _commentVar) if ("${_language}" STREQUAL "CMAKE") set (${_commentVar} "# ${_commentText}") else() set (${_commentVar} "/* ${_commentText} */") endif() endmacro() function (cotire_write_file _language _file _contents _force) get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) cotire_comment_str("${_language}" "${_moduleName} ${COTIRE_CMAKE_MODULE_VERSION} generated file" _header1) cotire_comment_str("${_language}" "${_file}" _header2) set (_contents "${_header1}\n${_header2}\n${_contents}") if (COTIRE_DEBUG) message (STATUS "${_contents}") endif() if (_force OR NOT EXISTS "${_file}") file (WRITE "${_file}" "${_contents}") else() file (READ "${_file}" _oldContents) if (NOT "${_oldContents}" STREQUAL "${_contents}") file (WRITE "${_file}" "${_contents}") else() if (COTIRE_DEBUG) message (STATUS "${_file} unchanged") endif() endif() endif() endfunction() function (cotire_generate_unity_source _unityFile) set(_options "") set(_oneValueArgs LANGUAGE) set(_multiValueArgs DEPENDS SOURCES_COMPILE_DEFINITIONS PRE_UNDEFS SOURCES_PRE_UNDEFS POST_UNDEFS SOURCES_POST_UNDEFS PROLOGUE EPILOGUE) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) if (_option_DEPENDS) cotire_check_file_up_to_date(_unityFileIsUpToDate "${_unityFile}" ${_option_DEPENDS}) if (_unityFileIsUpToDate) return() endif() endif() set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) if (NOT _option_PRE_UNDEFS) set (_option_PRE_UNDEFS "") endif() if (NOT _option_SOURCES_PRE_UNDEFS) set (_option_SOURCES_PRE_UNDEFS "") endif() if (NOT _option_POST_UNDEFS) set (_option_POST_UNDEFS "") endif() if (NOT _option_SOURCES_POST_UNDEFS) set (_option_SOURCES_POST_UNDEFS "") endif() set (_contents "") if (_option_PROLOGUE) list (APPEND _contents ${_option_PROLOGUE}) endif() if (_option_LANGUAGE AND _sourceFiles) if ("${_option_LANGUAGE}" STREQUAL "CXX") list (APPEND _contents "#ifdef __cplusplus") elseif ("${_option_LANGUAGE}" STREQUAL "C") list (APPEND _contents "#ifndef __cplusplus") endif() endif() set (_compileUndefinitions "") foreach (_sourceFile ${_sourceFiles}) cotire_get_source_compile_definitions( "${_option_CONFIGURATION}" "${_option_LANGUAGE}" "${_sourceFile}" _compileDefinitions ${_option_SOURCES_COMPILE_DEFINITIONS}) cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_PRE_UNDEFS _sourcePreUndefs ${_option_SOURCES_PRE_UNDEFS}) cotire_get_source_undefs("${_sourceFile}" COTIRE_UNITY_SOURCE_POST_UNDEFS _sourcePostUndefs ${_option_SOURCES_POST_UNDEFS}) if (_option_PRE_UNDEFS) list (APPEND _compileUndefinitions ${_option_PRE_UNDEFS}) endif() if (_sourcePreUndefs) list (APPEND _compileUndefinitions ${_sourcePreUndefs}) endif() if (_compileUndefinitions) cotire_append_undefs(_contents ${_compileUndefinitions}) set (_compileUndefinitions "") endif() if (_sourcePostUndefs) list (APPEND _compileUndefinitions ${_sourcePostUndefs}) endif() if (_option_POST_UNDEFS) list (APPEND _compileUndefinitions ${_option_POST_UNDEFS}) endif() foreach (_definition ${_compileDefinitions}) if ("${_definition}" MATCHES "^([a-zA-Z0-9_]+)=(.+)$") list (APPEND _contents "#define ${CMAKE_MATCH_1} ${CMAKE_MATCH_2}") list (INSERT _compileUndefinitions 0 "${CMAKE_MATCH_1}") else() list (APPEND _contents "#define ${_definition}") list (INSERT _compileUndefinitions 0 "${_definition}") endif() endforeach() get_filename_component(_sourceFile "${_sourceFile}" ABSOLUTE) if (WIN32) file (TO_NATIVE_PATH "${_sourceFile}" _sourceFile) endif() list (APPEND _contents "#include \"${_sourceFile}\"") endforeach() if (_compileUndefinitions) cotire_append_undefs(_contents ${_compileUndefinitions}) set (_compileUndefinitions "") endif() if (_option_LANGUAGE AND _sourceFiles) list (APPEND _contents "#endif") endif() if (_option_EPILOGUE) list (APPEND _contents ${_option_EPILOGUE}) endif() list (APPEND _contents "") string (REPLACE ";" "\n" _contents "${_contents}") if (COTIRE_VERBOSE) message ("${_contents}") endif() cotire_write_file("${_option_LANGUAGE}" "${_unityFile}" "${_contents}" TRUE) endfunction() function (cotire_generate_prefix_header _prefixFile) set(_options "") set(_oneValueArgs LANGUAGE COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION) set(_multiValueArgs DEPENDS COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES IGNORE_PATH INCLUDE_PATH IGNORE_EXTENSIONS) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) if (_option_DEPENDS) cotire_check_file_up_to_date(_prefixFileIsUpToDate "${_prefixFile}" ${_option_DEPENDS}) if (_prefixFileIsUpToDate) return() endif() endif() set (_epilogue "") if (_option_COMPILER_ID MATCHES "Intel") # Intel compiler requires hdrstop pragma to stop generating PCH file set (_epilogue "#pragma hdrstop") endif() set (_sourceFiles ${_option_UNPARSED_ARGUMENTS}) cotire_scan_includes(_selectedHeaders ${_sourceFiles} LANGUAGE "${_option_LANGUAGE}" COMPILER_EXECUTABLE "${_option_COMPILER_EXECUTABLE}" COMPILER_ID "${_option_COMPILER_ID}" COMPILER_VERSION "${_option_COMPILER_VERSION}" COMPILE_DEFINITIONS ${_option_COMPILE_DEFINITIONS} COMPILE_FLAGS ${_option_COMPILE_FLAGS} INCLUDE_DIRECTORIES ${_option_INCLUDE_DIRECTORIES} IGNORE_PATH ${_option_IGNORE_PATH} INCLUDE_PATH ${_option_INCLUDE_PATH} IGNORE_EXTENSIONS ${_option_IGNORE_EXTENSIONS} UNPARSED_LINES _unparsedLines) cotire_generate_unity_source("${_prefixFile}" EPILOGUE ${_epilogue} LANGUAGE "${_option_LANGUAGE}" ${_selectedHeaders}) set (_unparsedLinesFile "${_prefixFile}.log") if (_unparsedLines) if (COTIRE_VERBOSE OR NOT _selectedHeaders) list (LENGTH _unparsedLines _skippedLineCount) file (RELATIVE_PATH _unparsedLinesFileRelPath "${CMAKE_BINARY_DIR}" "${_unparsedLinesFile}") message (STATUS "${_skippedLineCount} line(s) skipped, see ${_unparsedLinesFileRelPath}") endif() string (REPLACE ";" "\n" _unparsedLines "${_unparsedLines}") endif() file (WRITE "${_unparsedLinesFile}" "${_unparsedLines}\n") endfunction() function (cotire_add_makedep_flags _language _compilerID _compilerVersion _flagsVar) set (_flags ${${_flagsVar}}) if (_compilerID MATCHES "MSVC") # cl.exe options used # /nologo suppresses display of sign-on banner # /TC treat all files named on the command line as C source files # /TP treat all files named on the command line as C++ source files # /EP preprocess to stdout without #line directives # /showIncludes list include files set (_sourceFileTypeC "/TC") set (_sourceFileTypeCXX "/TP") if (_flags) # append to list list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /showIncludes) else() # return as a flag string set (_flags "${_sourceFileType${_language}} /EP /showIncludes") endif() elseif (_compilerID MATCHES "GNU") # GCC options used # -H print the name of each header file used # -E invoke preprocessor # -fdirectives-only do not expand macros, requires GCC >= 4.3 if (_flags) # append to list list (APPEND _flags -H -E) if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") list (APPEND _flags "-fdirectives-only") endif() else() # return as a flag string set (_flags "-H -E") if (NOT "${_compilerVersion}" VERSION_LESS "4.3.0") set (_flags "${_flags} -fdirectives-only") endif() endif() elseif (_compilerID MATCHES "Clang") # Clang options used # -H print the name of each header file used # -E invoke preprocessor if (_flags) # append to list list (APPEND _flags -H -E) else() # return as a flag string set (_flags "-H -E") endif() elseif (_compilerID MATCHES "Intel") if (WIN32) # Windows Intel options used # /nologo do not display compiler version information # /QH display the include file order # /EP preprocess to stdout, omitting #line directives # /TC process all source or unrecognized file types as C source files # /TP process all source or unrecognized file types as C++ source files set (_sourceFileTypeC "/TC") set (_sourceFileTypeCXX "/TP") if (_flags) # append to list list (APPEND _flags /nologo "${_sourceFileType${_language}}" /EP /QH) else() # return as a flag string set (_flags "${_sourceFileType${_language}} /EP /QH") endif() else() # Linux / Mac OS X Intel options used # -H print the name of each header file used # -EP preprocess to stdout, omitting #line directives # -Kc++ process all source or unrecognized file types as C++ source files if (_flags) # append to list if ("${_language}" STREQUAL "CXX") list (APPEND _flags -Kc++) endif() list (APPEND _flags -H -EP) else() # return as a flag string if ("${_language}" STREQUAL "CXX") set (_flags "-Kc++ ") endif() set (_flags "${_flags}-H -EP") endif() endif() else() message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") endif() set (${_flagsVar} ${_flags} PARENT_SCOPE) endfunction() function (cotire_add_pch_compilation_flags _language _compilerID _compilerVersion _prefixFile _pchFile _hostFile _flagsVar) set (_flags ${${_flagsVar}}) if (_compilerID MATCHES "MSVC") file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) # cl.exe options used # /Yc creates a precompiled header file # /Fp specifies precompiled header binary file name # /FI forces inclusion of file # /TC treat all files named on the command line as C source files # /TP treat all files named on the command line as C++ source files # /Zs syntax check only set (_sourceFileTypeC "/TC") set (_sourceFileTypeCXX "/TP") if (_flags) # append to list list (APPEND _flags /nologo "${_sourceFileType${_language}}" "/Yc${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") else() # return as a flag string set (_flags "/Yc\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") endif() elseif (_compilerID MATCHES "GNU|Clang") # GCC / Clang options used # -x specify the source language # -c compile but do not link # -o place output in file set (_xLanguage_C "c-header") set (_xLanguage_CXX "c++-header") if (_flags) # append to list list (APPEND _flags "-x" "${_xLanguage_${_language}}" "-c" "${_prefixFile}" -o "${_pchFile}") else() # return as a flag string set (_flags "-x ${_xLanguage_${_language}} -c \"${_prefixFile}\" -o \"${_pchFile}\"") endif() elseif (_compilerID MATCHES "Intel") if (WIN32) file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) file (TO_NATIVE_PATH "${_hostFile}" _hostFileNative) # Windows Intel options used # /nologo do not display compiler version information # /Yc create a precompiled header (PCH) file # /Fp specify a path or file name for precompiled header files # /FI tells the preprocessor to include a specified file name as the header file # /TC process all source or unrecognized file types as C source files # /TP process all source or unrecognized file types as C++ source files # /Zs syntax check only # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) set (_sourceFileTypeC "/TC") set (_sourceFileTypeCXX "/TP") if (_flags) # append to list list (APPEND _flags /nologo "${_sourceFileType${_language}}" "/Yc" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}" /Zs "${_hostFileNative}") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") list (APPEND _flags "/Wpch-messages") endif() else() # return as a flag string set (_flags "/Yc /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") set (_flags "${_flags} /Wpch-messages") endif() endif() else() # Linux / Mac OS X Intel options used # -pch-dir location for precompiled header files # -pch-create name of the precompiled header (PCH) to create # -Kc++ process all source or unrecognized file types as C++ source files # -fsyntax-only check only for correct syntax # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) get_filename_component(_pchDir "${_pchFile}" PATH) get_filename_component(_pchName "${_pchFile}" NAME) set (_xLanguage_C "c-header") set (_xLanguage_CXX "c++-header") if (_flags) # append to list if ("${_language}" STREQUAL "CXX") list (APPEND _flags -Kc++) endif() list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-create" "${_pchName}" "-fsyntax-only" "${_hostFile}") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") list (APPEND _flags "-Wpch-messages") endif() else() # return as a flag string set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-create \"${_pchName}\"") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") set (_flags "${_flags} -Wpch-messages") endif() endif() endif() else() message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") endif() set (${_flagsVar} ${_flags} PARENT_SCOPE) endfunction() function (cotire_add_pch_inclusion_flags _language _compilerID _compilerVersion _prefixFile _pchFile _flagsVar) set (_flags ${${_flagsVar}}) if (_compilerID MATCHES "MSVC") file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) # cl.exe options used # /Yu uses a precompiled header file during build # /Fp specifies precompiled header binary file name # /FI forces inclusion of file if (_flags) # append to list list (APPEND _flags "/Yu${_prefixFileNative}" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") else() # return as a flag string set (_flags "/Yu\"${_prefixFileNative}\" /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") endif() elseif (_compilerID MATCHES "GNU") # GCC options used # -include process include file as the first line of the primary source file # -Winvalid-pch warns if precompiled header is found but cannot be used if (_flags) # append to list list (APPEND _flags "-include" "${_prefixFile}" "-Winvalid-pch") else() # return as a flag string set (_flags "-include \"${_prefixFile}\" -Winvalid-pch") endif() elseif (_compilerID MATCHES "Clang") # Clang options used # -include process include file as the first line of the primary source file # -Qunused-arguments don't emit warning for unused driver arguments if (_flags) # append to list list (APPEND _flags "-include" "${_prefixFile}" "-Qunused-arguments") else() # return as a flag string set (_flags "-include \"${_prefixFile}\" -Qunused-arguments") endif() elseif (_compilerID MATCHES "Intel") if (WIN32) file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) # Windows Intel options used # /Yu use a precompiled header (PCH) file # /Fp specify a path or file name for precompiled header files # /FI tells the preprocessor to include a specified file name as the header file # /Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) if (_flags) # append to list list (APPEND _flags "/Yu" "/Fp${_pchFileNative}" "/FI${_prefixFileNative}") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") list (APPEND _flags "/Wpch-messages") endif() else() # return as a flag string set (_flags "/Yu /Fp\"${_pchFileNative}\" /FI\"${_prefixFileNative}\"") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") set (_flags "${_flags} /Wpch-messages") endif() endif() else() # Linux / Mac OS X Intel options used # -pch-dir location for precompiled header files # -pch-use name of the precompiled header (PCH) to use # -include process include file as the first line of the primary source file # -Wpch-messages enable diagnostics related to pre-compiled headers (requires Intel XE 2013 Update 2) get_filename_component(_pchDir "${_pchFile}" PATH) get_filename_component(_pchName "${_pchFile}" NAME) if (_flags) # append to list list (APPEND _flags "-include" "${_prefixFile}" "-pch-dir" "${_pchDir}" "-pch-use" "${_pchName}") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") list (APPEND _flags "-Wpch-messages") endif() else() # return as a flag string set (_flags "-include \"${_prefixFile}\" -pch-dir \"${_pchDir}\" -pch-use \"${_pchName}\"") if (NOT "${_compilerVersion}" VERSION_LESS "13.1.0") set (_flags "${_flags} -Wpch-messages") endif() endif() endif() else() message (FATAL_ERROR "Unsupported ${_language} compiler ${_compilerID} version ${_compilerVersion}.") endif() set (${_flagsVar} ${_flags} PARENT_SCOPE) endfunction() function (cotire_precompile_prefix_header _prefixFile _pchFile _hostFile) set(_options "") set(_oneValueArgs COMPILER_EXECUTABLE COMPILER_ID COMPILER_VERSION LANGUAGE) set(_multiValueArgs COMPILE_DEFINITIONS COMPILE_FLAGS INCLUDE_DIRECTORIES) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) if (NOT _option_LANGUAGE) set (_option_LANGUAGE "CXX") endif() if (NOT _option_COMPILER_ID) set (_option_COMPILER_ID "${CMAKE_${_option_LANGUAGE}_ID}") endif() cotire_init_compile_cmd(_cmd "${_option_LANGUAGE}" "${_option_COMPILER_EXECUTABLE}" "${_option_COMPILER_ARG1}") cotire_add_definitions_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_COMPILE_DEFINITIONS}) cotire_add_compile_flags_to_cmd(_cmd ${_option_COMPILE_FLAGS}) cotire_add_includes_to_cmd(_cmd "${_option_LANGUAGE}" ${_option_INCLUDE_DIRECTORIES}) cotire_add_pch_compilation_flags( "${_option_LANGUAGE}" "${_option_COMPILER_ID}" "${_option_COMPILER_VERSION}" "${_prefixFile}" "${_pchFile}" "${_hostFile}" _cmd) if (COTIRE_VERBOSE) message (STATUS "execute_process: ${_cmd}") endif() if (_option_COMPILER_ID MATCHES "MSVC") if (COTIRE_DEBUG) message (STATUS "clearing VS_UNICODE_OUTPUT") endif() # cl.exe messes with the output streams unless the environment variable VS_UNICODE_OUTPUT is cleared unset (ENV{VS_UNICODE_OUTPUT}) endif() execute_process(COMMAND ${_cmd} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE _result) if (_result) message (FATAL_ERROR "Error ${_result} precompiling ${_prefixFile}.") endif() endfunction() function (cotire_check_precompiled_header_support _language _targetSourceDir _target _msgVar) set (_unsupportedCompiler "Precompiled headers not supported for ${_language} compiler ${CMAKE_${_language}_COMPILER_ID}") if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") # supported since Visual Studio C++ 6.0 # and CMake does not support an earlier version set (${_msgVar} "" PARENT_SCOPE) elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU") # GCC PCH support requires version >= 3.4 cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "3.4.0") set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) else() set (${_msgVar} "" PARENT_SCOPE) endif() elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Clang") # all Clang versions have PCH support set (${_msgVar} "" PARENT_SCOPE) elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") # Intel PCH support requires version >= 8.0.0 cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) if ("${COTIRE_${_language}_COMPILER_VERSION}" MATCHES ".+" AND "${COTIRE_${_language}_COMPILER_VERSION}" VERSION_LESS "8.0.0") set (${_msgVar} "${_unsupportedCompiler} version ${COTIRE_${_language}_COMPILER_VERSION}." PARENT_SCOPE) else() set (${_msgVar} "" PARENT_SCOPE) endif() else() set (${_msgVar} "${_unsupportedCompiler}." PARENT_SCOPE) endif() if (APPLE) # PCH compilation not supported by GCC / Clang for multi-architecture builds (e.g., i386, x86_64) if (CMAKE_CONFIGURATION_TYPES) set (_configs ${CMAKE_CONFIGURATION_TYPES}) elseif (CMAKE_BUILD_TYPE) set (_configs ${CMAKE_BUILD_TYPE}) else() set (_configs "None") endif() foreach (_config ${_configs}) set (_targetFlags "") cotire_get_target_compile_flags("${_config}" "${_language}" "${_targetSourceDir}" "${_target}" _targetFlags) cotire_filter_compile_flags("${_language}" "arch" _architectures _ignore ${_targetFlags}) list (LENGTH _architectures _numberOfArchitectures) if (_numberOfArchitectures GREATER 1) string (REPLACE ";" ", " _architectureStr "${_architectures}") set (${_msgVar} "Precompiled headers not supported on Darwin for multi-architecture builds (${_architectureStr})." PARENT_SCOPE) break() endif() endforeach() endif() endfunction() macro (cotire_get_intermediate_dir _cotireDir) get_filename_component(${_cotireDir} "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${COTIRE_INTDIR}" ABSOLUTE) endmacro() macro (cotire_setup_file_extension_variables) set (_unityFileExt_C ".c") set (_unityFileExt_CXX ".cxx") set (_prefixFileExt_C ".h") set (_prefixFileExt_CXX ".hxx") endmacro() function (cotire_make_single_unity_source_file_path _language _target _unityFileVar) cotire_setup_file_extension_variables() if (NOT DEFINED _unityFileExt_${_language}) set (${_unityFileVar} "" PARENT_SCOPE) return() endif() set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") set (_unityFileName "${_unityFileBaseName}${_unityFileExt_${_language}}") cotire_get_intermediate_dir(_baseDir) set (_unityFile "${_baseDir}/${_unityFileName}") set (${_unityFileVar} "${_unityFile}" PARENT_SCOPE) if (COTIRE_DEBUG) message(STATUS "${_unityFile}") endif() endfunction() function (cotire_make_unity_source_file_paths _language _target _maxIncludes _unityFilesVar) cotire_setup_file_extension_variables() if (NOT DEFINED _unityFileExt_${_language}) set (${_unityFileVar} "" PARENT_SCOPE) return() endif() set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") cotire_get_intermediate_dir(_baseDir) set (_startIndex 0) set (_index 0) set (_unityFiles "") set (_sourceFiles ${ARGN}) foreach (_sourceFile ${_sourceFiles}) get_source_file_property(_startNew "${_sourceFile}" COTIRE_START_NEW_UNITY_SOURCE) math (EXPR _unityFileCount "${_index} - ${_startIndex}") if (_startNew OR (_maxIncludes GREATER 0 AND NOT _unityFileCount LESS _maxIncludes)) if (_index GREATER 0) # start new unity file segment math (EXPR _endIndex "${_index} - 1") set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") endif() set (_startIndex ${_index}) endif() math (EXPR _index "${_index} + 1") endforeach() list (LENGTH _sourceFiles _numberOfSources) if (_startIndex EQUAL 0) # there is only a single unity file cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFiles) elseif (_startIndex LESS _numberOfSources) # end with final unity file segment math (EXPR _endIndex "${_index} - 1") set (_unityFileName "${_unityFileBaseName}_${_startIndex}_${_endIndex}${_unityFileExt_${_language}}") list (APPEND _unityFiles "${_baseDir}/${_unityFileName}") endif() set (${_unityFilesVar} ${_unityFiles} PARENT_SCOPE) if (COTIRE_DEBUG) message(STATUS "${_unityFiles}") endif() endfunction() function (cotire_unity_to_prefix_file_path _language _target _unityFile _prefixFileVar) cotire_setup_file_extension_variables() if (NOT DEFINED _unityFileExt_${_language}) set (${_prefixFileVar} "" PARENT_SCOPE) return() endif() set (_unityFileBaseName "${_target}_${_language}${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}") set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") string (REPLACE "${_unityFileBaseName}" "${_prefixFileBaseName}" _prefixFile "${_unityFile}") string (REGEX REPLACE "${_unityFileExt_${_language}}$" "${_prefixFileExt_${_language}}" _prefixFile "${_prefixFile}") set (${_prefixFileVar} "${_prefixFile}" PARENT_SCOPE) endfunction() function (cotire_make_prefix_file_name _language _target _prefixFileBaseNameVar _prefixFileNameVar) cotire_setup_file_extension_variables() if (NOT _language) set (_prefixFileBaseName "${_target}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_C}") elseif (DEFINED _prefixFileExt_${_language}) set (_prefixFileBaseName "${_target}_${_language}${COTIRE_PREFIX_HEADER_FILENAME_SUFFIX}") set (_prefixFileName "${_prefixFileBaseName}${_prefixFileExt_${_language}}") else() set (_prefixFileBaseName "") set (_prefixFileName "") endif() set (${_prefixFileBaseNameVar} "${_prefixFileBaseName}" PARENT_SCOPE) set (${_prefixFileNameVar} "${_prefixFileName}" PARENT_SCOPE) endfunction() function (cotire_make_prefix_file_path _language _target _prefixFileVar) cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) set (${_prefixFileVar} "" PARENT_SCOPE) if (_prefixFileName) if (NOT _language) set (_language "C") endif() if (MSVC OR CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang|Intel") cotire_get_intermediate_dir(_baseDir) set (${_prefixFileVar} "${_baseDir}/${_prefixFileName}" PARENT_SCOPE) endif() endif() endfunction() function (cotire_make_pch_file_path _language _targetSourceDir _target _pchFileVar) cotire_make_prefix_file_name("${_language}" "${_target}" _prefixFileBaseName _prefixFileName) set (${_pchFileVar} "" PARENT_SCOPE) if (_prefixFileBaseName AND _prefixFileName) cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _msg) if (NOT _msg) if (XCODE) # For Xcode, we completely hand off the compilation of the prefix header to the IDE return() endif() cotire_get_intermediate_dir(_baseDir) if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC") # MSVC uses the extension .pch added to the prefix header base name set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pch" PARENT_SCOPE) elseif (CMAKE_${_language}_COMPILER_ID MATCHES "GNU|Clang") # GCC / Clang look for a precompiled header corresponding to the prefix header with the extension .gch appended set (${_pchFileVar} "${_baseDir}/${_prefixFileName}.gch" PARENT_SCOPE) elseif (CMAKE_${_language}_COMPILER_ID MATCHES "Intel") # Intel uses the extension .pchi added to the prefix header base name set (${_pchFileVar} "${_baseDir}/${_prefixFileBaseName}.pchi" PARENT_SCOPE) endif() endif() endif() endfunction() function (cotire_select_unity_source_files _unityFile _sourcesVar) set (_sourceFiles ${ARGN}) if (_sourceFiles AND "${_unityFile}" MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}_([0-9]+)_([0-9]+)") set (_startIndex ${CMAKE_MATCH_1}) set (_endIndex ${CMAKE_MATCH_2}) list (LENGTH _sourceFiles _numberOfSources) if (NOT _startIndex LESS _numberOfSources) math (EXPR _startIndex "${_numberOfSources} - 1") endif() if (NOT _endIndex LESS _numberOfSources) math (EXPR _endIndex "${_numberOfSources} - 1") endif() set (_files "") foreach (_index RANGE ${_startIndex} ${_endIndex}) list (GET _sourceFiles ${_index} _file) list (APPEND _files "${_file}") endforeach() else() set (_files ${_sourceFiles}) endif() set (${_sourcesVar} ${_files} PARENT_SCOPE) endfunction() function (cotire_get_unity_source_dependencies _language _target _dependencySourcesVar) set (_dependencySources "") # depend on target's generated source files cotire_get_objects_with_property_on(_generatedSources GENERATED SOURCE ${ARGN}) if (_generatedSources) # but omit all generated source files that have the COTIRE_EXCLUDED property set to true cotire_get_objects_with_property_on(_excludedGeneratedSources COTIRE_EXCLUDED SOURCE ${_generatedSources}) if (_excludedGeneratedSources) list (REMOVE_ITEM _generatedSources ${_excludedGeneratedSources}) endif() # and omit all generated source files that have the COTIRE_DEPENDENCY property set to false explicitly cotire_get_objects_with_property_off(_excludedNonDependencySources COTIRE_DEPENDENCY SOURCE ${_generatedSources}) if (_excludedNonDependencySources) list (REMOVE_ITEM _generatedSources ${_excludedNonDependencySources}) endif() if (_generatedSources) list (APPEND _dependencySources ${_generatedSources}) endif() endif() if (COTIRE_DEBUG AND _dependencySources) message (STATUS "${_language} ${_target} unity source depends on ${_dependencySources}") endif() set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) endfunction() function (cotire_get_prefix_header_dependencies _language _target _dependencySourcesVar) # depend on target source files marked with custom COTIRE_DEPENDENCY property set (_dependencySources "") cotire_get_objects_with_property_on(_dependencySources COTIRE_DEPENDENCY SOURCE ${ARGN}) if (COTIRE_DEBUG AND _dependencySources) message (STATUS "${_language} ${_target} prefix header DEPENDS ${_dependencySources}") endif() set (${_dependencySourcesVar} ${_dependencySources} PARENT_SCOPE) endfunction() function (cotire_generate_target_script _language _configurations _targetSourceDir _targetBinaryDir _target _targetScriptVar) set (COTIRE_TARGET_SOURCES ${ARGN}) get_filename_component(_moduleName "${COTIRE_CMAKE_MODULE_FILE}" NAME) set (_targetCotireScript "${CMAKE_CURRENT_BINARY_DIR}/${_target}_${_language}_${_moduleName}") cotire_get_prefix_header_dependencies(${_language} ${_target} COTIRE_TARGET_PREFIX_DEPENDS ${COTIRE_TARGET_SOURCES}) cotire_get_unity_source_dependencies(${_language} ${_target} COTIRE_TARGET_UNITY_DEPENDS ${COTIRE_TARGET_SOURCES}) # set up variables to be configured set (COTIRE_TARGET_LANGUAGE "${_language}") cotire_determine_compiler_version("${COTIRE_TARGET_LANGUAGE}" COTIRE_${_language}_COMPILER) get_target_property(COTIRE_TARGET_IGNORE_PATH ${_target} COTIRE_PREFIX_HEADER_IGNORE_PATH) cotire_add_sys_root_paths(COTIRE_TARGET_IGNORE_PATH) get_target_property(COTIRE_TARGET_INCLUDE_PATH ${_target} COTIRE_PREFIX_HEADER_INCLUDE_PATH) cotire_add_sys_root_paths(COTIRE_TARGET_INCLUDE_PATH) get_target_property(COTIRE_TARGET_PRE_UNDEFS ${_target} COTIRE_UNITY_SOURCE_PRE_UNDEFS) get_target_property(COTIRE_TARGET_POST_UNDEFS ${_target} COTIRE_UNITY_SOURCE_POST_UNDEFS) get_target_property(COTIRE_TARGET_MAXIMUM_NUMBER_OF_INCLUDES ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_PRE_UNDEFS COTIRE_TARGET_SOURCES_PRE_UNDEFS ${COTIRE_TARGET_SOURCES}) cotire_get_source_files_undefs(COTIRE_UNITY_SOURCE_POST_UNDEFS COTIRE_TARGET_SOURCES_POST_UNDEFS ${COTIRE_TARGET_SOURCES}) set (COTIRE_TARGET_CONFIGURATION_TYPES "${_configurations}") foreach (_config ${_configurations}) string (TOUPPER "${_config}" _upperConfig) cotire_get_target_include_directories( "${_config}" "${_language}" "${_targetSourceDir}" "${_targetBinaryDir}" "${_target}" COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}) cotire_get_target_compile_definitions( "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}) cotire_get_target_compiler_flags( "${_config}" "${_language}" "${_targetSourceDir}" "${_target}" COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}) cotire_get_source_files_compile_definitions( "${_config}" "${_language}" COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig} ${COTIRE_TARGET_SOURCES}) endforeach() get_cmake_property(_vars VARIABLES) string (REGEX MATCHALL "COTIRE_[A-Za-z0-9_]+" _matchVars "${_vars}") # remove COTIRE_VERBOSE which is passed as a CMake define on command line list (REMOVE_ITEM _matchVars COTIRE_VERBOSE) set (_contents "") foreach (_var IN LISTS _matchVars ITEMS MSVC CMAKE_GENERATOR CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES CMAKE_${_language}_COMPILER_ID CMAKE_${_language}_COMPILER CMAKE_${_language}_COMPILER_ARG1 CMAKE_${_language}_SOURCE_FILE_EXTENSIONS) if (DEFINED ${_var}) string (REPLACE "\"" "\\\"" _value "${${_var}}") set (_contents "${_contents}set (${_var} \"${_value}\")\n") endif() endforeach() cotire_write_file("CMAKE" "${_targetCotireScript}" "${_contents}" FALSE) set (${_targetScriptVar} "${_targetCotireScript}" PARENT_SCOPE) endfunction() function (cotire_setup_pch_file_compilation _language _targetBinaryDir _targetScript _prefixFile _pchFile) set (_sourceFiles ${ARGN}) if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") # for Visual Studio and Intel, we attach the precompiled header compilation to the first source file # the remaining files include the precompiled header, see cotire_setup_prefix_file_inclusion if (_sourceFiles) file (TO_NATIVE_PATH "${_prefixFile}" _prefixFileNative) file (TO_NATIVE_PATH "${_pchFile}" _pchFileNative) list (GET _sourceFiles 0 _hostFile) set (_flags "") cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) cotire_add_pch_compilation_flags( "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" "${_prefixFile}" "${_pchFile}" "${_hostFile}" _flags) set_property (SOURCE ${_hostFile} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_OUTPUTS "${_pchFile}") # make first source file depend on prefix header set_property (SOURCE ${_hostFile} APPEND PROPERTY OBJECT_DEPENDS "${_prefixFile}") endif() elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") # for makefile based generator, we add a custom command to precompile the prefix header if (_targetScript) cotire_set_cmd_to_prologue(_cmds) list (GET _sourceFiles 0 _hostFile) list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "precompile" "${_targetScript}" "${_prefixFile}" "${_pchFile}" "${_hostFile}") file (RELATIVE_PATH _pchFileRelPath "${CMAKE_BINARY_DIR}" "${_pchFile}") if (COTIRE_DEBUG) message (STATUS "add_custom_command: OUTPUT ${_pchFile} ${_cmds} DEPENDS ${_prefixFile} IMPLICIT_DEPENDS ${_language} ${_prefixFile}") endif() set_property (SOURCE "${_pchFile}" PROPERTY GENERATED TRUE) add_custom_command(OUTPUT "${_pchFile}" COMMAND ${_cmds} DEPENDS "${_prefixFile}" IMPLICIT_DEPENDS ${_language} "${_prefixFile}" WORKING_DIRECTORY "${_targetSourceDir}" COMMENT "Building ${_language} precompiled header ${_pchFileRelPath}" VERBATIM) endif() endif() endfunction() function (cotire_setup_prefix_file_inclusion _language _target _wholeTarget _prefixFile _pchFile) set (_sourceFiles ${ARGN}) if (CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") # for Visual Studio and Intel, we include the precompiled header in all but the first source file # the first source file does the precompiled header compilation, see cotire_setup_pch_file_compilation list (LENGTH _sourceFiles _numberOfSourceFiles) if (_numberOfSourceFiles GREATER 1) # mark sources as cotired to prevent them from being used in another cotired target set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") list (REMOVE_AT _sourceFiles 0) set (_flags "") cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) cotire_add_pch_inclusion_flags( "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" "${_prefixFile}" "${_pchFile}" _flags) set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") # make source files depend on precompiled header set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") endif() elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") if (NOT _wholeTarget) # for makefile based generator, we force the inclusion of the prefix header for a subset # of the source files, if this is a multi-language target or has excluded files set (_flags "") cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) cotire_add_pch_inclusion_flags( "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" "${_prefixFile}" "${_pchFile}" _flags) set_property (SOURCE ${_sourceFiles} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") # mark sources as cotired to prevent them from being used in another cotired target set_source_files_properties(${_sourceFiles} PROPERTIES COTIRE_TARGET "${_target}") endif() # make source files depend on precompiled header set_property (SOURCE ${_sourceFiles} APPEND PROPERTY OBJECT_DEPENDS "${_pchFile}") endif() endfunction() function (cotire_get_first_set_property_value _propertyValueVar _type _object) set (_properties ${ARGN}) foreach (_property ${_properties}) get_property(_propertyValue ${_type} "${_object}" PROPERTY ${_property}) if (_propertyValue) set (${_propertyValueVar} ${_propertyValue} PARENT_SCOPE) return() endif() endforeach() set (${_propertyValueVar} "" PARENT_SCOPE) endfunction() function (cotire_setup_combine_command _language _sourceDir _targetScript _joinedFile _cmdsVar) set (_files ${ARGN}) set (_filesPaths "") foreach (_file ${_files}) if (IS_ABSOLUTE "${_file}") set (_filePath "${_file}") else() get_filename_component(_filePath "${_sourceDir}/${_file}" ABSOLUTE) endif() file (RELATIVE_PATH _fileRelPath "${_sourceDir}" "${_filePath}") if (NOT IS_ABSOLUTE "${_fileRelPath}" AND NOT "${_fileRelPath}" MATCHES "^\\.\\.") list (APPEND _filesPaths "${_fileRelPath}") else() list (APPEND _filesPaths "${_filePath}") endif() endforeach() cotire_set_cmd_to_prologue(_prefixCmd) list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "combine") if (_targetScript) list (APPEND _prefixCmd "${_targetScript}") endif() list (APPEND _prefixCmd "${_joinedFile}" ${_filesPaths}) if (COTIRE_DEBUG) message (STATUS "add_custom_command: OUTPUT ${_joinedFile} COMMAND ${_prefixCmd} DEPENDS ${_files}") endif() set_property (SOURCE "${_joinedFile}" PROPERTY GENERATED TRUE) file (RELATIVE_PATH _joinedFileRelPath "${CMAKE_BINARY_DIR}" "${_joinedFile}") get_filename_component(_joinedFileName "${_joinedFileRelPath}" NAME_WE) if (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") set (_comment "Generating ${_language} unity source ${_joinedFileRelPath}") elseif (_language AND _joinedFileName MATCHES "${COTIRE_UNITY_SOURCE_FILENAME_SUFFIX}$") set (_comment "Generating ${_language} prefix header ${_joinedFileRelPath}") else() set (_comment "Generating ${_joinedFileRelPath}") endif() add_custom_command( OUTPUT "${_joinedFile}" COMMAND ${_prefixCmd} DEPENDS ${_files} COMMENT "${_comment}" WORKING_DIRECTORY "${_sourceDir}" VERBATIM) list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) endfunction() function (cotire_setup_target_pch_usage _languages _targetSourceDir _target _wholeTarget) if (XCODE) # for Xcode, we attach a pre-build action to generate the unity sources and prefix headers # if necessary, we also generate a single prefix header which includes all language specific prefix headers set (_prefixFiles "") foreach (_language ${_languages}) get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) if (_prefixFile) list (APPEND _prefixFiles "${_prefixFile}") endif() endforeach() set (_cmds ${ARGN}) list (LENGTH _prefixFiles _numberOfPrefixFiles) if (_numberOfPrefixFiles GREATER 1) cotire_make_prefix_file_path("" ${_target} _prefixHeader) cotire_setup_combine_command("" "${_targetSourceDir}" "" "${_prefixHeader}" _cmds ${_prefixFiles}) else() set (_prefixHeader "${_prefixFiles}") endif() if (COTIRE_DEBUG) message (STATUS "add_custom_command: TARGET ${_target} PRE_BUILD ${_cmds}") endif() add_custom_command(TARGET "${_target}" PRE_BUILD ${_cmds} WORKING_DIRECTORY "${_targetSourceDir}" COMMENT "Updating target ${_target} prefix headers" VERBATIM) # make Xcode precompile the generated prefix header with ProcessPCH and ProcessPCH++ set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PRECOMPILE_PREFIX_HEADER "YES") set_target_properties(${_target} PROPERTIES XCODE_ATTRIBUTE_GCC_PREFIX_HEADER "${_prefixHeader}") elseif ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") # for makefile based generator, we force inclusion of the prefix header for all target source files # if this is a single-language target without any excluded files if (_wholeTarget) set (_language "${_languages}") # for Visual Studio and Intel, precompiled header inclusion is always done on the source file level # see cotire_setup_prefix_file_inclusion if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") get_property(_prefixFile TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER) get_property(_pchFile TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER) set (_flags "") cotire_determine_compiler_version("${_language}" COTIRE_${_language}_COMPILER) cotire_add_pch_inclusion_flags( "${_language}" "${CMAKE_${_language}_COMPILER_ID}" "${COTIRE_${_language}_COMPILER_VERSION}" "${_prefixFile}" "${_pchFile}" _flags) set_property (TARGET ${_target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${_flags} ") endif() endif() endif() endfunction() function (cotire_setup_unity_generation_commands _language _targetSourceDir _target _targetScript _unityFiles _cmdsVar) set (_dependencySources "") cotire_get_unity_source_dependencies(${_language} ${_target} _dependencySources ${ARGN}) foreach (_unityFile ${_unityFiles}) file (RELATIVE_PATH _unityFileRelPath "${CMAKE_BINARY_DIR}" "${_unityFile}") set_property (SOURCE "${_unityFile}" PROPERTY GENERATED TRUE) # set up compiled unity source dependencies # this ensures that missing source files are generated before the unity file is compiled if (COTIRE_DEBUG AND _dependencySources) message (STATUS "${_unityFile} OBJECT_DEPENDS ${_dependencySources}") endif() if (_dependencySources) set_property (SOURCE "${_unityFile}" PROPERTY OBJECT_DEPENDS ${_dependencySources}) endif() if (WIN32 AND CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") # unity file compilation results in potentially huge object file, thus use /bigobj by default unter MSVC and Windows Intel set_property (SOURCE "${_unityFile}" APPEND_STRING PROPERTY COMPILE_FLAGS "/bigobj") endif() cotire_set_cmd_to_prologue(_unityCmd) list (APPEND _unityCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "unity" "${_targetScript}" "${_unityFile}") if (COTIRE_DEBUG) message (STATUS "add_custom_command: OUTPUT ${_unityFile} COMMAND ${_unityCmd} DEPENDS ${_targetScript}") endif() add_custom_command( OUTPUT "${_unityFile}" COMMAND ${_unityCmd} DEPENDS "${_targetScript}" COMMENT "Generating ${_language} unity source ${_unityFileRelPath}" WORKING_DIRECTORY "${_targetSourceDir}" VERBATIM) list (APPEND ${_cmdsVar} COMMAND ${_unityCmd}) endforeach() list (LENGTH _unityFiles _numberOfUnityFiles) if (_numberOfUnityFiles GREATER 1) # create a joint unity file from all unity file segments cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_unityFile}" ${_cmdsVar} ${_unityFiles}) endif() set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) endfunction() function (cotire_setup_single_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFile _cmdsVar) set (_sourceFiles ${ARGN}) set (_dependencySources "") cotire_get_prefix_header_dependencies(${_language} ${_target} _dependencySources ${_sourceFiles}) cotire_set_cmd_to_prologue(_prefixCmd) list (APPEND _prefixCmd -P "${COTIRE_CMAKE_MODULE_FILE}" "prefix" "${_targetScript}" "${_prefixFile}" "${_unityFile}") set_property (SOURCE "${_prefixFile}" PROPERTY GENERATED TRUE) if (COTIRE_DEBUG) message (STATUS "add_custom_command: OUTPUT ${_prefixFile} COMMAND ${_prefixCmd} DEPENDS ${_targetScript} ${_unityFile} ${_dependencySources}") endif() file (RELATIVE_PATH _prefixFileRelPath "${CMAKE_BINARY_DIR}" "${_prefixFile}") add_custom_command( OUTPUT "${_prefixFile}" "${_prefixFile}.log" COMMAND ${_prefixCmd} DEPENDS "${_targetScript}" "${_unityFile}" ${_dependencySources} COMMENT "Generating ${_language} prefix header ${_prefixFileRelPath}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) list (APPEND ${_cmdsVar} COMMAND ${_prefixCmd}) set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) endfunction() function (cotire_setup_multi_prefix_generation_command _language _target _targetSourceDir _targetScript _prefixFile _unityFiles _cmdsVar) set (_sourceFiles ${ARGN}) list (LENGTH _unityFiles _numberOfUnityFiles) if (_numberOfUnityFiles GREATER 1) cotire_make_single_unity_source_file_path(${_language} ${_target} _unityFile) cotire_setup_single_prefix_generation_command( ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFile}" ${_cmdsVar} ${_sourceFiles}) else() cotire_setup_single_prefix_generation_command( ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" ${_cmdsVar} ${_sourceFiles}) endif() set (${_cmdsVar} ${${_cmdsVar}} PARENT_SCOPE) endfunction() function (cotire_init_cotire_target_properties _target) get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER TRUE) endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD TRUE) endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_ADD_CLEAN FALSE) endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_SOURCE_DIR}") cotire_check_is_path_relative_to("${CMAKE_BINARY_DIR}" _isRelative "${CMAKE_SOURCE_DIR}") if (NOT _isRelative) set_property(TARGET ${_target} APPEND PROPERTY COTIRE_PREFIX_HEADER_IGNORE_PATH "${CMAKE_BINARY_DIR}") endif() endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_PREFIX_HEADER_INCLUDE_PATH "") endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_PRE_UNDEFS "") endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS SET) if (NOT _isSet) set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_POST_UNDEFS "") endif() get_property(_isSet TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES SET) if (NOT _isSet) if (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}") else() set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "") endif() endif() endfunction() function (cotire_make_target_message _target _languages _disableMsg _targetMsgVar) get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) string (REPLACE ";" " " _languagesStr "${_languages}") string (REPLACE ";" ", " _excludedStr "${ARGN}") set (_targetMsg "") if (NOT _languages) set (_targetMsg "Target ${_target} cannot be cotired.") if (_disableMsg) set (_targetMsg "${_targetMsg} ${_disableMsg}") endif() elseif (NOT _targetUsePCH AND NOT _targetAddSCU) set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build and precompiled header.") if (_disableMsg) set (_targetMsg "${_targetMsg} ${_disableMsg}") endif() elseif (NOT _targetUsePCH) if (_allExcludedSourceFiles) set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without precompiled header.") else() set (_targetMsg "${_languagesStr} target ${_target} cotired without precompiled header.") endif() if (_disableMsg) set (_targetMsg "${_targetMsg} ${_disableMsg}") endif() elseif (NOT _targetAddSCU) if (_allExcludedSourceFiles) set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr} without unity build.") else() set (_targetMsg "${_languagesStr} target ${_target} cotired without unity build.") endif() else() if (_allExcludedSourceFiles) set (_targetMsg "${_languagesStr} target ${_target} cotired excluding files ${_excludedStr}.") else() set (_targetMsg "${_languagesStr} target ${_target} cotired.") endif() endif() set (${_targetMsgVar} "${_targetMsg}" PARENT_SCOPE) endfunction() function (cotire_choose_target_languages _targetSourceDir _target _targetLanguagesVar) set (_languages ${ARGN}) set (_allSourceFiles "") set (_allExcludedSourceFiles "") set (_allCotiredSourceFiles "") set (_targetLanguages "") get_target_property(_targetType ${_target} TYPE) get_target_property(_targetSourceFiles ${_target} SOURCES) get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) set (_disableMsg "") foreach (_language ${_languages}) get_target_property(_prefixHeader ${_target} COTIRE_${_language}_PREFIX_HEADER) get_target_property(_unityBuildFile ${_target} COTIRE_${_language}_UNITY_SOURCE) if (_prefixHeader OR _unityBuildFile) message (WARNING "Target ${_target} has already been cotired.") set (${_targetLanguagesVar} "" PARENT_SCOPE) return() endif() if (_targetUsePCH AND "${_language}" STREQUAL "C" OR "${_language}" STREQUAL "CXX") cotire_check_precompiled_header_support("${_language}" "${_targetSourceDir}" "${_target}" _disableMsg) if (_disableMsg) set (_targetUsePCH FALSE) endif() endif() set (_sourceFiles "") set (_excludedSources "") set (_cotiredSources "") cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) if (_sourceFiles OR _excludedSources OR _cotiredSources) list (APPEND _targetLanguages ${_language}) endif() if (_sourceFiles) list (APPEND _allSourceFiles ${_sourceFiles}) endif() if (_excludedSources) list (APPEND _allExcludedSourceFiles ${_excludedSources}) endif() if (_cotiredSources) list (APPEND _allCotiredSourceFiles ${_cotiredSources}) endif() endforeach() set (_targetMsgLevel STATUS) if (NOT _targetLanguages) string (REPLACE ";" " or " _languagesStr "${_languages}") set (_disableMsg "No ${_languagesStr} source files.") set (_targetUsePCH FALSE) set (_targetAddSCU FALSE) endif() if (_targetUsePCH) list (LENGTH _allSourceFiles _numberOfSources) if (_numberOfSources LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) set (_disableMsg "Too few applicable sources.") set (_targetUsePCH FALSE) elseif (_allCotiredSourceFiles) cotire_get_source_file_property_values(_cotireTargets COTIRE_TARGET ${_allCotiredSourceFiles}) list (REMOVE_DUPLICATES _cotireTargets) string (REPLACE ";" ", " _cotireTargetsStr "${_cotireTargets}") set (_disableMsg "Target sources already include a precompiled header for target(s) ${_cotireTargets}.") set (_disableMsg "${_disableMsg} Set target property COTIRE_ENABLE_PRECOMPILED_HEADER to FALSE for targets ${_target},") set (_disableMsg "${_disableMsg} ${_cotireTargetsStr} to get a workable build system.") set (_targetMsgLevel SEND_ERROR) set (_targetUsePCH FALSE) elseif (XCODE AND _allExcludedSourceFiles) # for Xcode, we cannot apply the precompiled header to individual sources, only to the whole target set (_disableMsg "Exclusion of source files not supported for generator Xcode.") set (_targetUsePCH FALSE) elseif (XCODE AND "${_targetType}" STREQUAL "OBJECT_LIBRARY") # for Xcode, we cannot apply the required PRE_BUILD action to generate the prefix header to an OBJECT_LIBRARY target set (_disableMsg "Required PRE_BUILD action not supported for OBJECT_LIBRARY targets for generator Xcode.") set (_targetUsePCH FALSE) endif() endif() set_property(TARGET ${_target} PROPERTY COTIRE_ENABLE_PRECOMPILED_HEADER ${_targetUsePCH}) set_property(TARGET ${_target} PROPERTY COTIRE_ADD_UNITY_BUILD ${_targetAddSCU}) cotire_make_target_message(${_target} "${_targetLanguages}" "${_disableMsg}" _targetMsg ${_allExcludedSourceFiles}) if (_targetMsg) if (NOT DEFINED COTIREMSG_${_target}) set (COTIREMSG_${_target} "") endif() if (COTIRE_VERBOSE OR NOT "${_targetMsgLevel}" STREQUAL "STATUS" OR NOT "${COTIREMSG_${_target}}" STREQUAL "${_targetMsg}") # cache message to avoid redundant messages on re-configure set (COTIREMSG_${_target} "${_targetMsg}" CACHE INTERNAL "${_target} cotire message.") message (${_targetMsgLevel} "${_targetMsg}") endif() endif() set (${_targetLanguagesVar} ${_targetLanguages} PARENT_SCOPE) endfunction() function (cotire_compute_unity_max_number_of_includes _target _maxIncludesVar) set (_sourceFiles ${ARGN}) get_target_property(_maxIncludes ${_target} COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES) if (_maxIncludes MATCHES "(-j|--parallel|--jobs) ?([0-9]*)") set (_numberOfThreads "${CMAKE_MATCH_2}") if (NOT _numberOfThreads) # use all available cores ProcessorCount(_numberOfThreads) endif() list (LENGTH _sourceFiles _numberOfSources) math (EXPR _maxIncludes "(${_numberOfSources} + ${_numberOfThreads} - 1) / ${_numberOfThreads}") # a unity source segment must not contain less than COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES files if (_maxIncludes LESS ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) set (_maxIncludes ${COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES}) endif() elseif (NOT _maxIncludes MATCHES "[0-9]+") set (_maxIncludes 0) endif() if (COTIRE_DEBUG) message (STATUS "${_target} unity source max includes = ${_maxIncludes}") endif() set (${_maxIncludesVar} ${_maxIncludes} PARENT_SCOPE) endfunction() function (cotire_process_target_language _language _configurations _targetSourceDir _targetBinaryDir _target _wholeTargetVar _cmdsVar) set (${_cmdsVar} "" PARENT_SCOPE) get_target_property(_targetSourceFiles ${_target} SOURCES) set (_sourceFiles "") set (_excludedSources "") set (_cotiredSources "") cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) if (NOT _sourceFiles AND NOT _cotiredSources) return() endif() set (_wholeTarget ${${_wholeTargetVar}}) set (_cmds "") # check for user provided unity source file list get_property(_unitySourceFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE_INIT) if (NOT _unitySourceFiles) set (_unitySourceFiles ${_sourceFiles} ${_cotiredSources}) endif() cotire_generate_target_script( ${_language} "${_configurations}" "${_targetSourceDir}" "${_targetBinaryDir}" ${_target} _targetScript ${_unitySourceFiles}) cotire_compute_unity_max_number_of_includes(${_target} _maxIncludes ${_unitySourceFiles}) cotire_make_unity_source_file_paths(${_language} ${_target} ${_maxIncludes} _unityFiles ${_unitySourceFiles}) if (NOT _unityFiles) return() endif() cotire_setup_unity_generation_commands( ${_language} "${_targetSourceDir}" ${_target} "${_targetScript}" "${_unityFiles}" _cmds ${_unitySourceFiles}) cotire_make_prefix_file_path(${_language} ${_target} _prefixFile) if (_prefixFile) # check for user provided prefix header files get_property(_prefixHeaderFiles TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER_INIT) if (_prefixHeaderFiles) cotire_setup_combine_command(${_language} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" _cmds ${_prefixHeaderFiles}) else() cotire_setup_multi_prefix_generation_command( ${_language} ${_target} "${_targetSourceDir}" "${_targetScript}" "${_prefixFile}" "${_unityFiles}" _cmds ${_unitySourceFiles}) endif() get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) if (_targetUsePCH) cotire_make_pch_file_path(${_language} "${_targetSourceDir}" ${_target} _pchFile) if (_pchFile) cotire_setup_pch_file_compilation( ${_language} "${_targetBinaryDir}" "${_targetScript}" "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) if (_excludedSources) set (_wholeTarget FALSE) endif() cotire_setup_prefix_file_inclusion( ${_language} ${_target} ${_wholeTarget} "${_prefixFile}" "${_pchFile}" ${_sourceFiles}) endif() endif() endif() # mark target as cotired for language set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE "${_unityFiles}") if (_prefixFile) set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PREFIX_HEADER "${_prefixFile}") if (_targetUsePCH AND _pchFile) set_property(TARGET ${_target} PROPERTY COTIRE_${_language}_PRECOMPILED_HEADER "${_pchFile}") endif() endif() set (${_wholeTargetVar} ${_wholeTarget} PARENT_SCOPE) set (${_cmdsVar} ${_cmds} PARENT_SCOPE) endfunction() function (cotire_setup_clean_target _target) set (_cleanTargetName "${_target}${COTIRE_CLEAN_TARGET_SUFFIX}") if (NOT TARGET "${_cleanTargetName}") cotire_set_cmd_to_prologue(_cmds) get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}" ABSOLUTE) list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${_outputDir}" "${COTIRE_INTDIR}" "${_target}") add_custom_target(${_cleanTargetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up target ${_target} cotire generated files" VERBATIM) cotire_init_target("${_cleanTargetName}") endif() endfunction() function (cotire_setup_pch_target _languages _configurations _target) if ("${CMAKE_GENERATOR}" MATCHES "Makefiles|Ninja") # for makefile based generators, we add a custom target to trigger the generation of the cotire related files set (_dependsFiles "") foreach (_language ${_languages}) set (_props COTIRE_${_language}_PREFIX_HEADER COTIRE_${_language}_UNITY_SOURCE) if (NOT CMAKE_${_language}_COMPILER_ID MATCHES "MSVC|Intel") # Visual Studio and Intel only create precompiled header as a side effect list (INSERT _props 0 COTIRE_${_language}_PRECOMPILED_HEADER) endif() cotire_get_first_set_property_value(_dependsFile TARGET ${_target} ${_props}) if (_dependsFile) list (APPEND _dependsFiles "${_dependsFile}") endif() endforeach() if (_dependsFiles) set (_pchTargetName "${_target}${COTIRE_PCH_TARGET_SUFFIX}") add_custom_target("${_pchTargetName}" DEPENDS ${_dependsFiles}) cotire_init_target("${_pchTargetName}") cotire_add_to_pch_all_target(${_pchTargetName}) endif() else() # for other generators, we add the "clean all" target to clean up the precompiled header cotire_setup_clean_all_target() endif() endfunction() function (cotire_setup_unity_build_target _languages _configurations _targetSourceDir _target) get_target_property(_unityTargetName ${_target} COTIRE_UNITY_TARGET_NAME) if (NOT _unityTargetName) set (_unityTargetName "${_target}${COTIRE_UNITY_BUILD_TARGET_SUFFIX}") endif() # determine unity target sub type get_target_property(_targetType ${_target} TYPE) if ("${_targetType}" STREQUAL "EXECUTABLE") get_target_property(_isWin32 ${_target} WIN32_EXECUTABLE) get_target_property(_isMacOSX_Bundle ${_target} MACOSX_BUNDLE) if (_isWin32) set (_unityTargetSubType WIN32) elseif (_isMacOSX_Bundle) set (_unityTargetSubType MACOSX_BUNDLE) else() set (_unityTargetSubType "") endif() elseif (_targetType MATCHES "(STATIC|SHARED|MODULE|OBJECT)_LIBRARY") set (_unityTargetSubType "${CMAKE_MATCH_1}") else() message (WARNING "Unknown target type ${_targetType}.") return() endif() # determine unity target sources get_target_property(_targetSourceFiles ${_target} SOURCES) set (_unityTargetSources ${_targetSourceFiles}) foreach (_language ${_languages}) get_property(_unityFiles TARGET ${_target} PROPERTY COTIRE_${_language}_UNITY_SOURCE) if (_unityFiles) # remove source files that are included in the unity source set (_sourceFiles "") set (_excludedSources "") set (_cotiredSources "") cotire_filter_language_source_files(${_language} _sourceFiles _excludedSources _cotiredSources ${_targetSourceFiles}) if (_sourceFiles OR _cotiredSources) list (REMOVE_ITEM _unityTargetSources ${_sourceFiles} ${_cotiredSources}) endif() # if cotire is applied to a target which has not been added in the current source dir, # non-existing files cannot be referenced from the unity build target (this is a CMake restriction) if (NOT "${_targetSourceDir}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") set (_nonExistingFiles "") foreach (_file ${_unityTargetSources}) if (NOT EXISTS "${_file}") list (APPEND _nonExistingFiles "${_file}") endif() endforeach() if (_nonExistingFiles) if (COTIRE_VERBOSE) message (STATUS "removing non-existing ${_nonExistingFiles} from ${_unityTargetName}") endif() list (REMOVE_ITEM _unityTargetSources ${_nonExistingFiles}) endif() endif() # add unity source files instead list (APPEND _unityTargetSources ${_unityFiles}) endif() endforeach() if (COTIRE_DEBUG) message (STATUS "add ${_targetType} ${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}") endif() # generate unity target if ("${_targetType}" STREQUAL "EXECUTABLE") add_executable(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) else() add_library(${_unityTargetName} ${_unityTargetSubType} EXCLUDE_FROM_ALL ${_unityTargetSources}) endif() set (_outputDirProperties ARCHIVE_OUTPUT_DIRECTORY ARCHIVE_OUTPUT_DIRECTORY_ LIBRARY_OUTPUT_DIRECTORY LIBRARY_OUTPUT_DIRECTORY_ RUNTIME_OUTPUT_DIRECTORY RUNTIME_OUTPUT_DIRECTORY_) # copy output location properties if (COTIRE_UNITY_OUTPUT_DIRECTORY) set (_setDefaultOutputDir TRUE) if (IS_ABSOLUTE "${COTIRE_UNITY_OUTPUT_DIRECTORY}") set (_outputDir "${COTIRE_UNITY_OUTPUT_DIRECTORY}") else() cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) cotrie_resolve_config_properites("${_configurations}" _properties ${_outputDirProperties}) foreach (_property ${_properties}) get_property(_outputDir TARGET ${_target} PROPERTY ${_property}) if (_outputDir) get_filename_component(_outputDir "${_outputDir}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) set_property(TARGET ${_unityTargetName} PROPERTY ${_property} "${_outputDir}") set (_setDefaultOutputDir FALSE) endif() endforeach() if (_setDefaultOutputDir) get_filename_component(_outputDir "${CMAKE_CURRENT_BINARY_DIR}/${COTIRE_UNITY_OUTPUT_DIRECTORY}" ABSOLUTE) endif() endif() if (_setDefaultOutputDir) set_target_properties(${_unityTargetName} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${_outputDir}" LIBRARY_OUTPUT_DIRECTORY "${_outputDir}" RUNTIME_OUTPUT_DIRECTORY "${_outputDir}") endif() else() cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ${_outputDirProperties}) endif() # copy output name cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} ARCHIVE_OUTPUT_NAME ARCHIVE_OUTPUT_NAME_ LIBRARY_OUTPUT_NAME LIBRARY_OUTPUT_NAME_ OUTPUT_NAME OUTPUT_NAME_ RUNTIME_OUTPUT_NAME RUNTIME_OUTPUT_NAME_ PREFIX _POSTFIX SUFFIX) # copy compile stuff cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} COMPILE_DEFINITIONS COMPILE_DEFINITIONS_ COMPILE_FLAGS Fortran_FORMAT INCLUDE_DIRECTORIES INTERPROCEDURAL_OPTIMIZATION INTERPROCEDURAL_OPTIMIZATION_ POSITION_INDEPENDENT_CODE) # copy link stuff cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} BUILD_WITH_INSTALL_RPATH INSTALL_RPATH INSTALL_RPATH_USE_LINK_PATH SKIP_BUILD_RPATH LINKER_LANGUAGE LINK_DEPENDS LINK_FLAGS LINK_FLAGS_ LINK_INTERFACE_LIBRARIES LINK_INTERFACE_LIBRARIES_ LINK_INTERFACE_MULTIPLICITY LINK_INTERFACE_MULTIPLICITY_ LINK_SEARCH_START_STATIC LINK_SEARCH_END_STATIC STATIC_LIBRARY_FLAGS STATIC_LIBRARY_FLAGS_ NO_SONAME SOVERSION VERSION) # copy Qt stuff cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} AUTOMOC AUTOMOC_MOC_OPTIONS) # copy cmake stuff cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} IMPLICIT_DEPENDS_INCLUDE_TRANSFORM RULE_LAUNCH_COMPILE RULE_LAUNCH_CUSTOM RULE_LAUNCH_LINK) # copy platform stuff if (APPLE) cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} BUNDLE BUNDLE_EXTENSION FRAMEWORK INSTALL_NAME_DIR MACOSX_BUNDLE_INFO_PLIST MACOSX_FRAMEWORK_INFO_PLIST OSX_ARCHITECTURES OSX_ARCHITECTURES_ PRIVATE_HEADER PUBLIC_HEADER RESOURCE) elseif (WIN32) cotrie_copy_set_properites("${_configurations}" TARGET ${_target} ${_unityTargetName} GNUtoMS PDB_NAME PDB_NAME_ PDB_OUTPUT_DIRECTORY PDB_OUTPUT_DIRECTORY_ VS_DOTNET_REFERENCES VS_GLOBAL_KEYWORD VS_GLOBAL_PROJECT_TYPES VS_KEYWORD VS_SCC_AUXPATH VS_SCC_LOCALPATH VS_SCC_PROJECTNAME VS_SCC_PROVIDER VS_WINRT_EXTENSIONS VS_WINRT_REFERENCES) endif() # use output name from original target get_target_property(_targetOutputName ${_unityTargetName} OUTPUT_NAME) if (NOT _targetOutputName) set_property(TARGET ${_unityTargetName} PROPERTY OUTPUT_NAME "${_target}") endif() # use export symbol from original target cotire_get_target_export_symbol("${_target}" _defineSymbol) if (_defineSymbol) set_property(TARGET ${_unityTargetName} PROPERTY DEFINE_SYMBOL "${_defineSymbol}") if ("${_targetType}" STREQUAL "EXECUTABLE") set_property(TARGET ${_unityTargetName} PROPERTY ENABLE_EXPORTS TRUE) endif() endif() cotire_init_target(${_unityTargetName}) cotire_add_to_unity_all_target(${_unityTargetName}) set_property(TARGET ${_target} PROPERTY COTIRE_UNITY_TARGET_NAME "${_unityTargetName}") endfunction(cotire_setup_unity_build_target) function (cotire_target _target) set(_options "") set(_oneValueArgs SOURCE_DIR BINARY_DIR) set(_multiValueArgs LANGUAGES CONFIGURATIONS) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) if (NOT _option_SOURCE_DIR) set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") endif() if (NOT _option_BINARY_DIR) set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() if (NOT _option_LANGUAGES) get_property (_option_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) endif() if (NOT _option_CONFIGURATIONS) if (CMAKE_CONFIGURATION_TYPES) set (_option_CONFIGURATIONS ${CMAKE_CONFIGURATION_TYPES}) elseif (CMAKE_BUILD_TYPE) set (_option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}") else() set (_option_CONFIGURATIONS "None") endif() endif() # trivial checks get_target_property(_imported ${_target} IMPORTED) if (_imported) message (WARNING "Imported target ${_target} cannot be cotired.") return() endif() # check if target needs to be cotired for build type # when using configuration types, the test is performed at build time cotire_init_cotire_target_properties(${_target}) if (NOT CMAKE_CONFIGURATION_TYPES) if (CMAKE_BUILD_TYPE) list (FIND _option_CONFIGURATIONS "${CMAKE_BUILD_TYPE}" _index) else() list (FIND _option_CONFIGURATIONS "None" _index) endif() if (_index EQUAL -1) if (COTIRE_DEBUG) message (STATUS "CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} not cotired (${_option_CONFIGURATIONS})") endif() return() endif() endif() # choose languages that apply to the target cotire_choose_target_languages("${_option_SOURCE_DIR}" "${_target}" _targetLanguages ${_option_LANGUAGES}) if (NOT _targetLanguages) return() endif() list (LENGTH _targetLanguages _numberOfLanguages) if (_numberOfLanguages GREATER 1) set (_wholeTarget FALSE) else() set (_wholeTarget TRUE) endif() set (_cmds "") foreach (_language ${_targetLanguages}) cotire_process_target_language("${_language}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" "${_option_BINARY_DIR}" ${_target} _wholeTarget _cmd) if (_cmd) list (APPEND _cmds ${_cmd}) endif() endforeach() get_target_property(_targetAddSCU ${_target} COTIRE_ADD_UNITY_BUILD) if (_targetAddSCU) cotire_setup_unity_build_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" "${_option_SOURCE_DIR}" ${_target}) endif() get_target_property(_targetUsePCH ${_target} COTIRE_ENABLE_PRECOMPILED_HEADER) if (_targetUsePCH) cotire_setup_target_pch_usage("${_targetLanguages}" "${_option_SOURCE_DIR}" ${_target} ${_wholeTarget} ${_cmds}) cotire_setup_pch_target("${_targetLanguages}" "${_option_CONFIGURATIONS}" ${_target}) endif() get_target_property(_targetAddCleanTarget ${_target} COTIRE_ADD_CLEAN) if (_targetAddCleanTarget) cotire_setup_clean_target(${_target}) endif() endfunction() function (cotire_cleanup _binaryDir _cotireIntermediateDirName _targetName) if (_targetName) file (GLOB_RECURSE _cotireFiles "${_binaryDir}/${_targetName}*.*") else() file (GLOB_RECURSE _cotireFiles "${_binaryDir}/*.*") endif() # filter files in intermediate directory set (_filesToRemove "") foreach (_file ${_cotireFiles}) get_filename_component(_dir "${_file}" PATH) get_filename_component(_dirName "${_dir}" NAME) if ("${_dirName}" STREQUAL "${_cotireIntermediateDirName}") list (APPEND _filesToRemove "${_file}") endif() endforeach() if (_filesToRemove) if (COTIRE_VERBOSE) message (STATUS "removing ${_filesToRemove}") endif() file (REMOVE ${_filesToRemove}) endif() endfunction() function (cotire_init_target _targetName) if (COTIRE_TARGETS_FOLDER) set_target_properties(${_targetName} PROPERTIES FOLDER "${COTIRE_TARGETS_FOLDER}") endif() if (MSVC_IDE) set_target_properties(${_targetName} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD TRUE) endif() endfunction() function (cotire_add_to_pch_all_target _pchTargetName) set (_targetName "${COTIRE_PCH_ALL_TARGET_NAME}") if (NOT TARGET "${_targetName}") add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) cotire_init_target("${_targetName}") endif() cotire_setup_clean_all_target() add_dependencies(${_targetName} ${_pchTargetName}) endfunction() function (cotire_add_to_unity_all_target _unityTargetName) set (_targetName "${COTIRE_UNITY_BUILD_ALL_TARGET_NAME}") if (NOT TARGET "${_targetName}") add_custom_target("${_targetName}" WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" VERBATIM) cotire_init_target("${_targetName}") endif() cotire_setup_clean_all_target() add_dependencies(${_targetName} ${_unityTargetName}) endfunction() function (cotire_setup_clean_all_target) set (_targetName "${COTIRE_CLEAN_ALL_TARGET_NAME}") if (NOT TARGET "${_targetName}") cotire_set_cmd_to_prologue(_cmds) list (APPEND _cmds -P "${COTIRE_CMAKE_MODULE_FILE}" "cleanup" "${CMAKE_BINARY_DIR}" "${COTIRE_INTDIR}") add_custom_target(${_targetName} COMMAND ${_cmds} WORKING_DIRECTORY "${CMAKE_BINARY_DIR}" COMMENT "Cleaning up all cotire generated files" VERBATIM) cotire_init_target("${_targetName}") endif() endfunction() function (cotire) set(_options "") set(_oneValueArgs SOURCE_DIR BINARY_DIR) set(_multiValueArgs LANGUAGES CONFIGURATIONS) cmake_parse_arguments(_option "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) set (_targets ${_option_UNPARSED_ARGUMENTS}) if (NOT _option_SOURCE_DIR) set (_option_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") endif() if (NOT _option_BINARY_DIR) set (_option_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") endif() foreach (_target ${_targets}) if (TARGET ${_target}) cotire_target(${_target} LANGUAGES ${_option_LANGUAGES} CONFIGURATIONS ${_option_CONFIGURATIONS} SOURCE_DIR "${_option_SOURCE_DIR}" BINARY_DIR "${_option_BINARY_DIR}") else() message (WARNING "${_target} is not a target") endif() endforeach() endfunction() if (CMAKE_SCRIPT_MODE_FILE) # cotire is being run in script mode # locate -P on command args set (COTIRE_ARGC -1) foreach (_index RANGE ${CMAKE_ARGC}) if (COTIRE_ARGC GREATER -1) set (COTIRE_ARGV${COTIRE_ARGC} "${CMAKE_ARGV${_index}}") math (EXPR COTIRE_ARGC "${COTIRE_ARGC} + 1") elseif ("${CMAKE_ARGV${_index}}" STREQUAL "-P") set (COTIRE_ARGC 0) endif() endforeach() # include target script if available if ("${COTIRE_ARGV2}" MATCHES "\\.cmake$") # the included target scripts sets up additional variables relating to the target (e.g., COTIRE_TARGET_SOURCES) include("${COTIRE_ARGV2}") endif() if (COTIRE_DEBUG) message (STATUS "${COTIRE_ARGV0} ${COTIRE_ARGV1} ${COTIRE_ARGV2} ${COTIRE_ARGV3} ${COTIRE_ARGV4} ${COTIRE_ARGV5}") endif() if (WIN32) # for MSVC, compiler IDs may not always be set correctly if (MSVC) set (CMAKE_C_COMPILER_ID "MSVC") set (CMAKE_CXX_COMPILER_ID "MSVC") endif() endif() if (NOT COTIRE_BUILD_TYPE) set (COTIRE_BUILD_TYPE "None") endif() string (TOUPPER "${COTIRE_BUILD_TYPE}" _upperConfig) set (_includeDirs ${COTIRE_TARGET_INCLUDE_DIRECTORIES_${_upperConfig}}) set (_compileDefinitions ${COTIRE_TARGET_COMPILE_DEFINITIONS_${_upperConfig}}) set (_compileFlags ${COTIRE_TARGET_COMPILE_FLAGS_${_upperConfig}}) # check if target has been cotired for actual build type COTIRE_BUILD_TYPE list (FIND COTIRE_TARGET_CONFIGURATION_TYPES "${COTIRE_BUILD_TYPE}" _index) if (_index GREATER -1) set (_sources ${COTIRE_TARGET_SOURCES}) set (_sourcesDefinitions ${COTIRE_TARGET_SOURCES_COMPILE_DEFINITIONS_${_upperConfig}}) else() if (COTIRE_DEBUG) message (STATUS "COTIRE_BUILD_TYPE=${COTIRE_BUILD_TYPE} not cotired (${COTIRE_TARGET_CONFIGURATION_TYPES})") endif() set (_sources "") set (_sourcesDefinitions "") endif() set (_targetPreUndefs ${COTIRE_TARGET_PRE_UNDEFS}) set (_targetPostUndefs ${COTIRE_TARGET_POST_UNDEFS}) set (_sourcesPreUndefs ${COTIRE_TARGET_SOURCES_PRE_UNDEFS}) set (_sourcesPostUndefs ${COTIRE_TARGET_SOURCES_POST_UNDEFS}) if ("${COTIRE_ARGV1}" STREQUAL "unity") cotire_select_unity_source_files("${COTIRE_ARGV3}" _sources ${_sources}) cotire_generate_unity_source( "${COTIRE_ARGV3}" ${_sources} LANGUAGE "${COTIRE_TARGET_LANGUAGE}" DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV2}" SOURCES_COMPILE_DEFINITIONS ${_sourcesDefinitions} PRE_UNDEFS ${_targetPreUndefs} POST_UNDEFS ${_targetPostUndefs} SOURCES_PRE_UNDEFS ${_sourcesPreUndefs} SOURCES_POST_UNDEFS ${_sourcesPostUndefs}) elseif ("${COTIRE_ARGV1}" STREQUAL "prefix") set (_files "") foreach (_index RANGE 4 ${COTIRE_ARGC}) if (COTIRE_ARGV${_index}) list (APPEND _files "${COTIRE_ARGV${_index}}") endif() endforeach() cotire_generate_prefix_header( "${COTIRE_ARGV3}" ${_files} COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" LANGUAGE "${COTIRE_TARGET_LANGUAGE}" DEPENDS "${COTIRE_ARGV0}" "${COTIRE_ARGV4}" ${COTIRE_TARGET_PREFIX_DEPENDS} IGNORE_PATH "${COTIRE_TARGET_IGNORE_PATH};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH}" INCLUDE_PATH ${COTIRE_TARGET_INCLUDE_PATH} IGNORE_EXTENSIONS "${CMAKE_${COTIRE_TARGET_LANGUAGE}_SOURCE_FILE_EXTENSIONS};${COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS}" INCLUDE_DIRECTORIES ${_includeDirs} COMPILE_DEFINITIONS ${_compileDefinitions} COMPILE_FLAGS ${_compileFlags}) elseif ("${COTIRE_ARGV1}" STREQUAL "precompile") set (_files "") foreach (_index RANGE 5 ${COTIRE_ARGC}) if (COTIRE_ARGV${_index}) list (APPEND _files "${COTIRE_ARGV${_index}}") endif() endforeach() cotire_precompile_prefix_header( "${COTIRE_ARGV3}" "${COTIRE_ARGV4}" "${COTIRE_ARGV5}" COMPILER_EXECUTABLE "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER}" COMPILER_ARG1 ${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ARG1} COMPILER_ID "${CMAKE_${COTIRE_TARGET_LANGUAGE}_COMPILER_ID}" COMPILER_VERSION "${COTIRE_${COTIRE_TARGET_LANGUAGE}_COMPILER_VERSION}" LANGUAGE "${COTIRE_TARGET_LANGUAGE}" INCLUDE_DIRECTORIES ${_includeDirs} COMPILE_DEFINITIONS ${_compileDefinitions} COMPILE_FLAGS ${_compileFlags}) elseif ("${COTIRE_ARGV1}" STREQUAL "combine") if (COTIRE_TARGET_LANGUAGE) set (_startIndex 3) else() set (_startIndex 2) endif() set (_files "") foreach (_index RANGE ${_startIndex} ${COTIRE_ARGC}) if (COTIRE_ARGV${_index}) list (APPEND _files "${COTIRE_ARGV${_index}}") endif() endforeach() if (COTIRE_TARGET_LANGUAGE) cotire_generate_unity_source(${_files} LANGUAGE "${COTIRE_TARGET_LANGUAGE}") else() cotire_generate_unity_source(${_files}) endif() elseif ("${COTIRE_ARGV1}" STREQUAL "cleanup") cotire_cleanup("${COTIRE_ARGV2}" "${COTIRE_ARGV3}" "${COTIRE_ARGV4}") else() message (FATAL_ERROR "Unknown cotire command \"${COTIRE_ARGV1}\".") endif() else() # cotire is being run in include mode # set up all variable and property definitions unset (COTIRE_C_COMPILER_VERSION CACHE) unset (COTIRE_CXX_COMPILER_VERSION CACHE) if (NOT DEFINED COTIRE_DEBUG_INIT) if (DEFINED COTIRE_DEBUG) set (COTIRE_DEBUG_INIT ${COTIRE_DEBUG}) else() set (COTIRE_DEBUG_INIT FALSE) endif() endif() option (COTIRE_DEBUG "Enable cotire debugging output?" ${COTIRE_DEBUG_INIT}) if (NOT DEFINED COTIRE_VERBOSE_INIT) if (DEFINED COTIRE_VERBOSE) set (COTIRE_VERBOSE_INIT ${COTIRE_VERBOSE}) else() set (COTIRE_VERBOSE_INIT FALSE) endif() endif() option (COTIRE_VERBOSE "Enable cotire verbose output?" ${COTIRE_VERBOSE_INIT}) set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS "inc;inl;ipp" CACHE STRING "Ignore headers with the listed file extensions from the generated prefix header.") set (COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH "" CACHE STRING "Ignore headers from these directories when generating the prefix header.") set (COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS "m;mm" CACHE STRING "Ignore sources with the listed file extensions from the generated unity source.") set (COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES "3" CACHE STRING "Minimum number of sources in target required to enable use of precompiled header.") if (NOT DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT) if (DEFINED COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES) set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT ${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES}) elseif ("${CMAKE_GENERATOR}" MATCHES "JOM|Ninja|Visual Studio") # enable parallelization for generators that run multiple jobs by default set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "-j") else() set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT "0") endif() endif() set (COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES "${COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES_INIT}" CACHE STRING "Maximum number of source files to include in a single unity source file.") if (NOT COTIRE_PREFIX_HEADER_FILENAME_SUFFIX) set (COTIRE_PREFIX_HEADER_FILENAME_SUFFIX "_prefix") endif() if (NOT COTIRE_UNITY_SOURCE_FILENAME_SUFFIX) set (COTIRE_UNITY_SOURCE_FILENAME_SUFFIX "_unity") endif() if (NOT COTIRE_INTDIR) set (COTIRE_INTDIR "cotire") endif() if (NOT COTIRE_PCH_ALL_TARGET_NAME) set (COTIRE_PCH_ALL_TARGET_NAME "all_pch") endif() if (NOT COTIRE_UNITY_BUILD_ALL_TARGET_NAME) set (COTIRE_UNITY_BUILD_ALL_TARGET_NAME "all_unity") endif() if (NOT COTIRE_CLEAN_ALL_TARGET_NAME) set (COTIRE_CLEAN_ALL_TARGET_NAME "clean_cotire") endif() if (NOT COTIRE_CLEAN_TARGET_SUFFIX) set (COTIRE_CLEAN_TARGET_SUFFIX "_clean_cotire") endif() if (NOT COTIRE_PCH_TARGET_SUFFIX) set (COTIRE_PCH_TARGET_SUFFIX "_pch") endif() if (NOT COTIRE_UNITY_BUILD_TARGET_SUFFIX) set (COTIRE_UNITY_BUILD_TARGET_SUFFIX "_unity") endif() if (NOT DEFINED COTIRE_TARGETS_FOLDER) set (COTIRE_TARGETS_FOLDER "cotire") endif() if (NOT DEFINED COTIRE_UNITY_OUTPUT_DIRECTORY) if ("${CMAKE_GENERATOR}" MATCHES "Ninja") # generated Ninja build files do not work if the unity target produces the same output file as the cotired target set (COTIRE_UNITY_OUTPUT_DIRECTORY "unity") else() set (COTIRE_UNITY_OUTPUT_DIRECTORY "") endif() endif() # define cotire cache variables define_property( CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_PATH" BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." FULL_DOCS "The variable can be set to a semicolon separated list of include directories." "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." "If not defined, defaults to empty list." ) define_property( CACHED_VARIABLE PROPERTY "COTIRE_ADDITIONAL_PREFIX_HEADER_IGNORE_EXTENSIONS" BRIEF_DOCS "Ignore includes with the listed file extensions from the generated prefix header." FULL_DOCS "The variable can be set to a semicolon separated list of file extensions." "If a header file extension matches one in the list, it will be excluded from the generated prefix header." "Includes with an extension in CMAKE__SOURCE_FILE_EXTENSIONS are always ignored." "If not defined, defaults to inc;inl;ipp." ) define_property( CACHED_VARIABLE PROPERTY "COTIRE_UNITY_SOURCE_EXCLUDE_EXTENSIONS" BRIEF_DOCS "Exclude sources with the listed file extensions from the generated unity source." FULL_DOCS "The variable can be set to a semicolon separated list of file extensions." "If a source file extension matches one in the list, it will be excluded from the generated unity source file." "Source files with an extension in CMAKE__IGNORE_EXTENSIONS are always excluded." "If not defined, defaults to m;mm." ) define_property( CACHED_VARIABLE PROPERTY "COTIRE_MINIMUM_NUMBER_OF_TARGET_SOURCES" BRIEF_DOCS "Minimum number of sources in target required to enable use of precompiled header." FULL_DOCS "The variable can be set to an integer > 0." "If a target contains less than that number of source files, cotire will not enable the use of the precompiled header for the target." "If not defined, defaults to 3." ) define_property( CACHED_VARIABLE PROPERTY "COTIRE_MAXIMUM_NUMBER_OF_UNITY_INCLUDES" BRIEF_DOCS "Maximum number of source files to include in a single unity source file." FULL_DOCS "This may be set to an integer >= 0." "If 0, cotire will only create a single unity source file." "If a target contains more than that number of source files, cotire will create multiple unity source files for it." "Can be set to \"-j\" to optimize the count of unity source files for the number of available processor cores." "Can be set to \"-j jobs\" to optimize the number of unity source files for the given number of simultaneous jobs." "Is used to initialize the target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." "Defaults to \"-j\" for the generators Visual Studio, JOM or Ninja. Defaults to 0 otherwise." ) # define cotire directory properties define_property( DIRECTORY PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" BRIEF_DOCS "Modify build command of cotired targets added in this directory to make use of the generated precompiled header." FULL_DOCS "See target property COTIRE_ENABLE_PRECOMPILED_HEADER." ) define_property( DIRECTORY PROPERTY "COTIRE_ADD_UNITY_BUILD" BRIEF_DOCS "Add a new target that performs a unity build for cotired targets added in this directory." FULL_DOCS "See target property COTIRE_ADD_UNITY_BUILD." ) define_property( DIRECTORY PROPERTY "COTIRE_ADD_CLEAN" BRIEF_DOCS "Add a new target that cleans all cotire generated files for cotired targets added in this directory." FULL_DOCS "See target property COTIRE_ADD_CLEAN." ) define_property( DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." FULL_DOCS "See target property COTIRE_PREFIX_HEADER_IGNORE_PATH." ) define_property( DIRECTORY PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" BRIEF_DOCS "Honor headers from these directories when generating the prefix header." FULL_DOCS "See target property COTIRE_PREFIX_HEADER_INCLUDE_PATH." ) define_property( DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each source file." FULL_DOCS "See target property COTIRE_UNITY_SOURCE_PRE_UNDEFS." ) define_property( DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each source file." FULL_DOCS "See target property COTIRE_UNITY_SOURCE_POST_UNDEFS." ) define_property( DIRECTORY PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" BRIEF_DOCS "Maximum number of source files to include in a single unity source file." FULL_DOCS "See target property COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES." ) # define cotire target properties define_property( TARGET PROPERTY "COTIRE_ENABLE_PRECOMPILED_HEADER" INHERITED BRIEF_DOCS "Modify this target's build command to make use of the generated precompiled header." FULL_DOCS "If this property is set to TRUE, cotire will modify the build command to make use of the generated precompiled header." "Irrespective of the value of this property, cotire will setup custom commands to generate the unity source and prefix header for the target." "For makefile based generators cotire will also set up a custom target to manually invoke the generation of the precompiled header." "The target name will be set to this target's name with the suffix _pch appended." "Inherited from directory." "Defaults to TRUE." ) define_property( TARGET PROPERTY "COTIRE_ADD_UNITY_BUILD" INHERITED BRIEF_DOCS "Add a new target that performs a unity build for this target." FULL_DOCS "If this property is set to TRUE, cotire creates a new target of the same type that uses the generated unity source file instead of the target sources." "Most of the relevant target properties will be copied from this target to the new unity build target." "Target dependencies and linked libraries have to be manually set up for the new unity build target." "The unity target name will be set to this target's name with the suffix _unity appended." "Inherited from directory." "Defaults to TRUE." ) define_property( TARGET PROPERTY "COTIRE_ADD_CLEAN" INHERITED BRIEF_DOCS "Add a new target that cleans all cotire generated files for this target." FULL_DOCS "If this property is set to TRUE, cotire creates a new target that clean all files (unity source, prefix header, precompiled header)." "The clean target name will be set to this target's name with the suffix _clean_cotire appended." "Inherited from directory." "Defaults to FALSE." ) define_property( TARGET PROPERTY "COTIRE_PREFIX_HEADER_IGNORE_PATH" INHERITED BRIEF_DOCS "Ignore headers from these directories when generating the prefix header." FULL_DOCS "The property can be set to a list of directories." "If a header file is found in one of these directories or sub-directories, it will be excluded from the generated prefix header." "Inherited from directory." "If not set, this property is initialized to \${CMAKE_SOURCE_DIR};\${CMAKE_BINARY_DIR}." ) define_property( TARGET PROPERTY "COTIRE_PREFIX_HEADER_INCLUDE_PATH" INHERITED BRIEF_DOCS "Honor headers from these directories when generating the prefix header." FULL_DOCS "The property can be set to a list of directories." "If a header file is found in one of these directories or sub-directories, it will be included in the generated prefix header." "If a header file is both selected by COTIRE_PREFIX_HEADER_IGNORE_PATH and COTIRE_PREFIX_HEADER_INCLUDE_PATH," "the option which yields the closer relative path match wins." "Inherited from directory." "If not set, this property is initialized to the empty list." ) define_property( TARGET PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" INHERITED BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of each target source file." FULL_DOCS "This may be set to a semicolon-separated list of preprocessor symbols." "cotire will add corresponding #undef directives to the generated unit source file before each target source file." "Inherited from directory." "Defaults to empty string." ) define_property( TARGET PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" INHERITED BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of each target source file." FULL_DOCS "This may be set to a semicolon-separated list of preprocessor symbols." "cotire will add corresponding #undef directives to the generated unit source file after each target source file." "Inherited from directory." "Defaults to empty string." ) define_property( TARGET PROPERTY "COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES" INHERITED BRIEF_DOCS "Maximum number of source files to include in a single unity source file." FULL_DOCS "This may be set to an integer > 0." "If a target contains more than that number of source files, cotire will create multiple unity build files for it." "If not set, cotire will only create a single unity source file." "Inherited from directory." "Defaults to empty." ) define_property( TARGET PROPERTY "COTIRE__UNITY_SOURCE_INIT" BRIEF_DOCS "User provided unity source file to be used instead of the automatically generated one." FULL_DOCS "If set, cotire will only add the given file(s) to the generated unity source file." "If not set, cotire will add all the target source files to the generated unity source file." "The property can be set to a user provided unity source file." "Defaults to empty." ) define_property( TARGET PROPERTY "COTIRE__PREFIX_HEADER_INIT" BRIEF_DOCS "User provided prefix header file to be used instead of the automatically generated one." FULL_DOCS "If set, cotire will add the given header file(s) to the generated prefix header file." "If not set, cotire will generate a prefix header by tracking the header files included by the unity source file." "The property can be set to a user provided prefix header file (e.g., stdafx.h)." "Defaults to empty." ) define_property( TARGET PROPERTY "COTIRE__UNITY_SOURCE" BRIEF_DOCS "Read-only property. The generated unity source file(s)." FULL_DOCS "cotire sets this property to the path of the generated single computation unit source file for the target." "Defaults to empty string." ) define_property( TARGET PROPERTY "COTIRE__PREFIX_HEADER" BRIEF_DOCS "Read-only property. The generated prefix header file." FULL_DOCS "cotire sets this property to the full path of the generated language prefix header for the target." "Defaults to empty string." ) define_property( TARGET PROPERTY "COTIRE__PRECOMPILED_HEADER" BRIEF_DOCS "Read-only property. The generated precompiled header file." FULL_DOCS "cotire sets this property to the full path of the generated language precompiled header binary for the target." "Defaults to empty string." ) define_property( TARGET PROPERTY "COTIRE_UNITY_TARGET_NAME" BRIEF_DOCS "The name of the generated unity build target corresponding to this target." FULL_DOCS "This property can be set to the desired name of the unity target that will be created by cotire." "If not set, the unity target name will be set to this target's name with the suffix _unity appended." "After this target has been processed by cotire, the property is set to the actual name of the generated unity target." "Defaults to empty string." ) # define cotire source properties define_property( SOURCE PROPERTY "COTIRE_EXCLUDED" BRIEF_DOCS "Do not modify source file's build command." FULL_DOCS "If this property is set to TRUE, the source file's build command will not be modified to make use of the precompiled header." "The source file will also be excluded from the generated unity source file." "Source files that have their COMPILE_FLAGS property set will be excluded by default." "Defaults to FALSE." ) define_property( SOURCE PROPERTY "COTIRE_DEPENDENCY" BRIEF_DOCS "Add this source file to dependencies of the automatically generated prefix header file." FULL_DOCS "If this property is set to TRUE, the source file is added to dependencies of the generated prefix header file." "If the file is modified, cotire will re-generate the prefix header source upon build." "Defaults to FALSE." ) define_property( SOURCE PROPERTY "COTIRE_UNITY_SOURCE_PRE_UNDEFS" BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file before the inclusion of this source file." FULL_DOCS "This may be set to a semicolon-separated list of preprocessor symbols." "cotire will add corresponding #undef directives to the generated unit source file before this file is included." "Defaults to empty string." ) define_property( SOURCE PROPERTY "COTIRE_UNITY_SOURCE_POST_UNDEFS" BRIEF_DOCS "Preprocessor undefs to place in the generated unity source file after the inclusion of this source file." FULL_DOCS "This may be set to a semicolon-separated list of preprocessor symbols." "cotire will add corresponding #undef directives to the generated unit source file after this file is included." "Defaults to empty string." ) define_property( SOURCE PROPERTY "COTIRE_START_NEW_UNITY_SOURCE" BRIEF_DOCS "Start a new unity source file which includes this source file as the first one." FULL_DOCS "If this property is set to TRUE, cotire will complete the current unity file and start a new one." "The new unity source file will include this source file as the first one." "This property essentially works as a separator for unity source files." "Defaults to FALSE." ) define_property( SOURCE PROPERTY "COTIRE_TARGET" BRIEF_DOCS "Read-only property. Mark this source file as cotired for the given target." FULL_DOCS "cotire sets this property to the name of target, that the source file's build command has been altered for." "Defaults to empty string." ) message (STATUS "cotire ${COTIRE_CMAKE_MODULE_VERSION} loaded.") endif() pulseview-0.2.0/signalhandler.cpp000600 001750 001750 00000004365 12332253627 016522 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Adam Reichold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "signalhandler.h" #include #include #include #include #include #include #include int SignalHandler::_sockets[2]; bool SignalHandler::prepare_signals() { if(socketpair(AF_UNIX, SOCK_STREAM, 0, _sockets) != 0) return false; struct sigaction sig_action; sig_action.sa_handler = SignalHandler::handle_signals; sigemptyset(&sig_action.sa_mask); sig_action.sa_flags = SA_RESTART; if(sigaction(SIGINT, &sig_action, 0) != 0 || sigaction(SIGTERM, &sig_action, 0) != 0) { close(_sockets[0]); close(_sockets[1]); return false; } return true; } SignalHandler::SignalHandler(QObject* parent) : QObject(parent), _socket_notifier(0) { _socket_notifier = new QSocketNotifier(_sockets[1], QSocketNotifier::Read, this); connect(_socket_notifier, SIGNAL(activated(int)), SLOT(on_socket_notifier_activated())); } void SignalHandler::on_socket_notifier_activated() { _socket_notifier->setEnabled(false); int sig_number; if(read(_sockets[1], &sig_number, sizeof(int)) != sizeof(int)) { qDebug() << "Failed to catch signal"; abort(); } switch(sig_number) { case SIGINT: emit int_received(); break; case SIGTERM: emit term_received(); break; } _socket_notifier->setEnabled(true); } void SignalHandler::handle_signals(int sig_number) { if(write(_sockets[0], &sig_number, sizeof(int)) != sizeof(int)) { // Failed to handle signal abort(); } } pulseview-0.2.0/contrib/pulseview.desktop000600 001750 001750 00000000330 12332253627 020245 0ustar00uweuwe000000 000000 [Desktop Entry] Name=PulseView GenericName=Signal acquisition GUI for sigrok Categories=Electronics; Comment=Control your Logic Analyzer, Oscilloscope, or MSO Exec=pulseview Icon=sigrok-logo-notext Type=Application pulseview-0.2.0/contrib/pulseview_cross.nsi000600 001750 001750 00000020045 12332253627 020603 0ustar00uweuwe000000 000000 ## ## This file is part of the PulseView project. ## ## Copyright (C) 2013-2014 Uwe Hermann ## ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ## # # This file is used to create the PulseView Windows installer via NSIS. # It is meant for use in a cross-compile setup (not for native builds). # See the 'sigrok-cross-mingw' script in the sigrok-util repo for details. # # NSIS documentation: # http://nsis.sourceforge.net/Docs/ # http://nsis.sourceforge.net/Docs/Modern%20UI%202/Readme.html # # Include the "Modern UI" header, which gives us the usual Windows look-n-feel. !include "MUI2.nsh" # --- Global stuff ------------------------------------------------------------ # Installer/product name. Name "PulseView" # Filename of the installer executable. OutFile "pulseview-0.2.0-installer.exe" # Where to install the application. InstallDir "$PROGRAMFILES\sigrok\PulseView" # Request admin privileges for Windows Vista and Windows 7. # http://nsis.sourceforge.net/Docs/Chapter4.html RequestExecutionLevel admin # Local helper definitions. !define REGSTR "Software\Microsoft\Windows\CurrentVersion\Uninstall\PulseView" # --- MUI interface configuration --------------------------------------------- # Use the following icon for the installer EXE file. !define MUI_ICON "../icons/sigrok-logo-notext.ico" # Show a nice image at the top of each installer page. !define MUI_HEADERIMAGE # Don't automatically go to the Finish page so the user can check the log. !define MUI_FINISHPAGE_NOAUTOCLOSE # Upon "cancel", ask the user if he really wants to abort the installer. !define MUI_ABORTWARNING # Don't force the user to accept the license, just show it. # Details: http://trac.videolan.org/vlc/ticket/3124 !define MUI_LICENSEPAGE_BUTTON $(^NextBtn) !define MUI_LICENSEPAGE_TEXT_BOTTOM "Click Next to continue." # Path where the cross-compiled sigrok tools and libraries are located. # Change this to where-ever you installed libsigrok.a and so on. !define CROSS "$%HOME%/sr_mingw" # Path where the cross-compiled MXE tools and libraries are located. # Change this to where-ever you installed MXE (and the files it built). !define MXE "$%HOME%/mxe-git/usr/i686-pc-mingw32" # --- MUI pages --------------------------------------------------------------- # Show a nice "Welcome to the ... Setup Wizard" page. !insertmacro MUI_PAGE_WELCOME # Show the license of the project. !insertmacro MUI_PAGE_LICENSE "../COPYING" # Show a screen which allows the user to select which components to install. !insertmacro MUI_PAGE_COMPONENTS # Allow the user to select a different install directory. !insertmacro MUI_PAGE_DIRECTORY # Perform the actual installation, i.e. install the files. !insertmacro MUI_PAGE_INSTFILES # Show a final "We're done, click Finish to close this wizard" message. !insertmacro MUI_PAGE_FINISH # Pages used for the uninstaller. !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH # --- MUI language files ------------------------------------------------------ # Select an installer language (required!). !insertmacro MUI_LANGUAGE "English" # --- Default section --------------------------------------------------------- Section "PulseView (required)" Section1 # This section is gray (can't be disabled) in the component list. SectionIn RO # Install the file(s) specified below into the specified directory. SetOutPath "$INSTDIR" # License file. File "../COPYING" # PulseView (statically linked, includes all libs). File "${CROSS}/bin/pulseview.exe" # libusb0.dll (needed for libusb-0.1). File "${CROSS}/libusb0.dll" # Zadig (used for installing libusb-win32 and WinUSB drivers). File "${CROSS}/zadig.exe" File "${CROSS}/zadig_xp.exe" # Python File "${CROSS}/python32.dll" File "${CROSS}/python32.zip" # Protocol decoders. SetOutPath "$INSTDIR\decoders" File /r /x "__pycache__" "${CROSS}/share/libsigrokdecode/decoders/*" # Firmware files. SetOutPath "$INSTDIR\firmware" File /r "${CROSS}/share/sigrok-firmware/*" # Example *.sr files. SetOutPath "$INSTDIR\examples" File /r "${CROSS}/share/sigrok-dumps/*" # Generate the uninstaller executable. WriteUninstaller "$INSTDIR\Uninstall.exe" # Create a sub-directory in the start menu. CreateDirectory "$SMPROGRAMS\sigrok" CreateDirectory "$SMPROGRAMS\sigrok\PulseView" # Create a shortcut for the PulseView application. SetOutPath "$INSTDIR" CreateShortCut "$SMPROGRAMS\sigrok\PulseView\PulseView.lnk" \ "$INSTDIR\pulseview.exe" "" "$INSTDIR\pulseview.exe" \ 0 SW_SHOWNORMAL \ "" "Open-source, portable sigrok GUI" # Create a shortcut for the uninstaller. CreateShortCut "$SMPROGRAMS\sigrok\PulseView\Uninstall.lnk" \ "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0 \ SW_SHOWNORMAL "" "Uninstall PulseView" # Create registry keys for "Add/remove programs" in the control panel. WriteRegStr HKLM "${REGSTR}" "DisplayName" "PulseView" WriteRegStr HKLM "${REGSTR}" "UninstallString" \ "$\"$INSTDIR\Uninstall.exe$\"" WriteRegStr HKLM "${REGSTR}" "InstallLocation" "$\"$INSTDIR$\"" WriteRegStr HKLM "${REGSTR}" "DisplayIcon" \ "$\"$INSTDIR\sigrok-logo-notext.ico$\"" WriteRegStr HKLM "${REGSTR}" "Publisher" "sigrok" WriteRegStr HKLM "${REGSTR}" "HelpLink" \ "http://sigrok.org/wiki/PulseView" WriteRegStr HKLM "${REGSTR}" "URLUpdateInfo" \ "http://sigrok.org/wiki/Downloads" WriteRegStr HKLM "${REGSTR}" "URLInfoAbout" "http://sigrok.org" WriteRegStr HKLM "${REGSTR}" "DisplayVersion" "0.2.0" WriteRegStr HKLM "${REGSTR}" "Contact" \ "sigrok-devel@lists.sourceforge.org" WriteRegStr HKLM "${REGSTR}" "Comments" \ "This is a Qt based sigrok GUI." # Display "Remove" instead of "Modify/Remove" in the control panel. WriteRegDWORD HKLM "${REGSTR}" "NoModify" 1 WriteRegDWORD HKLM "${REGSTR}" "NoRepair" 1 SectionEnd # --- Uninstaller section ----------------------------------------------------- Section "Uninstall" # Always delete the uninstaller first (yes, this really works). Delete "$INSTDIR\Uninstall.exe" # Delete the application, the application data, and related libs. Delete "$INSTDIR\COPYING" Delete "$INSTDIR\pulseview.exe" Delete "$INSTDIR\libusb0.dll" Delete "$INSTDIR\zadig.exe" Delete "$INSTDIR\zadig_xp.exe" Delete "$INSTDIR\python32.dll" Delete "$INSTDIR\python32.zip" # Delete all decoders and everything else in decoders/. # There could be *.pyc files or __pycache__ subdirs and so on. RMDir /r "$INSTDIR\decoders\*" # Delete the firmware files. RMDir /r "$INSTDIR\firmware\*" # Delete the example *.sr files. RMDir /r "$INSTDIR\examples\*" # Delete the install directory and its sub-directories. RMDir "$INSTDIR\decoders" RMDir "$INSTDIR\firmware" RMDir "$INSTDIR\examples" RMDir "$INSTDIR" # Delete the links from the start menu. Delete "$SMPROGRAMS\sigrok\PulseView\PulseView.lnk" Delete "$SMPROGRAMS\sigrok\PulseView\Uninstall.lnk" # Delete the sub-directory in the start menu. RMDir "$SMPROGRAMS\sigrok\PulseView" RMDir "$SMPROGRAMS\sigrok" # Delete the registry key(s). DeleteRegKey HKLM "${REGSTR}" SectionEnd # --- Component selection section descriptions -------------------------------- LangString DESC_Section1 ${LANG_ENGLISH} "This installs the PulseView sigrok GUI, some firmware files, the protocol decoders, some example files, and all required libraries." !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${Section1} $(DESC_Section1) !insertmacro MUI_FUNCTION_DESCRIPTION_END pulseview-0.2.0/test/data/analogsnapshot.cpp000600 001750 001750 00000006773 12332253627 020625 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../pv/data/analogsnapshot.h" using pv::data::AnalogSnapshot; BOOST_AUTO_TEST_SUITE(AnalogSnapshotTest) void push_analog(AnalogSnapshot &s, unsigned int num_samples, float value) { float *const data = new float[num_samples]; for (unsigned int i = 0; i < num_samples; i++) data[i] = value; s.append_interleaved_samples(data, num_samples, 1); delete[] data; } BOOST_AUTO_TEST_CASE(Basic) { // Create an empty AnalogSnapshot object AnalogSnapshot s; //----- Test AnalogSnapshot::push_analog -----// BOOST_CHECK(s.get_sample_count() == 0); for (unsigned int i = 0; i < AnalogSnapshot::ScaleStepCount; i++) { const AnalogSnapshot::Envelope &m = s._envelope_levels[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.samples == NULL); } // Push 8 samples of all zeros push_analog(s, 8, 0.0f); BOOST_CHECK(s.get_sample_count() == 8); // There should not be enough samples to have a single mip map sample for (unsigned int i = 0; i < AnalogSnapshot::ScaleStepCount; i++) { const AnalogSnapshot::Envelope &m = s._envelope_levels[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.samples == NULL); } // Push 8 samples of 1.0s to bring the total up to 16 push_analog(s, 8, 1.0f); // There should now be enough data for exactly one sample // in mip map level 0, and that sample should be 0 const AnalogSnapshot::Envelope &e0 = s._envelope_levels[0]; BOOST_CHECK_EQUAL(e0.length, 1); BOOST_CHECK_EQUAL(e0.data_length, AnalogSnapshot::EnvelopeDataUnit); BOOST_REQUIRE(e0.samples != NULL); BOOST_CHECK_EQUAL(e0.samples[0].min, 0.0f); BOOST_CHECK_EQUAL(e0.samples[0].max, 1.0f); // The higher levels should still be empty for (unsigned int i = 1; i < AnalogSnapshot::ScaleStepCount; i++) { const AnalogSnapshot::Envelope &m = s._envelope_levels[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.samples == NULL); } // Push 240 samples of all zeros to bring the total up to 256 push_analog(s, 240, -1.0f); BOOST_CHECK_EQUAL(e0.length, 16); BOOST_CHECK_EQUAL(e0.data_length, AnalogSnapshot::EnvelopeDataUnit); for (unsigned int i = 1; i < e0.length; i++) { BOOST_CHECK_EQUAL(e0.samples[i].min, -1.0f); BOOST_CHECK_EQUAL(e0.samples[i].max, -1.0f); } const AnalogSnapshot::Envelope &e1 = s._envelope_levels[1]; BOOST_CHECK_EQUAL(e1.length, 1); BOOST_CHECK_EQUAL(e1.data_length, AnalogSnapshot::EnvelopeDataUnit); BOOST_REQUIRE(e1.samples != NULL); BOOST_CHECK_EQUAL(e1.samples[0].min, -1.0f); BOOST_CHECK_EQUAL(e1.samples[0].max, 1.0f); } BOOST_AUTO_TEST_SUITE_END() pulseview-0.2.0/test/data/logicsnapshot.cpp000600 001750 001750 00000035323 12332253627 020452 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "../../pv/data/logicsnapshot.h" using pv::data::LogicSnapshot; using std::vector; BOOST_AUTO_TEST_SUITE(LogicSnapshotTest) void push_logic(LogicSnapshot &s, unsigned int length, uint8_t value) { sr_datafeed_logic logic; logic.unitsize = 1; logic.length = length; logic.data = new uint8_t[length]; memset(logic.data, value, length * logic.unitsize); s.append_payload(logic); delete[] (uint8_t*)logic.data; } BOOST_AUTO_TEST_CASE(Pow2) { BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(0, 0), 0); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(1, 0), 1); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(2, 0), 2); BOOST_CHECK_EQUAL( LogicSnapshot::pow2_ceil(INT64_MIN, 0), INT64_MIN); BOOST_CHECK_EQUAL( LogicSnapshot::pow2_ceil(INT64_MAX, 0), INT64_MAX); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(0, 1), 0); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(1, 1), 2); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(2, 1), 2); BOOST_CHECK_EQUAL(LogicSnapshot::pow2_ceil(3, 1), 4); } BOOST_AUTO_TEST_CASE(Basic) { // Create an empty LogicSnapshot object sr_datafeed_logic logic; logic.length = 0; logic.unitsize = 1; logic.data = NULL; LogicSnapshot s(logic); //----- Test LogicSnapshot::push_logic -----// BOOST_CHECK(s.get_sample_count() == 0); for (unsigned int i = 0; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } // Push 8 samples of all zeros push_logic(s, 8, 0); BOOST_CHECK(s.get_sample_count() == 8); // There should not be enough samples to have a single mip map sample for (unsigned int i = 0; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } // Push 8 samples of 0x11s to bring the total up to 16 push_logic(s, 8, 0x11); // There should now be enough data for exactly one sample // in mip map level 0, and that sample should be 0 const LogicSnapshot::MipMapLevel &m0 = s._mip_map[0]; BOOST_CHECK_EQUAL(m0.length, 1); BOOST_CHECK_EQUAL(m0.data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(m0.data != NULL); BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[0], 0x11); // The higher levels should still be empty for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } // Push 240 samples of all zeros to bring the total up to 256 push_logic(s, 240, 0); BOOST_CHECK_EQUAL(m0.length, 16); BOOST_CHECK_EQUAL(m0.data_length, LogicSnapshot::MipMapDataUnit); BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[1], 0x11); for (unsigned int i = 2; i < m0.length; i++) BOOST_CHECK_EQUAL(((uint8_t*)m0.data)[i], 0); const LogicSnapshot::MipMapLevel &m1 = s._mip_map[1]; BOOST_CHECK_EQUAL(m1.length, 1); BOOST_CHECK_EQUAL(m1.data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(m1.data != NULL); BOOST_CHECK_EQUAL(((uint8_t*)m1.data)[0], 0x11); //----- Test LogicSnapshot::get_subsampled_edges -----// // Test a full view at full zoom. vector edges; s.get_subsampled_edges(edges, 0, 255, 1, 0); BOOST_REQUIRE_EQUAL(edges.size(), 4); BOOST_CHECK_EQUAL(edges[0].first, 0); BOOST_CHECK_EQUAL(edges[1].first, 8); BOOST_CHECK_EQUAL(edges[2].first, 16); BOOST_CHECK_EQUAL(edges[3].first, 256); // Test a subset at high zoom edges.clear(); s.get_subsampled_edges(edges, 6, 17, 0.05f, 0); BOOST_REQUIRE_EQUAL(edges.size(), 4); BOOST_CHECK_EQUAL(edges[0].first, 6); BOOST_CHECK_EQUAL(edges[1].first, 8); BOOST_CHECK_EQUAL(edges[2].first, 16); BOOST_CHECK_EQUAL(edges[3].first, 18); } BOOST_AUTO_TEST_CASE(LargeData) { uint8_t prev_sample; const unsigned int Length = 1000000; sr_datafeed_logic logic; logic.unitsize = 1; logic.length = Length; logic.data = new uint8_t[Length]; uint8_t *data = (uint8_t*)logic.data; for (unsigned int i = 0; i < Length; i++) *data++ = (uint8_t)(i >> 8); LogicSnapshot s(logic); delete[] (uint8_t*)logic.data; BOOST_CHECK(s.get_sample_count() == Length); // Check mip map level 0 BOOST_CHECK_EQUAL(s._mip_map[0].length, 62500); BOOST_CHECK_EQUAL(s._mip_map[0].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[0].data != NULL); prev_sample = 0; for (unsigned int i = 0; i < s._mip_map[0].length;) { BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]"); const uint8_t sample = (uint8_t)((i*16) >> 8); BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, prev_sample ^ sample); prev_sample = sample; for (int j = 1; i < s._mip_map[0].length && j < 16; j++) { BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]"); BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0); } } // Check mip map level 1 BOOST_CHECK_EQUAL(s._mip_map[1].length, 3906); BOOST_CHECK_EQUAL(s._mip_map[1].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[1].data != NULL); prev_sample = 0; for (unsigned int i = 0; i < s._mip_map[1].length; i++) { BOOST_TEST_MESSAGE("Testing mip_map[1].data[" << i << "]"); const uint8_t sample = i; const uint8_t expected = sample ^ prev_sample; prev_sample = i; BOOST_CHECK_EQUAL(s.get_subsample(1, i) & 0xFF, expected); } // Check mip map level 2 BOOST_CHECK_EQUAL(s._mip_map[2].length, 244); BOOST_CHECK_EQUAL(s._mip_map[2].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[2].data != NULL); prev_sample = 0; for (unsigned int i = 0; i < s._mip_map[2].length; i++) { BOOST_TEST_MESSAGE("Testing mip_map[2].data[" << i << "]"); const uint8_t sample = i << 4; const uint8_t expected = (sample ^ prev_sample) | 0x0F; prev_sample = sample; BOOST_CHECK_EQUAL(s.get_subsample(2, i) & 0xFF, expected); } // Check mip map level 3 BOOST_CHECK_EQUAL(s._mip_map[3].length, 15); BOOST_CHECK_EQUAL(s._mip_map[3].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[3].data != NULL); for (unsigned int i = 0; i < s._mip_map[3].length; i++) BOOST_CHECK_EQUAL(*((uint8_t*)s._mip_map[3].data + i), 0xFF); // Check the higher levels for (unsigned int i = 4; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } //----- Test LogicSnapshot::get_subsampled_edges -----// // Check in normal case vector edges; s.get_subsampled_edges(edges, 0, Length-1, 1, 7); BOOST_CHECK_EQUAL(edges.size(), 32); for (unsigned int i = 0; i < edges.size() - 1; i++) { BOOST_CHECK_EQUAL(edges[i].first, i * 32768); BOOST_CHECK_EQUAL(edges[i].second, i & 1); } BOOST_CHECK_EQUAL(edges[31].first, 1000000); // Check in very low zoom case edges.clear(); s.get_subsampled_edges(edges, 0, Length-1, 50e6f, 7); BOOST_CHECK_EQUAL(edges.size(), 2); } BOOST_AUTO_TEST_CASE(Pulses) { const int Cycles = 3; const int Period = 64; const int Length = Cycles * Period; vector edges; //----- Create a LogicSnapshot -----// sr_datafeed_logic logic; logic.unitsize = 1; logic.length = Length; logic.data = (uint64_t*)new uint8_t[Length]; uint8_t *p = (uint8_t*)logic.data; for (int i = 0; i < Cycles; i++) { *p++ = 0xFF; for (int j = 1; j < Period; j++) *p++ = 0x00; } LogicSnapshot s(logic); delete[] (uint8_t*)logic.data; //----- Check the mip-map -----// // Check mip map level 0 BOOST_CHECK_EQUAL(s._mip_map[0].length, 12); BOOST_CHECK_EQUAL(s._mip_map[0].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[0].data != NULL); for (unsigned int i = 0; i < s._mip_map[0].length;) { BOOST_TEST_MESSAGE("Testing mip_map[0].data[" << i << "]"); BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0xFF); for (int j = 1; i < s._mip_map[0].length && j < Period/LogicSnapshot::MipMapScaleFactor; j++) { BOOST_TEST_MESSAGE( "Testing mip_map[0].data[" << i << "]"); BOOST_CHECK_EQUAL(s.get_subsample(0, i++) & 0xFF, 0x00); } } // Check the higher levels are all inactive for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } //----- Test get_subsampled_edges at reduced scale -----// s.get_subsampled_edges(edges, 0, Length-1, 16.0f, 2); BOOST_REQUIRE_EQUAL(edges.size(), Cycles + 2); BOOST_CHECK_EQUAL(0, false); for (unsigned int i = 1; i < edges.size(); i++) BOOST_CHECK_EQUAL(edges[i].second, false); } BOOST_AUTO_TEST_CASE(LongPulses) { const int Cycles = 3; const int Period = 64; const int PulseWidth = 16; const int Length = Cycles * Period; int j; vector edges; //----- Create a LogicSnapshot -----// sr_datafeed_logic logic; logic.unitsize = 8; logic.length = Length * 8; logic.data = (uint64_t*)new uint64_t[Length]; uint64_t *p = (uint64_t*)logic.data; for (int i = 0; i < Cycles; i++) { for (j = 0; j < PulseWidth; j++) *p++ = ~0; for (; j < Period; j++) *p++ = 0; } LogicSnapshot s(logic); delete[] (uint64_t*)logic.data; //----- Check the mip-map -----// // Check mip map level 0 BOOST_CHECK_EQUAL(s._mip_map[0].length, 12); BOOST_CHECK_EQUAL(s._mip_map[0].data_length, LogicSnapshot::MipMapDataUnit); BOOST_REQUIRE(s._mip_map[0].data != NULL); for (unsigned int i = 0; i < s._mip_map[0].length;) { for (j = 0; i < s._mip_map[0].length && j < 2; j++) { BOOST_TEST_MESSAGE( "Testing mip_map[0].data[" << i << "]"); BOOST_CHECK_EQUAL(s.get_subsample(0, i++), ~0); } for (; i < s._mip_map[0].length && j < Period/LogicSnapshot::MipMapScaleFactor; j++) { BOOST_TEST_MESSAGE( "Testing mip_map[0].data[" << i << "]"); BOOST_CHECK_EQUAL(s.get_subsample(0, i++), 0); } } // Check the higher levels are all inactive for (unsigned int i = 1; i < LogicSnapshot::ScaleStepCount; i++) { const LogicSnapshot::MipMapLevel &m = s._mip_map[i]; BOOST_CHECK_EQUAL(m.length, 0); BOOST_CHECK_EQUAL(m.data_length, 0); BOOST_CHECK(m.data == NULL); } //----- Test get_subsampled_edges at a full scale -----// s.get_subsampled_edges(edges, 0, Length-1, 16.0f, 2); BOOST_REQUIRE_EQUAL(edges.size(), Cycles * 2 + 1); for (int i = 0; i < Cycles; i++) { BOOST_CHECK_EQUAL(edges[i*2].first, i * Period); BOOST_CHECK_EQUAL(edges[i*2].second, true); BOOST_CHECK_EQUAL(edges[i*2+1].first, i * Period + PulseWidth); BOOST_CHECK_EQUAL(edges[i*2+1].second, false); } BOOST_CHECK_EQUAL(edges.back().first, Length); BOOST_CHECK_EQUAL(edges.back().second, false); //----- Test get_subsampled_edges at a simplified scale -----// edges.clear(); s.get_subsampled_edges(edges, 0, Length-1, 17.0f, 2); BOOST_CHECK_EQUAL(edges[0].first, 0); BOOST_CHECK_EQUAL(edges[0].second, true); BOOST_CHECK_EQUAL(edges[1].first, 16); BOOST_CHECK_EQUAL(edges[1].second, false); for (int i = 1; i < Cycles; i++) { BOOST_CHECK_EQUAL(edges[i+1].first, i * Period); BOOST_CHECK_EQUAL(edges[i+1].second, false); } BOOST_CHECK_EQUAL(edges.back().first, Length); BOOST_CHECK_EQUAL(edges.back().second, false); } BOOST_AUTO_TEST_CASE(LisaMUsbHid) { /* This test was created from the beginning of the USB_DM signal in * sigrok-dumps-usb/lisa_m_usbhid/lisa_m_usbhid.sr */ const int Edges[] = { 7028, 7033, 7036, 7041, 7044, 7049, 7053, 7066, 7073, 7079, 7086, 7095, 7103, 7108, 7111, 7116, 7119, 7124, 7136, 7141, 7148, 7162, 7500 }; const int Length = Edges[countof(Edges) - 1]; bool state = false; int lastEdgePos = 0; //----- Create a LogicSnapshot -----// sr_datafeed_logic logic; logic.unitsize = 1; logic.length = Length; logic.data = new uint8_t[Length]; uint8_t *data = (uint8_t*)logic.data; for (unsigned int i = 0; i < countof(Edges); i++) { const int edgePos = Edges[i]; memset(&data[lastEdgePos], state ? 0x02 : 0, edgePos - lastEdgePos - 1); lastEdgePos = edgePos; state = !state; } LogicSnapshot s(logic); delete[] (uint64_t*)logic.data; vector edges; /* The trailing edge of the pulse train is falling in the source data. * Check this is always true at different scales */ edges.clear(); s.get_subsampled_edges(edges, 0, Length-1, 33.333332f, 1); BOOST_CHECK_EQUAL(edges[edges.size() - 2].second, false); } /* * This test checks the rendering of wide data (more than 8 probes) * Probe signals are either all-high, or all-low, but are interleaved such that * they would toggle during every sample if treated like 8 probes. * The packet contains a large number of samples, so the mipmap generation kicks * in. * * The signals should not toggle (have exactly two edges: the start and end) */ BOOST_AUTO_TEST_CASE(WideData) { const int Length = 512<<10; uint16_t *data = new uint16_t[Length]; sr_datafeed_logic logic; logic.unitsize = sizeof(data[0]); logic.length = Length * sizeof(data[0]); logic.data = data; for (int i = 0; i < Length; i++) data[i] = 0x0FF0; LogicSnapshot s(logic); vector edges; edges.clear(); s.get_subsampled_edges(edges, 0, Length-1, 1, 0); BOOST_CHECK_EQUAL(edges.size(), 2); edges.clear(); s.get_subsampled_edges(edges, 0, Length-1, 1, 8); BOOST_CHECK_EQUAL(edges.size(), 2); // Cleanup delete [] data; } /* * This test is a replica of sixteen.sr attached to Bug #33. */ BOOST_AUTO_TEST_CASE(Sixteen) { const int Length = 8; uint16_t data[Length]; sr_datafeed_logic logic; logic.unitsize = sizeof(data[0]); logic.length = Length * sizeof(data[0]); logic.data = data; for (int i = 0; i < Length; i++) data[i] = 0xFFFE; LogicSnapshot s(logic); vector edges; s.get_subsampled_edges(edges, 0, 2, 0.0004, 1); BOOST_CHECK_EQUAL(edges.size(), 2); } BOOST_AUTO_TEST_SUITE_END() pulseview-0.2.0/test/data/decoderstack.cpp000600 001750 001750 00000004502 12332253627 020223 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include /* First, so we avoid a _POSIX_C_SOURCE warning. */ #include #include #include "../../pv/data/decoderstack.h" #include "../../pv/devicemanager.h" #include "../../pv/sigsession.h" #include "../../pv/view/decodetrace.h" using boost::shared_ptr; using pv::data::DecoderStack; using pv::data::decode::Decoder; using pv::view::DecodeTrace; using std::vector; BOOST_AUTO_TEST_SUITE(DecoderStackTest) BOOST_AUTO_TEST_CASE(TwoDecoderStack) { sr_context *ctx = NULL; BOOST_REQUIRE(sr_init(&ctx) == SR_OK); BOOST_REQUIRE(ctx); BOOST_REQUIRE(srd_init(NULL) == SRD_OK); srd_decoder_load_all(); { pv::DeviceManager dm(ctx); pv::SigSession ss(dm); const GSList *l = srd_decoder_list(); BOOST_REQUIRE(l); srd_decoder *const dec = (struct srd_decoder*)l->data; BOOST_REQUIRE(dec); ss.add_decoder(dec); ss.add_decoder(dec); // Check the signals were created const vector< shared_ptr > sigs = ss.get_decode_signals(); shared_ptr dec0 = sigs[0]->decoder(); BOOST_REQUIRE(dec0); shared_ptr dec1 = sigs[0]->decoder(); BOOST_REQUIRE(dec1); // Wait for the decode threads to complete dec0->_decode_thread.join(); dec1->_decode_thread.join(); // Check there were no errors BOOST_CHECK_EQUAL(dec0->error_message().isEmpty(), true); BOOST_CHECK_EQUAL(dec1->error_message().isEmpty(), true); } srd_exit(); sr_exit(ctx); } BOOST_AUTO_TEST_SUITE_END() pulseview-0.2.0/test/test.cpp000600 001750 001750 00000001603 12332253627 015635 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define BOOST_TEST_MAIN #include pulseview-0.2.0/test/CMakeLists.txt000600 001750 001750 00000013566 12332253627 016725 0ustar00uweuwe000000 000000 ## ## This file is part of the PulseView project. ## ## Copyright (C) 2012 Joel Holdsworth ## Copyright (C) 2012 Alexandru Gagniuc ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## option(ENABLE_DECODE "Build with libsigrokdecode" FALSE) list(APPEND PKGDEPS libsigrok>=0.3.0) if(ENABLE_DECODE) list(APPEND PKGDEPS libsigrokdecode>=0.3.0) endif() find_package(PkgConfig) pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS}) # Find the platform's thread library (needed for boost-thread). # This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value. find_package(Threads) if(WIN32) # On Windows/MinGW we need to use 'thread_win32' instead of 'thread'. # The library is named libboost_thread_win32* (not libboost_thread*). find_package(Boost 1.42 COMPONENTS filesystem system thread_win32 unit_test_framework REQUIRED) else() find_package(Boost 1.42 COMPONENTS filesystem system thread unit_test_framework REQUIRED) endif() find_program(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 qmake-mac) find_package(Qt4 REQUIRED) set(pulseview_TEST_SOURCES ${PROJECT_SOURCE_DIR}/pv/devicemanager.cpp ${PROJECT_SOURCE_DIR}/pv/sigsession.cpp ${PROJECT_SOURCE_DIR}/pv/view/cursorpair.cpp ${PROJECT_SOURCE_DIR}/pv/data/analog.cpp ${PROJECT_SOURCE_DIR}/pv/data/analogsnapshot.cpp ${PROJECT_SOURCE_DIR}/pv/data/logic.cpp ${PROJECT_SOURCE_DIR}/pv/data/logicsnapshot.cpp ${PROJECT_SOURCE_DIR}/pv/data/snapshot.cpp ${PROJECT_SOURCE_DIR}/pv/data/signaldata.cpp ${PROJECT_SOURCE_DIR}/pv/device/device.cpp ${PROJECT_SOURCE_DIR}/pv/device/devinst.cpp ${PROJECT_SOURCE_DIR}/pv/device/file.cpp ${PROJECT_SOURCE_DIR}/pv/device/inputfile.cpp ${PROJECT_SOURCE_DIR}/pv/device/sessionfile.cpp ${PROJECT_SOURCE_DIR}/pv/prop/double.cpp ${PROJECT_SOURCE_DIR}/pv/prop/enum.cpp ${PROJECT_SOURCE_DIR}/pv/prop/int.cpp ${PROJECT_SOURCE_DIR}/pv/prop/property.cpp ${PROJECT_SOURCE_DIR}/pv/prop/string.cpp ${PROJECT_SOURCE_DIR}/pv/prop/binding/binding.cpp ${PROJECT_SOURCE_DIR}/pv/view/analogsignal.cpp ${PROJECT_SOURCE_DIR}/pv/view/cursor.cpp ${PROJECT_SOURCE_DIR}/pv/view/cursorpair.cpp ${PROJECT_SOURCE_DIR}/pv/view/header.cpp ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.cpp ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.cpp ${PROJECT_SOURCE_DIR}/pv/view/ruler.cpp ${PROJECT_SOURCE_DIR}/pv/view/selectableitem.cpp ${PROJECT_SOURCE_DIR}/pv/view/signal.cpp ${PROJECT_SOURCE_DIR}/pv/view/timemarker.cpp ${PROJECT_SOURCE_DIR}/pv/view/trace.cpp ${PROJECT_SOURCE_DIR}/pv/view/tracepalette.cpp ${PROJECT_SOURCE_DIR}/pv/view/view.cpp ${PROJECT_SOURCE_DIR}/pv/view/viewport.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/colourpopup.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/popup.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/wellarray.cpp data/analogsnapshot.cpp data/logicsnapshot.cpp test.cpp ) # This list includes only QObject derived class headers. set(pulseview_TEST_HEADERS ${PROJECT_SOURCE_DIR}/pv/sigsession.h ${PROJECT_SOURCE_DIR}/pv/device/devinst.h ${PROJECT_SOURCE_DIR}/pv/prop/double.h ${PROJECT_SOURCE_DIR}/pv/prop/enum.h ${PROJECT_SOURCE_DIR}/pv/prop/int.h ${PROJECT_SOURCE_DIR}/pv/prop/property.h ${PROJECT_SOURCE_DIR}/pv/prop/string.h ${PROJECT_SOURCE_DIR}/pv/view/cursor.h ${PROJECT_SOURCE_DIR}/pv/view/header.h ${PROJECT_SOURCE_DIR}/pv/view/logicsignal.h ${PROJECT_SOURCE_DIR}/pv/view/marginwidget.h ${PROJECT_SOURCE_DIR}/pv/view/ruler.h ${PROJECT_SOURCE_DIR}/pv/view/selectableitem.h ${PROJECT_SOURCE_DIR}/pv/view/signal.h ${PROJECT_SOURCE_DIR}/pv/view/timemarker.h ${PROJECT_SOURCE_DIR}/pv/view/trace.h ${PROJECT_SOURCE_DIR}/pv/view/view.h ${PROJECT_SOURCE_DIR}/pv/view/viewport.h ${PROJECT_SOURCE_DIR}/pv/widgets/colourbutton.h ${PROJECT_SOURCE_DIR}/pv/widgets/colourpopup.h ${PROJECT_SOURCE_DIR}/pv/widgets/popup.h ${PROJECT_SOURCE_DIR}/pv/widgets/wellarray.h ) if(ENABLE_DECODE) list(APPEND pulseview_TEST_SOURCES ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.cpp ${PROJECT_SOURCE_DIR}/pv/data/decode/annotation.cpp ${PROJECT_SOURCE_DIR}/pv/data/decode/decoder.cpp ${PROJECT_SOURCE_DIR}/pv/data/decode/row.cpp ${PROJECT_SOURCE_DIR}/pv/data/decode/rowdata.cpp ${PROJECT_SOURCE_DIR}/pv/prop/binding/decoderoptions.cpp ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.cpp ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.cpp data/decoderstack.cpp ) list(APPEND pulseview_TEST_HEADERS ${PROJECT_SOURCE_DIR}/pv/data/decoderstack.h ${PROJECT_SOURCE_DIR}/pv/view/decodetrace.h ${PROJECT_SOURCE_DIR}/pv/widgets/decodergroupbox.h ${PROJECT_SOURCE_DIR}/pv/widgets/decodermenu.h ) endif() qt4_wrap_cpp(pulseview_TEST_HEADERS_MOC ${pulseview_TEST_HEADERS}) if(ENABLE_DECODE) add_definitions(-DENABLE_DECODE) endif() # On MinGW we need to use static linking. if(NOT WIN32) add_definitions(-DBOOST_TEST_DYN_LINK) endif() add_definitions(${QT_DEFINITIONS}) include_directories( ${Boost_INCLUDE_DIRS} ${PKGDEPS_INCLUDE_DIRS} ) set(PULSEVIEW_LINK_LIBS ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${PKGDEPS_LIBRARIES} ${QT_LIBRARIES} ) if(WIN32) # Workaround for a MinGW linking issue. list(APPEND PULSEVIEW_LINK_LIBS "-llzma -llcms2") endif() add_executable(pulseview-test ${pulseview_TEST_SOURCES} ${pulseview_TEST_HEADERS_MOC} ) target_link_libraries(pulseview-test ${PULSEVIEW_LINK_LIBS}) pulseview-0.2.0/README000600 001750 001750 00000003435 12332253627 014060 0ustar00uweuwe000000 000000 ------------------------------------------------------------------------------- README ------------------------------------------------------------------------------- The sigrok project aims at creating a portable, cross-platform, Free/Libre/Open-Source signal analysis software suite that supports various device types (such as logic analyzers, oscilloscopes, multimeters, and more). PulseView is a Qt-based LA/scope/MSO GUI for sigrok. Status ------ PulseView is in a usable state and has had official tarball releases. However, it is still work in progress. Some basic functionality is available and working, but other things are still on the TODO list. Copyright and license --------------------- PulseView is licensed under the terms of the GNU General Public License (GPL), version 3 or later. While some individual source code files are licensed under the GPLv2+, and some files are licensed under the GPLv3+, this doesn't change the fact that the program as a whole is licensed under the terms of the GPLv3+ (e.g. also due to the fact that it links against GPLv3+ libraries). Please see the individual source files for the full list of copyright holders. Copyright notices ----------------- A copyright notice indicating a range of years, must be interpreted as having had copyrightable material added in each of those years. Example: Copyright (C) 2010-2013 Contributor Name is to be interpreted as Copyright (C) 2010,2011,2012,2013 Contributor Name Mailing lists ------------- There are two mailing lists for sigrok/PulseView: https://lists.sourceforge.net/lists/listinfo/sigrok-devel https://lists.sourceforge.net/lists/listinfo/sigrok-commits IRC --- You can find the sigrok developers in the #sigrok IRC channel on Freenode. Website ------- http://sigrok.org/wiki/PulseView pulseview-0.2.0/HACKING000600 001750 001750 00000005747 12332253627 014177 0ustar00uweuwe000000 000000 ------------------------------------------------------------------------------- HACKING ------------------------------------------------------------------------------- Coding style ------------ This project is programmed using the Linux kernel coding style, see http://lxr.linux.no/linux/Documentation/CodingStyle for details. Please use the same style for any code contributions, thanks! In some exceptional cases deviations from the above coding guidelines are OK (in order to meet Qt/C++ related guidelines, for example). Contributions ------------- - Patches should be sent to the development mailinglist at sigrok-devel@lists.sourceforge.net (please subscribe to the list first). https://lists.sourceforge.net/lists/listinfo/sigrok-devel - Alternatively, you can also clone the git repository and let us know from where to pull/review your changes. You can use gitorious.org, github.com, or any other public git hosting site. Random notes ------------ - Consistently use g_try_malloc() / g_try_malloc0(). Do not use standard malloc()/calloc() if it can be avoided (sometimes other libs such as libftdi can return malloc()'d memory, for example). - Always properly match allocations with the proper *free() functions. If glib's g_try_malloc()/g_try_malloc0() was used, use g_free() to free the memory. Otherwise use standard free(). Never use the wrong function! - Never use g_malloc() or g_malloc0(). These functions do not return NULL if not enough memory is available but rather lead to an exit() or segfault instead. This behaviour is not acceptable. Use g_try_malloc()/g_try_malloc0() instead and check the return value. - Use glib's gboolean / TRUE / FALSE for boolean types consistently. Do not use and its true / false, and do not invent private definitions for this either. - Consistently use the same naming convention for #include guards in headers: __ This ensures that all #include guards are always unique and consistent. Example: PULSEVIEW_PV_VIEW_RULER_H - Consistently use the same naming convention for functions, if appropriate: Getter/setter function names should usually end with "_get" or "_set". Functions creating new "objects" should end with "_new". Functions destroying "objects" should end with "_destroy". Functions adding or removing items (e.g. from lists) should end with either "_add" or "_remove". Functions operating on all items from a list (not on only one of them), should end with "_all", e.g. "_remove_all", "_get_all", and so on. Use "_remove_all" in favor of "_clear" for consistency. - In Doxygen comments, put an empty line between the block of @param lines and the final @return line. The @param lines themselves (if there is more than one) are not separated by empty lines. Release engineering ------------------- See http://sigrok.org/wiki/Developers/Release_process for a list of items that need to be done when releasing a new tarball. pulseview-0.2.0/pulseviewico.rc000600 001750 001750 00000000072 12332253627 016236 0ustar00uweuwe000000 000000 IDI_ICON1 ICON DISCARDABLE "icons/sigrok-logo-notext.ico" pulseview-0.2.0/signalhandler.h000600 001750 001750 00000002456 12332253627 016166 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Adam Reichold * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef SIGNALHANDLER_H #define SIGNALHANDLER_H #include class QSocketNotifier; class SignalHandler : public QObject { Q_OBJECT public: static bool prepare_signals(); public: explicit SignalHandler(QObject* parent = NULL); signals: void int_received(); void term_received(); private slots: void on_socket_notifier_activated(); private: static void handle_signals(int sig_number); private: QSocketNotifier* _socket_notifier; private: static int _sockets[2]; }; #endif // SIGNALHANDLER_H pulseview-0.2.0/NEWS000600 001750 001750 00000013753 12332253627 013703 0ustar00uweuwe000000 000000 0.2.0 (2014-05-06) ------------------ * Add support for protocol decoding. - A menu item Decoders->Add can add (only) low-level protocol decoders. - Clicking on a decoder's arrow on the left-hand side allows stacking (only) further, higher-level decoders (bug #333). - Add support for multiple decoder rows per PD (bugs #161, #303). * Add support for loading data from input files. * Add support for saving logic analyzer data in *.sr files (bug #245). * Show the filename of loaded *.sr files as "device name" in a drop-down. * Add support for the new "channel groups" feature in libsigrok. * Add a "Zoom to fit" and a "Zoom 1:1" icon/button. * Add a "Channels" icon/button for enabling and configuring device channels. * Add a "Device options" icon/button for configuring device specific settings such as voltage thresholds. * Double-clicking will zoom into the location the cursor points to. * Add shortcut keys for Zoom-in (CTRL++) and Zoom-out (Ctrl+-) (bug #235). * Add initial, basic support for analog data sources, such as oscilloscopes. This has been tested on e.g. the Rigol DS1052E oscilloscope (bug #127). * Almost all operations in PulseView work in an "auto-apply" style, i.e. any changes in the GUI popups (decoder channel assignment, decoder option changes, device channel changes, device option changes, and so on) have an immediate effect (you don't have to click on any "OK" buttons). * Open file dialog: Add 'All files' selection possibility. * Added GUI support for the following libsigrok config options: - SR_CONF_VOLTAGE_THRESHOLD - SR_CONF_TRIGGER_SLOPE - SR_CONF_EXTERNAL_CLOCK - SR_CONF_CLOCK_EDGE * Updated build requirements: - cmake >= 2.8.6 (required) - libsigrok >= 0.3.0 (required) - libsigrokdecode >= 0.3.0 (required) Note: libsigrokdecode is now always required (was optional before). - libboost-filesystem >= 1.42 (required) - libboost-test >= 1.42 (optional, only needed for running unit tests) * Fix a build issue on NetBSD due to an incorrect #include. * Fix a channel handling issue for devices with >= 16 channels. * Fixed a few display issues related to UTF-8 strings in decoder annotations, decoder options, and so on (bug #307, and other fixes without bug numbers). * Don't decode if required PD channels were not supplied (bug #204). * Fix some issues with partially disappearing PD annotations. * Update the samplerate selector when the device config changes (since e.g. en-/disabling logic analyzer channels can change available samplerates). * Fix a samplerate selector and device config update issue (bug #296). * Minor performance improvements via memory pre-allocation in some areas. * Remove old PD annotations upon new captures (bugs #172, #302). * Portability improvements by using portable sample pack/unpack code. * Fix a segfault related to thread joining (bug #323). * Fix a minor issue which lead to trigger config being unusable (bug #318). * Fix an issue which lead to the user-selected samplerate in the drop-down box not being selected and shown again after clicking "Run" (bug #324). * Start a new decode session whenever a new frame begins. * Fix an issue which arose when a session file load failed (bug #331). * Fix a segfault if only exactly one libsigrok driver is available (bug #334). * Fix an issue with channel names not being updated while typing (bug #338). * Fix an issue by stopping the capture when a device is changed (bug #223). * Fix an issue with the displayed cursor popup time (bug #229). * Fix handling of different integer types in some properties (bug #203). * Hide the 'Configure device' button when the popup would be empty (bug #232). * Fix a decoding chunk buffer / unitsize issue (bugs #171, #225). * Fix a zoom issue due to invalid samplerate, assume 1Hz if needed (bug #278). * Fix a QWellArray related issue by dropping QT_NO_MENU code (bug #265). * Fix an analog data channel interleaving issue (bug #279). * Only show the sample count widget if needed (i.e., hide it for scopes). * SweepTimingWidget: Show a 1-2-5 based list for samplerate and samplecount. * Fixed a unit test issue with AnalogSnapshotTest (bug #286). * Add an extra sample to edges to make the end sample visible (bug #280). * Fix an issue with decoder errors being shown even after the error was fixed. * Add support for SR_CONF_LIMIT_SAMPLES (device-specific limits) (bug #74). * Windows: - Use the sigrok logo as icon for pulseview.exe (bugs #110, #238). - Fix SVG icons not being displayed on Windows (bug #239). - Ship libusb0.dll in the NSIS based installer (bug #241). - Additionally ship decoders, firmware files, example *.sr files, and zadig.exe and zadig_xp.exe in the NSIS based installer. - Fix a "working path" issue resulting in PDs not being usable. * Device scan: Disable "OK" button if no devices were found (bug #237). * Fix a segfault related to incorrect decoder option handling (bug #160). * Fix an off-by-one issue resulting in one PD not being usable (bug #164). * Fix a PD channel auto-select logic issue with optional channels (bug #310). * Fix an issue when cancelling "Connect to Device" (bug #242). * Avoid confusing annotation color changes (bug #311). * Fix an issue that caused some decoded data to not be shown (bug #308). * Don't allow disabled channels (channel arrows) to be selected (bug #313). * The channel name field is now resized to always fit the contents (bug #167). * Build fixes for systems with (among other versions) Qt5 installed. * Fix a build issue related to missing libboost-filesystem (bug #133). * Add Cotire (optional, disabled by default) support for build performance. * Fix a build issue related to Cotire (bug #217). * Fix an issue with missing channel names from loaded files (bug #126). * Fix missing samplerate loading from session files (bug #123). * Fix some build issues related to C++ namespaces (bug #196). * Fix a file loading issue which triggered an assert (bug #320). 0.1.0 (2013-05-04) ------------------ * Initial release. pulseview-0.2.0/CMakeLists.txt000600 001750 001750 00000025050 12332253627 015735 0ustar00uweuwe000000 000000 ## ## This file is part of the PulseView project. ## ## Copyright (C) 2012 Joel Holdsworth ## Copyright (C) 2012-2013 Alexandru Gagniuc ## ## This program is free software: you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation, either version 2 of the License, or ## (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program. If not, see . ## cmake_minimum_required(VERSION 2.8.6) include(FindPkgConfig) include(GNUInstallDirs) set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake") project(pulseview) #=============================================================================== #= User Options #------------------------------------------------------------------------------- option(DISABLE_WERROR "Build without -Werror" FALSE) option(ENABLE_SIGNALS "Build with UNIX signals" TRUE) option(ENABLE_DECODE "Build with libsigrokdecode" TRUE) option(ENABLE_COTIRE "Enable cotire" FALSE) option(ENABLE_TESTS "Enable unit tests" FALSE) option(STATIC_PKGDEPS_LIBS "Statically link to (pkg-config) libraries" FALSE) if(WIN32) # On Windows/MinGW we need to statically link to libraries. # This option is user configurable, but enable it by default on win32. set(STATIC_PKGDEPS_LIBS TRUE) # For boost-thread we need two additional settings on win32: set(Boost_USE_STATIC_LIBS ON) add_definitions(-DBOOST_THREAD_USE_LIB) # Windows does not support UNIX signals. set(ENABLE_SIGNALS FALSE) endif() if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) endif() #=============================================================================== #= Dependencies #------------------------------------------------------------------------------- list(APPEND PKGDEPS libsigrok>=0.3.0) if(ENABLE_DECODE) list(APPEND PKGDEPS libsigrokdecode>=0.3.0) endif() find_package(PkgConfig) pkg_check_modules(PKGDEPS REQUIRED ${PKGDEPS}) find_program(QT_QMAKE_EXECUTABLE NAMES qmake4 qmake-qt4 qmake-mac) find_package(Qt4 REQUIRED) # Find the platform's thread library (needed for boost-thread). # This will set ${CMAKE_THREAD_LIBS_INIT} to the correct, OS-specific value. find_package(Threads) if(WIN32) # On Windows/MinGW we need to use 'thread_win32' instead of 'thread'. # The library is named libboost_thread_win32* (not libboost_thread*). find_package(Boost 1.42 COMPONENTS filesystem system thread_win32 REQUIRED) else() find_package(Boost 1.42 COMPONENTS filesystem system thread REQUIRED) endif() #=============================================================================== #= System Introspection #------------------------------------------------------------------------------- include(memaccess) memaccess_check_unaligned_le(HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS) #=============================================================================== #= Config Header #------------------------------------------------------------------------------- set(PV_TITLE PulseView) set(PV_DESCRIPTION "A GUI for sigrok") set(PV_VERSION_MAJOR 0) set(PV_VERSION_MINOR 2) set(PV_VERSION_MICRO 0) set(PV_VERSION_STRING ${PV_VERSION_MAJOR}.${PV_VERSION_MINOR}.${PV_VERSION_MICRO} ) configure_file ( ${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_BINARY_DIR}/config.h ) #=============================================================================== #= Sources #------------------------------------------------------------------------------- set(pulseview_SOURCES main.cpp pv/devicemanager.cpp pv/mainwindow.cpp pv/sigsession.cpp pv/storesession.cpp pv/data/analog.cpp pv/data/analogsnapshot.cpp pv/data/logic.cpp pv/data/logicsnapshot.cpp pv/data/signaldata.cpp pv/data/snapshot.cpp pv/device/device.cpp pv/device/file.cpp pv/device/devinst.cpp pv/device/inputfile.cpp pv/device/sessionfile.cpp pv/dialogs/about.cpp pv/dialogs/connect.cpp pv/dialogs/storeprogress.cpp pv/popups/deviceoptions.cpp pv/popups/probes.cpp pv/prop/bool.cpp pv/prop/double.cpp pv/prop/enum.cpp pv/prop/int.cpp pv/prop/property.cpp pv/prop/string.cpp pv/prop/binding/binding.cpp pv/prop/binding/deviceoptions.cpp pv/toolbars/samplingbar.cpp pv/view/analogsignal.cpp pv/view/cursor.cpp pv/view/cursorpair.cpp pv/view/header.cpp pv/view/marginwidget.cpp pv/view/logicsignal.cpp pv/view/ruler.cpp pv/view/selectableitem.cpp pv/view/signal.cpp pv/view/timemarker.cpp pv/view/trace.cpp pv/view/tracepalette.cpp pv/view/view.cpp pv/view/viewport.cpp pv/widgets/colourbutton.cpp pv/widgets/colourpopup.cpp pv/widgets/popup.cpp pv/widgets/popuptoolbutton.cpp pv/widgets/sweeptimingwidget.cpp pv/widgets/wellarray.cpp ) # This list includes only QObject derived class headers. set(pulseview_HEADERS pv/mainwindow.h pv/sigsession.h pv/storesession.h pv/device/devinst.h pv/dialogs/about.h pv/dialogs/connect.h pv/dialogs/storeprogress.h pv/popups/probes.h pv/popups/deviceoptions.h pv/prop/bool.h pv/prop/double.h pv/prop/enum.h pv/prop/int.h pv/prop/property.h pv/prop/string.h pv/toolbars/samplingbar.h pv/view/cursor.h pv/view/header.h pv/view/logicsignal.h pv/view/marginwidget.h pv/view/ruler.h pv/view/selectableitem.h pv/view/signal.h pv/view/timemarker.h pv/view/trace.h pv/view/view.h pv/view/viewport.h pv/widgets/colourbutton.h pv/widgets/colourpopup.h pv/widgets/popup.h pv/widgets/popuptoolbutton.h pv/widgets/sweeptimingwidget.h pv/widgets/wellarray.h ) set(pulseview_FORMS pv/dialogs/about.ui ) set(pulseview_RESOURCES pulseview.qrc ) if(ENABLE_SIGNALS) list(APPEND pulseview_SOURCES signalhandler.cpp) list(APPEND pulseview_HEADERS signalhandler.h) endif() if(ENABLE_DECODE) list(APPEND pulseview_SOURCES pv/data/decoderstack.cpp pv/data/decode/annotation.cpp pv/data/decode/decoder.cpp pv/data/decode/row.cpp pv/data/decode/rowdata.cpp pv/prop/binding/decoderoptions.cpp pv/view/decodetrace.cpp pv/widgets/decodergroupbox.cpp pv/widgets/decodermenu.cpp ) list(APPEND pulseview_HEADERS pv/data/decoderstack.h pv/view/decodetrace.h pv/widgets/decodergroupbox.h pv/widgets/decodermenu.h ) endif() if(WIN32) # Use the sigrok icon for the pulseview.exe executable. set(CMAKE_RC_COMPILE_OBJECT "${CMAKE_RC_COMPILER} -O coff -I${CMAKE_CURRENT_SOURCE_DIR} ") enable_language(RC) list(APPEND pulseview_SOURCES pulseviewico.rc) endif() qt4_wrap_cpp(pulseview_HEADERS_MOC ${pulseview_HEADERS}) qt4_wrap_ui(pulseview_FORMS_HEADERS ${pulseview_FORMS}) qt4_add_resources(pulseview_RESOURCES_RCC ${pulseview_RESOURCES}) include(${QT_USE_FILE}) #=============================================================================== #= Global Definitions #------------------------------------------------------------------------------- add_definitions(${QT_DEFINITIONS}) add_definitions(-D__STDC_LIMIT_MACROS) add_definitions(-Wall -Wextra) if(ENABLE_DECODE) add_definitions(-DENABLE_DECODE) endif() if(NOT DISABLE_WERROR) add_definitions(-Werror) endif() #=============================================================================== #= Global Include Directories #------------------------------------------------------------------------------- include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIRS} ) if(STATIC_PKGDEPS_LIBS) include_directories(${PKGDEPS_STATIC_INCLUDE_DIRS}) else() include_directories(${PKGDEPS_INCLUDE_DIRS}) endif() #=============================================================================== #= Linker Configuration #------------------------------------------------------------------------------- link_directories(${Boost_LIBRARY_DIRS}) set(PULSEVIEW_LINK_LIBS ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${QT_LIBRARIES} ) if(STATIC_PKGDEPS_LIBS) link_directories(${PKGDEPS_STATIC_LIBRARY_DIRS}) list(APPEND PULSEVIEW_LINK_LIBS ${PKGDEPS_STATIC_LIBRARIES}) if(WIN32) # Workaround for a MinGW linking issue. list(APPEND PULSEVIEW_LINK_LIBS "-llzma -llcms2") endif() else() link_directories(${PKGDEPS_LIBRARY_DIRS}) list(APPEND PULSEVIEW_LINK_LIBS ${PKGDEPS_LIBRARIES}) endif() if(WIN32) # On Windows we need to statically link the libqsvg imageformat # plugin (and the QtSvg component) for SVG graphics/icons to work. add_definitions(-DQT_STATICPLUGIN) link_directories("${QT_PLUGINS_DIR}/imageformats") list(APPEND PULSEVIEW_LINK_LIBS ${QT_QTSVG_LIBRARY}) list(APPEND PULSEVIEW_LINK_LIBS "-lqsvg") endif() add_executable(${PROJECT_NAME} ${pulseview_SOURCES} ${pulseview_HEADERS_MOC} ${pulseview_FORMS_HEADERS} ${pulseview_RESOURCES_RCC} ) target_link_libraries(${PROJECT_NAME} ${PULSEVIEW_LINK_LIBS}) if(WIN32) # Pass -mwindows so that no "DOS box" opens when PulseView is started. set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "-mwindows") endif() if(ENABLE_COTIRE) include(cotire) cotire(${PROJECT_NAME}) endif() #=============================================================================== #= Installation #------------------------------------------------------------------------------- # Install the executable. install(TARGETS ${PROJECT_NAME} DESTINATION bin/) # Install the manpage. install(FILES doc/pulseview.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT doc) #=============================================================================== #= Packaging (handled by CPack) #------------------------------------------------------------------------------- set(CPACK_PACKAGE_VERSION_MAJOR ${PV_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${PV_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${PV_VERSION_MICRO}) set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_SOURCE_DIR}/README) set(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_CURRENT_SOURCE_DIR}/COPYING) set(CPACK_SOURCE_IGNORE_FILES ${CMAKE_CURRENT_BINARY_DIR} ".gitignore" ".git") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}-${PV_VERSION_MAJOR}.${PV_VERSION_MINOR}.${PV_VERSION_MICRO}") set(CPACK_SOURCE_GENERATOR "TGZ") include(CPack) #=============================================================================== #= Tests #------------------------------------------------------------------------------- if(ENABLE_TESTS) add_subdirectory(test) enable_testing() add_test(test ${CMAKE_CURRENT_BINARY_DIR}/test/pulseview-test) endif() pulseview-0.2.0/doc/pulseview.1000600 001750 001750 00000003363 12332253627 016052 0ustar00uweuwe000000 000000 .TH PULSEVIEW 1 "May 4, 2013" .SH "NAME" PulseView \- Qt-based LA/scope/MSO GUI for sigrok .SH "SYNOPSIS" .B pulseview \fR[\fB\-lh?V\fR] [\fB\-l\fR|\fB\-\-loglevel\fR] [\fB\-h\fR|\fB\-?\fR|\fB\-\-help\fR] [\fB\-V\fR|\fB\-\-version\fR] [\fBfile.sr\fR] .SH "DESCRIPTION" .B PulseView is a cross-platform Qt-based GUI for the .B sigrok software suite for test and measurement equipment such as logic analyzers, oscilloscopes, MSOs, and more. .SH "OPTIONS" .B PulseView has very few command line options, as most configuration elements are available from the GUI itself. .sp If the optional \fBfile.sr\fR argument is supplied, PulseView tries to open the specified file. It has to be in the "libsigrok session" format. .TP .B "\-l, \-\-loglevel" Set the libsigrok and libsigrokdecode loglevel. At the moment PulseView doesn't support setting the two loglevels independently. The higher the number, the more debug output will be printed. Valid loglevels are: .sp \fB0\fP None .br \fB1\fP Error .br \fB2\fP Warnings .br \fB3\fP Informational .br \fB4\fP Debug .br \fB5\fP Spew .TP .B "\-h, \-?, \-\-help" Show a help text and exit. .TP .B "\-V, \-\-version" Show version information and exit. .SH "EXIT STATUS" .B PulseView exits with 0 on success, 1 on most failures. .SH "SEE ALSO" \fBsigrok\-cli\fP(1) .SH "BUGS" Please report any bugs via Bugzilla .RB "(" http://sigrok.org/bugzilla ")" or on the sigrok\-devel mailing list .RB "(" sigrok\-devel@lists.souceforge.net ")." .SH "LICENSE" .B PulseView is covered by the GNU General Public License (GPL), version 3 or later. .SH "AUTHORS" Please see the individual source code files. .PP This manual page was written by Uwe Hermann . It is licensed under the terms of the GNU GPL (version 2 or later). pulseview-0.2.0/pv/mainwindow.cpp000600 001750 001750 00000031446 12332253627 016510 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ENABLE_DECODE #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mainwindow.h" #include "devicemanager.h" #include "device/device.h" #include "dialogs/about.h" #include "dialogs/connect.h" #include "dialogs/storeprogress.h" #include "toolbars/samplingbar.h" #include "view/logicsignal.h" #include "view/view.h" #ifdef ENABLE_DECODE #include "widgets/decodermenu.h" #endif /* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */ #define __STDC_FORMAT_MACROS #include #include #include #include #include using boost::shared_ptr; using std::list; namespace pv { namespace view { class SelectableItem; } MainWindow::MainWindow(DeviceManager &device_manager, const char *open_file_name, QWidget *parent) : QMainWindow(parent), _device_manager(device_manager), _session(device_manager) { setup_ui(); if (open_file_name) { const QString s(QString::fromUtf8(open_file_name)); QMetaObject::invokeMethod(this, "load_file", Qt::QueuedConnection, Q_ARG(QString, s)); } } void MainWindow::setup_ui() { setObjectName(QString::fromUtf8("MainWindow")); resize(1024, 768); // Set the window icon QIcon icon; icon.addFile(QString::fromUtf8(":/icons/sigrok-logo-notext.png"), QSize(), QIcon::Normal, QIcon::Off); setWindowIcon(icon); // Setup the central widget _central_widget = new QWidget(this); _vertical_layout = new QVBoxLayout(_central_widget); _vertical_layout->setSpacing(6); _vertical_layout->setContentsMargins(0, 0, 0, 0); setCentralWidget(_central_widget); _view = new pv::view::View(_session, this); _vertical_layout->addWidget(_view); // Setup the menu bar QMenuBar *const menu_bar = new QMenuBar(this); menu_bar->setGeometry(QRect(0, 0, 400, 25)); // File Menu QMenu *const menu_file = new QMenu; menu_file->setTitle(QApplication::translate( "MainWindow", "&File", 0, QApplication::UnicodeUTF8)); QAction *const action_open = new QAction(this); action_open->setText(QApplication::translate( "MainWindow", "&Open...", 0, QApplication::UnicodeUTF8)); action_open->setIcon(QIcon::fromTheme("document-open", QIcon(":/icons/document-open.png"))); action_open->setObjectName(QString::fromUtf8("actionOpen")); menu_file->addAction(action_open); QAction *const action_save_as = new QAction(this); action_save_as->setText(QApplication::translate( "MainWindow", "&Save As...", 0, QApplication::UnicodeUTF8)); action_save_as->setIcon(QIcon::fromTheme("document-save-as", QIcon(":/icons/document-save-as.png"))); action_save_as->setObjectName(QString::fromUtf8("actionSaveAs")); menu_file->addAction(action_save_as); menu_file->addSeparator(); QAction *const action_connect = new QAction(this); action_connect->setText(QApplication::translate( "MainWindow", "&Connect to Device...", 0, QApplication::UnicodeUTF8)); action_connect->setObjectName(QString::fromUtf8("actionConnect")); menu_file->addAction(action_connect); menu_file->addSeparator(); QAction *action_quit = new QAction(this); action_quit->setText(QApplication::translate( "MainWindow", "&Quit", 0, QApplication::UnicodeUTF8)); action_quit->setIcon(QIcon::fromTheme("application-exit", QIcon(":/icons/application-exit.png"))); action_quit->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); action_quit->setObjectName(QString::fromUtf8("actionQuit")); menu_file->addAction(action_quit); // View Menu QMenu *menu_view = new QMenu; menu_view->setTitle(QApplication::translate( "MainWindow", "&View", 0, QApplication::UnicodeUTF8)); QAction *const action_view_zoom_in = new QAction(this); action_view_zoom_in->setText(QApplication::translate( "MainWindow", "Zoom &In", 0, QApplication::UnicodeUTF8)); action_view_zoom_in->setIcon(QIcon::fromTheme("zoom-in", QIcon(":/icons/zoom-in.png"))); // simply using Qt::Key_Plus shows no + in the menu action_view_zoom_in->setShortcut(QKeySequence::ZoomIn); action_view_zoom_in->setObjectName( QString::fromUtf8("actionViewZoomIn")); menu_view->addAction(action_view_zoom_in); QAction *const action_view_zoom_out = new QAction(this); action_view_zoom_out->setText(QApplication::translate( "MainWindow", "Zoom &Out", 0, QApplication::UnicodeUTF8)); action_view_zoom_out->setIcon(QIcon::fromTheme("zoom-out", QIcon(":/icons/zoom-out.png"))); action_view_zoom_out->setShortcut(QKeySequence::ZoomOut); action_view_zoom_out->setObjectName( QString::fromUtf8("actionViewZoomOut")); menu_view->addAction(action_view_zoom_out); QAction *const action_view_zoom_fit = new QAction(this); action_view_zoom_fit->setText(QApplication::translate( "MainWindow", "Zoom to &Fit", 0, QApplication::UnicodeUTF8)); action_view_zoom_fit->setIcon(QIcon::fromTheme("zoom-fit", QIcon(":/icons/zoom-fit.png"))); action_view_zoom_fit->setShortcut(QKeySequence(Qt::Key_F)); action_view_zoom_fit->setObjectName( QString::fromUtf8("actionViewZoomFit")); menu_view->addAction(action_view_zoom_fit); QAction *const action_view_zoom_one_to_one = new QAction(this); action_view_zoom_one_to_one->setText(QApplication::translate( "MainWindow", "Zoom to &One-to-One", 0, QApplication::UnicodeUTF8)); action_view_zoom_one_to_one->setIcon(QIcon::fromTheme("zoom-original", QIcon(":/icons/zoom-original.png"))); action_view_zoom_one_to_one->setShortcut(QKeySequence(Qt::Key_O)); action_view_zoom_one_to_one->setObjectName( QString::fromUtf8("actionViewZoomOneToOne")); menu_view->addAction(action_view_zoom_one_to_one); menu_view->addSeparator(); QAction *action_view_show_cursors = new QAction(this); action_view_show_cursors->setCheckable(true); action_view_show_cursors->setChecked(_view->cursors_shown()); action_view_show_cursors->setShortcut(QKeySequence(Qt::Key_C)); action_view_show_cursors->setObjectName( QString::fromUtf8("actionViewShowCursors")); action_view_show_cursors->setText(QApplication::translate( "MainWindow", "Show &Cursors", 0, QApplication::UnicodeUTF8)); menu_view->addAction(action_view_show_cursors); // Decoders Menu #ifdef ENABLE_DECODE QMenu *const menu_decoders = new QMenu; menu_decoders->setTitle(QApplication::translate( "MainWindow", "&Decoders", 0, QApplication::UnicodeUTF8)); pv::widgets::DecoderMenu *const menu_decoders_add = new pv::widgets::DecoderMenu(menu_decoders, true); menu_decoders_add->setTitle(QApplication::translate( "MainWindow", "&Add", 0, QApplication::UnicodeUTF8)); connect(menu_decoders_add, SIGNAL(decoder_selected(srd_decoder*)), this, SLOT(add_decoder(srd_decoder*))); menu_decoders->addMenu(menu_decoders_add); #endif // Help Menu QMenu *const menu_help = new QMenu; menu_help->setTitle(QApplication::translate( "MainWindow", "&Help", 0, QApplication::UnicodeUTF8)); QAction *const action_about = new QAction(this); action_about->setObjectName(QString::fromUtf8("actionAbout")); action_about->setText(QApplication::translate( "MainWindow", "&About...", 0, QApplication::UnicodeUTF8)); menu_help->addAction(action_about); menu_bar->addAction(menu_file->menuAction()); menu_bar->addAction(menu_view->menuAction()); #ifdef ENABLE_DECODE menu_bar->addAction(menu_decoders->menuAction()); #endif menu_bar->addAction(menu_help->menuAction()); setMenuBar(menu_bar); QMetaObject::connectSlotsByName(this); // Setup the toolbar QToolBar *const toolbar = new QToolBar(tr("Main Toolbar"), this); toolbar->addAction(action_open); toolbar->addSeparator(); toolbar->addAction(action_view_zoom_in); toolbar->addAction(action_view_zoom_out); toolbar->addAction(action_view_zoom_fit); addToolBar(toolbar); // Setup the sampling bar _sampling_bar = new toolbars::SamplingBar(_session, this); // Populate the device list and select the initially selected device update_device_list(); connect(_sampling_bar, SIGNAL(run_stop()), this, SLOT(run_stop())); addToolBar(_sampling_bar); // Set the title setWindowTitle(QApplication::translate("MainWindow", "PulseView", 0, QApplication::UnicodeUTF8)); // Setup _session events connect(&_session, SIGNAL(capture_state_changed(int)), this, SLOT(capture_state_changed(int))); } void MainWindow::session_error( const QString text, const QString info_text) { QMetaObject::invokeMethod(this, "show_session_error", Qt::QueuedConnection, Q_ARG(QString, text), Q_ARG(QString, info_text)); } void MainWindow::update_device_list() { assert(_sampling_bar); shared_ptr selected_device = _session.get_device(); list< shared_ptr > devices; std::copy(_device_manager.devices().begin(), _device_manager.devices().end(), std::back_inserter(devices)); if (std::find(devices.begin(), devices.end(), selected_device) == devices.end()) devices.push_back(selected_device); assert(selected_device); _sampling_bar->set_device_list(devices, selected_device); } void MainWindow::load_file(QString file_name) { const QString errorMessage( QString("Failed to load file %1").arg(file_name)); const QString infoMessage; try { _session.set_file(file_name.toStdString()); } catch(QString e) { show_session_error(tr("Failed to load ") + file_name, e); _session.set_default_device(); update_device_list(); return; } update_device_list(); _session.start_capture(boost::bind(&MainWindow::session_error, this, errorMessage, infoMessage)); } void MainWindow::show_session_error( const QString text, const QString info_text) { QMessageBox msg(this); msg.setText(text); msg.setInformativeText(info_text); msg.setStandardButtons(QMessageBox::Ok); msg.setIcon(QMessageBox::Warning); msg.exec(); } void MainWindow::on_actionOpen_triggered() { // Show the dialog const QString file_name = QFileDialog::getOpenFileName( this, tr("Open File"), "", tr( "Sigrok Sessions (*.sr);;" "All Files (*.*)")); if (!file_name.isEmpty()) load_file(file_name); } void MainWindow::on_actionSaveAs_triggered() { using pv::dialogs::StoreProgress; // Stop any currently running capture session _session.stop_capture(); // Show the dialog const QString file_name = QFileDialog::getSaveFileName( this, tr("Save File"), "", tr("Sigrok Sessions (*.sr)")); if (file_name.isEmpty()) return; StoreProgress *dlg = new StoreProgress(file_name, _session, this); dlg->run(); } void MainWindow::on_actionConnect_triggered() { // Stop any currently running capture session _session.stop_capture(); dialogs::Connect dlg(this, _device_manager); // If the user selected a device, select it in the device list. Select the // current device otherwise. if (dlg.exec()) _session.set_device(dlg.get_selected_device()); update_device_list(); } void MainWindow::on_actionQuit_triggered() { close(); } void MainWindow::on_actionViewZoomIn_triggered() { _view->zoom(1); } void MainWindow::on_actionViewZoomOut_triggered() { _view->zoom(-1); } void MainWindow::on_actionViewZoomFit_triggered() { _view->zoom_fit(); } void MainWindow::on_actionViewZoomOneToOne_triggered() { _view->zoom_one_to_one(); } void MainWindow::on_actionViewShowCursors_triggered() { assert(_view); const bool show = !_view->cursors_shown(); if(show) _view->centre_cursors(); _view->show_cursors(show); } void MainWindow::on_actionAbout_triggered() { dialogs::About dlg(this); dlg.exec(); } void MainWindow::add_decoder(srd_decoder *decoder) { #ifdef ENABLE_DECODE assert(decoder); _session.add_decoder(decoder); #else (void)decoder; #endif } void MainWindow::run_stop() { switch(_session.get_capture_state()) { case SigSession::Stopped: _session.start_capture( boost::bind(&MainWindow::session_error, this, QString("Capture failed"), _1)); break; case SigSession::AwaitingTrigger: case SigSession::Running: _session.stop_capture(); break; } } void MainWindow::capture_state_changed(int state) { _sampling_bar->set_capture_state((pv::SigSession::capture_state)state); } } // namespace pv pulseview-0.2.0/pv/dialogs/storeprogress.h000600 001750 001750 00000002745 12332253627 020344 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H #define PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H #include #include #include #include namespace pv { class SigSession; namespace dialogs { class StoreProgress : public QProgressDialog { Q_OBJECT public: StoreProgress(const QString &file_name, const SigSession &session, QWidget *parent = 0); virtual ~StoreProgress(); void run(); private: void show_error(); void closeEvent(QCloseEvent*); private slots: void on_progress_updated(); private: pv::StoreSession _session; }; } // dialogs } // pv #endif // PULSEVIEW_PV_DIALOGS_SAVEPROGRESS_H pulseview-0.2.0/pv/dialogs/about.cpp000600 001750 001750 00000006505 12332253627 017066 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ENABLE_DECODE #include #endif #include #include "about.h" #include /* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */ #define __STDC_FORMAT_MACROS #include #include namespace pv { namespace dialogs { About::About(QWidget *parent) : QDialog(parent), ui(new Ui::About) { struct sr_dev_driver **drivers; struct sr_input_format **inputs; struct sr_output_format **outputs; #ifdef ENABLE_DECODE struct srd_decoder *dec; #endif QString s; ui->setupUi(this); /* Setup the version field */ ui->versionInfo->setText(tr("%1 %2
%3
%4") .arg(QApplication::applicationName()) .arg(QApplication::applicationVersion()) .arg(tr("GNU GPL, version 3 or later")) .arg(QApplication::organizationDomain())); ui->versionInfo->setOpenExternalLinks(true); s.append(""); /* Set up the supported field */ s.append(""); drivers = sr_driver_list(); for (int i = 0; drivers[i]; ++i) { s.append(QString("") .arg(QString::fromUtf8(drivers[i]->name)) .arg(QString::fromUtf8(drivers[i]->longname))); } s.append(""); inputs = sr_input_list(); for (int i = 0; inputs[i]; ++i) { s.append(QString("") .arg(QString::fromUtf8(inputs[i]->id)) .arg(QString::fromUtf8(inputs[i]->description))); } s.append(""); outputs = sr_output_list(); for (int i = 0; outputs[i]; ++i) { s.append(QString("") .arg(QString::fromUtf8(outputs[i]->id)) .arg(QString::fromUtf8(outputs[i]->description))); } #ifdef ENABLE_DECODE s.append(""); for (const GSList *l = srd_decoder_list(); l; l = l->next) { dec = (struct srd_decoder *)l->data; s.append(QString("") .arg(QString::fromUtf8(dec->id)) .arg(QString::fromUtf8(dec->longname))); } #endif s.append("
" + tr("Supported hardware drivers:") + "
%1%2
" + tr("Supported input formats:") + "
%1%2
" + tr("Supported output formats:") + "
%1%2
" + tr("Supported protocol decoders:") + "
%1%2
"); supportedDoc.reset(new QTextDocument(this)); supportedDoc->setHtml(s); ui->supportList->setDocument(supportedDoc.get()); } About::~About() { delete ui; } } // namespace dialogs } // namespace pv pulseview-0.2.0/pv/dialogs/about.h000600 001750 001750 00000002370 12332253627 016527 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_ABOUT_H #define PULSEVIEW_PV_ABOUT_H #include #include class QTextDocument; namespace Ui { class About; } namespace pv { namespace dialogs { class About : public QDialog { Q_OBJECT public: explicit About(QWidget *parent = 0); ~About(); private: Ui::About *ui; std::auto_ptr supportedDoc; }; } // namespace dialogs } // namespace pv #endif // PULSEVIEW_PV_ABOUT_H pulseview-0.2.0/pv/dialogs/connect.h000600 001750 001750 00000004027 12332253627 017047 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012-2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_CONNECT_H #define PULSEVIEW_PV_CONNECT_H #include #include #include #include #include #include #include #include #include struct sr_config; struct sr_dev_inst; namespace pv { class DeviceManager; namespace device { class Device; } namespace dialogs { class Connect : public QDialog { Q_OBJECT public: Connect(QWidget *parent, pv::DeviceManager &device_manager); boost::shared_ptr get_selected_device() const; private: void populate_drivers(); void unset_connection(); void set_serial_connection(); private slots: void device_selected(int index); void scan_pressed(); private: static void free_drvopts(sr_config *src); private: pv::DeviceManager &_device_manager; QVBoxLayout _layout; QWidget _form; QFormLayout _form_layout; QComboBox _drivers; QLineEdit _serial_device; QPushButton _scan_button; QListWidget _device_list; std::map > _device_map; QDialogButtonBox _button_box; }; } // namespace dialogs } // namespace pv #endif // PULSEVIEW_PV_CONNECT_H pulseview-0.2.0/pv/dialogs/connect.cpp000600 001750 001750 00000014255 12332253627 017406 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012-2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "connect.h" #include "pv/devicemanager.h" #include "pv/device/device.h" extern "C" { /* __STDC_FORMAT_MACROS is required for PRIu64 and friends (in C++). */ #define __STDC_FORMAT_MACROS #include #include } using boost::shared_ptr; using std::list; using std::string; extern sr_context *sr_ctx; namespace pv { namespace dialogs { Connect::Connect(QWidget *parent, pv::DeviceManager &device_manager) : QDialog(parent), _device_manager(device_manager), _layout(this), _form(this), _form_layout(&_form), _drivers(&_form), _serial_device(&_form), _scan_button(tr("Scan for Devices"), this), _device_list(this), _button_box(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this) { setWindowTitle(tr("Connect to Device")); connect(&_button_box, SIGNAL(accepted()), this, SLOT(accept())); connect(&_button_box, SIGNAL(rejected()), this, SLOT(reject())); populate_drivers(); connect(&_drivers, SIGNAL(activated(int)), this, SLOT(device_selected(int))); _form.setLayout(&_form_layout); _form_layout.addRow(tr("Driver"), &_drivers); _form_layout.addRow(tr("Serial Port"), &_serial_device); unset_connection(); connect(&_scan_button, SIGNAL(pressed()), this, SLOT(scan_pressed())); setLayout(&_layout); _layout.addWidget(&_form); _layout.addWidget(&_scan_button); _layout.addWidget(&_device_list); _layout.addWidget(&_button_box); } shared_ptr Connect::get_selected_device() const { const QListWidgetItem *const item = _device_list.currentItem(); if (!item) return shared_ptr(); const sr_dev_inst *const sdi = (sr_dev_inst*)item->data( Qt::UserRole).value(); assert(sdi); std::map >:: const_iterator iter = _device_map.find(sdi); assert(iter != _device_map.end()); return (*iter).second; } void Connect::populate_drivers() { gsize num_opts = 0; const int32_t *hwopts; struct sr_dev_driver **drivers = sr_driver_list(); GVariant *gvar_opts; for (int i = 0; drivers[i]; ++i) { /** * We currently only support devices that can deliver * samples at a fixed samplerate i.e. oscilloscopes and * logic analysers. * @todo Add support for non-monotonic devices i.e. DMMs * and sensors. */ bool supported_device = false; if ((sr_config_list(drivers[i], NULL, NULL, SR_CONF_DEVICE_OPTIONS, &gvar_opts) == SR_OK)) { hwopts = (const int32_t *)g_variant_get_fixed_array(gvar_opts, &num_opts, sizeof(int32_t)); for (unsigned int j = 0; j < num_opts; j++) if (hwopts[j] == SR_CONF_SAMPLERATE) { supported_device = true; break; } } if (supported_device) _drivers.addItem(QString("%1 (%2)").arg( drivers[i]->longname).arg(drivers[i]->name), qVariantFromValue((void*)drivers[i])); } } void Connect::unset_connection() { _device_list.clear(); _device_map.clear(); _serial_device.hide(); _form_layout.labelForField(&_serial_device)->hide(); _button_box.button(QDialogButtonBox::Ok)->setDisabled(true); } void Connect::set_serial_connection() { _serial_device.show(); _form_layout.labelForField(&_serial_device)->show(); } void Connect::scan_pressed() { _device_list.clear(); _device_map.clear(); const int index = _drivers.currentIndex(); if (index == -1) return; sr_dev_driver *const driver = (sr_dev_driver*)_drivers.itemData( index).value(); GSList *drvopts = NULL; if (_serial_device.isVisible()) { sr_config *const src = (sr_config*)g_try_malloc(sizeof(sr_config)); src->key = SR_CONF_CONN; const QByteArray byteArray = _serial_device.text().toUtf8(); src->data = g_variant_new_string((const gchar*)byteArray.constData()); drvopts = g_slist_append(drvopts, src); } const list< shared_ptr > devices = _device_manager.driver_scan(driver, drvopts); g_slist_free_full(drvopts, (GDestroyNotify)free_drvopts); BOOST_FOREACH(shared_ptr dev_inst, devices) { assert(dev_inst); const sr_dev_inst *const sdi = dev_inst->dev_inst(); assert(sdi); const string title = dev_inst->format_device_title(); QString text = QString::fromUtf8(title.c_str()); if (sdi->channels) { text += QString(" with %1 channels").arg( g_slist_length(sdi->channels)); } QListWidgetItem *const item = new QListWidgetItem(text, &_device_list); item->setData(Qt::UserRole, qVariantFromValue((void*)sdi)); _device_list.addItem(item); _device_map[sdi] = dev_inst; } _device_list.setCurrentRow(0); _button_box.button(QDialogButtonBox::Ok)->setDisabled(_device_list.count() == 0); } void Connect::device_selected(int index) { gsize num_opts = 0; const int32_t *hwopts; GVariant *gvar_list; sr_dev_driver *const driver = (sr_dev_driver*)_drivers.itemData( index).value(); unset_connection(); if ((sr_config_list(driver, NULL, NULL, SR_CONF_SCAN_OPTIONS, &gvar_list) == SR_OK)) { hwopts = (const int32_t *)g_variant_get_fixed_array(gvar_list, &num_opts, sizeof(int32_t)); for (unsigned int i = 0; i < num_opts; i++) { switch(hwopts[i]) { case SR_CONF_SERIALCOMM: set_serial_connection(); break; default: continue; } break; } g_variant_unref(gvar_list); } } void Connect::free_drvopts(struct sr_config *src) { g_variant_unref(src->data); g_free(src); } } // namespace dialogs } // namespace pv pulseview-0.2.0/pv/dialogs/storeprogress.cpp000600 001750 001750 00000003750 12332253627 020674 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "storeprogress.h" #include namespace pv { namespace dialogs { StoreProgress::StoreProgress(const QString &file_name, const SigSession &session, QWidget *parent) : QProgressDialog(tr("Saving..."), tr("Cancel"), 0, 0, parent), _session(file_name.toStdString(), session) { connect(&_session, SIGNAL(progress_updated()), this, SLOT(on_progress_updated())); } StoreProgress::~StoreProgress() { _session.wait(); } void StoreProgress::run() { if (_session.start()) show(); else show_error(); } void StoreProgress::show_error() { QMessageBox msg(parentWidget()); msg.setText(tr("Failed to save session.")); msg.setInformativeText(_session.error()); msg.setStandardButtons(QMessageBox::Ok); msg.setIcon(QMessageBox::Warning); msg.exec(); } void StoreProgress::closeEvent(QCloseEvent*) { _session.cancel(); } void StoreProgress::on_progress_updated() { const std::pair p = _session.progress(); assert(p.first <= p.second); setValue(p.first); setMaximum(p.second); const QString err = _session.error(); if (!err.isEmpty()) { show_error(); close(); } if (p.first == p.second) close(); } } // dialogs } // pv pulseview-0.2.0/pv/dialogs/about.ui000600 001750 001750 00000004260 12332253627 016715 0ustar00uweuwe000000 000000 About Qt::WindowModal 0 0 600 400 About :/icons/sigrok-logo-notext.png Qt::Horizontal QDialogButtonBox::Ok buttonBox accepted() About accept() 248 254 157 274 buttonBox rejected() About reject() 316 260 286 274 pulseview-0.2.0/pv/toolbars/samplingbar.cpp000600 001750 001750 00000025765 12332253627 020467 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "samplingbar.h" #include #include #include #include using boost::shared_ptr; using std::map; using std::max; using std::min; using std::string; namespace pv { namespace toolbars { const uint64_t SamplingBar::MinSampleCount = 100ULL; const uint64_t SamplingBar::MaxSampleCount = 1000000000000ULL; const uint64_t SamplingBar::DefaultSampleCount = 1000000; SamplingBar::SamplingBar(SigSession &session, QWidget *parent) : QToolBar("Sampling Bar", parent), _session(session), _device_selector(this), _updating_device_selector(false), _configure_button(this), _configure_button_action(NULL), _probes_button(this), _sample_count(" samples", this), _sample_rate("Hz", this), _updating_sample_rate(false), _updating_sample_count(false), _sample_count_supported(false), _icon_red(":/icons/status-red.svg"), _icon_green(":/icons/status-green.svg"), _icon_grey(":/icons/status-grey.svg"), _run_stop_button(this) { connect(&_run_stop_button, SIGNAL(clicked()), this, SLOT(on_run_stop())); connect(&_device_selector, SIGNAL(currentIndexChanged (int)), this, SLOT(on_device_selected())); connect(&_sample_count, SIGNAL(value_changed()), this, SLOT(on_sample_count_changed())); connect(&_sample_rate, SIGNAL(value_changed()), this, SLOT(on_sample_rate_changed())); _sample_count.show_min_max_step(0, UINT64_MAX, 1); set_capture_state(pv::SigSession::Stopped); _configure_button.setIcon(QIcon::fromTheme("configure", QIcon(":/icons/configure.png"))); _probes_button.setIcon(QIcon::fromTheme("probes", QIcon(":/icons/probes.svg"))); _run_stop_button.setToolButtonStyle(Qt::ToolButtonTextBesideIcon); addWidget(&_device_selector); _configure_button_action = addWidget(&_configure_button); addWidget(&_probes_button); addWidget(&_sample_count); addWidget(&_sample_rate); addWidget(&_run_stop_button); } void SamplingBar::set_device_list( const std::list< shared_ptr > &devices, shared_ptr selected) { int selected_index = -1; assert(selected); _updating_device_selector = true; _device_selector.clear(); _device_selector_map.clear(); BOOST_FOREACH (shared_ptr dev_inst, devices) { assert(dev_inst); const string title = dev_inst->format_device_title(); const sr_dev_inst *sdi = dev_inst->dev_inst(); assert(sdi); if (selected == dev_inst) selected_index = _device_selector.count(); _device_selector_map[sdi] = dev_inst; _device_selector.addItem(title.c_str(), qVariantFromValue((void*)sdi)); } // The selected device should have been in the list assert(selected_index != -1); _device_selector.setCurrentIndex(selected_index); update_device_config_widgets(); _updating_device_selector = false; } shared_ptr SamplingBar::get_selected_device() const { const int index = _device_selector.currentIndex(); if (index < 0) return shared_ptr(); const sr_dev_inst *const sdi = (const sr_dev_inst*)_device_selector.itemData( index).value(); assert(sdi); map >:: const_iterator iter = _device_selector_map.find(sdi); if (iter == _device_selector_map.end()) return shared_ptr(); return shared_ptr((*iter).second); } void SamplingBar::set_capture_state(pv::SigSession::capture_state state) { const QIcon *icons[] = {&_icon_grey, &_icon_red, &_icon_green}; _run_stop_button.setIcon(*icons[state]); _run_stop_button.setText((state == pv::SigSession::Stopped) ? tr("Run") : tr("Stop")); } void SamplingBar::update_sample_rate_selector() { GVariant *gvar_dict, *gvar_list; const uint64_t *elements = NULL; gsize num_elements; if (_updating_sample_rate) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; assert(!_updating_sample_rate); _updating_sample_rate = true; if (!(gvar_dict = dev_inst->list_config(NULL, SR_CONF_SAMPLERATE))) { _sample_rate.show_none(); _updating_sample_rate = false; return; } if ((gvar_list = g_variant_lookup_value(gvar_dict, "samplerate-steps", G_VARIANT_TYPE("at")))) { elements = (const uint64_t *)g_variant_get_fixed_array( gvar_list, &num_elements, sizeof(uint64_t)); const uint64_t min = elements[0]; const uint64_t max = elements[1]; const uint64_t step = elements[2]; g_variant_unref(gvar_list); assert(min > 0); assert(max > 0); assert(max > min); assert(step > 0); if (step == 1) _sample_rate.show_125_list(min, max); else { // When the step is not 1, we cam't make a 1-2-5-10 // list of sample rates, because we may not be able to // make round numbers. Therefore in this case, show a // spin box. _sample_rate.show_min_max_step(min, max, step); } } else if ((gvar_list = g_variant_lookup_value(gvar_dict, "samplerates", G_VARIANT_TYPE("at")))) { elements = (const uint64_t *)g_variant_get_fixed_array( gvar_list, &num_elements, sizeof(uint64_t)); _sample_rate.show_list(elements, num_elements); g_variant_unref(gvar_list); } _updating_sample_rate = false; g_variant_unref(gvar_dict); update_sample_rate_selector_value(); } void SamplingBar::update_sample_rate_selector_value() { GVariant *gvar; uint64_t samplerate; if (_updating_sample_rate) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; if (!(gvar = dev_inst->get_config(NULL, SR_CONF_SAMPLERATE))) { qDebug() << "WARNING: Failed to get value of sample rate"; return; } samplerate = g_variant_get_uint64(gvar); g_variant_unref(gvar); assert(!_updating_sample_rate); _updating_sample_rate = true; _sample_rate.set_value(samplerate); _updating_sample_rate = false; } void SamplingBar::update_sample_count_selector() { GVariant *gvar; if (_updating_sample_count) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; assert(!_updating_sample_count); _updating_sample_count = true; if (_sample_count_supported) { uint64_t sample_count = _sample_count.value(); uint64_t min_sample_count = 0; uint64_t max_sample_count = MaxSampleCount; if (sample_count == 0) sample_count = DefaultSampleCount; if ((gvar = dev_inst->list_config(NULL, SR_CONF_LIMIT_SAMPLES))) { g_variant_get(gvar, "(tt)", &min_sample_count, &max_sample_count); g_variant_unref(gvar); } min_sample_count = min(max(min_sample_count, MinSampleCount), max_sample_count); _sample_count.show_125_list( min_sample_count, max_sample_count); if ((gvar = dev_inst->get_config(NULL, SR_CONF_LIMIT_SAMPLES))) { sample_count = g_variant_get_uint64(gvar); if (sample_count == 0) sample_count = DefaultSampleCount; sample_count = min(max(sample_count, MinSampleCount), max_sample_count); g_variant_unref(gvar); } _sample_count.set_value(sample_count); } else _sample_count.show_none(); _updating_sample_count = false; } void SamplingBar::update_device_config_widgets() { GVariant *gvar; using namespace pv::popups; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; // Update the configure popup DeviceOptions *const opts = new DeviceOptions(dev_inst, this); _configure_button_action->setVisible( !opts->binding().properties().empty()); _configure_button.set_popup(opts); // Update the probes popup Probes *const probes = new Probes(_session, this); _probes_button.set_popup(probes); // Update supported options. _sample_count_supported = false; if ((gvar = dev_inst->list_config(NULL, SR_CONF_DEVICE_OPTIONS))) { gsize num_opts; const int *const options = (const int32_t *)g_variant_get_fixed_array( gvar, &num_opts, sizeof(int32_t)); for (unsigned int i = 0; i < num_opts; i++) { switch (options[i]) { case SR_CONF_LIMIT_SAMPLES: _sample_count_supported = true; break; case SR_CONF_LIMIT_FRAMES: dev_inst->set_config(NULL, SR_CONF_LIMIT_FRAMES, g_variant_new_uint64(1)); break; } } } // Add notification of reconfigure events disconnect(this, SLOT(on_config_changed())); connect(dev_inst.get(), SIGNAL(config_changed()), this, SLOT(on_config_changed())); // Update sweep timing widgets. update_sample_count_selector(); update_sample_rate_selector(); } void SamplingBar::commit_sample_count() { uint64_t sample_count = 0; if (_updating_sample_count) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; sample_count = _sample_count.value(); // Set the sample count assert(!_updating_sample_count); _updating_sample_count = true; if (_sample_count_supported && !dev_inst->set_config(NULL, SR_CONF_LIMIT_SAMPLES, g_variant_new_uint64(sample_count))) { qDebug() << "Failed to configure sample count."; return; } _updating_sample_count = false; } void SamplingBar::commit_sample_rate() { uint64_t sample_rate = 0; if (_updating_sample_rate) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; sample_rate = _sample_rate.value(); if (sample_rate == 0) return; // Set the samplerate assert(!_updating_sample_rate); _updating_sample_rate = true; if (!dev_inst->set_config(NULL, SR_CONF_SAMPLERATE, g_variant_new_uint64(sample_rate))) { qDebug() << "Failed to configure samplerate."; return; } _updating_sample_rate = false; } void SamplingBar::on_device_selected() { if (_updating_device_selector) return; const shared_ptr dev_inst = get_selected_device(); if (!dev_inst) return; _session.set_device(dev_inst); update_device_config_widgets(); } void SamplingBar::on_sample_count_changed() { commit_sample_count(); } void SamplingBar::on_sample_rate_changed() { commit_sample_rate(); } void SamplingBar::on_run_stop() { commit_sample_count(); commit_sample_rate(); run_stop(); } void SamplingBar::on_config_changed() { commit_sample_count(); update_sample_count_selector(); commit_sample_rate(); update_sample_rate_selector(); } } // namespace toolbars } // namespace pv pulseview-0.2.0/pv/toolbars/samplingbar.h000600 001750 001750 00000005575 12332253627 020131 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H #define PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H #include #include #include #include #include #include #include #include #include #include #include class QAction; namespace pv { class SigSession; namespace device { class DevInst; } namespace toolbars { class SamplingBar : public QToolBar { Q_OBJECT private: static const uint64_t MinSampleCount; static const uint64_t MaxSampleCount; static const uint64_t DefaultSampleCount; public: SamplingBar(SigSession &session, QWidget *parent); void set_device_list( const std::list< boost::shared_ptr > &devices, boost::shared_ptr selected); boost::shared_ptr get_selected_device() const; void set_capture_state(pv::SigSession::capture_state state); signals: void run_stop(); private: void update_sample_rate_selector(); void update_sample_rate_selector_value(); void update_sample_count_selector(); void update_device_config_widgets(); void commit_sample_rate(); void commit_sample_count(); private slots: void on_device_selected(); void on_sample_count_changed(); void on_sample_rate_changed(); void on_run_stop(); void on_config_changed(); private: SigSession &_session; QComboBox _device_selector; std::map > _device_selector_map; bool _updating_device_selector; pv::widgets::PopupToolButton _configure_button; QAction *_configure_button_action; pv::widgets::PopupToolButton _probes_button; pv::widgets::SweepTimingWidget _sample_count; pv::widgets::SweepTimingWidget _sample_rate; bool _updating_sample_rate; bool _updating_sample_count; bool _sample_count_supported; QIcon _icon_red; QIcon _icon_green; QIcon _icon_grey; QToolButton _run_stop_button; }; } // namespace toolbars } // namespace pv #endif // PULSEVIEW_PV_TOOLBARS_SAMPLINGBAR_H pulseview-0.2.0/pv/sigsession.cpp000600 001750 001750 00000034753 12332253627 016526 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012-14 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ENABLE_DECODE #include #endif #include "sigsession.h" #include "devicemanager.h" #include "device/device.h" #include "device/file.h" #include "data/analog.h" #include "data/analogsnapshot.h" #include "data/decoderstack.h" #include "data/logic.h" #include "data/logicsnapshot.h" #include "data/decode/decoder.h" #include "view/analogsignal.h" #include "view/decodetrace.h" #include "view/logicsignal.h" #include #include #include #include #include using boost::dynamic_pointer_cast; using boost::function; using boost::lock_guard; using boost::mutex; using boost::shared_ptr; using std::list; using std::map; using std::set; using std::string; using std::vector; namespace pv { // TODO: This should not be necessary SigSession* SigSession::_session = NULL; SigSession::SigSession(DeviceManager &device_manager) : _device_manager(device_manager), _capture_state(Stopped) { // TODO: This should not be necessary _session = this; set_default_device(); } SigSession::~SigSession() { using pv::device::Device; stop_capture(); if (_sampling_thread.joinable()) _sampling_thread.join(); _dev_inst->release(); // TODO: This should not be necessary _session = NULL; } shared_ptr SigSession::get_device() const { return _dev_inst; } void SigSession::set_device( shared_ptr dev_inst) throw(QString) { using pv::device::Device; // Ensure we are not capturing before setting the device stop_capture(); if (_dev_inst) { sr_session_datafeed_callback_remove_all(); _dev_inst->release(); } _dev_inst = dev_inst; _decode_traces.clear(); if (dev_inst) { dev_inst->use(this); sr_session_datafeed_callback_add(data_feed_in_proc, NULL); update_signals(dev_inst); } } void SigSession::set_file(const string &name) throw(QString) { // Deslect the old device, because file type detection in File::create // destorys the old session inside libsigrok. set_device(shared_ptr()); set_device(shared_ptr(device::File::create(name))); } void SigSession::set_default_device() { shared_ptr default_device; const list< shared_ptr > &devices = _device_manager.devices(); if (!devices.empty()) { // Fall back to the first device in the list. default_device = devices.front(); // Try and find the demo device and select that by default BOOST_FOREACH (shared_ptr dev, devices) if (strcmp(dev->dev_inst()->driver->name, "demo") == 0) { default_device = dev; break; } } set_device(default_device); } void SigSession::release_device(device::DevInst *dev_inst) { (void)dev_inst; assert(_dev_inst.get() == dev_inst); assert(_capture_state == Stopped); _dev_inst = shared_ptr(); } SigSession::capture_state SigSession::get_capture_state() const { lock_guard lock(_sampling_mutex); return _capture_state; } void SigSession::start_capture(function error_handler) { stop_capture(); // Check that a device instance has been selected. if (!_dev_inst) { qDebug() << "No device selected"; return; } assert(_dev_inst->dev_inst()); // Check that at least one probe is enabled const GSList *l; for (l = _dev_inst->dev_inst()->channels; l; l = l->next) { sr_channel *const probe = (sr_channel*)l->data; assert(probe); if (probe->enabled) break; } if (!l) { error_handler(tr("No channels enabled.")); return; } // Begin the session _sampling_thread = boost::thread( &SigSession::sample_thread_proc, this, _dev_inst, error_handler); } void SigSession::stop_capture() { if (get_capture_state() == Stopped) return; sr_session_stop(); // Check that sampling stopped if (_sampling_thread.joinable()) _sampling_thread.join(); } set< shared_ptr > SigSession::get_data() const { lock_guard lock(_signals_mutex); set< shared_ptr > data; BOOST_FOREACH(const shared_ptr sig, _signals) { assert(sig); data.insert(sig->data()); } return data; } vector< shared_ptr > SigSession::get_signals() const { lock_guard lock(_signals_mutex); return _signals; } #ifdef ENABLE_DECODE bool SigSession::add_decoder(srd_decoder *const dec) { map > probes; shared_ptr decoder_stack; try { lock_guard lock(_signals_mutex); // Create the decoder decoder_stack = shared_ptr( new data::DecoderStack(*this, dec)); // Make a list of all the probes std::vector all_probes; for(const GSList *i = dec->channels; i; i = i->next) all_probes.push_back((const srd_channel*)i->data); for(const GSList *i = dec->opt_channels; i; i = i->next) all_probes.push_back((const srd_channel*)i->data); // Auto select the initial probes BOOST_FOREACH(const srd_channel *pdch, all_probes) BOOST_FOREACH(shared_ptr s, _signals) { shared_ptr l = dynamic_pointer_cast(s); if (l && QString::fromUtf8(pdch->name). toLower().contains( l->get_name().toLower())) probes[pdch] = l; } assert(decoder_stack); assert(!decoder_stack->stack().empty()); assert(decoder_stack->stack().front()); decoder_stack->stack().front()->set_probes(probes); // Create the decode signal shared_ptr d( new view::DecodeTrace(*this, decoder_stack, _decode_traces.size())); _decode_traces.push_back(d); } catch(std::runtime_error e) { return false; } signals_changed(); // Do an initial decode decoder_stack->begin_decode(); return true; } vector< shared_ptr > SigSession::get_decode_signals() const { lock_guard lock(_signals_mutex); return _decode_traces; } void SigSession::remove_decode_signal(view::DecodeTrace *signal) { for (vector< shared_ptr >::iterator i = _decode_traces.begin(); i != _decode_traces.end(); i++) if ((*i).get() == signal) { _decode_traces.erase(i); signals_changed(); return; } } #endif void SigSession::set_capture_state(capture_state state) { lock_guard lock(_sampling_mutex); const bool changed = _capture_state != state; _capture_state = state; if(changed) capture_state_changed(state); } void SigSession::update_signals(shared_ptr dev_inst) { assert(dev_inst); assert(_capture_state == Stopped); unsigned int logic_probe_count = 0; // Clear the decode traces _decode_traces.clear(); // Detect what data types we will receive if(dev_inst) { assert(dev_inst->dev_inst()); for (const GSList *l = dev_inst->dev_inst()->channels; l; l = l->next) { const sr_channel *const probe = (const sr_channel *)l->data; if (!probe->enabled) continue; switch(probe->type) { case SR_CHANNEL_LOGIC: logic_probe_count++; break; } } } // Create data containers for the logic data snapshots { lock_guard data_lock(_data_mutex); _logic_data.reset(); if (logic_probe_count != 0) { _logic_data.reset(new data::Logic( logic_probe_count)); assert(_logic_data); } } // Make the Signals list do { lock_guard lock(_signals_mutex); _signals.clear(); if(!dev_inst) break; assert(dev_inst->dev_inst()); for (const GSList *l = dev_inst->dev_inst()->channels; l; l = l->next) { shared_ptr signal; sr_channel *const probe = (sr_channel *)l->data; assert(probe); switch(probe->type) { case SR_CHANNEL_LOGIC: signal = shared_ptr( new view::LogicSignal(dev_inst, probe, _logic_data)); break; case SR_CHANNEL_ANALOG: { shared_ptr data( new data::Analog()); signal = shared_ptr( new view::AnalogSignal(dev_inst, probe, data)); break; } default: assert(0); break; } assert(signal); _signals.push_back(signal); } } while(0); signals_changed(); } shared_ptr SigSession::signal_from_probe( const sr_channel *probe) const { lock_guard lock(_signals_mutex); BOOST_FOREACH(shared_ptr sig, _signals) { assert(sig); if (sig->probe() == probe) return sig; } return shared_ptr(); } void SigSession::read_sample_rate(const sr_dev_inst *const sdi) { GVariant *gvar; uint64_t sample_rate = 0; // Read out the sample rate if(sdi->driver) { const int ret = sr_config_get(sdi->driver, sdi, NULL, SR_CONF_SAMPLERATE, &gvar); if (ret != SR_OK) { qDebug("Failed to get samplerate\n"); return; } sample_rate = g_variant_get_uint64(gvar); g_variant_unref(gvar); } // Set the sample rate of all data const set< shared_ptr > data_set = get_data(); BOOST_FOREACH(shared_ptr data, data_set) { assert(data); data->set_samplerate(sample_rate); } } void SigSession::sample_thread_proc(shared_ptr dev_inst, function error_handler) { assert(dev_inst); assert(dev_inst->dev_inst()); assert(error_handler); read_sample_rate(dev_inst->dev_inst()); try { dev_inst->start(); } catch(const QString e) { error_handler(e); return; } set_capture_state(dev_inst->is_trigger_enabled() ? AwaitingTrigger : Running); dev_inst->run(); set_capture_state(Stopped); // Confirm that SR_DF_END was received if (_cur_logic_snapshot) { qDebug("SR_DF_END was not received."); assert(0); } } void SigSession::feed_in_header(const sr_dev_inst *sdi) { read_sample_rate(sdi); } void SigSession::feed_in_meta(const sr_dev_inst *sdi, const sr_datafeed_meta &meta) { (void)sdi; for (const GSList *l = meta.config; l; l = l->next) { const sr_config *const src = (const sr_config*)l->data; switch (src->key) { case SR_CONF_SAMPLERATE: /// @todo handle samplerate changes /// samplerate = (uint64_t *)src->value; break; default: // Unknown metadata is not an error. break; } } signals_changed(); } void SigSession::feed_in_frame_begin() { if (_cur_logic_snapshot || !_cur_analog_snapshots.empty()) frame_began(); } void SigSession::feed_in_logic(const sr_datafeed_logic &logic) { lock_guard lock(_data_mutex); if (!_logic_data) { qDebug() << "Unexpected logic packet"; return; } if (!_cur_logic_snapshot) { // This could be the first packet after a trigger set_capture_state(Running); // Create a new data snapshot _cur_logic_snapshot = shared_ptr( new data::LogicSnapshot(logic, _dev_inst->get_sample_limit())); _logic_data->push_snapshot(_cur_logic_snapshot); // @todo Putting this here means that only listeners querying // for logic will be notified. Currently the only user of // frame_began is DecoderStack, but in future we need to signal // this after both analog and logic sweeps have begun. frame_began(); } else { // Append to the existing data snapshot _cur_logic_snapshot->append_payload(logic); } data_received(); } void SigSession::feed_in_analog(const sr_datafeed_analog &analog) { lock_guard lock(_data_mutex); const unsigned int probe_count = g_slist_length(analog.channels); const size_t sample_count = analog.num_samples / probe_count; const float *data = analog.data; bool sweep_beginning = false; for (GSList *p = analog.channels; p; p = p->next) { shared_ptr snapshot; sr_channel *const probe = (sr_channel*)p->data; assert(probe); // Try to get the snapshot of the probe const map< const sr_channel*, shared_ptr >:: iterator iter = _cur_analog_snapshots.find(probe); if (iter != _cur_analog_snapshots.end()) snapshot = (*iter).second; else { // If no snapshot was found, this means we havn't // created one yet. i.e. this is the first packet // in the sweep containing this snapshot. sweep_beginning = true; // Create a snapshot, keep it in the maps of probes snapshot = shared_ptr( new data::AnalogSnapshot(_dev_inst->get_sample_limit())); _cur_analog_snapshots[probe] = snapshot; // Find the annalog data associated with the probe shared_ptr sig = dynamic_pointer_cast( signal_from_probe(probe)); assert(sig); shared_ptr data(sig->analog_data()); assert(data); // Push the snapshot into the analog data. data->push_snapshot(snapshot); } assert(snapshot); // Append the samples in the snapshot snapshot->append_interleaved_samples(data++, sample_count, probe_count); } if (sweep_beginning) { // This could be the first packet after a trigger set_capture_state(Running); } data_received(); } void SigSession::data_feed_in(const struct sr_dev_inst *sdi, const struct sr_datafeed_packet *packet) { assert(sdi); assert(packet); switch (packet->type) { case SR_DF_HEADER: feed_in_header(sdi); break; case SR_DF_META: assert(packet->payload); feed_in_meta(sdi, *(const sr_datafeed_meta*)packet->payload); break; case SR_DF_FRAME_BEGIN: feed_in_frame_begin(); break; case SR_DF_LOGIC: assert(packet->payload); feed_in_logic(*(const sr_datafeed_logic*)packet->payload); break; case SR_DF_ANALOG: assert(packet->payload); feed_in_analog(*(const sr_datafeed_analog*)packet->payload); break; case SR_DF_END: { { lock_guard lock(_data_mutex); _cur_logic_snapshot.reset(); _cur_analog_snapshots.clear(); } frame_ended(); break; } } } void SigSession::data_feed_in_proc(const struct sr_dev_inst *sdi, const struct sr_datafeed_packet *packet, void *cb_data) { (void) cb_data; assert(_session); _session->data_feed_in(sdi, packet); } } // namespace pv pulseview-0.2.0/pv/view/selectableitem.h000600 001750 001750 00000003276 12332253627 017735 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_SELECTABLEITEM_H #define PULSEVIEW_PV_SELECTABLEITEM_H #include #include class QAction; class QMenu; class QWidget; namespace pv { namespace widgets { class Popup; } namespace view { class SelectableItem : public QObject { Q_OBJECT private: static const int HighlightRadius; public: SelectableItem(); public: /** * Returns true if the signal has been selected by the user. */ bool selected() const; /** * Selects or deselects the signal. */ void select(bool select = true); public: virtual QMenu* create_context_menu(QWidget *parent); virtual pv::widgets::Popup* create_popup(QWidget *parent) = 0; virtual void delete_pressed(); protected: static QPen highlight_pen(); protected: QWidget *_context_parent; private: bool _selected; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_SELECTABLEITEM_H pulseview-0.2.0/pv/view/tracepalette.cpp000600 001750 001750 00000004320 12332253627 017752 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "tracepalette.h" namespace pv { namespace view { const QColor TracePalette::Colours[Cols * Rows] = { // Light Colours QColor(0xFC, 0xE9, 0x4F), // Butter QColor(0xFC, 0xAF, 0x3E), // Orange QColor(0xE9, 0xB9, 0x6E), // Chocolate QColor(0x8A, 0xE2, 0x34), // Chameleon QColor(0x72, 0x9F, 0xCF), // Sky Blue QColor(0xAD, 0x7F, 0xA8), // Plum QColor(0xCF, 0x72, 0xC3), // Magenta QColor(0xEF, 0x29, 0x29), // Scarlet Red // Mid Colours QColor(0xED, 0xD4, 0x00), // Butter QColor(0xF5, 0x79, 0x00), // Orange QColor(0xC1, 0x7D, 0x11), // Chocolate QColor(0x73, 0xD2, 0x16), // Chameleon QColor(0x34, 0x65, 0xA4), // Sky Blue QColor(0x75, 0x50, 0x7B), // Plum QColor(0xA3, 0x34, 0x96), // Magenta QColor(0xCC, 0x00, 0x00), // Scarlet Red // Dark Colours QColor(0xC4, 0xA0, 0x00), // Butter QColor(0xCE, 0x5C, 0x00), // Orange QColor(0x8F, 0x59, 0x02), // Chocolate QColor(0x4E, 0x9A, 0x06), // Chameleon QColor(0x20, 0x4A, 0x87), // Sky Blue QColor(0x5C, 0x35, 0x66), // Plum QColor(0x87, 0x20, 0x7A), // Magenta QColor(0xA4, 0x00, 0x00), // Scarlet Red // Greys QColor(0x16, 0x19, 0x1A), // Black QColor(0x2E, 0x34, 0x36), // Grey 1 QColor(0x55, 0x57, 0x53), // Grey 2 QColor(0x88, 0x8A, 0x8F), // Grey 3 QColor(0xBA, 0xBD, 0xB6), // Grey 4 QColor(0xD3, 0xD7, 0xCF), // Grey 5 QColor(0xEE, 0xEE, 0xEC), // Grey 6 QColor(0xFF, 0xFF, 0xFF), // White }; } // view } // pv pulseview-0.2.0/pv/view/ruler.cpp000600 001750 001750 00000014507 12332253627 016436 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "ruler.h" #include "cursor.h" #include "view.h" #include "viewport.h" #include #include #include #include #include #include #include #include #include using namespace Qt; using boost::shared_ptr; namespace pv { namespace view { const int Ruler::RulerHeight = 30; const int Ruler::MinorTickSubdivision = 4; const int Ruler::ScaleUnits[3] = {1, 2, 5}; const QString Ruler::SIPrefixes[9] = {"f", "p", "n", QChar(0x03BC), "m", "", "k", "M", "G"}; const int Ruler::FirstSIPrefixPower = -15; const int Ruler::HoverArrowSize = 5; Ruler::Ruler(View &parent) : MarginWidget(parent), _dragging(false) { setMouseTracking(true); connect(&_view, SIGNAL(hover_point_changed()), this, SLOT(hover_point_changed())); } void Ruler::clear_selection() { CursorPair &cursors = _view.cursors(); cursors.first()->select(false); cursors.second()->select(false); update(); } QString Ruler::format_time(double t, unsigned int prefix, unsigned int precision) { const double multiplier = pow(10.0, (int)- prefix * 3 - FirstSIPrefixPower); QString s; QTextStream ts(&s); ts.setRealNumberPrecision(precision); ts << fixed << forcesign << (t * multiplier) << SIPrefixes[prefix] << "s"; return s; } QSize Ruler::sizeHint() const { return QSize(0, RulerHeight); } void Ruler::paintEvent(QPaintEvent*) { const double SpacingIncrement = 32.0f; const double MinValueSpacing = 32.0f; const int ValueMargin = 3; QPainter p(this); p.setRenderHint(QPainter::Antialiasing); double min_width = SpacingIncrement, typical_width; double tick_period; unsigned int prefix; // Find tick spacing, and number formatting that does not cause // value to collide. do { const double min_period = _view.scale() * min_width; const int order = (int)floorf(log10f(min_period)); const double order_decimal = pow(10.0, order); unsigned int unit = 0; do { tick_period = order_decimal * ScaleUnits[unit++]; } while (tick_period < min_period && unit < countof(ScaleUnits)); prefix = (order - FirstSIPrefixPower) / 3; assert(prefix < countof(SIPrefixes)); typical_width = p.boundingRect(0, 0, INT_MAX, INT_MAX, AlignLeft | AlignTop, format_time(_view.offset(), prefix)).width() + MinValueSpacing; min_width += SpacingIncrement; } while(typical_width > tick_period / _view.scale()); const int text_height = p.boundingRect(0, 0, INT_MAX, INT_MAX, AlignLeft | AlignTop, "8").height(); // Draw the tick marks p.setPen(palette().color(foregroundRole())); const double minor_tick_period = tick_period / MinorTickSubdivision; const double first_major_division = floor(_view.offset() / tick_period); const double first_minor_division = ceil(_view.offset() / minor_tick_period); const double t0 = first_major_division * tick_period; int division = (int)round(first_minor_division - first_major_division * MinorTickSubdivision) - 1; const int major_tick_y1 = text_height + ValueMargin * 2; const int tick_y2 = height(); const int minor_tick_y1 = (major_tick_y1 + tick_y2) / 2; double x; do { const double t = t0 + division * minor_tick_period; x = (t - _view.offset()) / _view.scale(); if (division % MinorTickSubdivision == 0) { // Draw a major tick p.drawText(x, ValueMargin, 0, text_height, AlignCenter | AlignTop | TextDontClip, format_time(t, prefix)); p.drawLine(QPointF(x, major_tick_y1), QPointF(x, tick_y2)); } else { // Draw a minor tick p.drawLine(QPointF(x, minor_tick_y1), QPointF(x, tick_y2)); } division++; } while (x < width()); // Draw the cursors if (_view.cursors_shown()) _view.cursors().draw_markers(p, rect(), prefix); // Draw the hover mark draw_hover_mark(p); p.end(); } void Ruler::mouseMoveEvent(QMouseEvent *e) { if (!(e->buttons() & Qt::LeftButton)) return; if ((e->pos() - _mouse_down_point).manhattanLength() < QApplication::startDragDistance()) return; _dragging = true; if (shared_ptr m = _grabbed_marker.lock()) m->set_time(_view.offset() + ((double)e->x() + 0.5) * _view.scale()); } void Ruler::mousePressEvent(QMouseEvent *e) { if (e->buttons() & Qt::LeftButton) { _mouse_down_point = e->pos(); _grabbed_marker.reset(); clear_selection(); if (_view.cursors_shown()) { CursorPair &cursors = _view.cursors(); if (cursors.first()->get_label_rect( rect()).contains(e->pos())) _grabbed_marker = cursors.first(); else if (cursors.second()->get_label_rect( rect()).contains(e->pos())) _grabbed_marker = cursors.second(); } if (shared_ptr m = _grabbed_marker.lock()) m->select(); selection_changed(); } } void Ruler::mouseReleaseEvent(QMouseEvent *) { using pv::widgets::Popup; if (!_dragging) if (shared_ptr m = _grabbed_marker.lock()) { Popup *const p = m->create_popup(&_view); p->set_position(mapToGlobal(QPoint(m->get_x(), height())), Popup::Bottom); p->show(); } _dragging = false; _grabbed_marker.reset(); } void Ruler::draw_hover_mark(QPainter &p) { const int x = _view.hover_point().x(); if (x == -1 || _dragging) return; p.setPen(QPen(Qt::NoPen)); p.setBrush(QBrush(palette().color(foregroundRole()))); const int b = height() - 1; const QPointF points[] = { QPointF(x, b), QPointF(x - HoverArrowSize, b - HoverArrowSize), QPointF(x + HoverArrowSize, b - HoverArrowSize) }; p.drawPolygon(points, countof(points)); } void Ruler::hover_point_changed() { update(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/trace.h000600 001750 001750 00000011734 12332253627 016047 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_TRACE_H #define PULSEVIEW_PV_VIEW_TRACE_H #include #include #include #include #include #include #include "selectableitem.h" class QFormLayout; namespace pv { namespace view { class View; class Trace : public SelectableItem { Q_OBJECT private: static const QPen AxisPen; static const int LabelHitPadding; protected: Trace(QString name); public: /** * Gets the name of this signal. */ QString get_name() const; /** * Sets the name of the signal. */ virtual void set_name(QString name); /** * Get the colour of the signal. */ QColor get_colour() const; /** * Set the colour of the signal. */ void set_colour(QColor colour); /** * Gets the vertical layout offset of this signal. */ int get_v_offset() const; /** * Sets the vertical layout offset of this signal. */ void set_v_offset(int v_offset); /** * Returns true if the trace is visible and enabled. */ virtual bool enabled() const = 0; virtual void set_view(pv::view::View *view); /** * Paints the background layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ virtual void paint_back(QPainter &p, int left, int right); /** * Paints the mid-layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ virtual void paint_mid(QPainter &p, int left, int right); /** * Paints the foreground layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ virtual void paint_fore(QPainter &p, int left, int right); /** * Paints the signal label into a QGLWidget. * @param p the QPainter to paint into. * @param right the x-coordinate of the right edge of the header * area. * @param hover true if the label is being hovered over by the mouse. */ virtual void paint_label(QPainter &p, int right, bool hover); /** * Determines if a point is in the header label rect. * @param left the x-coordinate of the left edge of the header * area. * @param right the x-coordinate of the right edge of the header * area. * @param point the point to test. */ bool pt_in_label_rect(int left, int right, const QPoint &point); virtual QMenu* create_context_menu(QWidget *parent); pv::widgets::Popup* create_popup(QWidget *parent); /** * Gets the y-offset of the axis. */ int get_y() const; /** * Computes the outline rectangle of a label. * @param p the QPainter to lay out text with. * @param right the x-coordinate of the right edge of the header * area. * @return Returns the rectangle of the signal label. */ QRectF get_label_rect(int right); protected: /** * Gets the text colour. * @remarks This colour is computed by comparing the lightness * of the trace colour against a threshold to determine whether * white or black would be more visible. */ QColor get_text_colour() const; /** * Paints a zero axis across the viewport. * @param p the QPainter to paint into. * @param y the y-offset of the axis. * @param left the x-coordinate of the left edge of the view. * @param right the x-coordinate of the right edge of the view. */ void paint_axis(QPainter &p, int y, int left, int right); void add_colour_option(QWidget *parent, QFormLayout *form); void create_popup_form(); virtual void populate_popup_form(QWidget *parent, QFormLayout *form); private slots: void on_text_changed(const QString &text); void on_colour_changed(const QColor &colour); void on_popup_closed(); signals: void visibility_changed(); void text_changed(); void colour_changed(); protected: pv::view::View *_view; QString _name; QColor _colour; int _v_offset; private: pv::widgets::Popup *_popup; QFormLayout *_popup_form; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_TRACE_H pulseview-0.2.0/pv/view/view.cpp000600 001750 001750 00000030145 12332253627 016253 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifdef ENABLE_DECODE #include #endif #include #include #include #include #include #include #include #include "decodetrace.h" #include "header.h" #include "ruler.h" #include "signal.h" #include "view.h" #include "viewport.h" #include "pv/sigsession.h" #include "pv/data/logic.h" #include "pv/data/logicsnapshot.h" using boost::shared_ptr; using boost::weak_ptr; using pv::data::SignalData; using std::deque; using std::list; using std::max; using std::make_pair; using std::min; using std::pair; using std::set; using std::vector; namespace pv { namespace view { const double View::MaxScale = 1e9; const double View::MinScale = 1e-15; const int View::MaxScrollValue = INT_MAX / 2; const int View::SignalHeight = 30; const int View::SignalMargin = 10; const int View::SignalSnapGridSize = 10; const QColor View::CursorAreaColour(220, 231, 243); const QSizeF View::LabelPadding(4, 0); View::View(SigSession &session, QWidget *parent) : QAbstractScrollArea(parent), _session(session), _viewport(new Viewport(*this)), _ruler(new Ruler(*this)), _header(new Header(*this)), _scale(1e-6), _offset(0), _v_offset(0), _updating_scroll(false), _show_cursors(false), _cursors(*this), _hover_point(-1, -1) { connect(horizontalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(h_scroll_value_changed(int))); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(v_scroll_value_changed(int))); connect(&_session, SIGNAL(signals_changed()), this, SLOT(signals_changed())); connect(&_session, SIGNAL(data_received()), this, SLOT(data_updated())); connect(&_session, SIGNAL(frame_ended()), this, SLOT(data_updated())); connect(_cursors.first().get(), SIGNAL(time_changed()), this, SLOT(marker_time_changed())); connect(_cursors.second().get(), SIGNAL(time_changed()), this, SLOT(marker_time_changed())); connect(_header, SIGNAL(geometry_updated()), this, SLOT(on_geometry_updated())); connect(_header, SIGNAL(signals_moved()), this, SLOT(on_signals_moved())); connect(_header, SIGNAL(selection_changed()), _ruler, SLOT(clear_selection())); connect(_ruler, SIGNAL(selection_changed()), _header, SLOT(clear_selection())); connect(_header, SIGNAL(selection_changed()), this, SIGNAL(selection_changed())); connect(_ruler, SIGNAL(selection_changed()), this, SIGNAL(selection_changed())); setViewport(_viewport); _viewport->installEventFilter(this); _ruler->installEventFilter(this); _header->installEventFilter(this); // Trigger the initial event manually. The default device has signals // which were created before this object came into being signals_changed(); } SigSession& View::session() { return _session; } const SigSession& View::session() const { return _session; } double View::scale() const { return _scale; } double View::offset() const { return _offset; } int View::v_offset() const { return _v_offset; } void View::zoom(double steps) { zoom(steps, _viewport->width() / 2); } void View::zoom(double steps, int offset) { set_zoom(_scale * pow(3.0/2.0, -steps), offset); } void View::zoom_fit() { const pair extents = get_time_extents(); const double delta = extents.second - extents.first; if (delta < 1e-12) return; assert(_viewport); const int w = _viewport->width(); if (w <= 0) return; const double scale = max(min(delta / w, MaxScale), MinScale); set_scale_offset(scale, extents.first); } void View::zoom_one_to_one() { using pv::data::SignalData; const vector< shared_ptr > sigs( session().get_signals()); // Make a set of all the visible data objects set< shared_ptr > visible_data = get_visible_data(); if (visible_data.empty()) return; double samplerate = 0.0; BOOST_FOREACH(const shared_ptr d, visible_data) { assert(d); samplerate = max(samplerate, d->samplerate()); } if (samplerate == 0.0) return; assert(_viewport); const int w = _viewport->width(); if (w <= 0) return; set_zoom(1.0 / samplerate, w / 2); } void View::set_scale_offset(double scale, double offset) { _scale = scale; _offset = offset; update_scroll(); _ruler->update(); _viewport->update(); scale_offset_changed(); } vector< shared_ptr > View::get_traces() const { const vector< shared_ptr > sigs( session().get_signals()); #ifdef ENABLE_DECODE const vector< shared_ptr > decode_sigs( session().get_decode_signals()); vector< shared_ptr > traces( sigs.size() + decode_sigs.size()); #else vector< shared_ptr > traces(sigs.size()); #endif vector< shared_ptr >::iterator i = traces.begin(); i = copy(sigs.begin(), sigs.end(), i); #ifdef ENABLE_DECODE i = copy(decode_sigs.begin(), decode_sigs.end(), i); #endif stable_sort(traces.begin(), traces.end(), compare_trace_v_offsets); return traces; } list > View::selected_items() const { list > items; // List the selected signals const vector< shared_ptr > traces(get_traces()); BOOST_FOREACH (shared_ptr t, traces) { assert(t); if (t->selected()) items.push_back(t); } // List the selected cursors if (_cursors.first()->selected()) items.push_back(_cursors.first()); if (_cursors.second()->selected()) items.push_back(_cursors.second()); return items; } set< shared_ptr > View::get_visible_data() const { const vector< shared_ptr > sigs( session().get_signals()); // Make a set of all the visible data objects set< shared_ptr > visible_data; BOOST_FOREACH(const shared_ptr sig, sigs) if (sig->enabled()) visible_data.insert(sig->data()); return visible_data; } pair View::get_time_extents() const { const set< shared_ptr > visible_data = get_visible_data(); if (visible_data.empty()) return make_pair(0.0, 0.0); double left_time = DBL_MAX, right_time = DBL_MIN; BOOST_FOREACH(const shared_ptr d, visible_data) { const double start_time = d->get_start_time(); double samplerate = d->samplerate(); samplerate = (samplerate <= 0.0) ? 1.0 : samplerate; left_time = min(left_time, start_time); right_time = max(right_time, start_time + d->get_max_sample_count() / samplerate); } assert(left_time < right_time); return make_pair(left_time, right_time); } bool View::cursors_shown() const { return _show_cursors; } void View::show_cursors(bool show) { _show_cursors = show; _ruler->update(); _viewport->update(); } void View::centre_cursors() { const double time_width = _scale * _viewport->width(); _cursors.first()->set_time(_offset + time_width * 0.4); _cursors.second()->set_time(_offset + time_width * 0.6); _ruler->update(); _viewport->update(); } CursorPair& View::cursors() { return _cursors; } const CursorPair& View::cursors() const { return _cursors; } const QPoint& View::hover_point() const { return _hover_point; } void View::normalize_layout() { const vector< shared_ptr > traces(get_traces()); int v_min = INT_MAX; BOOST_FOREACH(const shared_ptr t, traces) v_min = min(t->get_v_offset(), v_min); const int delta = -min(v_min, 0); BOOST_FOREACH(shared_ptr t, traces) t->set_v_offset(t->get_v_offset() + delta); verticalScrollBar()->setSliderPosition(_v_offset + delta); v_scroll_value_changed(verticalScrollBar()->sliderPosition()); } void View::update_viewport() { assert(_viewport); _viewport->update(); } void View::get_scroll_layout(double &length, double &offset) const { const pair extents = get_time_extents(); length = (extents.second - extents.first) / _scale; offset = _offset / _scale; } void View::set_zoom(double scale, int offset) { const double cursor_offset = _offset + _scale * offset; const double new_scale = max(min(scale, MaxScale), MinScale); const double new_offset = cursor_offset - new_scale * offset; set_scale_offset(new_scale, new_offset); } void View::update_scroll() { assert(_viewport); const QSize areaSize = _viewport->size(); // Set the horizontal scroll bar double length = 0, offset = 0; get_scroll_layout(length, offset); length = max(length - areaSize.width(), 0.0); horizontalScrollBar()->setPageStep(areaSize.width() / 2); _updating_scroll = true; if (length < MaxScrollValue) { horizontalScrollBar()->setRange(0, length); horizontalScrollBar()->setSliderPosition(offset); } else { horizontalScrollBar()->setRange(0, MaxScrollValue); horizontalScrollBar()->setSliderPosition( _offset * MaxScrollValue / (_scale * length)); } _updating_scroll = false; // Set the vertical scrollbar verticalScrollBar()->setPageStep(areaSize.height()); verticalScrollBar()->setRange(0, _viewport->get_total_height() + SignalMargin - areaSize.height()); } void View::update_layout() { setViewportMargins(_header->sizeHint().width(), _ruler->sizeHint().height(), 0, 0); _ruler->setGeometry(_viewport->x(), 0, _viewport->width(), _viewport->y()); _header->setGeometry(0, _viewport->y(), _viewport->x(), _viewport->height()); update_scroll(); } bool View::compare_trace_v_offsets(const shared_ptr &a, const shared_ptr &b) { assert(a); assert(b); return a->get_v_offset() < b->get_v_offset(); } bool View::eventFilter(QObject *object, QEvent *event) { const QEvent::Type type = event->type(); if (type == QEvent::MouseMove) { const QMouseEvent *const mouse_event = (QMouseEvent*)event; if (object == _viewport) _hover_point = mouse_event->pos(); else if (object == _ruler) _hover_point = QPoint(mouse_event->x(), 0); else if (object == _header) _hover_point = QPoint(0, mouse_event->y()); else _hover_point = QPoint(-1, -1); hover_point_changed(); } else if (type == QEvent::Leave) { _hover_point = QPoint(-1, -1); hover_point_changed(); } return QObject::eventFilter(object, event); } bool View::viewportEvent(QEvent *e) { switch(e->type()) { case QEvent::Paint: case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::Wheel: return false; default: return QAbstractScrollArea::viewportEvent(e); } } void View::resizeEvent(QResizeEvent*) { update_layout(); } void View::h_scroll_value_changed(int value) { if (_updating_scroll) return; const int range = horizontalScrollBar()->maximum(); if (range < MaxScrollValue) _offset = _scale * value; else { double length = 0, offset; get_scroll_layout(length, offset); _offset = _scale * length * value / MaxScrollValue; } _ruler->update(); _viewport->update(); } void View::v_scroll_value_changed(int value) { _v_offset = value; _header->update(); _viewport->update(); } void View::signals_changed() { int offset = SignalMargin + SignalHeight; const vector< shared_ptr > traces(get_traces()); BOOST_FOREACH(shared_ptr t, traces) { t->set_view(this); t->set_v_offset(offset); offset += SignalHeight + 2 * SignalMargin; } update_layout(); normalize_layout(); } void View::data_updated() { // Update the scroll bars update_scroll(); // Repaint the view _viewport->update(); } void View::marker_time_changed() { _ruler->update(); _viewport->update(); } void View::on_signals_moved() { update_scroll(); signals_moved(); } void View::on_geometry_updated() { update_layout(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/analogsignal.cpp000600 001750 001750 00000012261 12332253627 017737 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "analogsignal.h" #include "pv/data/analog.h" #include "pv/data/analogsnapshot.h" #include "pv/view/view.h" using boost::shared_ptr; using std::max; using std::min; using std::deque; namespace pv { namespace view { const QColor AnalogSignal::SignalColours[4] = { QColor(0xC4, 0xA0, 0x00), // Yellow QColor(0x87, 0x20, 0x7A), // Magenta QColor(0x20, 0x4A, 0x87), // Blue QColor(0x4E, 0x9A, 0x06) // Green }; const float AnalogSignal::EnvelopeThreshold = 256.0f; AnalogSignal::AnalogSignal(shared_ptr dev_inst, const sr_channel *const probe, shared_ptr data) : Signal(dev_inst, probe), _data(data), _scale(1.0f) { _colour = SignalColours[probe->index % countof(SignalColours)]; } AnalogSignal::~AnalogSignal() { } shared_ptr AnalogSignal::data() const { return _data; } shared_ptr AnalogSignal::analog_data() const { return _data; } void AnalogSignal::set_scale(float scale) { _scale = scale; } void AnalogSignal::paint_back(QPainter &p, int left, int right) { if (_probe->enabled) paint_axis(p, get_y(), left, right); } void AnalogSignal::paint_mid(QPainter &p, int left, int right) { assert(_data); assert(right >= left); assert(_view); const int y = _v_offset - _view->v_offset(); const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); if (!_probe->enabled) return; const deque< shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) return; const shared_ptr &snapshot = snapshots.front(); const double pixels_offset = offset / scale; const double samplerate = _data->samplerate(); const double start_time = _data->get_start_time(); const int64_t last_sample = snapshot->get_sample_count() - 1; const double samples_per_pixel = samplerate * scale; const double start = samplerate * (offset - start_time); const double end = start + samples_per_pixel * (right - left); const int64_t start_sample = min(max((int64_t)floor(start), (int64_t)0), last_sample); const int64_t end_sample = min(max((int64_t)ceil(end) + 1, (int64_t)0), last_sample); if (samples_per_pixel < EnvelopeThreshold) paint_trace(p, snapshot, y, left, start_sample, end_sample, pixels_offset, samples_per_pixel); else paint_envelope(p, snapshot, y, left, start_sample, end_sample, pixels_offset, samples_per_pixel); } void AnalogSignal::paint_trace(QPainter &p, const shared_ptr &snapshot, int y, int left, const int64_t start, const int64_t end, const double pixels_offset, const double samples_per_pixel) { const int64_t sample_count = end - start; const float *const samples = snapshot->get_samples(start, end); assert(samples); p.setPen(_colour); QPointF *points = new QPointF[sample_count]; QPointF *point = points; for (int64_t sample = start; sample != end; sample++) { const float x = (sample / samples_per_pixel - pixels_offset) + left; *point++ = QPointF(x, y - samples[sample - start] * _scale); } p.drawPolyline(points, point - points); delete[] samples; delete[] points; } void AnalogSignal::paint_envelope(QPainter &p, const shared_ptr &snapshot, int y, int left, const int64_t start, const int64_t end, const double pixels_offset, const double samples_per_pixel) { using pv::data::AnalogSnapshot; AnalogSnapshot::EnvelopeSection e; snapshot->get_envelope_section(e, start, end, samples_per_pixel); if (e.length < 2) return; p.setPen(QPen(Qt::NoPen)); p.setBrush(_colour); QRectF *const rects = new QRectF[e.length]; QRectF *rect = rects; for(uint64_t sample = 0; sample < e.length-1; sample++) { const float x = ((e.scale * sample + e.start) / samples_per_pixel - pixels_offset) + left; const AnalogSnapshot::EnvelopeSample *const s = e.samples + sample; // We overlap this sample with the next so that vertical // gaps do not appear during steep rising or falling edges const float b = y - max(s->max, (s+1)->min) * _scale; const float t = y - min(s->min, (s+1)->max) * _scale; float h = b - t; if(h >= 0.0f && h <= 1.0f) h = 1.0f; if(h <= 0.0f && h >= -1.0f) h = -1.0f; *rect++ = QRectF(x, t, 1.0f, h); } p.drawRects(rects, e.length); delete[] rects; delete[] e.samples; } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/tracepalette.h000600 001750 001750 00000002231 12332253627 017416 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_TRACEPALETTE_H #define PULSEVIEW_PV_TRACEPALETTE_H #include namespace pv { namespace view { class TracePalette { public: static const unsigned int Cols = 8; static const unsigned int Rows = 4; static const QColor Colours[Cols * Rows]; }; } // view } // pv #endif // PULSEVIEW_PV_VIEW_TRACEPALETTE_H pulseview-0.2.0/pv/view/view.h000600 001750 001750 00000010614 12332253627 015717 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_VIEW_H #define PULSEVIEW_PV_VIEW_VIEW_H #include #include #include #include #include #include #include #include #include "cursorpair.h" namespace pv { class SigSession; namespace view { class Header; class Ruler; class Trace; class Viewport; class View : public QAbstractScrollArea { Q_OBJECT private: static const double MaxScale; static const double MinScale; static const int MaxScrollValue; public: static const int SignalHeight; static const int SignalMargin; static const int SignalSnapGridSize; static const QColor CursorAreaColour; static const QSizeF LabelPadding; public: explicit View(SigSession &session, QWidget *parent = 0); SigSession& session(); const SigSession& session() const; /** * Returns the view time scale in seconds per pixel. */ double scale() const; /** * Returns the time offset of the left edge of the view in * seconds. */ double offset() const; int v_offset() const; void zoom(double steps); void zoom(double steps, int offset); void zoom_fit(); void zoom_one_to_one(); /** * Sets the scale and offset. * @param scale The new view scale in seconds per pixel. * @param offset The view time offset in seconds. */ void set_scale_offset(double scale, double offset); std::vector< boost::shared_ptr > get_traces() const; std::list > selected_items() const; std::set< boost::shared_ptr > get_visible_data() const; std::pair get_time_extents() const; /** * Returns true if cursors are displayed. false otherwise. */ bool cursors_shown() const; /** * Shows or hides the cursors. */ void show_cursors(bool show = true); /** * Moves the cursors to a convenient position in the view. */ void centre_cursors(); /** * Returns a reference to the pair of cursors. */ CursorPair& cursors(); /** * Returns a reference to the pair of cursors. */ const CursorPair& cursors() const; const QPoint& hover_point() const; void normalize_layout(); void update_viewport(); signals: void hover_point_changed(); void signals_moved(); void selection_changed(); void scale_offset_changed(); private: void get_scroll_layout(double &length, double &offset) const; /** * Simultaneously sets the zoom and offset. * @param scale The scale to set the view to in seconds per pixel. This * value is clamped between MinScale and MaxScale. * @param offset The offset of the left edge of the view in seconds. */ void set_zoom(double scale, int offset); void update_scroll(); void update_layout(); static bool compare_trace_v_offsets( const boost::shared_ptr &a, const boost::shared_ptr &b); private: bool eventFilter(QObject *object, QEvent *event); bool viewportEvent(QEvent *e); void resizeEvent(QResizeEvent *e); private slots: void h_scroll_value_changed(int value); void v_scroll_value_changed(int value); void signals_changed(); void data_updated(); void marker_time_changed(); void on_signals_moved(); void on_geometry_updated(); private: SigSession &_session; Viewport *_viewport; Ruler *_ruler; Header *_header; /// The view time scale in seconds per pixel. double _scale; /// The view time offset in seconds. double _offset; int _v_offset; bool _updating_scroll; bool _show_cursors; CursorPair _cursors; QPoint _hover_point; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_VIEW_H pulseview-0.2.0/pv/view/trace.cpp000600 001750 001750 00000014546 12332253627 016406 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "trace.h" #include "tracepalette.h" #include "view.h" #include #include namespace pv { namespace view { const QPen Trace::AxisPen(QColor(128, 128, 128, 64)); const int Trace::LabelHitPadding = 2; Trace::Trace(QString name) : _name(name), _v_offset(0), _popup(NULL), _popup_form(NULL) { } QString Trace::get_name() const { return _name; } void Trace::set_name(QString name) { _name = name; } QColor Trace::get_colour() const { return _colour; } void Trace::set_colour(QColor colour) { _colour = colour; } int Trace::get_v_offset() const { return _v_offset; } void Trace::set_v_offset(int v_offset) { _v_offset = v_offset; } void Trace::set_view(pv::view::View *view) { assert(view); _view = view; } void Trace::paint_back(QPainter &p, int left, int right) { (void)p; (void)left; (void)right; } void Trace::paint_mid(QPainter &p, int left, int right) { (void)p; (void)left; (void)right; } void Trace::paint_fore(QPainter &p, int left, int right) { (void)p; (void)left; (void)right; } void Trace::paint_label(QPainter &p, int right, bool hover) { assert(_view); const int y = _v_offset - _view->v_offset(); p.setBrush(_colour); if (!enabled()) return; const QColor colour = get_colour(); const QRectF label_rect = get_label_rect(right); // Paint the label const QPointF points[] = { label_rect.topLeft(), label_rect.topRight(), QPointF(right, y), label_rect.bottomRight(), label_rect.bottomLeft() }; const QPointF highlight_points[] = { QPointF(label_rect.left() + 1, label_rect.top() + 1), QPointF(label_rect.right(), label_rect.top() + 1), QPointF(right - 1, y), QPointF(label_rect.right(), label_rect.bottom() - 1), QPointF(label_rect.left() + 1, label_rect.bottom() - 1) }; if (selected()) { p.setPen(highlight_pen()); p.setBrush(Qt::transparent); p.drawPolygon(points, countof(points)); } p.setPen(Qt::transparent); p.setBrush(hover ? colour.lighter() : colour); p.drawPolygon(points, countof(points)); p.setPen(colour.lighter()); p.setBrush(Qt::transparent); p.drawPolygon(highlight_points, countof(highlight_points)); p.setPen(colour.darker()); p.setBrush(Qt::transparent); p.drawPolygon(points, countof(points)); // Paint the text p.setPen(get_text_colour()); p.setFont(QApplication::font()); p.drawText(label_rect, Qt::AlignCenter | Qt::AlignVCenter, _name); } bool Trace::pt_in_label_rect(int left, int right, const QPoint &point) { (void)left; const QRectF label = get_label_rect(right); return enabled() && QRectF( QPointF(label.left() - LabelHitPadding, label.top() - LabelHitPadding), QPointF(right, label.bottom() + LabelHitPadding) ).contains(point); } QMenu* Trace::create_context_menu(QWidget *parent) { QMenu *const menu = SelectableItem::create_context_menu(parent); return menu; } pv::widgets::Popup* Trace::create_popup(QWidget *parent) { using pv::widgets::Popup; _popup = new Popup(parent); create_popup_form(); connect(_popup, SIGNAL(closed()), this, SLOT(on_popup_closed())); return _popup; } int Trace::get_y() const { return _v_offset - _view->v_offset(); } QRectF Trace::get_label_rect(int right) { using pv::view::View; assert(_view); QFontMetrics m(QApplication::font()); const QSize text_size( m.boundingRect(QRect(), 0, _name).width(), m.boundingRect(QRect(), 0, "Tg").height()); const QSizeF label_size( text_size.width() + View::LabelPadding.width() * 2, ceilf((text_size.height() + View::LabelPadding.height() * 2) / 2) * 2); const float label_arrow_length = label_size.height() / 2; return QRectF( right - label_arrow_length - label_size.width() - 0.5, get_y() + 0.5f - label_size.height() / 2, label_size.width(), label_size.height()); } QColor Trace::get_text_colour() const { return (_colour.lightness() > 64) ? Qt::black : Qt::white; } void Trace::paint_axis(QPainter &p, int y, int left, int right) { p.setPen(AxisPen); p.drawLine(QPointF(left, y + 0.5f), QPointF(right, y + 0.5f)); } void Trace::add_colour_option(QWidget *parent, QFormLayout *form) { using pv::widgets::ColourButton; ColourButton *const colour_button = new ColourButton( TracePalette::Rows, TracePalette::Cols, parent); colour_button->set_palette(TracePalette::Colours); colour_button->set_colour(_colour); connect(colour_button, SIGNAL(selected(const QColor&)), this, SLOT(on_colour_changed(const QColor&))); form->addRow(tr("Colour"), colour_button); } void Trace::create_popup_form() { // Clear the layout // Transfer the layout and the child widgets to a temporary widget // which then goes out of scope destroying the layout and all the child // widgets. if (_popup_form) QWidget().setLayout(_popup_form); // Repopulate the popup _popup_form = new QFormLayout(_popup); _popup->setLayout(_popup_form); populate_popup_form(_popup, _popup_form); } void Trace::populate_popup_form(QWidget *parent, QFormLayout *form) { QLineEdit *const name_edit = new QLineEdit(parent); name_edit->setText(_name); connect(name_edit, SIGNAL(textChanged(const QString&)), this, SLOT(on_text_changed(const QString&))); form->addRow(tr("Name"), name_edit); add_colour_option(parent, form); } void Trace::on_popup_closed() { _popup = NULL; _popup_form = NULL; } void Trace::on_text_changed(const QString &text) { set_name(text); text_changed(); } void Trace::on_colour_changed(const QColor &colour) { set_colour(colour); colour_changed(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/decodetrace.h000600 001750 001750 00000011534 12332253627 017211 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_DECODETRACE_H #define PULSEVIEW_PV_VIEW_DECODETRACE_H #include "trace.h" #include #include #include #include #include struct srd_channel; struct srd_decoder; class QComboBox; namespace pv { class SigSession; namespace data { class DecoderStack; namespace decode { class Annotation; class Decoder; class Row; } } namespace widgets { class DecoderGroupBox; } namespace view { class DecodeTrace : public Trace { Q_OBJECT private: struct ProbeSelector { const QComboBox *_combo; const boost::shared_ptr _decoder; const srd_channel *_pdch; }; private: static const QColor DecodeColours[4]; static const QColor ErrorBgColour; static const QColor NoDecodeColour; static const int ArrowSize; static const double EndCapWidth; static const int DrawPadding; static const QColor Colours[16]; static const QColor OutlineColours[16]; public: DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index); bool enabled() const; const boost::shared_ptr& decoder() const; void set_view(pv::view::View *view); /** * Paints the background layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ void paint_back(QPainter &p, int left, int right); /** * Paints the mid-layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ void paint_mid(QPainter &p, int left, int right); /** * Paints the foreground layer of the trace with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal * @param right the x-coordinate of the right edge of the signal **/ void paint_fore(QPainter &p, int left, int right); void populate_popup_form(QWidget *parent, QFormLayout *form); QMenu* create_context_menu(QWidget *parent); void delete_pressed(); private: void draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, QColor text_colour, int text_height, int left, int right, double samples_per_pixel, double pixels_offset, int y, size_t base_colour) const; void draw_instant(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double x, int y) const; void draw_range(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double start, double end, int y) const; void draw_error(QPainter &p, const QString &message, int left, int right); void draw_unresolved_period(QPainter &p, int h, int left, int right, double samples_per_pixel, double pixels_offset); void create_decoder_form(int index, boost::shared_ptr &dec, QWidget *parent, QFormLayout *form); QComboBox* create_probe_selector(QWidget *parent, const boost::shared_ptr &dec, const srd_channel *const pdch); void commit_decoder_probes( boost::shared_ptr &dec); void commit_probes(); private slots: void on_new_decode_data(); void on_delete(); void on_probe_selected(int); void on_stack_decoder(srd_decoder *decoder); void on_delete_decoder(int index); void on_show_hide_decoder(int index); private: pv::SigSession &_session; boost::shared_ptr _decoder_stack; uint64_t _decode_start, _decode_end; std::list< boost::shared_ptr > _bindings; std::list _probe_selectors; std::vector _decoder_forms; std::vector _cur_row_headings; QSignalMapper _delete_mapper, _show_hide_mapper; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_DECODETRACE_H pulseview-0.2.0/pv/view/cursorpair.cpp000600 001750 001750 00000010040 12332253627 017462 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cursorpair.h" #include "ruler.h" #include "view.h" #include using boost::shared_ptr; using std::max; using std::make_pair; using std::min; using std::pair; namespace pv { namespace view { const int CursorPair::DeltaPadding = 8; CursorPair::CursorPair(View &view) : _first(new Cursor(view, 0.0)), _second(new Cursor(view, 1.0)), _view(view) { } shared_ptr CursorPair::first() const { return _first; } shared_ptr CursorPair::second() const { return _second; } QRectF CursorPair::get_label_rect(const QRect &rect) const { const QSizeF label_size( _text_size.width() + View::LabelPadding.width() * 2, _text_size.height() + View::LabelPadding.height() * 2); const pair offsets(get_cursor_offsets()); const pair normal_offsets( (offsets.first < offsets.second) ? offsets : make_pair(offsets.second, offsets.first)); const float height = label_size.height(); const float left = max(normal_offsets.first + DeltaPadding, -height); const float right = min(normal_offsets.second - DeltaPadding, (float)rect.width() + height); return QRectF(left, rect.height() - label_size.height() - Cursor::ArrowSize - Cursor::Offset - 0.5f, right - left, height); } void CursorPair::draw_markers(QPainter &p, const QRect &rect, unsigned int prefix) { assert(_first); assert(_second); compute_text_size(p, prefix); QRectF delta_rect(get_label_rect(rect)); const int radius = delta_rect.height() / 2; const QRectF text_rect(delta_rect.intersected( rect).adjusted(radius, 0, -radius, 0)); if(text_rect.width() >= _text_size.width()) { const int highlight_radius = delta_rect.height() / 2 - 2; p.setBrush(Cursor::FillColour); p.setPen(Cursor::LineColour); p.drawRoundedRect(delta_rect, radius, radius); delta_rect.adjust(1, 1, -1, -1); p.setPen(Cursor::HighlightColour); p.drawRoundedRect(delta_rect, highlight_radius, highlight_radius); p.setPen(Cursor::TextColour); p.drawText(text_rect, Qt::AlignCenter | Qt::AlignVCenter, Ruler::format_time(_second->time() - _first->time(), prefix, 2)); } // Paint the cursor markers _first->paint_label(p, rect, prefix); _second->paint_label(p, rect, prefix); } void CursorPair::draw_viewport_background(QPainter &p, const QRect &rect) { p.setPen(Qt::NoPen); p.setBrush(QBrush(View::CursorAreaColour)); const pair offsets(get_cursor_offsets()); const int l = (int)max(min( offsets.first, offsets.second), 0.0f); const int r = (int)min(max( offsets.first, offsets.second), (float)rect.width()); p.drawRect(l, 0, r - l, rect.height()); } void CursorPair::draw_viewport_foreground(QPainter &p, const QRect &rect) { assert(_first); assert(_second); _first->paint(p, rect); _second->paint(p, rect); } void CursorPair::compute_text_size(QPainter &p, unsigned int prefix) { assert(_first); assert(_second); _text_size = p.boundingRect(QRectF(), 0, Ruler::format_time( _second->time() - _first->time(), prefix, 2)).size(); } pair CursorPair::get_cursor_offsets() const { assert(_first); assert(_second); return pair( (_first->time() - _view.offset()) / _view.scale(), (_second->time() - _view.offset()) / _view.scale()); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/marginwidget.cpp000600 001750 001750 00000002016 12332253627 017756 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "view.h" #include "marginwidget.h" namespace pv { namespace view { MarginWidget::MarginWidget(View &parent) : QWidget(&parent), _view(parent) { } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/timemarker.cpp000600 001750 001750 00000004551 12332253627 017443 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "timemarker.h" #include "view.h" #include #include #include namespace pv { namespace view { TimeMarker::TimeMarker(View &view, const QColor &colour, double time) : _view(view), _colour(colour), _time(time), _value_action(NULL), _value_widget(NULL), _updating_value_widget(false) { } double TimeMarker::time() const { return _time; } float TimeMarker::get_x() const { return (_time - _view.offset()) / _view.scale(); } void TimeMarker::set_time(double time) { _time = time; if (_value_widget) { _updating_value_widget = true; _value_widget->setValue(time); _updating_value_widget = false; } time_changed(); } void TimeMarker::paint(QPainter &p, const QRect &rect) { const float x = get_x(); p.setPen(_colour); p.drawLine(QPointF(x, rect.top()), QPointF(x, rect.bottom())); } pv::widgets::Popup* TimeMarker::create_popup(QWidget *parent) { using pv::widgets::Popup; Popup *const popup = new Popup(parent); QFormLayout *const form = new QFormLayout(popup); popup->setLayout(form); _value_widget = new QDoubleSpinBox(parent); _value_widget->setDecimals(9); _value_widget->setSuffix("s"); _value_widget->setSingleStep(1e-6); _value_widget->setValue(_time); connect(_value_widget, SIGNAL(valueChanged(double)), this, SLOT(on_value_changed(double))); form->addRow(tr("Time"), _value_widget); return popup; } void TimeMarker::on_value_changed(double value) { if (!_updating_value_widget) { _time = value; time_changed(); } } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/marginwidget.h000600 001750 001750 00000002433 12332253627 017426 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_MARGINWIDGET_H #define PULSEVIEW_PV_MARGINWIDGET_H #include namespace pv { namespace view { class View; class MarginWidget : public QWidget { Q_OBJECT public: MarginWidget(pv::view::View &parent); public slots: virtual void clear_selection() = 0; signals: void selection_changed(); void geometry_updated(); protected: pv::view::View &_view; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_MARGINWIDGET_H pulseview-0.2.0/pv/view/header.h000600 001750 001750 00000003751 12332253627 016201 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_HEADER_H #define PULSEVIEW_PV_VIEW_HEADER_H #include #include #include #include #include "marginwidget.h" namespace pv { namespace view { class Trace; class View; class Header : public MarginWidget { Q_OBJECT private: static const int Padding; public: Header(View &parent); QSize sizeHint() const; private: boost::shared_ptr get_mouse_over_trace( const QPoint &pt); void clear_selection(); private: void paintEvent(QPaintEvent *event); private: void mousePressEvent(QMouseEvent * event); void mouseReleaseEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void leaveEvent(QEvent *event); void contextMenuEvent(QContextMenuEvent *event); void keyPressEvent(QKeyEvent *e); private slots: void on_signals_changed(); void on_signals_moved(); void on_trace_text_changed(); signals: void signals_moved(); private: QPoint _mouse_point; QPoint _mouse_down_point; bool _dragging; std::list, int> > _drag_traces; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_HEADER_H pulseview-0.2.0/pv/view/analogsignal.h000600 001750 001750 00000005221 12332253627 017402 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H #define PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H #include "signal.h" #include namespace pv { namespace data { class Analog; class AnalogSnapshot; } namespace view { class AnalogSignal : public Signal { private: static const QColor SignalColours[4]; static const float EnvelopeThreshold; public: AnalogSignal(boost::shared_ptr dev_inst, const sr_channel *const probe, boost::shared_ptr data); virtual ~AnalogSignal(); boost::shared_ptr data() const; boost::shared_ptr analog_data() const; void set_scale(float scale); /** * Paints the background layer of the signal with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ void paint_back(QPainter &p, int left, int right); /** * Paints the mid-layer of the signal with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ void paint_mid(QPainter &p, int left, int right); private: void paint_trace(QPainter &p, const boost::shared_ptr &snapshot, int y, int left, const int64_t start, const int64_t end, const double pixels_offset, const double samples_per_pixel); void paint_envelope(QPainter &p, const boost::shared_ptr &snapshot, int y, int left, const int64_t start, const int64_t end, const double pixels_offset, const double samples_per_pixel); private: boost::shared_ptr _data; float _scale; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_ANALOGSIGNAL_H pulseview-0.2.0/pv/view/signal.h000600 001750 001750 00000003747 12332253627 016233 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_SIGNAL_H #define PULSEVIEW_PV_VIEW_SIGNAL_H #include #include #include #include #include "trace.h" struct sr_channel; namespace pv { namespace data { class SignalData; } namespace device { class DevInst; } namespace view { class Signal : public Trace { Q_OBJECT protected: Signal(boost::shared_ptr dev_inst, const sr_channel *const probe); public: /** * Sets the name of the signal. */ void set_name(QString name); virtual boost::shared_ptr data() const = 0; /** * Returns true if the trace is visible and enabled. */ bool enabled() const; void enable(bool enable = true); const sr_channel* probe() const; virtual void populate_popup_form(QWidget *parent, QFormLayout *form); QMenu* create_context_menu(QWidget *parent); void delete_pressed(); private slots: void on_disable(); protected: boost::shared_ptr _dev_inst; const sr_channel *const _probe; QComboBox *_name_widget; bool _updating_name_widget; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_SIGNAL_H pulseview-0.2.0/pv/view/cursor.cpp000600 001750 001750 00000010337 12332253627 016617 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "cursor.h" #include "ruler.h" #include "view.h" #include #include #include #include #include #include #include using boost::shared_ptr; namespace pv { namespace view { const QColor Cursor::LineColour(32, 74, 135); const QColor Cursor::FillColour(52, 101, 164); const QColor Cursor::HighlightColour(83, 130, 186); const QColor Cursor::TextColour(Qt::white); const int Cursor::Offset = 1; const int Cursor::ArrowSize = 4; Cursor::Cursor(View &view, double time) : TimeMarker(view, LineColour, time) { } QRectF Cursor::get_label_rect(const QRect &rect) const { const shared_ptr other(get_other_cursor()); assert(other); const float x = (_time - _view.offset()) / _view.scale(); const QSizeF label_size( _text_size.width() + View::LabelPadding.width() * 2, _text_size.height() + View::LabelPadding.height() * 2); const float top = rect.height() - label_size.height() - Cursor::Offset - Cursor::ArrowSize - 0.5f; const float height = label_size.height(); if (_time > other->time()) return QRectF(x, top, label_size.width(), height); else return QRectF(x - label_size.width(), top, label_size.width(), height); } void Cursor::paint_label(QPainter &p, const QRect &rect, unsigned int prefix) { const shared_ptr other(get_other_cursor()); assert(other); compute_text_size(p, prefix); const QRectF r(get_label_rect(rect)); const QPointF left_points[] = { r.topLeft(), r.topRight(), r.bottomRight(), QPointF(r.left() + ArrowSize, r.bottom()), QPointF(r.left(), rect.bottom()), }; const QPointF right_points[] = { r.topRight(), r.topLeft(), r.bottomLeft(), QPointF(r.right() - ArrowSize, r.bottom()), QPointF(r.right(), rect.bottom()), }; const QPointF left_highlight_points[] = { QPointF(r.left() + 1, r.top() + 1), QPointF(r.right() - 1, r.top() + 1), QPointF(r.right() - 1, r.bottom() - 1), QPointF(r.left() + ArrowSize - 1, r.bottom() - 1), QPointF(r.left() + 1, rect.bottom() - 1), }; const QPointF right_highlight_points[] = { QPointF(r.right() - 1, r.top() + 1), QPointF(r.left() + 1, r.top() + 1), QPointF(r.left() + 1, r.bottom() - 1), QPointF(r.right() - ArrowSize + 1, r.bottom() - 1), QPointF(r.right() - 1, rect.bottom() - 1), }; const QPointF *const points = (_time > other->time()) ? left_points : right_points; const QPointF *const highlight_points = (_time > other->time()) ? left_highlight_points : right_highlight_points; if (selected()) { p.setPen(highlight_pen()); p.setBrush(Qt::transparent); p.drawPolygon(points, countof(left_points)); } p.setPen(Qt::transparent); p.setBrush(FillColour); p.drawPolygon(points, countof(left_points)); p.setPen(HighlightColour); p.setBrush(Qt::transparent); p.drawPolygon(highlight_points, countof(left_highlight_points)); p.setPen(LineColour); p.setBrush(Qt::transparent); p.drawPolygon(points, countof(left_points)); p.setPen(TextColour); p.drawText(r, Qt::AlignCenter | Qt::AlignVCenter, Ruler::format_time(_time, prefix, 2)); } void Cursor::compute_text_size(QPainter &p, unsigned int prefix) { _text_size = p.boundingRect(QRectF(), 0, Ruler::format_time(_time, prefix, 2)).size(); } shared_ptr Cursor::get_other_cursor() const { const CursorPair &cursors = _view.cursors(); return (cursors.first().get() == this) ? cursors.second() : cursors.first(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/signal.cpp000600 001750 001750 00000005447 12332253627 016565 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "signal.h" #include "view.h" #include using boost::shared_ptr; namespace pv { namespace view { const char *const ProbeNames[] = { "CLK", "DATA", "IN", "OUT", "RST", "Tx", "Rx", "EN", "SCLK", "MOSI", "MISO", "/SS", "SDA", "SCL" }; Signal::Signal(shared_ptr dev_inst, const sr_channel *const probe) : Trace(probe->name), _dev_inst(dev_inst), _probe(probe), _name_widget(NULL), _updating_name_widget(false) { assert(_probe); } void Signal::set_name(QString name) { Trace::set_name(name); _updating_name_widget = true; _name_widget->setEditText(name); _updating_name_widget = false; } bool Signal::enabled() const { return _probe->enabled; } void Signal::enable(bool enable) { _dev_inst->enable_probe(_probe, enable); visibility_changed(); } const sr_channel* Signal::probe() const { return _probe; } void Signal::populate_popup_form(QWidget *parent, QFormLayout *form) { _name_widget = new QComboBox(parent); _name_widget->setEditable(true); for(unsigned int i = 0; i < countof(ProbeNames); i++) _name_widget->insertItem(i, ProbeNames[i]); _name_widget->setEditText(_probe->name); connect(_name_widget, SIGNAL(editTextChanged(const QString&)), this, SLOT(on_text_changed(const QString&))); form->addRow(tr("Name"), _name_widget); add_colour_option(parent, form); } QMenu* Signal::create_context_menu(QWidget *parent) { QMenu *const menu = Trace::create_context_menu(parent); menu->addSeparator(); QAction *const disable = new QAction(tr("Disable"), this); disable->setShortcuts(QKeySequence::Delete); connect(disable, SIGNAL(triggered()), this, SLOT(on_disable())); menu->addAction(disable); return menu; } void Signal::delete_pressed() { on_disable(); } void Signal::on_disable() { enable(false); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/cursor.h000600 001750 001750 00000004204 12332253627 016260 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_CURSOR_H #define PULSEVIEW_PV_VIEW_CURSOR_H #include "timemarker.h" #include #include class QPainter; namespace pv { namespace view { class Cursor : public TimeMarker { Q_OBJECT public: static const QColor LineColour; static const QColor FillColour; static const QColor HighlightColour; static const QColor TextColour; static const int Offset; static const int ArrowSize; public: /** * Constructor. * @param view A reference to the view that owns this cursor pair. * @param time The time to set the flag to. */ Cursor(View &view, double time); public: /** * Gets the marker label rectangle. * @param rect The rectangle of the ruler client area. * @return Returns the label rectangle. */ QRectF get_label_rect(const QRect &rect) const; /** * Paints the cursor's label to the ruler. * @param p The painter to draw with. * @param rect The rectangle of the ruler client area. * @param prefix The index of the SI prefix to use. */ void paint_label(QPainter &p, const QRect &rect, unsigned int prefix); private: void compute_text_size(QPainter &p, unsigned int prefix); boost::shared_ptr get_other_cursor() const; private: QSizeF _text_size; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_CURSOR_H pulseview-0.2.0/pv/view/logicsignal.cpp000600 001750 001750 00000022173 12332253627 017576 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "logicsignal.h" #include "view.h" #include #include #include #include #include using boost::shared_ptr; using std::deque; using std::max; using std::min; using std::pair; using std::vector; namespace pv { namespace view { const float LogicSignal::Oversampling = 2.0f; const QColor LogicSignal::EdgeColour(0x80, 0x80, 0x80); const QColor LogicSignal::HighColour(0x00, 0xC0, 0x00); const QColor LogicSignal::LowColour(0xC0, 0x00, 0x00); const QColor LogicSignal::SignalColours[10] = { QColor(0x16, 0x19, 0x1A), // Black QColor(0x8F, 0x52, 0x02), // Brown QColor(0xCC, 0x00, 0x00), // Red QColor(0xF5, 0x79, 0x00), // Orange QColor(0xED, 0xD4, 0x00), // Yellow QColor(0x73, 0xD2, 0x16), // Green QColor(0x34, 0x65, 0xA4), // Blue QColor(0x75, 0x50, 0x7B), // Violet QColor(0x88, 0x8A, 0x85), // Grey QColor(0xEE, 0xEE, 0xEC), // White }; LogicSignal::LogicSignal(shared_ptr dev_inst, const sr_channel *const probe, shared_ptr data) : Signal(dev_inst, probe), _data(data), _trigger_none(NULL), _trigger_rising(NULL), _trigger_high(NULL), _trigger_falling(NULL), _trigger_low(NULL), _trigger_change(NULL) { _colour = SignalColours[probe->index % countof(SignalColours)]; } LogicSignal::~LogicSignal() { } shared_ptr LogicSignal::data() const { return _data; } shared_ptr LogicSignal::logic_data() const { return _data; } void LogicSignal::paint_back(QPainter &p, int left, int right) { if (_probe->enabled) paint_axis(p, get_y(), left, right); } void LogicSignal::paint_mid(QPainter &p, int left, int right) { using pv::view::View; QLineF *line; vector< pair > edges; assert(_probe); assert(_data); assert(right >= left); assert(_view); const int y = _v_offset - _view->v_offset(); const double scale = _view->scale(); assert(scale > 0); const double offset = _view->offset(); if (!_probe->enabled) return; const float high_offset = y - View::SignalHeight + 0.5f; const float low_offset = y + 0.5f; const deque< shared_ptr > &snapshots = _data->get_snapshots(); if (snapshots.empty()) return; const shared_ptr &snapshot = snapshots.front(); double samplerate = _data->samplerate(); // Show sample rate as 1Hz when it is unknown if (samplerate == 0.0) samplerate = 1.0; const double pixels_offset = offset / scale; const double start_time = _data->get_start_time(); const int64_t last_sample = snapshot->get_sample_count() - 1; const double samples_per_pixel = samplerate * scale; const double start = samplerate * (offset - start_time); const double end = start + samples_per_pixel * (right - left); snapshot->get_subsampled_edges(edges, min(max((int64_t)floor(start), (int64_t)0), last_sample), min(max((int64_t)ceil(end), (int64_t)0), last_sample), samples_per_pixel / Oversampling, _probe->index); assert(edges.size() >= 2); // Paint the edges const unsigned int edge_count = edges.size() - 2; QLineF *const edge_lines = new QLineF[edge_count]; line = edge_lines; for (vector::const_iterator i = edges.begin() + 1; i != edges.end() - 1; i++) { const float x = ((*i).first / samples_per_pixel - pixels_offset) + left; *line++ = QLineF(x, high_offset, x, low_offset); } p.setPen(EdgeColour); p.drawLines(edge_lines, edge_count); delete[] edge_lines; // Paint the caps const unsigned int max_cap_line_count = edges.size(); QLineF *const cap_lines = new QLineF[max_cap_line_count]; p.setPen(HighColour); paint_caps(p, cap_lines, edges, true, samples_per_pixel, pixels_offset, left, high_offset); p.setPen(LowColour); paint_caps(p, cap_lines, edges, false, samples_per_pixel, pixels_offset, left, low_offset); delete[] cap_lines; } void LogicSignal::paint_caps(QPainter &p, QLineF *const lines, vector< pair > &edges, bool level, double samples_per_pixel, double pixels_offset, float x_offset, float y_offset) { QLineF *line = lines; for (vector::const_iterator i = edges.begin(); i != (edges.end() - 1); i++) if ((*i).second == level) { *line++ = QLineF( ((*i).first / samples_per_pixel - pixels_offset) + x_offset, y_offset, ((*(i+1)).first / samples_per_pixel - pixels_offset) + x_offset, y_offset); } p.drawLines(lines, line - lines); } void LogicSignal::init_trigger_actions(QWidget *parent) { _trigger_none = new QAction(QIcon(":/icons/trigger-none.svg"), tr("No trigger"), parent); _trigger_none->setCheckable(true); connect(_trigger_none, SIGNAL(triggered()), this, SLOT(on_trigger_none())); _trigger_rising = new QAction(QIcon(":/icons/trigger-rising.svg"), tr("Trigger on rising edge"), parent); _trigger_rising->setCheckable(true); connect(_trigger_rising, SIGNAL(triggered()), this, SLOT(on_trigger_rising())); _trigger_high = new QAction(QIcon(":/icons/trigger-high.svg"), tr("Trigger on high level"), parent); _trigger_high->setCheckable(true); connect(_trigger_high, SIGNAL(triggered()), this, SLOT(on_trigger_high())); _trigger_falling = new QAction(QIcon(":/icons/trigger-falling.svg"), tr("Trigger on falling edge"), parent); _trigger_falling->setCheckable(true); connect(_trigger_falling, SIGNAL(triggered()), this, SLOT(on_trigger_falling())); _trigger_low = new QAction(QIcon(":/icons/trigger-low.svg"), tr("Trigger on low level"), parent); _trigger_low->setCheckable(true); connect(_trigger_low, SIGNAL(triggered()), this, SLOT(on_trigger_low())); _trigger_change = new QAction(QIcon(":/icons/trigger-change.svg"), tr("Trigger on rising or falling edge"), parent); _trigger_change->setCheckable(true); connect(_trigger_change, SIGNAL(triggered()), this, SLOT(on_trigger_change())); } void LogicSignal::populate_popup_form(QWidget *parent, QFormLayout *form) { GVariant *gvar; Signal::populate_popup_form(parent, form); // Add the trigger actions assert(_dev_inst); if ((gvar = _dev_inst->list_config(NULL, SR_CONF_TRIGGER_TYPE))) { const char *const trig_types = g_variant_get_string(gvar, NULL); if (trig_types && trig_types[0] != '\0') { _trigger_bar = new QToolBar(parent); init_trigger_actions(_trigger_bar); _trigger_bar->addAction(_trigger_none); add_trigger_action(trig_types, 'r', _trigger_rising); add_trigger_action(trig_types, '1', _trigger_high); add_trigger_action(trig_types, 'f', _trigger_falling); add_trigger_action(trig_types, '0', _trigger_low); add_trigger_action(trig_types, 'c', _trigger_change); update_trigger_actions(); form->addRow(tr("Trigger"), _trigger_bar); } g_variant_unref(gvar); } } void LogicSignal::add_trigger_action(const char *trig_types, char type, QAction *action) { while(*trig_types) if(*trig_types++ == type) { _trigger_bar->addAction(action); break; } } void LogicSignal::update_trigger_actions() { const char cur_trigger = _probe->trigger ? _probe->trigger[0] : '\0'; _trigger_none->setChecked(cur_trigger == '\0'); _trigger_rising->setChecked(cur_trigger == 'r'); _trigger_high->setChecked(cur_trigger == '1'); _trigger_falling->setChecked(cur_trigger == 'f'); _trigger_low->setChecked(cur_trigger == '0'); _trigger_change->setChecked(cur_trigger == 'c'); } void LogicSignal::set_trigger(char type) { const char trigger_type_string[2] = {type, 0}; const char *const trigger_string = (type != 0) ? trigger_type_string : NULL; assert(_dev_inst); const sr_dev_inst *const sdi = _dev_inst->dev_inst(); assert(sdi); const int probe_count = g_slist_length(sdi->channels); assert(probe_count > 0); assert(_probe && _probe->index < probe_count); for (int i = 0; i < probe_count; i++) { sr_dev_trigger_set(sdi, i, (i == _probe->index) ? trigger_string : NULL); } update_trigger_actions(); } void LogicSignal::on_trigger_none() { set_trigger('\0'); } void LogicSignal::on_trigger_rising() { set_trigger('r'); } void LogicSignal::on_trigger_high() { set_trigger('1'); } void LogicSignal::on_trigger_falling() { set_trigger('f'); } void LogicSignal::on_trigger_low() { set_trigger('0'); } void LogicSignal::on_trigger_change() { set_trigger('c'); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/selectableitem.cpp000600 001750 001750 00000003074 12332253627 020264 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "selectableitem.h" #include #include #include namespace pv { namespace view { const int SelectableItem::HighlightRadius = 6; SelectableItem::SelectableItem() : _context_parent(NULL), _selected(false) { } bool SelectableItem::selected() const { return _selected; } void SelectableItem::select(bool select) { _selected = select; } QMenu* SelectableItem::create_context_menu(QWidget *parent) { _context_parent = parent; return new QMenu(parent); } void SelectableItem::delete_pressed() { } QPen SelectableItem::highlight_pen() { return QPen(QApplication::palette().brush( QPalette::Highlight), HighlightRadius, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/viewport.cpp000600 001750 001750 00000007441 12332253627 017163 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "view.h" #include "viewport.h" #include "signal.h" #include "../sigsession.h" #include #include using boost::shared_ptr; using std::max; using std::min; using std::vector; namespace pv { namespace view { Viewport::Viewport(View &parent) : QWidget(&parent), _view(parent) { setMouseTracking(true); setAutoFillBackground(true); setBackgroundRole(QPalette::Base); connect(&_view.session(), SIGNAL(signals_changed()), this, SLOT(on_signals_changed())); connect(&_view, SIGNAL(signals_moved()), this, SLOT(on_signals_moved())); // Trigger the initial event manually. The default device has signals // which were created before this object came into being on_signals_changed(); } int Viewport::get_total_height() const { int h = 0; const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(const shared_ptr t, traces) { assert(t); h = max(t->get_v_offset() + View::SignalHeight, h); } return h; } void Viewport::paintEvent(QPaintEvent*) { const vector< shared_ptr > traces(_view.get_traces()); QPainter p(this); p.setRenderHint(QPainter::Antialiasing); if (_view.cursors_shown()) _view.cursors().draw_viewport_background(p, rect()); // Plot the signal BOOST_FOREACH(const shared_ptr t, traces) { assert(t); t->paint_back(p, 0, width()); } BOOST_FOREACH(const shared_ptr t, traces) t->paint_mid(p, 0, width()); BOOST_FOREACH(const shared_ptr t, traces) t->paint_fore(p, 0, width()); if (_view.cursors_shown()) _view.cursors().draw_viewport_foreground(p, rect()); p.end(); } void Viewport::mousePressEvent(QMouseEvent *event) { assert(event); _mouse_down_point = event->pos(); _mouse_down_offset = _view.offset(); } void Viewport::mouseMoveEvent(QMouseEvent *event) { assert(event); if (event->buttons() & Qt::LeftButton) { _view.set_scale_offset(_view.scale(), _mouse_down_offset + (_mouse_down_point - event->pos()).x() * _view.scale()); } } void Viewport::mouseDoubleClickEvent(QMouseEvent *event) { assert(event); if (event->buttons() & Qt::LeftButton) _view.zoom(2.0, event->x()); else if (event->buttons() & Qt::RightButton) _view.zoom(-2.0, event->x()); } void Viewport::wheelEvent(QWheelEvent *event) { assert(event); if (event->orientation() == Qt::Vertical) { // Vertical scrolling is interpreted as zooming in/out _view.zoom(event->delta() / 120, event->x()); } else if (event->orientation() == Qt::Horizontal) { // Horizontal scrolling is interpreted as moving left/right _view.set_scale_offset(_view.scale(), event->delta() * _view.scale() + _view.offset()); } } void Viewport::on_signals_changed() { const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(shared_ptr t, traces) { assert(t); connect(t.get(), SIGNAL(visibility_changed()), this, SLOT(update())); } } void Viewport::on_signals_moved() { update(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/timemarker.h000600 001750 001750 00000005231 12332253627 017104 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_MARKER_H #define PULSEVIEW_PV_VIEW_MARKER_H #include #include #include #include #include #include "selectableitem.h" class QPainter; class QRect; namespace pv { namespace view { class View; class TimeMarker : public SelectableItem { Q_OBJECT protected: /** * Constructor. * @param view A reference to the view that owns this marker. * @param colour A reference to the colour of this cursor. * @param time The time to set the flag to. */ TimeMarker(View &view, const QColor &colour, double time); public: /** * Gets the time of the marker. */ double time() const; /** * Sets the time of the marker. */ void set_time(double time); float get_x() const; /** * Paints the marker to the viewport. * @param p The painter to draw with. * @param rect The rectangle of the viewport client area. */ virtual void paint(QPainter &p, const QRect &rect); /** * Gets the marker label rectangle. * @param rect The rectangle of the ruler client area. * @return Returns the label rectangle. */ virtual QRectF get_label_rect(const QRect &rect) const = 0; /** * Paints the marker's label to the ruler. * @param p The painter to draw with. * @param rect The rectangle of the ruler client area. * @param prefix The SI prefix to paint time value with. */ virtual void paint_label(QPainter &p, const QRect &rect, unsigned int prefix) = 0; pv::widgets::Popup* create_popup(QWidget *parent); private slots: void on_value_changed(double value); signals: void time_changed(); protected: View &_view; const QColor &_colour; double _time; QSizeF _text_size; QWidgetAction *_value_action; QDoubleSpinBox *_value_widget; bool _updating_value_widget; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_MARKER_H pulseview-0.2.0/pv/view/decodetrace.cpp000600 001750 001750 00000045443 12332253627 017552 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ extern "C" { #include } #include #include #include #include #include #include #include #include #include #include #include "decodetrace.h" #include #include #include #include #include #include #include #include #include #include using boost::dynamic_pointer_cast; using boost::shared_ptr; using std::list; using std::max; using std::map; using std::min; using std::vector; namespace pv { namespace view { const QColor DecodeTrace::DecodeColours[4] = { QColor(0xEF, 0x29, 0x29), // Red QColor(0xFC, 0xE9, 0x4F), // Yellow QColor(0x8A, 0xE2, 0x34), // Green QColor(0x72, 0x9F, 0xCF) // Blue }; const QColor DecodeTrace::ErrorBgColour = QColor(0xEF, 0x29, 0x29); const QColor DecodeTrace::NoDecodeColour = QColor(0x88, 0x8A, 0x85); const int DecodeTrace::ArrowSize = 4; const double DecodeTrace::EndCapWidth = 5; const int DecodeTrace::DrawPadding = 100; const QColor DecodeTrace::Colours[16] = { QColor(0xEF, 0x29, 0x29), QColor(0xF6, 0x6A, 0x32), QColor(0xFC, 0xAE, 0x3E), QColor(0xFB, 0xCA, 0x47), QColor(0xFC, 0xE9, 0x4F), QColor(0xCD, 0xF0, 0x40), QColor(0x8A, 0xE2, 0x34), QColor(0x4E, 0xDC, 0x44), QColor(0x55, 0xD7, 0x95), QColor(0x64, 0xD1, 0xD2), QColor(0x72, 0x9F, 0xCF), QColor(0xD4, 0x76, 0xC4), QColor(0x9D, 0x79, 0xB9), QColor(0xAD, 0x7F, 0xA8), QColor(0xC2, 0x62, 0x9B), QColor(0xD7, 0x47, 0x6F) }; const QColor DecodeTrace::OutlineColours[16] = { QColor(0x77, 0x14, 0x14), QColor(0x7B, 0x35, 0x19), QColor(0x7E, 0x57, 0x1F), QColor(0x7D, 0x65, 0x23), QColor(0x7E, 0x74, 0x27), QColor(0x66, 0x78, 0x20), QColor(0x45, 0x71, 0x1A), QColor(0x27, 0x6E, 0x22), QColor(0x2A, 0x6B, 0x4A), QColor(0x32, 0x68, 0x69), QColor(0x39, 0x4F, 0x67), QColor(0x6A, 0x3B, 0x62), QColor(0x4E, 0x3C, 0x5C), QColor(0x56, 0x3F, 0x54), QColor(0x61, 0x31, 0x4D), QColor(0x6B, 0x23, 0x37) }; DecodeTrace::DecodeTrace(pv::SigSession &session, boost::shared_ptr decoder_stack, int index) : Trace(QString::fromUtf8( decoder_stack->stack().front()->decoder()->name)), _session(session), _decoder_stack(decoder_stack), _delete_mapper(this), _show_hide_mapper(this) { assert(_decoder_stack); _colour = DecodeColours[index % countof(DecodeColours)]; connect(_decoder_stack.get(), SIGNAL(new_decode_data()), this, SLOT(on_new_decode_data())); connect(&_delete_mapper, SIGNAL(mapped(int)), this, SLOT(on_delete_decoder(int))); connect(&_show_hide_mapper, SIGNAL(mapped(int)), this, SLOT(on_show_hide_decoder(int))); } bool DecodeTrace::enabled() const { return true; } const boost::shared_ptr& DecodeTrace::decoder() const { return _decoder_stack; } void DecodeTrace::set_view(pv::view::View *view) { assert(view); Trace::set_view(view); } void DecodeTrace::paint_back(QPainter &p, int left, int right) { Trace::paint_back(p, left, right); paint_axis(p, get_y(), left, right); } void DecodeTrace::paint_mid(QPainter &p, int left, int right) { using namespace pv::data::decode; const double scale = _view->scale(); assert(scale > 0); double samplerate = _decoder_stack->samplerate(); _cur_row_headings.clear(); // Show sample rate as 1Hz when it is unknown if (samplerate == 0.0) samplerate = 1.0; const double pixels_offset = (_view->offset() - _decoder_stack->get_start_time()) / scale; const double samples_per_pixel = samplerate * scale; const uint64_t start_sample = (uint64_t)max((left + pixels_offset) * samples_per_pixel, 0.0); const uint64_t end_sample = (uint64_t)max((right + pixels_offset) * samples_per_pixel, 0.0); QFontMetrics m(QApplication::font()); const int text_height = m.boundingRect(QRect(), 0, "Tg").height(); const int annotation_height = (text_height * 5) / 4; const int row_height = (text_height * 6) / 4; assert(_decoder_stack); const QString err = _decoder_stack->error_message(); if (!err.isEmpty()) { draw_unresolved_period(p, annotation_height, left, right, samples_per_pixel, pixels_offset); draw_error(p, err, left, right); return; } // Iterate through the rows assert(_view); int y = get_y(); assert(_decoder_stack); const vector rows(_decoder_stack->get_visible_rows()); for (size_t i = 0; i < rows.size(); i++) { const Row &row = rows[i]; size_t base_colour = 0x13579BDF; boost::hash_combine(base_colour, this); boost::hash_combine(base_colour, row.decoder()); boost::hash_combine(base_colour, row.row()); base_colour >>= 16; vector annotations; _decoder_stack->get_annotation_subset(annotations, row, start_sample, end_sample); if (!annotations.empty()) { BOOST_FOREACH(const Annotation &a, annotations) draw_annotation(a, p, get_text_colour(), annotation_height, left, right, samples_per_pixel, pixels_offset, y, base_colour); y += row_height; _cur_row_headings.push_back(row.title()); } } // Draw the hatching draw_unresolved_period(p, annotation_height, left, right, samples_per_pixel, pixels_offset); } void DecodeTrace::paint_fore(QPainter &p, int left, int right) { using namespace pv::data::decode; (void)right; QFontMetrics m(QApplication::font()); const int text_height = m.boundingRect(QRect(), 0, "Tg").height(); const int row_height = (text_height * 6) / 4; for (size_t i = 0; i < _cur_row_headings.size(); i++) { const int y = i * row_height + get_y(); p.setPen(QPen(Qt::NoPen)); p.setBrush(QApplication::palette().brush(QPalette::WindowText)); if (i != 0) { const QPointF points[] = { QPointF(left, y - ArrowSize), QPointF(left + ArrowSize, y), QPointF(left, y + ArrowSize) }; p.drawPolygon(points, countof(points)); } const QRect r(left + ArrowSize * 2, y - row_height / 2, right - left, row_height); const QString h(_cur_row_headings[i]); const int f = Qt::AlignLeft | Qt::AlignVCenter | Qt::TextDontClip; // Draw the outline p.setPen(QApplication::palette().color(QPalette::Base)); for (int dx = -1; dx <= 1; dx++) for (int dy = -1; dy <= 1; dy++) if (dx != 0 && dy != 0) p.drawText(r.translated(dx, dy), f, h); // Draw the text p.setPen(QApplication::palette().color(QPalette::WindowText)); p.drawText(r, f, h); } } void DecodeTrace::populate_popup_form(QWidget *parent, QFormLayout *form) { using pv::data::decode::Decoder; assert(form); assert(parent); assert(_decoder_stack); // Add the standard options Trace::populate_popup_form(parent, form); // Add the decoder options _bindings.clear(); _probe_selectors.clear(); _decoder_forms.clear(); const list< shared_ptr >& stack = _decoder_stack->stack(); if (stack.empty()) { QLabel *const l = new QLabel( tr("

No decoders in the stack

")); l->setAlignment(Qt::AlignCenter); form->addRow(l); } else { list< shared_ptr >::const_iterator iter = stack.begin(); for (int i = 0; i < (int)stack.size(); i++, iter++) { shared_ptr dec(*iter); create_decoder_form(i, dec, parent, form); } form->addRow(new QLabel( tr("* Required channels"), parent)); } // Add stacking button pv::widgets::DecoderMenu *const decoder_menu = new pv::widgets::DecoderMenu(parent); connect(decoder_menu, SIGNAL(decoder_selected(srd_decoder*)), this, SLOT(on_stack_decoder(srd_decoder*))); QPushButton *const stack_button = new QPushButton(tr("Stack Decoder"), parent); stack_button->setMenu(decoder_menu); QHBoxLayout *stack_button_box = new QHBoxLayout; stack_button_box->addWidget(stack_button, 0, Qt::AlignRight); form->addRow(stack_button_box); } QMenu* DecodeTrace::create_context_menu(QWidget *parent) { QMenu *const menu = Trace::create_context_menu(parent); menu->addSeparator(); QAction *const del = new QAction(tr("Delete"), this); del->setShortcuts(QKeySequence::Delete); connect(del, SIGNAL(triggered()), this, SLOT(on_delete())); menu->addAction(del); return menu; } void DecodeTrace::draw_annotation(const pv::data::decode::Annotation &a, QPainter &p, QColor text_color, int h, int left, int right, double samples_per_pixel, double pixels_offset, int y, size_t base_colour) const { const double start = a.start_sample() / samples_per_pixel - pixels_offset; const double end = a.end_sample() / samples_per_pixel - pixels_offset; const size_t colour = (base_colour + a.format()) % countof(Colours); const QColor &fill = Colours[colour]; const QColor &outline = OutlineColours[colour]; if (start > right + DrawPadding || end < left - DrawPadding) return; if (a.start_sample() == a.end_sample()) draw_instant(a, p, fill, outline, text_color, h, start, y); else draw_range(a, p, fill, outline, text_color, h, start, end, y); } void DecodeTrace::draw_instant(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double x, int y) const { const QString text = a.annotations().empty() ? QString() : a.annotations().back(); const double w = min((double)p.boundingRect(QRectF(), 0, text).width(), 0.0) + h; const QRectF rect(x - w / 2, y - h / 2, w, h); p.setPen(outline); p.setBrush(fill); p.drawRoundedRect(rect, h / 2, h / 2); p.setPen(text_color); p.drawText(rect, Qt::AlignCenter | Qt::AlignVCenter, text); } void DecodeTrace::draw_range(const pv::data::decode::Annotation &a, QPainter &p, QColor fill, QColor outline, QColor text_color, int h, double start, double end, int y) const { const double top = y + .5 - h / 2; const double bottom = y + .5 + h / 2; const vector annotations = a.annotations(); p.setPen(outline); p.setBrush(fill); // If the two ends are within 1 pixel, draw a vertical line if (start + 1.0 > end) { p.drawLine(QPointF(start, top), QPointF(start, bottom)); return; } const double cap_width = min((end - start) / 4, EndCapWidth); QPointF pts[] = { QPointF(start, y + .5f), QPointF(start + cap_width, top), QPointF(end - cap_width, top), QPointF(end, y + .5f), QPointF(end - cap_width, bottom), QPointF(start + cap_width, bottom) }; p.drawConvexPolygon(pts, countof(pts)); if (annotations.empty()) return; QRectF rect(start + cap_width, y - h / 2, end - start - cap_width * 2, h); if (rect.width() <= 4) return; p.setPen(text_color); // Try to find an annotation that will fit QString best_annotation; int best_width = 0; BOOST_FOREACH(const QString &a, annotations) { const int w = p.boundingRect(QRectF(), 0, a).width(); if (w <= rect.width() && w > best_width) best_annotation = a, best_width = w; } if (best_annotation.isEmpty()) best_annotation = annotations.back(); // If not ellide the last in the list p.drawText(rect, Qt::AlignCenter, p.fontMetrics().elidedText( best_annotation, Qt::ElideRight, rect.width())); } void DecodeTrace::draw_error(QPainter &p, const QString &message, int left, int right) { const int y = get_y(); p.setPen(ErrorBgColour.darker()); p.setBrush(ErrorBgColour); const QRectF bounding_rect = QRectF(left, INT_MIN / 2 + y, right - left, INT_MAX); const QRectF text_rect = p.boundingRect(bounding_rect, Qt::AlignCenter, message); const float r = text_rect.height() / 4; p.drawRoundedRect(text_rect.adjusted(-r, -r, r, r), r, r, Qt::AbsoluteSize); p.setPen(get_text_colour()); p.drawText(text_rect, message); } void DecodeTrace::draw_unresolved_period(QPainter &p, int h, int left, int right, double samples_per_pixel, double pixels_offset) { using namespace pv::data; using pv::data::decode::Decoder; assert(_decoder_stack); shared_ptr data; shared_ptr logic_signal; const list< shared_ptr > &stack = _decoder_stack->stack(); // We get the logic data of the first probe in the list. // This works because we are currently assuming all // LogicSignals have the same data/snapshot BOOST_FOREACH (const shared_ptr &dec, stack) if (dec && !dec->channels().empty() && ((logic_signal = (*dec->channels().begin()).second)) && ((data = logic_signal->logic_data()))) break; if (!data || data->get_snapshots().empty()) return; const shared_ptr snapshot = data->get_snapshots().front(); assert(snapshot); const int64_t sample_count = (int64_t)snapshot->get_sample_count(); if (sample_count == 0) return; const int64_t samples_decoded = _decoder_stack->samples_decoded(); if (sample_count == samples_decoded) return; const int y = get_y(); const double start = max(samples_decoded / samples_per_pixel - pixels_offset, left - 1.0); const double end = min(sample_count / samples_per_pixel - pixels_offset, right + 1.0); const QRectF no_decode_rect(start, y - h/2 + 0.5, end - start, h); p.setPen(QPen(Qt::NoPen)); p.setBrush(Qt::white); p.drawRect(no_decode_rect); p.setPen(NoDecodeColour); p.setBrush(QBrush(NoDecodeColour, Qt::Dense6Pattern)); p.drawRect(no_decode_rect); } void DecodeTrace::create_decoder_form(int index, shared_ptr &dec, QWidget *parent, QFormLayout *form) { const GSList *l; assert(dec); const srd_decoder *const decoder = dec->decoder(); assert(decoder); pv::widgets::DecoderGroupBox *const group = new pv::widgets::DecoderGroupBox( QString::fromUtf8(decoder->name)); group->set_decoder_visible(dec->shown()); _delete_mapper.setMapping(group, index); connect(group, SIGNAL(delete_decoder()), &_delete_mapper, SLOT(map())); _show_hide_mapper.setMapping(group, index); connect(group, SIGNAL(show_hide_decoder()), &_show_hide_mapper, SLOT(map())); QFormLayout *const decoder_form = new QFormLayout; group->add_layout(decoder_form); // Add the mandatory channels for(l = decoder->channels; l; l = l->next) { const struct srd_channel *const pdch = (struct srd_channel *)l->data; QComboBox *const combo = create_probe_selector(parent, dec, pdch); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_probe_selected(int))); decoder_form->addRow(tr("%1 (%2) *") .arg(QString::fromUtf8(pdch->name)) .arg(QString::fromUtf8(pdch->desc)), combo); const ProbeSelector s = {combo, dec, pdch}; _probe_selectors.push_back(s); } // Add the optional channels for(l = decoder->opt_channels; l; l = l->next) { const struct srd_channel *const pdch = (struct srd_channel *)l->data; QComboBox *const combo = create_probe_selector(parent, dec, pdch); connect(combo, SIGNAL(currentIndexChanged(int)), this, SLOT(on_probe_selected(int))); decoder_form->addRow(tr("%1 (%2)") .arg(QString::fromUtf8(pdch->name)) .arg(QString::fromUtf8(pdch->desc)), combo); const ProbeSelector s = {combo, dec, pdch}; _probe_selectors.push_back(s); } // Add the options shared_ptr binding( new prop::binding::DecoderOptions(_decoder_stack, dec)); binding->add_properties_to_form(decoder_form, true); _bindings.push_back(binding); form->addRow(group); _decoder_forms.push_back(group); } QComboBox* DecodeTrace::create_probe_selector( QWidget *parent, const shared_ptr &dec, const srd_channel *const pdch) { assert(dec); const vector< shared_ptr > sigs = _session.get_signals(); assert(_decoder_stack); const map >::const_iterator probe_iter = dec->channels().find(pdch); QComboBox *selector = new QComboBox(parent); selector->addItem("-", qVariantFromValue((void*)NULL)); if (probe_iter == dec->channels().end()) selector->setCurrentIndex(0); for(size_t i = 0; i < sigs.size(); i++) { const shared_ptr s(sigs[i]); assert(s); if (dynamic_pointer_cast(s) && s->enabled()) { selector->addItem(s->get_name(), qVariantFromValue((void*)s.get())); if ((*probe_iter).second == s) selector->setCurrentIndex(i + 1); } } return selector; } void DecodeTrace::commit_decoder_probes(shared_ptr &dec) { assert(dec); map > probe_map; const vector< shared_ptr > sigs = _session.get_signals(); BOOST_FOREACH(const ProbeSelector &s, _probe_selectors) { if(s._decoder != dec) break; const LogicSignal *const selection = (LogicSignal*)s._combo->itemData( s._combo->currentIndex()).value(); BOOST_FOREACH(shared_ptr sig, sigs) if(sig.get() == selection) { probe_map[s._pdch] = dynamic_pointer_cast(sig); break; } } dec->set_probes(probe_map); } void DecodeTrace::commit_probes() { assert(_decoder_stack); BOOST_FOREACH(shared_ptr dec, _decoder_stack->stack()) commit_decoder_probes(dec); _decoder_stack->begin_decode(); } void DecodeTrace::on_new_decode_data() { if (_view) _view->update_viewport(); } void DecodeTrace::delete_pressed() { on_delete(); } void DecodeTrace::on_delete() { _session.remove_decode_signal(this); } void DecodeTrace::on_probe_selected(int) { commit_probes(); } void DecodeTrace::on_stack_decoder(srd_decoder *decoder) { assert(decoder); assert(_decoder_stack); _decoder_stack->push(shared_ptr( new data::decode::Decoder(decoder))); _decoder_stack->begin_decode(); create_popup_form(); } void DecodeTrace::on_delete_decoder(int index) { _decoder_stack->remove(index); // Update the popup create_popup_form(); _decoder_stack->begin_decode(); } void DecodeTrace::on_show_hide_decoder(int index) { using pv::data::decode::Decoder; const list< shared_ptr > stack(_decoder_stack->stack()); // Find the decoder in the stack list< shared_ptr >::const_iterator iter = stack.begin(); for(int i = 0; i < index; i++, iter++) assert(iter != stack.end()); shared_ptr dec = *iter; assert(dec); const bool show = !dec->shown(); dec->show(show); assert(index < (int)_decoder_forms.size()); _decoder_forms[index]->set_decoder_visible(show); _view->update_viewport(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/cursorpair.h000600 001750 001750 00000003677 12332253627 017151 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_CURSORPAIR_H #define PULSEVIEW_PV_VIEW_CURSORPAIR_H #include "cursor.h" #include #include class QPainter; namespace pv { namespace view { class CursorPair { private: static const int DeltaPadding; public: /** * Constructor. * @param view A reference to the view that owns this cursor pair. */ CursorPair(View &view); /** * Returns a pointer to the first cursor. */ boost::shared_ptr first() const; /** * Returns a pointer to the second cursor. */ boost::shared_ptr second() const; public: QRectF get_label_rect(const QRect &rect) const; void draw_markers(QPainter &p, const QRect &rect, unsigned int prefix); void draw_viewport_background(QPainter &p, const QRect &rect); void draw_viewport_foreground(QPainter &p, const QRect &rect); void compute_text_size(QPainter &p, unsigned int prefix); std::pair get_cursor_offsets() const; private: boost::shared_ptr _first, _second; const View &_view; QSizeF _text_size; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_CURSORPAIR_H pulseview-0.2.0/pv/view/ruler.h000600 001750 001750 00000003667 12332253627 016110 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_RULER_H #define PULSEVIEW_PV_VIEW_RULER_H #include #include "marginwidget.h" namespace pv { namespace view { class TimeMarker; class View; class Ruler : public MarginWidget { Q_OBJECT private: static const int RulerHeight; static const int MinorTickSubdivision; static const int ScaleUnits[3]; static const QString SIPrefixes[9]; static const int FirstSIPrefixPower; static const int HoverArrowSize; public: Ruler(View &parent); void clear_selection(); static QString format_time(double t, unsigned int prefix, unsigned precision = 0); public: QSize sizeHint() const; private: void paintEvent(QPaintEvent *event); void mouseMoveEvent(QMouseEvent *e); void mousePressEvent(QMouseEvent *e); void mouseReleaseEvent(QMouseEvent *); private: /** * Draw a hover arrow under the cursor position. */ void draw_hover_mark(QPainter &p); private slots: void hover_point_changed(); private: boost::weak_ptr _grabbed_marker; QPoint _mouse_down_point; bool _dragging; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_RULER_H pulseview-0.2.0/pv/view/viewport.h000600 001750 001750 00000003144 12332253627 016624 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_VIEWPORT_H #define PULSEVIEW_PV_VIEW_VIEWPORT_H #include #include class QPainter; class QPaintEvent; class SigSession; namespace pv { namespace view { class View; class Viewport : public QWidget { Q_OBJECT public: explicit Viewport(View &parent); int get_total_height() const; protected: void paintEvent(QPaintEvent *event); private: void mousePressEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event); void mouseDoubleClickEvent(QMouseEvent * event); void wheelEvent(QWheelEvent *event); private slots: void on_signals_changed(); void on_signals_moved(); private: View &_view; QPoint _mouse_down_point; double _mouse_down_offset; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_VIEWPORT_H pulseview-0.2.0/pv/view/header.cpp000600 001750 001750 00000015252 12332253627 016533 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "header.h" #include "view.h" #include "signal.h" #include "../sigsession.h" #include #include #include #include #include #include #include #include using boost::shared_ptr; using std::max; using std::make_pair; using std::pair; using std::vector; namespace pv { namespace view { const int Header::Padding = 12; Header::Header(View &parent) : MarginWidget(parent), _dragging(false) { setFocusPolicy(Qt::ClickFocus); setMouseTracking(true); connect(&_view.session(), SIGNAL(signals_changed()), this, SLOT(on_signals_changed())); connect(&_view, SIGNAL(signals_moved()), this, SLOT(on_signals_moved())); // Trigger the initial event manually. The default device has signals // which were created before this object came into being on_signals_changed(); } QSize Header::sizeHint() const { int max_width = 0; const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(shared_ptr t, traces) { assert(t); max_width = max(max_width, (int)t->get_label_rect(0).width()); } return QSize(max_width + Padding, 0); } shared_ptr Header::get_mouse_over_trace(const QPoint &pt) { const int w = width(); const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(const shared_ptr t, traces) { assert(t); if (t->pt_in_label_rect(0, w, pt)) return t; } return shared_ptr(); } void Header::clear_selection() { const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(const shared_ptr t, traces) { assert(t); t->select(false); } update(); } void Header::paintEvent(QPaintEvent*) { const int w = width(); const vector< shared_ptr > traces(_view.get_traces()); QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const bool dragging = !_drag_traces.empty(); BOOST_FOREACH(const shared_ptr t, traces) { assert(t); const bool highlight = !dragging && t->pt_in_label_rect( 0, w, _mouse_point); t->paint_label(painter, w, highlight); } painter.end(); } void Header::mousePressEvent(QMouseEvent *event) { assert(event); const vector< shared_ptr > traces(_view.get_traces()); if (event->button() & Qt::LeftButton) { _mouse_down_point = event->pos(); // Save the offsets of any signals which will be dragged BOOST_FOREACH(const shared_ptr t, traces) if (t->selected()) _drag_traces.push_back( make_pair(t, t->get_v_offset())); } // Select the signal if it has been clicked const shared_ptr mouse_over_trace = get_mouse_over_trace(event->pos()); if (mouse_over_trace) { if (mouse_over_trace->selected()) mouse_over_trace->select(false); else { mouse_over_trace->select(true); if (~QApplication::keyboardModifiers() & Qt::ControlModifier) _drag_traces.clear(); // Add the signal to the drag list if (event->button() & Qt::LeftButton) _drag_traces.push_back( make_pair(mouse_over_trace, mouse_over_trace->get_v_offset())); } } if (~QApplication::keyboardModifiers() & Qt::ControlModifier) { // Unselect all other signals because the Ctrl is not // pressed BOOST_FOREACH(const shared_ptr t, traces) if (t != mouse_over_trace) t->select(false); } selection_changed(); update(); } void Header::mouseReleaseEvent(QMouseEvent *event) { using pv::widgets::Popup; assert(event); if (event->button() == Qt::LeftButton) { if (_dragging) _view.normalize_layout(); else { const shared_ptr mouse_over_trace = get_mouse_over_trace(event->pos()); if (mouse_over_trace) { Popup *const p = mouse_over_trace->create_popup(&_view); p->set_position(mapToGlobal(QPoint(width(), mouse_over_trace->get_y())), Popup::Right); p->show(); } } _dragging = false; _drag_traces.clear(); } } void Header::mouseMoveEvent(QMouseEvent *event) { assert(event); _mouse_point = event->pos(); if (!(event->buttons() & Qt::LeftButton)) return; if ((event->pos() - _mouse_down_point).manhattanLength() < QApplication::startDragDistance()) return; // Move the signals if we are dragging if (!_drag_traces.empty()) { _dragging = true; const int delta = event->pos().y() - _mouse_down_point.y(); for (std::list, int> >::iterator i = _drag_traces.begin(); i != _drag_traces.end(); i++) { const boost::shared_ptr trace((*i).first); if (trace) { const int y = (*i).second + delta; const int y_snap = ((y + View::SignalSnapGridSize / 2) / View::SignalSnapGridSize) * View::SignalSnapGridSize; trace->set_v_offset(y_snap); // Ensure the trace is selected trace->select(); } } signals_moved(); } update(); } void Header::leaveEvent(QEvent*) { _mouse_point = QPoint(-1, -1); update(); } void Header::contextMenuEvent(QContextMenuEvent *event) { const shared_ptr t = get_mouse_over_trace(_mouse_point); if (t) t->create_context_menu(this)->exec(event->globalPos()); } void Header::keyPressEvent(QKeyEvent *e) { assert(e); switch (e->key()) { case Qt::Key_Delete: { const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(const shared_ptr t, traces) if (t->selected()) t->delete_pressed(); break; } } } void Header::on_signals_changed() { const vector< shared_ptr > traces(_view.get_traces()); BOOST_FOREACH(shared_ptr t, traces) { assert(t); connect(t.get(), SIGNAL(visibility_changed()), this, SLOT(update())); connect(t.get(), SIGNAL(text_changed()), this, SLOT(on_trace_text_changed())); connect(t.get(), SIGNAL(colour_changed()), this, SLOT(update())); } } void Header::on_signals_moved() { update(); } void Header::on_trace_text_changed() { update(); geometry_updated(); } } // namespace view } // namespace pv pulseview-0.2.0/pv/view/logicsignal.h000600 001750 001750 00000006062 12332253627 017242 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_LOGICSIGNAL_H #define PULSEVIEW_PV_VIEW_LOGICSIGNAL_H #include "signal.h" #include class QToolBar; namespace pv { namespace data { class Logic; } namespace view { class LogicSignal : public Signal { Q_OBJECT private: static const float Oversampling; static const QColor EdgeColour; static const QColor HighColour; static const QColor LowColour; static const QColor SignalColours[10]; public: LogicSignal(boost::shared_ptr dev_inst, const sr_channel *const probe, boost::shared_ptr data); virtual ~LogicSignal(); boost::shared_ptr data() const; boost::shared_ptr logic_data() const; /** * Paints the background layer of the signal with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ void paint_back(QPainter &p, int left, int right); /** * Paints the mid-layer of the signal with a QPainter * @param p the QPainter to paint into. * @param left the x-coordinate of the left edge of the signal. * @param right the x-coordinate of the right edge of the signal. **/ void paint_mid(QPainter &p, int left, int right); private: void paint_caps(QPainter &p, QLineF *const lines, std::vector< std::pair > &edges, bool level, double samples_per_pixel, double pixels_offset, float x_offset, float y_offset); void init_trigger_actions(QWidget *parent); void populate_popup_form(QWidget *parent, QFormLayout *form); void add_trigger_action(const char *trig_types, char type, QAction *action); void update_trigger_actions(); void set_trigger(char type); private slots: void on_trigger_none(); void on_trigger_rising(); void on_trigger_high(); void on_trigger_falling(); void on_trigger_low(); void on_trigger_change(); private: boost::shared_ptr _data; QToolBar *_trigger_bar; QAction *_trigger_none; QAction *_trigger_rising; QAction *_trigger_high; QAction *_trigger_falling; QAction *_trigger_low; QAction *_trigger_change; }; } // namespace view } // namespace pv #endif // PULSEVIEW_PV_VIEW_LOGICSIGNAL_H pulseview-0.2.0/pv/data/snapshot.cpp000600 001750 001750 00000004374 12332253627 017104 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "snapshot.h" #include #include #include using boost::lock_guard; using boost::recursive_mutex; namespace pv { namespace data { Snapshot::Snapshot(int unit_size) : _data(NULL), _sample_count(0), _capacity(0), _unit_size(unit_size) { lock_guard lock(_mutex); assert(_unit_size > 0); } Snapshot::~Snapshot() { lock_guard lock(_mutex); free(_data); } uint64_t Snapshot::get_sample_count() const { lock_guard lock(_mutex); return _sample_count; } int Snapshot::unit_size() const { return _unit_size; } void Snapshot::set_capacity(const uint64_t new_capacity) { lock_guard lock(_mutex); assert(_capacity >= _sample_count); if (new_capacity > _capacity) { _capacity = new_capacity; _data = realloc(_data, (new_capacity * _unit_size) + sizeof(uint64_t)); } } uint64_t Snapshot::capacity() const { lock_guard lock(_mutex); return _capacity; } void Snapshot::append_data(void *data, uint64_t samples) { lock_guard lock(_mutex); assert(_capacity >= _sample_count); // Ensure there's enough capacity to copy. const uint64_t free_space = _capacity - _sample_count; if (free_space < samples) { set_capacity(_sample_count + samples); } memcpy((uint8_t*)_data + _sample_count * _unit_size, data, samples * _unit_size); _sample_count += samples; } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/logic.cpp000600 001750 001750 00000003160 12332253627 016332 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "logic.h" #include "logicsnapshot.h" using boost::shared_ptr; using std::deque; using std::max; namespace pv { namespace data { Logic::Logic(unsigned int num_probes) : SignalData(), _num_probes(num_probes) { assert(_num_probes > 0); } int Logic::get_num_probes() const { return _num_probes; } void Logic::push_snapshot( shared_ptr &snapshot) { _snapshots.push_front(snapshot); } deque< shared_ptr >& Logic::get_snapshots() { return _snapshots; } void Logic::clear() { _snapshots.clear(); } uint64_t Logic::get_max_sample_count() const { uint64_t l = 0; BOOST_FOREACH(boost::shared_ptr s, _snapshots) { assert(s); l = max(l, s->get_sample_count()); } return l; } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/snapshot.h000600 001750 001750 00000004502 12332253627 016542 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_SNAPSHOT_H #define PULSEVIEW_PV_DATA_SNAPSHOT_H #include #include namespace pv { namespace data { class Snapshot { public: Snapshot(int unit_size); virtual ~Snapshot(); uint64_t get_sample_count() const; int unit_size() const; /** * @brief Increase the capacity of the snapshot. * * Increasing the capacity allows samples to be appended without needing * to reallocate memory. * * For the best efficiency @c set_capacity() should be called once before * @c append_data() is called to set up the snapshot with the expected number * of samples that will be appended in total. * * @note The capacity will automatically be increased when @c append_data() * is called if there is not enough capacity in the buffer to store the samples. * * @param[in] new_capacity The new capacity of the snapshot. If this value is * smaller or equal than the current capacity then the method has no effect. */ void set_capacity(uint64_t new_capacity); /** * @brief Get the current capacity of the snapshot. * * The capacity can be increased by calling @c set_capacity(). * * @return The current capacity of the snapshot. */ uint64_t capacity() const; protected: void append_data(void *data, uint64_t samples); protected: mutable boost::recursive_mutex _mutex; void *_data; uint64_t _sample_count; uint64_t _capacity; int _unit_size; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_SNAPSHOT_H pulseview-0.2.0/pv/data/analogsnapshot.h000600 001750 001750 00000004330 12332253627 017723 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H #define PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H #include "snapshot.h" #include #include namespace AnalogSnapshotTest { class Basic; } namespace pv { namespace data { class AnalogSnapshot : public Snapshot { public: struct EnvelopeSample { float min; float max; }; struct EnvelopeSection { uint64_t start; unsigned int scale; uint64_t length; EnvelopeSample *samples; }; private: struct Envelope { uint64_t length; uint64_t data_length; EnvelopeSample *samples; }; private: static const unsigned int ScaleStepCount = 10; static const int EnvelopeScalePower; static const int EnvelopeScaleFactor; static const float LogEnvelopeScaleFactor; static const uint64_t EnvelopeDataUnit; public: AnalogSnapshot(uint64_t expected_num_samples = 0); virtual ~AnalogSnapshot(); void append_interleaved_samples(const float *data, size_t sample_count, size_t stride); const float* get_samples(int64_t start_sample, int64_t end_sample) const; void get_envelope_section(EnvelopeSection &s, uint64_t start, uint64_t end, float min_length) const; private: void reallocate_envelope(Envelope &l); void append_payload_to_envelope_levels(); private: struct Envelope _envelope_levels[ScaleStepCount]; friend class AnalogSnapshotTest::Basic; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_ANALOGSNAPSHOT_H pulseview-0.2.0/pv/data/analogsnapshot.cpp000600 001750 001750 00000013453 12332253627 020264 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include "analogsnapshot.h" using boost::lock_guard; using boost::recursive_mutex; using std::max; using std::max_element; using std::min; using std::min_element; namespace pv { namespace data { const int AnalogSnapshot::EnvelopeScalePower = 4; const int AnalogSnapshot::EnvelopeScaleFactor = 1 << EnvelopeScalePower; const float AnalogSnapshot::LogEnvelopeScaleFactor = logf(EnvelopeScaleFactor); const uint64_t AnalogSnapshot::EnvelopeDataUnit = 64*1024; // bytes AnalogSnapshot::AnalogSnapshot(const uint64_t expected_num_samples) : Snapshot(sizeof(float)) { set_capacity(expected_num_samples); lock_guard lock(_mutex); memset(_envelope_levels, 0, sizeof(_envelope_levels)); } AnalogSnapshot::~AnalogSnapshot() { lock_guard lock(_mutex); BOOST_FOREACH(Envelope &e, _envelope_levels) free(e.samples); } void AnalogSnapshot::append_interleaved_samples(const float *data, size_t sample_count, size_t stride) { assert(_unit_size == sizeof(float)); lock_guard lock(_mutex); _data = realloc(_data, (_sample_count + sample_count) * sizeof(float)); float *dst = (float*)_data + _sample_count; const float *dst_end = dst + sample_count; while (dst != dst_end) { *dst++ = *data; data += stride; } _sample_count += sample_count; // Generate the first mip-map from the data append_payload_to_envelope_levels(); } const float* AnalogSnapshot::get_samples( int64_t start_sample, int64_t end_sample) const { assert(start_sample >= 0); assert(start_sample < (int64_t)_sample_count); assert(end_sample >= 0); assert(end_sample < (int64_t)_sample_count); assert(start_sample <= end_sample); lock_guard lock(_mutex); float *const data = new float[end_sample - start_sample]; memcpy(data, (float*)_data + start_sample, sizeof(float) * (end_sample - start_sample)); return data; } void AnalogSnapshot::get_envelope_section(EnvelopeSection &s, uint64_t start, uint64_t end, float min_length) const { assert(end <= get_sample_count()); assert(start <= end); assert(min_length > 0); lock_guard lock(_mutex); const unsigned int min_level = max((int)floorf(logf(min_length) / LogEnvelopeScaleFactor) - 1, 0); const unsigned int scale_power = (min_level + 1) * EnvelopeScalePower; start >>= scale_power; end >>= scale_power; s.start = start << scale_power; s.scale = 1 << scale_power; s.length = end - start; s.samples = new EnvelopeSample[s.length]; memcpy(s.samples, _envelope_levels[min_level].samples + start, s.length * sizeof(EnvelopeSample)); } void AnalogSnapshot::reallocate_envelope(Envelope &e) { const uint64_t new_data_length = ((e.length + EnvelopeDataUnit - 1) / EnvelopeDataUnit) * EnvelopeDataUnit; if (new_data_length > e.data_length) { e.data_length = new_data_length; e.samples = (EnvelopeSample*)realloc(e.samples, new_data_length * sizeof(EnvelopeSample)); } } void AnalogSnapshot::append_payload_to_envelope_levels() { Envelope &e0 = _envelope_levels[0]; uint64_t prev_length; EnvelopeSample *dest_ptr; // Expand the data buffer to fit the new samples prev_length = e0.length; e0.length = _sample_count / EnvelopeScaleFactor; // Break off if there are no new samples to compute if (e0.length == prev_length) return; reallocate_envelope(e0); dest_ptr = e0.samples + prev_length; // Iterate through the samples to populate the first level mipmap const float *const end_src_ptr = (float*)_data + e0.length * EnvelopeScaleFactor; for (const float *src_ptr = (float*)_data + prev_length * EnvelopeScaleFactor; src_ptr < end_src_ptr; src_ptr += EnvelopeScaleFactor) { const EnvelopeSample sub_sample = { *min_element(src_ptr, src_ptr + EnvelopeScaleFactor), *max_element(src_ptr, src_ptr + EnvelopeScaleFactor), }; *dest_ptr++ = sub_sample; } // Compute higher level mipmaps for (unsigned int level = 1; level < ScaleStepCount; level++) { Envelope &e = _envelope_levels[level]; const Envelope &el = _envelope_levels[level-1]; // Expand the data buffer to fit the new samples prev_length = e.length; e.length = el.length / EnvelopeScaleFactor; // Break off if there are no more samples to computed if (e.length == prev_length) break; reallocate_envelope(e); // Subsample the level lower level const EnvelopeSample *src_ptr = el.samples + prev_length * EnvelopeScaleFactor; const EnvelopeSample *const end_dest_ptr = e.samples + e.length; for (dest_ptr = e.samples + prev_length; dest_ptr < end_dest_ptr; dest_ptr++) { const EnvelopeSample *const end_src_ptr = src_ptr + EnvelopeScaleFactor; EnvelopeSample sub_sample = *src_ptr++; while (src_ptr < end_src_ptr) { sub_sample.min = min(sub_sample.min, src_ptr->min); sub_sample.max = max(sub_sample.max, src_ptr->max); src_ptr++; } *dest_ptr = sub_sample; } } } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/logicsnapshot.h000600 001750 001750 00000006060 12332253627 017561 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H #define PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H #include "snapshot.h" #include #include namespace LogicSnapshotTest { class Pow2; class Basic; class LargeData; class Pulses; class LongPulses; } namespace pv { namespace data { class LogicSnapshot : public Snapshot { private: struct MipMapLevel { uint64_t length; uint64_t data_length; void *data; }; private: static const unsigned int ScaleStepCount = 10; static const int MipMapScalePower; static const int MipMapScaleFactor; static const float LogMipMapScaleFactor; static const uint64_t MipMapDataUnit; public: typedef std::pair EdgePair; public: LogicSnapshot(const sr_datafeed_logic &logic, uint64_t expected_num_samples = 0); virtual ~LogicSnapshot(); void append_payload(const sr_datafeed_logic &logic); void get_samples(uint8_t *const data, int64_t start_sample, int64_t end_sample) const; private: uint64_t unpack_sample(const uint8_t *ptr) const; void pack_sample(uint8_t *ptr, uint64_t value); void reallocate_mipmap_level(MipMapLevel &m); void append_payload_to_mipmap(); uint64_t get_sample(uint64_t index) const; public: /** * Parses a logic data snapshot to generate a list of transitions * in a time interval to a given level of detail. * @param[out] edges The vector to place the edges into. * @param[in] start The start sample index. * @param[in] end The end sample index. * @param[in] min_length The minimum number of samples that * can be resolved at this level of detail. * @param[in] sig_index The index of the signal. **/ void get_subsampled_edges(std::vector &edges, uint64_t start, uint64_t end, float min_length, int sig_index); private: uint64_t get_subsample(int level, uint64_t offset) const; static uint64_t pow2_ceil(uint64_t x, unsigned int power); private: struct MipMapLevel _mip_map[ScaleStepCount]; uint64_t _last_append_sample; friend class LogicSnapshotTest::Pow2; friend class LogicSnapshotTest::Basic; friend class LogicSnapshotTest::LargeData; friend class LogicSnapshotTest::Pulses; friend class LogicSnapshotTest::LongPulses; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_LOGICSNAPSHOT_H pulseview-0.2.0/pv/data/logicsnapshot.cpp000600 001750 001750 00000027156 12332253627 020125 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "config.h" #include "logicsnapshot.h" using boost::lock_guard; using boost::recursive_mutex; using std::max; using std::min; using std::pair; namespace pv { namespace data { const int LogicSnapshot::MipMapScalePower = 4; const int LogicSnapshot::MipMapScaleFactor = 1 << MipMapScalePower; const float LogicSnapshot::LogMipMapScaleFactor = logf(MipMapScaleFactor); const uint64_t LogicSnapshot::MipMapDataUnit = 64*1024; // bytes LogicSnapshot::LogicSnapshot(const sr_datafeed_logic &logic, const uint64_t expected_num_samples) : Snapshot(logic.unitsize), _last_append_sample(0) { set_capacity(expected_num_samples); lock_guard lock(_mutex); memset(_mip_map, 0, sizeof(_mip_map)); append_payload(logic); } LogicSnapshot::~LogicSnapshot() { lock_guard lock(_mutex); BOOST_FOREACH(MipMapLevel &l, _mip_map) free(l.data); } uint64_t LogicSnapshot::unpack_sample(const uint8_t *ptr) const { #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS return *(uint64_t*)ptr; #else uint64_t value = 0; switch(_unit_size) { default: value |= ((uint64_t)ptr[7]) << 56; /* FALLTHRU */ case 7: value |= ((uint64_t)ptr[6]) << 48; /* FALLTHRU */ case 6: value |= ((uint64_t)ptr[5]) << 40; /* FALLTHRU */ case 5: value |= ((uint64_t)ptr[4]) << 32; /* FALLTHRU */ case 4: value |= ((uint32_t)ptr[3]) << 24; /* FALLTHRU */ case 3: value |= ((uint32_t)ptr[2]) << 16; /* FALLTHRU */ case 2: value |= ptr[1] << 8; /* FALLTHRU */ case 1: value |= ptr[0]; /* FALLTHRU */ case 0: break; } return value; #endif } void LogicSnapshot::pack_sample(uint8_t *ptr, uint64_t value) { #ifdef HAVE_UNALIGNED_LITTLE_ENDIAN_ACCESS *(uint64_t*)ptr = value; #else switch(_unit_size) { default: ptr[7] = value >> 56; /* FALLTHRU */ case 7: ptr[6] = value >> 48; /* FALLTHRU */ case 6: ptr[5] = value >> 40; /* FALLTHRU */ case 5: ptr[4] = value >> 32; /* FALLTHRU */ case 4: ptr[3] = value >> 24; /* FALLTHRU */ case 3: ptr[2] = value >> 16; /* FALLTHRU */ case 2: ptr[1] = value >> 8; /* FALLTHRU */ case 1: ptr[0] = value; /* FALLTHRU */ case 0: break; } #endif } void LogicSnapshot::append_payload( const sr_datafeed_logic &logic) { assert(_unit_size == logic.unitsize); assert((logic.length % _unit_size) == 0); lock_guard lock(_mutex); append_data(logic.data, logic.length / _unit_size); // Generate the first mip-map from the data append_payload_to_mipmap(); } void LogicSnapshot::get_samples(uint8_t *const data, int64_t start_sample, int64_t end_sample) const { assert(data); assert(start_sample >= 0); assert(start_sample <= (int64_t)_sample_count); assert(end_sample >= 0); assert(end_sample <= (int64_t)_sample_count); assert(start_sample <= end_sample); lock_guard lock(_mutex); const size_t size = (end_sample - start_sample) * _unit_size; memcpy(data, (const uint8_t*)_data + start_sample * _unit_size, size); } void LogicSnapshot::reallocate_mipmap_level(MipMapLevel &m) { const uint64_t new_data_length = ((m.length + MipMapDataUnit - 1) / MipMapDataUnit) * MipMapDataUnit; if (new_data_length > m.data_length) { m.data_length = new_data_length; // Padding is added to allow for the uint64_t write word m.data = realloc(m.data, new_data_length * _unit_size + sizeof(uint64_t)); } } void LogicSnapshot::append_payload_to_mipmap() { MipMapLevel &m0 = _mip_map[0]; uint64_t prev_length; const uint8_t *src_ptr; uint8_t *dest_ptr; uint64_t accumulator; unsigned int diff_counter; // Expand the data buffer to fit the new samples prev_length = m0.length; m0.length = _sample_count / MipMapScaleFactor; // Break off if there are no new samples to compute if (m0.length == prev_length) return; reallocate_mipmap_level(m0); dest_ptr = (uint8_t*)m0.data + prev_length * _unit_size; // Iterate through the samples to populate the first level mipmap const uint8_t *const end_src_ptr = (uint8_t*)_data + m0.length * _unit_size * MipMapScaleFactor; for (src_ptr = (uint8_t*)_data + prev_length * _unit_size * MipMapScaleFactor; src_ptr < end_src_ptr;) { // Accumulate transitions which have occurred in this sample accumulator = 0; diff_counter = MipMapScaleFactor; while (diff_counter-- > 0) { const uint64_t sample = unpack_sample(src_ptr); accumulator |= _last_append_sample ^ sample; _last_append_sample = sample; src_ptr += _unit_size; } pack_sample(dest_ptr, accumulator); dest_ptr += _unit_size; } // Compute higher level mipmaps for (unsigned int level = 1; level < ScaleStepCount; level++) { MipMapLevel &m = _mip_map[level]; const MipMapLevel &ml = _mip_map[level-1]; // Expand the data buffer to fit the new samples prev_length = m.length; m.length = ml.length / MipMapScaleFactor; // Break off if there are no more samples to computed if (m.length == prev_length) break; reallocate_mipmap_level(m); // Subsample the level lower level src_ptr = (uint8_t*)ml.data + _unit_size * prev_length * MipMapScaleFactor; const uint8_t *const end_dest_ptr = (uint8_t*)m.data + _unit_size * m.length; for (dest_ptr = (uint8_t*)m.data + _unit_size * prev_length; dest_ptr < end_dest_ptr; dest_ptr += _unit_size) { accumulator = 0; diff_counter = MipMapScaleFactor; while (diff_counter-- > 0) { accumulator |= unpack_sample(src_ptr); src_ptr += _unit_size; } pack_sample(dest_ptr, accumulator); } } } uint64_t LogicSnapshot::get_sample(uint64_t index) const { assert(_data); assert(index < _sample_count); return unpack_sample((uint8_t*)_data + index * _unit_size); } void LogicSnapshot::get_subsampled_edges( std::vector &edges, uint64_t start, uint64_t end, float min_length, int sig_index) { uint64_t index = start; unsigned int level; bool last_sample; bool fast_forward; assert(end <= get_sample_count()); assert(start <= end); assert(min_length > 0); assert(sig_index >= 0); assert(sig_index < 64); lock_guard lock(_mutex); const uint64_t block_length = (uint64_t)max(min_length, 1.0f); const unsigned int min_level = max((int)floorf(logf(min_length) / LogMipMapScaleFactor) - 1, 0); const uint64_t sig_mask = 1ULL << sig_index; // Store the initial state last_sample = (get_sample(start) & sig_mask) != 0; edges.push_back(pair(index++, last_sample)); while (index + block_length <= end) { //----- Continue to search -----// level = min_level; // We cannot fast-forward if there is no mip-map data at // at the minimum level. fast_forward = (_mip_map[level].data != NULL); if (min_length < MipMapScaleFactor) { // Search individual samples up to the beginning of // the next first level mip map block const uint64_t final_index = min(end, pow2_ceil(index, MipMapScalePower)); for (; index < final_index && (index & ~(~0 << MipMapScalePower)) != 0; index++) { const bool sample = (get_sample(index) & sig_mask) != 0; // If there was a change we cannot fast forward if (sample != last_sample) { fast_forward = false; break; } } } else { // If resolution is less than a mip map block, // round up to the beginning of the mip-map block // for this level of detail const int min_level_scale_power = (level + 1) * MipMapScalePower; index = pow2_ceil(index, min_level_scale_power); if (index >= end) break; // We can fast forward only if there was no change const bool sample = (get_sample(index) & sig_mask) != 0; if (last_sample != sample) fast_forward = false; } if (fast_forward) { // Fast forward: This involves zooming out to higher // levels of the mip map searching for changes, then // zooming in on them to find the point where the edge // begins. // Slide right and zoom out at the beginnings of mip-map // blocks until we encounter a change while (1) { const int level_scale_power = (level + 1) * MipMapScalePower; const uint64_t offset = index >> level_scale_power; // Check if we reached the last block at this // level, or if there was a change in this block if (offset >= _mip_map[level].length || (get_subsample(level, offset) & sig_mask)) break; if ((offset & ~(~0 << MipMapScalePower)) == 0) { // If we are now at the beginning of a // higher level mip-map block ascend one // level if (level + 1 >= ScaleStepCount || !_mip_map[level + 1].data) break; level++; } else { // Slide right to the beginning of the // next mip map block index = pow2_ceil(index + 1, level_scale_power); } } // Zoom in, and slide right until we encounter a change, // and repeat until we reach min_level while (1) { assert(_mip_map[level].data); const int level_scale_power = (level + 1) * MipMapScalePower; const uint64_t offset = index >> level_scale_power; // Check if we reached the last block at this // level, or if there was a change in this block if (offset >= _mip_map[level].length || (get_subsample(level, offset) & sig_mask)) { // Zoom in unless we reached the minimum // zoom if (level == min_level) break; level--; } else { // Slide right to the beginning of the // next mip map block index = pow2_ceil(index + 1, level_scale_power); } } // If individual samples within the limit of resolution, // do a linear search for the next transition within the // block if (min_length < MipMapScaleFactor) { for (; index < end; index++) { const bool sample = (get_sample(index) & sig_mask) != 0; if (sample != last_sample) break; } } } //----- Store the edge -----// // Take the last sample of the quanization block const int64_t final_index = index + block_length; if (index + block_length > end) break; // Store the final state const bool final_sample = (get_sample(final_index - 1) & sig_mask) != 0; edges.push_back(pair(index, final_sample)); index = final_index; last_sample = final_sample; } // Add the final state const bool end_sample = get_sample(end) & sig_mask; if (last_sample != end_sample) edges.push_back(pair(end, end_sample)); edges.push_back(pair(end + 1, end_sample)); } uint64_t LogicSnapshot::get_subsample(int level, uint64_t offset) const { assert(level >= 0); assert(_mip_map[level].data); return unpack_sample((uint8_t*)_mip_map[level].data + _unit_size * offset); } uint64_t LogicSnapshot::pow2_ceil(uint64_t x, unsigned int power) { const uint64_t p = 1 << power; return (x + p - 1) / p * p; } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/decode/decoder.h000600 001750 001750 00000004174 12332253627 017540 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_DECODE_DECODER_H #define PULSEVIEW_PV_DATA_DECODE_DECODER_H #include #include #include #include struct srd_decoder; struct srd_decoder_inst; struct srd_channel; struct srd_session; namespace pv { namespace view { class LogicSignal; } namespace data { class Logic; namespace decode { class Decoder { public: Decoder(const srd_decoder *const decoder); virtual ~Decoder(); const srd_decoder* decoder() const; bool shown() const; void show(bool show = true); const std::map >& channels() const; void set_probes(std::map > probes); const std::map& options() const; void set_option(const char *id, GVariant *value); bool have_required_probes() const; srd_decoder_inst* create_decoder_inst( srd_session *session, int unit_size) const; std::set< boost::shared_ptr > get_data(); private: const srd_decoder *const _decoder; bool _shown; std::map > _probes; std::map _options; }; } // namespace decode } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_DECODE_DECODER_H pulseview-0.2.0/pv/data/decode/rowdata.cpp000600 001750 001750 00000003002 12332253627 020114 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "rowdata.h" using std::vector; namespace pv { namespace data { namespace decode { RowData::RowData() { } uint64_t RowData::get_max_sample() const { if (_annotations.empty()) return 0; return _annotations.back().end_sample(); } void RowData::get_annotation_subset( vector &dest, uint64_t start_sample, uint64_t end_sample) const { for (vector::const_iterator i = _annotations.begin(); i != _annotations.end(); i++) if ((*i).end_sample() > start_sample && (*i).start_sample() <= end_sample) dest.push_back(*i); } void RowData::push_annotation(const Annotation &a) { _annotations.push_back(a); } } // decode } // data } // pv pulseview-0.2.0/pv/data/decode/row.h000600 001750 001750 00000002731 12332253627 016737 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_DECODE_ROW_H #define PULSEVIEW_PV_DATA_DECODE_ROW_H #include #include "annotation.h" struct srd_decoder; struct srd_decoder_annotation_row; namespace pv { namespace data { namespace decode { class Row { public: Row(); Row(const srd_decoder *decoder, const srd_decoder_annotation_row *row = NULL); const srd_decoder* decoder() const; const srd_decoder_annotation_row* row() const; const QString title() const; bool operator<(const Row &other) const; private: const srd_decoder *_decoder; const srd_decoder_annotation_row *_row; }; } // decode } // data } // pv #endif // PULSEVIEW_PV_DATA_DECODE_ROW_H pulseview-0.2.0/pv/data/decode/row.cpp000600 001750 001750 00000003370 12332253627 017272 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "row.h" #include namespace pv { namespace data { namespace decode { Row::Row() : _decoder(NULL), _row(NULL) { } Row::Row(const srd_decoder *decoder, const srd_decoder_annotation_row *row) : _decoder(decoder), _row(row) { } const srd_decoder* Row::decoder() const { return _decoder; } const srd_decoder_annotation_row* Row::row() const { return _row; } const QString Row::title() const { if (_decoder && _decoder->name && _row && _row->desc) return QString("%1: %2") .arg(QString::fromUtf8(_decoder->name)) .arg(QString::fromUtf8(_row->desc)); if (_decoder && _decoder->name) return QString::fromUtf8(_decoder->name); if (_row && _row->desc) return QString::fromUtf8(_row->desc); return QString(); } bool Row::operator<(const Row &other) const { return (_decoder < other._decoder) || (_decoder == other._decoder && _row < other._row); } } // decode } // data } // pv pulseview-0.2.0/pv/data/decode/decoder.cpp000600 001750 001750 00000007324 12332253627 020073 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "decoder.h" #include using boost::shared_ptr; using std::set; using std::map; using std::string; namespace pv { namespace data { namespace decode { Decoder::Decoder(const srd_decoder *const dec) : _decoder(dec), _shown(true) { } Decoder::~Decoder() { for (map::const_iterator i = _options.begin(); i != _options.end(); i++) g_variant_unref((*i).second); } const srd_decoder* Decoder::decoder() const { return _decoder; } bool Decoder::shown() const { return _shown; } void Decoder::show(bool show) { _shown = show; } const map >& Decoder::channels() const { return _probes; } void Decoder::set_probes(std::map > probes) { _probes = probes; } const std::map& Decoder::options() const { return _options; } void Decoder::set_option(const char *id, GVariant *value) { assert(value); g_variant_ref(value); _options[id] = value; } bool Decoder::have_required_probes() const { for (GSList *l = _decoder->channels; l; l = l->next) { const srd_channel *const pdch = (const srd_channel*)l->data; assert(pdch); if (_probes.find(pdch) == _probes.end()) return false; } return true; } set< shared_ptr > Decoder::get_data() { set< shared_ptr > data; for(map >:: const_iterator i = _probes.begin(); i != _probes.end(); i++) { shared_ptr signal((*i).second); assert(signal); data.insert(signal->logic_data()); } return data; } srd_decoder_inst* Decoder::create_decoder_inst(srd_session *session, int unit_size) const { GHashTable *const opt_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); for (map::const_iterator i = _options.begin(); i != _options.end(); i++) { GVariant *const value = (*i).second; g_variant_ref(value); g_hash_table_replace(opt_hash, (void*)g_strdup( (*i).first.c_str()), value); } srd_decoder_inst *const decoder_inst = srd_inst_new( session, _decoder->id, opt_hash); g_hash_table_destroy(opt_hash); if(!decoder_inst) return NULL; // Setup the probes GHashTable *const probes = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_variant_unref); for(map >:: const_iterator i = _probes.begin(); i != _probes.end(); i++) { shared_ptr signal((*i).second); GVariant *const gvar = g_variant_new_int32( signal->probe()->index); g_variant_ref_sink(gvar); g_hash_table_insert(probes, (*i).first->id, gvar); } srd_inst_channel_set_all(decoder_inst, probes, unit_size); return decoder_inst; } } // decode } // data } // pv pulseview-0.2.0/pv/data/decode/annotation.cpp000600 001750 001750 00000003364 12332253627 020640 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ extern "C" { #include } #include #include "annotation.h" namespace pv { namespace data { namespace decode { Annotation::Annotation(const srd_proto_data *const pdata) : _start_sample(pdata->start_sample), _end_sample(pdata->end_sample) { assert(pdata); const srd_proto_data_annotation *const pda = (const srd_proto_data_annotation*)pdata->data; assert(pda); _format = pda->ann_format; const char *const *annotations = (char**)pda->ann_text; while(*annotations) { _annotations.push_back(QString::fromUtf8(*annotations)); annotations++; } } uint64_t Annotation::start_sample() const { return _start_sample; } uint64_t Annotation::end_sample() const { return _end_sample; } int Annotation::format() const { return _format; } const std::vector& Annotation::annotations() const { return _annotations; } } // namespace decode } // namespace data } // namespace pv pulseview-0.2.0/pv/data/decode/annotation.h000600 001750 001750 00000002710 12332253627 020277 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H #define PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H #include #include struct srd_proto_data; namespace pv { namespace data { namespace decode { class Annotation { public: Annotation(const srd_proto_data *const pdata); uint64_t start_sample() const; uint64_t end_sample() const; int format() const; const std::vector& annotations() const; private: uint64_t _start_sample; uint64_t _end_sample; int _format; std::vector _annotations; }; } // namespace decode } // namespace data } // namespace pv #endif // PULSEVIEW_PV_VIEW_DECODE_ANNOTATION_H pulseview-0.2.0/pv/data/decode/rowdata.h000600 001750 001750 00000002703 12332253627 017570 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_DECODE_ROWDATA_H #define PULSEVIEW_PV_DATA_DECODE_ROWDATA_H #include #include "annotation.h" namespace pv { namespace data { namespace decode { class RowData { public: RowData(); public: uint64_t get_max_sample() const; /** * Extracts sorted annotations between two period into a vector. */ void get_annotation_subset( std::vector &dest, uint64_t start_sample, uint64_t end_sample) const; void push_annotation(const Annotation &a); private: std::vector _annotations; }; } } // data } // pv #endif // PULSEVIEW_PV_DATA_DECODE_ROWDATA_H pulseview-0.2.0/pv/data/logic.h000600 001750 001750 00000002760 12332253627 016004 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_LOGIC_H #define PULSEVIEW_PV_DATA_LOGIC_H #include "signaldata.h" #include #include namespace pv { namespace data { class LogicSnapshot; class Logic : public SignalData { public: Logic(unsigned int num_probes); int get_num_probes() const; void push_snapshot( boost::shared_ptr &snapshot); std::deque< boost::shared_ptr >& get_snapshots(); void clear(); uint64_t get_max_sample_count() const; private: const unsigned int _num_probes; std::deque< boost::shared_ptr > _snapshots; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_LOGIC_H pulseview-0.2.0/pv/data/analog.h000600 001750 001750 00000002643 12332253627 016150 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_ANALOG_H #define PULSEVIEW_PV_DATA_ANALOG_H #include "signaldata.h" #include #include namespace pv { namespace data { class AnalogSnapshot; class Analog : public SignalData { public: Analog(); void push_snapshot( boost::shared_ptr &snapshot); std::deque< boost::shared_ptr >& get_snapshots(); void clear(); uint64_t get_max_sample_count() const; private: std::deque< boost::shared_ptr > _snapshots; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_ANALOG_H pulseview-0.2.0/pv/data/signaldata.h000600 001750 001750 00000002500 12332253627 017006 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_SIGNALDATA_H #define PULSEVIEW_PV_DATA_SIGNALDATA_H #include namespace pv { namespace data { class SignalData { public: SignalData(); public: double samplerate() const; void set_samplerate(double samplerate); double get_start_time() const; virtual void clear() = 0; virtual uint64_t get_max_sample_count() const = 0; protected: double _start_time; double _samplerate; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_SIGNALDATA_H pulseview-0.2.0/pv/data/signaldata.cpp000600 001750 001750 00000002311 12332253627 017341 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "signaldata.h" namespace pv { namespace data { SignalData::SignalData() : _start_time(0), _samplerate(0) { } double SignalData::samplerate() const { return _samplerate; } void SignalData::set_samplerate(double samplerate) { _samplerate = samplerate; clear(); } double SignalData::get_start_time() const { return _start_time; } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/analog.cpp000600 001750 001750 00000002770 12332253627 016504 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "analog.h" #include "analogsnapshot.h" using boost::shared_ptr; using std::deque; using std::max; namespace pv { namespace data { Analog::Analog() : SignalData() { } void Analog::push_snapshot(shared_ptr &snapshot) { _snapshots.push_front(snapshot); } deque< shared_ptr >& Analog::get_snapshots() { return _snapshots; } void Analog::clear() { _snapshots.clear(); } uint64_t Analog::get_max_sample_count() const { uint64_t l = 0; BOOST_FOREACH(const boost::shared_ptr s, _snapshots) { assert(s); l = max(l, s->get_sample_count()); } return l; } } // namespace data } // namespace pv pulseview-0.2.0/pv/data/decoderstack.h000600 001750 001750 00000007234 12332253627 017343 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DATA_DECODERSTACK_H #define PULSEVIEW_PV_DATA_DECODERSTACK_H #include "signaldata.h" #include #include #include #include #include #include #include #include struct srd_decoder; struct srd_decoder_annotation_row; struct srd_channel; struct srd_proto_data; struct srd_session; namespace DecoderStackTest { class TwoDecoderStack; } namespace pv { class SigSession; namespace view { class LogicSignal; } namespace data { class LogicSnapshot; namespace decode { class Annotation; class Decoder; } class Logic; class DecoderStack : public QObject, public SignalData { Q_OBJECT private: static const double DecodeMargin; static const double DecodeThreshold; static const int64_t DecodeChunkLength; static const unsigned int DecodeNotifyPeriod; public: DecoderStack(pv::SigSession &_session, const srd_decoder *const decoder); virtual ~DecoderStack(); const std::list< boost::shared_ptr >& stack() const; void push(boost::shared_ptr decoder); void remove(int index); int64_t samples_decoded() const; std::vector get_visible_rows() const; /** * Extracts sorted annotations between two period into a vector. */ void get_annotation_subset( std::vector &dest, const decode::Row &row, uint64_t start_sample, uint64_t end_sample) const; QString error_message(); void clear(); uint64_t get_max_sample_count() const; void begin_decode(); private: boost::optional wait_for_data() const; void decode_data(const int64_t sample_count, const unsigned int unit_size, srd_session *const session); void decode_proc(); static void annotation_callback(srd_proto_data *pdata, void *decoder); private slots: void on_new_frame(); void on_data_received(); void on_frame_ended(); signals: void new_decode_data(); private: pv::SigSession &_session; /** * This mutex prevents more than one decode operation occuring * concurrently. * @todo A proper solution should be implemented to allow multiple * decode operations. */ static boost::mutex _global_decode_mutex; std::list< boost::shared_ptr > _stack; boost::shared_ptr _snapshot; mutable boost::mutex _input_mutex; mutable boost::condition_variable _input_cond; int64_t _sample_count; bool _frame_complete; mutable boost::mutex _output_mutex; int64_t _samples_decoded; std::map _rows; std::map, decode::Row> _class_rows; QString _error_message; boost::thread _decode_thread; friend class DecoderStackTest::TwoDecoderStack; }; } // namespace data } // namespace pv #endif // PULSEVIEW_PV_DATA_DECODERSTACK_H pulseview-0.2.0/pv/data/decoderstack.cpp000600 001750 001750 00000025160 12332253627 017674 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "decoderstack.h" #include #include #include #include #include #include using boost::lock_guard; using boost::mutex; using boost::optional; using boost::shared_ptr; using boost::unique_lock; using std::deque; using std::make_pair; using std::max; using std::min; using std::list; using std::map; using std::pair; using std::vector; using namespace pv::data::decode; namespace pv { namespace data { const double DecoderStack::DecodeMargin = 1.0; const double DecoderStack::DecodeThreshold = 0.2; const int64_t DecoderStack::DecodeChunkLength = 4096; const unsigned int DecoderStack::DecodeNotifyPeriod = 65536; mutex DecoderStack::_global_decode_mutex; DecoderStack::DecoderStack(pv::SigSession &session, const srd_decoder *const dec) : _session(session), _sample_count(0), _frame_complete(false), _samples_decoded(0) { connect(&_session, SIGNAL(frame_began()), this, SLOT(on_new_frame())); connect(&_session, SIGNAL(data_received()), this, SLOT(on_data_received())); connect(&_session, SIGNAL(frame_ended()), this, SLOT(on_frame_ended())); _stack.push_back(shared_ptr( new decode::Decoder(dec))); } DecoderStack::~DecoderStack() { if (_decode_thread.joinable()) { _decode_thread.interrupt(); _decode_thread.join(); } } const std::list< boost::shared_ptr >& DecoderStack::stack() const { return _stack; } void DecoderStack::push(boost::shared_ptr decoder) { assert(decoder); _stack.push_back(decoder); } void DecoderStack::remove(int index) { assert(index >= 0); assert(index < (int)_stack.size()); // Find the decoder in the stack list< shared_ptr >::iterator iter = _stack.begin(); for(int i = 0; i < index; i++, iter++) assert(iter != _stack.end()); // Delete the element _stack.erase(iter); } int64_t DecoderStack::samples_decoded() const { lock_guard decode_lock(_output_mutex); return _samples_decoded; } std::vector DecoderStack::get_visible_rows() const { lock_guard lock(_output_mutex); vector rows; BOOST_FOREACH (const shared_ptr &dec, _stack) { assert(dec); if (!dec->shown()) continue; const srd_decoder *const decc = dec->decoder(); assert(dec->decoder()); // Add a row for the decoder if it doesn't have a row list if (!decc->annotation_rows) rows.push_back(Row(decc)); // Add the decoder rows for (const GSList *l = decc->annotation_rows; l; l = l->next) { const srd_decoder_annotation_row *const ann_row = (srd_decoder_annotation_row *)l->data; assert(ann_row); rows.push_back(Row(decc, ann_row)); } } return rows; } void DecoderStack::get_annotation_subset( std::vector &dest, const Row &row, uint64_t start_sample, uint64_t end_sample) const { lock_guard lock(_output_mutex); std::map::const_iterator iter = _rows.find(row); if (iter != _rows.end()) (*iter).second.get_annotation_subset(dest, start_sample, end_sample); } QString DecoderStack::error_message() { lock_guard lock(_output_mutex); return _error_message; } void DecoderStack::clear() { _sample_count = 0; _frame_complete = false; _samples_decoded = 0; _error_message = QString(); _rows.clear(); _class_rows.clear(); } void DecoderStack::begin_decode() { shared_ptr logic_signal; shared_ptr data; if (_decode_thread.joinable()) { _decode_thread.interrupt(); _decode_thread.join(); } clear(); // Check that all decoders have the required channels BOOST_FOREACH(const shared_ptr &dec, _stack) if (!dec->have_required_probes()) { _error_message = tr("One or more required channels " "have not been specified"); return; } // Add classes BOOST_FOREACH (const shared_ptr &dec, _stack) { assert(dec); const srd_decoder *const decc = dec->decoder(); assert(dec->decoder()); // Add a row for the decoder if it doesn't have a row list if (!decc->annotation_rows) _rows[Row(decc)] = decode::RowData(); // Add the decoder rows for (const GSList *l = decc->annotation_rows; l; l = l->next) { const srd_decoder_annotation_row *const ann_row = (srd_decoder_annotation_row *)l->data; assert(ann_row); const Row row(decc, ann_row); // Add a new empty row data object _rows[row] = decode::RowData(); // Map out all the classes for (const GSList *ll = ann_row->ann_classes; ll; ll = ll->next) _class_rows[make_pair(decc, GPOINTER_TO_INT(ll->data))] = row; } } // We get the logic data of the first channel in the list. // This works because we are currently assuming all // LogicSignals have the same data/snapshot BOOST_FOREACH (const shared_ptr &dec, _stack) if (dec && !dec->channels().empty() && ((logic_signal = (*dec->channels().begin()).second)) && ((data = logic_signal->logic_data()))) break; if (!data) return; // Check we have a snapshot of data const deque< shared_ptr > &snapshots = data->get_snapshots(); if (snapshots.empty()) return; _snapshot = snapshots.front(); // Get the samplerate and start time _start_time = data->get_start_time(); _samplerate = data->samplerate(); if (_samplerate == 0.0) _samplerate = 1.0; _decode_thread = boost::thread(&DecoderStack::decode_proc, this); } uint64_t DecoderStack::get_max_sample_count() const { uint64_t max_sample_count = 0; for (map::const_iterator i = _rows.begin(); i != _rows.end(); i++) max_sample_count = max(max_sample_count, (*i).second.get_max_sample()); return max_sample_count; } optional DecoderStack::wait_for_data() const { unique_lock input_lock(_input_mutex); while(!boost::this_thread::interruption_requested() && !_frame_complete && _samples_decoded >= _sample_count) _input_cond.wait(input_lock); return boost::make_optional( !boost::this_thread::interruption_requested() && (_samples_decoded < _sample_count || !_frame_complete), _sample_count); } void DecoderStack::decode_data( const int64_t sample_count, const unsigned int unit_size, srd_session *const session) { uint8_t chunk[DecodeChunkLength]; const unsigned int chunk_sample_count = DecodeChunkLength / _snapshot->unit_size(); for (int64_t i = 0; !boost::this_thread::interruption_requested() && i < sample_count; i += chunk_sample_count) { lock_guard decode_lock(_global_decode_mutex); const int64_t chunk_end = min( i + chunk_sample_count, sample_count); _snapshot->get_samples(chunk, i, chunk_end); if (srd_session_send(session, i, i + sample_count, chunk, (chunk_end - i) * unit_size) != SRD_OK) { _error_message = tr("Decoder reported an error"); break; } { lock_guard lock(_output_mutex); _samples_decoded = chunk_end; } if (i % DecodeNotifyPeriod == 0) new_decode_data(); } new_decode_data(); } void DecoderStack::decode_proc() { optional sample_count; srd_session *session; srd_decoder_inst *prev_di = NULL; assert(_snapshot); // Create the session srd_session_new(&session); assert(session); // Create the decoders const unsigned int unit_size = _snapshot->unit_size(); BOOST_FOREACH(const shared_ptr &dec, _stack) { srd_decoder_inst *const di = dec->create_decoder_inst(session, unit_size); if (!di) { _error_message = tr("Failed to create decoder instance"); srd_session_destroy(session); return; } if (prev_di) srd_inst_stack (session, prev_di, di); prev_di = di; } // Get the intial sample count { unique_lock input_lock(_input_mutex); sample_count = _sample_count = _snapshot->get_sample_count(); } // Start the session srd_session_metadata_set(session, SRD_CONF_SAMPLERATE, g_variant_new_uint64((uint64_t)_samplerate)); srd_pd_output_callback_add(session, SRD_OUTPUT_ANN, DecoderStack::annotation_callback, this); srd_session_start(session); do { decode_data(*sample_count, unit_size, session); } while(_error_message.isEmpty() && (sample_count = wait_for_data())); // Destroy the session srd_session_destroy(session); } void DecoderStack::annotation_callback(srd_proto_data *pdata, void *decoder) { assert(pdata); assert(decoder); DecoderStack *const d = (DecoderStack*)decoder; assert(d); lock_guard lock(d->_output_mutex); const Annotation a(pdata); // Find the row assert(pdata->pdo); assert(pdata->pdo->di); const srd_decoder *const decc = pdata->pdo->di->decoder; assert(decc); map::iterator row_iter = d->_rows.end(); // Try looking up the sub-row of this class const map, Row>::const_iterator r = d->_class_rows.find(make_pair(decc, a.format())); if (r != d->_class_rows.end()) row_iter = d->_rows.find((*r).second); else { // Failing that, use the decoder as a key row_iter = d->_rows.find(Row(decc)); } assert(row_iter != d->_rows.end()); if (row_iter == d->_rows.end()) { qDebug() << "Unexpected annotation: decoder = " << decc << ", format = " << a.format(); assert(0); return; } // Add the annotation (*row_iter).second.push_annotation(a); } void DecoderStack::on_new_frame() { begin_decode(); } void DecoderStack::on_data_received() { { unique_lock lock(_input_mutex); if (_snapshot) _sample_count = _snapshot->get_sample_count(); } _input_cond.notify_one(); } void DecoderStack::on_frame_ended() { { unique_lock lock(_input_mutex); if (_snapshot) _frame_complete = true; } _input_cond.notify_one(); } } // namespace data } // namespace pv pulseview-0.2.0/pv/device/inputfile.cpp000600 001750 001750 00000006062 12332253627 017566 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include "inputfile.h" #include using std::string; namespace pv { namespace device { InputFile::InputFile(const std::string &path) : File(path), _input(NULL) { } sr_dev_inst* InputFile::dev_inst() const { assert(_input); return _input->sdi; } void InputFile::use(SigSession *owner) throw(QString) { assert(!_input); _input = load_input_file_format(_path, NULL); File::use(owner); sr_session_new(); if (sr_session_dev_add(_input->sdi) != SR_OK) throw tr("Failed to add session device."); } void InputFile::release() { if (!_owner) return; assert(_input); File::release(); sr_dev_close(_input->sdi); sr_session_destroy(); _input = NULL; } sr_input_format* InputFile::determine_input_file_format( const string &filename) { int i; /* If there are no input formats, return NULL right away. */ sr_input_format *const *const inputs = sr_input_list(); if (!inputs) { g_critical("No supported input formats available."); return NULL; } /* Otherwise, try to find an input module that can handle this file. */ for (i = 0; inputs[i]; i++) { if (inputs[i]->format_match(filename.c_str())) break; } /* Return NULL if no input module wanted to touch this. */ if (!inputs[i]) { g_critical("Error: no matching input module found."); return NULL; } return inputs[i]; } sr_input* InputFile::load_input_file_format(const string &filename, sr_input_format *format) { struct stat st; sr_input *in; if (!format && !(format = determine_input_file_format(filename.c_str()))) { /* The exact cause was already logged. */ throw tr("Failed to load file"); } if (stat(filename.c_str(), &st) == -1) throw tr("Failed to load file"); /* Initialize the input module. */ if (!(in = new sr_input)) { throw tr("Failed to allocate input module."); } in->format = format; in->param = NULL; if (in->format->init && in->format->init(in, filename.c_str()) != SR_OK) { throw tr("Failed to load file"); } return in; } void InputFile::start() { } void InputFile::run() { assert(_input); assert(_input->format); assert(_input->format->loadfile); _input->format->loadfile(_input, _path.c_str()); } } // device } // pv pulseview-0.2.0/pv/device/device.h000600 001750 001750 00000002445 12332253627 016474 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICE_DEVICE_H #define PULSEVIEW_PV_DEVICE_DEVICE_H #include "devinst.h" namespace pv { namespace device { class Device : public DevInst { public: Device(sr_dev_inst *dev_inst); sr_dev_inst* dev_inst() const; void use(SigSession *owner) throw(QString); void release(); std::string format_device_title() const; bool is_trigger_enabled() const; private: sr_dev_inst *const _sdi; }; } // device } // pv #endif // PULSVIEW_PV_DEVICE_DEVICE_H pulseview-0.2.0/pv/device/device.cpp000600 001750 001750 00000004206 12332253627 017024 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "device.h" using std::ostringstream; using std::string; namespace pv { namespace device { Device::Device(sr_dev_inst *sdi) : _sdi(sdi) { assert(_sdi); } sr_dev_inst* Device::dev_inst() const { return _sdi; } void Device::use(SigSession *owner) throw(QString) { DevInst::use(owner); sr_session_new(); assert(_sdi); sr_dev_open(_sdi); if (sr_session_dev_add(_sdi) != SR_OK) throw QString(tr("Failed to use device.")); } void Device::release() { if (_owner) { DevInst::release(); sr_session_destroy(); } sr_dev_close(_sdi); } std::string Device::format_device_title() const { ostringstream s; assert(_sdi); if (_sdi->vendor && _sdi->vendor[0]) { s << _sdi->vendor; if ((_sdi->model && _sdi->model[0]) || (_sdi->version && _sdi->version[0])) s << ' '; } if (_sdi->model && _sdi->model[0]) { s << _sdi->model; if (_sdi->version && _sdi->version[0]) s << ' '; } if (_sdi->version && _sdi->version[0]) s << _sdi->version; return s.str(); } bool Device::is_trigger_enabled() const { assert(_sdi); for (const GSList *l = _sdi->channels; l; l = l->next) { const sr_channel *const p = (const sr_channel *)l->data; assert(p); if (p->trigger && p->trigger[0] != '\0') return true; } return false; } } // device } // pv pulseview-0.2.0/pv/device/sessionfile.h000600 001750 001750 00000002367 12332253627 017563 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICE_SESSIONFILE_H #define PULSEVIEW_PV_DEVICE_SESSIONFILE_H #include "file.h" namespace pv { namespace device { class SessionFile : public File { public: SessionFile(const std::string &path); sr_dev_inst* dev_inst() const; virtual void use(SigSession *owner) throw(QString); virtual void release(); private: sr_dev_inst *_sdi; }; } // device } // pv #endif // PULSEVIEW_PV_DEVICE_SESSIONFILE_H pulseview-0.2.0/pv/device/file.h000600 001750 001750 00000002361 12332253627 016151 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICE_FILE_H #define PULSEVIEW_PV_DEVICE_FILE_H #include #include "devinst.h" namespace pv { namespace device { class File : public DevInst { protected: File(const std::string path); public: static File* create(const std::string &name); public: std::string format_device_title() const; protected: const std::string _path; }; } // device } // pv #endif // PULSEVIEW_PV_DEVICE_FILE_H pulseview-0.2.0/pv/device/file.cpp000600 001750 001750 00000003141 12332253627 016501 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "file.h" #include "inputfile.h" #include "sessionfile.h" #include #include using std::string; namespace pv { namespace device { File::File(const std::string path) : _path(path) { } std::string File::format_device_title() const { return boost::filesystem::path(_path).filename().string(); } File* File::create(const string &name) { if (sr_session_load(name.c_str()) == SR_OK) { GSList *devlist = NULL; sr_session_dev_list(&devlist); sr_session_destroy(); if (devlist) { sr_dev_inst *const sdi = (sr_dev_inst*)devlist->data; g_slist_free(devlist); if (sdi) { sr_dev_close(sdi); sr_dev_clear(sdi->driver); return new SessionFile(name); } } } return new InputFile(name); } } // device } // pv pulseview-0.2.0/pv/device/devinst.cpp000600 001750 001750 00000005600 12332253627 017240 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "devinst.h" #include namespace pv { namespace device { DevInst::DevInst() : _owner(NULL) { } void DevInst::use(SigSession *owner) throw(QString) { assert(owner); assert(!_owner); _owner = owner; } void DevInst::release() { if (_owner) { _owner->release_device(this); _owner = NULL; } } SigSession* DevInst::owner() const { return _owner; } GVariant* DevInst::get_config(const sr_channel_group *group, int key) { GVariant *data = NULL; assert(_owner); sr_dev_inst *const sdi = dev_inst(); assert(sdi); if (sr_config_get(sdi->driver, sdi, group, key, &data) != SR_OK) return NULL; return data; } bool DevInst::set_config(const sr_channel_group *group, int key, GVariant *data) { assert(_owner); sr_dev_inst *const sdi = dev_inst(); assert(sdi); if(sr_config_set(sdi, group, key, data) == SR_OK) { config_changed(); return true; } return false; } GVariant* DevInst::list_config(const sr_channel_group *group, int key) { GVariant *data = NULL; assert(_owner); sr_dev_inst *const sdi = dev_inst(); assert(sdi); if (sr_config_list(sdi->driver, sdi, group, key, &data) != SR_OK) return NULL; return data; } void DevInst::enable_probe(const sr_channel *probe, bool enable) { assert(_owner); sr_dev_inst *const sdi = dev_inst(); assert(sdi); for (const GSList *p = sdi->channels; p; p = p->next) if (probe == p->data) { const_cast(probe)->enabled = enable; config_changed(); return; } // Probe was not found in the device assert(0); } uint64_t DevInst::get_sample_limit() { uint64_t sample_limit; GVariant* gvar = get_config(NULL, SR_CONF_LIMIT_SAMPLES); if (gvar != NULL) { sample_limit = g_variant_get_uint64(gvar); g_variant_unref(gvar); } else { sample_limit = 0U; } return sample_limit; } bool DevInst::is_trigger_enabled() const { return false; } void DevInst::start() { if (sr_session_start() != SR_OK) throw tr("Failed to start session."); } void DevInst::run() { sr_session_run(); } } // device } // pv pulseview-0.2.0/pv/device/sessionfile.cpp000600 001750 001750 00000003242 12332253627 020107 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "sessionfile.h" #include namespace pv { namespace device { SessionFile::SessionFile(const std::string &path) : File(path), _sdi(NULL) { } sr_dev_inst* SessionFile::dev_inst() const { return _sdi; } void SessionFile::use(SigSession *owner) throw(QString) { assert(!_sdi); if (sr_session_load(_path.c_str()) != SR_OK) throw tr("Failed to open file.\n"); GSList *devlist = NULL; sr_session_dev_list(&devlist); if (!devlist || !devlist->data) { if (devlist) g_slist_free(devlist); throw tr("Failed to start session."); } _sdi = (sr_dev_inst*)devlist->data; g_slist_free(devlist); File::use(owner); } void SessionFile::release() { if (!_owner) return; assert(_sdi); File::release(); sr_dev_close(_sdi); sr_dev_clear(_sdi->driver); sr_session_destroy(); _sdi = NULL; } } // device } // pv pulseview-0.2.0/pv/device/devinst.h000600 001750 001750 00000004122 12332253627 016703 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICE_DEVINST_H #define PULSEVIEW_PV_DEVICE_DEVINST_H #include #include #include #include #include struct sr_dev_inst; struct sr_channel; struct sr_channel_group; namespace pv { class SigSession; namespace device { class DevInst : public QObject { Q_OBJECT protected: DevInst(); public: virtual sr_dev_inst* dev_inst() const = 0; virtual void use(SigSession *owner) throw(QString); virtual void release(); SigSession* owner() const; virtual std::string format_device_title() const = 0; GVariant* get_config(const sr_channel_group *group, int key); bool set_config(const sr_channel_group *group, int key, GVariant *data); GVariant* list_config(const sr_channel_group *group, int key); void enable_probe(const sr_channel *probe, bool enable = true); /** * @brief Gets the sample limit from the driver. * * @return The returned sample limit from the driver, or 0 if the * sample limit could not be read. */ uint64_t get_sample_limit(); virtual bool is_trigger_enabled() const; public: virtual void start(); virtual void run(); signals: void config_changed(); protected: SigSession *_owner; }; } // device } // pv #endif // PULSEVIEW_PV_DEVICE_DEVINST_H pulseview-0.2.0/pv/device/inputfile.h000600 001750 001750 00000003434 12332253627 017233 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICE_INPUTFILE_H #define PULSEVIEW_PV_DEVICE_INPUTFILE_H #include "file.h" #include struct sr_input; struct sr_input_format; namespace pv { namespace device { class InputFile : public File { public: InputFile(const std::string &path); sr_dev_inst* dev_inst() const; virtual void use(SigSession *owner) throw(QString); virtual void release(); virtual void start(); virtual void run(); private: /** * Attempts to autodetect the format. Failing that * @param filename The filename of the input file. * @return A pointer to the 'struct sr_input_format' that should be used, * or NULL if no input format was selected or auto-detected. */ static sr_input_format* determine_input_file_format( const std::string &filename); static sr_input* load_input_file_format(const std::string &filename, sr_input_format *format); private: sr_input *_input; }; } // device } // pv #endif // PULSEVIEW_PV_DEVICE_INPUTFILE_H pulseview-0.2.0/pv/devicemanager.h000600 001750 001750 00000003463 12332253627 016571 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_DEVICEMANAGER_H #define PULSEVIEW_PV_DEVICEMANAGER_H #include #include #include #include struct sr_context; struct sr_dev_driver; namespace pv { class SigSession; namespace device { class Device; } class DeviceManager { public: DeviceManager(struct sr_context *sr_ctx); ~DeviceManager(); const std::list< boost::shared_ptr >& devices() const; std::list< boost::shared_ptr > driver_scan( struct sr_dev_driver *const driver, GSList *const drvopts = NULL); private: void init_drivers(); void release_devices(); void scan_all_drivers(); void release_driver(struct sr_dev_driver *const driver); static bool compare_devices(boost::shared_ptr a, boost::shared_ptr b); private: struct sr_context *const _sr_ctx; std::list< boost::shared_ptr > _devices; }; } // namespace pv #endif // PULSEVIEW_PV_DEVICEMANAGER_H pulseview-0.2.0/pv/devicemanager.cpp000600 001750 001750 00000007527 12332253627 017131 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "devicemanager.h" #include "device/device.h" #include "sigsession.h" #include #include #include #include #include using boost::shared_ptr; using std::list; using std::map; using std::ostringstream; using std::runtime_error; using std::string; namespace pv { DeviceManager::DeviceManager(struct sr_context *sr_ctx) : _sr_ctx(sr_ctx) { init_drivers(); scan_all_drivers(); } DeviceManager::~DeviceManager() { release_devices(); } const list< shared_ptr >& DeviceManager::devices() const { return _devices; } list< shared_ptr > DeviceManager::driver_scan( struct sr_dev_driver *const driver, GSList *const drvopts) { list< shared_ptr > driver_devices; assert(driver); // Remove any device instances from this driver from the device // list. They will not be valid after the scan. list< shared_ptr >::iterator i = _devices.begin(); while (i != _devices.end()) { if ((*i)->dev_inst()->driver == driver) i = _devices.erase(i); else i++; } // Release this driver and all it's attached devices release_driver(driver); // Do the scan GSList *const devices = sr_driver_scan(driver, drvopts); for (GSList *l = devices; l; l = l->next) driver_devices.push_back(shared_ptr( new device::Device((sr_dev_inst*)l->data))); g_slist_free(devices); driver_devices.sort(compare_devices); // Add the scanned devices to the main list _devices.insert(_devices.end(), driver_devices.begin(), driver_devices.end()); _devices.sort(compare_devices); return driver_devices; } void DeviceManager::init_drivers() { // Initialise all libsigrok drivers sr_dev_driver **const drivers = sr_driver_list(); for (sr_dev_driver **driver = drivers; *driver; driver++) { if (sr_driver_init(_sr_ctx, *driver) != SR_OK) { throw runtime_error( string("Failed to initialize driver ") + string((*driver)->name)); } } } void DeviceManager::release_devices() { // Release all the used devices BOOST_FOREACH(shared_ptr dev, _devices) { assert(dev); dev->release(); } // Clear all the drivers sr_dev_driver **const drivers = sr_driver_list(); for (sr_dev_driver **driver = drivers; *driver; driver++) sr_dev_clear(*driver); } void DeviceManager::scan_all_drivers() { // Scan all drivers for all devices. struct sr_dev_driver **const drivers = sr_driver_list(); for (struct sr_dev_driver **driver = drivers; *driver; driver++) driver_scan(*driver); } void DeviceManager::release_driver(struct sr_dev_driver *const driver) { BOOST_FOREACH(shared_ptr dev, _devices) { assert(dev); if(dev->dev_inst()->driver == driver) dev->release(); } // Clear all the old device instances from this driver sr_dev_clear(driver); } bool DeviceManager::compare_devices(shared_ptr a, shared_ptr b) { assert(a); assert(b); return a->format_device_title().compare(b->format_device_title()) < 0; } } // namespace pv pulseview-0.2.0/pv/storesession.h000600 001750 001750 00000003336 12332253627 016536 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_STORESESSION_H #define PULSEVIEW_PV_STORESESSION_H #include #include #include #include namespace pv { class SigSession; namespace data { class LogicSnapshot; } class StoreSession : public QObject { Q_OBJECT private: static const size_t BlockSize; public: StoreSession(const std::string &file_name, const SigSession &session); ~StoreSession(); std::pair progress() const; const QString& error() const; bool start(); void wait(); void cancel(); private: void store_proc(boost::shared_ptr snapshot); signals: void progress_updated(); private: const std::string _file_name; const SigSession &_session; boost::thread _thread; mutable boost::mutex _mutex; uint64_t _units_stored; uint64_t _unit_count; QString _error; }; } // pv #endif // PULSEVIEW_PV_STORESESSION_H pulseview-0.2.0/pv/widgets/colourbutton.h000600 001750 001750 00000003103 12332253627 020173 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H #define PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H #include #include "colourpopup.h" namespace pv { namespace widgets { class ColourButton : public QPushButton { Q_OBJECT; private: static const int SwatchMargin; public: ColourButton(int rows, int cols, QWidget *parent); ColourPopup& popup(); const QColor& colour() const; void set_colour(QColor colour); void set_palette(const QColor *const palette); private: void paintEvent(QPaintEvent *e); private slots: void on_clicked(bool); void on_selected(int row, int col); signals: void selected(const QColor &colour); private: ColourPopup _popup; QColor _cur_colour; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_COLOURBUTTON_H pulseview-0.2.0/pv/widgets/colourbutton.cpp000600 001750 001750 00000005374 12332253627 020542 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "colourbutton.h" #include #include #include namespace pv { namespace widgets { const int ColourButton::SwatchMargin = 7; ColourButton::ColourButton(int rows, int cols, QWidget *parent) : QPushButton("", parent), _popup(rows, cols, this) { connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool))); connect(&_popup, SIGNAL(selected(int, int)), this, SLOT(on_selected(int, int))); } ColourPopup& ColourButton::popup() { return _popup; } const QColor& ColourButton::colour() const { return _cur_colour; } void ColourButton::set_colour(QColor colour) { _cur_colour = colour; const unsigned int rows = _popup.well_array().numRows(); const unsigned int cols = _popup.well_array().numCols(); for (unsigned int r = 0; r < rows; r++) for (unsigned int c = 0; c < cols; c++) if (_popup.well_array().cellBrush(r, c).color() == colour) { _popup.well_array().setSelected(r, c); _popup.well_array().setCurrent(r, c); return; } } void ColourButton::set_palette(const QColor *const palette) { assert(palette); const unsigned int rows = _popup.well_array().numRows(); const unsigned int cols = _popup.well_array().numCols(); for (unsigned int r = 0; r < rows; r++) for (unsigned int c = 0; c < cols; c++) _popup.well_array().setCellBrush(r, c, QBrush(palette[r * cols + c])); } void ColourButton::on_clicked(bool) { _popup.set_position(mapToGlobal(rect().center()), Popup::Bottom); _popup.show(); } void ColourButton::on_selected(int row, int col) { _cur_colour = _popup.well_array().cellBrush(row, col).color(); selected(_cur_colour); } void ColourButton::paintEvent(QPaintEvent *e) { QPushButton::paintEvent(e); QPainter p(this); const QRect r = rect().adjusted(SwatchMargin, SwatchMargin, -SwatchMargin, -SwatchMargin); p.setPen(QApplication::palette().color(QPalette::Dark)); p.setBrush(QBrush(_cur_colour)); p.drawRect(r); } } // widgets } // pv pulseview-0.2.0/pv/widgets/popup.h000600 001750 001750 00000003614 12332253627 016606 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_POPUP_H #define PULSEVIEW_PV_WIDGETS_POPUP_H #include namespace pv { namespace widgets { class Popup : public QWidget { Q_OBJECT public: enum Position { Right, Top, Left, Bottom }; private: static const unsigned int ArrowLength; static const unsigned int ArrowOverlap; static const unsigned int MarginWidth; public: Popup(QWidget *parent); const QPoint& point() const; Position position() const; void set_position(const QPoint point, Position pos); private: bool space_for_arrow() const; QPolygon arrow_polygon() const; QRegion arrow_region() const; QRect bubble_rect() const; QRegion bubble_region() const; QRegion popup_region() const; void reposition_widget(); private: void closeEvent(QCloseEvent*); void paintEvent(QPaintEvent*); void resizeEvent(QResizeEvent*); void mouseReleaseEvent(QMouseEvent *e); protected: void showEvent(QShowEvent *e); signals: void closed(); private: QPoint _point; Position _pos; }; } // namespace widgets } // namespace pv #endif // PULSEVIEW_PV_WIDGETS_POPUP_H pulseview-0.2.0/pv/widgets/decodergroupbox.h000600 001750 001750 00000002631 12332253627 020634 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H #define PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H #include class QGridLayout; class QToolBar; namespace pv { namespace widgets { class DecoderGroupBox : public QWidget { Q_OBJECT public: DecoderGroupBox(QString title, QWidget *parent = NULL); void add_layout(QLayout *layout); void set_decoder_visible(bool visible); signals: void delete_decoder(); void show_hide_decoder(); private: QGridLayout *const _layout; QPushButton _show_hide_button; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_DECODERGROUPBOX_H pulseview-0.2.0/pv/widgets/colourpopup.h000600 001750 001750 00000002550 12332253627 020030 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H #define PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H #include "popup.h" #include "wellarray.h" #include namespace pv { namespace widgets { class ColourPopup : public Popup { Q_OBJECT public: ColourPopup(int rows, int cols, QWidget *partent); QWellArray& well_array(); signals: void selected(int row, int col); private slots: void colour_selected(int, int); private: QWellArray _well_array; QVBoxLayout _layout; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_COLOURPOPUP_H pulseview-0.2.0/pv/widgets/popuptoolbutton.cpp000600 001750 001750 00000002715 12332253627 021274 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "popuptoolbutton.h" namespace pv { namespace widgets { PopupToolButton::PopupToolButton(QWidget *parent) : QToolButton(parent), _popup(NULL) { connect(this, SIGNAL(clicked(bool)), this, SLOT(on_clicked(bool))); } Popup* PopupToolButton::popup() const { return _popup; } void PopupToolButton::set_popup(Popup *popup) { assert(popup); _popup = popup; } void PopupToolButton::on_clicked(bool) { if(!_popup) return; const QRect r = rect(); _popup->set_position(mapToGlobal(QPoint((r.left() + r.right()) / 2, ((r.top() + r.bottom() * 3) / 4))), Popup::Bottom); _popup->show(); } } // widgets } // pv pulseview-0.2.0/pv/widgets/popup.cpp000600 001750 001750 00000013175 12332253627 017144 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "popup.h" using std::max; using std::min; namespace pv { namespace widgets { const unsigned int Popup::ArrowLength = 10; const unsigned int Popup::ArrowOverlap = 3; const unsigned int Popup::MarginWidth = 6; Popup::Popup(QWidget *parent) : QWidget(parent, Qt::Popup | Qt::FramelessWindowHint), _point(), _pos(Left) { } const QPoint& Popup::point() const { return _point; } Popup::Position Popup::position() const { return _pos; } void Popup::set_position(const QPoint point, Position pos) { _point = point, _pos = pos; setContentsMargins( MarginWidth + ((pos == Right) ? ArrowLength : 0), MarginWidth + ((pos == Bottom) ? ArrowLength : 0), MarginWidth + ((pos == Left) ? ArrowLength : 0), MarginWidth + ((pos == Top) ? ArrowLength : 0)); } bool Popup::space_for_arrow() const { // Check if there is room for the arrow switch (_pos) { case Right: if (_point.x() > x()) return false; return true; case Bottom: if (_point.y() > y()) return false; return true; case Left: if (_point.x() < (x() + width())) return false; return true; case Top: if (_point.y() < (y() + height())) return false; return true; } return true; } QPolygon Popup::arrow_polygon() const { QPolygon poly; const QPoint p = mapFromGlobal(_point); const int l = ArrowLength + ArrowOverlap; switch (_pos) { case Right: poly << QPoint(p.x() + l, p.y() - l); break; case Bottom: poly << QPoint(p.x() - l, p.y() + l); break; case Left: case Top: poly << QPoint(p.x() - l, p.y() - l); break; } poly << p; switch (_pos) { case Right: case Bottom: poly << QPoint(p.x() + l, p.y() + l); break; case Left: poly << QPoint(p.x() - l, p.y() + l); break; case Top: poly << QPoint(p.x() + l, p.y() - l); break; } return poly; } QRegion Popup::arrow_region() const { return QRegion(arrow_polygon()); } QRect Popup::bubble_rect() const { return QRect( QPoint((_pos == Right) ? ArrowLength : 0, (_pos == Bottom) ? ArrowLength : 0), QSize(width() - ((_pos == Left || _pos == Right) ? ArrowLength : 0), height() - ((_pos == Top || _pos == Bottom) ? ArrowLength : 0))); } QRegion Popup::bubble_region() const { const QRect rect(bubble_rect()); const unsigned int r = MarginWidth; const unsigned int d = 2 * r; return QRegion(rect.adjusted(r, 0, -r, 0)).united( QRegion(rect.adjusted(0, r, 0, -r))).united( QRegion(rect.left(), rect.top(), d, d, QRegion::Ellipse)).united( QRegion(rect.right() - d, rect.top(), d, d, QRegion::Ellipse)).united( QRegion(rect.left(), rect.bottom() - d, d, d, QRegion::Ellipse)).united( QRegion(rect.right() - d, rect.bottom() - d, d, d, QRegion::Ellipse)); } QRegion Popup::popup_region() const { if (space_for_arrow()) return arrow_region().united(bubble_region()); else return bubble_region(); } void Popup::reposition_widget() { QPoint o; const QRect screen_rect = QApplication::desktop()->availableGeometry( QApplication::desktop()->screenNumber(_point)); if (_pos == Right || _pos == Left) o.ry() = -height() / 2; else o.rx() = -width() / 2; if (_pos == Left) o.rx() = -width(); else if(_pos == Top) o.ry() = -height(); o += _point; move(max(min(o.x(), screen_rect.right() - width()), screen_rect.left()), max(min(o.y(), screen_rect.bottom() - height()), screen_rect.top())); } void Popup::closeEvent(QCloseEvent*) { closed(); } void Popup::paintEvent(QPaintEvent*) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const QColor outline_color(QApplication::palette().color( QPalette::Dark)); // Draw the bubble const QRegion b = bubble_region(); const QRegion bubble_outline = QRegion(rect()).subtracted( b.translated(1, 0).intersected(b.translated(0, 1).intersected( b.translated(-1, 0).intersected(b.translated(0, -1))))); painter.setPen(Qt::NoPen); painter.setBrush(QApplication::palette().brush(QPalette::Window)); painter.drawRect(rect()); // Draw the arrow if (!space_for_arrow()) return; const QPoint ArrowOffsets[] = { QPoint(1, 0), QPoint(0, -1), QPoint(-1, 0), QPoint(0, 1)}; const QRegion a(arrow_region()); const QRegion arrow_outline = a.subtracted( a.translated(ArrowOffsets[_pos])); painter.setClipRegion(bubble_outline.subtracted(a).united( arrow_outline)); painter.setBrush(outline_color); painter.drawRect(rect()); } void Popup::resizeEvent(QResizeEvent*) { reposition_widget(); setMask(popup_region()); } void Popup::mouseReleaseEvent(QMouseEvent *e) { assert(e); // We need our own out-of-bounds click handler because QWidget counts // the drop-shadow region as inside the widget if(!bubble_rect().contains(e->pos())) close(); } void Popup::showEvent(QShowEvent*) { reposition_widget(); } } // namespace widgets } // namespace pv pulseview-0.2.0/pv/widgets/colourpopup.cpp000600 001750 001750 00000002607 12332253627 020366 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "colourpopup.h" namespace pv { namespace widgets { ColourPopup::ColourPopup(int rows, int cols, QWidget *parent) : Popup(parent), _well_array(rows, cols, this), _layout(this) { _layout.addWidget(&_well_array); setLayout(&_layout); connect(&_well_array, SIGNAL(selected(int, int)), this, SIGNAL(selected(int, int))); connect(&_well_array, SIGNAL(selected(int, int)), this, SLOT(colour_selected(int, int))); } QWellArray& ColourPopup::well_array() { return _well_array; } void ColourPopup::colour_selected(int, int) { close(); } } // widgets } // pv pulseview-0.2.0/pv/widgets/wellarray.h000600 001750 001750 00000010430 12332253627 017437 0ustar00uweuwe000000 000000 /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include struct QWellArrayData; class QWellArray : public QWidget { Q_OBJECT Q_PROPERTY(int selectedColumn READ selectedColumn) Q_PROPERTY(int selectedRow READ selectedRow) public: QWellArray(int rows, int cols, QWidget* parent=0); QString cellContent(int row, int col) const; int selectedColumn() const { return selCol; } int selectedRow() const { return selRow; } virtual void setCurrent(int row, int col); virtual void setSelected(int row, int col); QSize sizeHint() const; virtual void setCellBrush(int row, int col, const QBrush &); QBrush cellBrush(int row, int col); inline int cellWidth() const { return cellw; } inline int cellHeight() const { return cellh; } inline int rowAt(int y) const { return y / cellh; } inline int columnAt(int x) const { if (isRightToLeft()) return ncols - (x / cellw) - 1; return x / cellw; } inline int rowY(int row) const { return cellh * row; } inline int columnX(int column) const { if (isRightToLeft()) return cellw * (ncols - column - 1); return cellw * column; } inline int numRows() const { return nrows; } inline int numCols() const {return ncols; } inline QRect cellRect() const { return QRect(0, 0, cellw, cellh); } inline QSize gridSize() const { return QSize(ncols * cellw, nrows * cellh); } QRect cellGeometry(int row, int column) { QRect r; if (row >= 0 && row < nrows && column >= 0 && column < ncols) r.setRect(columnX(column), rowY(row), cellw, cellh); return r; } inline void updateCell(int row, int column) { update(cellGeometry(row, column)); } signals: void selected(int row, int col); protected: virtual void paintCell(QPainter *, int row, int col, const QRect&); virtual void paintCellContents(QPainter *, int row, int col, const QRect&); void mousePressEvent(QMouseEvent*); void mouseReleaseEvent(QMouseEvent*); void keyPressEvent(QKeyEvent*); void focusInEvent(QFocusEvent*); void focusOutEvent(QFocusEvent*); void paintEvent(QPaintEvent *); private: Q_DISABLE_COPY(QWellArray) int nrows; int ncols; int cellw; int cellh; int curRow; int curCol; int selRow; int selCol; QWellArrayData *d; }; pulseview-0.2.0/pv/widgets/decodergroupbox.cpp000600 001750 001750 00000004342 12332253627 021170 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "decodergroupbox.h" #include #include #include #include #include namespace pv { namespace widgets { DecoderGroupBox::DecoderGroupBox(QString title, QWidget *parent) : QWidget(parent), _layout(new QGridLayout), _show_hide_button(QIcon(":/icons/decoder-shown.svg"), QString(), this) { _layout->setContentsMargins(0, 0, 0, 0); setLayout(_layout); _layout->addWidget(new QLabel(QString("

%1

").arg(title)), 0, 0); _layout->setColumnStretch(0, 1); QHBoxLayout *const toolbar = new QHBoxLayout; _layout->addLayout(toolbar, 0, 1); _show_hide_button.setFlat(true); _show_hide_button.setIconSize(QSize(16, 16)); connect(&_show_hide_button, SIGNAL(clicked()), this, SIGNAL(show_hide_decoder())); toolbar->addWidget(&_show_hide_button); QPushButton *const delete_button = new QPushButton( QIcon(":/icons/decoder-delete.svg"), QString(), this); delete_button->setFlat(true); delete_button->setIconSize(QSize(16, 16)); connect(delete_button, SIGNAL(clicked()), this, SIGNAL(delete_decoder())); toolbar->addWidget(delete_button); } void DecoderGroupBox::add_layout(QLayout *layout) { assert(layout); _layout->addLayout(layout, 1, 0, 1, 2); } void DecoderGroupBox::set_decoder_visible(bool visible) { _show_hide_button.setIcon(QIcon(visible ? ":/icons/decoder-shown.svg" : ":/icons/decoder-hidden.svg")); } } // widgets } // pv pulseview-0.2.0/pv/widgets/sweeptimingwidget.cpp000600 001750 001750 00000007763 12332253627 021546 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "sweeptimingwidget.h" #include #include #include using std::vector; namespace pv { namespace widgets { SweepTimingWidget::SweepTimingWidget(const char *suffix, QWidget *parent) : QWidget(parent), _suffix(suffix), _layout(this), _value(this), _list(this), _value_type(None) { setContentsMargins(0, 0, 0, 0); _value.setDecimals(0); _value.setSuffix(QString::fromUtf8(suffix)); connect(&_list, SIGNAL(currentIndexChanged(int)), this, SIGNAL(value_changed())); connect(&_value, SIGNAL(editingFinished()), this, SIGNAL(value_changed())); setLayout(&_layout); _layout.setMargin(0); _layout.addWidget(&_list); _layout.addWidget(&_value); show_none(); } void SweepTimingWidget::show_none() { _value_type = None; _value.hide(); _list.hide(); } void SweepTimingWidget::show_min_max_step(uint64_t min, uint64_t max, uint64_t step) { assert(max > min); assert(step > 0); _value_type = MinMaxStep; _value.setRange(min, max); _value.setSingleStep(step); _value.show(); _list.hide(); } void SweepTimingWidget::show_list(const uint64_t *vals, size_t count) { _value_type = List; _list.clear(); for (size_t i = 0; i < count; i++) { char *const s = sr_si_string_u64(vals[i], _suffix); _list.addItem(QString::fromUtf8(s), qVariantFromValue(vals[i])); g_free(s); } _value.hide(); _list.show(); } void SweepTimingWidget::show_125_list(uint64_t min, uint64_t max) { assert(max > min); // Create a 1-2-5-10 list of entries. const unsigned int FineScales[] = {1, 2, 5}; uint64_t value, decade; unsigned int fine; vector values; // Compute the starting decade for (decade = 1; decade * 10 <= min; decade *= 10); // Compute the first entry for (fine = 0; fine < countof(FineScales); fine++) if (FineScales[fine] * decade >= min) break; assert(fine < countof(FineScales)); // Add the minimum entry if it's not on the 1-2-5 progression if (min != FineScales[fine] * decade) values.push_back(min); while ((value = FineScales[fine] * decade) < max) { values.push_back(value); if (++fine >= countof(FineScales)) fine = 0, decade *= 10; } // Add the max value values.push_back(max); // Make a C array, and give it to the sweep timing widget uint64_t *const values_array = new uint64_t[values.size()]; copy(values.begin(), values.end(), values_array); show_list(values_array, values.size()); delete[] values_array; } uint64_t SweepTimingWidget::value() const { switch(_value_type) { case None: return 0; case MinMaxStep: return (uint64_t)_value.value(); case List: { const int index = _list.currentIndex(); return (index >= 0) ? _list.itemData( index).value() : 0; } default: // Unexpected value type assert(0); return 0; } } void SweepTimingWidget::set_value(uint64_t value) { _value.setValue(value); int best_match = _list.count() - 1; int64_t best_variance = INT64_MAX; for (int i = 0; i < _list.count(); i++) { const int64_t this_variance = abs( (int64_t)value - _list.itemData(i).value()); if (this_variance < best_variance) { best_variance = this_variance; best_match = i; } } _list.setCurrentIndex(best_match); } } // widgets } // pv pulseview-0.2.0/pv/widgets/decodermenu.h000600 001750 001750 00000002624 12332253627 017735 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_DECODERMENU_H #define PULSEVIEW_PV_WIDGETS_DECODERMENU_H #include #include struct srd_decoder; namespace pv { namespace widgets { class DecoderMenu : public QMenu { Q_OBJECT; public: DecoderMenu(QWidget *parent, bool first_level_decoder = false); private: static int decoder_name_cmp(const void *a, const void *b); private slots: void on_action(QObject *action); signals: void decoder_selected(srd_decoder *decoder); private: QSignalMapper _mapper; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_DECODERMENU_H pulseview-0.2.0/pv/widgets/wellarray.cpp000600 001750 001750 00000020056 12332253627 017777 0ustar00uweuwe000000 000000 /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the QtGui module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include "wellarray.h" void QWellArray::paintEvent(QPaintEvent *e) { QRect r = e->rect(); int cx = r.x(); int cy = r.y(); int ch = r.height(); int cw = r.width(); int colfirst = columnAt(cx); int collast = columnAt(cx + cw); int rowfirst = rowAt(cy); int rowlast = rowAt(cy + ch); if (isRightToLeft()) { int t = colfirst; colfirst = collast; collast = t; } QPainter painter(this); QPainter *p = &painter; QRect rect(0, 0, cellWidth(), cellHeight()); if (collast < 0 || collast >= ncols) collast = ncols-1; if (rowlast < 0 || rowlast >= nrows) rowlast = nrows-1; // Go through the rows for (int r = rowfirst; r <= rowlast; ++r) { // get row position and height int rowp = rowY(r); // Go through the columns in the row r // if we know from where to where, go through [colfirst, collast], // else go through all of them for (int c = colfirst; c <= collast; ++c) { // get position and width of column c int colp = columnX(c); // Translate painter and draw the cell rect.translate(colp, rowp); paintCell(p, r, c, rect); rect.translate(-colp, -rowp); } } } struct QWellArrayData { QBrush *brush; }; QWellArray::QWellArray(int rows, int cols, QWidget *parent) : QWidget(parent) ,nrows(rows), ncols(cols) { d = 0; setFocusPolicy(Qt::StrongFocus); cellw = 28; cellh = 24; curCol = 0; curRow = 0; selCol = -1; selRow = -1; } QSize QWellArray::sizeHint() const { ensurePolished(); return gridSize().boundedTo(QSize(640, 480)); } void QWellArray::paintCell(QPainter* p, int row, int col, const QRect &rect) { int b = 3; //margin const QPalette & g = palette(); QStyleOptionFrame opt; int dfw = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); opt.lineWidth = dfw; opt.midLineWidth = 1; opt.rect = rect.adjusted(b, b, -b, -b); opt.palette = g; opt.state = QStyle::State_Enabled | QStyle::State_Sunken; style()->drawPrimitive(QStyle::PE_Frame, &opt, p, this); b += dfw; if ((row == curRow) && (col == curCol)) { if (hasFocus()) { QStyleOptionFocusRect opt; opt.palette = g; opt.rect = rect; opt.state = QStyle::State_None | QStyle::State_KeyboardFocusChange; style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, this); } } paintCellContents(p, row, col, opt.rect.adjusted(dfw, dfw, -dfw, -dfw)); } /*! Reimplement this function to change the contents of the well array. */ void QWellArray::paintCellContents(QPainter *p, int row, int col, const QRect &r) { if (d) { p->fillRect(r, d->brush[row*numCols()+col]); } else { p->fillRect(r, Qt::white); p->setPen(Qt::black); p->drawLine(r.topLeft(), r.bottomRight()); p->drawLine(r.topRight(), r.bottomLeft()); } } void QWellArray::mousePressEvent(QMouseEvent *e) { // The current cell marker is set to the cell the mouse is pressed in QPoint pos = e->pos(); setCurrent(rowAt(pos.y()), columnAt(pos.x())); } void QWellArray::mouseReleaseEvent(QMouseEvent * /* event */) { // The current cell marker is set to the cell the mouse is clicked in setSelected(curRow, curCol); } /* Sets the cell currently having the focus. This is not necessarily the same as the currently selected cell. */ void QWellArray::setCurrent(int row, int col) { if ((curRow == row) && (curCol == col)) return; if (row < 0 || col < 0) row = col = -1; int oldRow = curRow; int oldCol = curCol; curRow = row; curCol = col; updateCell(oldRow, oldCol); updateCell(curRow, curCol); } /* Sets the currently selected cell to \a row, \a column. If \a row or \a column are less than zero, the current cell is unselected. Does not set the position of the focus indicator. */ void QWellArray::setSelected(int row, int col) { int oldRow = selRow; int oldCol = selCol; if (row < 0 || col < 0) row = col = -1; selCol = col; selRow = row; updateCell(oldRow, oldCol); updateCell(selRow, selCol); if (row >= 0) emit selected(row, col); } void QWellArray::focusInEvent(QFocusEvent*) { updateCell(curRow, curCol); } void QWellArray::setCellBrush(int row, int col, const QBrush &b) { if (!d) { d = new QWellArrayData; int i = numRows()*numCols(); d->brush = new QBrush[i]; } if (row >= 0 && row < numRows() && col >= 0 && col < numCols()) d->brush[row*numCols()+col] = b; } /* Returns the brush set for the cell at \a row, \a column. If no brush is set, Qt::NoBrush is returned. */ QBrush QWellArray::cellBrush(int row, int col) { if (d && row >= 0 && row < numRows() && col >= 0 && col < numCols()) return d->brush[row*numCols()+col]; return Qt::NoBrush; } /*!\reimp */ void QWellArray::focusOutEvent(QFocusEvent*) { updateCell(curRow, curCol); } /*\reimp */ void QWellArray::keyPressEvent(QKeyEvent* e) { switch(e->key()) { // Look at the key code case Qt::Key_Left: // If 'left arrow'-key, if(curCol > 0) // and cr't not in leftmost col setCurrent(curRow, curCol - 1); // set cr't to next left column break; case Qt::Key_Right: // Correspondingly... if(curCol < numCols()-1) setCurrent(curRow, curCol + 1); break; case Qt::Key_Up: if(curRow > 0) setCurrent(curRow - 1, curCol); break; case Qt::Key_Down: if(curRow < numRows()-1) setCurrent(curRow + 1, curCol); break; case Qt::Key_Space: setSelected(curRow, curCol); break; default: // If not an interesting key, e->ignore(); // we don't accept the event return; } } pulseview-0.2.0/pv/widgets/sweeptimingwidget.h000600 001750 001750 00000003403 12332253627 021176 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H #define PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H #include #include #include #include #include #include namespace pv { namespace widgets { class SweepTimingWidget : public QWidget { Q_OBJECT private: enum ValueType { None, MinMaxStep, List }; public: SweepTimingWidget(const char *suffix, QWidget *parent = NULL); void show_none(); void show_min_max_step(uint64_t min, uint64_t max, uint64_t step); void show_list(const uint64_t *vals, size_t count); void show_125_list(uint64_t min, uint64_t max); uint64_t value() const; void set_value(uint64_t value); signals: void value_changed(); private: const char *const _suffix; QHBoxLayout _layout; QDoubleSpinBox _value; QComboBox _list; ValueType _value_type; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_SWEEPTIMINGWIDGET_H pulseview-0.2.0/pv/widgets/decodermenu.cpp000600 001750 001750 00000003772 12332253627 020275 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "decodermenu.h" namespace pv { namespace widgets { DecoderMenu::DecoderMenu(QWidget *parent, bool first_level_decoder) : QMenu(parent), _mapper(this) { GSList *l = g_slist_sort(g_slist_copy( (GSList*)srd_decoder_list()), decoder_name_cmp); for(; l; l = l->next) { const srd_decoder *const d = (srd_decoder*)l->data; assert(d); const bool have_probes = (d->channels || d->opt_channels) != 0; if (first_level_decoder == have_probes) { QAction *const action = addAction(QString::fromUtf8(d->name)); action->setData(qVariantFromValue(l->data)); _mapper.setMapping(action, action); connect(action, SIGNAL(triggered()), &_mapper, SLOT(map())); } } g_slist_free(l); connect(&_mapper, SIGNAL(mapped(QObject*)), this, SLOT(on_action(QObject*))); } int DecoderMenu::decoder_name_cmp(const void *a, const void *b) { return strcmp(((const srd_decoder*)a)->name, ((const srd_decoder*)b)->name); } void DecoderMenu::on_action(QObject *action) { assert(action); srd_decoder *const dec = (srd_decoder*)((QAction*)action)->data().value(); assert(dec); decoder_selected(dec); } } // widgets } // pv pulseview-0.2.0/pv/widgets/popuptoolbutton.h000600 001750 001750 00000002437 12332253627 020742 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H #define PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H #include "popup.h" #include namespace pv { namespace widgets { class PopupToolButton : public QToolButton { Q_OBJECT; public: PopupToolButton(QWidget *parent); Popup* popup() const; void set_popup(Popup *popup); private slots: void on_clicked(bool); private: Popup *_popup; }; } // widgets } // pv #endif // PULSEVIEW_PV_WIDGETS_POPUPTOOLBUTTON_H pulseview-0.2.0/pv/prop/bool.h000600 001750 001750 00000002477 12332253627 015716 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_BOOL_H #define PULSEVIEW_PV_PROP_BOOL_H #include "property.h" class QCheckBox; namespace pv { namespace prop { class Bool : public Property { Q_OBJECT; public: Bool(QString name, Getter getter, Setter setter); virtual ~Bool(); QWidget* get_widget(QWidget *parent, bool auto_commit); bool labeled_widget() const; void commit(); private slots: void on_state_changed(int); private: QCheckBox *_check_box; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_BOOL_H pulseview-0.2.0/pv/prop/property.h000600 001750 001750 00000003103 12332253627 016632 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_PROPERTY_H #define PULSEVIEW_PV_PROP_PROPERTY_H #include #include #include #include class QWidget; namespace pv { namespace prop { class Property : public QObject { Q_OBJECT; public: typedef boost::function Getter; typedef boost::function Setter; protected: Property(QString name, Getter getter, Setter setter); public: const QString& name() const; virtual QWidget* get_widget(QWidget *parent, bool auto_commit = false) = 0; virtual bool labeled_widget() const; virtual void commit() = 0; protected: const Getter _getter; const Setter _setter; private: QString _name; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_PROPERTY_H pulseview-0.2.0/pv/prop/double.cpp000600 001750 001750 00000004074 12332253627 016563 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "double.h" using boost::optional; using std::pair; namespace pv { namespace prop { Double::Double(QString name, int decimals, QString suffix, optional< pair > range, optional step, Getter getter, Setter setter) : Property(name, getter, setter), _decimals(decimals), _suffix(suffix), _range(range), _step(step), _spin_box(NULL) { } Double::~Double() { } QWidget* Double::get_widget(QWidget *parent, bool auto_commit) { if (_spin_box) return _spin_box; GVariant *const value = _getter ? _getter() : NULL; if (!value) return NULL; _spin_box = new QDoubleSpinBox(parent); _spin_box->setDecimals(_decimals); _spin_box->setSuffix(_suffix); if (_range) _spin_box->setRange(_range->first, _range->second); if (_step) _spin_box->setSingleStep(*_step); _spin_box->setValue(g_variant_get_double(value)); g_variant_unref(value); if (auto_commit) connect(_spin_box, SIGNAL(valueChanged(double)), this, SLOT(on_value_changed(double))); return _spin_box; } void Double::commit() { assert(_setter); if (!_spin_box) return; _setter(g_variant_new_double(_spin_box->value())); } void Double::on_value_changed(double) { commit(); } } // prop } // pv pulseview-0.2.0/pv/prop/enum.h000600 001750 001750 00000002701 12332253627 015715 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_ENUM_H #define PULSEVIEW_PV_PROP_ENUM_H #include #include #include "property.h" class QComboBox; namespace pv { namespace prop { class Enum : public Property { Q_OBJECT; public: Enum(QString name, std::vector > values, Getter getter, Setter setter); virtual ~Enum(); QWidget* get_widget(QWidget *parent, bool auto_commit); void commit(); private slots: void on_current_item_changed(int); private: const std::vector< std::pair > _values; QComboBox *_selector; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_ENUM_H pulseview-0.2.0/pv/prop/double.h000600 001750 001750 00000003164 12332253627 016227 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_DOUBLE_H #define PULSEVIEW_PV_PROP_DOUBLE_H #include #include #include "property.h" class QDoubleSpinBox; namespace pv { namespace prop { class Double : public Property { Q_OBJECT public: Double(QString name, int decimals, QString suffix, boost::optional< std::pair > range, boost::optional step, Getter getter, Setter setter); virtual ~Double(); QWidget* get_widget(QWidget *parent, bool auto_commit); void commit(); private slots: void on_value_changed(double); private: const int _decimals; const QString _suffix; const boost::optional< std::pair > _range; const boost::optional _step; QDoubleSpinBox *_spin_box; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_DOUBLE_H pulseview-0.2.0/pv/prop/string.cpp000600 001750 001750 00000003406 12332253627 016615 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "string.h" namespace pv { namespace prop { String::String(QString name, Getter getter, Setter setter) : Property(name, getter, setter), _line_edit(NULL) { } QWidget* String::get_widget(QWidget *parent, bool auto_commit) { if (_line_edit) return _line_edit; GVariant *const value = _getter ? _getter() : NULL; if (!value) return NULL; _line_edit = new QLineEdit(parent); _line_edit->setText(QString::fromUtf8( g_variant_get_string(value, NULL))); g_variant_unref(value); if (auto_commit) connect(_line_edit, SIGNAL(textEdited(const QString&)), this, SLOT(on_text_edited(const QString&))); return _line_edit; } void String::commit() { assert(_setter); if (!_line_edit) return; QByteArray ba = _line_edit->text().toLocal8Bit(); _setter(g_variant_new_string(ba.data())); } void String::on_text_edited(const QString&) { commit(); } } // prop } // pv pulseview-0.2.0/pv/prop/property.cpp000600 001750 001750 00000002174 12332253627 017174 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "property.h" namespace pv { namespace prop { Property::Property(QString name, Getter getter, Setter setter) : _getter(getter), _setter(setter), _name(name) { } const QString& Property::name() const { return _name; } bool Property::labeled_widget() const { return false; } } // prop } // pv pulseview-0.2.0/pv/prop/bool.cpp000600 001750 001750 00000003423 12332253627 016241 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "bool.h" namespace pv { namespace prop { Bool::Bool(QString name, Getter getter, Setter setter) : Property(name, getter, setter), _check_box(NULL) { } Bool::~Bool() { } QWidget* Bool::get_widget(QWidget *parent, bool auto_commit) { if (_check_box) return _check_box; GVariant *const value = _getter ? _getter() : NULL; if (!value) return NULL; _check_box = new QCheckBox(name(), parent); _check_box->setCheckState(g_variant_get_boolean(value) ? Qt::Checked : Qt::Unchecked); g_variant_unref(value); if (auto_commit) connect(_check_box, SIGNAL(stateChanged(int)), this, SLOT(on_state_changed(int))); return _check_box; } bool Bool::labeled_widget() const { return true; } void Bool::commit() { assert(_setter); if (!_check_box) return; _setter(g_variant_new_boolean( _check_box->checkState() == Qt::Checked)); } void Bool::on_state_changed(int) { commit(); } } // prop } // pv pulseview-0.2.0/pv/prop/binding/binding.h000600 001750 001750 00000003103 12332253627 017772 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_BINDING_BINDING_H #define PULSEVIEW_PV_PROP_BINDING_BINDING_H #include #include #include #include class QFormLayout; class QWidget; namespace pv { namespace prop { class Property; namespace binding { class Binding { public: const std::vector< boost::shared_ptr >& properties(); void commit(); void add_properties_to_form(QFormLayout *layout, bool auto_commit = false) const; QWidget* get_property_form(QWidget *parent, bool auto_commit = false) const; static QString print_gvariant(GVariant *const gvar); protected: std::vector< boost::shared_ptr > _properties; }; } // binding } // prop } // pv #endif // PULSEVIEW_PV_PROP_BINDING_BINDING_H pulseview-0.2.0/pv/prop/binding/decoderoptions.h000600 001750 001750 00000003403 12332253627 021404 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H #define PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H #include "binding.h" #include struct srd_decoder_option; namespace pv { namespace data { class DecoderStack; namespace decode { class Decoder; } } namespace prop { namespace binding { class DecoderOptions : public Binding { public: DecoderOptions(boost::shared_ptr decoder_stack, boost::shared_ptr decoder); private: static boost::shared_ptr bind_enum(const QString &name, const srd_decoder_option *option, Property::Getter getter, Property::Setter setter); GVariant* getter(const char *id); void setter(const char *id, GVariant *value); private: boost::shared_ptr _decoder_stack; boost::shared_ptr _decoder; }; } // binding } // prop } // pv #endif // PULSEVIEW_PV_PROP_BINDING_DECODEROPTIONS_H pulseview-0.2.0/pv/prop/binding/binding.cpp000600 001750 001750 00000004252 12332253627 020333 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "binding.h" using boost::shared_ptr; namespace pv { namespace prop { namespace binding { const std::vector< boost::shared_ptr >& Binding::properties() { return _properties; } void Binding::commit() { BOOST_FOREACH(shared_ptr p, _properties) { assert(p); p->commit(); } } void Binding::add_properties_to_form(QFormLayout *layout, bool auto_commit) const { assert(layout); BOOST_FOREACH(shared_ptr p, _properties) { assert(p); QWidget *const widget = p->get_widget(layout->parentWidget(), auto_commit); if (p->labeled_widget()) layout->addRow(widget); else layout->addRow(p->name(), widget); } } QWidget* Binding::get_property_form(QWidget *parent, bool auto_commit) const { QWidget *const form = new QWidget(parent); QFormLayout *const layout = new QFormLayout(form); form->setLayout(layout); add_properties_to_form(layout, auto_commit); return form; } QString Binding::print_gvariant(GVariant *const gvar) { QString s; if (g_variant_is_of_type(gvar, G_VARIANT_TYPE("s"))) s = QString::fromUtf8(g_variant_get_string(gvar, NULL)); else { gchar *const text = g_variant_print(gvar, FALSE); s = QString::fromUtf8(text); g_free(text); } return s; } } // binding } // prop } // pv pulseview-0.2.0/pv/prop/binding/deviceoptions.cpp000600 001750 001750 00000011530 12332253627 021571 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "deviceoptions.h" #include #include #include #include #include #include using boost::bind; using boost::function; using boost::optional; using boost::shared_ptr; using std::make_pair; using std::pair; using std::string; using std::vector; namespace pv { namespace prop { namespace binding { DeviceOptions::DeviceOptions(shared_ptr dev_inst, const sr_channel_group *group) : _dev_inst(dev_inst), _group(group) { assert(dev_inst); GVariant *gvar_opts; gsize num_opts; if (!(gvar_opts = dev_inst->list_config(group, SR_CONF_DEVICE_OPTIONS))) /* Driver supports no device instance options. */ return; const int *const options = (const int32_t *)g_variant_get_fixed_array( gvar_opts, &num_opts, sizeof(int32_t)); for (unsigned int i = 0; i < num_opts; i++) { const struct sr_config_info *const info = sr_config_info_get(options[i]); if (!info) continue; const int key = info->key; GVariant *const gvar_list = dev_inst->list_config(group, key); const QString name = QString::fromUtf8(info->name); switch(key) { case SR_CONF_SAMPLERATE: // Sample rate values are not bound because they are shown // in the SamplingBar break; case SR_CONF_CAPTURE_RATIO: bind_int(name, key, "%", pair(0, 100)); break; case SR_CONF_PATTERN_MODE: case SR_CONF_BUFFERSIZE: case SR_CONF_TRIGGER_SOURCE: case SR_CONF_TRIGGER_SLOPE: case SR_CONF_FILTER: case SR_CONF_COUPLING: case SR_CONF_CLOCK_EDGE: bind_enum(name, key, gvar_list); break; case SR_CONF_EXTERNAL_CLOCK: case SR_CONF_RLE: bind_bool(name, key); break; case SR_CONF_TIMEBASE: bind_enum(name, key, gvar_list, print_timebase); break; case SR_CONF_VDIV: bind_enum(name, key, gvar_list, print_vdiv); break; case SR_CONF_VOLTAGE_THRESHOLD: bind_enum(name, key, gvar_list, print_voltage_threshold); break; } if (gvar_list) g_variant_unref(gvar_list); } g_variant_unref(gvar_opts); } void DeviceOptions::bind_bool(const QString &name, int key) { assert(_dev_inst); _properties.push_back(shared_ptr(new Bool(name, bind(&device::DevInst::get_config, _dev_inst, _group, key), bind(&device::DevInst::set_config, _dev_inst, _group, key, _1)))); } void DeviceOptions::bind_enum(const QString &name, int key, GVariant *const gvar_list, function printer) { GVariant *gvar; GVariantIter iter; vector< pair > values; assert(_dev_inst); if (!gvar_list) { qDebug() << "Config key " << key << " was listed, but no " "options were given"; return; } g_variant_iter_init (&iter, gvar_list); while ((gvar = g_variant_iter_next_value (&iter))) values.push_back(make_pair(gvar, printer(gvar))); _properties.push_back(shared_ptr(new Enum(name, values, bind(&device::DevInst::get_config, _dev_inst, _group, key), bind(&device::DevInst::set_config, _dev_inst, _group, key, _1)))); } void DeviceOptions::bind_int(const QString &name, int key, QString suffix, optional< std::pair > range) { assert(_dev_inst); _properties.push_back(shared_ptr(new Int(name, suffix, range, bind(&device::DevInst::get_config, _dev_inst, _group, key), bind(&device::DevInst::set_config, _dev_inst, _group, key, _1)))); } QString DeviceOptions::print_timebase(GVariant *const gvar) { uint64_t p, q; g_variant_get(gvar, "(tt)", &p, &q); return QString::fromUtf8(sr_period_string(p * q)); } QString DeviceOptions::print_vdiv(GVariant *const gvar) { uint64_t p, q; g_variant_get(gvar, "(tt)", &p, &q); return QString::fromUtf8(sr_voltage_string(p, q)); } QString DeviceOptions::print_voltage_threshold(GVariant *const gvar) { gdouble lo, hi; char buf[64]; g_variant_get(gvar, "(dd)", &lo, &hi); snprintf(buf, sizeof(buf), "L<%.1fV H>%.1fV", lo, hi); return QString::fromUtf8(buf); } } // binding } // prop } // pv pulseview-0.2.0/pv/prop/binding/decoderoptions.cpp000600 001750 001750 00000007535 12332253627 021751 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "decoderoptions.h" #include #include #include #include #include #include #include #include #include using boost::bind; using boost::none; using boost::shared_ptr; using std::make_pair; using std::map; using std::pair; using std::string; using std::vector; namespace pv { namespace prop { namespace binding { DecoderOptions::DecoderOptions( shared_ptr decoder_stack, shared_ptr decoder) : _decoder_stack(decoder_stack), _decoder(decoder) { assert(_decoder); const srd_decoder *const dec = _decoder->decoder(); assert(dec); for (GSList *l = dec->options; l; l = l->next) { const srd_decoder_option *const opt = (srd_decoder_option*)l->data; const QString name = QString::fromUtf8(opt->desc); const Property::Getter getter = bind( &DecoderOptions::getter, this, opt->id); const Property::Setter setter = bind( &DecoderOptions::setter, this, opt->id, _1); shared_ptr prop; if (opt->values) prop = bind_enum(name, opt, getter, setter); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("d"))) prop = shared_ptr(new Double(name, 2, "", none, none, getter, setter)); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("x"))) prop = shared_ptr( new Int(name, "", none, getter, setter)); else if (g_variant_is_of_type(opt->def, G_VARIANT_TYPE("s"))) prop = shared_ptr( new String(name, getter, setter)); else continue; _properties.push_back(prop); } } shared_ptr DecoderOptions::bind_enum( const QString &name, const srd_decoder_option *option, Property::Getter getter, Property::Setter setter) { vector< pair > values; for (GSList *l = option->values; l; l = l->next) { GVariant *const var = (GVariant*)l->data; assert(var); values.push_back(make_pair(var, print_gvariant(var))); } return shared_ptr(new Enum(name, values, getter, setter)); } GVariant* DecoderOptions::getter(const char *id) { GVariant *val = NULL; assert(_decoder); // Get the value from the hash table if it is already present const map& options = _decoder->options(); map::const_iterator iter = options.find(id); if (iter != options.end()) val = (*iter).second; else { assert(_decoder->decoder()); // Get the default value if not for (GSList *l = _decoder->decoder()->options; l; l = l->next) { const srd_decoder_option *const opt = (srd_decoder_option*)l->data; if (strcmp(opt->id, id) == 0) { val = opt->def; break; } } } if (val) g_variant_ref(val); return val; } void DecoderOptions::setter(const char *id, GVariant *value) { assert(_decoder); _decoder->set_option(id, value); assert(_decoder_stack); _decoder_stack->begin_decode(); } } // binding } // prop } // pv pulseview-0.2.0/pv/prop/binding/deviceoptions.h000600 001750 001750 00000003745 12332253627 021247 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H #define PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H #include #include #include #include "binding.h" #include struct sr_dev_inst; struct sr_channel_group; namespace pv { namespace device { class DevInst; } namespace prop { namespace binding { class DeviceOptions : public Binding { public: DeviceOptions(boost::shared_ptr dev_inst, const sr_channel_group *group = NULL); private: void bind_bool(const QString &name, int key); void bind_enum(const QString &name, int key, GVariant *const gvar_list, boost::function printer = print_gvariant); void bind_int(const QString &name, int key, QString suffix, boost::optional< std::pair > range); static QString print_timebase(GVariant *const gvar); static QString print_vdiv(GVariant *const gvar); static QString print_voltage_threshold(GVariant *const gvar); protected: boost::shared_ptr _dev_inst; const sr_channel_group *const _group; }; } // binding } // prop } // pv #endif // PULSEVIEW_PV_PROP_BINDING_DEVICEOPTIONS_H pulseview-0.2.0/pv/prop/enum.cpp000600 001750 001750 00000004467 12332253627 016263 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "enum.h" using std::pair; using std::vector; namespace pv { namespace prop { Enum::Enum(QString name, vector > values, Getter getter, Setter setter) : Property(name, getter, setter), _values(values), _selector(NULL) { for (vector< pair >::const_iterator i = _values.begin(); i != _values.end(); i++) g_variant_ref((*i).first); } Enum::~Enum() { for (vector< pair >::const_iterator i = _values.begin(); i != _values.end(); i++) g_variant_unref((*i).first); } QWidget* Enum::get_widget(QWidget *parent, bool auto_commit) { if (_selector) return _selector; GVariant *const value = _getter ? _getter() : NULL; if (!value) return NULL; _selector = new QComboBox(parent); for (unsigned int i = 0; i < _values.size(); i++) { const pair &v = _values[i]; _selector->addItem(v.second, qVariantFromValue((void*)v.first)); if (value && g_variant_equal(v.first, value)) _selector->setCurrentIndex(i); } g_variant_unref(value); if (auto_commit) connect(_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(on_current_item_changed(int))); return _selector; } void Enum::commit() { assert(_setter); if (!_selector) return; const int index = _selector->currentIndex(); if (index < 0) return; _setter((GVariant*)_selector->itemData(index).value()); } void Enum::on_current_item_changed(int) { commit(); } } // prop } // pv pulseview-0.2.0/pv/prop/int.cpp000600 001750 001750 00000011105 12332253627 016074 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include "int.h" using boost::optional; using std::max; using std::min; using std::pair; namespace pv { namespace prop { Int::Int(QString name, QString suffix, optional< pair > range, Getter getter, Setter setter) : Property(name, getter, setter), _suffix(suffix), _range(range), _value(NULL), _spin_box(NULL) { } Int::~Int() { if (_value) g_variant_unref(_value); } QWidget* Int::get_widget(QWidget *parent, bool auto_commit) { int64_t int_val = 0, range_min = 0, range_max = 0; if (_spin_box) return _spin_box; if (_value) g_variant_unref(_value); _value = _getter ? _getter() : NULL; if (!_value) return NULL; _spin_box = new QSpinBox(parent); _spin_box->setSuffix(_suffix); const GVariantType *const type = g_variant_get_type(_value); assert(type); if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) { int_val = g_variant_get_byte(_value); range_min = 0, range_max = UINT8_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) { int_val = g_variant_get_int16(_value); range_min = INT16_MIN, range_max = INT16_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) { int_val = g_variant_get_uint16(_value); range_min = 0, range_max = UINT16_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) { int_val = g_variant_get_int32(_value); range_min = INT32_MIN, range_max = INT32_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) { int_val = g_variant_get_uint32(_value); range_min = 0, range_max = UINT32_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) { int_val = g_variant_get_int64(_value); range_min = INT64_MIN, range_max = INT64_MAX; } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) { int_val = g_variant_get_uint64(_value); range_min = 0, range_max = UINT64_MAX; } else { // Unexpected value type. assert(0); } // @todo Sigrok supports 64-bit quantities, but Qt does not have a // standard widget to allow the values to be modified over the full // 64-bit range on 32-bit machines. To solve the issue we need a // custom widget. range_min = max(range_min, (int64_t)INT_MIN); range_max = min(range_max, (int64_t)INT_MAX); if (_range) _spin_box->setRange((int)_range->first, (int)_range->second); else _spin_box->setRange((int)range_min, (int)range_max); _spin_box->setValue((int)int_val); if (auto_commit) connect(_spin_box, SIGNAL(valueChanged(int)), this, SLOT(on_value_changed(int))); return _spin_box; } void Int::commit() { assert(_setter); if (!_spin_box) return; assert(_value); GVariant *new_value = NULL; const GVariantType *const type = g_variant_get_type(_value); assert(type); if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) new_value = g_variant_new_byte(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) new_value = g_variant_new_int16(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) new_value = g_variant_new_uint16(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) new_value = g_variant_new_int32(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) new_value = g_variant_new_int32(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) new_value = g_variant_new_int64(_spin_box->value()); else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) new_value = g_variant_new_uint64(_spin_box->value()); else { // Unexpected value type. assert(0); } assert(new_value); g_variant_unref(_value); g_variant_ref(new_value); _value = new_value; _setter(new_value); } void Int::on_value_changed(int) { commit(); } } // prop } // pv pulseview-0.2.0/pv/prop/int.h000600 001750 001750 00000002777 12332253627 015560 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_INT_H #define PULSEVIEW_PV_PROP_INT_H #include #include #include "property.h" class QSpinBox; namespace pv { namespace prop { class Int : public Property { Q_OBJECT; public: Int(QString name, QString suffix, boost::optional< std::pair > range, Getter getter, Setter setter); virtual ~Int(); QWidget* get_widget(QWidget *parent, bool auto_commit); void commit(); private slots: void on_value_changed(int); private: const QString _suffix; const boost::optional< std::pair > _range; GVariant *_value; QSpinBox *_spin_box; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_INT_H pulseview-0.2.0/pv/prop/string.h000600 001750 001750 00000002441 12332253627 016260 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2013 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_PROP_STRING_H #define PULSEVIEW_PV_PROP_STRING_H #include "property.h" class QLineEdit; namespace pv { namespace prop { class String : public Property { Q_OBJECT; public: String(QString name, Getter getter, Setter setter); QWidget* get_widget(QWidget *parent, bool auto_commit); void commit(); private slots: void on_text_edited(const QString&); private: QLineEdit *_line_edit; }; } // prop } // pv #endif // PULSEVIEW_PV_PROP_STRING_H pulseview-0.2.0/pv/mainwindow.h000600 001750 001750 00000004655 12332253627 016157 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_MAINWINDOW_H #define PULSEVIEW_PV_MAINWINDOW_H #include #include #include #include "sigsession.h" struct srd_decoder; class QVBoxLayout; namespace pv { class DeviceManager; namespace device { class DevInst; } namespace toolbars { class ContextBar; class SamplingBar; } namespace view { class View; } namespace widgets { class DecoderMenu; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(DeviceManager &device_manager, const char *open_file_name = NULL, QWidget *parent = 0); private: void setup_ui(); void session_error(const QString text, const QString info_text); /** * Updates the device list in the sampling bar */ void update_device_list(); private slots: void load_file(QString file_name); void show_session_error( const QString text, const QString info_text); void on_actionOpen_triggered(); void on_actionSaveAs_triggered(); void on_actionQuit_triggered(); void on_actionConnect_triggered(); void on_actionViewZoomIn_triggered(); void on_actionViewZoomOut_triggered(); void on_actionViewZoomFit_triggered(); void on_actionViewZoomOneToOne_triggered(); void on_actionViewShowCursors_triggered(); void on_actionAbout_triggered(); void add_decoder(srd_decoder *decoder); void run_stop(); void capture_state_changed(int state); private: DeviceManager &_device_manager; SigSession _session; pv::view::View *_view; QWidget *_central_widget; QVBoxLayout *_vertical_layout; toolbars::SamplingBar *_sampling_bar; }; } // namespace pv #endif // PULSEVIEW_PV_MAINWINDOW_H pulseview-0.2.0/pv/popups/deviceoptions.cpp000600 001750 001750 00000002577 12332253627 020540 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "deviceoptions.h" #include #include #include #include using boost::shared_ptr; namespace pv { namespace popups { DeviceOptions::DeviceOptions(shared_ptr dev_inst, QWidget *parent) : Popup(parent), _dev_inst(dev_inst), _layout(this), _binding(dev_inst) { setLayout(&_layout); _layout.addWidget(_binding.get_property_form(this, true)); } pv::prop::binding::DeviceOptions& DeviceOptions::binding() { return _binding; } } // namespace popups } // namespace pv pulseview-0.2.0/pv/popups/probes.h000600 001750 001750 00000004406 12332253627 016615 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_POPUPS_PROBES_H #define PULSEVIEW_PV_POPUPS_PROBES_H #include #include #include #include #include #include #include #include struct sr_channel_group; class QCheckBox; class QGridLayout; namespace pv { class SigSession; namespace prop { namespace binding { class DeviceOptions; } } namespace view { class Signal; } namespace popups { class Probes : public pv::widgets::Popup { Q_OBJECT public: Probes(SigSession &_session, QWidget *parent); private: void set_all_probes(bool set); void populate_group(const sr_channel_group *group, const std::vector< boost::shared_ptr > sigs); QGridLayout* create_channel_group_grid( const std::vector< boost::shared_ptr > sigs); private: void showEvent(QShowEvent *e); private slots: void on_probe_checked(QWidget *widget); void enable_all_probes(); void disable_all_probes(); private: pv::SigSession &_session; QFormLayout _layout; bool _updating_probes; std::vector< boost::shared_ptr > _group_bindings; std::map< QCheckBox*, boost::shared_ptr > _check_box_signal_map; QHBoxLayout _buttons_bar; QPushButton _enable_all_probes; QPushButton _disable_all_probes; QSignalMapper _check_box_mapper; }; } // popups } // pv #endif // PULSEVIEW_PV_POPUPS_PROBES_H pulseview-0.2.0/pv/popups/deviceoptions.h000600 001750 001750 00000002726 12332253627 020201 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H #define PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H #include #include #include #include namespace pv { namespace popups { class DeviceOptions : public pv::widgets::Popup { Q_OBJECT public: DeviceOptions(boost::shared_ptr dev_inst, QWidget *parent); pv::prop::binding::DeviceOptions& binding(); private: boost::shared_ptr _dev_inst; QVBoxLayout _layout; pv::prop::binding::DeviceOptions _binding; }; } // namespace popups } // namespace pv #endif // PULSEVIEW_PV_POPUPS_DEVICEOPTIONS_H pulseview-0.2.0/pv/popups/probes.cpp000600 001750 001750 00000014675 12332253627 017161 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include "probes.h" #include #include #include #include using namespace Qt; using boost::shared_ptr; using std::map; using std::set; using std::vector; using pv::view::Signal; namespace pv { namespace popups { Probes::Probes(SigSession &session, QWidget *parent) : Popup(parent), _session(session), _updating_probes(false), _enable_all_probes(tr("Enable All"), this), _disable_all_probes(tr("Disable All"), this), _check_box_mapper(this) { // Create the layout setLayout(&_layout); shared_ptr dev_inst = _session.get_device(); assert(dev_inst); const sr_dev_inst *const sdi = dev_inst->dev_inst(); assert(sdi); // Collect a set of signals map > signal_map; const vector< shared_ptr > sigs = _session.get_signals(); BOOST_FOREACH(const shared_ptr &sig, sigs) signal_map[sig->probe()] = sig; // Populate channel groups for (const GSList *g = sdi->channel_groups; g; g = g->next) { const sr_channel_group *const group = (const sr_channel_group*)g->data; assert(group); // Make a set of signals, and removed this signals from the // signal map. vector< shared_ptr > group_sigs; for (const GSList *p = group->channels; p; p = p->next) { const sr_channel *const probe = (const sr_channel*)p->data; assert(probe); const map >:: iterator iter = signal_map.find(probe); assert(iter != signal_map.end()); group_sigs.push_back((*iter).second); signal_map.erase(iter); } populate_group(group, group_sigs); } // Make a vector of the remaining probes vector< shared_ptr > global_sigs; for (const GSList *p = sdi->channels; p; p = p->next) { const sr_channel *const probe = (const sr_channel*)p->data; assert(probe); const map >:: const_iterator iter = signal_map.find(probe); if (iter != signal_map.end()) global_sigs.push_back((*iter).second); } // Create a group populate_group(NULL, global_sigs); // Create the enable/disable all buttons connect(&_enable_all_probes, SIGNAL(clicked()), this, SLOT(enable_all_probes())); connect(&_disable_all_probes, SIGNAL(clicked()), this, SLOT(disable_all_probes())); _enable_all_probes.setFlat(true); _disable_all_probes.setFlat(true); _buttons_bar.addWidget(&_enable_all_probes); _buttons_bar.addWidget(&_disable_all_probes); _buttons_bar.addStretch(1); _layout.addRow(&_buttons_bar); // Connect the check-box signal mapper connect(&_check_box_mapper, SIGNAL(mapped(QWidget*)), this, SLOT(on_probe_checked(QWidget*))); } void Probes::set_all_probes(bool set) { _updating_probes = true; for (map >::const_iterator i = _check_box_signal_map.begin(); i != _check_box_signal_map.end(); i++) { const shared_ptr sig = (*i).second; assert(sig); sig->enable(set); (*i).first->setChecked(set); } _updating_probes = false; } void Probes::populate_group(const sr_channel_group *group, const vector< shared_ptr > sigs) { using pv::prop::binding::DeviceOptions; // Only bind options if this is a group. We don't do it for general // options, because these properties are shown in the device config // popup. shared_ptr binding; if (group) binding = shared_ptr(new DeviceOptions( _session.get_device(), group)); // Create a title if the group is going to have any content if ((!sigs.empty() || (binding && !binding->properties().empty())) && group && group->name) _layout.addRow(new QLabel( QString("

%1

").arg(group->name))); // Create the channel group grid QGridLayout *const probe_grid = create_channel_group_grid(sigs); _layout.addRow(probe_grid); // Create the channel group options if (binding) { binding->add_properties_to_form(&_layout, true); _group_bindings.push_back(binding); } } QGridLayout* Probes::create_channel_group_grid( const vector< shared_ptr > sigs) { int row = 0, col = 0; QGridLayout *const grid = new QGridLayout(); BOOST_FOREACH(const shared_ptr& sig, sigs) { assert(sig); QCheckBox *const checkbox = new QCheckBox(sig->get_name()); _check_box_mapper.setMapping(checkbox, checkbox); connect(checkbox, SIGNAL(toggled(bool)), &_check_box_mapper, SLOT(map())); grid->addWidget(checkbox, row, col); _check_box_signal_map[checkbox] = sig; if(++col >= 8) col = 0, row++; } return grid; } void Probes::showEvent(QShowEvent *e) { pv::widgets::Popup::showEvent(e); _updating_probes = true; for (map >::const_iterator i = _check_box_signal_map.begin(); i != _check_box_signal_map.end(); i++) { const shared_ptr sig = (*i).second; assert(sig); (*i).first->setChecked(sig->enabled()); } _updating_probes = false; } void Probes::on_probe_checked(QWidget *widget) { if (_updating_probes) return; QCheckBox *const check_box = (QCheckBox*)widget; assert(check_box); // Look up the signal of this check-box map< QCheckBox*, shared_ptr >::const_iterator iter = _check_box_signal_map.find((QCheckBox*)check_box); assert(iter != _check_box_signal_map.end()); const shared_ptr s = (*iter).second; assert(s); s->enable(check_box->isChecked()); } void Probes::enable_all_probes() { set_all_probes(true); } void Probes::disable_all_probes() { set_all_probes(false); } } // popups } // pv pulseview-0.2.0/pv/sigsession.h000600 001750 001750 00000011552 12332253627 016163 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012-14 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_PV_SIGSESSION_H #define PULSEVIEW_PV_SIGSESSION_H #include #include #include #include #include #include #include #include #include #include struct srd_decoder; struct srd_channel; namespace pv { class DeviceManager; namespace data { class Analog; class AnalogSnapshot; class Logic; class LogicSnapshot; class SignalData; } namespace device { class DevInst; } namespace view { class DecodeTrace; class LogicSignal; class Signal; } class SigSession : public QObject { Q_OBJECT public: enum capture_state { Stopped, AwaitingTrigger, Running }; public: SigSession(DeviceManager &device_manager); ~SigSession(); boost::shared_ptr get_device() const; /** * Sets device instance that will be used in the next capture session. */ void set_device(boost::shared_ptr dev_inst) throw(QString); void set_file(const std::string &name) throw(QString); void set_default_device(); void release_device(device::DevInst *dev_inst); capture_state get_capture_state() const; void start_capture(boost::function error_handler); void stop_capture(); std::set< boost::shared_ptr > get_data() const; std::vector< boost::shared_ptr > get_signals() const; #ifdef ENABLE_DECODE bool add_decoder(srd_decoder *const dec); std::vector< boost::shared_ptr > get_decode_signals() const; void remove_decode_signal(view::DecodeTrace *signal); #endif private: void set_capture_state(capture_state state); void update_signals(boost::shared_ptr dev_inst); boost::shared_ptr signal_from_probe( const sr_channel *probe) const; void read_sample_rate(const sr_dev_inst *const sdi); private: /** * Attempts to autodetect the format. Failing that * @param filename The filename of the input file. * @return A pointer to the 'struct sr_input_format' that should be * used, or NULL if no input format was selected or * auto-detected. */ static sr_input_format* determine_input_file_format( const std::string &filename); static sr_input* load_input_file_format( const std::string &filename, boost::function error_handler, sr_input_format *format = NULL); void sample_thread_proc(boost::shared_ptr dev_inst, boost::function error_handler); void feed_in_header(const sr_dev_inst *sdi); void feed_in_meta(const sr_dev_inst *sdi, const sr_datafeed_meta &meta); void feed_in_frame_begin(); void feed_in_logic(const sr_datafeed_logic &logic); void feed_in_analog(const sr_datafeed_analog &analog); void data_feed_in(const struct sr_dev_inst *sdi, const struct sr_datafeed_packet *packet); static void data_feed_in_proc(const struct sr_dev_inst *sdi, const struct sr_datafeed_packet *packet, void *cb_data); private: DeviceManager &_device_manager; /** * The device instance that will be used in the next capture session. */ boost::shared_ptr _dev_inst; std::vector< boost::shared_ptr > _decode_traces; mutable boost::mutex _sampling_mutex; capture_state _capture_state; mutable boost::mutex _signals_mutex; std::vector< boost::shared_ptr > _signals; mutable boost::mutex _data_mutex; boost::shared_ptr _logic_data; boost::shared_ptr _cur_logic_snapshot; std::map< const sr_channel*, boost::shared_ptr > _cur_analog_snapshots; boost::thread _sampling_thread; signals: void capture_state_changed(int state); void signals_changed(); void frame_began(); void data_received(); void frame_ended(); private: // TODO: This should not be necessary. Multiple concurrent // sessions should should be supported and it should be // possible to associate a pointer with a sr_session. static SigSession *_session; }; } // namespace pv #endif // PULSEVIEW_PV_SIGSESSION_H pulseview-0.2.0/pv/storesession.cpp000600 001750 001750 00000010720 12332253627 017064 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2014 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "storesession.h" #include #include #include #include using boost::dynamic_pointer_cast; using boost::mutex; using boost::shared_ptr; using boost::thread; using boost::lock_guard; using std::deque; using std::make_pair; using std::min; using std::pair; using std::set; using std::string; using std::vector; namespace pv { const size_t StoreSession::BlockSize = 1024 * 1024; StoreSession::StoreSession(const std::string &file_name, const SigSession &session) : _file_name(file_name), _session(session), _units_stored(0), _unit_count(0) { } StoreSession::~StoreSession() { wait(); } pair StoreSession::progress() const { lock_guard lock(_mutex); return make_pair(_units_stored, _unit_count); } const QString& StoreSession::error() const { lock_guard lock(_mutex); return _error; } bool StoreSession::start() { set< shared_ptr > data_set = _session.get_data(); const vector< shared_ptr > sigs = _session.get_signals(); // Check we have logic data if (data_set.empty() || sigs.empty()) { _error = tr("No data to save."); return false; } if (data_set.size() > 1) { _error = tr("PulseView currently only has support for " "storing a single data stream."); return false; } // Get the logic data //shared_ptr data; if (!(data = dynamic_pointer_cast(*data_set.begin()))) { _error = tr("PulseView currently only has support for " "storing a logic data."); return false; } // Get the snapshot const deque< shared_ptr > &snapshots = data->get_snapshots(); if (snapshots.empty()) { _error = tr("No snapshots to save."); return false; } const shared_ptr snapshot(snapshots.front()); assert(snapshot); // Make a list of probes char **const probes = new char*[sigs.size() + 1]; for (size_t i = 0; i < sigs.size(); i++) { shared_ptr sig(sigs[i]); assert(sig); probes[i] = strdup(sig->get_name().toUtf8().constData()); } probes[sigs.size()] = NULL; // Begin storing if (sr_session_save_init(_file_name.c_str(), data->samplerate(), probes) != SR_OK) { _error = tr("Error while saving."); return false; } // Delete the probes array for (size_t i = 0; i <= sigs.size(); i++) free(probes[i]); delete[] probes; _thread = boost::thread(&StoreSession::store_proc, this, snapshot); return true; } void StoreSession::wait() { if (_thread.joinable()) _thread.join(); } void StoreSession::cancel() { _thread.interrupt(); } void StoreSession::store_proc(shared_ptr snapshot) { assert(snapshot); uint64_t start_sample = 0; /// TODO: Wrap this in a std::unique_ptr when we transition to C++11 uint8_t *const data = new uint8_t[BlockSize]; assert(data); const int unit_size = snapshot->unit_size(); assert(unit_size != 0); { lock_guard lock(_mutex); _unit_count = snapshot->get_sample_count(); } const unsigned int samples_per_block = BlockSize / unit_size; while (!boost::this_thread::interruption_requested() && start_sample < _unit_count) { progress_updated(); const uint64_t end_sample = min( start_sample + samples_per_block, _unit_count); snapshot->get_samples(data, start_sample, end_sample); if(sr_session_append(_file_name.c_str(), data, unit_size, end_sample - start_sample) != SR_OK) { _error = tr("Error while saving."); break; } start_sample = end_sample; { lock_guard lock(_mutex); _units_stored = start_sample; } } progress_updated(); delete[] data; } } // pv pulseview-0.2.0/pulseview.qrc000600 001750 001750 00000001552 12332253627 015730 0ustar00uweuwe000000 000000 icons/application-exit.png icons/configure.png icons/decoder-delete.svg icons/decoder-hidden.svg icons/decoder-shown.svg icons/document-open.png icons/document-save-as.png icons/probes.svg icons/sigrok-logo-notext.png icons/status-green.svg icons/status-grey.svg icons/status-red.svg icons/trigger-change.svg icons/trigger-falling.svg icons/trigger-high.svg icons/trigger-low.svg icons/trigger-none.svg icons/trigger-rising.svg icons/zoom-fit.png icons/zoom-in.png icons/zoom-original.png icons/zoom-out.png pulseview-0.2.0/extdef.h000600 001750 001750 00000002022 12332253627 014617 0ustar00uweuwe000000 000000 /* * This file is part of the PulseView project. * * Copyright (C) 2012 Joel Holdsworth * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef PULSEVIEW_EXTDEF_H #define PULSEVIEW_EXTDEF_H #define countof(x) (sizeof(x)/sizeof(x[0])) #define begin_element(x) (&x[0]) #define end_element(x) (&x[countof(x)]) #endif // PULSEVIEW_EXTDEF_H pulseview-0.2.0/INSTALL000600 001750 001750 00000002103 12332253627 014220 0ustar00uweuwe000000 000000 ------------------------------------------------------------------------------- INSTALL ------------------------------------------------------------------------------- Requirements ------------ - git - g++ - make - libtool - pkg-config >= 0.22 - cmake >= 2.8.6 - libglib >= 2.28.0 - Qt >= 4.5 - libboost >= 1.42 (including the following libs): - libboost-system - libboost-thread - libboost-filesystem - libboost-test (optional, only needed to run the unit tests) - libsigrok >= 0.3.0 - libsigrokdecode >= 0.3.0 Building and installing ----------------------- In order to get the PulseView source code and build it, run: $ git clone git://sigrok.org/pulseview $ cd pulseview $ cmake . $ make For installing PulseView: $ make install See the following wiki page for more (OS-specific) instructions: http://sigrok.org/wiki/Building Creating a source distribution package -------------------------------------- In order to build a source package begin with an unconfigured source tree. $ mkdir dist $ cd dist $ cmake .. $ make package_source