From 72897010645a24ab77b1ffd3bba63d277c22b06a Mon Sep 17 00:00:00 2001 From: Alf Gaida Date: Wed, 1 Jul 2015 00:01:53 +0200 Subject: [PATCH] Cherry-Picking upstream version 1.10.16+20150823. --- .clang-format | 2 + CMakeLists.txt | 69 + LICENSE | 280 +++ Qt5TranslationLoader.cpp.in | 33 + README.md | 10 + cmake/LXQtTranslateDesktop.cmake | 107 + cmake/LXQtTranslateTs.cmake | 140 ++ cmake/Qt5PatchedLinguistToolsMacros.cmake | 112 + cmake/Qt5TranslationLoader.cmake | 47 + icon/icon-small.xpm | 196 ++ icon/icon.xpm | 57 + icon/letters.png | Bin 0 -> 720 bytes icon/letters.xcf | Bin 0 -> 9159 bytes icon/letters.xpm | 19 + icon/pause.png | Bin 0 -> 502 bytes icon/preferences-system.png | Bin 0 -> 2868 bytes icon/qps.png | Bin 0 -> 623 bytes icon/rec.png | Bin 0 -> 686 bytes icon/superman.png | Bin 0 -> 4497 bytes icon/vcross.png | Bin 0 -> 656 bytes icon/vista.png | Bin 0 -> 3178 bytes icon/vpointer.png | Bin 0 -> 660 bytes icon/vpointer.xpm | 25 + icon/warn.xpm | 46 + icon/x1.xpm | 98 + icon/x2.xpm | 49 + qps.1 | 317 +++ qps.desktop.in | 13 + src/CMakeLists.txt | 91 + src/command.cpp | 1197 ++++++++++ src/command.h | 226 ++ src/config.h | 20 + src/ctrlbar.cpp | 0 src/ctrlbar.h | 0 src/details.cpp | 727 ++++++ src/details.h | 236 ++ src/dialogs.cpp | 397 ++++ src/dialogs.h | 105 + src/fieldsel.cpp | 107 + src/fieldsel.h | 49 + src/global.h | 19 + src/htable.cpp | 1269 ++++++++++ src/htable.h | 362 +++ src/htable2.cpp | 210 ++ src/htable2.h | 186 ++ src/infobar.cpp | 1367 +++++++++++ src/infobar.h | 215 ++ src/lookup.cpp | 267 +++ src/lookup.h | 88 + src/message.ui | 37 + src/misc.cpp | 1215 ++++++++++ src/misc.h | 296 +++ src/prefs.cpp | 352 +++ src/prefs.h | 37 + src/proc.cpp | 2255 ++++++++++++++++++ src/proc.h | 873 +++++++ src/proc_common.cpp | 1100 +++++++++ src/proc_linux.cpp | 2255 ++++++++++++++++++ src/proc_mosix.cpp | 2463 ++++++++++++++++++++ src/proc_solaris.cpp | 1328 +++++++++++ src/pstable.cpp | 586 +++++ src/pstable.h | 64 + src/pstable2.cpp | 491 ++++ src/pstable2.h | 71 + src/qps.cpp | 2553 +++++++++++++++++++++ src/qps.h | 318 +++ src/qps.qrc | 11 + src/qrc_qps.cpp | 1205 ++++++++++ src/qticonloader.cpp | 389 ++++ src/qticonloader.h | 55 + src/qttableview.cpp | 1793 +++++++++++++++ src/qttableview.h | 219 ++ src/screenshot.cpp | 425 ++++ src/screenshot.h | 110 + src/stable.h | 88 + src/svec.cpp | 145 ++ src/svec.h | 138 ++ src/translations/qps.ts | 170 ++ src/trayicon.cpp | 240 ++ src/trayicon.h | 98 + src/ttystr.cpp | 211 ++ src/ttystr.h | 35 + src/uidstr.cpp | 61 + src/uidstr.h | 23 + src/watchdog.cpp | 0 src/watchdog.h | 0 src/watchdog.ui | 458 ++++ src/wchan.cpp | 185 ++ src/wchan.h | 31 + 89 files changed, 31142 insertions(+) create mode 100644 .clang-format create mode 100644 CMakeLists.txt create mode 100644 LICENSE create mode 100644 Qt5TranslationLoader.cpp.in create mode 100644 README.md create mode 100644 cmake/LXQtTranslateDesktop.cmake create mode 100644 cmake/LXQtTranslateTs.cmake create mode 100644 cmake/Qt5PatchedLinguistToolsMacros.cmake create mode 100644 cmake/Qt5TranslationLoader.cmake create mode 100644 icon/icon-small.xpm create mode 100644 icon/icon.xpm create mode 100644 icon/letters.png create mode 100644 icon/letters.xcf create mode 100644 icon/letters.xpm create mode 100644 icon/pause.png create mode 100644 icon/preferences-system.png create mode 100644 icon/qps.png create mode 100644 icon/rec.png create mode 100644 icon/superman.png create mode 100644 icon/vcross.png create mode 100644 icon/vista.png create mode 100644 icon/vpointer.png create mode 100644 icon/vpointer.xpm create mode 100644 icon/warn.xpm create mode 100644 icon/x1.xpm create mode 100644 icon/x2.xpm create mode 100644 qps.1 create mode 100644 qps.desktop.in create mode 100644 src/CMakeLists.txt create mode 100644 src/command.cpp create mode 100644 src/command.h create mode 100644 src/config.h create mode 100644 src/ctrlbar.cpp create mode 100644 src/ctrlbar.h create mode 100644 src/details.cpp create mode 100644 src/details.h create mode 100644 src/dialogs.cpp create mode 100644 src/dialogs.h create mode 100644 src/fieldsel.cpp create mode 100644 src/fieldsel.h create mode 100644 src/global.h create mode 100644 src/htable.cpp create mode 100644 src/htable.h create mode 100644 src/htable2.cpp create mode 100644 src/htable2.h create mode 100644 src/infobar.cpp create mode 100644 src/infobar.h create mode 100644 src/lookup.cpp create mode 100644 src/lookup.h create mode 100644 src/message.ui create mode 100644 src/misc.cpp create mode 100644 src/misc.h create mode 100644 src/prefs.cpp create mode 100644 src/prefs.h create mode 100644 src/proc.cpp create mode 100644 src/proc.h create mode 100644 src/proc_common.cpp create mode 100644 src/proc_linux.cpp create mode 100644 src/proc_mosix.cpp create mode 100644 src/proc_solaris.cpp create mode 100644 src/pstable.cpp create mode 100644 src/pstable.h create mode 100644 src/pstable2.cpp create mode 100644 src/pstable2.h create mode 100644 src/qps.cpp create mode 100644 src/qps.h create mode 100644 src/qps.qrc create mode 100644 src/qrc_qps.cpp create mode 100644 src/qticonloader.cpp create mode 100644 src/qticonloader.h create mode 100644 src/qttableview.cpp create mode 100644 src/qttableview.h create mode 100644 src/screenshot.cpp create mode 100644 src/screenshot.h create mode 100644 src/stable.h create mode 100644 src/svec.cpp create mode 100644 src/svec.h create mode 100644 src/translations/qps.ts create mode 100644 src/trayicon.cpp create mode 100644 src/trayicon.h create mode 100644 src/ttystr.cpp create mode 100644 src/ttystr.h create mode 100644 src/uidstr.cpp create mode 100644 src/uidstr.h create mode 100644 src/watchdog.cpp create mode 100644 src/watchdog.h create mode 100644 src/watchdog.ui create mode 100644 src/wchan.cpp create mode 100644 src/wchan.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1e70b4d --- /dev/null +++ b/.clang-format @@ -0,0 +1,2 @@ +BreakBeforeBraces: Allman +IndentWidth: 4 diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a1c6641 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) +set(PROJECT qps) +project(${PROJECT}) + +option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF) + +# additional cmake files +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +set(MAJOR_VERSION 1) +set(MINOR_VERSION 10) +set(PATCH_VERSION 16) +set(QPS_VERSION_STRING ${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}) +add_definitions(-DQPS_VERSION="${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") + +# C++11 support +include(CheckCXXCompilerFlag) +CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) +CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) +if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +elseif(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") +else() + message(FATAL "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. C++11 support is required") +endif() + +find_package(Qt5 REQUIRED COMPONENTS Widgets X11Extras DBus LinguistTools) + +include(GNUInstallDirs) +include(LXQtTranslateTs) # Altough the name it doesn't depend on LXQt in any way +include(LXQtTranslateDesktop) +include(Qt5TranslationLoader) + +# Must be defined after including GNUInstallDirs. Move with care. +set(QPS_TRANSLATIONS_DIR + "${CMAKE_INSTALL_FULL_DATAROOTDIR}/${PROJECT_NAME}/translations" +) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +add_subdirectory(src) + +# install the man page +install(FILES qps.1 DESTINATION "${CMAKE_INSTALL_FULL_DATADIR}/man/man1") + +# install the app icon +install(FILES + icon/qps.png + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/48x48/apps" +) + +# building tarball with CPack +include(InstallRequiredSystemLibraries) +set(CPACK_PACKAGE_VERSION_MAJOR ${LXQT_MAJOR_VERSION}) +set(CPACK_PACKAGE_VERSION_MINOR ${LXQT_MINOR_VERSION}) +set(CPACK_PACKAGE_VERSION_PATCH ${LXQT_PATCH_VERSION}) +set(CPACK_GENERATOR TBZ2) +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp) +include(CPack) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8cf7d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) 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 +this service 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 make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. 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. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the 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 a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE 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. + + END OF TERMS AND CONDITIONS diff --git a/Qt5TranslationLoader.cpp.in b/Qt5TranslationLoader.cpp.in new file mode 100644 index 0000000..ffdfcb4 --- /dev/null +++ b/Qt5TranslationLoader.cpp.in @@ -0,0 +1,33 @@ +/* This file has been generated by the CMake qt_translation_loader(). + * It loads Qt application translations. + * + * Attention: All changes will be overwritten!!! + */ + +#include +#include +#include +#include + +static void loadQtTranslation() +{ + QString locale = QLocale::system().name(); + QTranslator *qtTranslator = new QTranslator(qApp); + + if (qtTranslator->load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) { + qApp->installTranslator(qtTranslator); + } else { + delete qtTranslator; + } + + QTranslator *appTranslator = new QTranslator(qApp); + if (appTranslator->load(QString("@translations_dir@/@catalog_name@_%1.qm").arg(locale))) { + QCoreApplication::installTranslator(appTranslator); + } else if (locale == QLatin1String("C") || + locale.startsWith(QLatin1String("en"))) { + // English is the default. It's translated anyway. + delete appTranslator; + } +} + +Q_COREAPP_STARTUP_FUNCTION(loadQtTranslation) diff --git a/README.md b/README.md new file mode 100644 index 0000000..717d040 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +## Qt Process Manager + +### Building + +* Requires Qt 5 + +### License + +Qps is licensed under the terms of the +[GPLv2](http://choosealicense.com/licenses/gpl-2.0/) or any later version. diff --git a/cmake/LXQtTranslateDesktop.cmake b/cmake/LXQtTranslateDesktop.cmake new file mode 100644 index 0000000..a984d71 --- /dev/null +++ b/cmake/LXQtTranslateDesktop.cmake @@ -0,0 +1,107 @@ +#============================================================================= +# The lxqt_translate_desktop() function was copied from the +# LXQt LxQtTranslate.cmake +# +# Original Author: Alexander Sokolov +# +# funtion lxqt_translate_desktop(_RESULT +# SOURCES +# [TRANSLATION_DIR] translation_directory +# ) +# Output: +# _RESULT The generated .desktop (.desktop) files +# +# Input: +# +# SOURCES List of input desktop files (.destktop.in) to be translated +# (merged), relative to the CMakeList.txt. +# +# TRANSLATION_DIR Optional path to the directory with the .ts files, +# relative to the CMakeList.txt. Defaults to +# "translations". +# +#============================================================================= + +function(lxqt_translate_desktop _RESULT) + # Parse arguments *************************************** + set(oneValueArgs TRANSLATION_DIR) + set(multiValueArgs SOURCES) + + cmake_parse_arguments(_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # check for unknown arguments + set(_UNPARSED_ARGS ${_ARGS_UNPARSED_ARGUMENTS}) + if (NOT ${_UNPARSED_ARGS} STREQUAL "") + MESSAGE(FATAL_ERROR + "Unknown arguments '${_UNPARSED_ARGS}'.\n" + "See lxqt_translate_desktop() documentation for more information.\n" + ) + endif() + + if (NOT DEFINED _ARGS_SOURCES) + set(${_RESULT} "" PARENT_SCOPE) + return() + else() + set(_sources ${_ARGS_SOURCES}) + endif() + + if (NOT DEFINED _ARGS_TRANSLATION_DIR) + set(_translationDir "translations") + else() + set(_translationDir ${_ARGS_TRANSLATION_DIR}) + endif() + + + get_filename_component (_translationDir ${_translationDir} ABSOLUTE) + + foreach (_inFile ${_sources}) + get_filename_component(_inFile ${_inFile} ABSOLUTE) + get_filename_component(_fileName ${_inFile} NAME_WE) + #Extract the real extension ............ + get_filename_component(_fileExt ${_inFile} EXT) + string(REPLACE ".in" "" _fileExt ${_fileExt}) + #....................................... + set(_outFile "${CMAKE_CURRENT_BINARY_DIR}/${_fileName}${_fileExt}") + + file(GLOB _translations + ${_translationDir}/${_fileName}_*${_fileExt} + ) + + set(_pattern "'\\[.*]\\s*='") + if (_translations) + list(SORT _translations) + add_custom_command(OUTPUT ${_outFile} + COMMAND grep -v "'#TRANSLATIONS_DIR='" ${_inFile} > ${_outFile} + COMMAND grep -h ${_pattern} ${_translations} >> ${_outFile} + COMMENT "Generating ${_fileName}${_fileExt}" + ) + else() + add_custom_command(OUTPUT ${_outFile} + COMMAND grep -v "'#TRANSLATIONS_DIR='" ${_inFile} > ${_outFile} + COMMENT "Generating ${_fileName}${_fileExt}" + ) + endif() + + set(__result ${__result} ${_outFile}) + + + # TX file *********************************************** + set(_txFile "${CMAKE_BINARY_DIR}/tx/${_fileName}${_fileExt}.tx.sh") + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" _tx_translationDir ${_translationDir}) + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" _tx_inFile ${_inFile}) + string(REPLACE "." "" _fileType ${_fileExt}) + + file(WRITE ${_txFile} + "[ -f ${_inFile} ] || exit 0\n" + "echo '[lxde-qt.${_fileName}_${_fileType}]'\n" + "echo 'type = DESKTOP'\n" + "echo 'source_lang = en'\n" + "echo 'source_file = ${_tx_inFile}'\n" + "echo 'file_filter = ${_tx_translationDir}/${_fileName}_${_fileExt}'\n" + "echo ''\n" + ) + + endforeach() + + set(${_RESULT} ${__result} PARENT_SCOPE) +endfunction(lxqt_translate_desktop) diff --git a/cmake/LXQtTranslateTs.cmake b/cmake/LXQtTranslateTs.cmake new file mode 100644 index 0000000..cf52e15 --- /dev/null +++ b/cmake/LXQtTranslateTs.cmake @@ -0,0 +1,140 @@ +#============================================================================= +# Copyright 2014 Luís Pereira +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= +# +# funtion lxqt_translate_ts(qmFiles +# [USE_QT5 [Yes | No]] +# [UPDATE_TRANSLATIONS [Yes | No]] +# SOURCES +# [TEMPLATE] translation_template +# [TRANSLATION_DIR] translation_directory +# [INSTALL_DIR] install_directory +# [COMPONENT] component +# ) +# Output: +# qmFiles The generated compiled translations (.qm) files +# +# Input: +# USE_QT5 Optional flag to choose between Qt4 and Qt5. Defaults to Qt5 +# +# UPDATE_TRANSLATIONS Optional flag. Setting it to Yes, extracts and +# compiles the translations. Setting it No, only +# compiles them. +# +# TEMPLATE Optional translations files base name. Defaults to +# ${PROJECT_NAME}. An .ts extensions is added. +# +# TRANSLATION_DIR Optional path to the directory with the .ts files, +# relative to the CMakeList.txt. Defaults to +# "translations". +# +# INSTALL_DIR Optional destination of the file compiled files (qmFiles). +# If not present no installation is performed +# +# COMPONENT Optional install component. Only effective if INSTALL_DIR +# present. Defaults to "Runtime". + +# CMake v2.8.3 needed to use the CMakeParseArguments module +cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR) + +# We use our patched version to round a annoying bug. +include(Qt5PatchedLinguistToolsMacros) + +function(lxqt_translate_ts qmFiles) + set(oneValueArgs USE_QT5 UPDATE_TRANSLATIONS TEMPLATE TRANSLATION_DIR INSTALL_DIR COMPONENT) + set(multiValueArgs SOURCES) + cmake_parse_arguments(TR "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT DEFINED TR_UPDATE_TRANSLATIONS) + set(TR_UPDATE_TRANSLATIONS "No") + endif() + + if (NOT DEFINED TR_USE_QT5) + set(TR_USE_QT5 "Yes") + endif() + + if(NOT DEFINED TR_TEMPLATE) + set(TR_TEMPLATE "${PROJECT_NAME}") + endif() + + if (NOT DEFINED TR_TRANSLATION_DIR) + set(TR_TRANSLATION_DIR "translations") + endif() + + file(GLOB tsFiles "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}_*.ts") + set(templateFile "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}.ts") + + if(TR_USE_QT5) + # Qt5 + if (TR_UPDATE_TRANSLATIONS) + qt5_patched_create_translation(QMS + ${TR_SOURCES} + ${templateFile} + OPTIONS -locations absolute + ) + qt5_patched_create_translation(QM + ${TR_SOURCES} + ${tsFiles} + OPTIONS -locations absolute + ) + else() + qt5_patched_add_translation(QM ${tsFiles}) + endif() + else() + # Qt4 + if(TR_UPDATE_TRANSLATIONS) + qt4_create_translation(QMS + ${TR_SOURCES} + ${templateFile} + OPTIONS -locations absolute + ) + qt4_create_translation(QM + ${TR_SOURCES} + ${tsFiles} + OPTIONS -locations absolute + ) + else() + qt4_add_translation(QM ${tsFiles}) + endif() + endif() + + if(TR_UPDATE_TRANSLATIONS) + add_custom_target("update_${TR_TEMPLATE}_ts" ALL DEPENDS ${QMS}) + endif() + + if(DEFINED TR_INSTALL_DIR) + if(NOT DEFINED TR_COMPONENT) + set(TR_COMPONENT "Runtime") + endif() + + install(FILES ${QM} + DESTINATION "${TR_INSTALL_DIR}" + COMPONENT "${TR_COMPONENT}" + ) + endif() + + set(${qmFiles} ${QM} PARENT_SCOPE) +endfunction() diff --git a/cmake/Qt5PatchedLinguistToolsMacros.cmake b/cmake/Qt5PatchedLinguistToolsMacros.cmake new file mode 100644 index 0000000..e5c2f5c --- /dev/null +++ b/cmake/Qt5PatchedLinguistToolsMacros.cmake @@ -0,0 +1,112 @@ +#============================================================================= +# Copyright 2005-2011 Kitware, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Kitware, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +include(CMakeParseArguments) + +function(QT5_PATCHED_CREATE_TRANSLATION _qm_files) + set(options) + set(oneValueArgs) + set(multiValueArgs OPTIONS) + + cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(_lupdate_files ${_LUPDATE_UNPARSED_ARGUMENTS}) + set(_lupdate_options ${_LUPDATE_OPTIONS}) + + set(_my_sources) + set(_my_tsfiles) + foreach(_file ${_lupdate_files}) + get_filename_component(_ext ${_file} EXT) + get_filename_component(_abs_FILE ${_file} ABSOLUTE) + if(_ext MATCHES "ts") + list(APPEND _my_tsfiles ${_abs_FILE}) + else() + list(APPEND _my_sources ${_abs_FILE}) + endif() + endforeach() + foreach(_ts_file ${_my_tsfiles}) + if(_my_sources) + # make a list file to call lupdate on, so we don't make our commands too + # long for some systems +# get_filename_component(_ts_name ${_ts_file} NAME_WE) + + get_filename_component(_name ${_ts_file} NAME) + string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _ts_name ${_name}) + + set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file") + set(_lst_file_srcs) + foreach(_lst_file_src ${_my_sources}) + set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}") + endforeach() + + get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) + foreach(_pro_include ${_inc_DIRS}) + get_filename_component(_abs_include "${_pro_include}" ABSOLUTE) + set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}") + endforeach() + + file(WRITE ${_ts_lst_file} "${_lst_file_srcs}") + endif() + add_custom_command(OUTPUT ${_ts_file} + COMMAND ${Qt5_LUPDATE_EXECUTABLE} + ARGS ${_lupdate_options} "@${_ts_lst_file}" -ts ${_ts_file} + DEPENDS ${_my_sources} ${_ts_lst_file} VERBATIM) + endforeach() + qt5_patched_add_translation(${_qm_files} ${_my_tsfiles}) + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() + + +function(QT5_PATCHED_ADD_TRANSLATION _qm_files) + foreach(_current_FILE ${ARGN}) + get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE) +# get_filename_component(qm ${_abs_FILE} NAME_WE) + + get_filename_component(_name ${_abs_FILE} NAME) + string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" qm ${_name}) + + get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION) + if(output_location) + file(MAKE_DIRECTORY "${output_location}") + set(qm "${output_location}/${qm}.qm") + else() + set(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm") + endif() + + add_custom_command(OUTPUT ${qm} + COMMAND ${Qt5_LRELEASE_EXECUTABLE} + ARGS ${_abs_FILE} -qm ${qm} + DEPENDS ${_abs_FILE} VERBATIM + ) + list(APPEND ${_qm_files} ${qm}) + endforeach() + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() diff --git a/cmake/Qt5TranslationLoader.cmake b/cmake/Qt5TranslationLoader.cmake new file mode 100644 index 0000000..26e2850 --- /dev/null +++ b/cmake/Qt5TranslationLoader.cmake @@ -0,0 +1,47 @@ +#============================================================================= +# Copyright 2014 Luís Pereira +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= +# +# These functions enables "automatic" translation loading in Qt5 apps +# and libs. They generate a .cpp file that takes care of everything. The +# user doesn't have to do anything in the source code. +# +# qt5_translation_loader( ) +# +# Output: +# Appends the generated file to this variable. +# +# Input: +# Full path name to the translations dir. +# Translation catalog to be loaded. + +function(qt5_translation_loader source_files translations_dir catalog_name) + configure_file( + "${CMAKE_SOURCE_DIR}/Qt5TranslationLoader.cpp.in" + Qt5TranslationLoader.cpp @ONLY + ) + set(${source_files} ${${source_files}} ${CMAKE_CURRENT_BINARY_DIR}/Qt5TranslationLoader.cpp PARENT_SCOPE) +endfunction() diff --git a/icon/icon-small.xpm b/icon/icon-small.xpm new file mode 100644 index 0000000..4488ef9 --- /dev/null +++ b/icon/icon-small.xpm @@ -0,0 +1,196 @@ +/* XPM */ +static char * icon_small_xpm[] = { +"25 25 168 2", +" c None", +". c #181918", +"+ c #3F413F", +"@ c #242524", +"# c #008600", +"$ c #008000", +"% c #000300", +"& c #002000", +"* c #00AF00", +"= c #000000", +"- c #004500", +"; c #010101", +"> c #2D2E2D", +", c #139F13", +"' c #000400", +") c #007200", +"! c #005800", +"~ c #131313", +"{ c #2CAB2C", +"] c #033803", +"^ c #001800", +"/ c #00B200", +"( c #030303", +"_ c #2C2D2C", +": c #039603", +"< c #000700", +"[ c #007100", +"} c #005900", +"| c #005000", +"1 c #007A00", +"2 c #001300", +"3 c #333433", +"4 c #020202", +"5 c #006C00", +"6 c #001900", +"7 c #008800", +"8 c #016101", +"9 c #2B2C2B", +"0 c #9A9A9A", +"a c #494A49", +"b c #001500", +"c c #008900", +"d c #033703", +"e c #25CA25", +"f c #9E9D9E", +"g c #9E9A9E", +"h c #828282", +"i c #404240", +"j c #007000", +"k c #004200", +"l c #040404", +"m c #232423", +"n c #9E9E9E", +"o c #9B989B", +"p c #7A7A7A", +"q c #4F514F", +"r c #3F423F", +"s c #00B700", +"t c #232323", +"u c #9A979A", +"v c #3F3F3F", +"w c #8F8C8F", +"x c #5C5B5C", +"y c #696B69", +"z c #484948", +"A c #008500", +"B c #9D999D", +"C c #676567", +"D c #3D3E3D", +"E c #4E4C4E", +"F c #2A292A", +"G c #323132", +"H c #666466", +"I c #999699", +"J c #535253", +"K c #004A00", +"L c #010401", +"M c #252625", +"N c #484748", +"O c #4C4A4C", +"P c #979597", +"Q c #949294", +"R c #949394", +"S c #A1A3A1", +"T c #000500", +"U c #004800", +"V c #2B6E2B", +"W c #6D6A6D", +"X c #545254", +"Y c #9C989C", +"Z c #272627", +"` c #726F72", +" . c #929192", +".. c #BFC1BF", +"+. c #A1C2A1", +"@. c #003D00", +"#. c #00C200", +"$. c #1C1D1C", +"%. c #8B8B8B", +"&. c #878487", +"*. c #373637", +"=. c #9B979B", +"-. c #787578", +";. c #716E71", +">. c #919091", +",. c #BEC0BE", +"'. c #9C9D9C", +"). c #494949", +"!. c #292929", +"~. c #8B8E8B", +"{. c #7D7E7D", +"]. c #7E7B7E", +"^. c #2E2C2E", +"/. c #908F90", +"(. c #BCBEBC", +"_. c #999A99", +":. c #4F4F4F", +"<. c #001400", +"[. c #00B600", +"}. c #060606", +"|. c #1D1D1D", +"1. c #6B6F6B", +"2. c #787978", +"3. c #444244", +"4. c #686668", +"5. c #8F8F8F", +"6. c #BABCBA", +"7. c #969796", +"8. c #555555", +"9. c #004700", +"0. c #151615", +"a. c #8F8E8F", +"b. c #B8BAB8", +"c. c #949494", +"d. c #5A5A5A", +"e. c #001000", +"f. c #6C6F6C", +"g. c #8D8D8D", +"h. c #B3B5B3", +"i. c #929292", +"j. c #608660", +"k. c #001A00", +"l. c #00B000", +"m. c #141514", +"n. c #6F726F", +"o. c #8D8E8D", +"p. c #646464", +"q. c #007E00", +"r. c #004C00", +"s. c #153215", +"t. c #656565", +"u. c #005400", +"v. c #007600", +"w. c #000900", +"x. c #009800", +"y. c #004000", +"z. c #004100", +"A. c #038603", +"B. c #03A003", +"C. c #2C4D2C", +"D. c #00AD00", +"E. c #002800", +"F. c #008700", +"G. c #139113", +"H. c #002500", +"I. c #00BB00", +"J. c #00A100", +"K. c #007C00", +" ", +" . + @ # $ % & * = = - = = = = = = = = = @ + . ", +" + ; > , ' = = ) ! = - = = = = = = = = ~ > ; + ", +" @ > { ] = = = ^ / = - = = = = = = = = ( _ > @ ", +" = ~ : < = = = = [ } - = = = = = = = = = ( ~ = ", +" = | 1 = = = = = 2 [ - = 3 4 = = = = = = = = = ", +" = 5 6 = = = = = = 7 8 9 0 a ( = = = = = = = = ", +" b c = = = = = = = d e f g h i ( = = = = = = = ", +" j k = = = = = = l m n g o p q r ( = = = = = = ", +" s 2 = = = = = l t n g u v w x y z ( = = = = = ", +" A = = = = = ( m n B C D E F G H I J = = = = = ", +" K % % % % L M n g I F N O P Q g R S % % % % T ", +" U k k k k V f g W X Y Z ` g g ...+.@.@.@.@.#. ", +" = = = = = $.%.&.*.=.-.Y ;.g >.,.'.).= = = = A ", +" = = = = = !.~.{.Z ].^.g g /.(._.:.= = = = <.[. ", +" = = = = = }.|.1.2.3.4.g 5.6.7.8.= = = = = 9.j ", +" = = = = = = = 0.1.%.g a.b.c.d.= = = = = = 7 e. ", +" = = = = = = = = 0.f.g.h.i.j.= = = = = = k.l.= ", +" = = = = = = = = = m.n.o.p.j <.= = = = = q.r.= ", +" = = = = = = = = = = s.t.= u.v.= = = = w.x.= = ", +" = ~ ( = = = = = = = y.= = = l.k.= = = z.A.~ = ", +" @ > _ ( = = = = = = y.= = = u.v.= = < B.C.> @ ", +" + ; > ~ = = = = = = y.= = = = D.E.T F.G.> ; + ", +" . + @ = = = = = = = y.= = = = H.I.J.K.= @ + . ", +" "}; diff --git a/icon/icon.xpm b/icon/icon.xpm new file mode 100644 index 0000000..29a3bcb --- /dev/null +++ b/icon/icon.xpm @@ -0,0 +1,57 @@ +/* XPM */ +static const char *icon_xpm[] = { +"48 48 6 1", +" c None", +". c #000000", +"X c #00FF00", +"o c #7A7E7A", +"O c #FFFFFF", +"+ c #9E9A9E", +" ", +" .......XX..XX............................. ", +" ..oo...XX....XX.....X...................oo.. ", +" ..o..o.XX......X........................o..o.. ", +" .o....oX.......XX....X.................o....o. ", +" .o....oX........X......................o....o. ", +" ..o..oX.........XX...X..................o..o.. ", +" ...ooXX..........X.......................oo... ", +" .....X...........XX..X........................ ", +" .....X............X........................... ", +" ....XX............X..X........................ ", +" ....X..............X....Oo.................... ", +" ....X..............X.X..o+o................... ", +" ...X...............XX.Oo+++o.................. ", +" ...X................XXo++++oo................. ", +" ..XX................Oo++++o.oo................ ", +" ..X.................o++++++o.oo............... ", +" ..X...............Oo++++o+++o.oo.............. ", +" .XX...............o+++++..+++.o+o............. ", +" .X..............Oo++++oo+..++..++o............ ", +" .X..............o++++....+....+++++........... ", +" XX............Oo++++..++.++oo++++oO........... ", +" X.............o++++++....++++++++oO........... ", +" XX.X.X.X.X.XOo++++++++..+++++++oOoOX.X.X.X.XXX ", +" ............o++++...+++..++++++oOoO.........X. ", +" ............o+++..++++++.++++oOoO.O.........X. ", +" ............oo++.++++.+++++++oOoO...........X. ", +" ............ooo+..+++.+++++oOoO.O..........XX. ", +" ............Oooo+..+..+++++oOoO............X.. ", +" ..............ooo+...++++oOoO.O............X.. ", +" ...............ooo+++++++oOoO.............XX.. ", +" ................ooo++++oOoO.O.............X... ", +" .................ooo+++oOoO...............X... ", +" ..................ooo+OoO.O..............XX... ", +" ...................oooOoO.X..............X.... ", +" ....................ooO.O..X............XX.... ", +" .....................oO....X............X..... ", +" ......................O....XX...........X..... ", +" .....................X......X..........XX..... ", +" ............................XX.........X...... ", +" ...oo................X.......X........XX.oo... ", +" ..o..o.......................XX.......X.o..o.. ", +" .o....o..............X........X......XXo....o. ", +" .o....o.......................XX....XX.o....o. ", +" ..o..o...............X.........XX..XX...o..o.. ", +" ..oo...........................XXXX.....oo.. ", +" ...................X...................... ", +" "}; diff --git a/icon/letters.png b/icon/letters.png new file mode 100644 index 0000000000000000000000000000000000000000..060124531850d61ef7795198047a905d1e65795e GIT binary patch literal 720 zcmV;>0x$iEP)Px#5>QN3MF0Q*07pImeQ{CjxBvhD0Hct^`Bt<50004WQchCe8{l_bjU~AH zG|TQG9#|ewvzP;jK|N;dTy4n`r?p~7G;`KW^#dFJ@GlV(14aQ^0s#>?_LDyK7U`F@ z8HdSLoKq&2mG6_)c2xWjbXD|I{w;vybiB)E;I@k%fe+Z@5s=i7^Wy|Fu?O8J33Kcq zBot4#;h{F)*V-ep`HSI*Z}W!Jow?HE9RG@j`;O?rkHlzCZ^GXb!zuC`d{lo6KCy!U z*-(TX*z%E`LQt>RasAP-8|)Oh<>9W_!CWXLO#m_Xb6QsZJv$Trx9kl313RPGKqbJ0 z^rC9x-v>P_-?l54oKI_0790c>hq53(XLCsln3b{wRbm!P8XQ$wHS|YGROT6{L{?%8 za9w&)665re1WN+!*~MOcEwh$ASu%nz%JpTVO~mLrzJ52};mfKpDGRNr)Y(?jFdb0XJz^Eo#!k3**n7MS%1}& z%%8%GpeALdgSEcXdKPJ6Q`55cENgs4VwR-AeTks0p^2Tp2<+i_KliM?KW?qZVXX}x z>NfDb;?tUFkrNGkugw>05&T8*DV%%OtwBHZ-_Cy*mRPmjb_2Np0000 literal 0 HcmV?d00001 diff --git a/icon/letters.xcf b/icon/letters.xcf new file mode 100644 index 0000000000000000000000000000000000000000..4e9fc3e09082ca5eff4f651bb2e651b26e652bb0 GIT binary patch literal 9159 zcmc&)&yQU<6}GSECSgiT5iLThs*W;UB%q+A5-PBmNt#6$tPqu0bWx?#nPEhmgv@}_ z1u9ZET>-0hg@n{KVnIb0-SyAthDFPQb`Vk0%&+(6UB2&}V_*9|zc(oq@!t5_K0db3 z@%fH@eBW$ty>@fy_S(kMuP$D^6`X-|xE4z59C* zzK-O{@(rXj&w<2*&tpgnNIyklcb3WbII^daKe~xYFKoSbZFBv?=B=$)Kw`T;b8dU* z^;g%Iwyy82-`ZYZ+u6Etef#|4xsA=W8?WBDwRBFh3%533zP5DnyBFnCDO|Fq^XU3= zrE6?onq6Sm+uJv`x0XKp<6FCbcyIUZ_sB$}@k4`g)jOm!R;C4LSkn1f zM2h8yvhr86^21sAYbbN9_&JS2>>n*oe)}{pX(0zx^Cq+bTFyR&^c|#UOP=O05d9g5 z`JrwW8z46rOX%w3lq48)lqb9 zC^B?v3p<%kG{>H3)2L4JEEeEM&9vgKxTf=Np_{laqz6g9TR;Z888u%qqNLL{1vzA4g^9C@zDq* z(He7r;?ToWa)+ar0-<-2i9ZpE{6?G>wL8S96C@`-Ef^B-fAIP3ZQ|KXS?wd1 zRThM1-C~B$Mmmzq$W73T;yUQuOAq0-0nGYi# zEbR^w&cwMc&!y!RagBoMmj)*3ip$Eua%7IKyu3r z^roYC#!nSRHwh@b--m8I;6>n4u98@)rkoYk%1$<5Nj!NZvqKAIjuABUhC4=-ipFHx zVEUR(QTyENCc)1NP@snr=8Pxl@JJOePzRVBBbKnO)(R}2=(5!zj984+2ji5zP$X4Y zpd0vZ%mCp()})yDf2D#bl2sBO$(0JHrk;GJCK)E9 zmBU~q?V>D>i)$(jHKyYt3o%wAvG-hKVF%fc5b|Gv(S32{%<*>&ROg2-+%^Nwp+&{% zthodB_$O_trRsKruu`L5?>I{Foj=NcVir9LZym@kRaJn1_pW|-6nBi$)b<9WF`&=B z^To`^8H_#9peA%Cc^N=F$KaTDH`_)N69sTXX03n$1{WWI;^O115<+;NP@rzAM%YE7 zg4F_N;1hRPyxAegBxlnBKLwUiLLamd*HT7~j&g~UjxiV~16jGTsPN1rUdQ>Z(jGE6 zkmhs+S5}RXGiB@rSCl~I)=4=LB@l6#$9n+FrU-86COQ;QZPKyJ{!wV{g4L;w124n- z1h!4gDQS*94TpID7cU$dCAup#=ZlD$U@$~rVTzmU{MSQOH>?u#UBDQ4LCyc{vWnFd zs(8ZC2l`VqKuio8<%LCxODpL~_D_;q>3r&Bc16|s(&VM|B6hwFQDRu2IAO~!)mqS3 zK`?7K60PGgX{TsZgE?=ls|b1)y>H=LcrnW~ZX52iD8>XbFFI)U{$ue7fwRB_z$div za)Y=;M_mVKPhhukmbFDyg5SST~LWYGSUr+LFqgp3>)-=xM2*bN-;71 z2=9af)j&CT9Qf#f*TAiBM>19jkOuh0fG7mM`LRiSI7L#N>>kfE5tTwxt$_DcCfAgj zGHmP$v?ReGC)bhhd)P^foL;C{XcV!(K?Ygfu!>_Xz7tWH;qXp}98GC8@8pH)e-XM> z(Nl4rDqoeDK~+(fOWtq(QnIj9$~@5+iq3iCTAi;XX0v18nt6vVJ5_7NNc}#b{!-{c zxsyTIo?=n_AVDh&VhKYS8t9>WMZW?-!x%q?5OUx{2FGg!S_?p;^`JZ57uOTVOP4X3ePpu?m!V#2F#tye`$YT8Nd97`KWXq)|KA#15nASy^~Gh~o;x zn;5fV#1lptj9e4H-xi|$+}MUXh!d~4A0TR?LE%k2m^8CmBmxBpzPQ)(eLC(2eW=JY zB~L^SF_wNCB$&!L3N`h!g>b~a%E5x)!$XGS6cUQzh>))h0W_+M%kO|hr?Pu^k;Z>= zeUY~*a3bi24TR^;X^adP!(_o9^4yV8Yg6zC9+L{ME3-oj@?jD=?`B9RuQ&DGmK3qF zL00yVkv2CE_*hy=`>6=ST4amB?)$JuZ@#IL?&NA!;xNTURcM#Q8U`!d_}o~|?ldWu z>oGM5;?zk@0F^h%`XDi!dRCg}iv({qJr{6}$QULtPec7Nq+d&l%>V!JPvQ}O+y5=f zK)!sQ#}gov&$8vn(r^3--`%6deLZ@Z?FT(OWNbe&3 z8R>(PXZb2vgBmD;HcjSMgxy;nf%3t6;=MDdlI+K4~o6lSGBJxE< J^Iyyc{{}Ff+J*oC literal 0 HcmV?d00001 diff --git a/icon/letters.xpm b/icon/letters.xpm new file mode 100644 index 0000000..cd7459a --- /dev/null +++ b/icon/letters.xpm @@ -0,0 +1,19 @@ +/* XPM */ +static char * letters_xpm[] = { +"570 9 7 1", +" c None", +". c #000000", +"+ c #00473E", +"@ c #007D71", +"# c #51ECB7", +"$ c #FFFFFF", +"% c #00A390", +" ", +" +++ @###@ @###@ @###@ @### @@ +++ +#@ ### ### @ +@+ +++ +++ +++ +++ ### +%+ @###@ @###@ @+++@ @###@ @###@ @###@ @###@ @###@ +++ @###@ @###@ @###@ @###@ @###@ @###@ ### ###% +###@ @### @###@ @###@ @###@ @+++@ +#+ +++@ @+++@ @+++ # # #### @###@ ####@ @###@ %### @###@ @###@ %+++% %+++% %+++% @+++@ %+++% @###@ @##+ +++ +++ ### +++ +++ +++ %+++ +++ +++% @###@ @###@ ###@ @+++@ + + @+++@ %+++@ %+++ +++ +++ +++ @###@ @###@ #### ###@ %###% %+++% %+++% @+++% %+++% %+++% @###@ +++ +%+ +++ +++ ", +" + + # @ # + + # # # @# # + + + @%+ # # # # + # + + + + + + + + # # # + # + + # + # # # # + # + + # # # # # + # + # # # # # # # # # # # # # # # # # + # # # + # + # + # # + # + + # # # # + ## ## # # # # # # # # # # # + + # + # # # # # # # # # # + # # + # + + + % % + + + + + + # + + + + # # + # + # + # # + + + # # # # + + + + + + + # # # # # # # + + # + # # # # # # # # # # + # + + + # + + + + +", +" + + # @ # + + # # # + # + + + + # # #%# + # + + + + + + + + #+ # ## + # + + # + # # # # + # + + # # # # # + @ + + # + # + # + # + # # # # # # # # + # # # + # + # + # # + # + + # # #+ # + # # # # # # # # # # # # # # + + # + # # # # # # +# #+ #+ +# + #+ # + +# + + + # # + + + + + % # + + + + # # + # + # + # # + # + + # # #+ # + + + + + + + # # # # # # # + + # + # # # # # # +# #+ #+ +# + #+ + + + # + + + + +", +" +++ @###@ @###@ ###@ ### # +++ +++ %#% ##@## +++ ##@## +++ +%+ %+#+% +%+ @###@ ###@ @###@ @###@ @###@ +++@ @###@ @###@ +++ +@#@ +@#@ +@#@ +@#@ +@#@ @+##@ %###% %### @+++ @+++@ @### @### @+##@ @###@ +@+ +++@ @##+ @+++ @+++@ @+++@ @+++@ %###@ @#++@ ###% @###@ +@+ @+++@ @+++@ @+++@ # ### +#+ @+++ +%+ @+++@ +++ +++ +++ #### %###+ @###@ @###@ @###@ @###@ % ##@ @###@ +++ +++@ @##+@ @+++ %#+#% ##%+ ### @###@ @#++@ #### ### +@+ @+++@ @+++@ @+++@ # ### +#+ +++ + + +++ +++ ", +" + + @ # # # + # # # # + + + + # # #%# + # + + + + + + + +# + ## # + # + # + + # + # + # # # + # # # + # + @ + + # + + # + + # + + # + + # + # ##+ # # # # # + # # # + # + # # # # + # + + # # #+ # + # # # # # # # + # # # # # + # + # + # # # # # # # +# #+ + # + +# + # + + #+ + + + + + + + + # # # # # + # # # + # + # # # # + # + + # # ##+ # + # # # # #+ # # # + # # # # # + # + # + # # # # # # # +# #+ + # + +# + + + + # + + + + +", +" + + @ # # # + # # # # #@ + + + + # # # # + # + + @ + + + + + # + # # + # + # + + # + # + # # # + # # # + # + # + + + + + + + + + + + # + # # # # # + # # # + # + # # # # + # + + # # # # + # # # # # # # + # ## # # + # + # + # # +# #+ ## ## # # + # + # + # + + # + + + + + + + + # # # # # + # # # + # + # # # # + # + + # # # # + # # # # #+ # # # + # ## # # + # + # + # # +# #+ ## ## # # + # + # + + + + # + + + + +", +" +++ %###@ @###@ ###@ @###@ @@ +++ +++ ### ### @ +@+ @#+ +++ +#+ +++ ### +#+ @###@ @###@ +++@ @###@ @###@ +++@ @###@ @###@ +++ +#+ +#+ +#+ +#+ +#+ @###@ %+++% ###% +###@ #### @###@ #+++ @###@ @+++@ +#+ @###@ @+++@ @###+ %+++% #+++# @###@ %+++ @###@ %+++% @###@ +%+ ### # #+ +# %+++% +#+ %###@ @##+ +++ +++ +++ @###@ @###@ @###% %### @###@ +#### @###@ #+++ ###% @+++@ +#+ @###@ %+++@ @###+ # % # #++# ### %+++ @###@ %+++% %### +%+ +###+ # #+++# %+++% #+ @###@ +++ +%+ +++ +++ ", +" # "}; diff --git a/icon/pause.png b/icon/pause.png new file mode 100644 index 0000000000000000000000000000000000000000..9b5dc47b63a28075c2e28e5985de028bd938c07b GIT binary patch literal 502 zcmVPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FZT01FZU(%pXi00007bV*G`2iOP% z03rjk5dZD~00DMML_t(Y$L&>3Pr^VDeKT!sVX3w_!7KP{EIP%zCsLIs%5<|vg)i3#2T07Mv%$6(q{Gnm0l$Y!&t z6fiSK%#1>z0K+gg<9C*2f{1s;5-9kGW4w0{nECC%IQsL2S}Yb7gBg}(fO)Hq8{4)K zMPp2-v%j4p0Epvw)Ah7!IF6H^fQUfE$Zb!eKR1`~8pjw61}PN~@G=?!L_5iu04JAA zub|s~1RDnKJDoMZ>$;ci_U+EtuTia5*B-lN9{}Ka-b1ZcivT=-%Si?iQ6fUic2Pu% slgY#u5fTw=X+;sqh{*ZrhdW+;0FC&7eq0c$4*&oF07*qoM6N<$f?~eMBme*a literal 0 HcmV?d00001 diff --git a/icon/preferences-system.png b/icon/preferences-system.png new file mode 100644 index 0000000000000000000000000000000000000000..a8103d82ea7437f9b457475782d08ce479cfc1d8 GIT binary patch literal 2868 zcmV-43(NG0P)z^Q>-8KIssI20AY({UO#lFHH2?s0HvjFc3h|e-=V%E)639B}=eJ21>y^ z?BX&_8YE=!v_NQn>v$J3d0eSgDig?UC*0Z>&w00030|5T2#3VG!&S{q&c01E;u;Wbo}w;l(lfbpI9lNH9uU@ePtglR6O0a#N4tx3mOi!B5Y!FvyY zG3_Jv7(s-t>+9mmx!io*00030|BR4L3W7iYg+H;f(Q+|R`%Y2E@f02`=nU=Mhqlwg zC@jZOCtC=K(4x0|yvO^o0J+)ja=Bi8$=l`1aev?!CV&-ZDN}z5QUVZXDN1VqTrsCq z)i@g9-ZXeY(8op*P2JKOgHn1B+xB26@|+?J0qB&XH3oo%=kk1&{d>j?hDz&ES0Mxd zjx)o&yU}RBHr<0ia88ob2LJ&7|BRDQO2c3f#eezJWYa~lDY~l{aFG*;h!;>1mlcHG zMhZo7A=Dxs!BsXMLtG^n&_$}W^W~e*MKo=|MF$4v4R7EL^Tq=XfC1aZtGD=Cxx>rz zR`=!M@n81dZhz!g)*`A%uUDrkm9-Im~PH$%N_I|acH9me=EuWP}f*_3l zI^EG3Cn}Qc`fu+dEQsJ^cve(A$?kezqT<1;Zym)#bbVc4O?Q7)BY?rntLwT~7@6T6&2bz?hldswP$+#EpBec)%lzCN zjE!}ppPo3Y*YT^T-H8SVVmSqw^=7jP@KFJ19UW>b2;ly{Z&e8{ZAOJuDnD6WTkqRr z?8fJRaW5$rU!CM*e*zO?OxQDK2}~Hh_2*U#;W()2Y1G03LMb|0qiZ#U?_1pQ(O)*d zZrCJSiET$j`pwmJ7KC)H=r9f+p@6$2H;!aPh{Q3P0r6f{HZW|3I zCZ`C)5Mwk4KPq^h_d*cHm01a$pGRIQMI}8vK)Y>mJG*HxnFLB9rR*8A1c|ndF>i_|-2>7D zN~)eQAo^=?U5EYh_M6G~%iFlF+vNz!#+PrsWAU8I0iS?LK)z$QXaX1iSucOrD1s=C ze`aOs(uWCh4y~u&EetU zFu9IDzkh8SV=jfj#6jnP%QvnwIhkLeo6RO4UakEXz|_>~3qUyptf(+vsUWpluJgFH zh)O;!7qDsU-z$Y8N)$yzBKbXv5YgPeva*6`zHNXioCs`g7A`qC`kCESMMX3A0jhBE zUjk6CXJ^^k@=TfmRz*}Zc9#ZF(LN95o#rS)Me|o6>ihBc{SaviV0#-oI*N(8MY~(%!As8FuXmxc^$Sf{;=^-MVo0;~u`*E?E=m~C~J%7Q~g=^m5 zhsZex)?!K}jt9wU1zV3Ztx?06KM-(~g^}65qfe`sDXt#;h*D-T*@n#9M+kVMG5b)sPBR|30 zwbv{zEzybZ6XkNbm%15%Bzv6emCZCab-Vt9k&zM3oSpHeQ{xlN&er_h!=H^bKss-I zYcz&JdcG#-p49pI-g9H4u;v!kLnieLj?cvGs^g|^WD!3!Z3 zM8V5cdl4b^&JTn}TT0S2X(_XsQN6(3CZJ`0xP$fBpG8+r>ai za>|r;N;poLT&{zYZ@rD|Yq}Ls6oe3{ZEbi*j!?XM6?0+&>+D&~n>Vq}oC)7E7cb&D z4nosnv$(Uf!`%ElAAU5FIHO+=yjgd~fBAWw*WVZjWmb87B?)LdjuUyxg#zV`4IH4f zyzJX2Pa^N-jSb3$0tDm8k8RuBpS}0OdH??GJ?wxu$bN=*PQdWO()yTXSq!}Y9*M1943CUN zu3IYD-!x5f9UU|`w}gf4a8G-*kjarna=9m2qjzrA(a1l z1**9JOI-m?)9TavMh;d3Up7?`z&08K4MbNswPKgTu2MX&_FLx4R2Vf5y!~Acwug)7O>#J_kFyzQQ`SN+zJR zucwP+h{fsBDGv(|EAY6@^?Sg7=6_ciW0!4*e(L{FtwzT~z6C<_zgBeoP?`4hZ3RO| zjc;;l1CRSB#@-4xZaL3olV_e^SX!x4v1+yE;|WaZJ#030PZ}p**dtPOd!p|o#RptR zIVBS&n>?`EAziDL=)Iot=W~`{T!q$NRjhZyHE%LpXXN&u&m-}F`;J^%_couP` zYt$z5c(gLJUOy(Xfng3u(dq}yy9(MiKMDHASK`g^UqeCizWd6ZmitZ@|5MPOY5a2%ITrA_G;*;i$j@p#?a+AMtsp?bOFZqt z=X~)UKX@GOm9JIdw&4zcdzNAM%4?iCJEA`Ftq3pM)3~i_cA)feiLb9$nDKp|8mQ7K z9h=SLDdc|roTtp!b-u?|EO(F1y7}eU1M?6Ab1M^bD`O)d+seRThr>G~6b-rgDVb@NxHX)V S4eJAHVDNPHb6Mw<&;$UmZQ+9e literal 0 HcmV?d00001 diff --git a/icon/rec.png b/icon/rec.png new file mode 100644 index 0000000000000000000000000000000000000000..6ef3a55ae1d20fd95c8b724cdea19173b133afd1 GIT binary patch literal 686 zcmV;f0#W^mP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXB1 z4lf5v843CT000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0006BNkl$4hER7ILO)#bQ)L>ML8_>0~vCV zhM6#+aiZ=-7UhlP`(}Uz(;HPNYIg=0k4vKz^|_nOw$50&rG(x-l+UMh3(mNxt-JC6 za7vS4Q3L4q5V}>dsUi3}0N^JjnNMrd&@i+FtLmK?!y4zYVAC+`tZ|$-j<()M4eJ%% z{oZgW%aRRq$^Y3HOp`2=hNp&Q!*UGdK-BLOwBi|W1TO?H4IczQ2hlz%XvWOEGF))A zodu_}5%$EzFdi?5c^u$farH32B6u$N;$o)WFOyRHy3218)w_}Y`g;8nAOFnr8~s65 Uk2y6tTL1t607*qoM6N<$f+b`hH~;_u literal 0 HcmV?d00001 diff --git a/icon/superman.png b/icon/superman.png new file mode 100644 index 0000000000000000000000000000000000000000..60609886c11ef1c3701737e305945c5247a62f08 GIT binary patch literal 4497 zcmV;C5pM2@P)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOZ0 z3l|6_qd^-001**ML_t(&-tC%cbY0bT$AA0W`|g|cG+CBqW9A_kY``R8urZd6EZZ1R za16AeO*6G!NjjwoX~PO_7fplB5C|sIN{ClNnxPqhW&jF~0S^qtHXaykELol?UvZhbvkxZ9drfd~;20 zh+_m5PywTFZ$djVqY9uHfw4n~zH?=)T8f2)J@;du`PQt@+|+&409bzE)aj;c$LhIF z#dJC&h|$+d{Wh8^V2R!zncWR;#2_?sJses2Koy}bE? z(u>bLJg1>hmqPG|fiRld0uF)|5E-i>s0tzxP>LXcsz%q!#0sbg4vIw(6cwTJ8loCm znFxwjp2?`v(bH(`5lu`|AZgi@%RGAW=Wbnc-d}t&`;Gzl@%2j<99;XGpIh1;G`fMi?c85=cQRvC~ zoIGPPr(b*=lizzB<4t``S)Ap58rgqp~Gq)do#kHfYnD_ujtZic>CK zIQTmO_~A8+TKYDwe)zK5At!<2_d$D7*k<74-^!q*Sn;V|PK^oElOg_qQW{nWVOu86 z*8VaVeq;(4TsM{UxC}_F(w#_gF;O4&d=*325L8h@&;Ydo2ex&w?mO$*`kQv@Ydz*v z8It)TUJYcE4!_y0EIh@bt`Jh}x7hL_jRqSGGWSyic<$j?vsk{Vy^qVP6EXj($9^}lpVT$8Z8iXLPh=rY*B-`V0 z{_N|`)J`5ZM)4R@0RV9U7Zp?Sf~Mj~6wwF-B>-y?>jORzuxZ)^O}}v92mn^JwKNxA zdT!P1>H$&;em`_3U@P#EZ^WpoPV(JP4Kukcoa2W0{f3}y!GfQ1c{p(S%bzTudO|H2 z4^m7AfZoJb2S8UVdy3ZA~7jToDr! ztgl#G8Azk}A}Fg;!rpx_>qOzy95~fVIj(F>I?ALJ z$kZ!yJ^`ycm7OUFvqG#0=jC@yU-`*vA0OP^UJYRGyJk^y|EA|w&7_~S1wX6oO~Ph{ zk1vOsCfN6aWz(-hX4Qh9Q*5yk>qQYGBghb=JY>!%l;%&s6MK|hDX>EZw*>Fm z`{MNfzJBrj16}*ZIrAE7A3C?Uy`heXU<_u2rwYnPz6P}uBZu{?+muN~nB*&d5!4=< z&-I|HDb5S~1J0Ox3dX6dpm8Gw#X}^9iVMa?#Etv`E>dxf5tWhXDl&3cSy79qjVO;o zx-QOvZ9R1E%pxB2iO^@1K0~Do2OVL+f%88ooID-EPx&gUH&nvgH&=xgk!m zz^eOq(!DcF7y`y4=EV`mLyU`<7-B}Yfg4$daS(A(aX<_rQdtHCwWvf_sn8<92UV&k zCE34ofLvXITz#BeeT;m4jC`F#u3pI3!U-3^sqZzQ%J!$gbi>r75ayJ;h3;t={nJ(V zJ$SDG{KMk5$&;RY;@K7*oS5N&nqL5gjx2PWC(G}BDY!o0|a1svb`Z(3&lZ-p9hNjc2 zscm$DB4`n80kitP&X4(Cy(pqaM_ge~O!|JB@-1oQ7zVzYOM1j9+ z`@k8;Z+v{!TpgH{j)7AH>qR)>1Hzi`Djz^8+aq%z8FOP?a3VJlZErWyO-;12@)nXE z6-J4Sh(OGt5KoZ{m4R%DzMLUfmm+ggHD}M8z$q8j;F=;z9tn#mxe6#b9=RvSu7{MC z1_=6~Sc26Pr~G2~oGU(b+p=3rNAQ5Zzw(MxPI!LJZ)Y=jVloD9Ej*Kh-Gi_=t%Uwq z_Vxy;*7221KeK&%|FpRaR`zxsxN2fptTtsh zp;{O~s7?u{cu3DT?3Mg})K854?)9F%Ua#y7wO}hf^dT%NS1*vm!kbhIDWRPs1rPuWcJxj7GiCYe-uz2j)E`n7Zt$>Yu zuR2l{6RaNo4o5y4^X}nYN8YgzhEOVl9|&6-PkUi+%hgvbU3Sav-@7LGxwb3jHLZX0 z=jVmljOReC%CNBnUHz6YgmlKCx++eU8miPM8IO^OIT#C0Xfc5WKY%bq)gIagjtgE4 zbzDLt5jUtf3sI1YR;KKO?_aeVAn8(zC58H?FkVuMi9r~I1XY24V54W z&``1VkUtS~C=x_rRB#|89WpkgF)5@5e9A#sVT&WA3SOCT^7cLRubsd2)@7YX zwJaX_g!!{4ZeI7$Sz#_|#Nt&s>>Zcn%CF9(W?~J3hnfV6M^HAD@`7Jf{IbOl4ETnS zsOTxi#kdAH5yOkeFs@=;qB6PqXiz3774QoM3WLK89_Zu1<}NxncC&v|FK10o&^TD6 z97x3#Djf|czOrlKwR3K}?Y4bK{aj$AeSTr{`EFW^ zM8HeA^fslDh9m=92bkzWZ6bzKEMbcQfm*~E+(eRm%%RV>j9+vftv9q{Tp4p%U{Q@q zVrYn{PdT6G)t#@>zIGQcJ+=$F^PCn}%6&e5Xi;Ea-ElAOn19`6cYgINUB~QX=tvM+ zmo#jB`lqLrvmk<(a`BT6@j;(b7)E)Gyv_`jIA&_%m`l%O+LCkeivj*{iNaux;hrIe z59S%@%2IqaOVFDqQ3}bJfNJAV$`&byGI}o2RX3?~=e!SIa>w%J``(Ej?-&4p8x~!D z{e+EAt~fEs#Z(kW$K(ofMARPzKqcY?wkAO#Uq&2*>$=2Z4qnXQs^S&{f?|nM5N!_C zhzBrol%_&=UE?e5t=G)IbNM|xkNMf)8?}GFe8H8C8(075@nJ4An#AbnLXRzNj2>h# za>650G5yyHGJ29!8EEu`Nu)x2l-}ybt?di{r0w>*@9ub44oBat-PAgJM*Y?ek4+AT zk|PRRsl;;Ln7QfLqNpA+n!}m45y7IE%7JQ6U1NJk%eAeyf9;-*-&c0$XaMkJ`>s7R zE}r#F>ENp$t_jPi2nI;k$1znNaRd$Y2qZ?plFF8em~&Jli@AbV7bl+fkYY$sL8!N8 z{OJ**lIABYT7L1^{=@^~9a^{kW1UblOLrR5!uoPlP{v+TBu4hQ39MW|$c5dt? z*I8y-+F>|b0-&#I-0p32u9|ztcUH6?O?i(x001}6Z~0K;=5^nnWb=+uaMLc?<5FCG z*V&}&6H%E=%@_xHs1~^4f&D!Gl@6vR4a0*zR$#Dt+<|SEw#~i$-g{p@+Oj&w0)W-+ z9b3-5_>y+IyROPeU_xI>6$74M*U#y*tMC$0ekX(|enfnv%!UW{@%WvcOiLPu27N#n z$c%fnedhdmx2^czi^oFNV*&uc+K$dG(`Q`LPEU7RHNFV~NELiGJ~hDUm!^ru6~RX| z1S#|UFMC*d`yQsH3`2tfDlkx8*Y(Q9t#fX@??*2jD>;uD003(`I=7rNYj%(6>RMQB zePaV5nfKZB>=36nd$^tje6~E&%VW27F)ihg8w?Q<2GVs0Ixko_bJ-7n^vbc6@t6Vt zuy)7J_2>$$;Fq{nmA)Bu0*?z&iSxbNX zla6C8-!TUu0-@vC^Jce{W&fVJRfIyI#Bw3)SLfOMP@ZXVhg>#9f#Fn5cl-NW<}Uro z&$hiAvb`$+0DSBVUtGJhKYP$Quy1~qhK4{$lr7^8l!h%>HfC`^UmcDvEA5w(ePx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOS( z3?3P=N66X$00Iz6L_t(I%e9kDXcJKsg}<3;{v}D69Z)^CT%q!r8HKv(4Z}D z?9PQC1>F@Fg4j)KmFPw{ZgeApo8m%SBP~Ti1YLBYLdeDxETN@QCX}@KnasG!=!gmd z@xtXjc<-KjIrkj+UlDCAO0E18O{b9-0JT_h0)jcSevdWl_gGE!Z9jlwnb}(O<;@1r z2kbU?(fu9?Ai8);aCN0ffy`L-%`}w(aUCLDTef zxsm|(1DY{{pzFv7&ZoCs48Zpf6P3gdHJR6SaO9+<K1^!pv$w6fOXAOU1DlH`JlIDK+AA9|y+06Q?^*-n;y^e}1|Oi~$`c z?tWg3h~(Els)C>uqG1_$quhV?bt4pzOKz71C^lDo@VCzW3o~l+@zr-g5Eus%=J%A% z5*oRCsStU6XGd__Mu9`WOkl6pwPL6HFc1QEfLCUK`D#7C(X+4JR;gK>gS}qBLsR4C q=xZu*4fyakpEdoG1GfIzwci04lcjiM*vpjw0000Px#24YJ`L;xfJBmgANML1Rf000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOc9 z3MB}>g^Qv9000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}000ZcNklXO*xwdondo=MjPsfnY(-wIF4_}Xsh36XLPFKf^-{<(W_{}nAnMw}Whon07Vcp}c z+yKrC$EM5+WNcpdjcaRVcsw@^rSIYx)2_c|`|7iEX`ZL>8oADA-)~k9MxDbk1(&$? zFvR6FCWq^~JnX^|ou8W{Gv&1p#}vlBAHF;w;swL2&lP2`=y*Oqy#w)_WdFs1IOFUx zLttzkP+PK*i2^w>@;N8=DY4h4#&yg+NN6dy;A@*Nha=|5W@aAa<+;?T15jeJ+~;jU z0MC=jvek4?*!vJYc5XTyVIR_WSTA_xHaNXPI9+H#VoX?$IATjhKGwwC5J*7rRe5H? z%RFHj))-bLBr0&@spWlPaC0-j16KBPU5vQbEzzf~YVK@~aA5QS%tkcE7ThEZ#pXH_ zpJOfQ7>PP%SQqmlKJwr^Jw=N177QbSM7a{h$AXvTq0+7Lecgx7I`OOthT-E><)mvv z4$Q6aNjCIZMiVnI906e%?DvcXlZ}IdVHEGh1I#=o1|Sx+UwhfqWiEPnla$798IuXF zak|}6c7S=~w2L!&xN8#UdX6_KmdziOl0FV90`n2DQlW+&JIgL{`-oaNcm~;Io=A6X zpG!F4z>zcJ@n4|mqD2qudKpBRP+2^e^hk%j`+U zf*FL=j>BUQ&LsPJaibHrYa=rZur~IDhI@(1mpIJ`d0xyNpnaS?;rhUAblRsg1%APu zHw>NJt#SA<0qbm`NfYe%!B5;XiGSz&=X!cw<=ntHF$e2xf=A^^2ie8VWuHco3t5>E z>=#mEH#hx(tlEnwdhx4|N4m4)i1Iv+^s1dGK6Oo(A_B;|kVfR0>Y3!@9ac{oX$J?l z^CTqJDfOhJfl103M$JJE{-OkFzZ7znJY--oCAZgLb!03fkB4SyV-tp>vPmX`Zxxq{ z^C{cu?w(G9LjxBnR|RjH0RtE|UgSIq}%=gK^IX)urjc3!~}>fAbUl1QyuiA45~^A>z? zGFVgTS2kOS6)!QQW!L(QH~E_+Oh5mnq#-x)S25&gFg*zzp8^<%meY2;-{cF2WwY&P zP3xW!f0{XDQ{NmRueb^FR1J0Jl^x)m07SSL)5LSLUsBMCcW)Z>?^N%4pQmW`9uwy@ z@EGr@cQx^S*=AY*Wkgpflc)F(rqks7`fm%tys2ermk@H8_^GNLMM+T+N{O%( zlTNEXuqR-Qy!FFrA-ZnUQR{BTE_1`25jdsz`jmf z2+odKg4Wa8@`8u4Rx{+x9x}p+Z4{H`+e^W z%9Eq`A%H3L2yrKmis<2`Qp!JTf2o*$hRQ{|%Cs2u?KdTX!2r7|SXI)5*~03GLxB_C z)yTD05fgeBE`0rw$*MV5=?ztvkL*Kr{XXEGZ8-5ug>R;Mc48^_X{4x_=d@&_tKy36rtcP)PPDZeFu#y*yNw_@0yQN%Cv?=Z+& zcgI51OH)@socQ+q1%Z_CwI#$U7n2;JMt6_Uy^BPknXUb-gkQwS-`GejQ~@U;#$zoD zxUvAP(+H1N#jV* z8Cj}&thD!$hk1{&6~hbn#4u7a?o_L*cCE9=X2zlsM|~7DR^}dS@(b9|#8%bgz45+6 zhrkVHMg8mO$i&`!y4lx*R3;V&S#;|Tp$e`3l|~>Qoj8rb8|!V9kWKn1_CFtm5;xrW z{vddvc)Ae8fKV3}+}CjMFXN6h-AWd#z%MClDuy$0RH5-Gx@5CSZnRdZ5T~|2m#(Ij zT^4I7Z`!|y?Ly{cMH7W74ns4H0$WQU;LF-<1MG_flcA~T_hAp3THsZk+~Qi4!GEY; zp5~-Bm3;8cn^L7ml1DS+**KsS53W^*Jp>-P7sX5cA zs_*-^4#7+xvv`9;eN>Hw%@y!)FdiK2B$51%)Q{S)IeHi*a>d@XXzAUDDSPm_7(F(Y z?vwjq-$<6KX&s^_;KA%Ad8HJF0wPw0@~%QA^rYiBImw=MHhJ)fAq!=ssVD~f@>?fx z*@}&)X!pTIsI_;&8h5FS^=8h>VZ%WkC^M<}X|KX|R`2+fWQz`2y|V=gtLc3x<6vR7eI3~uEz8KO!&*WFY;^E8hGu8c zE_*`NOiZyg%dcycp4mM{2_YkWJi3D!Ir`{ZJnAXb60;PPApMVS%{jD3A>gh_W^Y)v z(FrOz^&nO)s@jWZ>mBT<%9Wl`{7!g96_Dt>!pLCF=#4kuo7Q1+W3r|~NM#||OrL`N?WO8unar!nKwWpurzSnmMZowSn%?9u;Vw~AT;`?kF=cW78J6x;kcWMcc zHb-p-hg3|<{>S$m{2bFc%Hlm(ki~-^DCB(jX!7<-YqQtBF}~HUCS~*{U7P%*?5oyY zvuToU;HvPqnSELVjisqX4E?|j>rrBbG*gJaG48c@5$Rd=@qi20Drj2r>|Q=5V^a!# z5m0LO;gSWMO5~`(W`6?8AzM%IQRK_iQF)ACNhAAv2c--ayAB?)@+6}hGqmH_ME!$) za|a!svC4B7GOt~Q?HV2Wiydh40t@A^ohcm$l~+gesPx#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iOM? z6BrX5Mvz2Av zzVE#EM)1cZJLUmeM+ z@a*XW2i4G_qa*RW8Hwjj<<8A(z#!1?$gM&@Eu9o&vg^d)fOGbinoS^|xoQF=DwDh| zt24LIEsjs0JM8!E*IFy9P-pN)W?0?b0QBmU$yXm9Rc76PD&`az$E;FLN0>uOh!dog z&fo769ZmtNR4V1Q*11lnlXK$%Hcw7G9M@W_fl_I&#qP5tvBmThkc^Fu6Reh>OfZkTZ@Z}UsqOE7y`C* zj1a;L!|>;BDez*0F-8CczDGW%Xti2Pz$VZYsXZv!4HTz(bDw?XF?`|P#X_M_d~?4% zZ48WFd3wE8t39*we*haY)x0*a3Uq}KtoOF4*X!>*&r1?x$oU>&7{0cZBVgV3;xFq1 zjsU~JX}5{hYV{Uy+{TLO(W?wW5BLrg?05lax7#1>BigW)&Ho6Tq&6T&ug+c$f}r>* uirxZiR=!RX2>xv%)zo!cS=-a3fxiKcl*Y~(?DT&C0000 c #040404", +", c #060606", +"' c #0E0E0E", +") c #101810", +"! c #163C15", +"~ c #040E04", +"{ c #535653", +"] c #123711", +"^ c #4BEF47", +"/ c #1E5F1C", +"( c #040C04", +"_ c #020602", +": c #010101", +"< c #133C12", +"[ c #4EF94B", +"} c #133D12", +"| c #2E2E2E", +"1 c #030A03", +"2 c #10350F", +"3 c #43D840", +"4 c #0D2A0C", +"5 c #051105", +"6 c #010501", +"7 c #0B260B", +"8 c #44E444", +"9 c #123E12", +"0 c #000000", +"a c #343434", +"b c #48F548", +"c c #061606", +"d c #0C290C", +"e c #45E645", +"f c #051305", +"g c #0A0A0A", +"h c #323232", +"i c #313131", +"j c #4AFC4A", +"k c #091F09", +"l c #0C0C0C", +"m c #2D2D2D", +"n c #071907", +"o c #4AFA4A", +"p c #030D03", +"q c #363636", +"r c #061506", +"s c #4CFE4C", +"t c #051405", +"u c #3FD23F", +"v c #081D08", +"w c #383838", +"x c #103810", +"y c #46EB46", +"z c #144314", +"A c #164715", +"B c #4CFD4C", +"C c #010401", +"D c #0C280C", +"E c #4FFC4B", +"F c #164714", +"G c #4D4D4D", +"H c #113A11", +"I c #154514", +"J c #020402", +"K c #525252", +"L c #464646", +"M c #202020", +"N c #2A2A2A", +"O c #242424", +"P c #282828", +"Q c #272727", +"R c #515151", +" .+@#+$$#%& ", +"*=-;>,'')!~{", +"$]^/(_::<[}|", +"%1234567890a", +"%065bcdef0gh", +"i0065jbk60lh", +"m000noj7p6>q", +"h00rs6tuv56w", +"%0xyr065uz_w", +"%AB7000CDEFw", +"G;H000000IJK", +" LMNOPQQQ|R "}; diff --git a/icon/x2.xpm b/icon/x2.xpm new file mode 100644 index 0000000..73c2f8a --- /dev/null +++ b/icon/x2.xpm @@ -0,0 +1,49 @@ +/* XPM */ +static const char * x2_xpm[] = { +"12 12 34 1", +" c None", +". c #565656", +"+ c #353535", +"@ c #3E3E3E", +"# c #373737", +"$ c #333333", +"% c #303030", +"& c #555555", +"* c #595959", +"= c #030303", +"- c #010101", +"; c #050505", +"> c #060606", +", c #0E0E0E", +"' c #000000", +") c #545454", +"! c #313131", +"~ c #383838", +"{ c #363636", +"] c #0C0C0C", +"^ c #323232", +"/ c #2D2D2D", +"( c #040404", +"_ c #4D4D4D", +": c #020202", +"< c #525252", +"[ c #464646", +"} c #202020", +"| c #2A2A2A", +"1 c #242424", +"2 c #282828", +"3 c #272727", +"4 c #2E2E2E", +"5 c #515151", +" .+@#$%%#+& ", +"*=-=;>,,,>')", +"!---------'~", +"{'''''''''-#", +"+''''''''']^", +"!''''''''']^", +"/'''''''''({", +"^''''''''''~", +"+'''''''''-~", +"+'''''''''-~", +"_:'''''''':<", +" [}|1233345 "}; diff --git a/qps.1 b/qps.1 new file mode 100644 index 0000000..4e87253 --- /dev/null +++ b/qps.1 @@ -0,0 +1,317 @@ +.\" -*-nroff-*- +.TH QPS 1 "Sept 30 1999" +.UC 4 +.SH NAME +qps \- Visual Process Manager +.SH SYNOPSIS +.B qps +[ +.I options +] +.SH DESCRIPTION +.PP +.B qps +is a monitor that displays the status of the processes currently in existence, +much like top(1) or ps(1). +The user interface uses the Qt toolkit, and most operations should be +fairly intuitive. +.PP +The process list is sorted by the highlighted field. Click on another +title to change; click again to reverse the sorting order. Rearrange the +columns by dragging the titles. +.PP +Left-clicking on a process selects or deselects it. Shift-click to select +multiple processes. The PIDs of selected processes can be pasted into other +applications (this option can be disabled). +.PP +The right mouse button pops up a context menu, which duplicates some +functions from the main menu for convenience. It works both on processes and +on the column headings. +.PP +Control-clicking in the process table selects all processes with the same +displayed value in the particular column clicked in. For instance, to select +all processes owned by "joshua", keep Control pressed while clicking on +"joshua". Shift and Control together produces the expected result. +.PP +In Tree mode, the parent-child relations between processes is shown in a +more obvious way. Click on the triangles to show or hide an entire subtree. +Sorting only affects siblings; the tree structure imposes the global order. +.PP +To change the time-sharing priority of the selected processes, type the new +priority in the +.I Renice... +dialog. The new nice value should be in the range -20 to 20; 0 is the +default. A larger number means that the process gets less CPU time. +Only the super-user may decrease the nice value. +.PP +The +.I Change Scheduling... +dialog allows the super-user to change the scheduling policy of the +selected processes (using Posix.1b scheduling control). +Normal processes are set to SCHED_OTHER and have static priority 0; +(soft) real-time processes have the policy SCHED_FIFO or SCHED_RR and +a static priority in the range of 1 to 99. (See +sched_setscheduler(2).) Solaris has additional scheduling policies, +but right now qps doesn't allow setting these. +.PP +By default, the process display updates every 5 seconds. To change, type the +new update period in the +.I Update Period... +dialog. The units min, s and ms may be used (if none, seconds are +assumed). You can force an update by pressing the space bar or +clicking the +.I Update +button. qps will consume a lot of CPU time if the update period is +short or zero. If iconified, however, qps will use very little CPU. +.PP +The USER field shows the real user ID. If the effective user ID of a +process is different from its real user ID, a plus sign (+) is appended to +the user name; if it is the super-user, an asterisk (*) is appended. +.PP +The load, CPU, memory and swap displays in the status bar can be +toggled between graphic and text representations by clicking on them, +or by settings in the +.I Preferences... +dialog. The load numbers shown are the number of jobs in the run queue +averaged over 1, 5 and 15 minutes. +.PP +The swap bar will turn red if free swap space falls below a certain value, +which can be changed in the +.I Preferences... +dialog. The number can be entered in K, M (megabytes) or % (percent of total +swap space). The default is 10%. +.PP +On SMP (multi-CPU) machines running Solaris 2.6 or Linux 2.1.x or +later, the CPU stats will be shown for each processor in vertical +mode, and the average of all CPUs in horizontal mode. +.PP +For displaying the WCHAN field as symbols, the kernel symbol file +System.map is needed. qps will search for it in the following +locations: +.PP +.nf + /boot/System.map-\c +.I RELEASE + /boot/System.map + /lib/modules/\c +.I RELEASE\c +/System.map + /usr/src/linux-\c +.I RELEASE\c +/System.map + /usr/src/linux/System.map + /usr/local/src/linux-\c +.I RELEASE\c +/System.map + /usr/local/src/linux/System.map +.fi +.PP +where +.I RELEASE +is the kernel release number, for instance "2.0.29". If the +System.map file isn't found or unreadable, hexadecimal addresses will be +displayed instead. The prefixes "sys_" and "do_" are stripped from the +symbols before they are displayed. +Under Solaris, symbolic names are currently not supported and hexadecimal +addresses will always be shown. +.PP +The +.I View Details +menu item opens a window that shows different aspects of the selected +processes. Double-clicking on a process has the same effect. All +information is only available to the owner of the process (and to the +super-user). +.PP +The +.I Sockets +table (Linux only) shows the currently used TCP and UDP sockets. If +.I Host Name Lookup +is checked in the +.I Preferences +dialog, a host name lookup will be done for each IP address. This is +done by a background process but can take a while for difficult cases +(but once looked up, addresses are cached). +.PP +The +.I Memory Maps +table shows the process's memory mappings. In Linux 2.0.x and Solaris, +the file names are not given. Anonymous mappings (allocated memory not +bound to a file or device) are marked (anonymous). +.PP +The +.I Files +table shows the process's open files. In Linux 2.0.x, the files +are given on the form [\c +.I AABB\c +]:\c +.I inode, +where +.I AA +and +.I BB +are the device major/minor numbers in hexadecimal. +.PP +The +.I Environment +table shows the process's environment variables. Note that this is the +environment with which the process was started, not necessarily +incorporating later changes. Some processes that modify their command +line, notably sendmail(8) and ftpd(8), may use the environment space +for this, showing nonsense in this table. Clicking on the field +headings changes sorting order as usual. (On Solaris, only the first +8K of the environment are shown. It will be fixed if it turns out to +be a limitation.) +.PP +.I Find Parent +and +.I Find Children +will select the parent/children of the selected processes, and center the +table on the first of them. +.I Find Descendants +will select the tree of all children of the selected processes. +.PP +If +.I Include Child Times +is selected in the +.I Options +menu, the TIME field will show the sum of the CPU times used by the process +and all of its children. +.PP +You can specify commands to be run on the selected processes by bringing +up the +.I Edit Commands... +dialog. The "Description" of each command is what appears in the menu; +the "Command Line" is a shell command (executed with /bin/sh). Before the +command is passed to the shell, the following substitutions are made: +.TP +.B %p +with the PID (Process ID) of the selected process +.TP +.B %c +with the short command name of the process +.TP +.B %C +with the complete command line of the process +.TP +.B %u +with the name of the (real) owner of the process +.TP +.B %% +with a literal '%'. +.PP +Any other % + letter combination is removed. The command line will be run +once for each selected process (in unspecified order). +.PP +.SH KEYBOARD ACCELERATORS +(valid in most contexts) +.TP +.B "Meta-W" +Close the active window (except the main window) +.TP +.B "Q, Meta-Q" +Quit qps. +.TP +.B "Space" +Force an update of the displayed tables. +.TP +.B "Control-Z" +Iconify qps. +.SH OPTIONS +.TP +.RI \-display \ display +sets the X display (default is $DISPLAY) +.TP +.RI \-geometry \ geometry +sets the geometry of the main window of qps +.TP +.RI \-background \ color +sets the default background color and an application palette (light and dark +shades are calculated). This doesn't work very well at the moment. +.TP +.RI \-foreground \ color +sets the default foreground color. This has limited use as well. +.TP +.RI \-title \ title +sets the application title (caption). +.TP +.RI \-style \ style +sets the application GUI style. Possible styles are +.I motif +and +.I windows. +(If you are using Qt 2.x, the styles +.I cde +and +.I platinum +are also available.) +.TP +.RI \-font \ font +sets the application font +.TP +.RI \-iconic +starts the application iconified. +.TP +.RI \-version +prints the version of qps and the Qt library, and exits. +.TP +.RI \-help +prints a summary of command-line options and exits. +.PP +.SH ENVIRONMENT +QPS_COLORS contains color specifications of comma-separated +.I name\c +:\c +.I value +pairs, where +.I name +is one of the following: +.PP +cpu-user, cpu-nice (Linux), cpu-sys, cpu-wait (Solaris), cpu-idle, +mem-used, mem-buff, mem-cache, mem-free, +swap-used, swap-free, swap-warn, +load-bg, load-fg, load-lines, +selection-bg, selection-fg +.PP +.I value +is an X11 color name, either a symbolic name like "salmon" or an RGB color +like #c5b769. +.PP +.SH FILES +.br +.DT +.ta \w'$HOME/.qps-settings\ \ \ 'u +/proc kernel information pseudo-filesystem +.br +$HOME/.qps-settings saved settings between invocations +.br +/etc/services port number to service name mapping (Linux) +.br +System.map kernel symbol map for WCHAN (Linux) +.br +.SH SEE ALSO +ps(1), top(1), kill(1), free(1), renice(8), proc(5), sched_setscheduler(2) +.SH AUTHOR +.PP +Mattias Engdegard (f91-men@nada.kth.se) +.SH LICENSE +qps is free software and may be redistributed under certain +conditions. See the GNU General Public License for details. +.PP +.SH BUGS +qps is too big and too slow. + +The %CPU number isn't accurate at very short update intervals due to +timer granularity. + +The %WCPU field isn't recalculated when qps is iconified, so it might take +a while to readjust when the window is deiconified again. + +The WCHAN field doesn't show a function name if a process sleeps in a +location outside those in System.map (for instance, in a kernel +module), but a hex address instead. The function name can then be +found in /proc/ksyms but has to be found by hand right now. + +The CPU indicator in the status bar will display nonsense in SMP systems +running Linux 2.0.x due to a kernel bug. + +Adding/removing CPUs at runtime will probably confuse qps. diff --git a/qps.desktop.in b/qps.desktop.in new file mode 100644 index 0000000..57c0c92 --- /dev/null +++ b/qps.desktop.in @@ -0,0 +1,13 @@ +[Desktop Entry] +Type=Application +Name=qps +GenericName=Qt process manager +Comment=Qt application to display and manage running processes +Icon=qps +Categories=System; +TryExec=qps +Exec=qps +Terminal=false +StartupNotify=false + +#TRANSLATIONS_DIR=src/translations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..db29ac4 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,91 @@ + +set(SOURCES + proc.cpp + qps.cpp + screenshot.cpp + pstable.cpp + uidstr.cpp + ttystr.cpp + dialogs.cpp + infobar.cpp + fieldsel.cpp + wchan.cpp + prefs.cpp + lookup.cpp + details.cpp + command.cpp + misc.cpp + trayicon.cpp + htable.cpp +# htable2.cpp +# pstable2.cpp + qttableview.cpp +) + +SET(HEADERS + qps.h + screenshot.h + pstable.h + dialogs.h + fieldsel.h + prefs.h + infobar.h + lookup.h + details.h + command.h + misc.h + trayicon.h + htable.h +# htable2.h +# pstable2.h + qttableview.h +) + +set(UIS + watchdog.ui + message.ui +) + +#----------------------------------------------------------------------------- +# Translations +#----------------------------------------------------------------------------- +lxqt_translate_ts(QM_FILES + UPDATE_TRANSLATIONS ${UPDATE_TRANSLATIONS} + SOURCES ${SOURCES} ${HEADERS} ${UIS} + INSTALL_DIR "${QPS_TRANSLATIONS_DIR}/${PROJECT_NAME}" +) + +qt5_translation_loader( + QM_LOADER + "${QPS_TRANSLATIONS_DIR}" + ${PROJECT_NAME} +) + +lxqt_translate_desktop(DESKTOP_FILES + SOURCES "${CMAKE_SOURCE_DIR}/qps.desktop.in" +) +#----------------------------------------------------------------------------- +# End of translations +#----------------------------------------------------------------------------- + + +add_executable(${PROJECT} + ${SOURCES} + ${HEADERS} + ${UIS} + ${QM_FILES} + ${QM_LOADER} + ${DESKTOP_FILES} +) + +target_link_libraries(${PROJECT} + Qt5::Widgets + Qt5::X11Extras + Qt5::DBus +) + +install(TARGETS ${PROJECT} RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) +install(FILES + ${DESKTOP_FILES} + DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/applications" +) diff --git a/src/command.cpp b/src/command.cpp new file mode 100644 index 0000000..1f2b6d2 --- /dev/null +++ b/src/command.cpp @@ -0,0 +1,1197 @@ +// command.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "command.h" +#include "qps.h" +#include "proc.h" +#include "uidstr.h" +#include "dialogs.h" + +extern Qps *qps; +extern ControlBar *controlbar; +extern QList commands; + +int find_command(QString s) +{ + for (int i = 0; i < commands.size(); i++) + if (s == commands[i]->name) + return i; + return -1; +} + +// DEL has "&" end of the string ? +bool hasAmpersand(QString cmdline) +{ + QString str; + int len; + str = cmdline.simplified(); + + if (str == "%update") + return true; // internal command + + len = str.length(); + if (str[len - 1] == '&') + return true; + else + return false; +} + +void check_command(int idx) {} + +// +void check_commandAll() +{ + int i, idx; + + return; + for (int i = 0; i < commands.size(); i++) + { + if (hasAmpersand(commands[i]->cmdline) == false) + commands[i]->cmdline.append("&"); + } +} + +// after read ~/.qpsrc +void add_default_command() +{ + + int idx; + + /* + idx=find_command("Update"); + if (idx>=0) + commands[idx]->cmdline="%update"; + else + commands.add(new Command("Update","%update",true)); + */ + + /* + * PAUSED + idx=find_command("Quit"); + if (idx>=0) + commands[idx]->cmdline="killall qps"; + else commands.add(new Command("Quit","killall qps",false)); + */ + + // check_commandAll(); DEL? +} + +Command::Command(QString n, QString cmd, bool flag) +{ + // printf("Command init\n"); + name = n; + cmdline = cmd; + toolbar = false; + popup = false; + + // toolbutton=new CommandButton(controlbar,name); + // toolbutton->hide(); + + ////toolbutton->setTextLabel (name) ; + ////toolbutton->setUsesTextLabel ( true ); + /// toolbutton->setAutoRaise(true); + // QObject::connect(toolbutton, SIGNAL(clicked()),toolbutton, + // SLOT(exec_command())); +} + +QString watchCond::getVal(QString &str, const char *key) +{ + int n; // key.length(); + int idx = str.indexOf(key); + if (idx < 0) + return "cant' found"; + + idx = str.indexOf("[", idx); + if (idx < 0) + return "[ error"; + int idx_end = str.indexOf("]", idx); + if (idx_end < 0) + return "] error"; + n = idx_end - idx; + return str.mid(idx + 1, n - 1); +} +QString watchCond::getstring() +{ + QString string; + string.clear(); + switch (cond) + { + case WATCH_PROCESS_FINISH: + string.append("if process [" + procname + "] finish, "); + // string=string.sprintf("if process [%s] finish",procname); + break; + case WATCH_PROCESS_START: + string.append("if process [" + procname + "] start, "); + break; + case WATCH_SYS_CPU_OVER: + string.append("if sys_cpu over [" + QString::number(cpu) + "], "); + break; + case WATCH_SYS_CPU_UNDER: + string.append("if sys_cpu under [" + QString::number(cpu) + "], "); + break; + default: + ; + } + if (!command.isEmpty()) + string.append("exec [" + command + "] "); + if (!message.isEmpty()) + string.append("showmsg [" + message + "] "); + if (enable) + string.append("enabled"); + else + string.append("disabled"); + + return string; +} +void watchCond::putstring(QString str) +{ + if (str.contains("if process")) + { + if (str.contains("start")) + cond = WATCH_PROCESS_START; + if (str.contains("finish")) + cond = WATCH_PROCESS_FINISH; + procname = getVal(str, "if process"); + } + if (str.contains("exec")) + command = getVal(str, "exec"); + if (str.contains("showmsg")) + message = getVal(str, "showmsg"); + if (str.contains("enabled")) + enable = true; + else + enable = false; +} +Command::~Command() +{ + // toolbutton->hide(); + // delete toolbutton; +} + +QString Command::getString() +{ + QString str; + str.append(name); + str.append(","); + str.append(cmdline); + return str; +} + +void Command::putString(QString str) +{ + int idx = str.indexOf(","); + // idx=str.indexOf(",",idx); + name = str.mid(0, idx); + cmdline = str.mid(idx + 1, str.size()); +} + +// dirty code... +// Description : this command need "select process" +bool Command::IsNeedProc() +{ + int i, len; + len = cmdline.length(); + + for (i = 0; i < len;) + { + int v = cmdline.indexOf('%', i); + if (v < 0) + break; + if (++v >= len) + break; + + char c = cmdline[v].cell(); //.toLatin1().data(); + switch (c) + { + case 'p': + case 'c': + case 'C': + case 'u': + if (cmdline.indexOf("update", v) == v) + { + v += 5; + break; + } + // printf("true\n"); + return true; + default: + ; + } + i = v + 1; + } + return false; +} + +QString substString(QString str, Procinfo *p) +{ + QString s; + int len = str.length(); + for (int i = 0;;) + { + int v = str.indexOf('%', i); + if (v < 0) + { + s.append(str.right(len - i)); + break; + } + else + { + s.append(str.mid(i, v - i)); + if (++v >= len) + break; + QString subst; + // need change to LOCALE(by fasthyun@magicn.com) + char c = str[v].cell(); + switch (c) + { + case 'p': + subst.setNum(p->pid); + break; + case 'c': + subst = p->command; + break; + case 'C': + subst = p->cmdline; + break; + case 'u': + subst = Uidstr::userName(p->uid); + break; + case '%': + subst = "%"; + break; + } + s.append(subst); + i = v + 1; + } + } + return s; +} + +// execute command +void Command::call(Procinfo *p) +{ + QString s; + QString msg; + + printf("called !\n"); + int len = cmdline.length(); + + if (p == NULL) + { + if (cmdline == "%update") + { + qps->refresh(); + return; + } + + s = cmdline; + } + else + s = substString(cmdline, p); + + int ret = system(s.toLatin1().data()); /// + /* + pr=new QProcess; // leak? + if(!wc->command.isEmpty()) //conflict pid's command + { + pr->start(wc->command); // thread run, if + null then + segfault occurs. ? + } + + connect(pr, SIGNAL(started()), this, SLOT(cmd_started())); + connect(pr, SIGNAL(finished ( int , QProcess::ExitStatus + )),this,SLOT(cmd_finished ( int , QProcess::ExitStatus ))); + connect(pr, SIGNAL(error ( QProcess::ProcessError )),this, + SLOT(cmd_error(QProcess::ProcessError))); + */ + + if (ret) + { + msg = "The command:\n\n"; + msg.append(s); + if (ret == -1) + { + msg.append("\n\nfailed with the error:\n\n"); + const char *e = + (errno == EAGAIN) ? "Too many processes" : strerror(errno); + msg.append(e ? e : "Unknown error"); + } + else if (ret & 0xff) + { + msg.append("\n\nwas terminated by signal "); + msg.append(QString().setNum(ret & 0xff)); + msg.append("."); + } + else if (ret == 0x7f00) + { + msg.append("\n\ncould not be executed because it was not " + "found,\nor you did not have execute permission."); + } + else + { + msg.append("\n\nexited with status "); + msg.append(QString().setNum(ret >> 8)); + msg.append("."); + } + QMessageBox::warning(0, "Command Failed", msg); + } +} + +CommandDialog::CommandDialog() +{ + setWindowTitle("Edit Commands 0.1 alpha"); + // setWindowFlags(Qt::WindowStaysOnTopHint); + + QHBoxLayout *hbox = new QHBoxLayout(this); // TOP + CommandModel *cmdModel = new CommandModel(this); + // item list + listview = new QListView(this); + listview->setModel(cmdModel); + listview->setFixedWidth(fontMetrics().width("0") * 16); + hbox->addWidget(listview); + + QVBoxLayout *vbox = new QVBoxLayout; // TOP-> RIGHT + hbox->addLayout(vbox); + + QHBoxLayout *h1 = new QHBoxLayout; + vbox->addLayout(h1); + QLabel *l1 = new QLabel("Name:", this); + h1->addWidget(l1); + name = new QLineEdit(this); + name->setMinimumWidth(170); + name->setText(""); + h1->addWidget(name); + + QHBoxLayout *hbox2 = new QHBoxLayout; + vbox->addLayout(hbox2); + // qcheck1 = new QCheckBox (this); + // qcheck1->setText("Toolbar"); + // qcheck1->setEnabled(false); + // hbox2->addWidget(qcheck1); + if (0) + { + qcheck2 = new QCheckBox(this); + qcheck2->setText("Popup"); + qcheck2->setEnabled(false); + hbox2->addWidget(qcheck2); + } + + QLabel *l2 = new QLabel("Command Line:", this); + l2->setFixedHeight(l2->sizeHint().height()); + l2->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); + vbox->addWidget(l2); + + cmdline = new QLineEdit(this); + cmdline->setFixedHeight(cmdline->sizeHint().height()); + cmdline->setMinimumWidth(250); + cmdline->setText(""); + vbox->addWidget(cmdline); + + QLabel *l3 = new QLabel("Substitutions:\n" + "%p\tPID\n" + "%c\tCOMMAND\n%C\tCMDLINE\n%u\tUSER\n" + "%%\t%\n" + "\n", + this); + + l3->setFrameStyle(QFrame::Panel); + l3->setFrameShadow(QFrame::Sunken); + l3->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); // | Qt::ExpandTabs); + vbox->addWidget(l3); + + QHBoxLayout *hl = new QHBoxLayout; + vbox->addLayout(hl); + new0 = new QPushButton("New...", this); + hl->addWidget(new0); + add = new QPushButton("Add...", this); + hl->addWidget(add); + del = new QPushButton("Delete", this); + hl->addWidget(del); + button_ok = new QPushButton("Close", this); + hl->addWidget(button_ok); + + connect(listview, SIGNAL(clicked(const QModelIndex &)), + SLOT(set_select(const QModelIndex &))); + connect(new0, SIGNAL(clicked()), SLOT(new_cmd())); + connect(add, SIGNAL(clicked()), SLOT(add_new())); + connect(del, SIGNAL(clicked()), SLOT(del_current())); + connect(button_ok, SIGNAL(clicked()), SLOT(close())); + connect(name, SIGNAL(textChanged(const QString &)), + SLOT(event_name_midified(const QString &))); + connect(cmdline, SIGNAL(textChanged(const QString &)), + SLOT(event_cmd_modified())); + // connect(qcheck1, SIGNAL(toggled ( bool ) ), + // SLOT(event_toolbar_checked(bool + // ))); + + TBloon *bloon = new TBloon(this); + /// for(int i = 0; i < commands.size(); i++) + /// listview->insertItem(commands[i]->name); + // listview->addItem(commands[i]->name); + /// vbox->freeze(); +} + +// DEL +void CommandDialog::event_toolbar_checked(bool on) +{ + // name->text(); + int idx = find_command(name->text()); + if (idx >= 0) + commands[idx]->toolbar = on; + + /// controlbar->update_bar(); +} + +void CommandDialog::event_name_midified(const QString &new_name) +{ + int idx; + FUNC_START; + // printf("debug:changed_description() start \n"); + idx = find_command(new_name); + if (idx == -1) + { + add->setEnabled(1); + } + else + add->setEnabled(0); + + // printf("debug:changed_description() end \n"); +} + +// if modified then call this function +void CommandDialog::event_cmd_modified() +{ + int idx; + // if(name->text()=="") return; + if (find_command(name->text()) < 0) + return; + + idx = find_command(name->text()); + + commands[idx]->name = name->text(); + commands[idx]->cmdline = cmdline->text(); + emit command_change(); +} + +// set the description,cmdline from current selected QListBox item +void CommandDialog::set_buttons(int index) +{ + if (index < 0) + { + new_cmd(); + return; + } + /* + //bool sel = (lb->currentRow() >= 0); + Command *c ; + if(sel) + //c = commands[find_command(lb->currentText())]; + c = commands[find_command(lb->currentText())]; + else + c = commands[find_command(lb->text(index))]; + name->setText(c->name); + cmdline->setText(c->cmdline); + del->setEnabled(sel); + */ +} + +// called when clicked ! +void CommandDialog::set_select(const QModelIndex &index) +{ + Command *c = + static_cast(index.internalPointer()); // never Null ? + /* + if (item==NULL) return; // important + Command *c = commands[find_command(item->text())]; + */ + name->setText(c->name); + cmdline->setText(c->cmdline); + // DEL qcheck1->setChecked(c->toolbar); + // qcheck2->setChecked(c->popup); + + // bool sel = (listview->currentItem() >= 0); + if (c->name == "Update") + del->setEnabled(false); + else + del->setEnabled(true); +} + +void CommandDialog::reset() +{ + listview->reset(); + name->setText(""); + cmdline->setText(""); + add->setText("Add..."); + add->setEnabled(0); + button_ok->setEnabled(1); + listview->clearSelection(); +} + +void CommandDialog::new_cmd() +{ + reset(); + add->setEnabled(1); + name->setFocus(); +} + +void CommandDialog::add_new() +{ + if (name->text() == "") + return; + + // commands.add(new Command(name->text(), + // cmdline->text(),qcheck1->isChecked + // () )); + commands.append(new Command(name->text(), cmdline->text(), false)); + check_commandAll(); // TEMP + + listview->reset(); + add->setEnabled(0); + del->setEnabled(0); + button_ok->setEnabled(1); + + emit command_change(); // notice to refresh Qps::make_command_menu() + // control_bar->update_bar(); // ** important +} + +void CommandDialog::del_current() +{ + int idx = find_command(name->text()); + if (idx >= 0) + { + // printf("del\n"); + commands.removeAt(idx); + listview->reset(); // listview->reset(); + // control_bar->update_bar(); + emit command_change(); // notice to refresh menu_commands + } +} + +// CommandModel +CommandModel::CommandModel(QObject *parent) {} +CommandModel::~CommandModel() {} + +QModelIndex CommandModel::index(int row, int column, + const QModelIndex &parent) const +{ + if (row >= 0 and column >= 0 and row < commands.size() and column < 1) + { + Command *cmd = commands[row]; + return createIndex(row, column, cmd); + } + else + return QModelIndex(); +} +QModelIndex CommandModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} +int CommandModel::rowCount(const QModelIndex &parent) const +{ + return commands.size(); +} +// int CommandModel::columnCount(const QModelIndex &parent) const{return 1;}; +QVariant CommandModel::data(const QModelIndex &index, int role) const +{ + if (role == Qt::DisplayRole) + { + Command *cmd = static_cast(index.internalPointer()); + return cmd->name; + } + if (role == Qt::DecorationRole) + { + } + return QVariant(); +} +void CommandModel::update() {} // TEMP + +//---------------------------------------------------------------- +QList watchlist; +QList execlist; + +extern WatchdogDialog *watchdogDialog; + +// except threads, already running process +void watchdog_check_if_start(QString cmd, Procinfo *pi) +{ + /// printf("cmd=%s\n", cmd.toLatin1().data()); + for (int i = 0; i < watchlist.size(); ++i) + { + watchCond *wc = watchlist.at(i); + if (wc->enable == false) + continue; + if (wc->cond == WATCH_PROCESS_START) + if (wc->procname == cmd) + { + // printf("Watchdog: start\n"); + if (!pi->isThread()) + // ExecWindow *mw=new + // ExecWindow(wc->message,wc->command,pi->pid,pi->command); + // // leak + ExecWindow *mw = + new ExecWindow(wc, pi->pid, pi->command); // leak + + // note : + // 1.system("./loop"); //block !! + // 2.pr.setEnvironment(QProcess::systemEnvironment + //()); + } + } +} + +void watchdog_check_if_finish(QString cmd, Procinfo *pi) +{ + for (int i = 0; i < watchlist.size(); ++i) + { + watchCond *w = watchlist.at(i); + if (w->enable == false) + continue; + if (w->cond == WATCH_PROCESS_FINISH) + { + if (w->procname == cmd) + { + // printf("Watchdog: finish\n"); + if (!pi->isThread()) + // if(pi->pid==pi->tgid) // not a thread + // ! + ExecWindow *mw = + new ExecWindow(w, pi->pid, pi->command); // leak + // ExecWindow *mw=new + // ExecWindow(w->message,w->command,pi->pid,pi->command); + } + } + } +} + +// NOTYET +void watchdog_syscpu(int cpu) +{ + // printf("Watchdog: watchdog_syscpu\n"); + // if(watchdogDialog->flag_ifsyscpu) + { + // if(cpu> watchdogDialog->syscpu_over) + // printf("Watchdog: event %d%\n",cpu); + // QMessageBox::warning(0, "Watchdog", "aaaaa"); //blocking + } +} + +// ExecWindow +ExecWindow::ExecWindow() +{ + setupUi(this); + // connect(okButton, SIGNAL(clicked()), this, SLOT(close())); + // show(); +} + +ExecWindow::~ExecWindow() {} + +// eventcat_id; +ExecWindow::ExecWindow(watchCond *wc, int pid, QString cmd) +{ + setupUi(this); + setWindowTitle("Qps Watchdog"); + + wcond = wc; + + QString str; + + if (wc->cond == WATCH_PROCESS_START) + { + textEdit->append(cmd + "(" + QString::number(pid) + ")" + " start"); + } + if (wc->cond == WATCH_PROCESS_FINISH) + textEdit->append(cmd + "(" + QString::number(pid) + ")" + " finished"); + + flag_started = false; + + pr = new QProcess; // leak? + if (!wc->command.isEmpty()) // conflict pid's command + { + pr->start(wc->command); // thread run, if null then segfault occurs. ? + } + + connect(okButton, SIGNAL(clicked()), this, SLOT(cmd_ok())); + + connect(pr, SIGNAL(started()), this, SLOT(cmd_started())); + connect(pr, SIGNAL(finished(int, QProcess::ExitStatus)), this, + SLOT(cmd_finished(int, QProcess::ExitStatus))); + connect(pr, SIGNAL(error(QProcess::ProcessError)), this, + SLOT(cmd_error(QProcess::ProcessError))); + + show(); + + execlist.append(this); +} + +ExecWindow::ExecWindow(QString str, QString exec_cmd, int pid, QString cmd) +{ + setupUi(this); + // +} + +// QProcess: Destroyed while process is still running.(Segmentation fault) +void ExecWindow::cmd_ok() +{ + if (pr->state() == QProcess::Running) + { + // pr->kill(); + pr->terminate(); + return; + } + close(); // Qt::WA_DeleteOnClose +} + +// slot : catch terminate signal. +void ExecWindow::cmd_finished(int exitCode, QProcess::ExitStatus exitStatus) +{ + textEdit->append(wcond->command + " exit with code " + + QString::number(exitStatus)); + okButton->setText("Close"); + delete pr; +} + +void ExecWindow::cmd_started() +{ + textEdit->append(wcond->command + " [running]"); + okButton->setText("terminate command"); + flag_started = true; +} + +void ExecWindow::cmd_error(QProcess::ProcessError e) +{ + // not found command + // Error ? : + if (e == QProcess::FailedToStart) + // textEdit->append("Error : command not found [" + + // command + "]" + "(code + //" + QString::number(e) + ")" ); + textEdit->append("Error " + QString::number(e) + " : [" + + wcond->command + "] Maybe command not found"); + delete pr; +} + +void ExecWindow::setText(QString str) +{ + textEdit->append(str); + // label->setText(str); +} + +WatchdogDialog::WatchdogDialog() +{ + setupUi(this); + listmodel = new ListModel(); + + tableView->setModel(listmodel); + checkBoxDelegate delegate; + tableView->setEditTriggers(QAbstractItemView::SelectedClicked); + /// tableView->setItemDelegate(&delegate); + + tableView->setSelectionBehavior(QAbstractItemView::SelectRows); + tableView->setSelectionMode(QAbstractItemView::SingleSelection); + QHeaderView *h = tableView->verticalHeader(); + h->setVisible(false); + + QHeaderView *v = tableView->horizontalHeader(); +#if QT_VERSION >= 0x050000 + v->setSectionResizeMode(0, QHeaderView::Stretch); + v->setSectionResizeMode(1, QHeaderView::ResizeToContents); +#endif + // v->setClickable (false); + connect(newButton, SIGNAL(clicked()), this, SLOT(_new())); + connect(closeButton, SIGNAL(clicked()), this, SLOT(apply())); + connect(addButton, SIGNAL(clicked()), this, SLOT(add())); + connect(delButton, SIGNAL(clicked()), this, SLOT(del())); + connect(comboBox, SIGNAL(activated(int)), SLOT(comboChanged(int))); + connect(comboBox, SIGNAL(highlighted(const QString &)), + SLOT(condChanged(const QString &))); + + connect(tableView, SIGNAL(clicked(const QModelIndex &)), + SLOT(eventcat_slected(const QModelIndex &))); + connect(message, SIGNAL(textEdited(const QString &)), + SLOT(Changed(const QString &))); + connect(command, SIGNAL(textEdited(const QString &)), + SLOT(Changed(const QString &))); + connect(proc_name, SIGNAL(textEdited(const QString &)), + SLOT(Changed(const QString &))); + connect(comboBox, SIGNAL(activated(const QString &)), + SLOT(Changed(const QString &))); + + checkBox_alreadyrun->hide(); + listView->hide(); + spinBox->hide(); + label_cpu->hide(); + /// printf("close ...\n"); + // tableView->update(); + // listmodel->update(); // meaningless.. + + TBloon *bloon = new TBloon(this); + return; +} + +void WatchdogDialog::showEvent(QShowEvent *event) +{ + // Qt 4.4.0 bug? + // printf("show!!!!!!!!!\n"); + listmodel->update(); +} +void WatchdogDialog::comboChanged(int idx) +{ + + // itemText(idx); + QString str = comboBox->currentText(); + + if (str.contains("cpu")) + { + label_cpu->show(); + spinBox->show(); + } + else + { + spinBox->hide(); + label_cpu->hide(); + } + + if (str.contains("process")) + { + label_procname->show(); + proc_name->show(); + } + else + { + label_procname->hide(); + proc_name->hide(); + } + + if (message->text().isEmpty()) + { + // if(str.contains("start")) message->setText("%CMD start + // with pid %PID"); + // if(str.contains("finish")) message->setText("%CMD + // finish with pid + //%PID"); + } +} + +void WatchdogDialog::eventcat_slected(const QModelIndex &idx) +{ + + watchCond *w = watchlist[idx.row()]; + // printf("row=%d\n",at=idx.row()); + + if (idx.column() == 1) + { + w->enable = !(w->enable); + listmodel->update(idx.row()); + return; + } + + QString str = idx.data().toString(); // Qt::DisplayRol + + if (str.contains("process")) + proc_name->setText(w->procname); + else + proc_name->setText(""); + if (str.contains("cpu")) + spinBox->setSingleStep(w->cpu); + else + spinBox->setSingleStep(50); + if (str.contains("exec")) + command->setText(w->command); + else + command->setText(""); + if (str.contains("showmsg")) + message->setText(w->message); + else + message->setText(""); + + checkCombo(); + comboBox->setCurrentIndex(w->cond); +} + +void WatchdogDialog::Changed(const QString &str) +{ + QModelIndex idx = tableView->currentIndex(); + // QModelIndexList list=tableView->selectedIndexes (); + bool flag = tableView->selectionModel()->hasSelection(); + // if(list.count() and idx.isValid()) + if (flag and idx.isValid()) + { + int at = idx.row(); + watchCond *w = watchlist[at]; + w->message = message->text(); + w->command = command->text(); + w->procname = proc_name->text(); + w->cond = comboBox->currentIndex(); + listmodel->update(at); + // watchlist.removeAt(at); + } + // listmodel->update(); +} + +void WatchdogDialog::checkCombo() +{ + if (comboBox->count() == 1) + { + comboBox->clear(); + comboBox->addItem("if process start"); + comboBox->addItem("if process finish"); + return; + } +} + +// comboChanged() -> checkCombo() +void WatchdogDialog::condChanged(const QString &str) +{ + checkCombo(); + // what is this? + // printf("chagend\n"); + // comboBox->currentText(); + // command->text(); + // message->text(); +} + +void WatchdogDialog::_new() +{ + tableView->clearSelection(); + proc_name->clear(); + command->clear(); + message->clear(); + comboBox->clear(); + comboBox->addItem("select condition"); +} + +void WatchdogDialog::add() +{ + watchCond *w = new watchCond; + w->enable = true; + w->cond = comboBox->currentIndex(); + w->command = command->text(); + w->message = message->text(); + w->procname = proc_name->text(); + watchlist.append(w); + // listView->update(QModelIndex()); + // listView->reset(); + // tableView->reset(); + // listmodel->insertRow(listmodel->rowCount(QModelIndex())); + // tableView->update(QModelIndex()); + // tableView->dataChanged(QModelIndex(),QModelIndex()); //protected + listmodel->update(); +} + +void WatchdogDialog::del() +{ + // QModelIndex idx=listView->currentIndex(); + QModelIndex idx = tableView->currentIndex(); + if (idx.isValid()) + { + int at = idx.row(); + watchlist.removeAt(at); + } + listmodel->update(); + tableView->setCurrentIndex(idx); +} + +void WatchdogDialog::apply() +{ + qps->write_settings(); + close(); +} + +QModelIndex ListModel::index(int row, int column, + const QModelIndex &parent) const +{ + // printf("index %d %d\n",row,column); + if (row >= 0 and column >= 0 and row < watchlist.size()) + { + if (column < 2) + { + watchCond *item = watchlist[row]; + return createIndex(row, column, item); + } + } + return QModelIndex(); +} + +void ListModel::update(int row) +{ + emit dataChanged(index(row, 0), index(row, 1)); +} + +void ListModel::update(const QModelIndex &idx) { emit dataChanged(idx, idx); } + +// Pure Virtual +QModelIndex ListModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); // no parent! +} +int ListModel::rowCount(const QModelIndex &parent) const +{ + return watchlist.size(); +} +QVariant ListModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + // printf("headerData\n"); + if (role == Qt::DisplayRole) + { + if (section == 0) + return QString("Event Category"); + if (section == 1) + return QString("Enable"); + } + /* + if (role == Qt::FontRole) + { + QFont f=QFont(); + f.setBold(false); + return f; + } */ + if (role == Qt::SizeHintRole) + { + // return QSize(18,18); + } + return QVariant(); +} + +QVariant ListModel::data(const QModelIndex &index, int role) const +{ + // printf("data\n"); + watchCond *item = static_cast(index.internalPointer()); + if (index.column() == 0) + { + if (role == Qt::DisplayRole) + { + return QString(item->getstring()); + } + if (role == Qt::DecorationRole) + { + } + if (role == Qt::EditRole) + { + } + } + + if (index.column() == 1) + { + if (role == Qt::CheckStateRole) + { + if (item->enable) + return Qt::Checked; + else + return Qt::Unchecked; + } + if (role == Qt::TextAlignmentRole) + return Qt::AlignRight; + if (role == Qt::EditRole) + { + } + } + if (role == Qt::SizeHintRole) + { + // return QSize(18,18); + } + return QVariant(); +} +// void CommandModel::update(){} //TEMP + +#include +checkBoxDelegate::checkBoxDelegate(QObject *parent) : QItemDelegate(parent) {} +//! [0] + +//! [1] +QWidget *checkBoxDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + printf("createEditor\n"); + // return 0; + if (index.column() == 1) + { + QSpinBox *editor = new QSpinBox(parent); + editor->setMinimum(0); + editor->setMaximum(100); + return editor; + } + return QItemDelegate::createEditor(parent, option, index); + return 0; +} +//! [1] + +//! [2] +void checkBoxDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + printf("setEditorData\n"); + return; + // if(index + if (index.column() == 1) + { + int value = index.model()->data(index, Qt::EditRole).toInt(); + // int value=0; + QSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value); + } +} +//! [2] + +//! [3] +void checkBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + printf("setModelData\n"); + return; + if (index.column() == 1) + { + QSpinBox *spinBox = static_cast(editor); + spinBox->interpretText(); + int value = spinBox->value(); + model->setData(index, value, Qt::EditRole); + } +} +//! [3] + +//! [4] +void checkBoxDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + printf("updateEditorGeometry\n"); + // if(index.column()==1) + editor->setGeometry(option.rect); +} +//! [4] + +void checkBoxDelegate::paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + printf("paint\n"); + if (index.column() == 1) + { + QVariant value = 0; // index.model()->data(index, Qt::UserRole); + /* if (!isSupportedType(value.type())) { + QStyleOptionViewItem myOption = option; + myOption.state &= ~QStyle::State_Enabled; + QItemDelegate::paint(painter, myOption, index); + return; + } */ + } + QItemDelegate::paint(painter, option, index); +} +// QSize QAbstractItemDelegate::sizeHint ( const QStyleOptionViewItem & option, +// const QModelIndex & index ) const [pure virtual] diff --git a/src/command.h b/src/command.h new file mode 100644 index 0000000..ef603d7 --- /dev/null +++ b/src/command.h @@ -0,0 +1,226 @@ +// command.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef COMMAND_H +#define COMMAND_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#include +#endif + +#include "proc.h" +#include "misc.h" + +int find_command(QString s); +void add_default_command(); + +class Command +{ + public: + Command(){}; + Command(QString n, QString cmd, bool toolbar = false); + ~Command(); + void call(Procinfo *p); + bool IsNeedProc(); + QString getString(); + void putString(QString str); + + // CommandButton *toolbutton; + + QString name; // appears in the menu + QString cmdline; // may contain variables (%p etc) + int menu; // index in menu + bool toolbar; + bool popup; +}; + +class CommandModel : public QAbstractItemModel +{ + Q_OBJECT + public: + CommandModel(QObject *parent = 0); + ~CommandModel(); + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const { return 1; }; + QVariant data(const QModelIndex &index, int role) const; + // Qt::ItemFlags flags(const QModelIndex &index) const; + void update(); // TEMP +}; + +// class CommandDialog : public QWidget +class CommandDialog : public QDialog +{ + Q_OBJECT + public: + CommandDialog(); + +signals: + void command_change(); + + protected slots: + void new_cmd(); + void add_new(); + void del_current(); + void set_buttons(int); + void reset(); + void set_select(const QModelIndex &); + void event_name_midified(const QString &new_name); + void event_cmd_modified(); + void event_toolbar_checked(bool); + + private: + QListView *listview; + QPushButton *new0, *add, *del, *edit, *button_ok; + QLineEdit *name, *cmdline; + QCheckBox *qcheck1; + QCheckBox *qcheck2; +}; + +#include +class checkBoxDelegate : public QItemDelegate +// class checkBoxDelegate : public QAbstractItemDelegate +{ + Q_OBJECT + public: + checkBoxDelegate(QObject *parent = 0); + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const; + void updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; +}; + +#include +// class ListModel : public QAbstractItemModel +class ListModel : public QAbstractTableModel +// class ListModel : public QStandardItemModel +{ + Q_OBJECT + public: + ListModel(QObject *parent = 0){}; + ~ListModel(){}; + QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; // pure + QModelIndex parent(const QModelIndex &child) const; // pure virtual + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const { return 2; }; + QVariant data(const QModelIndex &index, int role) const; + QVariant headerData(int section, Qt::Orientation o, int role) const; + // QMap itemData ( const QModelIndex & index ) const + //; + void update() + { // reset(); + } + void update(const QModelIndex &idx); + void update(int row); + // Qt::ItemFlags flags(const QModelIndex &index) const; + // void update(); //TEMP +}; + +#include "ui_watchdog.h" +class WatchdogDialog : public QDialog, private Ui_EventDialog +{ + Q_OBJECT + public: + WatchdogDialog(); + ListModel *listmodel; + // signals: + // void command_change(); + void checkCombo(); + protected slots: + void _new(); + void apply(); + void add(); + void del(); + void condChanged(const QString &str); + void Changed(const QString &str); + + void comboChanged(int); + void eventcat_slected(const QModelIndex &idx); + + protected: + virtual void showEvent(QShowEvent *event); + + // void set_select( const QModelIndex & ); + // void event_name_midified(const QString &new_name); + // void event_cmd_modified(); + // void event_toolbar_checked(bool); + private: +}; + +#define WATCH_PROCESS_START 0 +#define WATCH_PROCESS_FINISH 1 +#define WATCH_PROCESS_CPU_OVER 2 +#define WATCH_SYS_CPU_OVER 3 +#define WATCH_SYS_CPU_UNDER 4 + +// if process [name] start, exec [command], showmsg [xxx] +// if process [name] finish exec [command], showmsg [xxx] +// if system cpu over [90%], exec [command], msg [xxx] +// if system cpu under [10%], exec [command], msg [xxx] +// if process [name] cpu over [90%] exec [command] msg [xxx] +// if process [name] start, kill_it, msg [xxx] +// if process [name] start, soundplay [ ], msg [xxx] + +class watchCond +{ + public: + int cond; + int enable; + int cpu; + QString procname; + QString command; + QString message; + + watchCond() + { + enable = 0; + // procname[0]=0; command[0]=0; + // message[0]=0; + } + // key [txt] [a] + // QString getVal(QString &str, QString &key) + QString getVal(QString &str, const char *key); + QString getstring(); + void putstring(QString str); +}; + +#include +#include "ui_message.h" +class ExecWindow : public QWidget, private Ui_ExecWindow +{ + Q_OBJECT + public: + ExecWindow(); + ExecWindow(QString str, QString exec_cmd, int pid = 0, QString cmd = ""); + ExecWindow(watchCond *wc, int pid = 0, QString cmd = ""); + ~ExecWindow(); + void setText(QString str); + QProcess *pr; + // QProcess proc; + QString execmd; + int flag_started; + watchCond *wcond; + protected slots: + void cmd_started(); + void cmd_finished(int exitCode, QProcess::ExitStatus exitStatus); + void cmd_error(QProcess::ProcessError error); + void cmd_ok(); +}; + +#endif // COMMAND_H diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..86c569e --- /dev/null +++ b/src/config.h @@ -0,0 +1,20 @@ +// config.h -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef CONFIG_H +#define CONFIG_H + +#if defined(__linux__) +#define LINUX + +#elif defined(sun) && defined(__SVR4) +#define SOLARIS + +#else +#error Only Linux >= 2.0 and Solaris >= 2.6 supported, sorry + +#endif + +#endif // CONFIG_H diff --git a/src/ctrlbar.cpp b/src/ctrlbar.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/ctrlbar.h b/src/ctrlbar.h new file mode 100644 index 0000000..e69de29 diff --git a/src/details.cpp b/src/details.cpp new file mode 100644 index 0000000..f9c4cb8 --- /dev/null +++ b/src/details.cpp @@ -0,0 +1,727 @@ +// details.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include +#include +#include + +#include "details.h" +//#include "proc.h" // static flag +#include "qps.h" // static flag +Details::Details(Procinfo *p, Proc *proc) : QWidget(0) +{ + QString cap; + pi = p; + pi->detail = this; + pr = proc; + // printf("pi=%x\n",pi); + cap.sprintf("Process %d ( %s ) - details", pi->pid, + qPrintable(pi->command)); + setWindowTitle(cap); + + tbar = new QTabWidget(this); + // tbar->setMargin(5); + + // if(pi->fd_files) //if(pi->fd_files->size()) + if (pi->read_fds()) + { + tbar->addTab(new Files(this), "&Files"); + +// if(pi->read_fds()) // create sock_inodes +#ifdef LINUX + // printf(" pi->socks.size=%d\n", p->sock_inodes->size() ); + // this means if a process dont have socket then + // no socket pane show in Detail dialog. + // Procinfo::read_sockets(); + if (pi->sock_inodes.size() != 0) + tbar->addTab(new Sockets(this), "&Sockets"); +#endif + } + + if (pi->read_maps()) + tbar->addTab(new Maps(this), "&Memory Maps"); + if (pi->read_environ()) + tbar->addTab(new Environ(this), "&Environment"); + tbar->addTab(new AllFields(this), "&All Fields"); + + tbar->adjustSize(); + QSize s0 = tbar->sizeHint(); + if (s0.width() > 800) + s0.setWidth(800); + resize(s0); +} + +Details::~Details() +{ + // printf("~Details()\n"); + if (pi) + pi->detail = 0; + int i; + /// for(i=0;icount();i++) delete tbar->page(i); + delete tbar; +} + +void Details::set_procinfo(Procinfo *p) +{ + // printf("p=%x, pi=%x\n",p,pi); +} +void Details::refresh() +{ + printf("Details::refresh()\n"); + QWidget *w = NULL; // tbar->currentPage (); + // QWidget *w= tbar->currentPage (); + if (w != NULL) + { + /// printf("refresh()\n"); + ((SimpleTable *)w)->refresh(); + } +} + +void Details::process_gone() +{ + /// printf("process_gone() *******************************\n"); + pi = 0; + // for now, we just close the window. Another possibility would be + // to leave it with a "process terminated" note. + close(); +} + +// slot: react to changes in preferences +void Details::config_change() { return; } + +void Details::resizeEvent(QResizeEvent *s) +{ + tbar->resize(s->size()); //** + QWidget::resizeEvent(s); +} + +// user closed the window (via window manager) +void Details::closeEvent(QCloseEvent *) { emit closed(this); } + +SimpleTable::SimpleTable(QWidget *parent, int nfields, TableField *f, + int options) + : HeadedTable(parent, options), fields(f) +{ + detail = (Details *)parent; + setNumCols(nfields); +} + +QSize SimpleTable::sizeHint() const +{ + /// return QSize(tableWidth(), 300); + return QSize(480, 300); +} + +QString SimpleTable::title(int col) { return fields[col].name; } + +int SimpleTable::colWidth(int col) { return fields[col].width; } + +inline int SimpleTable::alignment(int col) { return fields[col].align; } + +int SimpleTable::leftGap(int col) { return fields[col].gap; } + +QString SimpleTable::tipText(int col) { return fields[col].tooltip; } + +#ifdef LINUX + +// declaration of static members +bool Sockets::have_services = false; +QHash Sockets::servdict; +Lookup *Sockets::lookup = 0; + +TableField Sockets::fields[] = { + {"Fd", 5, 8, Qt::AlignRight, "File descriptor"}, + {"Proto", 4, 8, Qt::AlignLeft, "Protocol (TCP or UDP)"}, + {"Recv-Q", 9, 8, Qt::AlignRight, "Bytes in receive queue"}, + {"Send-Q", 9, 8, Qt::AlignRight, "Bytes in send queue"}, + {"Local Addr", -1, 8, Qt::AlignLeft, "Local IP address"}, + {"Port", 6, 8, Qt::AlignLeft, "Local port"}, + {"Remote Addr", -1, 8, Qt::AlignLeft, "Remote IP address"}, + {"Port", 6, 8, Qt::AlignLeft, "Remote port"}, + {"State", 18, 8, Qt::AlignLeft, "Connection state"}}; + +Sockets::Sockets(QWidget *parent) : SimpleTable(parent, SOCKFIELDS, fields) +{ + if (!lookup) + lookup = new Lookup(); + connect(lookup, SIGNAL(resolved(unsigned)), + SLOT(update_hostname(unsigned))); + doing_lookup = Qps::hostname_lookup; + refresh(); + // compute total width = window width + int totw = 400; + // for(int i = 0; i < SOCKFIELDS; i++) + // totw += actualColWidth(i); + resize(totw, 200); +} + +Sockets::~Sockets() +{ + // why seg? + // if(lookup) delete lookup; +} + +static const char *tcp_states[] = { + "ESTABLISHED", // 1 + "SYN_SENT", "SYN_RECV", "FIN_WAIT1", "FIN_WAIT2", "TIME_WAIT", + "CLOSE", "CLOSE_WAIT", "LAST_ACK", "LISTEN", + "CLOSING" // 11 +}; + +static inline const char *tcp_state_name(int st) +{ + return (st < 1 || st > 11) ? "UNKNOWN" : tcp_states[st - 1]; +} + +QString Sockets::text(int row, int col) +{ + Procinfo *p = procinfo(); + if (p->sock_inodes.size() == 0) + refresh_sockets(); + SockInode *sock_ino = p->sock_inodes[row]; + // SockInode *sock_ino = p->sock_inodes[row]; + Sockinfo *si = p->proc->socks.value(sock_ino->inode, NULL); + if (!si) + return ""; // process gone, return empty string + + QString s; + switch (col) + { + case FD: + s.setNum(sock_ino->fd); + break; + + case PROTO: + s = (si->proto == Sockinfo::TCP) ? "tcp" : "udp"; + break; + + case RECVQ: + s.setNum(si->rx_queue); + break; + + case SENDQ: + s.setNum(si->tx_queue); + break; + + case LOCALADDR: + s = ipAddr(si->local_addr); + break; + + case LOCALPORT: + if (Qps::service_lookup) + { + const char *serv = servname(si->local_port); + if (serv) + { + s = serv; + break; + } + } + s.setNum(si->local_port); + break; + + case REMOTEADDR: + s = ipAddr(si->rem_addr); + break; + + case REMOTEPORT: + if (Qps::service_lookup) + { + const char *serv = servname(si->rem_port); + if (serv) + { + s = serv; + break; + } + } + s.setNum(si->rem_port); + break; + + case STATE: + s = tcp_state_name(si->st); + break; + } + return s; +} + +QString Sockets::ipAddr(unsigned addr) +{ + unsigned a = htonl(addr); + QString s; + if (doing_lookup) + { + s = lookup->hostname(addr); + if (s.isNull()) + { + s.sprintf("(%d.%d.%d.%d)", (a >> 24) & 0xff, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff); + } + } + else + { + if (a == 0) + s = "*"; + else + s.sprintf("%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff); + } + return s; +} + +void Sockets::refresh_window() +{ + Procinfo *p = procinfo(); + if (!p) + return; + int rows = p->sock_inodes.size(); + /// resetWidths(); + setNumRows(rows); + setNumCols(SOCKFIELDS); + /// repaint_changed(); + repaintAll(); +} + +void Sockets::refresh() +{ + if (refresh_sockets()) + refresh_window(); +} + +// return true if sockets could be read successfully, false otherwise +bool Sockets::refresh_sockets() +{ + // Procinfo::read_sockets(); + return procinfo()->read_fds(); +} + +// react to changes in preferences +void Sockets::config_change() +{ + if (doing_lookup != Qps::hostname_lookup) + { + doing_lookup = Qps::hostname_lookup; + ////setNumCols(SOCKFIELDS); + // for(int col = 0; col < SOCKFIELDS; col++) + // widthChanged(col); + ////updateTableSize(); + ////repaintAll(); + } +} + +// slot: called when a host name has been looked up +void Sockets::update_hostname(unsigned addr) +{ + // if(widthChanged(REMOTEADDR) || widthChanged(LOCALADDR)) { + if (1) + { + //// updateTableSize(); + /// repaintAll(); + } + else + { + // just repaint some rows + Procinfo *p = procinfo(); + if (!p->sock_inodes.size()) + { + /// Procinfo::read_sockets(); + p->read_fds(); + } + int rows = p->sock_inodes.size(); + for (int i = 0; i < rows; i++) + { + int inode = p->sock_inodes[i]->inode; + /// Sockinfo *si = Procinfo::socks[inode]; + /// if(si->local_addr == addr) updateCell(i, LOCALADDR); + /// if(si->rem_addr == addr) updateCell(i, + /// REMOTEADDR); + } + } +} + +const char *Sockets::servname(unsigned port) +{ + if (!have_services) + { + have_services = true; + // fill servdict from /etc/services (just try once) + setservent(1); + struct servent *s; + while ((s = getservent()) != 0) + { + unsigned short hport = ntohs((unsigned short)s->s_port); + if (!servdict.value(hport, NULL)) + { + servdict.insert(hport, strdup(s->s_name)); + } + } + endservent(); + } + return servdict.value(port, NULL); +} + +#endif // LINUX + +#ifdef SOLARIS + +// Stupid code to make up for moc:s inability to grok preprocessor conditionals +void Sockets::refresh() {} +QString Sockets::text(int, int) { return 0; } +void Sockets::config_change() {} +Sockets::~Sockets() {} +void Sockets::update_hostname(unsigned int) {} + +#endif + +TableField Maps::fields[] = { + {"Address Range", -1, 8, Qt::AlignLeft, "Mapped addresses (hex)"}, + {"Size", 8, 8, Qt::AlignRight, "Kbytes mapped (dec)"}, + {"Perm", 5, 8, Qt::AlignLeft, "Permission flags"}, + {"Offset", -1, 8, Qt::AlignRight, "File offset at start of mapping (hex)"}, + {"Device", 8, 8, Qt::AlignLeft, "Major,Minor device numbers (dec)"}, + {"Inode", 10, 8, Qt::AlignRight, "Inode number (dec)"}, + {"File", -9, 8, Qt::AlignLeft, "File name (if available)"}}; + +// memory leak +Maps::Maps(QWidget *parent) : SimpleTable(parent, MAPSFIELDS, fields) +{ + // monospaced font looks best in the table body since it contains + // hex numerals and flag fields. Pick Courier (why not) + body->setFont(QFont("Courier", font().pointSize())); + bool mono = true; + QFont f = font(); + if (f.rawMode()) + { + /* see if the font is monospaced enough for our needs */ + QFontMetrics fm(f); + int zw = fm.width('0'); + const char *test = "abcdef"; + for (const char *p = test; *p; p++) + if (fm.width(*p) != zw) + { + mono = false; + break; + } + } + else + mono = f.fixedPitch(); + if (mono) + { + ////setBodyFont(f); + } + else + { + int ps = f.pointSize(); + /// setBodyFont(QFont("Courier", ps ? ps : 10)); + } + + refresh(); + // compute total width = window width + int totw = 300; + // for(int i = 0; i < MAPSFIELDS; i++) totw += actualColWidth(i); + resize(totw + 20, 200); +} + +Maps::~Maps() +{ + // if(maps) + // maps->clear(); +} + +QString Maps::text(int row, int col) +{ + Procinfo *p = procinfo(); + if (p->maps.size() == 0) + { + refresh_maps(); + if (p->maps.size() == 0) + return ""; + } + Mapsinfo *mi = p->maps[row]; + + QString s; + char buf[80]; + switch (col) + { + case ADDRESS: + sprintf(buf, (sizeof(void *) == 4) ? "%08lx-%08lx" : "%016lx-%016lx", + mi->from, mi->to); + s = buf; + break; + case SIZE: + s.setNum((mi->to - mi->from) >> 10); + s += "kb"; + break; + case PERM: + s = " "; + for (int i = 0; i < 4; i++) + s[i] = mi->perm[i]; + break; + case OFFSET: + sprintf(buf, (sizeof(void *) == 4) ? "%08lx" : "%016lx", mi->offset); + s = buf; + break; + case DEVICE: + s.sprintf("%2u,%2u", mi->major, mi->minor); + break; + case INODE: + s.setNum(mi->inode); + break; + case FILENAME: + s = mi->filename; + if (!Qps::show_file_path) + { + int i = s.lastIndexOf('/'); + if (i >= 0) + s.remove(0, i + 1); + } + break; + } + return s; +} + +void Maps::refresh_window() +{ + if (!procinfo()) + return; + int rows = procinfo()->maps.size(); + ////resetWidths(); + setNumRows(rows); + setNumCols(MAPSFIELDS); + repaintAll(); +} + +void Maps::refresh() +{ + if (refresh_maps()) + refresh_window(); +} + +bool Maps::refresh_maps() { return procinfo()->read_maps(); } + +TableField Files::fields[] = { + {"Fd", 5, 8, Qt::AlignRight, "File descriptor"}, +#ifdef LINUX + {"Mode", 3, 8, Qt::AlignLeft, "Open mode"}, +#endif + {"Name", -1, 8, Qt::AlignLeft, "File name (if available)"}}; + +Files::Files(QWidget *parent) : SimpleTable(parent, FILEFIELDS, fields) +{ + // compute total width = window width + refresh_window(); +} + +Files::~Files() {} + +void Files::refresh() +{ + printf("Files::refresh()\n"); + // return true if fds could be read successfully, false otherwise + if (procinfo()->read_fds()) + refresh_window(); +} + +bool Files::refresh_fds() { return false; } + +void Files::refresh_window() +{ + Procinfo *p = procinfo(); + if (!p) + return; + + // if(p->fd_files==NULL) printf("qps :dddds\n"); + + int rows = p->fd_files.size(); + + resetWidths(); + // printf("size=%d\n",rows); + setNumRows(rows); + setNumCols(FILEFIELDS); + repaintAll(); // useless ? +} + +QString Files::text(int row, int col) +{ + Procinfo *p = procinfo(); // alot!! + if (p == 0) + return "zero"; + // printf("p=%x px=%x\n",p,px); + if (p->fd_files.size() == 0) + { //???????????/////// + refresh_fds(); + if (p->fd_files.size() == 0) + return ""; + } + if (row >= p->fd_files.size()) + return ""; + + Fileinfo *fi = p->fd_files[row]; + QString s; + switch (col) + { + case FILEDESC: + s.setNum(fi->fd); + break; + +#ifdef LINUX + case FILEMODE: + if (fi->mode & OPEN_READ) + s.append("R"); + if (fi->mode & OPEN_WRITE) + s.append("W"); + break; +#endif + + case FILENAME: + s = fi->filename; + break; + } + return s; +} + +TableField Environ::fields[] = { + {"Variable", -1, 8, Qt::AlignLeft, "Variable name"}, + {"Value", -1, 8, Qt::AlignLeft, "Variable value"}}; + +Environ *Environ::static_env = 0; +Environ::Environ(QWidget *parent) + : SimpleTable(parent, ENVFIELDS, fields), rev(false) +{ + connect(this, SIGNAL(titleClicked(int)), SLOT(sort_change(int))); + refresh(); +} + +Environ::~Environ() {} + +QString Environ::text(int row, int col) +{ + Procinfo *p = procinfo(); // if dead process then + if (row >= p->environ.size()) + printf("size dddd=row=%d\n", row); + NameValue nv = p->environ[row]; // Segfault !! + return (col == ENVNAME) ? nv.name : nv.value; +} + +void Environ::refresh_window() +{ + if (!procinfo()) + return; + /// resetWidths(); + int rows = procinfo()->environ.size(); + setNumRows(rows); + setNumCols(ENVFIELDS); + repaintAll(); +} + +void Environ::refresh() +{ + if (procinfo()->read_environ()) + { + // sort(); + refresh_window(); + } +} + +void Environ::sort_change(int col) +{ + Procinfo *p = procinfo(); + /* + if(!p->environ) { + refresh_environ(); + if(!p->environ) + return; + }*/ + ////rev = (col == sortedCol()) ? !rev : false; + setSortedCol(col); + sort(); + refresh_window(); +} + +// sort table according to current settings +void Environ::sort() +{ + /////if(sortedCol() >= 0) + { + static_env = this; + // if(procinfo()->environ==NULL) + // printf("qps : Environ::sort() error ???\n"); + // else + // procinfo()->environ.sort(compare); + } +} + +int Environ::compare(const NameValue *a, const NameValue *b) +{ + Environ *e = Environ::static_env; + int r; + /* + if(e->sortedCol() == ENVNAME) + r = strcmp(a->name, b->name); + else + r = strcmp(a->value, b->value); SimpleTable(QWidget *parent, + int + nfields, TableField *f, int options = 0); + */ + return e->rev ? -r : r; +} + +TableField AllFields::fields[] = { + {"Field", -1, 8, Qt::AlignLeft, "Field name"}, + {"Description", -1, 8, Qt::AlignLeft, "Field description"}, + {"Value", -1, 8, Qt::AlignLeft, "Field value"}}; + +AllFields::AllFields(QWidget *parent) + : SimpleTable(parent, FIELDSFIELDS, fields) +{ + refresh(); + // compute total width = window width + int totw = 0; + // for(int i = 0; i < FIELDSFIELDS; i++) totw += actualColWidth(i); + resize(totw + 20, 200); +} + +AllFields::~AllFields() {} + +// Proc not changed ? +QString AllFields::text(int row, int col) +{ + QString s; + // printf("text start r=%d , c=%d\n",row,col); + // if( ((Details *)parent())->proc()==NULL) + // printf("size=%d\n", ((Details *)parent())->proc()->allcats.size() ); + // printf("size=%d\n", proc()->allcats.size() ); + + Category *cat = proc()->categories.values()[row]; + // Category *cat = proc()->allcats[row]; + switch (col) + { + case FIELDNAME: + s = cat->name; + break; + case FIELDDESC: + s = cat->help; + break; + case FIELDVALUE: + s = cat->string(procinfo()); + break; + } + // printf("text end\n"); + return s; +} + +// parent will be tbar(TabWidget) !!! +void AllFields::refresh_window() +{ + // printf("refresh_window\n"); + if (!procinfo()) + return; + setNumRows(proc()->categories.size()); + setNumCols(FIELDSFIELDS); + // DEL resetWidths(); + // repaint_changed(); + repaintAll(); +} + +void AllFields::refresh() { refresh_window(); } diff --git a/src/details.h b/src/details.h new file mode 100644 index 0000000..ed4eab6 --- /dev/null +++ b/src/details.h @@ -0,0 +1,236 @@ +// details.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef DETAILS_H +#define DETAILS_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#include +#endif + +#include "proc.h" +#include "lookup.h" + +class Details : public QWidget +{ + Q_OBJECT + public: + Details(Procinfo *p, Proc *proc); + ~Details(); + + void refresh(); + void config_change(); + void process_gone(); + Procinfo *get_procinfo() { return pi; } + Proc *proc() { return pr; } + void set_procinfo(Procinfo *p); + +signals: + void closed(Details *); + + protected: + virtual void resizeEvent(QResizeEvent *); + virtual void closeEvent(QCloseEvent *); + + private: + QTabWidget *tbar; + Procinfo *pi; + Proc *pr; +}; + +struct TableField +{ + const char *name; + int width; + int gap; + int align; + const char *tooltip; +}; + +// SimpleTable: a HeadedTable with fixed number of columns +#include "htable.h" +class SimpleTable : public HeadedTable +{ + Q_OBJECT + public: + SimpleTable(QWidget *parent, int nfields, TableField *f, int options = 0); + QSize sizeHint() const; + virtual void refresh(){}; + + protected: + virtual QString title(int col); + virtual QString text(int row, int col) = 0; + virtual int colWidth(int col); + virtual int alignment(int col); + virtual int leftGap(int col); + virtual QString tipText(int col); + // Procinfo *procinfo() { return ((Details + // *)parentWidget())->procinfo(); } + Procinfo *procinfo() { return detail->get_procinfo(); } + Proc *proc() { return detail->proc(); } + + private: + const TableField *fields; + Details *detail; +}; + +class Sockets : public SimpleTable +{ + Q_OBJECT + public: + Sockets(QWidget *parent); + ~Sockets(); + + void refresh(); + void refresh_window(); + bool refresh_sockets(); + const char *servname(unsigned port); + QString ipAddr(unsigned addr); + QString hostname(unsigned addr); + void config_change(); + + public slots: + void update_hostname(unsigned addr); + + protected: + virtual QString text(int row, int col); + + private: + enum + { + FD, + PROTO, + RECVQ, + SENDQ, + LOCALADDR, + LOCALPORT, + REMOTEADDR, + REMOTEPORT, + STATE, + SOCKFIELDS + }; + static TableField fields[SOCKFIELDS]; + + bool doing_lookup; // if table painted with host lookup + + static Lookup *lookup; + static bool have_services; // true if we have tried reading services + static QHash servdict; +}; + +class Maps : public SimpleTable +{ + public: + Maps(QWidget *parent); + ~Maps(); + + void refresh(); + void refresh_window(); + bool refresh_maps(); + + protected: + virtual QString text(int row, int col); + + private: + enum + { + ADDRESS, + SIZE, + PERM, + OFFSET, + DEVICE, + INODE, + FILENAME, + MAPSFIELDS + }; + static TableField fields[MAPSFIELDS]; +}; + +class Files : public SimpleTable +{ + public: + Files(QWidget *parent); + ~Files(); + + void refresh(); + void refresh_window(); + bool refresh_fds(); + + protected: + virtual QString text(int row, int col); + + private: + enum + { + FILEDESC, +#ifdef LINUX + FILEMODE, +#endif + FILENAME, + FILEFIELDS + }; + static TableField fields[FILEFIELDS]; +}; + +class Environ : public SimpleTable +{ + Q_OBJECT + public: + Environ(QWidget *parent); + ~Environ(); + + void refresh(); + void refresh_window(); + + public slots: + void sort_change(int); + + protected: + virtual QString text(int row, int col); + + void sort(); + static int compare(const NameValue *a, const NameValue *b); + + private: + enum + { + ENVNAME, + ENVVALUE, + ENVFIELDS + }; + bool rev; // sorting reversed + static Environ *static_env; // for sorting, must have static pointer + static TableField fields[ENVFIELDS]; +}; + +class AllFields : public SimpleTable +{ + public: + AllFields(QWidget *parent); + ~AllFields(); + + void refresh(); + void refresh_window(); + + protected: + virtual QString text(int row, int col); + + private: + enum + { + FIELDNAME, + FIELDDESC, + FIELDVALUE, + FIELDSFIELDS + }; + static TableField fields[FIELDSFIELDS]; +}; + +#endif // DETAILS_H diff --git a/src/dialogs.cpp b/src/dialogs.cpp new file mode 100644 index 0000000..522b560 --- /dev/null +++ b/src/dialogs.cpp @@ -0,0 +1,397 @@ +// dialogs.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include "qps.h" +#include "dialogs.h" +#include + +extern Qps *qps; + +// center window a with respect to main application window +static void center_window(QWidget *a) +{ + QWidget *b = QApplication::activeWindow(); + // QWidget *b = qApp->mainWidget(); + a->move(b->x() + (b->width() - a->width()) / 2, + b->y() + (b->height() - a->height()) / 2); +} + +static void move_mouse_window(QWidget *a) +{ + // QWidget *b = qApp->mainWidget(); + QWidget *b = QApplication::activeWindow(); + a->move(b->x() + (b->width() - a->width()) / 2, + b->y() + (b->height() - a->height()) / 2); +} + +static void fix_size(QWidget *w) { w->setFixedSize(w->sizeHint()); } + +// Modal dialog +IntervalDialog::IntervalDialog(const char *ed_txt, bool enabled) : QDialog() +{ + setWindowTitle("Change Update Period"); + QVBoxLayout *tl = new QVBoxLayout; + QHBoxLayout *h1 = new QHBoxLayout; + setLayout(tl); + tl->addLayout(h1); + + QLabel *label1 = new QLabel("New Update Period", this); + h1->addWidget(label1); + h1->addStretch(1); + + lined = new QLineEdit(this); + QFont f = font(); + lined->setMaxLength(7); + lined->setText(ed_txt); + if (enabled) + { + lined->selectAll(); + lined->setFocus(); + } + // lined->setEnabled(enabled); + lined->setEnabled(true); + fix_size(lined); + lined->setFixedWidth(64); + h1->addWidget(lined); + + label = new QLabel(this); + label->setFrameStyle(QFrame::Panel); + label->setFrameShadow(QFrame::Sunken); + label->setText(""); + label->setAlignment(Qt::AlignRight); + tl->addWidget(label); + + /* + toggle = new CrossBox("Dynamic Update (under devel)", this); + fix_size(toggle); + toggle->setChecked(enabled); + //toggle->setChecked(false); + toggle->setChecked(true); + toggle->setEnabled(false); + tl->addWidget(toggle); + */ + QHBoxLayout *h2 = new QHBoxLayout; + // h2->addStretch(1); + cancel = new QPushButton("Cancel", this); + h2->addWidget(cancel); + ok = new QPushButton("OK", this); + // ok->setFocus(); + h2->addWidget(ok); + tl->addLayout(h2); + + connect(ok, SIGNAL(clicked()), SLOT(done_dialog())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + connect(lined, SIGNAL(returnPressed()), SLOT(done_dialog())); + connect(lined, SIGNAL(textChanged(const QString &)), + SLOT(event_label_changed())); + // connect(toggle, SIGNAL(toggled(bool)), lined, + // SLOT(setEnabled(bool))); + + ok->setDefault(true); + + /// Q3Accel *acc = new Q3Accel(this); + /// acc->connectItem(acc->insertItem(Qt::Key_Escape), this, + /// SLOT(reject())); + // tl->setSizeConstraint(QLayout::SetFixedSize); + // center_window(this); +} + +void IntervalDialog::event_label_changed() +{ + QString txt; + int i = 0; + ed_result = lined->text(); + ed_result = ed_result.simplified(); + + // if(toggle->isChecked()) + + QString s = ed_result; + if (s.length() == 0) + { + label->setText("No UPDATE"); + return; + } + + while ((s[i] >= '0' && s[i] <= '9') || s[i] == '.') + i++; + + float period = (i > 0) ? s.left(i).toFloat() : -1; + + s = s.mid(i, 3).simplified(); + if (s.length() == 0 || s == "s") + period *= 1000; + else if (s == "min") + period *= 60000; + else if (s != "ms") + period = -1; + if (period <= 0) + { + label->setText("Invalid value"); + return; + } + + txt.sprintf("%d ms", (int)period); + label->setText(txt); +} + +void IntervalDialog::done_dialog() +{ + int i = 0; + + ed_result = lined->text(); + ed_result = ed_result.simplified(); + + // if(toggle->isChecked()) + QString s = ed_result; + while ((s[i] >= '0' && s[i] <= '9') || s[i] == '.') + i++; + + float period = (i > 0) ? s.left(i).toFloat() : -1; + + s = s.mid(i, 3).simplified(); + if (s.length() == 0 || s == "s") + period *= 1000; + else if (s == "min") + period *= 60000; + else if (s != "ms") + period = -1; + if (period < 0) + return; + + qps->set_update_period((int)period); + qps->update_timer(); + + accept(); +} + +SliderDialog::SliderDialog(int defaultval, int minval, int maxval) : QDialog() +{ + setWindowTitle("Renice Process"); + QVBoxLayout *tl = new QVBoxLayout; + QHBoxLayout *h1 = new QHBoxLayout; + setLayout(tl); + tl->addLayout(h1); + + label = new QLabel("New nice value:", this); + h1->addWidget(label); + + h1->addStretch(1); + + lined = new QLineEdit(this); + lined->setMaxLength(3); + lined->setText(QString::number(defaultval)); + lined->setFocus(); + lined->setFixedWidth(64); + h1->addWidget(lined); + + slider = new QSlider(Qt::Horizontal, this); + slider->setMaximum(maxval); + slider->setMinimum(minval); + slider->setTickInterval(10); + slider->setTickPosition(QSlider::TicksBelow); + slider->setValue(defaultval); + tl->addWidget(slider); + + QHBoxLayout *h2 = new QHBoxLayout; + tl->addLayout(h2); + + // decorate slider + QLabel *left = new QLabel(this); + QLabel *mid = new QLabel(this); + QLabel *right = new QLabel(this); + left->setNum(minval); + mid->setNum((minval + maxval) / 2); + right->setNum(maxval); + h2->addWidget(left); + h2->addStretch(1); + h2->addWidget(mid); + h2->addStretch(1); + h2->addWidget(right); + + QHBoxLayout *h3 = new QHBoxLayout; + tl->addLayout(h3); + + h3->addStretch(1); + cancel = new QPushButton("Cancel", this); + // fix_size(cancel); + h3->addWidget(cancel); + + ok = new QPushButton("OK", this); + ok->setFixedSize(cancel->sizeHint()); + h3->addWidget(ok); + + connect(ok, SIGNAL(clicked()), SLOT(done_dialog())); + ok->setDefault(true); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + connect(lined, SIGNAL(returnPressed()), SLOT(done_dialog())); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(slider_change(int))); + // Q3Accel *acc = new Q3Accel(this); + // acc->connectItem(acc->insertItem(Qt::Key_Escape), this, + // SLOT(reject())); + // tl->freeze(); + + center_window(this); +} + +void SliderDialog::done_dialog() +{ + ed_result = lined->text(); + ed_result = ed_result.simplified(); + accept(); +} + +void SliderDialog::slider_change(int val) +{ + QString s; + s.setNum(val); + lined->setText(s); + lined->selectAll(); +} + +// DRAFT CODE, +PermissionDialog::PermissionDialog(QString msg, QString passwd) : QDialog() +{ + setWindowTitle("Permission"); + QVBoxLayout *vbox = new QVBoxLayout; + label = new QLabel(msg, this); + vbox->addWidget(label); + + setLayout(vbox); + + QHBoxLayout *hbox = new QHBoxLayout; + vbox->addLayout(hbox); + label = new QLabel("root password", this); + hbox->addWidget(label); + lined = new QLineEdit(this); + hbox->addWidget(lined); + + hbox = new QHBoxLayout; + vbox->addLayout(hbox); + QPushButton *cancel = new QPushButton("Cancel", this); + hbox->addWidget(cancel); + + QPushButton *ok = new QPushButton("OK", this); + hbox->addWidget(ok); + + connect(ok, SIGNAL(clicked()), SLOT(accept())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); +} + +SchedDialog::SchedDialog(int policy, int prio) : QDialog() +{ + setWindowTitle("Change scheduling"); + QVBoxLayout *vl = new QVBoxLayout; + setLayout(vl); + + bgrp = new QGroupBox("Scheduling Policy", this); + vl->addWidget(bgrp); // bgrp->setCheckable(1); + rb_other = new QRadioButton("SCHED_OTHER (time-sharing)", bgrp); + rb_fifo = new QRadioButton("SCHED_FIFO (real-time)", bgrp); + rb_rr = new QRadioButton("SCHED_RR (real-time)", bgrp); + + QVBoxLayout *vbox = new QVBoxLayout; + vbox->addWidget(rb_other); + vbox->addWidget(rb_fifo); + vbox->addWidget(rb_rr); + bgrp->setLayout(vbox); + + connect(rb_other, SIGNAL(clicked(bool)), SLOT(button_clicked(bool))); + connect(rb_fifo, SIGNAL(clicked(bool)), SLOT(button_clicked(bool))); + connect(rb_rr, SIGNAL(clicked(bool)), SLOT(button_clicked(bool))); + + QHBoxLayout *hbox1 = new QHBoxLayout; + QPushButton *ok, *cancel; + ok = new QPushButton("OK", this); + ok->setDefault(true); + cancel = new QPushButton("Cancel", this); + hbox1->addWidget(ok); + hbox1->addWidget(cancel); + vl->addLayout(hbox1); + + connect(ok, SIGNAL(clicked()), SLOT(done_dialog())); + connect(cancel, SIGNAL(clicked()), SLOT(reject())); + + int active = 0; + QRadioButton *rb; + switch (policy) + { + case SCHED_OTHER: + active = 0; + rb = rb_other; + break; + case SCHED_FIFO: + active = 1; + rb = rb_fifo; + break; + case SCHED_RR: + active = 2; + rb = rb_rr; + break; + } + rb->setChecked(true); + out_policy = policy; + out_prio = prio; + + QHBoxLayout *hbox = new QHBoxLayout; + lbl = new QLabel("Priority (1-99):", this); + lined = new QLineEdit(this); + hbox->addWidget(lbl); + hbox->addWidget(lined); + vbox->addLayout(hbox); + QFont f = font(); + f.setBold(false); + lined->setFont(f); + lined->resize(60, lined->sizeHint().height()); + lined->setMaxLength(4); + QString s; + s.setNum(prio); + lined->setText(s); + button_clicked(active); + + // make sure return and escape work as accelerators + connect(lined, SIGNAL(returnPressed()), SLOT(done_dialog())); +} + +void SchedDialog::done_dialog() +{ + if (rb_rr->isChecked()) + out_policy = SCHED_RR; + else if (rb_fifo->isChecked()) + out_policy = SCHED_FIFO; + else + out_policy = SCHED_OTHER; + QString s(lined->text()); + bool ok; + out_prio = s.toInt(&ok); + if (out_policy != SCHED_OTHER && (!ok || out_prio < 1 || out_prio > 99)) + { + QMessageBox::warning(this, "Invalid Input", + "The priority must be in the range 1..99"); + } + else + accept(); +} + +void SchedDialog::button_clicked(bool val) +{ + // printf("SchedDialog::checked()\n"); + if (rb_other->isChecked()) + { + lbl->setEnabled(false); + lined->setEnabled(false); + } + else + { + QString s(lined->text()); + bool ok; + int n = s.toInt(&ok); + if (ok && n == 0) + lined->setText("1"); + lbl->setEnabled(true); + lined->setEnabled(true); + lined->setFocus(); + lined->selectAll(); + } +} diff --git a/src/dialogs.h b/src/dialogs.h new file mode 100644 index 0000000..d70296a --- /dev/null +++ b/src/dialogs.h @@ -0,0 +1,105 @@ +// dialogs.h emacs, this is a -*-c++-*- file +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +// misc. handy dialogs for use everywhere + +#ifndef DIALOGS_H +#define DIALOGS_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "misc.h" + +class IntervalDialog : public QDialog +{ + Q_OBJECT + public: + IntervalDialog(const char *ed_txt, bool toggle_state); + + protected slots: + void done_dialog(); + void event_label_changed(); + + public: + QString ed_result; + CrossBox *toggle; + + protected: + QPushButton *ok, *cancel; + QLabel *label; + QLineEdit *lined; +}; + +class SliderDialog : public QDialog +{ + Q_OBJECT + public: + SliderDialog(int defaultval, int minval, int maxval); + + QString ed_result; + + protected slots: + void slider_change(int val); + void done_dialog(); + + protected: + QPushButton *ok, *cancel; + QLabel *label; + QLineEdit *lined; + QSlider *slider; +}; + +class PermissionDialog : public QDialog +{ + Q_OBJECT + public: + PermissionDialog(QString msg, QString passwd); + QLineEdit *lined; + QLabel *label; + + // protected slots: + // void slider_change(int val); + // void done_dialog(); + /* + protected: + QPushButton *ok, *cancel; + QLabel *label; + QSlider *slider; */ +}; + +class SchedDialog : public QDialog +{ + Q_OBJECT + public: + SchedDialog(int policy, int prio); + + int out_prio; + int out_policy; + + protected slots: + void done_dialog(); + void button_clicked(bool); + + private: + QGroupBox *bgrp; + QRadioButton *rb_other, *rb_fifo, *rb_rr; + QLabel *lbl; + QLineEdit *lined; +}; +#endif // DIALOGS_H diff --git a/src/fieldsel.cpp b/src/fieldsel.cpp new file mode 100644 index 0000000..4b179b1 --- /dev/null +++ b/src/fieldsel.cpp @@ -0,0 +1,107 @@ +// indent by VIM +// fieldsel.cpp +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include "fieldsel.h" + +FieldSelect::FieldSelect(Procview *pv) +{ + nbuttons = pv->categories.size(); + disp_fields.resize(64); + procview = pv; + + int half = (nbuttons + 1) / 2; + updating = false; + + setWindowTitle("Select Custom Fields "); + QBoxLayout *v_layout = new QVBoxLayout; + setLayout(v_layout); + + QGridLayout *grid = new QGridLayout; + grid->setSpacing(0); + grid->setColumnMinimumWidth(2, 17); + v_layout->addLayout(grid); + + buts = new QCheckBox *[nbuttons]; + + QList keys = pv->categories.keys(); + + for (int i = 0; i < nbuttons; i++) + { + Category *cat = pv->categories[keys.takeFirst()]; // fieldlist + + QCheckBox *but = new QCheckBox(cat->name, this); + QLabel *desc = new QLabel(cat->help, this); + if (i < half) + { + grid->addWidget(but, i, 0); + grid->addWidget(desc, i, 1); + } + else + { + grid->addWidget(but, i - half, 3); + grid->addWidget(desc, i - half, 4); + } + buts[i] = but; + connect(but, SIGNAL(toggled(bool)), this, SLOT(field_toggled(bool))); + } + update_boxes(); + + QPushButton *closebut = new QPushButton("Close", this); + connect(closebut, SIGNAL(clicked()), SLOT(closed())); + closebut->setFocus(); + + v_layout->addWidget(closebut); + // v_layout->freeze(); ///!!! +} + +// CALLBACK : one of the fields was toggled (we don't know which one yet) +void FieldSelect::field_toggled(bool) +{ + if (updating) + return; + set_disp_fields(); + + for (int i = 0; i < nbuttons; i++) + { + Category *cat = procview->cat_by_name(buts[i]->text().toLatin1()); + + if (buts[i]->isChecked() != disp_fields.testBit(cat->id)) + { + if (buts[i]->isChecked()) + emit added_field(cat->id); // send cat_index + else + emit removed_field(cat->id); + } + } +} + +void FieldSelect::closed() { hide(); } + +void FieldSelect::closeEvent(QCloseEvent *) { closed(); } + +void FieldSelect::set_disp_fields() +{ + disp_fields.fill(false); + int n = procview->cats.size(); + for (int i = 0; i < n; i++) + disp_fields.setBit(procview->cats[i]->id); +} + +void FieldSelect::showEvent(QShowEvent *) { update_boxes(); } + +// init check button ? +void FieldSelect::update_boxes() +{ + set_disp_fields(); + + updating = true; + for (int i = 0; i < nbuttons; i++) + { + Category *cat = procview->cat_by_name(buts[i]->text().toLatin1()); + buts[i]->setChecked(disp_fields.testBit(cat->id)); + } + updating = false; +} diff --git a/src/fieldsel.h b/src/fieldsel.h new file mode 100644 index 0000000..e9dc952 --- /dev/null +++ b/src/fieldsel.h @@ -0,0 +1,49 @@ +// fieldsel.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef FIELDSEL_H +#define FIELDSEL_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "proc.h" + +class FieldSelect : public QDialog +{ + Q_OBJECT + public: + FieldSelect(Procview *pv); + + void update_boxes(); + + public slots: + void field_toggled(bool); + void closed(); + +signals: + void added_field(int); + void removed_field(int); + + protected: + QCheckBox **buts; + int nbuttons; + QBitArray disp_fields; + bool updating; + Procview *procview; + + void set_disp_fields(); + void closeEvent(QCloseEvent *); + virtual void showEvent(QShowEvent *); +}; + +#endif // FIELDSEL_H diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..8c09fa0 --- /dev/null +++ b/src/global.h @@ -0,0 +1,19 @@ +#ifndef GLOBAL_H +#define GLOBAL_H + +#include "qps.h" +#include "misc.h" + +extern QList commands; +extern ControlBar *controlbar; +extern int default_font_height; +extern bool flag_show_thread; +extern int flag_thread_ok; +extern bool previous_flag_show_thread; +extern int num_opened_files; + +extern Qps *qps; +extern SearchBox *search_box; +extern TFrame *infobox; + +#endif // GLOBAL_H diff --git a/src/htable.cpp b/src/htable.cpp new file mode 100644 index 0000000..67cea83 --- /dev/null +++ b/src/htable.cpp @@ -0,0 +1,1269 @@ +// very dirty and complex code ( fasthyun@magicn.com) +// Disaster !! + +// htable.cpp +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +// TODO: +// * autoscroll speed proportional to distance from edge +// * interface to add/remove rows (for disclosure triangles) +// * interface to display pixmaps in cells (for disclosure triangles etc) +// * include sorting functionality here for more generality + +#include "htable.h" +#include +#include +#include +#include + +#define DEBUG qDebug +//#define DEBUG +VPointer *vp = NULL; // Temporary + +VPointer::VPointer(QWidget *parent) : QWidget(parent) +{ + pix = new QPixmap(":/icon/vpointer.png"); + pix_vcross = new QPixmap(":/icon/vcross.png"); + int w = pix->width(); + bool flag_movable = true; + setGeometry(0, 0, w, w); +} + +void VPointer::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + p.setClipping(false); // not work + if (flag_movable) + p.drawPixmap(0, 0, *pix); + else + p.drawPixmap(0, 0, *pix_vcross); +} + +FloatingHead::FloatingHead(QWidget *parent) : QWidget(parent) {} + +void FloatingHead::setTitle(QString str, int w, int h) +{ + title = str; + resize(w, h); +} +void FloatingHead::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + QStyleOptionHeader opt; + p.setOpacity(0.7); + opt.rect = rect(); + opt.text = title; + opt.state = QStyle::State_Enabled; + // if(htable->sortedCol()==col) opt.state= opt.state | + // QStyle::State_Sunken; + // CE_Header, CE_HeaderSection, CE_HeaderLabel + style()->drawControl(QStyle::CE_Header, &opt, &p, this); + return; +} + +// TableHead: the horizontally scrollable table head +TableHead::TableHead(HeadedTable *parent) + : QtTableView(parent), htable(parent), dragging(false) +{ + setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum)); + // setSizePolicy(QSizePolicy + // (QSizePolicy::Minimum,QSizePolicy::Expanding)); + // setTableFlags(Tbl_smoothHScrolling | Tbl_scrollLastHCell); + setTableFlags(Tbl_scrollLastHCell); // ? + setNumRows(1); + + setMouseTracking(true); // for tooltip + + floatHead = new FloatingHead(parent); // tiny memory leak! don't care. + floatHead->hide(); + // setMinimumHeight(20);//cellHeight()); //ZERO!! fault +} + +// Description : draw a field name of table when DRAG !! +// called by paintHeading() +// called by QtTableView::repaint() +void TableHead::paintCell(QPainter *p, int row, int col, bool use_cache) +{ + static int count = 0; + /// printf(" paintcell %d,%d\n",row,col); + int w = htable->max_widths[col]; + + // if( isCellChanged (row,col,true)==false) + { + // printf(" paintcell %d,%d\n",row,col); + // if(use_cache==true) // check cached value + { + // return; + } + } + + // if( col ==11 ) printf("head: %d (%d) \n",col,count++); + // w = cellUpdateR.width(); + { + QStyleOptionHeader opt; + QRect rectR(0, 0, w, height()); + opt.rect = rectR; + opt.text = htable->title(col); + opt.state = QStyle::State_Enabled; + if (htable->sortedCol() == col) + opt.state = opt.state | QStyle::State_Sunken; + // CE_Header, CE_HeaderSection, CE_HeaderLabel + style()->drawControl(QStyle::CE_Header, &opt, p, this); + } +} + +// works +void TableHead::eraseRight(QPainter *p, QRect &r) +{ + QStyleOptionHeader opt; + // printf("widt=%d \n",r.width()); + opt.rect = r; + opt.text = ""; + style()->drawControl(QStyle::CE_Header, &opt, p, this); +} + +// virtual ! +int TableHead::cellWidth(int col) { return htable->max_widths[col]; } + +// +void TableHead::scrollSideways(int val) { setXOffset(val); } + +void TableHead::mousePressEvent(QMouseEvent *e) +{ + int col = findCol(e->x()); + // printf("col=%d\n",col); + if (col == -1) + return; + click_col = col; + + if (e->button() == Qt::RightButton) + // && htable->options & HTBL_HEADING_CONTEXT_MENU) + { + emit rightClicked(e->globalPos(), col); + return; + } + + if (e->button() == Qt::LeftButton) + { + press = e->pos(); + } +} + +void TableHead::dragEnterEvent(QDragEnterEvent *event) +{ + printf("dragEnterEvent()\n"); +} +void TableHead::dragLeaveEvent(QDragLeaveEvent *event) +{ + printf("dragLeaveEvent()\n"); +} + +void TableHead::mouseMoveEvent(QMouseEvent *e) +{ + // printf("mouseMoveEvent()\n"); + int col = findCol(e->x()); + if (col < 0) + { + emit htable->outOfHCell(); + return; + } + // if( !dragging) return; + emit htable->flyOnHCell(col); + + int thold = abs(press.x() - e->pos().x()); + + if (e->buttons() == Qt::LeftButton // Button state + and htable->options & HTBL_REORDER_COLS) + { + { + + if (!dragging and thold > 5) + { + dragging = true; + drag_offset = + press.x() - htable->colOffset(click_col) + xOffset(); + floatHead->setTitle(htable->title(click_col), + cellWidth(click_col), height()); + floatHead->show(); + // vp->show(); + } + + if (dragging) + { + // htable->body->drawGhostCol(drag_pos - + // drag_offset, w); + floatHead->move(e->x() - drag_offset, 0); + + int vcol = findColNoMinus(e->x() - drag_offset); + int vcol_offx = htable->colOffset(vcol); + static int old_pos = -1; + // printf("vcol=%d\n",vcol); + + // virtual Pointer move! + if (old_pos != vcol_offx) + { + QWidget *p = parentWidget(); // htable + // vp->setMovable(htable->columnMovable(htable->columnMovable(vcol) + // and htable->columnMovable(click_col) + // )); + // vp->move(vcol_offx - + // p->geometry().x() + //,p->geometry().y() - + // 13); + // vp->move(e->x() ,10); + old_pos = vcol_offx; + } + drag_pos = e->x(); // save old pos + + return; + } + } + } + + if (col != -1) // ToolTip + { + // for(int i=0;i<1024*1024;i++) htable->tipText(col); + //// memory leak + // test + QString s = htable->tipText(col); + + if (!s.isEmpty()) + QToolTip::showText(e->globalPos(), s); + } +} + +void TableHead::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) // Returns the button that caused the + // event. + { + int col = findCol(e->x()); + if (col < 0) + col = (e->x() < 0) ? 0 : htable->ncols - 1; // good! + + if (!dragging and col == click_col) + { + // htable->emit_click_signal(col); // just click no drag + emit htable->titleClicked(col); + } + + if (dragging) + { + int vcol = findColNoMinus(e->x() - drag_offset); + if (click_col >= 0) + { + htable->moveCol(click_col, vcol); // call + // Pstable::moveCol(int + // col, int place) + // should emit !! + // emit colMoved(click_col, vcol); + } + /// printf(" vcol2=%d \n",vcol); + } + dragging = false; + floatHead->hide(); + // vp->hide(); + } +} + +#include + +// TableBody: the table body, scrollable in all ways +TableBody::TableBody(HeadedTable *parent) : QtTableView(parent), htable(parent) +{ + + // setTableFlags(Tbl_autoScrollBars | Tbl_smoothScrolling); + setTableFlags(Tbl_snapToVGrid); // works + setTableFlags(Tbl_hScrollBar | Tbl_vScrollBar); + first_drag_row = prev_drag_row = -1; + autoscrolling = false; + gadget_click = false; + setMouseTracking(true); + + QStyle *s = QStyleFactory::create("windows"); + if (s) + setStyle(s); +} + +// virtual from QtTableView +void TableBody::scrollTrigger(int rx, int ry) +{ + if (ry == 0) + return; + int n = ry / cellHeight(); + /// printf("ry=%d n=%d\n",ry,n); + if (ry > 0) + tablecache.cutDown(n); // error? + else + tablecache.cutUp(-n); + // printf("ry=%d cellheight=%d n=%d\n",ry,cellHeight(),n); +} + +// new, possible COMMON? +bool TableBody::isCellChanged(int row, int col) +{ + QString str; + int xpos; + int fold, dep; + bool selected; + bool sorted; + int lc; + int width; + + { + str = htable->text(row, col); + xpos = htable->colXPos(col); + selected = htable->isSelected(row); + sorted = (htable->sortedCol() == col); + width = htable->max_widths[col]; + } + + // int left = leftCell(), top = topCell(); + CellAttribute *attr = tablecache.value(row - topCell(), col - leftCell()); + + if (attr == NULL) // never NULL + { + return true; + } + else + { + bool result = false; + if (attr->text == str and attr->selected == selected and + attr->sorted == sorted and attr->xpos == xpos and attr->w == width + // and attr->size==tmp_size + and + (attr->size.height() >= tmp_size.height()) + // and (attr->size.width()>=tmp_size.width()) + ) + { + result = false; + } + else + { + // attr->text=str; + // attr->selected=selected; + // attr->sorted=sorted; + // attr->xpos=xpos; + // attr->size=tmp_size; + // attr->w=width; + + // if (row==29 and col==0) + // printf("(%d %d) xpos=%d %d sorted=%d %d sel=%d + //%d str=%s + //%s\n",attr->size.height(),tmp_size.height(), + // attr->xpos,xpos,attr->sorted,sorted,attr->selected,selected + // ,qPrintable(attr->text),qPrintable(str)); + // printf("true (%d + //%d)\n",row-topCell(),col-leftCell()); + return true; + } + + if (col == 0) + { + dep = htable->rowDepth(row); // folding_level + fold = htable->folded(row); + lc = htable->lastChild(row); + + if (attr->depth == dep /* Uninitial*/ + and attr->folded == fold and attr->lastchild == lc) + { + // false + } + else + { + // attr->depth=dep; + // attr->folded=fold; + result = true; + // printf("true (%d + //%d)\n",row-topCell(),col-leftCell()); + } + } + return result; + } +} + +bool TableHead::isCellChanged(int row, int col) +{ + int xpos; + int ypos; + int w; + int fold, dep; + QString str; + bool sorted = (htable->sortedCol() == col); + + w = htable->max_widths[col]; + xpos = htable->colXPos(col); + + { + str = htable->title(col); + } + // int left = leftCell(); + // int top = topCell(); + CellAttribute *attr = tablecache.value(row - topCell(), col - leftCell()); + + if (attr == NULL) // Uninitialed? + { + return true; + } + else + { + bool result = false; + if (attr->text == str and attr->sorted == sorted and + attr->size == tmp_size and attr->xpos == xpos + // and attr->w==w + ) + { + return false; + } + else + { + attr->text = str; + attr->sorted = sorted; + attr->xpos = xpos; + // attr->w=w; + attr->size = tmp_size; + // if(head) printf("head + // true"); + return true; + } + // printf("return %s x=%d\n",str.toAscii().data(),xpos); + } +} + +#include + +// DRAFT CODE !! BOTTLENECK !!! +// Description : draw a cell of table +// 1. draw the background of a cell +// 2. draw the text of a cell +// +// called by +// 1.QtTableView::paintEvent() +void TableBody::paintCell(QPainter *p, int row, int col, bool use_cache) +{ + // *** sequence important !!! + // if(isCellChanged(row,col)==false) { + // if(use_cache==true) return; + //} + + CellAttribute *attr = + tablecache.value(row - topCell(), col - leftCell()); // save cache + // isCellChanged(row,col); + + bool sort; + int h = cellHeight(); + int w = htable->max_widths[col]; + attr->w = w; // + + QColor baseColor = palette().brush(QPalette::Base).color(); + sort = (col == htable->sorted_col); + attr->sorted = sort; + + // gridline + QColor lineColor, textColor; + QColor altColor = palette().brush(QPalette::AlternateBase).color(); + lineColor.setRgb((baseColor.red() * 0.5 + altColor.red() * 0.5), + (baseColor.green() * 0.4 + altColor.green() * 0.6), + (baseColor.blue() * 0.4 + altColor.blue() * 0.6)); + + // QStyleOption opt; + // opt.rect=QRect(0,0,w,h-1); + // opt.state = QStyle::State_Active; //? + // QStyle::SH_ItemView_PaintAlternatingRowColorsForEmptyArea + // const int gridHint = + // style()->styleHint(QStyle::SH_Table_GridLineColor, + // &opt, this); + + p->setPen(lineColor); + p->drawLine(0, h - 1, w, h - 1); + + // background + if (htable->isSelected(row)) + { + p->fillRect(0, 0, w, h - 1, palette().brush(QPalette::Highlight)); + p->setPen(palette().brush(QPalette::HighlightedText).color()); // text + attr->selected = true; + } + else + { + attr->selected = false; + if (sort) + { + p->fillRect(0, 0, w, h - 1, lineColor.dark(101)); + } + else + { + p->fillRect(0, 0, w, h - 1, baseColor); + } + // style()->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, p, + // this); + p->setPen(palette().brush(QPalette::Text).color()); // text + } + + // tree gadget , treestep=height + int gap = h / 4; + int testFlag = 0; + if (col == 0 and htable->treemode == true) + { + QStyleOption opt; + int d = htable->rowDepth(row); + attr->depth = d; //** + int treestep = htable->treestep; + if (true /*lines*/) + { + /// int dx = folding ? gadget_space : 6; + for (int level = d, prow = row; level >= 0 and prow >= 0; + level--, prow = htable->parentRow(prow)) + { + if (level == d) + continue; + int x = gap + level * treestep; + QRect branchR(x, 0, treestep, h); + opt.rect = branchR; + + if (htable->lastChild(prow) == false) + { + if (testFlag) + { + p->drawLine(x + treestep / 2, 0, x + treestep / 2, h); + } + else + { + opt.state = QStyle::State_Sibling; // | vertical + // line + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, + p, this); + } + } + } + + QRect branchR(gap + d * treestep, 0, treestep, h); + opt.rect = branchR; + if (testFlag) + { + int x = gap + d * treestep + treestep / 2; + + p->drawLine(x, h / 2, x + treestep / 2, h / 2); + if (!htable->lastChild(row)) + { + p->drawLine(x, 0, x, h); + } + else + { + p->drawLine(x, 0, x, h / 2); + } + } + else + { + opt.state = QStyle::State_Item; //? + if (!htable->lastChild(row)) + opt.state = opt.state | QStyle::State_Sibling; // | + } + attr->lastchild = htable->lastChild(row); + } + + if (true /*folding*/) + { + HeadedTable::NodeState fs = htable->folded(row); + attr->folded = fs; //** + if (fs != HeadedTable::Leaf) + { + opt.state = opt.state | QStyle::State_Children; + if (fs != HeadedTable::Closed) + opt.state = opt.state | QStyle::State_Open; + } + p->save(); // temp , save pencolor + style()->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, p, this); + p->restore(); + } + gap = htable->gadget_space + d * treestep + 1; + } + + // virtual int Pstable::alignment(int col) + if (htable->alignment_col[col] == Qt::AlignRight) // using cache + { + w -= gap; + gap = 0; + } + // Qt::IncludeTrailingSpaces + // p->drawText(gap, 0, w , h, Qt::TextIncludeTrailingSpace | + // Qt::IncludeTrailingSpaces | Qt::AlignVCenter | + // htable->alignment_col[col],htable->text(row,col)); + // p->drawText(gap, 0, w , h, Qt::TextIncludeTrailingSpace | + // Qt::AlignVCenter + // | htable->alignment_col[col],htable->text(row,col)); + p->drawText(gap, 0, w, h, Qt::AlignVCenter | htable->alignment_col[col], + htable->text(row, col)); + + htable->overpaintCell(p, row, col, gap); + + // cache write! + // attr->xpos=tmp_x ; + attr->text = htable->text(row, col); + attr->xpos = htable->colXPos(col); + attr->size = tmp_size; +} + +// DEL , Home key +void TableBody::jumpTop() { setYOffset(0); } + +// DEL, End Key +void TableBody::jumpBottom() { setYOffset(maxYOffset()); } + +/// !!!! +void TableBody::centerVertically(int row) +{ + int topcell = row - viewHeight() / (cellHeight() * 2); + setTopCell(qMax(topcell, 0)); + update(); +} + +//?? +void TableBody::showRange(int from, int to) +{ + int h = viewHeight() / cellHeight(); + if (to >= topCell() + h) + setTopCell(qMax(0, qMin(from, to - h + 1))); +} + +// virtual +// called by +// 1. +int TableBody::cellWidth(int col) { return htable->max_widths[col]; } + +// **** fix !! +void TableBody::updateRow(int row) +{ + // ?? + // for(int col = 0; col < htable->ncols; col++) + // updateCell(row, col,false); updateCell(row, col); +} + +void TableBody::mousePressEvent(QMouseEvent *e) +{ + if (numRows() == 0) + return; // *** prevent out of range + + // printf("mousePressEvent() 1\n"); + static int last_row = -1; + + int row = findRow(e->y()); + if (row == -1) + { + // printf("mousePressEvent\n"); + htable->clearAllSelections(); + if (e->y() >= 0) + row = numRows(); // if SHIFT+click outside ~ + first_drag_row = prev_drag_row = row; + return; + } + + if (!(htable->options & HTBL_ROW_SELECTION)) + return; + + if (e->button() == Qt::LeftButton) + { + // folding + if (htable->treemode && htable->folding && + e->x() < htable->gadget_space + + htable->treestep * htable->rowDepth(row) && + htable->folded(row) != HeadedTable::Leaf) + { + emit htable->foldSubTree(row); + gadget_click = true; + last_row = row; + // clearCache(); // dont use cache + return; + } + + if (e->modifiers() & Qt::ShiftModifier) + { + if (row < last_row) + for (int i = row; i < last_row; i++) + htable->setSelected(i, true); // virtual + else + for (int i = last_row; i <= row; i++) + htable->setSelected(i, true); // virtual + } + else if (e->modifiers() & Qt::ControlModifier) + { + htable->setSelected(row, !htable->isSelected(row)); + } + else + htable->selectOnlyOne(row); + + first_drag_row = prev_drag_row = row; + + emit htable->selectionChanged(); + } + + else if (e->button() == Qt::RightButton) + { + if (!htable->isSelected(row)) + htable->selectOnlyOne(row); + emit htable->selectionChanged(); + // better? emit htable->rightClicked(e->globalPos()); + } + last_row = row; + // htable->repaint_changed(); + repaintChanged(); // repaint + // view->update(); // fast but use more CPU +} + +void TableBody::mouseReleaseEvent(QMouseEvent *e) +{ + gadget_click = false; + + if (e->button() == Qt::LeftButton) + { + if (autoscrolling) + { + ////killTimers(); // no more autoscrolling + first_drag_row = prev_drag_row = -1; + autoscrolling = false; + } + } + else if (e->button() == Qt::RightButton) + { + QPoint p(6, 3); + p += e->globalPos(); + emit htable->rightClicked(p); + } +} + +void TableBody::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) + { + int row = findRow(e->y()); + if (row != -1) + { + if (htable->options & HTBL_ROW_SELECTION && + !htable->isSelected(row)) + htable->selectOnlyOne(row); + // htable->selectionNotify(); + emit htable->selectionChanged(); + // htable->emit_double_click_signal(row); + emit htable->doubleClicked(row); + } + } +} + +// Bottle Neck ? no +void TableBody::leaveEvent(QEvent *) { emit htable->outOfCell(); } + +void TableBody::mouseMoveEvent(QMouseEvent *e) +{ + if (numRows() == 0) + return; // *** prevent out of range + + // DRAFT CODE ! + // Signal : find row, col(field name) emit flyOnEvent Signal + int row = findRow(e->y()); + int col = findCol(e->x()); + + if (row < 0 or col < 0) + { + emit htable->outOfCell(); // if... + return; + } + + emit htable->flyOnCell(row, col); + + if (e->buttons() == Qt::NoButton) + return; + if (e->buttons() == Qt::RightButton) + return; + + if (e->buttons() & Qt::ControlModifier || gadget_click) + return; + + if (row != prev_drag_row) + { + if (row == -1) + { + if (!autoscrolling) + { + // dragging outside table, cause scrolling + scrolldir = (e->y() < 0) ? UP : DOWN; + // killTimers(); + // startTimer(scroll_delay); + autoscrolling = true; + } + } + else + { + ////killTimers(); + autoscrolling = false; + dragSelectTo(row); + } + } + // repaintRow(row); + // view->update(); + repaintChanged(); +} + +void TableBody::timerEvent(QTimerEvent *) +{ + // timer not proved ! + return; + // printf("timer\n"); + if (!autoscrolling) + return; + ////killTimers(); + if (scrolldir == UP) + { + int top = topCell(); + setTopCell((top > 1) ? top - 1 : 0); + dragSelectTo(topCell()); + } + else + { + setTopCell(topCell() + 1); + int bottom = lastRowVisible(); + dragSelectTo((bottom < numRows()) ? bottom : numRows() - 1); + } + startTimer(scroll_delay); +} + +// change drag selection point from previous drag position to row +void TableBody::dragSelectTo(int row) +{ + int dir = (row > prev_drag_row) ? 1 : -1; + if ((prev_drag_row - first_drag_row) * dir >= 0) + { + // moving away from start point + for (int i = prev_drag_row + dir; i - dir != row; i += dir) + htable->setSelected(i, true); + } + else + { + // moving towards start point + for (int i = prev_drag_row; i != row; i += dir) + htable->setSelected(i, false); + } + prev_drag_row = row; + // htable->selectionNotify(); + emit htable->selectionChanged(); +} + +// heuristic for determining a good XOR color: This is in general a hard +// problem but since we know (most of) the background and the foreground, +// we can try. Note that this function might not return an allocated QColor, +// so it is only useful for XOR drawing. + +QColor TableBody::getXorColor() +{ + QColor fg = palette().brush(QPalette::Base).color(); + + return fg; /// QColor(0, fg.pixel() ^ backgroundColor().pixel()); +} + +void TableBody::drawGhostCol(int x, int w) +{ + static QColor xorcol = getXorColor(); + + QPainter p(this); + + p.setPen(xorcol); + // p.setRasterOp(XorROP); + /// p.drawLine(x, 0, x, height()); + ////p.drawLine(x + w, 0, x + w, height()); +} + +// HeadedTable: the actually useable class +HeadedTable::HeadedTable(QWidget *parent, int opts) : QWidget(parent) +{ + // meaningless? + options = HTBL_ROW_CONTEXT_MENU | HTBL_ROW_SELECTION | + HTBL_ROW_DOUBLE_CLICK | HTBL_HEADING_TOOLTIPS | + HTBL_HEADING_CONTEXT_MENU | HTBL_HEADING_CLICK | + HTBL_REORDER_COLS; + + head = new TableHead(this); + body = new TableBody(this); + head->setObjectName("head"); + body->setObjectName("body"); + + sorted_col = -1; + treemode = false; + treestep = 0; + lines = true; + folding = true; + gadget_space = 0; + nrows = ncols = 0; // hmm... + + // printf("style name=%s \n",qPrintable(style()->objectName())); + // QApplication::setStyle(new QWindowsStyle); + head->setFrameShape(QFrame::NoFrame); + if (style()->objectName().contains("oxygen")) + ; + else + { + body->setFrameShape(QFrame::NoFrame); + } + + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->setSpacing(0); // distance between Widget and Widget + +#if QT_VERSION < 0x040300 + vlayout->setMargin(0); // qt-4.2 +#else + vlayout->setContentsMargins(0, 0, 0, 0); // qt-4.3 +#endif + + // vlayout->setSizeConstraint ( SizeConstraint ) + vlayout->addWidget(head); + vlayout->addWidget(body); + setLayout(vlayout); + + // connect keyboard shortcuts + QShortcut *c; + c = new QShortcut(Qt::CTRL + Qt::Key_A, this, SLOT(selectAll())); + // synchronize horizontal scrolling of head and body + connect(body->horizontalScrollBar(), SIGNAL(valueChanged(int)), head, + SLOT(scrollSideways(int))); + fontChange(font()); // *** need for init + // vp=new VPointer((QWidget *)QObject::parent()); + // vp->hide(); //memory leak!! +} + +HeadedTable::~HeadedTable() {} + +// ok : move to Qttableview +void HeadedTable::fontChange(const QFont &oldFont) +{ + // DEBUG("fontChange()\n"); + int fontHeight = fontMetrics().height() + 1; + // printf("cell height =%d \n", cellH); + if (fontHeight % 2 != 0) + fontHeight++; // for pretty fold-lines + head->setCellHeight(fontHeight + 5); + head->setMaximumHeight(head->cellHeight()); + body->setCellHeight(fontHeight); + treestep = fontHeight; + gadget_space = folding ? fontHeight + (fontHeight / 2) : 0; + // QWidget::fontChange ( oldFont ); +} + +// DRAFT virtual ! +void HeadedTable::moveCol(int col, int place) +{ + // go to void Procview::moveColumn(int col, int place) +} + +// distance (in table coords) from left table edge of physical column +int HeadedTable::colOffset(int col) +{ + int x = 0; + for (int c = 0; c < col; c++) + x += max_widths[c]; + // x += body->cellWidth(c); + return x; +} + +// OPTIZ +inline int HeadedTable::colXPos(int col) +{ + int x = 0; // + coloffset + for (int c = 0; c < col; c++) + x += max_widths[c]; + return x; +} + +// repaint columns from col0 to col1. If col1 is -1, repaint all +// the way to the right edge of the table. +// called by +// 1.void Qps::update_table(int col) +void HeadedTable::repaintColumns(int col0, int col1) +{ + QRect bvr = body->viewRect(); + QRect hvr = head->viewRect(); + int x0 = colOffset(col0) - body->xOffset(); + if (x0 > hvr.width()) + return; + if (x0 < 0) + x0 = 0; + bvr.setLeft(x0); + hvr.setLeft(x0); + if (col1 >= 0) + { + int x1 = colOffset(col1) + max_widths[col1] - body->xOffset(); + if (x1 < hvr.width()) + { + hvr.setRight(x1); + bvr.setRight(x1); + } + } + head->repaint(hvr); + body->repaint(bvr); +} + +// DEL -> virtual +// called by Pstable::setTreeMode(bool) +void HeadedTable::setTreeMode(bool tm) +{ + treemode = tm; + head->clearCache(); + body->clearCache(); +} + +// update table Head !! +void HeadedTable::setSortedCol(int col) +{ + { + int old_sorted = sorted_col; + sorted_col = col; + // printf("old_sorted=%d\n",old_sorted); + // printf("sorted_col=%d\n",sorted_col); + if (old_sorted != -1 && old_sorted < ncols) // ncols bug + updateHeading(old_sorted); + if (col != -1 && col < ncols) + updateHeading(col); + } +} + +// should be virtual. why? +void HeadedTable::clearAllSelections() +{ + for (int row = 0; row < nrows; row++) + setSelected(row, false); + body->view->update(); + // update(); // not work why? +} + +// BottleNeck ? +void HeadedTable::selectOnlyOne(int row) +{ + // this apply to the current list only! + for (int r = 0; r < nrows; r++) + setSelected(r, r == row); +} + +void HeadedTable::selectAll() +{ + for (int r = 0; r < nrows; r++) + setSelected(r, true); + emit selectionChanged(); // notwork? +} + +// default implementation returns a null string (no tip d) +QString HeadedTable::tipText(int) { return ""; } +char *HeadedTable::total_selectedRow(int col) { return 0; } + +void HeadedTable::setNumRows(int rows) +{ + nrows = rows; + head->setNumRows(1); + body->setNumRows(rows); +} + +void TableCache::setRow(int row) +{ + if (row < 0) + return; + + int i, size = rows.size(); + if (row >= size) + { + // rows.append(new TableRow); + } + + if (row < nrow) + { + // printf("row=%d,nrow=%d \n",row,nrow); + if (size < nrow) + nrow = size; + for (i = row; i < nrow; i++) + { + for (int j = 0; j < 48; j++) + rows[i]->cells[j].text = ""; + } + } + nrow = row; +} + +void TableCache::setCol(int col) +{ + if (col < 0) + return; + if (col < ncol) + { + // printf("clear cache\n"); + int i, size = rows.size(); + for (i = 0; i < size; i++) + for (int j = col; j < ncol; j++) + rows[i]->cells[j].text = ""; + } + ncol = col; +} + +// TEST +void TableHead::checkProfile() +{ + QRect viewR = viewRect(); + // if(viewR.y()!=0) printf(" qps: ooooohss....\n"); + int maxViewCol = findCol(viewR.width()); + if (maxViewCol < 0) + maxViewCol = numCols(); + tablecache.setCol(maxViewCol); + tablecache.setRow(1); +} + +// TESt +void TableBody::checkProfile() +{ + htable->checkTableModel(); // important + + QRect viewR = viewRect(); + if (viewR.y() != 0) + printf(" qps: ooooophss....\n"); + int maxViewRow = findRow(viewR.height()); + if (maxViewRow < 0) + maxViewRow = numRows(); + int maxViewCol = findCol(viewR.width()); + if (maxViewCol < 0) + maxViewCol = numCols(); + tablecache.setCol(maxViewCol); + tablecache.setRow(maxViewRow); +} + +void HeadedTable::setNumCols(int cols) +{ + // printf("cols=%d\n",cols); + + ncols = cols; + // resetWidths(); + for (int i = 0; i < cols; i++) + { + alignment_col[i] = alignment(i); + updateColWidth(i); + } + // printf("ncols=%d\n",ncols); + head->setNumCols(ncols); + body->setNumCols(ncols); +} + +/* +//virtual +int HeadedTable::colWidth(int col) +{ + int r = numRows(); + for(int i=0;i w) + w = sw; + } + + if (treecol) + { + w += gadget_space; + } + } + // don't forget the width of the heading + hw = fontMetrics().width(title(col)) + 12; + // return 0; + if (hw > w) + w = hw; + + // sw=fontmetric.width("0") * colWidth(col); + // if(sw>w) w=sw; + + max_widths[col] = w; + return 0; +} + +void HeadedTable::resetWidths() +{ + for (int i = 0; i < numCols() + 1; i++) + max_widths[i] = 0; +} + +// called by ? +void HeadedTable::repaintAll() // -> repaint() +{ + int count = 0, trow = 0; + for (int row = 0; row < nrows; row++) + if (isSelected(row)) + { + count++; + trow = row; + } + // body->view->update(); + // update(); // not work why? + + // printf("Qps debug: repaintAll()\n"); + // head->clearCache(); + // body->clearCache(); + // update(); + body->view->update(); + head->view->update(); + if (count == 1) + centerVertically(trow); +} + +// PROBLEM : update() + repaint() call = draw twice !!! +// called by pstable::refresh() +// DEL if (render_area smaller than full_arear ) then force draw +void HeadedTable::repaint_changed() +{ + // printf("repaint_changed()\n"); + // body->setUpdatesEnabled(true); + head->repaintChanged(); + body->repaintChanged(); +} + +void HeadedTable::hideEvent(QHideEvent *event) {} + +void HeadedTable::paintEvent(QPaintEvent *e) +{ + static int c = 0; + // printf("HeadedTable paintEvent %d\n",c++); //works ? + // QWidget::paintEvent(e); + // body->clearCache(); + // head->update(); +} + +void HeadedTable::showEvent(QShowEvent *) +{ + // printf("HeadedTable showEvent\n"); // works + // before paintEvent call + head->clearCache(); + body->clearCache(); +} + +void HeadedTable::resizeEvent(QResizeEvent *e) +{ + // DEBUG("resizeEvent() HeadedTable %d\n",head->cellHeight()); + //// head->repaintRow(0); + QWidget::resizeEvent(e); + head->clearCache(); + body->clearCache(); +} diff --git a/src/htable.h b/src/htable.h new file mode 100644 index 0000000..bb9231f --- /dev/null +++ b/src/htable.h @@ -0,0 +1,362 @@ +// htable.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +// This file defines the HeadedTable class. +// A HeadedTable is a QTableView with one line of headers over each column. + +#ifndef HTABLE_H +#define HTABLE_H + +#include "qttableview.h" + +#ifndef USING_PCH +#include +#include +#include +#endif + +// table options (bits to be ORed together) +#define HTBL_ROW_SELECTION 1 // rows are selectable +#define HTBL_ROW_DOUBLE_CLICK 2 // rows can be double-clicked +#define HTBL_ROW_CONTEXT_MENU 4 // right button menu on rows +#define HTBL_HEADING_TOOLTIPS 8 // tooltips on headings +#define HTBL_HEADING_CONTEXT_MENU 16 // right button menu on headings +#define HTBL_HEADING_CLICK 32 // able to click on heading (change sort order) +#define HTBL_REORDER_COLS 64 // able to reorder columns + +class HeadedTable; + +class CellAttribute +{ + public: + QString text; + bool selected; + bool sorted; + QSize size; + QColor backColor; // table share + QColor foreColor; // rows share + int xpos; // cols share + int ypos; // rows share + int w; // cols share + int depth; // rows share + int folded; // + int lastchild; // + CellAttribute() + { + text = ""; + sorted = selected = false; + xpos = -1, ypos = -1, w = 0, depth = -1, folded = -1; + lastchild = 0; + } +}; + +struct TableRow +{ + public: + CellAttribute cells[64]; // enough +}; + +class TableCache +{ + int nrow, ncol; + QList rows; + + public: + TableCache() + { + // for(int i=0;i<1;i++) // tmp + rows.append(new TableRow); + nrow = 0; + ncol = 0; + } + + void reset() {} // clear cache + void setSize(int row, int col); + void setRow(int row); + void setCol(int col); + + CellAttribute *value(int row, int col) + { + if (row < 0) + qFatal("Qps: Error ! TableCache() under size !! row=%d \n", row); + + if (row >= rows.size()) + { + row = rows.size(); + rows.append(new TableRow); + } + // if(row<0 or row>rows.size()) + return &(rows[row]->cells[col]); + } + + void cutUp(int n) + { + for (int i = 0; i < n; i++) + { + rows.append(rows.takeFirst()); + } + } + void cutDown(int n) + { + for (int i = 0; i < n; i++) + { + TableRow *row = rows.takeLast(); + rows.prepend(row); + } + } +}; + +class VPointer : public QWidget +{ + Q_OBJECT + public: + VPointer(QWidget *parent); + QPixmap *pix; + QPixmap *pix_vcross; + void setMovable(bool b) { flag_movable = b; } + bool flag_movable; + protected slots: + // void event_cursor_moved(QMouseEvent *e); + protected: + // virtual void drawButton 3( QPainter * ) ; + virtual void paintEvent(QPaintEvent *event); + // void resizeEvent(QResizeEvent *p); +}; + +class FloatingHead : public QWidget +{ + Q_OBJECT + public: + FloatingHead(QWidget *parent); + QPixmap *pix; + void setTitle(QString str, int w, int h); + QString title; + protected slots: + protected: + virtual void paintEvent(QPaintEvent *event); +}; + +class TableHead : public QtTableView +{ + Q_OBJECT + public: + TableCache tablecache; + TableHead(HeadedTable *parent = 0); + virtual bool isCellChanged(int row, int col); + virtual void checkProfile(); + + protected slots: + void scrollSideways(int); + + protected: + virtual void paintCell(QPainter *p, int row, int col, bool update); + virtual int cellWidth(int col); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void eraseRight(QPainter *, QRect &r); + + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + + FloatingHead *floatHead; + + HeadedTable *htable; // to access parent class + QPoint press; + int click_col; // physical column clicked in + bool dragging; + int drag_pos; // previous dragging position + int drag_offset; +signals: + void rightClicked(QPoint where, int col); + void toolTip(QPoint where, int col); + + friend class HeadedTable; +}; + +class TableBody : public QtTableView +{ + Q_OBJECT + public: + TableCache tablecache; + TableBody(HeadedTable *parent = 0); + void drawGhostCol(int x, int w); + // int zerowidth; // width of the digit 0 + + virtual bool isCellChanged(int row, int col); + virtual void checkProfile(); + protected slots: + // for keyboard accelerators + void jumpTop(); + void jumpBottom(); + void centerVertically(int row); + void showRange(int from, int to); + + protected: + virtual void scrollTrigger(int x, int y); // tmp + + virtual void paintCell(QPainter *p, int row, int col, bool forced); + virtual int cellWidth(int col); + void mousePressEvent(QMouseEvent *e); + void mouseDoubleClickEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *); + void leaveEvent(QEvent *); + + void timerEvent(QTimerEvent *); + void updateRow(int row); + void dragSelectTo(int row); + QColor getXorColor(); + HeadedTable *htable; // to access parent class + int first_drag_row; // row where drag started + int prev_drag_row; // row where drag was at last event + bool autoscrolling; // true if we are autoscrolling right now + enum + { + UP, + DOWN + } scrolldir; + static const int scroll_delay = 10; // time delay when autoscrolling, in ms + bool gadget_click; // whether mouse press was on folding gadget + private: + QColor background; + QColor color_text; + friend class HeadedTable; +}; + +class HeadedTable : public QWidget +{ + Q_OBJECT + public: + HeadedTable(QWidget *parent, int opts = 0); + ~HeadedTable(); + TableHead *header() { return head; } // interface + + TableHead *head; + TableBody *body; + + CellAttribute *attr; // DEL temporarily buffer + enum NodeState + { + Leaf, + Open, + Closed + }; + + void repaint_changed(); + int updateColWidth(int col); + + void resetWidths(); + void resetWidth(int col) { max_widths[col] = -1; } // dont use ? + void setProxy(QWidget *w) { body->setFocusProxy(w); } + void setSortedCol(int col); + int sortedCol() { return sorted_col; } + int numRows() { return nrows; } + int numCols() { return ncols; } + void setNumRows(int rows); + void setNumCols(int cols); + int clickedColumn() { return head->click_col; } + void deleteCol(int col, bool update = true); + int leftCell() { return body->leftCell(); }; + int lastColVisible() { return body->lastColVisible(); }; + int topCell() { return body->topCell(); }; + int lastRowVisible() { return body->lastRowVisible(); }; + void updateCell(int row, int col, bool erase = false); + void updateHeading(int col){/* head->updateCell(0, col); */}; + void setAutoUpdate(bool update) + { + head->setAutoUpdate(update); + body->setAutoUpdate(update); + }; + void centerVertically(int row) { body->centerVertically(row); }; + void showRange(int from, int to) { body->showRange(from, to); }; + void repaintColumns(int col0, int col1 = -1); + void setTreeMode(bool tm); + bool treeMode() { return treemode; }; + int tableWidth() const + { + return body->totalWidth() + body->verticalScrollBar()->width(); + } + + void selectOnlyOne(int row); + int numSelected() { return 0; } + void clearAllSelections(); + + virtual void setSelected(int row, bool sel){}; + virtual bool isSelected(int row) { return false; } + virtual void checkTableModel(){}; +signals: + void titleClicked(int col); + void selectionChanged(); + void doubleClicked(int row); + void rightClicked(QPoint where); + void foldSubTree(int row); + void colMoved(int col, int place); + void flyOnCell(int row, int col); + void flyOnHCell(int col); + void outOfCell(); + void outOfHCell(); + + public slots: + void selectAll(); + void repaintAll(); + + protected: + void fontChange(const QFont &oldFont); + // These must be implemented in subclasses + virtual QString title(int col) = 0; + virtual QString text(int row, int col) = 0; + virtual char *total_selectedRow(int col); + // colWidth returns width in digit units; negative means variable width. + virtual int colWidth(int col) = 0; // head_width + virtual int alignment(int col) { return 0; } + virtual int sizeHintForColumn(int col) const { return -1; } + virtual void paintEvent(QPaintEvent *); + virtual void hideEvent(QHideEvent *event); + virtual void showEvent(QShowEvent *event); + + // may be reimplemented (default exists) + virtual void moveCol(int col, int place); + + virtual QString tipText(int col); + virtual int rowDepth(int row) { return 0; }; + virtual NodeState folded(int row) { return Leaf; }; + virtual int parentRow(int row) { return 0; }; + virtual bool lastChild(int row) { return false; }; + virtual bool columnMovable(int col) { return true; }; + // virtual bool modified(int row){return true;}; + virtual void overpaintCell(QPainter *p, int row, int col, int xpos){}; + + void resizeEvent(QResizeEvent *); + + bool treemode; + int treestep; // indentation for each tree level + + private: + inline int computedWidth(int col); + int colOffset(int col); + inline int colXPos(int col); + void updateCols(int deltacols, int place, bool update); + + int alignment_col[64]; + int headwidth_col[64]; + int max_widths[64]; // row widths, indexed by columns + // Svec widths; // row widths, indexed by columns + + int sorted_col; // column to emphasize + int reversed_sort; // true if sorting backwards + int options; + int nrows; + int ncols; + + // text cache is in logical columns (reorder causes no flush) + bool folding; // true if folding gadgets are used + bool lines; // true if tree lines are drawn + int gadget_space; // horizontal space for show/hide tree gadget + + friend class TableHead; + friend class TableBody; +}; +#endif // HTABLE_H diff --git a/src/htable2.cpp b/src/htable2.cpp new file mode 100644 index 0000000..c726b87 --- /dev/null +++ b/src/htable2.cpp @@ -0,0 +1,210 @@ + +#include "htable2.h" +HtableModel::HtableModel(QObject *parent) : QAbstractItemModel(parent) +{ + htable = (HeadedTable2 *)parent; +} + +QModelIndex HtableModel::index(int row, int column, + const QModelIndex &parent) const +{ + // printf("index %d %d\n",row,column); + if (row >= 0 and column >= 0 and row < htable->nrows) + { + // if( column <2) + { + // watchCond *item=watchlist[row]; + return createIndex(row, column, NULL); + } + } + return QModelIndex(); +} + +// Pure Virtual +QModelIndex HtableModel::parent(const QModelIndex &child) const +{ + int row, col; + row = child.row(); + col = child.column(); + if (row >= 0 and col >= 0 and row < htable->nrows) + return createIndex(htable->parentRow(row), col, NULL); + return QModelIndex(); // no parent! +} + +// return chilren count +int HtableModel::rowCount(const QModelIndex &parent) const +{ + return htable->nrows; +} +// why segfault? +int HtableModel::columnCount(const QModelIndex &parent) const +{ + return htable->ncols; +}; + +QVariant HtableModel::headerData(int section, Qt::Orientation orientation, + int role) const +{ + + // printf("headerData\n"); + if (role == Qt::DisplayRole) + { + return htable->title(section); + } + else + + if (role == Qt::TextAlignmentRole) + return htable->alignment(section); + // return Qt::AlignCenter; + else if (role == Qt::ToolTipRole) + return htable->tipText(section); + else + /* + if (role == Qt::FontRole) + { + QFont f=QFont(); + f.setBold(false); + return f; + } */ + if (role == Qt::SizeHintRole) + { + // return QSize(18,18); + } + return QVariant(); +} + +QVariant HtableModel::data(const QModelIndex &index, int role) const +{ + // printf("data\n"); + // watchCond *item= + // static_cast(index.internalPointer()); + if (role == Qt::DisplayRole) + { + return htable->text(index.row(), index.column()); + } + else if (role == Qt::DecorationRole) + { + } + if (role == Qt::EditRole) + { + } + if (role == Qt::CheckStateRole) + { + // if(item->enable) + // return Qt::Checked; + // else + // return Qt::Unchecked; + } + if (role == Qt::TextAlignmentRole) + return Qt::AlignRight; + if (role == Qt::EditRole) + { + } + if (role == Qt::SizeHintRole) + { + // return QSize(18,18); + } + return QVariant(); +} + +Qt::ItemFlags HtableModel::flags(const QModelIndex &index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +// HtableHeader ////////////////////////// + +HtableHeader::HtableHeader(QWidget *parent) + : QHeaderView(Qt::Horizontal, parent) +{ + htable = (HeadedTable2 *)parent; + setMovable(true); +} + +void HtableHeader::mousePressEvent(QMouseEvent *e) +{ + QHeaderView::mousePressEvent(e); + int col = logicalIndexAt(e->pos()); + // printf("col=%d\n",col); + if (e->button() == Qt::RightButton) + { + // printf("pressed col=%d\n",col); //ok + emit rightClicked(e->globalPos(), col); + return; + } +} + +HeadedTable2::HeadedTable2(QWidget *parent, int opts) : QTreeView(parent) +// :QTreeWidget(parent) +{ + nrows = ncols = 0; + model = new HtableModel(this); + HtableHeader *header = new HtableHeader(this); + setHeader(header); + setModel(model); + // setEditTriggers (QAbstractItemView::SelectedClicked ); + // tableView->setItemDelegateForColumn(1,&delegate); + // tableView->setItemDelegate(&delegate); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + // setSelectionMode (QAbstractItemView::ExtendedSelection); + // setSelectionMode (QAbstractItemView::MultiSelection); + + setUniformRowHeights(true); + setAllColumnsShowFocus(true); + setFrameShape(QFrame::NoFrame); + setAlternatingRowColors(true); + + // v->setResizeMode (0,QHeaderView::Stretch); + // QHeaderView *h=header (); + header->setResizeMode( + QHeaderView::ResizeToContents); // use sizeHintForColumn() + header->setClickable(true); + // header->setVisible(false); +} + +void HeadedTable2::setTreeMode(bool mode) {} + +void HeadedTable2::setSortedCol(int c) {} + +void HeadedTable2::setNumCols(int c) { ncols = c; } +void HeadedTable2::setNumRows(int row) { nrows = row; } +void HeadedTable2::selectAll() {} +void HeadedTable2::repaintAll() {} +void HeadedTable2::repaint_changed() {} + +extern int flag_x; +void HeadedTable2::mousePressEvent(QMouseEvent *e) +{ + printf("mousePressEvent() \n"); + QTreeView::mousePressEvent(e); + // falg_x=1; + flag_x = !flag_x; + if (e->button() == Qt::RightButton) + { + emit rightClicked(e->globalPos()); + return; + } +} + +// TEST & DRAFT: loop .. +void HeadedTable2::modelIterate(const QModelIndex &parent) +{ + int rows = model->rowCount(parent); + // printf("rows=%d\n",rows); + // expand(parent); + setExpanded(parent, true); // expand(ci); + for (int i = 0; i < rows; i++) + { + QModelIndex ci = model->index(i, 0, parent); + // Procinfo *pi= static_cast(ci.internalPointer()); + // if(!pi->hidekids)// setExpanded(ci,true); // + // expand(ci); + // if(!pi->hidekids and !isExpanded(ci)) expand(ci); + // if(!folded(ci) and !isExpanded(ci)) expand(ci); + // if(!folded(ci) and !isExpanded(ci)) + + if (model->rowCount(ci) > 0) + modelIterate(ci); + } +} diff --git a/src/htable2.h b/src/htable2.h new file mode 100644 index 0000000..b59b177 --- /dev/null +++ b/src/htable2.h @@ -0,0 +1,186 @@ + +#include +#include +#include +#include + +#include + +class HeadedTable2; +class CellAttribute2 +{ + public: + QString text; + bool selected; + bool sorted; + + QColor backColor; // table share + QColor foreColor; // rows share + int xpos; // cols share + int ypos; // rows share + int w; // cols share + int depth; // rows share + int folded; // +}; + +class HtableModel : public QAbstractItemModel +// class HtableModel : public QAbstractTableModel +{ + Q_OBJECT + public: + HtableModel(QObject *parent = 0); + ~HtableModel(){}; + virtual QModelIndex + index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; // pure + virtual QModelIndex parent(const QModelIndex &child) const; // pure virtual + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + int columnCount(const QModelIndex &parent) const; + QVariant headerData(int section, Qt::Orientation o, int role) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + // Qt::ItemFlags flags(const QModelIndex &index) const; + // QMap itemData ( const QModelIndex & index ) const + //; + // void update(){ reset();} + void update() { emit layoutChanged(); } + // void update(const QModelIndex &idx); + // void update(int row); + // void update(); //TEMP + HeadedTable2 *htable; +}; + +class HtableHeader : public QHeaderView +// QHeaderView ( Qt::Orientation orientation, QWidget * parent = 0 ) +{ + Q_OBJECT + public: + HtableHeader(QWidget *parent = 0); + ~HtableHeader(){}; + + protected: + virtual void mousePressEvent(QMouseEvent *e); +signals: + void rightClicked(QPoint where, int col); + void toolTip(QPoint where, int col); + + private: + HeadedTable2 *htable; +}; + +class HeadedTable2 : public QTreeView +// class HeadedTable2 : public QTreeWidget +{ + Q_OBJECT + public: + HeadedTable2(QWidget *parent, int opts = 0); + ~HeadedTable2(){}; + + friend class HtableModel; + HtableModel *model; + + enum NodeState + { + Leaf, + Open, + Closed + }; + void repaint_changed(); + int clickedColumn() { return 0; }; // need imp + int sortedCol() { return sorted_col; } // ok + + /* + void deleteCol(int col, bool update = TRUE); + void topAndRepaint(); + int lastRowVisible() { return body->lastRowVisible(); }; + void updateCell(int row, int col, bool erase=false); + void updateHeading(int col) { }; + void selectionNotify(); + void repaintColumns(int col0, int col1 = -1); + bool treeMode() { return treemode; }; + int tableWidth() const { return body->totalWidth()+ + body->verticalScrollBar()->width(); } + bool isCellContentsChanged(int row,int col,bool head); + + + void selectOnly(int row); + void clearAllSelections(); + */ + + int numRows() { return nrows; } + int numCols() { return ncols; } + int numSelected() { return 0; } + void setTreeMode(bool mode); + void setSortedCol(int col); + void setNumRows(int rows); + void setNumCols(int cols); + virtual bool isSelected(int row) { return false; } + virtual void setSelected(int row, bool sel){}; + void modelIterate(const QModelIndex &idx); +signals: + void titleClicked(int col); + void doubleClicked(int row); + void rightClicked(QPoint where); + void foldSubTree(int row); + void colMoved(int col, int place); + + public slots: + void selectAll(); + void repaintAll(); + + protected: + virtual void mousePressEvent(QMouseEvent *e); + virtual int rowDepth(int row) { return 0; }; + virtual NodeState folded(int row) { return Leaf; }; + virtual bool folded(QModelIndex &idx) { return false; }; + virtual int parentRow(int row) { return 0; }; + virtual bool lastChild(int row) { return false; }; + virtual bool columnMovable(int col) { return true; }; + virtual int colWidth(int col) = 0; // head_width + virtual void moveCol(int col, int place){}; + virtual QString title(int col) = 0; + virtual QString text(int row, int col) = 0; + + virtual QString tipText(int col) { return ""; }; + virtual int alignment(int col) { return 0; }; + + /* + virtual char *total_selectedRow(int col); + virtual int sizeHintForColumn(int col) const; + virtual void hideEvent ( QHideEvent * event ); + virtual void showEvent ( QShowEvent * event ); + virtual void resizeEvent(QResizeEvent *); + */ + // colWidth returns width in digit units; negative means variable width. + + // virtual void drawCellContents(int row, int col, int w, int + // h, + // QPainter *painter); + // virtual bool modified(int row){return true;}; + bool treemode; + + private: + // to make signals originate from the right object + /* void emit_click_signal(int col); + void emit_double_click_signal(int row); + void emit_right_click_signal(QPoint where); + void emit_fold(int row); + + inline int computedWidth(int col); + int colOffset(int col); + inline int colXPos(int col); + void updateCols(int deltacols, int place, bool update); + */ + int sorted_col; // column to emphasize + int reversed_sort; // true if sorting backwards + int options; + + int nrows; + int ncols; + + // text cache is in logical columns (reorder causes no flush) + QHash + cached_attr; // indexed by (row << 16) + log_col + QHash + cached_attr_h; // indexed by (row << 16) + log_col +}; diff --git a/src/infobar.cpp b/src/infobar.cpp new file mode 100644 index 0000000..ebfe1f4 --- /dev/null +++ b/src/infobar.cpp @@ -0,0 +1,1367 @@ +// infobar.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +// 13,245,200 +#include +#include +#include + +#include "infobar.h" +#include "proc.h" +#include "qps.h" +#include "misc.h" +#include + +extern ControlBar *controlbar; +extern TFrame *infobox; // testing +extern Qps *qps; // testing + +int history_start_idx = 0; // testing + +float cpu_total = 0; +float cpu_idle = 0; +float cpu_used = 0; + +QMenu *m_popup; +subcpuRack *cpubar = 0; // DEL extra CPU_window + +// ============================ +gwidget *x_cpu; +gwidget *x_mem; +gwidget *x_swap; +gwidget *x_utime; +gwidget *x_load_avg; + +class VCursor +{ + public: + int px; + int py; + int idx; + bool enable; +}; +VCursor vcursor; + +// DEL +void Infobar::hideEvent(QHideEvent *event) +{ + // printf("Infobar::hideEvent()\n"); + // cpubar->hide(); +} + +// return true if the swap meter is redlined +bool Infobar::swaplim_exceeded() +{ + /* + if(Qps::swaplim_percent) { + if(procview->swap_total > 0) { + int free_p = 100 * procview->swap_free / + procview->swap_total; + return free_p < Qps::swaplimit; + } else + return false; + } else { + return procview->swap_free < Qps::swaplimit; + } */ + return false; +} + +char rotate_str[] = "|/-\\|/-\\"; +int rotate_idx = 0; +char rotate_char = '|'; + +// who call ? +// DEL +void Infobar::refresh() +{ + QString s; + + if (rotate_str[++rotate_idx] == 0) + rotate_idx = 0; + rotate_char = rotate_str[rotate_idx]; + + if (Qps::show_load_graph) + update(); +} + +// DEL +// public : add point,, update Pixmap(graph) +// called by Qps::refresh() +void Infobar::update_load() +{ + // add_history_point2(cpu_used); + // NEED SOLUTION!!!!!!!! draw graph before exposed + if (isVisible()) + drawGraphOnPixmap(); + // MouseCurosrCheck +} + +void Infobar::drawGraphOnPixmap() +{ + // if(isVisible()==false) return; + if (size() != pixmap.size()) + { + /// printf("icon_pm changed!!\n"); + pixmap = QPixmap(size()); // resize(w, h); + pixmap.setMask(QBitmap()); // clear the mask for drawing + } + + QPainter p(&pixmap); + p.fillRect(rect(), QBrush(Qt::black)); // p.fill(Qt::black); + + for (int i = 0; i < wlist.size(); i++) + wlist[i]->draw(&p); + + make_graph(width(), height(), &p); +} + +// if p, then show A and hide B; otherwise do the opposite +// DEL? +void Infobar::showup() +{ + // if(cpubar->isVisible()) cpubar->raise(); +} + +// DEL? +void Infobar::show_and_hide(bool p, QWidget *a, QWidget *b) +{ + if (!p) + { + QWidget *c = a; + a = b; + b = c; + } + if (!a->isVisible()) + { + b->hide(); + a->show(); + } +} + +#define TOTAL_BAR 30 +QColor total_color; +QColor part1_color; +QColor part2_color; +QColor part3_color; +// DRAFT CODE : DRAFT CODE !! +// return width +int drawSPECTRUM(QPainter *p, int x, int y, const char *name, int total, + int part1, int part2 = 0, int part3 = 0, int h = 0) +{ + int total_width; + // bluelay 0,180,255 + // Move to Infor::Inforbar() + total_color.setRgb(0, 65, 55); + part1_color.setRgb(0, 255, 210); + part2_color.setRgb(0, 185, 150); + part3_color.setRgb(0, 160, 120); + + int i, w; + int tx, ty, bar_xoffset, bar_h; + int bar; + + char buff[32] = "NO"; + + if (total == 0) + { + name = strcat(buff, name); + } + + if (h == 0) + h = pf_char_height(); + + w = 2 + pf_write(p, x + 2, y + 2, name) + 3; + bar_xoffset = x + w; + + if (total == 0) + return w; //** + + // draw total_dark_null line + bar_h = h - 1; + ty = y + 2; + tx = 0; + p->setPen(total_color); + for (i = 0; i < TOTAL_BAR; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx++; + tx++; + } + total_width = w + tx; + + if (total == 0) + total = 1; + tx = 0; + // draw part1 + p->setPen(part1_color); + bar = part1 * TOTAL_BAR / total; + if (bar == 0 and part1 != 0) + bar = 1; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + // tx++; + } + + if (part2 >= 0) + { + p->setPen(part2_color); + bar = part2 * TOTAL_BAR / total; + if (bar == 0 and part2 != 0) + bar = 1; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + // tx++; + } + } + if (part3 >= 0) + { + p->setPen(part3_color); + bar = part3 * (TOTAL_BAR) / total; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + } + } + // this occured by reporter. + // if(tx>TOTAL_BAR*2 ) printf("Error: %s total =%d, part1=%d, part2=%d + // part3=%d\n",name,total,part1,part2,part3); + return total_width; +} + +int drawSPECTRUM2(QPainter *p, int x, int y, char *name, int total, int part1, + int part2 = 0, int part3 = 0, int bar_h = 9, + int bar_w = TOTAL_BAR) +{ + // Notice: + // total=1, part1=1 occurs + + int total_width; + // bluelay 0,180,255 + // Move to Infor::Inforbar() + total_color.setRgb(0, 67, 75); // dark + part1_color.setRgb(0, 255, 210); + part2_color.setRgb(0, 220, 175); + part3_color.setRgb(0, 180, 150); + + int i, w; + int tx, ty, bar_xoffset; + int bar; + + char buff[32] = "NO"; + + if (total == 0) + { + // printf("total=0\n") ; //occur + name = strcat(buff, name); + } + + w = 2 + pf_write(p, x + 2, y + 2, name) + 2; + bar_xoffset = x + w; + + if (total == 0) + return w; //** + + // draw total_dark_null line + bar_h = bar_h - 1; // if(h==0) h=pf_char_height(); //9 + + ty = y + 2; + tx = 0; + // draw Total_dark_bar + p->setPen(total_color); + for (i = 0; i < bar_w; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + } + total_width = w + tx; + + if (total == 0) + total = 1; // safer divide error. + + tx = 0; + // draw part1 + p->setPen(part1_color); + bar = part1 * bar_w / total; + if (bar == 0 and part1 != 0) + bar = 1; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + // tx++; + } + + if (part2 >= 0) + { + p->setPen(part2_color); + bar = part2 * bar_w / total; + if (bar == 0 and part2 != 0) + bar = 1; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + // tx++; + } + } + if (part3 >= 0) + { + p->setPen(part3_color); + bar = part3 * (bar_w) / total; + for (i = 0; i < bar; i++) + { + p->drawLine(bar_xoffset + tx, ty, bar_xoffset + tx, ty + bar_h); + tx += 2; + } + } + // this occured by reporter. + if (tx > TOTAL_BAR * 2) + { + printf("Error: TOTAL_BAR*2=%d, name= %s total =%d, part1=%d, " + "part2=%d " + "part3=%d\n", + TOTAL_BAR * 2, name, total, part1, part2, part3); + return -1; + } + // if(part1<0){ printf("total=%d part1=%d + // part2=%d\n",total,part1,part2); + // return -1;} + // printf("%s total =%d, part1=%d, part2=%d + // part3=%d\n",name,total,part1,part2,part3); + return total_width; +} + +char str_buff[512]; +#define TIMEDIFF(kind) \ + procview->cpu_times(cpu_id, Proc::kind) - \ + procview->old_cpu_times(cpu_id, Proc::kind) +class w_cpu : public gwidget +{ + private: + int cpu_n; + long total, user, system, idle, nice, wait; + + public: + virtual void draw(QPainter *p) + { + x = 0; + char buff[32]; + int cpu_id; + int cpu_n; + int gheight = 10; + int w; + int mw; + + user = 0, system = 0, idle = 0, nice = 0, wait = 0; + + cpu_n = procview->num_cpus; + + width = 0; + + // if(cpubar->isVisible() and cpu_n>3) + if (true) + { + // show Total CPU + cpu_id = cpu_n; + user = TIMEDIFF(CPUTIME_USER); +#ifdef LINUX + nice = TIMEDIFF(CPUTIME_NICE); +#endif + system = TIMEDIFF(CPUTIME_SYSTEM); +#ifdef SOLARIS + wait = TIMEDIFF(CPUTIME_WAIT); +#endif + idle = TIMEDIFF(CPUTIME_IDLE); + total = user + system + wait + nice + idle; + width = drawSPECTRUM(p, 0, 0, "CPU", total, user, system, nice, + gheight - 1); + // width+=5; + } + + for (cpu_id = 0; cpu_id < cpu_n; cpu_id++) + { + + // if(procview->num_cpus == procview->old_num_cpus) + user = TIMEDIFF(CPUTIME_USER); + idle = TIMEDIFF(CPUTIME_IDLE); + system = TIMEDIFF(CPUTIME_SYSTEM); +#ifdef LINUX + nice = TIMEDIFF(CPUTIME_NICE); +#endif +#ifdef SOLARIS + wait = TIMEDIFF(CPUTIME_WAIT); +#endif + +#ifdef LINUX + total = user + system + nice + idle; +#endif +#ifdef SOLARIS + total = user + system + wait + idle; +#endif + + if (cpu_n > 1 and cpu_n < 129) // 9~16 + { + int bar_w = 0; + if (cpu_n <= 2) + bar_w = TOTAL_BAR; // default + else if (cpu_n < 9) + bar_w = TOTAL_BAR / 2; + else // if(cpu_n<17) + bar_w = TOTAL_BAR / 3; + // else bar_w=13; + + int r, c; + c = cpu_id / 2; + r = cpu_id % 2; + sprintf(buff, "%d", cpu_id); + mw = 4 * 3 + (bar_w * 2 + 8 * 2 - 1) * c; + if (c >= 1) + mw -= 6; + if (c >= 2) + mw -= 6; + + if (0) + { + printf("cpuid %d , user %d , %d \n", 1, + procview->cpu_times(1, Proc::CPUTIME_USER), + procview->old_cpu_times(0, Proc::CPUTIME_USER)); + } + +#ifdef LINUX + int z = drawSPECTRUM2(p, mw, gheight + 1 + r * (gheight - 1), + buff, total, user, system, nice, + gheight - 2, bar_w); +#endif + +#ifdef SOLARIS + int z = drawSPECTRUM2(p, mw, gheight + 1 + r * (gheight - 1), + buff, total, user, system, wait, + gheight - 2, bar_w); +// drawSPECTRUM(p,0,cpu_id*10,buff,total,user,system,wait); +#endif + } + } + p->fillRect(0, gheight + 2, 50 + mw + 100, gheight * 4, + QColor(0, 0, 0, 130)); + height = gheight * 1; + } + + virtual char *info() + { + char str[80]; + float f_user, f_nice, f_system, f_wait; + + f_user = (float)user / total * 100; + f_nice = (float)nice / total * 100; + f_wait = (float)wait / total * 100; + f_system = (float)system / total * 100; +#ifdef LINUX + sprintf(str_buff, "user: %1.1f%% system:%1.1f%% nice:%1.1f%% ", + f_user, f_system, f_nice); +#endif + +#ifdef SOLARIS + sprintf(str_buff, "user: %1.1f%% system:%1.1f%% nice:%1.1f%% ", + f_user, f_system, f_wait); +#endif + return str_buff; + }; +}; + +class w_mem : public gwidget +{ + private: + int used; + + public: + virtual void draw(QPainter *p) + { + height = pf_char_height() + 4; + x = x_cpu->xpluswidth() + 10; +#ifdef LINUX + used = procview->mem_total - procview->mem_free - + procview->mem_buffers - procview->mem_cached; + width = drawSPECTRUM(p, x, 0, "MEM", procview->mem_total, used, + procview->mem_cached, procview->mem_buffers); +#endif + +#ifdef SOLARIS + used = procview->mem_total - procview->mem_free; + width = drawSPECTRUM(p, x, 0, "MEM", procview->mem_total, used); +#endif + } + virtual char *info() + { + char str[80]; + + strcpy(str_buff, "Total: "); + mem_string(procview->mem_total, str); + strcat(str_buff, str); + + strcat(str_buff, " used: "); + mem_string(used, str); + strcat(str_buff, str); + +#ifdef LINUX + strcat(str_buff, " cached: "); + mem_string(procview->mem_cached, str); + strcat(str_buff, str); + strcat(str_buff, " buffer: "); + mem_string(procview->mem_buffers, str); + strcat(str_buff, str); +#endif + + // sprintf(str_buff,"Total: %dKb , cache: %dKb , buffer: + //%dKb", + // procview->mem_total,procview->mem_cached,procview->mem_buffers); + return str_buff; + }; +}; + +class w_swap : public gwidget +{ + private: + int used; + + public: + virtual void draw(QPainter *p) + { + x = x_mem->xpluswidth() + 10; + used = procview->swap_total - procview->swap_free; + width = drawSPECTRUM(p, x, 0, "SWAP", procview->swap_total, used); + height = pf_char_height() + 4; + } + virtual char *info() + { + char str[80]; + + strcpy(str_buff, "Total: "); + mem_string(procview->swap_total, str); + strcat(str_buff, str); + strcat(str_buff, " Free: "); + mem_string(procview->swap_free, str); + strcat(str_buff, str); + strcat(str_buff, " Used: "); + mem_string(used, str); + strcat(str_buff, str); + + // sprintf(str_buff,"Total: %d Kbyte , used %d Kbyte", + // procview->swap_total,procview->swap_free); + return str_buff; + }; +}; + +// DRAFT CODE !! +int drawUTIME(QPainter *p, int x, int y, long boot_time) +{ + char buff[1024]; + // printf("size of long=%d, size of time_t=%d + // \n",sizeof(long),sizeof(time_t)); + long u = (long)time(NULL) - (long)boot_time; + int up_days = u / (3600 * 24); + u %= (3600 * 24); + int up_hrs = u / 3600; + u %= 3600; + int up_mins = u / 60; + int sec = u % 60; + if (up_days == 0) + { + if (up_hrs == 0) + sprintf(buff, "UPTIME %d:%02d", up_mins, sec); + else + sprintf(buff, "UPTIME %d:%02d:%02d", up_hrs, up_mins, sec); + } + else + sprintf(buff, "UPTIME %dDAY%s,%d:%02d:%02d", up_days, + (up_days == 1) ? "" : "s", up_hrs, up_mins, sec); + return pf_write(p, x, y, buff); +} + +class w_utime : public gwidget +{ + public: + virtual void draw(QPainter *p) + { + height = pf_char_height() + 4; + x = x_swap->xpluswidth() + 10; + width = drawUTIME(p, x, 2, procview->boot_time); + } + virtual const char *info() { return "passed time after system booting"; }; +}; + +class w_load_avg : public gwidget +{ + virtual void draw(QPainter *p) + { + char buff[64]; + // printf("w_load_avg\n"); + // sprintf(buff,"QPS %3.02f%%", Procinfo::loadQps); + sprintf(buff, " 1m:%1.02f 5m:%1.02f 15m:%1.02f", procview->loadavg[0], + procview->loadavg[1], procview->loadavg[2]); + + width = pf_str_width(buff); + + x = parent->width() - width - 6; + + int w = x_utime->xpluswidth() + 15; + if (x < w) + x = w; + + pf_write(p, x, 2, buff); + + // + x = parent->width() - 8; + y = parent->height() - 9; + + char str[2] = {0, 0}; + str[0] = rotate_char; + pf_write(p, x, y, str); + } + virtual const char *info() + { + return "Average CPU%% each 1, 5 ,15 minutes"; + }; +}; + +GraphBase::GraphBase(QWidget *parent, Procview *pv) +{ + procview = pv; + // setCursor ( QCursor(Qt::CrossCursor) ) ; + npoints = 0, peak = 0, h_index = 0, dirty = true; + official_height = 39; + + hist_size = 1280; + history = new float[hist_size]; + + setMinimumHeight(24); + + QWidget::setMouseTracking(true); +} + +IO_Graph::IO_Graph(QWidget *parent, Procview *pv) : GraphBase(parent, pv) +{ + setMinimumHeight(22); +} + +void GraphBase::make_graph(int w, int h, QPainter *p) +{ + float ratio = h; + + int hsize = procview->history.size(); + int start = hsize - w; + + if (start < 0) + start = 0; + + QPolygon pa(hsize - start); // QVector pa(npts); + + int idx = 0; + for (int i = start; i < procview->history.size(); i++, idx++) + { + SysHistory *hist = procview->history[i]; + // printf("[%d] hist =%f\n",i,hist->load_cpu); + pa[idx] = QPoint(idx, h - 1 - (int)(hist->load_cpu * ratio)); + } + + if (h == official_height) + { + history_start_idx = start; // for MousePointer!! + npoints = idx; // printf("x npoints=%d \n",npoints); + } + + // draw scale lines + p->setPen(QColor(0, 210, 100)); // p.setPen(QColor(0,70,54)); + p->drawPolyline(pa); + + dirty = false; +} + +void IO_Graph::make_graph(int w, int h, QPainter *p) +{ + // p->fillRect(0,0,w,h,QBrush(Qt::black)); + // p->fill(Qt::black); + float ratio = 1.3; // test + + int hsize = procview->history.size(); + int start = hsize - w; + + if (start < 0) + start = 0; + + p->setPen(QColor(80, 90, 254)); // BLUE color + + int idx = 0; + for (int i = start; i < procview->history.size(); i++, idx++) + { + SysHistory *hist = procview->history[i]; + // printf("[%d] hist =%f\n",i,hist->load_cpu); + p->drawLine(idx, h - 1, idx, h - 1 - (int)(hist->load_io * ratio)); + } + + // if(h==official_height) // jump not init!!! + { + history_start_idx = start; // for MousePointer!! + npoints = idx; // printf("x npoints=%d \n",npoints); + } +} + +void GraphBase::drawGraphOnPixmap() +{ + // + if (rotate_str[++rotate_idx] == 0) + rotate_idx = 0; + rotate_char = rotate_str[rotate_idx]; + + // if(isVisible()==false) return; + if (size() != pixmap.size()) + { + /// printf("icon_pm changed!!\n"); + pixmap = QPixmap(size()); // resize(w, h); + pixmap.setMask(QBitmap()); // clear the mask for drawing + } + + QPainter p(&pixmap); + p.fillRect(rect(), QBrush(Qt::black)); // p.fill(Qt::black); + + make_graph(width(), height(), &p); +} + +// DRAFT CODE !!! +void Infobar::paintEvent(QPaintEvent *e) +{ + // printf("Infobar()::paintEvent\n"); + QRect ur = e->rect(); // update rectangle + QPainter p(this); + + // full re-draw! by update(); ??? + // if( ur.width()==width() and ur.height()==height() ) + { + /// drawGraphOnPixmap(); + } + + p.drawPixmap(ur, pixmap, ur); + + return; + // drww VCursor + if (vcursor.enable) + { + int px = vcursor.px; + /// p.setPen(QColor(80,195,80)); + /// p.drawLine (px,0,px,height()); + } +} + +void GraphBase::paintEvent(QPaintEvent *e) +{ + QRect ur = e->rect(); // update rectangle + QPainter p(this); + + // full re-draw! by update(); + if (ur.width() == width() and ur.height() == height()) + { + drawGraphOnPixmap(); + } + + p.drawPixmap(ur, pixmap, ur); + + return; + + // if(vcursor.enable); + // ------- Cursor testing --------- + p.setPen(QColor(250, 159, 5)); + int rel_x = x(); + p.drawLine(vcursor.px - rel_x, 0, vcursor.px - rel_x, height()); +} + +/* DEL +void IO_Graph::paintEvent ( QPaintEvent *e ) +{ + GraphBase::paintEvent (e); +} */ + +// TODO: 1.sort 2. time(?) +QString doHistory(SysHistory *sysh) +{ + QString str; + Procinfo *p; + int max = 0; + + char buf[128]; + // sprintf(buf,"miniHistory /* %.02f%%",sysh->load_cpu*100); + sprintf(buf, "miniHistory CPU"); + str += QString::fromLatin1(buf); + + // void linearize_tree(QVector *ps, int level, int prow, + // bool + // hide) + // qsort(ps->data(), ps->size(), sizeof(Procinfo *),(compare_func) + // compare_backwards); + + foreach (p, sysh->procs) + { + if (p->pcpu == 0) + continue; + sprintf(buf, " (%.01f%%)", p->pcpu); + str += "\n" + p->command + QString::fromLatin1(buf); + } + + return str; +} + +// TODO: name change! +QString GraphBase::doHistoryTXT(SysHistory *sysh) +{ + QString str; + Procinfo *p; + int max = 0; + + char buf[128]; + // sprintf(buf,"miniHistory /* %.02f%%",sysh->load_cpu*100); + sprintf(buf, "%%CPU miniHistory test"); + str += QString::fromLatin1(buf); + foreach (p, sysh->procs) + { + if (p->pcpu == 0) + continue; + sprintf(buf, " (%.02f%%)", p->pcpu); + str += "\n" + p->command + QString::fromLatin1(buf); + } + return str; +} + +void GraphBase::mouseMoveEvent(QMouseEvent *e) +{ + int x = 0, y = 0; + int half_height = height() / 2; + int dy; + int gap; + + px = e->pos().x(); // x in Infobar + py = e->pos().y(); // y in Infobar + + dy = py - half_height; // + dy /= 2; + + // printf("px=%d py=%d y=%d h=%d , dy=%d\n",px,py,y,height(),dy); + // gap=infobox->width() + px - width(); + + int i; + int setinfo = 0; + + // printf("procview npoints=%d px=%d\n",npoints,px); + QString text; + int idx = px + history_start_idx; + // if(pxhistory.size() and idx >= 0) + { + // printf("procview idx=%d px=%d\n",idx,px); + text += doHistoryTXT(procview->history[idx]); + } + else + { + // text ="xxx"; + } + + if (setinfo == 0) + infobox->setText(text); + + QPoint p = mapTo(qps, e->pos()); //?? + + // infobox->move(a.x()+16,a.y()+4); + // infobox->setPos(p.x()+16,p.y()+4); + infobox->setPos(); + + // PROBLEM : leaving old vcursor ... + vcursor.px = p.x(); + vcursor.py = p.y(); + int rel_x = GraphBase::x(); + update(p.x() - 5 - rel_x, 0, p.x() + 5 - rel_x, + height()); // only Vcursor update +} + +// TODO: 1.sort 2. time +QString IO_Graph::doHistoryTXT(SysHistory *sysh) +{ + QString str; + Procinfo *p; + int max = 0; + + char buf[64], mem_str[64]; + // sprintf(buf,"miniHistory /* %.02f%%",sysh->load_cpu*100); + sprintf(buf, "miniHistory IO"); + str += QString::fromLatin1(buf); + + foreach (p, sysh->procs) + { + if (p->io_read_KBps == 0 and p->io_write_KBps == 0) + continue; + buf[0] = 0; + + str += "\n" + p->command; + // str+="\n"+ p->command + QString::fromLatin1(buf); + str += " ("; + + if (p->io_read_KBps) + { + mem_string_k(p->io_read_KBps, mem_str); + sprintf(buf, "%s/s read", mem_str); + str += QString::fromLatin1(buf); + } + + if (p->io_write_KBps) + { + mem_string_k(p->io_write_KBps, mem_str); + sprintf(buf, " %s/s write", mem_str); + str += QString::fromLatin1(buf); + } + str += ")"; + } + return str; +} + +void GraphBase::leaveEvent(QEvent *) { infobox->hide(); } + +/* +void IO_Graph::mouseMoveEvent ( QMouseEvent *e ) { + GraphBase::mouseMoveEvent(e); +} */ + +void GraphBase::mousePressEvent(QMouseEvent *e) {} + +Infobar::Infobar(QWidget *parent, Procview *pv) : QFrame(parent) +{ + procview = pv; + official_height = 32; + + // setCursor ( QCursor(Qt::CrossCursor) ) ; + { + npoints = 0, peak = 0, h_index = 0, dirty = true; // + + hist_size = 1280; + history = new float[hist_size]; + + // setBackgroundRole (QPalette::WindowText); + setAutoFillBackground(false); + ////check setAttribute(Qt::WA_OpaquePaintEvent); + // setFrameShape(QFrame::Panel); + setFrameShadow(QFrame::Sunken); + setMinimumHeight(official_height); + // setSizePolicy ( QSizePolicy::Expanding, QSizePolicy::Fixed); + // setStyleSheet("QFrame { background-color: yellow }"); + x_cpu = new w_cpu(); + x_cpu->setParent(this, pv); + x_mem = new w_mem(); + x_mem->setParent(this, pv); + x_swap = new w_swap(); + x_swap->setParent(this, pv); + x_utime = new w_utime(); + x_utime->setParent(this, pv); + x_load_avg = new w_load_avg(); + x_load_avg->setParent(this, pv); + + wlist.append(x_cpu); + wlist.append(x_mem); + wlist.append(x_swap); + wlist.append(x_utime); + wlist.append(x_load_avg); + + QWidget::setMouseTracking(true); + } // + + int i = 0; + // is_vertical = Qps::vertical_cpu_bar; //DEL + + // cpubar=new subcpuRack(parent,pv); + + /* m_popup = new QMenu("popup",this); + QAction *act=new QAction("Under Development",this); + act->setDisabled(true); + m_popup->addAction(act); + */ +} + +Infobar::~Infobar() { delete[] history; } + +// a System's Info bar +Infobar2::Infobar2(QWidget *parent, Procview *pv) +{ + // procview=pv; //*** + official_height = 35; + // setAutoFillBackground ( false ); + // setAttribute(Qt::WA_OpaquePaintEvent); + // setFrameShape(QFrame::Panel); + setFrameShadow(QFrame::Sunken); + // setMinimumHeight(official_height); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + // if(layout()) delete layout(); + + QBoxLayout *layout = new QHBoxLayout(); + + setLayout(layout); + + layout->setSpacing(1); // betweeen gap + layout->setMargin(0); + + // BasicInfo *basic=new BasicInfo(this,pv); + // layout->addWidget(basic); + + QVBoxLayout *vlayout = new QVBoxLayout(); + vlayout->setSpacing(1); // betweeen gap + vlayout->setMargin(0); + + layout->addLayout(vlayout); + // layout->setContentsMargins (1,1,1,1); + // layout->setContentsMargins ( int left, int top, int right, int bottom + // ); + + // GraphBase *gp=new GraphBase(this,pv); // %CPU + basic = new Infobar(this, pv); + vlayout->addWidget(basic); + + // IO_Graph + io_graph = new IO_Graph(this, pv); // %IO + vlayout->addWidget(io_graph); + // basic-hide(); + // io_graph->hide(); + m_popup = new QMenu("popup", this); + QAction *act = new QAction("Under Development", this); + act->setDisabled(true); + m_popup->addAction(act); +} + +void Infobar2::updatePixmap() +{ + // NEED SOLUTION!!!!!!!! draw graph before exposed? + if (isVisible()) + { + basic->drawGraphOnPixmap(); + io_graph->drawGraphOnPixmap(); + } +} + +// old style : relative method +// add value to the history, updating peak. +// USING +void Infobar::add_history_point(unsigned int value) +{ + // simul circular buffer + history[h_index++] = value; // float + if (h_index >= hist_size) + h_index = 0; + if (npoints < hist_size) + npoints++; +} + +// BACKUP: add value to the history, updating peak. +void Infobar::add_history_point2(float value) +{ + static float v[3] = {0, 0, 0}; + static float last_val = 0; + + float f; + f = last_val - value; + // if (f > 0) + // value+=f/1.15; // slow up ,slow down + // else + value += f / 1.5; // slow up ,slow down + history[h_index++] = value; + if (h_index >= hist_size) + h_index = 0; + if (npoints < hist_size) + npoints++; + if (value > peak) + peak = value; + else + { + peak = 0; // no negative values + for (int i = 0; i < npoints; i++) + if (history[i] > peak) + peak = history[i]; + } + // printf("hist_size=%d h_index=%d val=%f\n",hist_size,h_index,value); + + last_val = value; +} + +// return updated pixmap for use as an icon +QPixmap *Infobar::make_icon(int w, int h) +{ + if (w != icon_pm.width() or h != icon_pm.height()) + { + /// printf("icon_pm changed!!!!!!!!!!!!!!!!!!11\n"); + icon_pm = QPixmap(w, h); // pm.resize(w, h); + icon_pm.setMask(QBitmap()); // remove the mask for drawing + } + QPainter pt(&icon_pm); + pt.fillRect(0, 0, w, h, QBrush(Qt::black)); + + if (0) + { + /* int thick=h/10; + int bottom=h/4; + + pt.setClipRect(); + pt.translate(); */ + } + + make_graph(w, h, &pt, true); + return &icon_pm; +} + +void Infobar::mousePressEvent(QMouseEvent *e) +{ + emit clicked(); + if (e->button() == Qt::LeftButton) + { + vcursor.enable = true; + return; + } + + if (e->button() == Qt::RightButton) + m_popup->popup(e->globalPos()); +} + +// only works if mouse cursor in this area +void Infobar::mouseMoveEvent(QMouseEvent *e) +{ + int x = 0, y = 0; + int half_height = height() / 2; + int dy; + int gap; + + px = e->pos().x(); // x in Infobar + py = e->pos().y(); // y in Infobar + + dy = py - half_height; // + dy /= 2; + + // printf("px=%d py=%d y=%d h=%d , dy=%d\n",px,py,y,height(),dy); + // gap=infobox->width() + px - width(); + + int i; + int setinfo = 0; + for (i = 0; i < wlist.size(); i++) + if (wlist[i]->intersect(px, py)) + { + setinfo = 1; + infobox->setText(wlist[i]->info()); + break; + } + + // printf("procview npoints=%d px=%d\n",npoints,px); + QString text; + int idx = px + history_start_idx; + // if(pxhistory.size() and idx >= 0) + { + // printf("procview idx=%d px=%d\n",idx,px); + text += doHistory(procview->history[idx]); + } + else + { + text = ""; + } + + if (setinfo == 0) + infobox->setText(text); + + QPoint p = mapTo(qps, e->pos()); //?? + + // infobox->move(a.x()+16,a.y()+4); + // infobox->setPos(p.x()+16,p.y()+4); + infobox->setPos(); + + // vcursor.px=p.x(); + // update(p.x()-5,0,p.x()+5,height()); +} + +void Infobar::enterEvent(QEvent *) +{ + // infobox->hide(); +} + +void Infobar::leaveEvent(QEvent *) +{ + + infobox->hide(); + + return; + /* + if(controlbar and controlbar->isHidden()) + { + // controlbar->show(); + setMinimumHeight(official_height); + } */ +} + +// DRAFT CODE +// draw the load graph on the internal pixmap, if needed +// called by +// 1.make_icon(int w, int h) +// 2.paintEvent() +// 3.update_load() +void Infobar::make_graph(int w, int h, QPainter *p, bool test) +{ + // QPainter p(this); + // p.setBackgroundMode (Qt::OpaqueMode); + // p.setBackground(QBrush(Qt::black)); + // p->fillRect(0,0,w,h,QBrush(Qt::black)); + // p->fill(Qt::black); + float ratio = h; + int idx = 0; + + if (h == official_height) // tmp + { + ratio = h - 11; + } + + int hsize = procview->history.size(); + int start = hsize - w; + + if (start < 0) + start = 0; + p->setPen(QColor(0, 210, 100)); // p.setPen(QColor(0,70,54)); + + if (test == false) + { + QPolygon pa(hsize - start); // QVector pa(npts); + // QPolygon point_array_io(hsize-start); + + for (int i = start; i < procview->history.size(); i++, idx++) + { + SysHistory *hist = procview->history[i]; + // printf("[%d] hist =%f\n",i,hist->load_cpu); + pa[idx] = QPoint(idx, h - 1 - (int)(hist->load_cpu * ratio)); + /// point_array_io[idx] = QPoint(idx, h - 1 - + ///(int)(hist->load_io * + /// ratio)); + } + // p->setPen(QColor(100,100,250)); + ////p.setPen(QColor(0,70,54)); + // p->drawPolyline(point_array_io); + + // draw scale lines + p->drawPolyline(pa); + } + else + { + int idx = 0; + for (int i = start; i < procview->history.size(); i++, idx++) + { + SysHistory *hist = procview->history[i]; + p->drawLine(idx, h - 1, idx, h - 1 - (int)(hist->load_cpu * ratio)); + } + } + + // if(h==official_height) + { + history_start_idx = start; // for MousePointer!! + npoints = idx; // printf("x npoints=%d \n",npoints); + } + + dirty = false; +} + +void Infobar::resizeEvent(QResizeEvent *e) +{ + // static int first=0; if(first==0){ drawPixmap(); first=1;} + drawGraphOnPixmap(); +} +subcpuRack::subcpuRack(QWidget *p, Procview *pv) : QWidget(p) +{ + parent = p; + procview = pv; + // setWindowFlags(Qt::FramelessWindowHint | Qt::ToolTip); + // setWindowFlags(Qt::FramelessWindowHint |Qt::Tool ); + setAttribute(Qt::WA_OpaquePaintEvent); + QPalette pal; + pal.setColor(QPalette::Window, QColor(0, 0, 0)); + setPalette(pal); + QWidget::setMouseTracking(true); + setMinimumHeight(12); +} + +void subcpuRack::refresh() {} + +void subcpuRack::mousePressEvent(QMouseEvent *e) +{ + m_popup->popup(e->globalPos()); + // hide(); +} + +// DRAFT CODE !!! +void subcpuRack::paintEvent(QPaintEvent *e) +{ + + // static QPaint *p=// QPainter *p=new QPainter(this); + QPainter p(this); + char buff[128]; + int w; + int width; + int x, y; + int i; + // QRect cr = contentsRect(); + // QRect cr = p->viewport(); + QRect cr = p.window(); // rect. + + p.fillRect(rect(), QBrush(Qt::black)); + + // p->setPen(lineColor); + p.setPen(QColor(50, 50, 50)); + p.drawLine(0, 0, QWidget::width(), 0); + // p.fillRect(cr,QBrush(QColor(255,255,255,50))); + + unsigned long total = 0, user = 0, system = 0, idle = 0, nice = 0, wait = 0; + // if(procview->num_cpus>=4) // temporaly... + // else + int cpu_n = procview->num_cpus; + w = 2 + pf_write(&p, 2, 2, "SUB CPU"); + width = w; +} + +// GraphDisplay, miniDisplay + +PDisplay::PDisplay(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->setMargin(0); + vlayout->setSpacing(1); + setLayout(vlayout); + + // return; + // ** setBackgroundColor(color); ** + QPalette pal; + pal = palette(); + setAutoFillBackground(true); + // setBackgroundRole (QPalette::WindowText); + pal.setColor(backgroundRole(), QColor(80, 80, 80)); + // palette.setColor(QPalette::Window, QColor(0,0,100)); + setPalette(pal); +} +// a BAR in a RACK +// + +Infobar2 *PDisplay::addSystem(Procview *pv) +{ + // Infobar* bar= new Infobar(this,pv); + Infobar2 *bar2 = new Infobar2(this, pv); + layout()->addWidget(bar2); + + /// pv->read_system(); // + + if (0 and pv->num_cpus > 9) + { + QWidget *rack; + rack = new subcpuRack(this, pv); + layout()->addWidget(rack); + } + return bar2; //->basic; +} diff --git a/src/infobar.h b/src/infobar.h new file mode 100644 index 0000000..63c0c6c --- /dev/null +++ b/src/infobar.h @@ -0,0 +1,215 @@ +// infobar.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +#ifndef INFOBAR_H +#define INFOBAR_H + +#include "proc.h" + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#include +#endif + +class subcpuRack : public QWidget +{ + Q_OBJECT + public: + subcpuRack(QWidget *parent, Procview *p); + void refresh(); + // void update_load(); + // void show_and_hide(bool, QWidget *, QWidget *); + // signals: void config_change(); + protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + + private: + // List wlist; + QWidget *parent; + Procview *procview; // temp + bool is_vertical; +}; + +class gwidget +{ + public: + gwidget() { x = 0, y = 0, width = 0, height = 0; }; + gwidget(QWidget *p) { parent = p; }; + virtual void draw(QPainter *p) { printf("gwidget\n"); }; + virtual void setPosition(int parent_width, int parent_height){}; + virtual const char *info() { return NULL; }; + void setParent(QWidget *p, Procview *procv) + { + parent = p; + procview = procv; + }; + void resize(int x_, int y_, int w, int h) + { + x = x_; + y = y_; + width = w; + height = h; + }; + bool intersect(int cx, int cy) + { + cx = cx - x; + cy = cy - y; + if (cx > 0 and cx < width) + if (cy > 0 and cy < height) + return true; + return false; + } + + int xpluswidth() { return x + width; }; + Procview *procview; // temp + + protected: + int x; + int y; + int width; + int height; + QRect rect; + QWidget *parent; +}; + +class Infobar : public QFrame +{ + Q_OBJECT + public: + Infobar(){}; + + Infobar(QWidget *parent, Procview *); + ~Infobar(); + + void configure(); // respond to configuration change + + QPixmap *make_icon(int w, int h); + // DEL QPixmap *load_icon(int w, int h) { return make_icon(w, h); } + void refresh(); // update(); + void update_load(); + void drawGraphOnPixmap(); + bool swaplim_exceeded(); + void show_and_hide(bool, QWidget *, QWidget *); + void showup(); +signals: + void clicked(); + void config_change(); + + protected: + void paintEvent(QPaintEvent *); + void mousePressEvent(QMouseEvent *); + void mouseMoveEvent(QMouseEvent *); + void leaveEvent(QEvent *); + virtual void enterEvent(QEvent *event); + void hideEvent(QHideEvent *event); + void resizeEvent(QResizeEvent *e); + + void add_history_point(unsigned value); + void add_history_point2(float value); + void make_graph(int w, int h, QPainter *p, bool test = false); + Procview *procview; + + private: + // history points are stored in fixed point, scaled by history_scale + static const unsigned int history_scale = 256; + static const int time_dist = 60; // time between vertical lines (seconds) + + QList wlist; + int hist_size; // history buffer size DEL + int npoints; // nr of history points currently remembered + float *history; // (circular) history buffer DEL + int h_index; // next history index to use + float peak; // largest value in history + + bool dirty; // history changed since pixmap was drawn + QPixmap pixmap; + QPixmap icon_pm; + + int px, py; // pointer position + + bool is_vertical; + int official_height; +}; + +class GraphBase : public QWidget +{ + Q_OBJECT + public: + GraphBase(QWidget *parent, Procview *); + void drawGraphOnPixmap(); + + protected: + void paintEvent(QPaintEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *); + virtual void leaveEvent(QEvent *); + // virtual void enterEvent ( QEvent * event ) ; + // void resizeEvent ( QResizeEvent * e ); + + virtual void make_graph(int w, int h, QPainter *p); + virtual QString doHistoryTXT(SysHistory *sysh); + + Procview *procview; + int official_height; + int npoints; // nr of history points currently remembered + private: + // history points are stored in fixed point, scaled by history_scale + static const unsigned int history_scale = 256; + + int hist_size; // history buffer size DEL + float *history; // (circular) history buffer DEL + int h_index; // next history index to use + float peak; // largest value in history + + bool dirty; // history changed since pixmap was drawn + QPixmap pixmap; + QPixmap icon_pm; + + int px, py; // pointer position +}; + +class IO_Graph : public GraphBase +{ + Q_OBJECT + public: + IO_Graph(QWidget *parent, Procview *); + + protected: + void make_graph(int w, int h, QPainter *p); + QString doHistoryTXT(SysHistory *sysh); + + private: +}; + +// class Infobar2 : public Infobar +class Infobar2 : public QFrame +{ + Q_OBJECT + public: + Infobar2(QWidget *parent, Procview *); + void updatePixmap(); // TEMP + QPixmap *load_icon(int w, int h) { return basic->make_icon(w, h); } + void refresh(); // update(); + Infobar *basic; + IO_Graph *io_graph; + + private: + int official_height; +}; + +class PDisplay : public QWidget +{ + Q_OBJECT + public: + PDisplay(QWidget *parent); + Infobar2 *addSystem(Procview *p); +}; + +#endif // INFOBAR_H diff --git a/src/lookup.cpp b/src/lookup.cpp new file mode 100644 index 0000000..3ce5d73 --- /dev/null +++ b/src/lookup.cpp @@ -0,0 +1,267 @@ +// lookup.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +// This module implements asynchronous address->hostname lookup. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lookup.h" +#include "svec.cpp" + +// declarations of static members +char *Lookup::argv0; +int Lookup::maxtitlelen; + +UintQueue::UintQueue() { first = last = 0; }; + +void UintQueue::enqueue(unsigned x) +{ + if (last < 0) + { + // make room for inserting more elements + int step = qMax(first + 1, 8); + queue.setSize(first + step + 1); + for (int i = first; i > last; i--) + queue[i + step] = queue[i]; + first += step; + last += step; + } + queue.set(last--, x); +} + +unsigned UintQueue::dequeue() +{ + if (isEmpty()) + fatal("UintQueue: queue empty"); + return queue[first--]; +} + +// constructor for node head +Hostnode::Hostnode() : next(this), prev(this) {} + +// create a new cache node, initialized with null string +Hostnode::Hostnode(unsigned addr) : ipaddr(addr), next(0), prev(0) {} + +// must be called on the head of the list +void Hostnode::moveToFront(Hostnode *node) +{ + if (next != node) + { + Hostnode *p = node->prev, *n = node->next; + p->next = n; + n->prev = p; + node->next = next; + node->prev = this; + next->prev = node; + next = node; + } +} + +// must be called on the head of the list +void Hostnode::deleteLast() +{ + Hostnode *nuke = prev; + prev = nuke->prev; + prev->next = this; + delete nuke; +} + +// must be called on the head of the list +void Hostnode::insertFirst(Hostnode *node) +{ + node->prev = this; + node->next = next; + next->prev = node; + next = node; +} + +Lookup::Lookup() // : hostdict(17) +{ + sockfd = -1; // no lookup helper is running + readsn = writesn = 0; + outstanding = 0; +} + +// empty destructor, workaround for gcc bug +Lookup::~Lookup() {} + +// look up host name (addr is in host byte order) +// a null name means it is been looked up (signal will be sent when done) +QString Lookup::hostname(unsigned addr) +{ + // first look in our cache + Hostnode *hn = hostdict.value(addr, NULL); + if (hn) + { + hostlru.moveToFront(hn); + } + else + { + hn = new Hostnode(addr); + if (hostdict.count() >= hostname_cache_size) + { + // remove least recently used item + hostdict.remove(hostlru.last()->ipaddr); + hostlru.deleteLast(); + } + hostlru.insertFirst(hn); + hostdict.insert(addr, hn); + // if(hostdict.count() > hostdict.size() * 3) + // hostdict.resize(hostdict.count()); + if (addr == 0) + hn->name = "*"; + else + request(addr); + } + return hn->name; +} + +void Lookup::request(unsigned addr) +{ + addrqueue.enqueue(addr); + if (sockfd < 0) + { + int socks[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, socks); + // launch a new helper + signal(SIGCHLD, SIG_IGN); // Linux does automatic child reaping, nice + switch (fork()) + { + case -1: // error + return; // don't bother, we'll try again next time + case 0: // child + close(socks[0]); + do_child(socks[1]); + break; + default: // parent + close(socks[1]); + sockfd = socks[0]; + readsn = new QSocketNotifier(sockfd, QSocketNotifier::Read, this); + connect(readsn, SIGNAL(activated(int)), SLOT(receive_result(int))); + writesn = new QSocketNotifier(sockfd, QSocketNotifier::Write, this); + connect(writesn, SIGNAL(activated(int)), SLOT(send_request(int))); + break; + } + } + writesn->setEnabled(true); +} + +// the child helper process loop +void Lookup::do_child(int fd) +{ + setproctitle("qps-dns-helper"); + // close unused fds + for (int i = 0; i < fd; i++) + close(i); + for (;;) + { + unsigned addr; + int ret = read(fd, &addr, sizeof(addr)); + if (ret <= 0) + { + _exit(0); // connection closed + } + struct hostent *h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET); + char buf[256]; + if (!h) + { + unsigned a = htonl(addr); + sprintf(buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff, + (a >> 8) & 0xff, a & 0xff); + } + else + { + strncpy(buf, h->h_name, sizeof(buf)); + } + // if parent died, we'll get SIGPIPE here and terminate + // automatically + write(fd, &addr, sizeof(addr)); + int len = strlen(buf); + write(fd, &len, sizeof(len)); + write(fd, buf, len); + } +} + +// slot: receive result from helper +void Lookup::receive_result(int) +{ + unsigned addr; + int len; + char buf[256]; + + if (read(sockfd, &addr, sizeof(addr)) <= 0 || + read(sockfd, &len, sizeof(len)) <= 0 || read(sockfd, buf, len) <= 0) + { + // helper has died + delete readsn; + delete writesn; + close(sockfd); + sockfd = -1; + return; + } + buf[len] = '\0'; + Hostnode *hn = hostdict.value(addr, NULL); + if (!hn) + return; // gone from cache + hn->name = buf; + emit resolved(addr); + + outstanding--; + // if there is nothing more in the queue, kill the helper + if (addrqueue.isEmpty() && outstanding == 0) + { + close(sockfd); + sockfd = -1; + delete readsn; + delete writesn; + } +} + +// slot: send request to the helper +void Lookup::send_request(int) +{ + if (addrqueue.isEmpty()) + { + writesn->setEnabled(false); + return; + } + + unsigned addr = addrqueue.dequeue(); + if (write(sockfd, &addr, sizeof(addr)) < 0) + { + // This shouldn't happen, try to repair it anyway + addrqueue.enqueue(addr); + delete readsn; + delete writesn; + close(sockfd); + sockfd = -1; + return; + } + outstanding++; +} + +// register and measure the space for modifying the visible command line +void Lookup::initproctitle(char **argv, char **envp) +{ + argv0 = argv[0]; + while (*envp) + envp++; + maxtitlelen = envp[-1] + strlen(envp[-1]) - argv0 - 2; +} + +// set the process title (idea snarfed from sysvinit (thanks Miquel) and +// refined by peeking into wu-ftpd) +void Lookup::setproctitle(const char *txt) +{ + memset(argv0, 0, maxtitlelen); + strncpy(argv0, txt, maxtitlelen - 1); +} diff --git a/src/lookup.h b/src/lookup.h new file mode 100644 index 0000000..572329f --- /dev/null +++ b/src/lookup.h @@ -0,0 +1,88 @@ +// lookup.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef LOOKUP_H +#define LOOKUP_H + +#include +#include +#include "svec.h" +#include + +// queue (fifo) of unsigned ints (inet addresses) +class UintQueue +{ + public: + UintQueue(); + + bool isEmpty() { return first == last; }; + void enqueue(unsigned x); + unsigned dequeue(); + + private: + Svec queue; + int first; // index of first item in queue + int last; // index where next item will be put (< first) +}; + +// list node for LRU cache of hostnames: +// This list is doubly-linked circular (to reduce the amount of special cases), +// with a head node that carries no data. In addition, each node is +// entered into a hash table for rapid lookup +class Hostnode +{ + public: + Hostnode(); + Hostnode(unsigned addr); + + // these must be called on the head of the list + void moveToFront(Hostnode *node); + void deleteLast(); + void insertFirst(Hostnode *node); + Hostnode *last() { return prev; }; + + QString name; + unsigned ipaddr; // hash key + Hostnode *next, *prev; +}; + +class Lookup : public QObject +{ + Q_OBJECT + public: + Lookup(); + ~Lookup(); + QString hostname(unsigned addr); + + static void initproctitle(char **argv, char **envp); + +signals: + void resolved(unsigned addr); + + protected slots: + void receive_result(int); + void send_request(int); + + protected: + void request(unsigned addr); + void do_child(int fd); + + static void setproctitle(const char *txt); + + static char *argv0; + static int maxtitlelen; + + QHash hostdict; + Hostnode hostlru; // head of circular list + UintQueue addrqueue; // queue of addresses to lookup + int outstanding; // number of outstanding requests + int sockfd; // -1 if no helper process is running + + QSocketNotifier *readsn, *writesn; + + static const unsigned hostname_cache_size = 400; // max names to cache +}; + +#endif // LOOKUP_H diff --git a/src/message.ui b/src/message.ui new file mode 100644 index 0000000..d3c5583 --- /dev/null +++ b/src/message.ui @@ -0,0 +1,37 @@ + + ExecWindow + + + + 0 + 0 + 309 + 162 + + + + Qps + + + + + + + + + + + + + + Ok + + + + + + + + + + diff --git a/src/misc.cpp b/src/misc.cpp new file mode 100644 index 0000000..2c2ee94 --- /dev/null +++ b/src/misc.cpp @@ -0,0 +1,1215 @@ + + +#include "misc.h" +#include "proc.h" + +extern SearchBox *search_box; +extern bool flag_show_thread; +extern int flag_thread_ok; +extern bool flag_devel; + +#include "../icon/x1.xpm" +#include "../icon/x2.xpm" + +#include +#include +#include + +// 300% faster than glibc (by fasthyun@magicn.com) +int x_atoi(const char *sstr) +{ + const char *str = sstr; + int val = 0; + while (*str) + { + val *= 10; + val += *str - 48; + str++; + } + return val; +} + +#include +QWidget *getQpsWidget() +{ + QWidgetList list = QApplication::topLevelWidgets(); + for (int i = 0; i < list.size(); ++i) + { + QWidget *w = list.at(i); + if (w->objectName() == "qps") + return w; + } + return 0; +} + +#include +#include +/* + ver 0.2 + A simplified vsscanf implementation from Internet + + %S can read char_string_has_spaces. + ex. "(%S)" == (foo bar) or (foo bar ) + + + %s %d %ld %u %x? %lu %*s %f + %S: (%S) + + Only recognizes %s %f,%d, %u, %ld, %lu, %lg and whitespace. ...terminates + scanning. + space* same tab* + + */ + +// Description: +// mini_sscanf() == strstr() + sscanf() + %S +// +// there is test_sample_code in init_misc() +// fixed : invalid conversion from ‘const char*’ to ‘char*’ +// +// TOO COMPLEX !!!!!! -> simple code +char *strchar(const char *s, int k); +int mini_sscanf(const char *s1, const char *fmt, ...) +{ + + va_list va; + va_start(va, fmt); + char *s = (char *)s1; // *** for gcc 4.4 + char *p; + int k = 0; // count + while (*fmt and *s) // if (*fmt==0 or *s==0) break; + { + if (*fmt == '%') + { + int n; + if (fmt[1] == 'S') + { + // if(fmt[2]!=0) + p = strchr(s, fmt[2]); // get the token + int len = p - s; + p = va_arg(va, char *); + strncpy(p, s, len); + p[len] = 0; + s += len + 1; + fmt += 3; + k++; + } + else if (fmt[1] == 's') // extract string + { + // printf("%s : %%s =%s \n",__FUNCTION__,s); + k += sscanf(s, "%s%n", va_arg(va, char *), &n); + s += n; + fmt += 2; + } + else if (fmt[1] == '*' && fmt[2] == 's') // skip + { + p = strchr(s, ' '); // 0x20,0x00,0x0A, 123 435 54054 + if (p == 0) + { + // printf("%s : %c [%s] + //\n",__FUNCTION__,fmt[1],s); + break; + } + s = p; + fmt += 3; + } + else if (fmt[1] == 'c') + { + p = va_arg(va, char *); + // printf("%s : %c [%c] + //\n",__FUNCTION__,fmt[1],*s); + *p = *s; + s++; + k++; + fmt += 2; + } + else if (fmt[1] == 'f') + { + k += sscanf(s, "%f%n", va_arg(va, float *), &n); + s += n; + fmt += 2; + } + else if (fmt[1] == 'd') + { + k += sscanf(s, "%d%n", va_arg(va, int *), &n); + s += n; + fmt += 2; + } + else if (fmt[1] == 'u') + { + k += sscanf(s, "%u%n", va_arg(va, int *), &n); + s += n; + fmt += 2; + } + else if (fmt[1] == 'l' && fmt[2] == 'd') + { + k += sscanf(s, "%ld%n", va_arg(va, long *), &n); + s += n; + fmt += 3; + } + else if (fmt[1] == 'l' && fmt[2] == 'u') + { + k += sscanf(s, "%lu%n", va_arg(va, long *), &n); + s += n; + fmt += 3; + } + else if (fmt[1] == 'l' && fmt[2] == 'g') + { + k += sscanf(s, "%lg%n", va_arg(va, double *), &n); + s += n; + fmt += 3; + } + else + { + printf("%s: unsupported" + " format '%c'", + __FUNCTION__, fmt[1]); + break; + } + } + else + { + if (isspace(*fmt)) // isblank + { + while (isspace(*s)) + s++; + fmt++; + } + else + { + char sstr[32]; + int n = strcspn(fmt, " %\n"); // find delimiters noSEGFAULT + strncpy(sstr, fmt, n); + sstr[n] = 0; + p = strstr(s, sstr); + if (p == 0) + { + if (0 and flag_devel) + printf("%s : can't found [%s] " + "in %s\n", + __FUNCTION__, sstr, s); + break; + } + s = p + n; + fmt += n; + // ddd at %s, at%s + // printf("opp_vsscanf: unexpected ""char in + // format '%s'",fmt); + // break; + // return k; + } + } + + if (*fmt == '\0' || *fmt == '#' or *s == 0) + break; + } + va_end(va); + return k; +} + +#include +#include +#include +#include + +// return /proc/* file size +int fsize(char *fname) +{ + int size = 0; + if (0) + { + // !!! important !! not works with [/proc] , because [/proc/*] + // always zero + int fd = open(fname, O_RDONLY); + if (fd < 0) + return -1; + size = lseek(fd, 0, SEEK_END); + printf("size=%d\n", size); + close(fd); + return size; + } + + int buf[1024]; + int r; + int fd = open(fname, O_RDONLY); + if (fd < 0) + return -1; + do + { + r = read(fd, buf, 1024); + size += r; + // printf("r_size=%d\n",size); + } while (r); + // printf("size=%d\n",size); + close(fd); + return size; +} + +// sleep for mili seconds +void msleep(long msec) +{ +#ifdef LINUX + timespec req, remain; + timeval tv; + req.tv_sec = 0; + req.tv_nsec = msec * 1000000; + // int nanosleep(const struct timespec *req, struct timespec + // *remain) + /*while (nanosleep(&req, &req)) + { + if (errno != EINTR) + break; + } */ + + tv.tv_sec = 0; + tv.tv_usec = msec * 1000; + select(0, NULL, NULL, NULL, &tv); + return; +#endif +} + +void mem_string_k(int kbytes, char *buf) +{ + if (kbytes >= 1024) + { + int mb = kbytes >> 10; + if (mb >= 1024 * 100) + sprintf(buf, "%uGB", mb >> 10); + else + sprintf(buf, "%uMB", mb); + } + else + sprintf(buf, "%uKB", kbytes); +} +void mem_string(int kbytes, char *buf) +{ + if (kbytes >= 1024) + { + int meg = kbytes >> 10; + if (meg >= 1024 * 100) + sprintf(buf, "%uGb", meg >> 10); + else + sprintf(buf, "%uMb", meg); + } + else + sprintf(buf, "%uKb", kbytes); +} + +// DEL +CheckMenu::CheckMenu(QWidget *parent) : QMenu(parent) {} + +// ??? +CrossBox::CrossBox(const char *text, QWidget *parent) : QCheckBox(text, parent) +{ +} + +void CrossBox::drawButton(QPainter *p) +{ + ///// QCheckBox::drawButton(p); +} + +TBloon::TBloon(QWidget *parent) : QLabel(parent) +{ + + return; + paren = parent; + setStyleSheet("QLabel { " + //"border-width: 1px; border-style: solid; border-color: + // rgb(150,45,100); border-radius: 5px ;" + "border-width: 1px; border-style: solid; border-color: " + "rgb(150,180,180); border-radius: 5px ;" + "background-color : rgba(0,0,0,48%); padding: 3px; color: " + "rgb(255,120,60); }"); + // COLOR orange FF5d00 + + setText(" This is unstable Alpha feature\n You maybe see a SEGFAULT..."); + resize(sizeHint()); + // parent->installEventFilter(this); + // parent->setMouseTracking(true); + hide(); + + // Construct a 1-second timeline with a frame range of 0 - 100 + timeLine = new QTimeLine(100000, this); + timeLine->setFrameRange(0, 20000); + timeLine->setCurveShape(QTimeLine::EaseOutCurve); + // timeLine->set(0, 20000); + connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(update(int))); +} + +/* +void TBloon::start_ani() +{ + +} */ + +void TBloon::update(int val) +{ + //`QPoint p=QCursor::pos(); + // qDebug("pos %d %d", p.x(),p.y()); + QPoint p2 = paren->mapFromGlobal(QCursor::pos()); + + // qDebug("wpos %d %d", p2.x(),p2.y()); + int tx = p2.x(); + int ty = p2.y(); + int sx = pos().x(); + int sy = pos().y(); + float dx = tx - sx; + float dy = ty - sy; + dx = dx / 1.35; + dy = dy / 1.35; + move(tx + dx + 8, ty + dy + 14); +} + +bool TBloon::eventFilter(QObject *obj, QEvent *event) +{ + if (event->type() == QEvent::Enter) + { + QPoint p = paren->mapFromGlobal(QCursor::pos()); + move(p); + // qDebug("Enter parent"); + timeLine->start(); + show(); + return true; + } + else if (event->type() == QEvent::Leave) + { + // qDebug("Leave parent"); + timeLine->stop(); + hide(); + return true; + } + else if (event->type() == QEvent::MouseMove) + { + QMouseEvent *me = static_cast(event); + // qDebug("Ate key press %d %d", me->x(),me->y()); + // move(me->x()+5,me->y()+4); + return false; + } + else + { + // standard event processing + return QObject::eventFilter(obj, event); + } +} + +/* +void TBloon::paintEvent( QPaintEvent * event ) +{ + QLabel::paintEvent(event); +} */ + +TFrame::TFrame(QWidget *parent) : QLabel(parent) +// TFrame::TFrame(QWidget *parent):QFrame(parent) +{ + text = "this is Tframe widget"; + // setAutoFillBackground(false); + // setGeometry(50,50,100,100); + // setAttribute(Qt::WA_OpaquePaintEvent); + // setFrameShape(QFrame::StyledPanel); + // setFrameShadow(QFrame::Sunken); + // setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + // setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + // setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum); + // setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); + + //"border-width: 1px; border-style: solid; border-color: + // rgb(150,45,100); + // border-radius: 5px ;" + setStyleSheet("QLabel { " + "border-width: 1px; border-style: solid; border-color: " + "rgb(210,50,130); border-radius: 5px ;" + "background-color : rgba(0,0,0,69%); padding: 3px; color: " + "rgb(0,255,150); }"); + // setStyleSheet( "QLabel { " "border-width: 1px; border-style: + // solid; + // border-radius: 5px ; padding: 3px; }"); + hide(); + + timeLine = new QTimeLine(2000, this); + timeLine->setFrameRange(0, 100); + connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(setValue(int))); +} + +/* +void TFrame::showText(QPoint glob_pos,QString str) +{ + + +} + + +void TFrame::move(int x,int y) +{ + + +} */ + +// QToolTip::showText(e->globalPos(), s); +void TFrame::showText(QPoint glob_pos, QString str) {} + +void TFrame::setText(QString str) +{ + text = str; + + QLabel::setText(str); + + // resize(minimumSizeHint()); + resize(sizeHint()); // **** before show !! ***** + + if (str.size() == 0) + { + hide(); + return; + } + show(); +} + +// to avoid ~ +void TFrame::draw(QPainter &p) +{ + // if(!isVisible()) return; + return; + int h = fontMetrics().height() + 3; + int w = fontMetrics().width(text) + 9; + setFixedSize(w, h); + // QFont font("Adobe Helvetica"); // helvetica & No-Antialias + // font.setPixelSize(10); + // setFont(font); + // p.drawRoundRect(rect(),10,10); + // p.fillRect ( cr,QColor(0,0,0)); + QColor bg = QColor(0, 0, 0, 120); +#if QT_VERSION < 0x040400 + p.fillRect(rect(), QColor(0, 0, 0, 120)); +#else + // p.fillRect(rect(),QColor(0,0,0,90)); + + // p.setPen(QColor(0,255,155)); // less visually obtrusive than + // black + // p.setBrush(QBrush(bg)); + // p.drawRoundedRect(0,0,w,h, 4, 4); + p.fillRect(rect(), bg); + +#endif + + p.setPen(QColor(0, 255, 155)); // less visually obtrusive than black + p.drawText(0, 0, w, h, Qt::AlignVCenter | Qt::AlignHCenter, text); +} + +void TFrame::setPos() +{ + QWidget *parent = QWidget::parentWidget(); + + QPoint p = parent->mapFromGlobal(QCursor::pos()); + + setPos(p.x(), p.y()); +} + +void TFrame::setPos(int x, int y) +{ + QWidget *parent = QWidget::parentWidget(); + int pwidth = parent->width(); + + if (width() + x > pwidth) + { + x = pwidth - width(); + } + move(x + 16, y + 4); +} + +void TFrame::moveEvent(QMoveEvent *e) +{ + // int x=e->pos().x(); + // printf("pos (%d %d) \n",e->pos().x(),e->pos().y()); + // event->oldPos(); +} + +void TFrame::setValue(int val) +{ + opacity += ((float)val / 100); + update(); +} + +void TFrame::showEvent(QShowEvent *event) +{ + opacity = 0.2; + // timeLine->start(); +} + +// Maybe SEGFAULT +void setPaletteBlend(QPalette &p) +{ + for (int i = 0; i < (QPalette::NoRole); i++) + { + QColor c = p.color(QPalette::ColorRole(i)); + c.alpha(); + } +} + +void TFrame::paintEvent(QPaintEvent *event) +{ + QLabel::paintEvent(event); + return; + + QCursor::pos(); + // if(x()+width() > ) ; + QPalette np = palette(); + setPaletteBlend(np); + + QPainter p(this); // why? + + // if(opacity>1) timeLine->stop(); // + // p.setOpacity(opacity); + // p.setOpacity(0.01); + // setWindowOpacity (0.1 ); + // QFrame::paintEvent(event); + // p.scale(0.5,0.5); + + // draw(p); + // p.setBackgroundMode (Qt::TransparentMode); + // p.setRenderHint(QPainter::Antialiasing); + // painter.translate(width() / 2, height() / 2); +} + +UFrame::UFrame(QWidget *parent) : QFrame(parent) +// TFrame::TFrame(QWidget *parent):QFrame(parent) +{ + // setAttribute(Qt::WA_OpaquePaintEvent); + // setFrameShape(QFrame::StyledPanel); + // setFrameShadow(QFrame::Sunken); + // setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Preferred); + + //"border-width: 1px; border-style: solid; border-color: + // rgb(150,45,100); + // border-radius: 5px ;" + + // setStyleSheet( "QLabel { " "border-width: 1px; border-style: + // solid; + // border-color: rgb(210,50,130); border-radius: 5px ;" + // "background-color : + // rgba(0,0,0,70%); padding: 3px; color: rgb(0,255,150); }"); + hide(); + + QVBoxLayout *vlayout = new QVBoxLayout; + QLabel *label = new QLabel("title"); + vlayout->addWidget(label); + + setLayout(vlayout); + QWidget *qps = getQpsWidget(); + if (qps) + { + int w = qps->width(); + int h = qps->height(); + printf("qps w=%d h=%d\n", qps->width(), qps->height()); + move(w / 2, h / 2); + } +} + +FaderWidget::FaderWidget(QWidget *parent) : QWidget(parent) +{ + if (parent) + startBrush = parent->palette().window(); + else + startBrush = Qt::white; + + timeLine = new QTimeLine(333, this); + timeLine->setFrameRange(1000, 0); + connect(timeLine, SIGNAL(frameChanged(int)), this, SLOT(update())); + + setAttribute(Qt::WA_DeleteOnClose); + resize(parent->size()); +} + +void FaderWidget::start() +{ + timeLine->start(); + show(); +} + +void FaderWidget::paintEvent(QPaintEvent * /* event */) +{ + QPainter painter(this); + qreal frame = timeLine->currentFrame(); + painter.setOpacity(frame / 1000.); + painter.fillRect(rect(), startBrush); + + if (frame <= 0.) + close(); +} + +void UFrame::setTitle(QString str) +{ + // QLabel::setText(str); + title = str; +} + +void UFrame::paintEvent(QPaintEvent *event) {} +/* +//void SearchBox::event_cursor_moved(QMouseEvent *e) +{ + move(e->x(),e->y()); + //printf("xxxxxxxxxxxxxk\n"); +} + +*/ + +QPixmap *letters; +int pf_height = 9; +int pf_width = 6; + +void init_xpm() +{ + letters = new QPixmap(":/icon/letters.png"); + pf_height = 9; + pf_width = 6; +} + +#ifdef LINUX +#include +#include +char strong_buff[128]; +class MyThread : public QThread +{ + public: + void run() + { + prctl(PR_SET_NAME, "thread_test123456789"); + + while (true) + { + int m = 0; + for (int i = 0; i < 20000; i++) + { + int p = rand() % 120; + strong_buff[p] = p; + } + msleep(40); + } + exec(); + } +}; +class MyThread2 : public QThread +{ + public: + void run() + { + prctl(PR_SET_NAME, "thread_test987654321"); + + while (true) + { + int m = 0; + int i; + for (i = 0; i < 100000; i++) + { + int p = rand() % 120; + // strong_buff[p]=p; + m = i * p; + } + m += i; + msleep(60); + } + exec(); + } +}; +#endif + +int pf_str_width(char *str) +{ + int len; + len = strlen(str) * pf_width; + return len; +} +int pf_char_height() { return pf_height; } + +int pf_write(QPainter *p, int x, int y, const char *str) +{ + int i, len, n; + int ch; + int sx, sy; + int x0 = x; + len = strlen(str); + for (i = 0; i < len; i++) + { + ch = str[i]; + + if (ch == '\n') + { + // y+=pf_height; + continue; + } + // if(ch>96) ch-=(97-65); + n = ch - 32; + p->drawPixmap(x, y, *letters, n * pf_width, 0, pf_width + 1, pf_height); + x += pf_width; + } + return x - x0; +} + +QPixmap *x1, *x2; +XButton::XButton(QWidget *parent) : QAbstractButton(parent) +{ + setFocusPolicy(Qt::NoFocus); + x1 = new QPixmap((const char **)x1_xpm); + x2 = new QPixmap((const char **)x2_xpm); + int w = x1->width(); + setGeometry(0, 0, w, w); +} + +void XButton::resizeEvent(QResizeEvent *p) +{ + int i, h; + i = height() % 2; + + if (i != 0) + h = height() + 1; + else + h = height(); + setFixedSize(h, h); +} + +void XButton::paintEvent(QPaintEvent *event) +{ + QPainter p(this); + int w, h, m; + + if (isDown()) + p.drawPixmap(0, 0, *x2); + else + p.drawPixmap(0, 0, *x1); + + return; +} + +SearchBox::SearchBox(QWidget *parent) : QLineEdit(parent) +{ + setToolTip("PID,COMMAND,USER..."); + left_time = 0; + setMaximumWidth(300); + setMinimumWidth(20); + + // setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Preferred); + // setContentsMargins(0,3,1,0); + // setLineWidth( 1 ); + // setMargin(2); + + /// xb=new XButton(this); + /// connect(xb, SIGNAL(clicked()), SLOT(event_xbutton_clicked())); + setFocus(Qt::ActiveWindowFocusReason); // good +} + +void SearchBox::event_cursor_moved(QMouseEvent *e) { move(e->x(), e->y()); } + +void SearchBox::resizeEvent(QResizeEvent *p) +{ + int w, h, margin; + // setMinimumHeight(height()-4); + // setMaximumHeight(p->size().height()-1); + // setFixedHeight(p->size().height()-5); + // setMaximumHeight(height()-2); + w = width(); + h = height(); + /// margin=(h - xb->width())/2; + /// xb->move(w-h+margin-1,margin); + return; +} + +void SearchBox::event_xbutton_clicked() +{ + QKeyEvent ke(QEvent::KeyPress, Qt::Key_Escape, 0); // temp.. + keyPressEvent(&ke); +} + +// public +/*void SearchBox::keyPress( QKeyEvent * e ) +{ + +} */ + +void SearchBox::timerEvent(QTimerEvent *e) +{ + // qDebug( "timer event, id %d", e->timerId() ); +} + +LogBox::LogBox(QWidget *p) : QLabel(p) +{ + text = new QTextEdit(p); + text->setFocusPolicy(Qt::NoFocus); +} + +// Location: bottom +StatusBar::StatusBar(QWidget *parent) : QStatusBar(parent) +{ + setSizeGripEnabled(true); + // QWidget *le = new QLabel(this);//QButton *button = new + // QButton(this); + // QLineEdit *le=new QLineEdit(this); + + // showMessage ( const QString & message, int timeout = 0 ) + // showMessage("aaaaaa"); + label = new QLabel(this); // QButton *button = new QButton(this); + // label->setText ("") ; + label->setFrameShape(QFrame::NoFrame); + // label->setFrameShape (QFrame::StyledPanel); + // label->setFrameShape (QFrame::Panel); + // label->setFrameShape (QFrame::WinPanel); + // label->setFrameShadow(QFrame::Sunken); + // button2 = new QToolButton(this);//QButton *button = new + // QButton(this); + // button2->setTextLabel ("") ; + // button2->setUsesTextLabel ( true ); + // button2->setAutoRaise(true); + // button3 = new QLabel(this);//QButton *button = new + // QButton(this); + // button3->setText ("") ; + // addPermanentWidget(label); + addWidget(label); + // addPermanentWidget(le,1); +} + +void StatusBar::refresh() {} + +extern int num_opened_files; +void StatusBar::update(int total_n) +{ + QString str; + int x = 0; + label->setText("Process count: " + str.setNum(total_n)); + // button2->setTextLabel ("Network Process(testing): "+ + // str.setNum(Procinfo::num_network_process)); + // button3->setText ("Opened files : "+ + // str.setNum(num_opened_files)); +} + +#include + +// Pstable or Procview +// Location : +QSpacerItem *stretch; +ControlBar::ControlBar(QWidget *parent) : QFrame(parent) +{ + int h; + // setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Expanding); + setFrameStyle(QFrame::NoFrame); + // setFrameStyle(Panel | Raised);//bad + // setFrameStyle(Panel ); //bad + layout = new QHBoxLayout(this); + layout->addSpacing(4); // left gap + layout->setMargin(0); + + // setStyleSheet(" QFrame,QCheckBox,QRadioButton { color: rgb(244, 244, 244);\ + border-image: url(:/icon/vista.png); } "); + /* Frame { background-color: yellow } + * border: 2px solid #8f8f91; + min-width: 80px; */ + /* image: url(:/icon/vista.png);*/ + + b_linear = new QRadioButton("Linear", this); + b_linear->setFocusPolicy(Qt::NoFocus); + + b_tree = new QRadioButton("Tree", this); + b_tree->setFocusPolicy(Qt::NoFocus); + + search_box = new SearchBox(this); + connect(b_linear, SIGNAL(clicked()), SLOT(linear_clicked())); + connect(b_tree, SIGNAL(clicked()), SLOT(tree_clicked())); + connect(search_box, SIGNAL(textChanged(const QString &)), + SLOT(event_search_box_changed())); + // search_box->setFocusPolicy(Qt::NoFocus); + // search_box->setFocus (Qt::ActiveWindowFocusReason); + // search_box->setFocusProxy ( parent ); + + if (flag_thread_ok) + { + check_thread = new QCheckBox("Thread", this); + check_thread->setFocusPolicy(Qt::NoFocus); + connect(check_thread, SIGNAL(clicked()), SLOT(show_thread_clicked())); + check_thread->setChecked(flag_show_thread); + } + + view = new QComboBox(this); + view->insertItem(0, "All Processes", Procview::ALL); + view->insertItem(1, "Your Processes", Procview::OWNED); + view->insertItem(2, "Non-Root Processes", Procview::NROOT); + view->insertItem(3, "Running Processes", Procview::RUNNING); + connect(view, SIGNAL(activated(int)), SLOT(view_changed(int))); + view->setFocusPolicy(Qt::NoFocus); + // PAUSED view->insertItem("Hidden Processes", Procview::HIDDEN); + + layout->addWidget(b_linear); + layout->addWidget(b_tree); + if (flag_thread_ok) + layout->addWidget(check_thread); + layout->addWidget(search_box); + layout->addWidget(view); + + // stretch=new QSpacerItem(0,0,QSizePolicy::Expanding); + // layout->addItem(stretch); + layout->addStretch(); + + /* + QPushButton *mb=new QPushButton; + mb->setContentsMargins(0,0,0,0); + //mb->setDown(true); + mb->setCheckable(true); + mb->setText("pause"); + mb->setFixedWidth(32); + mb->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum); + layout->addWidget(mb); + */ + + pauseButton = new QToolButton; + // QPushButton *pauseButton = new QPushButton; + pauseButton->setCheckable(true); + pauseButton->setIcon(QIcon(":/icon/pause.png")); + pauseButton->setIconSize(QSize(19, 19)); + pauseButton->setToolTip(tr("Pause (Ctrl+Space)")); + pauseButton->setFocusPolicy(Qt::NoFocus); + // pauseButton->setFlat(true); + pauseButton->setAutoRaise(true); + connect(pauseButton, SIGNAL(clicked(bool)), SLOT(setPaused(bool))); + + layout->addWidget(pauseButton); + + setLayout(layout); +} + +void PSTABLE_SETTREEMODE(bool mode); +void ControlBar::setMode(bool treemode) // just.. interface function +{ + b_linear->setChecked(!treemode); + b_tree->setChecked(treemode); + + PSTABLE_SETTREEMODE(treemode); // pstable->setTreeMode(treemode); +} + +#include +void ControlBar::view_changed(int idx) +{ + QAction act(this); + act.setData(QVariant(idx)); + emit viewChange(&act); +} + +extern bool flag_refresh; +void ControlBar::setPaused(bool b) +{ + flag_refresh = !b; + // if(!pauseButton->isDown()) pauseButton->setDown(true); +} + +void ControlBar::linear_clicked() +{ + setMode(false); + // emit modeChange(FALSE); +} + +void ControlBar::tree_clicked() +{ + setMode(true); + // emit modeChange(TRUE); +} + +void ControlBar::show_thread_clicked() +{ + flag_show_thread = check_thread->isChecked(); + emit need_refresh(); +} + +void ControlBar::event_search_box_changed() +{ + // printf("search_box changed!\n"); +} + +void ControlBar::event_command_pressed() +{ + // printf("command!\n"); +} + +// not used +void ControlBar::update_bar() // trick for Command +{ +} + +// called after resize() +// simplified by fasthyun@magicn.com + +extern bool flag_smallscreen; + +void ControlBar::resizeEvent(QResizeEvent *e) +{ + return; + // QLayoutItem *item=layout->takeAt (layout->count()-1); + // return; + if (flag_smallscreen) + { + search_box->hide(); + pauseButton->hide(); + } + else + { + search_box->show(); + pauseButton->show(); + } + + // printf("size=%d\n",stretch->geometry().width()); + // printf("hsize=%d\n",stretch->sizeHint().height()); + // QSize s=e->size(); + return; + /* + if(stretch->geometry().width()==0) + search_box->hide(); + else + search_box->show(); + */ + + /* setMaximumHeight (b_tree->sizeHint().height()+2); + search_box->setMaximumHeight(height()-4); + //search_box->setMinimumHeight(height()-5); + + for(i=0;i< commands.size();i++) + { + QToolButton *b; + b=commands[i]->toolbutton; + if(commands[i]->toolbar==true) + { + if (layout->findWidget(b)<0) + layout->addWidget(b); + b->show(); + } + else + if(b!=NULL) + b->hide(); + } */ +} + +void ServerAdaptor::accelerate() {} + +//#include +//#include +//#include +#include +// ServerAdaptor::ServerAdaptor(){ } +// ServerAdaptor::~ServerAdaptor(){} + +#define SERVICE_NAME "com.trolltech.qps" +void dbus_register_server() +{ + ServerAdaptor *sv = new ServerAdaptor(); + QDBusConnection connection = QDBusConnection::sessionBus(); // user only?? + // QDBusConnection connection = QDBusConnection::systemBus(); //user + // only?? + connection.registerObject("/QPS_OBJ", sv); + connection.registerService(SERVICE_NAME); // +} + +#include +void qdbus_client() +{ + // connection.connect ( SERVICE_NAME,"path", "interface","name", + // QObject * + // receiver, const char * slot ); + + // QDBusConnection conn = + // QDBusConnection::connectToBus(SERVICE_NAME,"hyun"); + + QDBusConnection conn = QDBusConnection::sessionBus(); // owner-user + // only, hang.. + // blocked !!! + + QDBusReply reply = conn.interface()->registeredServiceNames(); + + if (!reply.isValid()) + { + qDebug() << "qdbus_client Error:" << reply.error().message(); + // exit(1); + } + + /* + foreach (QString name, reply.value()) + { + qDebug() << name; + //printf("name %s, value %s \n",qPrintable(name), + } */ + + if (conn.interface()->isServiceRegistered(SERVICE_NAME)) + { + printf("Qps: Already another QPS is running...\n"); + exit(1); + } +} + +void check_qps_running() +{ + // QDBusServer *server=QDBusServer("xaddr"); //peer2peer mode + // service,path,interface,name + // connect(server, SIGNAL(clicked()), SLOT(event_xbutton_clicked())); + // SIGNAL(newConnection ( const QDBusConnection & connection )) + // QDBusConnection::connectToBus ("xaddr", "name" ); + + // glib_dbus_getlist(); + // glib_dbus_server(); + + qdbus_client(); // hang... on root-user + // printf("D1-1-3\n"); + dbus_register_server(); + // printf("D1-1-6\n"); +} + +char *read_proc_file(const char *fname, int pid = -1, int tgid = -1); // Temp +void init_misc(QWidget *main) +{ + + init_xpm(); // xpm image file load + + if (true) // TESTING + { + } + +#ifdef LINUX + if (false or flag_devel) // thread's Name Change test + { + QThread *task = new MyThread(); + task->start(); + task = new MyThread2(); + task->start(); + } +#endif + + if (false) // test mini_sscanf() + { + char *buf; + int val; + char sstr[32] = "pu%s"; + char buffer[128]; + + if ((buf = read_proc_file("stat", 1)) == 0) + { + printf("cant open [%s]\n", "sss"); + } + if (mini_sscanf(buf, sstr, &val) != 0) + printf("found [%s] val=%d \n", sstr, val); + if (mini_sscanf(buf, "(%s)", &buffer) != 0) + printf("found str=%s \n", buffer); + + abort(); + } +} + +QTabWidgetX::QTabWidgetX(QWidget *parent) {} + +void QTabWidgetX::showTab(bool flag) +{ + + QTabBar *tb = tabBar(); + // tb->setDrawBase(false); //?? + + if (flag) + tb->show(); + else + tb->hide(); +// if flag_useTabView == false +#if QT_VERSION > 0x040500 +// setDocumentMode (true); // QT 4.5 +#endif +} + +#include "qticonloader.cpp" diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..2da8a8b --- /dev/null +++ b/src/misc.h @@ -0,0 +1,296 @@ +#ifndef MISC_H +#define MISC_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include "qticonloader.h" + +char *userName(int uid, int euid); +char *groupName(int gid, int egid); + +void setQpsTheme(); + +int fsize(char *fname); +void msleep(long msec); +void mem_string(int kbytes, char *buf); +void mem_string_k(int kbytes, char *buf); + +void init_xpm(); +void init_misc(QWidget *main); +int pf_write(QPainter *p, int x, int y, const char *str); +int pf_str_width(char *str); +int pf_char_height(); +void check_qps_running(); + +int QPS_PROCVIEW_CPU_NUM(); +void AddLog(QString str); + +class CrossBox : public QCheckBox +{ + public: + CrossBox(const char *text, QWidget *parent); + + protected: + virtual void drawButton(QPainter *paint); +}; + +class CheckMenu : public QMenu +{ + public: + CheckMenu(QWidget *parent = 0); +}; + +class TFrame : public QLabel +{ + Q_OBJECT + public: + TFrame(QWidget *parent); + void setText(QString str); + void draw(QPainter &p); + void showText(QPoint pos, QString str); + void setPos(int x, int y); + void setPos(); + + protected slots: + // void refresh(); + // void update(int n); + // QToolButton *button,*button2,*button3; + // void event_cursor_moved(QMouseEvent *e); + void setValue(int val); + + protected: + virtual void paintEvent(QPaintEvent *event); + virtual void moveEvent(QMoveEvent *event); + virtual void showEvent(QShowEvent *event); + + private: + QString text; + QTimeLine *timeLine; + float opacity; +}; + +class TBloon : public QLabel +{ + Q_OBJECT + public: + TBloon(QWidget *parent); + // void setText(QString str); + // void draw( QPainter &p ); + // void showText(QPoint pos,QString str); + + virtual bool eventFilter(QObject *watched, QEvent *event); + protected slots: + void update(int val); + // void refresh(); + // void update(int n); + // QToolButton *button,*button2,*button3; + // void event_cursor_moved(QMouseEvent *e); + protected: + // virtual void paintEvent( QPaintEvent * event ); + // virtual void moveEvent (QMoveEvent * event ); + private: + QWidget *paren; + QString text; + QTimeLine *timeLine; +}; + +class FaderWidget : public QWidget +{ + Q_OBJECT + Q_PROPERTY(QBrush fadeBrush READ fadeBrush WRITE setFadeBrush) + Q_PROPERTY(int fadeDuration READ fadeDuration WRITE setFadeDuration) + public: + FaderWidget(QWidget *parent); + + QBrush fadeBrush() const { return startBrush; } + void setFadeBrush(const QBrush &newColor) { startBrush = newColor; } + + int fadeDuration() const { return timeLine->duration(); } + void setFadeDuration(int milliseconds) + { + timeLine->setDuration(milliseconds); + } + + void start(); + + protected: + void paintEvent(QPaintEvent *event); + + private: + QTimeLine *timeLine; + QBrush startBrush; +}; + +class UFrame : public QFrame +{ + Q_OBJECT + public: + UFrame(QWidget *parent); + void setTitle(QString str); + protected slots: + // void refresh(); + // void update(int n); + // QToolButton *button,*button2,*button3; + // void event_cursor_moved(QMouseEvent *e); + protected: + virtual void paintEvent(QPaintEvent *event); + + private: + QString title; + QString stylesheet; +}; + +class XButton : public QAbstractButton +{ + Q_OBJECT + public: + XButton(QWidget *parent); + protected slots: + // void refresh(); + // void update(int n); + // QToolButton *button,*button2,*button3; + // void event_cursor_moved(QMouseEvent *e); + protected: + // virtual void drawButton 3( QPainter * ) ; + virtual void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *p); +}; + +class SearchBox : public QLineEdit +{ + Q_OBJECT + public: + SearchBox(QWidget *parent); + void event_cursor_moved(QMouseEvent *e); + void keyPressEvent(QKeyEvent *e); + protected slots: + void event_xbutton_clicked(); + // void refresh(); + // void update(int n); + // QToolButton *button,*button2,*button3; + protected: + void resizeEvent(QResizeEvent *); + void timerEvent(QTimerEvent *); + int left_time; + // XButton *xb; +}; + +class LogBox : public QLabel +{ + Q_OBJECT + public: + LogBox(QWidget *w); + QTextEdit *text; + // QLabel *label,*label2,*label3; +}; + +class StatusBar : public QStatusBar +{ + Q_OBJECT + public: + StatusBar(QWidget *parent); + void refresh(); + void update(int n); + QLabel *label, *label2, *label3; +}; + +class ControlBar : public QFrame +{ + Q_OBJECT + public: + QComboBox *view; + ControlBar(QWidget *parent); + void setMode(bool treemode); + void update_bar(); + QToolButton *pauseButton; + +signals: + void modeChange(bool treemode); + void viewChange(QAction *); + void need_refresh(); + + public slots: + void linear_clicked(); + void view_changed(int idx); + void tree_clicked(); + void show_thread_clicked(); + void event_search_box_changed(); + void event_command_pressed(); + void setPaused(bool); + + protected: + void resizeEvent(QResizeEvent *); + + private: + QRadioButton *b_tree, *b_linear, *b_treeT; + QCheckBox *check_thread; + QBoxLayout *layout; +}; + +#include + +class QTabWidgetX : public QTabWidget +{ + Q_OBJECT + public: + QTabWidgetX(QWidget *parent); + void showTab(bool); +}; + +#include +#include + +// class ServerAdaptor: public QDBusAbstractAdaptor +class ServerAdaptor : public QObject +{ + Q_OBJECT + Q_CLASSINFO("D-Bus Interface", "com.trolltech.Examples.CarInterface") + Q_CLASSINFO("D-Bus Introspection", + "" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "") + public: + // ServerAdaptor(QObject *parent); + ServerAdaptor(){}; + virtual ~ServerAdaptor(){}; + + public: // PROPERTIES + public Q_SLOTS: // METHODS + void accelerate(); +Q_SIGNALS: // SIGNALS + void crashed(); +}; + +#endif // MISC_H diff --git a/src/prefs.cpp b/src/prefs.cpp new file mode 100644 index 0000000..189ba9e --- /dev/null +++ b/src/prefs.cpp @@ -0,0 +1,352 @@ +// prefs.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +#include "prefs.h" +#include "proc.h" +#include "qps.h" +#include +#include +#include +#include +#include +#include +#include + +//#include +#include "qps.h" +extern Qps *qps; + +extern QFontComboBox *font_cb; +// class that validates input for the "swap warning limit" field +class Swapvalid : public QValidator +{ + public: + Swapvalid(QWidget *parent) : QValidator(parent) {} + virtual State validate(QString &s, int &) const; +}; + +struct Boxvar +{ + const char *text; + bool *variable; + // CrossBox *cb; + QCheckBox *cb; +}; + +static Boxvar general_boxes[] = { + // {"Graphic Load Display", &Qps::show_load_graph, 0}, + // {"Graphic CPU Display", &Qps::show_cpu_bar, 0}, + //{"Minimized on Close Button", &Qps::flag_systray, 0}, + {"Exit On Close Button", &Qps::flag_exit, 0}, + + // TEMPO + // {"Use Tab-View", &Qps::flag_useTabView, 0}, + // {"Hide qps in Linear mode", &Qps::flag_qps_hide, 0}, + // {"Load Graph in Icon", &Qps::load_in_icon, 0}, + // {"Selection: Copy PIDs to Clipboard", &Qps::pids_to_selection, 0}, + // {"show underdevelopment ", &Qps::flag_devel, 0}, + // {"Vertical CPU Bar (under development)", &Qps::vertical_cpu_bar, 0}, + {0, 0, 0}}; + +#ifdef LINUX +static Boxvar sockinfo_boxes[] = { + {"Host Name Lookup", &Qps::hostname_lookup, 0}, + {"Service Name Lookup", &Qps::service_lookup, 0}, + {0, 0, 0}}; +#endif + +static Boxvar tree_boxes[] = {{"Disclosure Triangles", &Qps::tree_gadgets, 0}, + {"Branch Lines", &Qps::tree_lines, 0}, + {0, 0, 0}}; + +static Boxvar misc_boxes[] = { + {"Auto Save Settings on Exit", &Qps::auto_save_options, 0}, + {"Selection: Copy PIDs to Clipboard", &Qps::pids_to_selection, 0}, +#ifdef SOLARIS + {"Normalize NICE", &Qps::normalize_nice, 0}, + {"Use pmap for Map Names", &Qps::use_pmap, 0}, +#endif + {0, 0, 0}}; + +struct Cbgroup +{ + const char *caption; + Boxvar *boxvar; +}; + +static Cbgroup groups[] = {{"General", general_boxes}, +#ifdef LINUX +// {"Socket Info Window", sockinfo_boxes}, +#endif + // {"Tree View", tree_boxes}, + // {"Miscellaneous", misc_boxes}, + {0, 0}}; + +void find_fontsets(); +// dual use function: both validate and apply changes +QValidator::State Swapvalid::validate(QString &s, int &) const +{ + // only accept /^[0-9]*[%kKmM]?$/ + int len = s.length(); + int i = 0; + while (i < len && s[i] >= '0' && s[i] <= '9') + i++; + if ((i < len && QString("kKmM%").contains(s[i]) == 0) || i < len - 1) + return Invalid; + if (s[i] == 'k') + s[i] = 'K'; + if (s[i] == 'm') + s[i] = 'M'; + // int val = atoi((const char *)s); + int val = s.toInt(); + bool percent; + if (s[i] == '%') + { + percent = true; + } + else + { + percent = false; + if (s[i] == 'M') + val <<= 10; + } + Qps::swaplimit = val; + Qps::swaplim_percent = percent; + return Acceptable; +} + +Preferences::Preferences() : QDialog() +{ + int flag_test = 0; + setWindowTitle("Preferences"); + QVBoxLayout *v_layout = new QVBoxLayout; + + if (flag_test) + { + QTabWidget *tbar = new QTabWidget(this); + QWidget *w = new QWidget(this); + tbar->addTab(w, "&Setting"); + w->setLayout(v_layout); + } + else + setLayout(v_layout); + + v_layout->setSpacing(1); + // v_layout->setSpacing(1); + const int border_x = 10; + int min_x = 0; + + for (Cbgroup *g = groups; g->caption; g++) + { + QGroupBox *grp = new QGroupBox(g->caption, this); + QVBoxLayout *vbox = new QVBoxLayout; + for (Boxvar *b = g->boxvar; b->text; b++) + { + b->cb = new QCheckBox(b->text, grp); + vbox->addWidget(b->cb); + connect(b->cb, SIGNAL(clicked()), SLOT(update_reality())); + // -> EMIT prefs_change() + } + grp->setLayout(vbox); + v_layout->addWidget(grp); + } + update_boxes(); + + /* + QGroupBox *wgrp = new QGroupBox("Swap Warning", this); + QHBoxLayout *hl = new QHBoxLayout; + wgrp->setLayout(hl); + QLabel *lbl = new QLabel("Warn when free swap below", wgrp); + QLineEdit *swaped = new QLineEdit(wgrp); + hl->addWidget(lbl); + hl->addWidget(swaped); + //swaped->setAlignment ( Qt::AlignRight ); + QString s; + if(Qps::swaplim_percent) { + s.sprintf("%d%%", Qps::swaplimit); + } else { + if(Qps::swaplimit >= 1024 && (Qps::swaplimit & 0x7ff) == 0) + s.sprintf("%dM", Qps::swaplimit >> 10); + else + s.sprintf("%dK", Qps::swaplimit); + } + swaped->setText(s); + swaped->setValidator(new Swapvalid(this)); + connect(swaped, SIGNAL(textChanged(const QString &)), + SIGNAL(prefs_change())); + v_layout->addWidget(wgrp); + */ + + rb_totalcpu = NULL; // tmp + + if (QPS_PROCVIEW_CPU_NUM() > 1) + { + QGroupBox *grp_cpu = new QGroupBox("%CPU divided by", this); + QVBoxLayout *vboxlayout = new QVBoxLayout; + QHBoxLayout *hbox = new QHBoxLayout; + vboxlayout->addLayout(hbox); + + // num_cpus + QString str; + str.sprintf("Total cpu: %d", QPS_PROCVIEW_CPU_NUM()); + rb_totalcpu = new QRadioButton(str, grp_cpu); + QRadioButton *rb2 = new QRadioButton("Single cpu: 1", grp_cpu); + if (!Procview::flag_pcpu_single) + rb_totalcpu->setChecked(true); + else + rb2->setChecked(true); + + rb_totalcpu->setToolTip("default"); + rb2->setToolTip("for developer"); + hbox->addWidget(rb_totalcpu); + hbox->addWidget(rb2); + grp_cpu->setLayout(vboxlayout); + v_layout->addWidget(grp_cpu); + + connect(rb_totalcpu, SIGNAL(clicked()), this, SLOT(update_config())); + connect(rb2, SIGNAL(clicked()), this, SLOT(update_config())); + } + + // Appearance ==================================== + if (font_cb == NULL) + { + font_cb = new QFontComboBox(this); // preload + font_cb->setWritingSystem(QFontDatabase::Latin); + font_cb->setCurrentFont(QApplication::font()); + + // remove Some Ugly Font : hershey... + // printf("size=%d\n",font_cb->count()); + for (int i = 0; i < font_cb->count();) + { + QString name = font_cb->itemText(i); + if (name.contains("hershey", Qt::CaseInsensitive) == true) + { + // printf("%s\n",qPrintable(name)); + font_cb->removeItem(i); + } + else + i++; + } + } + + if (font_cb) + { + font_cb->show(); + + QGroupBox *gbox = new QGroupBox("Appearance", this); + QVBoxLayout *vbox = new QVBoxLayout; + QHBoxLayout *hbox = new QHBoxLayout(); + + psizecombo = new QComboBox(this); + hbox->addWidget(font_cb); + hbox->addWidget(psizecombo); + vbox->addLayout(hbox); + gbox->setLayout(vbox); + v_layout->addWidget(gbox); + + // connect(font_cb, SIGNAL(activated( int)),this, + // SLOT(font_changed(int))); + connect(font_cb, SIGNAL(activated(int)), this, SLOT(font_changed(int))); + connect(psizecombo, SIGNAL(activated(int)), SLOT(font_changed(int))); + + // add to font size + init_font_size(); + } + + /* + // Style, Themes ================================== + QLabel *label3 = new QLabel("Themes", font_grp); + QComboBox *theme_combo = new QComboBox(font_grp); + + QStringList styles = QStyleFactory::keys(); + styles.sort(); + theme_combo->insertStringList(styles); + + QString currentstyle; + currentstyle = QApplication::style().className(); + */ + + QPushButton *saveButton = new QPushButton("OK", this); + // saveButton->setFocusPolicy(QWidget::NoFocus); + saveButton->setFocus(); + saveButton->setDefault(true); + v_layout->addWidget(saveButton); + // v_layout->freeze(); + + connect(saveButton, SIGNAL(clicked()), SLOT(closed())); +} + +// +void Preferences::init_font_size() +{ + // QFontDatabase db; + // QStringList families ( WritingSystem writingSystem = Any ) const + // QStringList families = db.families(QFontDatabase::Latin); + // QStringList extra; + psizecombo->clear(); + int i, idx = 0; + for (i = 5; i < 24; i++) + psizecombo->insertItem(idx++, QString::number(i)); + + // find current font size + i = 0; + for (int psize = QApplication::font().pointSize(); i < psizecombo->count(); + ++i) + { + const int sz = psizecombo->itemText(i).toInt(); + if (sz == psize) + { + psizecombo->setCurrentIndex(i); + break; + } + } +} + +// slot: update check boxes to reflect current status +void Preferences::update_boxes() +{ + for (Cbgroup *g = groups; g->caption; g++) + for (Boxvar *b = g->boxvar; b->text; b++) + b->cb->setChecked(*b->variable); +} + +// slot: update flags and repaint to reflect state of check boxes +void Preferences::update_reality() +{ + for (Cbgroup *g = groups; g->caption; g++) + for (Boxvar *b = g->boxvar; b->text; b++) + *b->variable = b->cb->isChecked(); + emit prefs_change(); +} + +void Preferences::update_config() +{ + if (rb_totalcpu and rb_totalcpu->isChecked() == true) + Procview::flag_pcpu_single = false; + else + Procview::flag_pcpu_single = true; +} + +void Preferences::closed() +{ + // delete font_cb; font_cb==NULL; + // font_cb->clear(); + update_config(); + hide(); + emit prefs_change(); +} + +// work +void Preferences::font_changed(int i) +{ + int size = psizecombo->currentText().toInt(); + QFont font = font_cb->currentFont(); + font.setPointSize(size); + + QApplication::setFont(font); +} + +// DRAFT CODE: +void Preferences::fontset_changed(int i) {} diff --git a/src/prefs.h b/src/prefs.h new file mode 100644 index 0000000..5c6324c --- /dev/null +++ b/src/prefs.h @@ -0,0 +1,37 @@ +// prefs.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef PREFS_H +#define PREFS_H + +#include +#include +#include +//#include +#include +#include "misc.h" + +class Preferences : public QDialog +{ + Q_OBJECT + public: + Preferences(); + QComboBox *psizecombo; + // QFontComboBox *font_cb; + QRadioButton *rb_totalcpu; + void init_font_size(); + + public slots: + void update_boxes(); + void update_reality(); + void update_config(); + void closed(); + void font_changed(int); + void fontset_changed(int); +signals: + void prefs_change(); +}; + +#endif // PREFS_H diff --git a/src/proc.cpp b/src/proc.cpp new file mode 100644 index 0000000..8f8cd8e --- /dev/null +++ b/src/proc.cpp @@ -0,0 +1,2255 @@ +// proc.cpp for Linux +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 +// Oliver + +/* + LWP (Light Weight Process): just thread, mainly used in Solaris + Task : thread and process in Linux + NPTL(Native POSIX Thread Library) + TGID thread group leader's pid +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // sched_rr_get_interval(pid, &ts); +#include // basename() +#include // sysconf() POSIX.1-2001 + +#include +//#include //HZ defined, no more used. +//#include "misc.h" // x_atoi() , userName() ,groupname() + +#include "proc.h" +#include "uidstr.h" +#include "ttystr.h" +#include "wchan.h" + +#ifdef GTK +#include "detail_gtk.h" +#else +#include "details.h" //qt +#endif + +#include "proc_common.cpp" // COMMON code !!!! + +#define PROCDIR "/proc" // hmmm + +bool flag_SMPsim = false; // SMP simulation + +extern int flag_thread_ok; +extern bool flag_schedstat; +extern bool flag_show_thread; +extern bool flag_devel; + +int pagesize; +int Proc::update_msec = 1024; + +// socket states, from and touched to avoid name collisions +enum +{ + SSFREE = 0, /* not allocated */ + SSUNCONNECTED, /* unconnected to any socket */ + SSCONNECTING, /* in process of connecting */ + SSCONNECTED, /* connected to socket */ + SSDISCONNECTING /* in process of disconnecting */ +}; + +#define QPS_SCHED_AFFINITY ok + +#ifdef QPS_SCHED_AFFINITY +#ifndef SYS_sched_setaffinity +#define SYS_sched_setaffinity 241 +#endif +#ifndef SYS_sched_getaffinity +#define SYS_sched_getaffinity 242 +#endif + +// Needed for some glibc +int qps_sched_setaffinity(pid_t pid, unsigned int len, unsigned long *mask) +{ + return syscall(SYS_sched_setaffinity, pid, len, mask); +}; +int qps_sched_getaffinity(pid_t pid, unsigned int len, unsigned long *mask) +{ + return syscall(SYS_sched_getaffinity, pid, len, mask); +}; +#endif + +/* + Thread Problems. + pthread_exit() + */ + +struct proc_info_ +{ + int proc_id; + char flag; + char type; + int files; +} proc_info; // TESTING + +struct list_files_ +{ + int proc_id; + int flag; + char *filename; // path + filename +} list_files; // TESTING + +// read() the number of bytes read is returned (zero indicates end of file) +// return the number of bytes read if ok, -1 if failed +inline int read_file(char *name, char *buf, int max) +{ + int fd = open(name, O_RDONLY); + if (fd < 0) + return -1; + int r = read(fd, buf, max); + close(fd); + // buf[r]=0; + return r; +} + +// Description : read proc files +// return 0 : if error occurs. +char buffer_proc[1024 * 4]; // enough..maybe +char *read_proc_file(const char *fname, int pid = -1, int tgid = -1, + int *size = NULL) +{ + static int max_size = 0; + char path[256]; + int r; + + if (pid < 0) + sprintf(path, "/proc/%s", fname); + else + { + if (tgid > 0) + sprintf(path, "/proc/%d/task/%d/%s", tgid, pid, fname); + else + sprintf(path, "/proc/%d/%s", pid, fname); + } + + if (strcmp(fname, "exe") == 0) + { + if ((r = readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0) + { + buffer_proc[r] = 0; // safer + return buffer_proc; + } + else + return 0; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + r = read(fd, buffer_proc, sizeof(buffer_proc) - 1); // return 0 , -1 , + if (r < 0) + return 0; + + if (max_size < r) + max_size = r; + + if (size != 0) + *size = r; + + buffer_proc[r] = 0; // safer + + return buffer_proc; + // note: not work fgets(sbuf, sizeof(64), fp) why??? +} + +char *read_proc_file2(char *r_path, const char *fname, int *size = NULL) +{ + static int max_size = 0; + char path[256]; + int r; + + // strcpy(path,r_path); + + sprintf(path, "%s/%s", r_path, fname); + + if (strcmp(fname, "exe") == 0) + { + if ((r = readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0) + { + buffer_proc[r] = 0; // safer + return buffer_proc; + } + else + return 0; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + r = read(fd, buffer_proc, + sizeof(buffer_proc) - 1); // return 0 , -1 , read_count + if (r < 0) + return 0; + + if (max_size < r) + max_size = r; + + if (size != 0) + *size = r; + + buffer_proc[r] = 0; // safer + close(fd); + + return buffer_proc; + // note: not work fgets(sbuf, sizeof(64), fp) why??? +} + +// TEST CODE , Bottleneck +// Description: read /proc/PID/fd/* check opened file, count opened files +// this fuction will be called when every update. +// Return Value : +int proc_pid_fd(const int pid) +{ + char path[256]; + char buffer[256], fname[256]; + DIR *d; + int fdnum; + int len, path_len; + + sprintf(path, "/proc/%d/fd", pid); + + path_len = strlen(path); + d = opendir(path); + + if (!d) + { + // this happend when the process died already or Zombie process + // printf("Qps : read fail !! /proc/%d/fd !!! kernel bug ? + // \n",pid); + return false; + } + + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip "." and ".." + + path[path_len] = '/'; + path[path_len + 1] = 0; + strcat(path, e->d_name); + + len = readlink(path, fname, sizeof(fname) - 1); + if (len > 0) + { + fname[len] = 0; + // printf("DEBUG: %s[%s]\n",path,fname); + // if (strcmp(fname,"/dev/null")==0 ) continue; + } + + /// num_opened_files++; + // strcpy(p, e->d_name); + // fdnum = atoi(p); + // read_fd(fdnum, path); + } + closedir(d); + return true; +} + +// new process created +Procinfo::Procinfo(Proc *system_proc, int process_id, int thread_id) : refcnt(1) +{ + first_run = true; + clone = false; + + proc = system_proc; + + if (thread_id < 0) // + { + pid = process_id; + tgid = process_id; // thread group leader's id + } + else + { + pid = thread_id; + tgid = process_id; // thread group leader's id + } + + ppid = 0; // no parent + selected = false; + hidekids = false; + envblock = 0; //!! + + table_child_seq = -1; + child_seq_prev = -1; + + lastchild = 0; + generation = -1; + detail = 0; + + /// per_cpu_times = 0; not yet + + size = 0; + resident = 0; + trs = 0; + drs = 0; + stack = 0; + share = 0; + mem = 0; + + io_read_prev = 0; // ** + io_write_prev = 0; + io_read = 0; // ** + io_write = 0; // ** + + // tgid=0; + pcpu = 0; + pmem = 0; + + old_utime = 0; // this must be current utime ! + old_wcpu = 0; + + command = "noname"; + tty = 0; + nice = 0; + starttime = 0; + state = 'Z'; + cutime = utime = 0; + + nthreads = 0; /* number of threads */ + + hashstr[0] = 0; + hashlen = 0; +} + +Procinfo::~Procinfo() +{ + if (!clone) + { + void watchdog_check_if_finish(QString cmd, Procinfo * p); + watchdog_check_if_finish(command, this); + + if (detail) + { + // printf("~Procinfo() : pid=%d\n",pid); + detail->process_gone(); + detail = 0; + } + + // if(environ) delete environ; + if (envblock) + free(envblock); /// double free , SEGFAULT + } + + // fd_files.squeeze(); + // maps.squeeze(); + /* + if(maps) { + maps->purge(); + delete maps; + } + if(fd_files) { + fd_files->purge(); + delete fd_files; + } + */ + + // if(children) + // { children->clear(); delete children; } + /// delete[] per_cpu_times; +} + +// miscellaneous static initializations +void Proc::init_static() +{ + + // socks.setAutoDelete(true); + /// usocks.setAutoDelete(true); + + pagesize = sysconf(_SC_PAGESIZE); // same getpagesize() in + // printf("pagesize=%d, %d\n",getpagesize(), + // sysconf(_SC_PAGESIZE)); //4027 +} + +// tricky function...(by fasthyun@magicn.com) +// Description : +// let's deal thread as normal process! +// read /proc/PID/task/* and add to Proc::procs[] +int Proc::read_pid_tasks(int pid) +{ + char path[256]; + struct dirent *e; + int thread_pid; + int thread_n = 0; + Procinfo *pi = 0; + + sprintf(path, "/proc/%d/task", pid); + + DIR *d = opendir(path); + if (!d) + return -1; // process dead already! + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip "." , ".." + + thread_pid = atoi(e->d_name); + if (pid == thread_pid) + continue; // skip + + pi = procs.value(thread_pid, NULL); + + if (pi == NULL) + { + pi = new Procinfo(this, pid, thread_pid); + procs.insert(thread_pid, pi); + } + if (pi->readproc() >= 0) + { + pi->generation = current_gen; + // if(pid!=thread_pid) + // pi->cmdline="(thread)"; + } + thread_n++; + } + closedir(d); + return thread_n; +} + +// update wcpu,%cpu field +void Procinfo::calculate_cpu() // +{ +} + +// using cache for Speed up +int Procinfo::hashcmp(char *sbuf) +{ + int statlen; + + statlen = strlen(sbuf); + if (statlen > sizeof(hashstr)) + { + // some user reported 265byte. + printf("Qps BUG: hashstr shortage statlen(%d) > hashstr(%lu), " + "report this " + "message to fasthyun@magicn.com \n", + statlen, sizeof(hashstr)); + abort(); + } + else if (statlen == hashlen) + { + if (memcmp(hashstr, sbuf, statlen) == 0) + { + pcpu = 0; + // 1. I am a sleeping process + // printf("[%d] sleep process \n",pid); + return 1; + } + } + memcpy(hashstr, sbuf, statlen); // to back + hashlen = statlen; + return 0; +} + +int mini_sscanf(const char *s, const char *fmt, ...); + +// Description : read /proc/PID/* or read /proc/PID/task/* +// be called every refresh() time. +// return -1 means the process already dead ! +int Procinfo::readproc() +{ + char cmdbuf[MAX_CMD_LEN]; + char path[64]; + int len; + char *sbuf; // should be enough to acommodate /proc/PID/stat + char *buf; + + int x_pid; // just pid + int i_tty; // + long stime, cstime; + + // Note : /proc/PID/* is not same /proc/task/PID/* + if (isThread()) // flag_thread_ok + { + sprintf(path, "/proc/%d/task/%d", tgid, pid); + } + else + sprintf(path, "/proc/%d", pid); + + if (first_run) + { + // Note: COMMAND(?) , TGID, UID , COMMAND_LINE never + // change ! + old_wcpu = wcpu = pcpu = 0.0; + + // read /proc/PID/status + if ((buf = read_proc_file2(path, "status")) == 0) + return -1; + + // Note: Process_name from + // 1.status (15 chars-name) + // 2.stat (15 chars-name, pass) + // 3.cmdline : full name (sometimes have null, Thread + // can't use + // cmdline) + // 4.exe : full name (frequently this does not exist, + // thread can't use + // exe) + // 5.comm : 15 chars ? + // + // Note: + // 1. thread's name_max is 15 chars + + if (mini_sscanf(buf, "Name: %S\n", cmdbuf) == 0) + return -1; + else + { + command = cmdbuf; + if (command.contains("kthread")) + hidekids = true; // kthread, kthreadd , + // ///Procinfo::qps_pid=pid; + } + + if (mini_sscanf(buf, "Tgid: %d ", &tgid) == 0) + return -1; + if (mini_sscanf(buf, "Uid: %d %d %d %d", &uid, &euid, &suid, &fsuid) != + 4) + return -1; + if (mini_sscanf(buf, "Gid: %d %d %d %d", &gid, &egid, &sgid, &fsgid) != + 4) + return -1; + + username = userName(uid, euid); + groupname = groupName(gid, egid); + + int bug = 0; + char cmdline_cmd[4096]; // some cmdline very large! ex)chrome + // read /proc/pid/cmdline + int size; + cmdline_cmd[0] = 0; + + // anyone can read [cmdline] + if ((buf = read_proc_file2(path, "cmdline", &size)) == 0) + return -1; + else + { + // printf("DEBUG: size=%d \n",size); + int cmdlen = strlen(buf); + + if (cmdlen == 0) + { + // 1. kthread + // printf("Qps:debug no_cmdline pid=%d\n",pid ); + cmdline = ""; + } + // for non-ascii locale language + // cmdline = codec->toUnicode(cmdbuf,strlen(cmdbuf)); + else + { + + // change 0x00,0xA to ' ' + for (int i = 0; i < size - 1; i++) // OVERFLOW + if (buf[i] == 0 or buf[i] == 0x0A) + buf[i] = ' '; + cmdline = buf; + strcpy(cmdline_cmd, buf); + } + } + + // VERY COMPLEX CODE + // because Command's MAX_length is only 15, so sometimes + // cmd_name truncated, + // we should guess ... + // + // The solution is... + // 1.check [exe] file ( only owner can read it) + // 2.check [cmdline] ( anyone can read it ) + // 3.check [comm] + // + if (command.size() == 15) + { + // only root & owner can read [exe] link + /* + if((buf= read_proc_file2(path,"exe")) !=0 ) + { + // printf("Qps:debug %s\n",buf ); + if(strlen(basename(buf))>15 and + strncmp(qPrintable(command),basename(buf),15)==0 + ) + command=basename(buf); // no + memory leak ! + else ;// just use command + //printf("Qps:debug %s\n",buf ); + } */ + + if (true) // guess the full name of the command + { + // Use /proc/PID/cmdline, comm, status + // ex) + // /usr/lib/chromium/chromium --option1 + // --option2 + // python /usr/lib/system-service-d + // pam: gdm-password + // hald-addon-input: Listing On /dev~ + // + char *p; + p = strstr(cmdline_cmd, ": "); // cut the options ! + if (p != 0) + *p = 0; + p = strchr(cmdline_cmd, ' '); // cut the options ! + if (p != 0) + *p = 0; + + // printf("Qps:debug %s\n",cmdline_cmd ); + char *pstart = strstr(basename(cmdline_cmd), cmdbuf); + if (pstart != 0) + { + command = pstart; // copy + /// printf("Qps:debug2 + /// %s\n",basename(cmdline_cmd)); + } + } + } + + if (isThread()) + cmdline = command + " (thread)"; + + if (flag_devel and bug) + { + // command.append("^"); + cmdline += " ^ Qps: may be a wrong commandline "; + } + + void watchdog_check_if_start(QString cmd, Procinfo * ps); + watchdog_check_if_start(command, this); // segfault possible. + + first_run = false; + } + + if (flag_schedstat == true) + { + // if no change then return. twice faster ! + // MAX_256 bytes check...? + // 2.6.9 upper only and some system no has + if ((sbuf = read_proc_file2(path, "schedstat")) == 0) + return -1; + + if (hashcmp(sbuf)) + return 1; // no change + } + // read /proc/PID/stat + if ((sbuf = read_proc_file2(path, "stat")) == 0) + return -1; + + if (flag_schedstat == false) // if no change then return. twice faster ! + { + if (hashcmp(sbuf)) + return 1; + } + + /// if (proc_pid_fd(pid)== true) ; // bottleneck !! + + /* + Not all values from /proc/#/stat are interesting; the ones left + out + have been retained in comments to see where they should go, in + case + they are needed again. + + Notes : + 1. man -S 5 proc + 2. man -S 2 times + 3. ppid can be changed when parent dead ! + 4. initial utime maybe 0, so %CPU field NotAnumber !! + utime: user time + stime: kernel mode tick + cutime : The number of jiffies that this process's waited-for + children have been scheduled in user mode. + + #jiffies == tick + */ + unsigned int guest_utime, cguest_utime; +#if 1 + char *p, *p1; + // in odd cases the name can contain spaces and '(' or ')' and numbers, + // so + // this makes + // parsing more difficult. We scan for the outermost '(' ')' to find the + // name. + p = strchr(sbuf, '('); + p1 = strrchr(sbuf, ')'); + if (p == 0 || p1 == 0) + return -1; + p1++; + // we can safely use sscanf() on the rest of the string + sscanf(p1, + " %c %d %d %d %d %d" + " %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %d %d %d %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s " + "%*s %*s %*s %*s %lu %*s %*s %*s %u %*s %*s %*s %u %u", +#else + // some errors will occur ! + mini_sscanf( + sbuf, + "%d (%S) %c %d %d %d %d %d" + "%lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %d %d %d %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s " + "%*s %*s %*s %*s %lu %*s %*s %*s %u %*s %*s %*s %u %u", + &x_pid, &cmdbuf[0], +#endif + &state, &ppid, &pgrp, &session, &i_tty, &tpgid, &flags, &minflt, + &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime, + &priority, &nice, &nthreads /* number of threads v2.6 */, + /* itrealvalue */ + &starttime, /* start time(static) */ // jiffes + /* vsize */ + /* rss */ + /* rlim, startcode, endcode, startstack kstkesp kstkeip, + signal, blocked, sigignore, sigcatch */ + &wchan, + /* 0L, 0L, exit_signal */ + &which_cpu + /* rt_priority, policy, delayacct_blkio_ticks */ + , + &guest_utime, &cguest_utime); + + starttime = proc->boot_time /* secs */ + (starttime / proc->clk_tick); + + tty = (dev_t)i_tty; // hmmm + // if(tty!=0) printf("pid=%d tty =%d\n",pid,tty); + + // if(guest_utime>0 or cguest_utime>0) + // printf("cmd [%s] guest_utime=%d cguest_utime + //=%d\n",qPrintable(command),guest_utime,cguest_utime); + + utime += stime; // we make no user/system time distinction + cutime += cstime; + + if (old_utime > 0) // check.. + { + int dcpu; + dcpu = utime - old_utime; // user_time from proc + if (dcpu < 0) + { + // why.. this occurs ? + // Qps exception:[3230,firefox] dcpu=-22 utime=39268 + // old_utime=39290 why + // occur? + if (flag_devel) + printf("Qps :[%d,%s] dcpu=%d utime=%ld " + "old_utime=%ld why occurs?\n", + pid, qPrintable(command), dcpu, utime, old_utime); + return 1; + } + + // gettimeofday(&tv, 0); //sys/time + if (proc->dt_total > 0) // move to Proc ?? + { + pcpu = 100.0 * dcpu / proc->dt_total; + if (Procview::flag_pcpu_single == true) + pcpu *= proc->num_cpus; // + } + // else too fast read again + // printf("Qps exception: dt_total=%d report to + // fasthyun@magicn.com + // \n",Proc::dt_total); + + if (flag_devel and pcpu > 100) // DEBUG CODE + { + printf("Qps pcpu error: %0.0f%% [%d,%s] dt_total=%ld " + "dcpu=%d utime=%ld " + "old_utime=%ld \n", + pcpu, pid, qPrintable(command), proc->dt_total, dcpu, utime, + old_utime); + pcpu = 99.99; + } + + const float a = Procview::avg_factor; + wcpu = a * old_wcpu + (1 - a) * pcpu; + } + old_tv = tv; + old_wcpu = wcpu; + old_utime = utime; // **** + + // read /proc/%PID/statm - memory usage + if (1) + { + if ((buf = read_proc_file2(path, "statm")) == 0) + return -1; // kernel 2.2 ? + sscanf(buf, "%lu %lu %lu %lu %lu %lu %lu", &size, &resident, &share, + &trs, &lrs, &drs, &dt); + size *= pagesize / 1024; // total memory in kByte + resident *= pagesize / 1024; + share *= pagesize / 1024; // share + // trs ; // text(code) + // lrs ; // zero : lib, awlays zero in + // Kernel 2.6 + // drs ; // data: wrong in kernel 2.6 + // dt ; // zero : in Kernel 2.6 + mem = resident - share; + // pmem = 100.0 * resident / proc->mem_total; + pmem = 100.0 * mem / proc->mem_total; + } + + // read /proc/PID/status check !! + if ((buf = read_proc_file2(path, "status")) == 0) + return -1; + else + { + // slpavg : not supported in kernel 2.4; default value of -1 + if (mini_sscanf(buf, "SleepAVG:%d", &slpavg) == 0) + slpavg = -1; + + if (strstr(buf, "VmSize:")) + { + // mini_sscanf(p, "VmSize: %d",&size); // XXX + // mini_sscanf(p, "VmRSS: %d",&resident); + // mini_sscanf(sbuf, "VmLib: %d",&share); + mini_sscanf(buf, "VmData: %d", &drs); // data in kByte + mini_sscanf(buf, "VmStk: %d", &stack); // stack in kByte + mini_sscanf(buf, "VmExe: %d", &trs); // text + } + } + + /* + generally + shared = RSS - ( CODE + DATA + STACK ) + share= resident - trs -drs -stack; + + // Defines from task_mmu.c of kernel source + total_vm==size + data = mm->total_vm - mm->shared_vm - mm->stack_vm; + swap = p->size - p->resident ; + */ + + // read /proc/PID/file_io + // NOTE: 2.6.11 dont have IO file + // COMPLEX_CODE + if ((buf = read_proc_file2(path, "io")) != 0) + { + // rchar = ... not file maybe sockread + // + mini_sscanf(buf, "read_bytes:%d", &io_read); + mini_sscanf(buf, "write_bytes:%d", &io_write); + + // if(io_read_prev!=0) + { + if (io_read_prev == 0) + io_read_prev = io_read; + if (io_write_prev == 0) + io_write_prev = io_write; + + // NOTE: Kbps right???? + io_read_KBps = (io_read - io_read_prev) / + proc->update_msec; // not accurate.... + io_write_KBps = (io_write - io_write_prev) / proc->update_msec; + + proc->io_byte += io_read_KBps; // test + proc->io_byte += io_write_KBps; + } + + io_read_prev = io_read; + io_write_prev = io_write; + + // io_read>>=10; // divide by 1024 + // io_write>>=10; // divide by 1024 + } + // per_cpu_times = 0; // not yet + + if ((buf = read_proc_file2(path, "wchan")) != 0) + { + wchan_str = buf; + } + + policy = -1; // will get it when needed + rtprio = -1; // ditto + tms = -1; // ditto + + // useless ? if(detail) detail->set_procinfo(this); // BAD !!! + return 2; // return ok. +} + +// just grab the load averages +// called by +void Proc::read_loadavg() +{ + char path[80]; + char buf[512]; + int n; + strcpy(path, "/proc/loadavg"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + fprintf(stderr, "qps: Cannot open /proc/loadavg (make sure " + "/proc is mounted)\n"); + exit(1); + } + buf[n] = '\0'; + sscanf(buf, "%f %f %f", &loadavg[0], &loadavg[1], &loadavg[2]); +} + +int Proc::countCpu() +{ + static bool first_run = true; + char path[80]; + char buf[1024 * 8]; // for SMP + + int num_cpus = 0, n; + // read system status /proc/stat + strcpy(path, "/proc/stat"); + // if((buf= read_proc_file("stat:)) ==0 ) return -1; + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + printf("Qps Error: /proc/stat can't be read ! check it and " + "report to " + "fasthyun@magicn.com\n"); + abort(); // return 0; + } + buf[n] = '\0'; + + // count (current) cpu of system + char *p; + p = strstr(buf, "cpu"); + while (p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) + { + num_cpus++; + if (strncmp(p, "cpu0", 4) == 0) + Proc::num_cpus--; + p = strchr(p, '\n'); + if (p) + p++; + } + + if (flag_devel and flag_SMPsim) + { + // num_cpus=64; + int vals[] = {2, 4, 8, 16, 32}; + int r = rand() % 5; + num_cpus = vals[r]; + } + return num_cpus; +} + +// LINUX +// Description: read common information for all processes +// return value +// -1 : too fast refresh ! +int Proc::read_system() // +{ + static bool first_run = true; + char path[80]; + char buf[1024 * 8]; // for SMP + + char *p; + int n; + + if (first_run) + { + /* Version 2.4.x ? */ + strcpy(path, "/proc/vmstat"); + if (!stat(path, (struct stat *)buf)) + flag_24_ok = false; + else + flag_24_ok = true; + + /* NPTL(Native POSIX Thread Library) */ + strcpy(path, "/proc/1/task"); + if (!stat(path, (struct stat *)buf)) + flag_thread_ok = true; + else + flag_thread_ok = false; + + /* check schedstat */ + strcpy(path, "/proc/1/schedstat"); // some system doesn't have + if (!stat(path, (struct stat *)buf)) + flag_schedstat = true; + else + flag_schedstat = false; + + strcpy(path, "/proc/stat"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return 0; + buf[n] = '\0'; + p = strstr(buf, "btime"); + if (p == NULL) + { + // used + printf("Qps: A bug occurs ! [boot_time] \n"); + // boot_time= current time + } + else + { + p += 6; + // sscanf(p, "%d", &Proc::boot_time); //???? why + // segfault??? + sscanf(p, "%d", &boot_time); + } + + // Max SMP 1024 cpus, MOVETO: COMMON + int max_cpus = 512; + cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; //??? +2 + old_cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; + + // init + for (int cpu = 0; cpu < max_cpus; cpu++) + for (int i = 0; i < CPUTIMES; i++) + { + cpu_times(cpu, i) = 0; + old_cpu_times(cpu, i) = 0; + } + + // first_run=false; // not yet , at the bottom of this function + } + + // read system status /proc/stat + strcpy(path, "/proc/stat"); + // if((buf= read_proc_file("stat:)) ==0 ) return -1; + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + printf("Qps Error: /proc/stat can't be read ! check it and " + "report to " + "fasthyun@magicn.com\n"); + abort(); // return 0; + } + buf[n] = '\0'; + + if (true) + { + // count (current) cpu of system + char *p; + p = strstr(buf, "cpu"); + num_cpus = 0; + while (p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) + { + num_cpus++; + if (strncmp(p, "cpu0", 4) == 0) + Proc::num_cpus--; + p = strchr(p, '\n'); + if (p) + p++; + } + + if (flag_SMPsim) + { + int vals[] = {2, 4, 8, 16, 32}; + int r = rand() % 5; + num_cpus = vals[r]; + num_cpus = 8; + } + + // Hotplugging Detection : save total_cpu + if (Proc::num_cpus != Proc::old_num_cpus) + { + // for(int i = 0; i < CPUTIMES; i++) + // cpu_times(num_cpus, i) = + // cpu_times(Proc::old_num_cpus, + // i); + + Proc::old_num_cpus = Proc::num_cpus; + } + } + + // backup old values : important******* + for (int cpu = 0; cpu < Proc::num_cpus + 1; cpu++) + { + for (int i = 0; i < CPUTIMES; i++) + old_cpu_times(cpu, i) = cpu_times(cpu, i); + } + + /* + /proc/stat + cpu# user nice system idle iowait(2.6) + irq(2.6) sft(2.6) steal(2.6.11) guest(2.6.24) + cpu0 3350 9 535 160879 + 1929 105 326 5 + 1200 + + Q1: kernel 2.4 cpu0 exist ? + */ + + // Total_cpu + int total_cpu = Proc::num_cpus; + unsigned user, nice, system, idle, iowait, irq, sftirq, steal, guest, nflds; + nflds = sscanf(buf, "cpu %u %u %u %u %u %u %u %u %u", &user, &nice, &system, + &idle, &iowait, &irq, &sftirq, &steal, &guest); + if (nflds > 4) + { + // kernel 2.6.x + system += (irq + sftirq); + idle += iowait; + } + if (nflds == 9) + { + system += steal; + system += guest; + } + cpu_times(Proc::num_cpus, CPUTIME_USER) = user; + cpu_times(Proc::num_cpus, CPUTIME_NICE) = nice; + cpu_times(Proc::num_cpus, CPUTIME_SYSTEM) = system; + cpu_times(Proc::num_cpus, CPUTIME_IDLE) = idle; + + // DRAFT! + // num_cpus == total_cpu + // + // dt_total= user + system + nice + idle + // dt_used= user + system; + Proc::dt_used = + user - old_cpu_times(Proc::num_cpus, + CPUTIME_USER); // infobar uses this value + Proc::dt_used += system - old_cpu_times(Proc::num_cpus, CPUTIME_SYSTEM); + Proc::dt_total = dt_used + nice - + old_cpu_times(Proc::num_cpus, CPUTIME_NICE) + idle - + old_cpu_times(Proc::num_cpus, CPUTIME_IDLE); + + load_cpu = (float)Proc::dt_used / Proc::dt_total; // COMMON + + if (first_run) + { + // printf("\n==================== tooo fast + //=================================\n"); + // printf("DEBUG:dt_total=%d + // dt_used=%d\n",Proc::dt_total,Proc::dt_used); + // return -1; // too early refresh again !! + } + if (Proc::dt_total == 0) + { + //????? + printf("Error: dt_total=0 , dt_used=%ld(%u) report to " + "fasthyun@magicn.com\n", + Proc::dt_used, old_cpu_times(Proc::num_cpus, CPUTIME_IDLE)); + dt_total = 500; // more tolerable? + // abort(); // stdlib.h + } + + // void watchdog_syscpu(int ); + // watchdog_syscpu((user-old_cpu_times(num_cpus,CPUTIME_USER))*100/dt_total); + //// test + + // if(flag_devel and flag_SMPsim ) + if (flag_SMPsim) + { + // for Developer only !!! + // printf("user%d nuce%d system%d + // idle%d\n",user,nice,system,idle); + for (int cpu = 0; cpu < num_cpus; cpu++) + { + // stdlib.h, int rand(); + if (dt_used != 0) + cpu_times(cpu, CPUTIME_USER) = + old_cpu_times(cpu, CPUTIME_USER) + rand() % dt_used; + else + cpu_times(cpu, CPUTIME_USER) = 0; + cpu_times(cpu, CPUTIME_NICE) = nice; + cpu_times(cpu, CPUTIME_SYSTEM) = system; + cpu_times(cpu, CPUTIME_IDLE) = idle; + } + } + else + { + // Single-CPU and SMP(Multi-CPU) + for (int cpu = 0; cpu < num_cpus; cpu++) + { + char cpu_buf[10]; + sprintf(cpu_buf, "cpu%d", cpu); + if ((p = strstr(buf, cpu_buf)) != 0) + { + nflds = sscanf(p, "%*s %u %u %u %u %u %u %u %u %u", + &cpu_times(cpu, CPUTIME_USER), + &cpu_times(cpu, CPUTIME_NICE), + &cpu_times(cpu, CPUTIME_SYSTEM), + &cpu_times(cpu, CPUTIME_IDLE), &iowait, &irq, + &sftirq, &steal, &guest); + // cpu_times(cpu, CPUTIME_USER),cpu_times(cpu, + // CPUTIME_NICE), + if (nflds > 4) + { + // kernel 2.6.x + cpu_times(cpu, CPUTIME_SYSTEM) += (irq + sftirq); + cpu_times(cpu, CPUTIME_IDLE) += iowait; + } + if (nflds == 9) + { + cpu_times(cpu, CPUTIME_SYSTEM) += (steal + guest); + } + + // 2.4.27-SMP bug ? + } + else + { + fprintf(stderr, "Qps: Error reading info for " + "cpu%d (/proc/stat)\n", + cpu); + abort(); + } + } + } + + // read memory info + strcpy(path, PROCDIR); + strcat(path, "/meminfo"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return 0; + buf[n] = '\0'; + + // Skip the old /meminfo cruft, making this work in post-2.1.42 kernels + // as well. (values are now in kB) + if ((p = strstr(buf, "MemTotal:"))) + sscanf(p, "MemTotal: %d kB\n", &mem_total); + if ((p = strstr(buf, "MemFree:")) != NULL) + sscanf(p, "MemFree: %d kB\n", &mem_free); + if ((p = strstr(buf, "Buffers:")) != NULL) + sscanf(p, "Buffers: %d kB\n", &mem_buffers); + if ((p = strstr(buf, "Cached:")) != NULL) + sscanf(p, "Cached: %d kB\n", &mem_cached); + + p = strstr(buf, "SwapTotal:"); + sscanf(p, "SwapTotal: %d kB\nSwapFree: %d kB\n", &swap_total, &swap_free); + + first_run = false; + return 0; +} + +int Procinfo::get_policy() +{ + if (policy == -1) + policy = sched_getscheduler(pid); + return policy; +} + +int Procinfo::get_rtprio() +{ + if (rtprio == -1) + { + struct sched_param p; + if (sched_getparam(pid, &p) == 0) + rtprio = p.sched_priority; + } + return rtprio; +} + +double Procinfo::get_tms() +{ + struct timespec ts; + if (sched_rr_get_interval(pid, &ts) == -1) // POSIX + tms = -1; // should not be possible + else + { + tms = ts.tv_nsec; // nano seconds + tms /= 1000000; // mili seconds + tms += ts.tv_sec * 1000; + } + return tms; +} + +unsigned long Procinfo::get_affcpu() +{ +#ifdef QPS_SCHED_AFFINITY + if (qps_sched_getaffinity(pid, sizeof(unsigned long), &affcpu) == -1) + affcpu = (unsigned long)0; +#else + if (sched_getaffinity(pid, sizeof(unsigned long), (cpu_set_t *)&affcpu) == + -1) + affcpu = (unsigned long)0; +#endif + return affcpu; +} + +// Description : read /proc/PID/fd/* (SYMBOLIC LINK NAME) +/* We need to implement support for IPV6 and sctp ? */ +void Procinfo::read_fd(int fdnum, char *path) +{ + int len; + char buf[128]; + struct stat sb; + + // The fd mode is contained in the link permission bits + if (lstat(path, &sb) < 0) + return; + int mode = 0; + if (sb.st_mode & 0400) + mode |= OPEN_READ; + if (sb.st_mode & 0200) + mode |= OPEN_WRITE; + + if ((len = readlink(path, buf, sizeof(buf) - 1)) > 0) + { + buf[len] = '\0'; + unsigned long dev, ino; + + // check socket_fd + if ((buf[0] == '[' and sscanf(buf, "[%lx]:%lu", &dev, &ino) == 2 and + dev == 0) // Linux 2.0 style + || + sscanf(buf, "socket:[%lu]", &ino) > 0) // Linux 2.1 upper + { + Sockinfo *si = NULL; + si = proc->socks.value(ino, NULL); // sock + char buf[80]; + if (si) + { + printf("sock ino=%lu\n", ino); + si->pid = pid; + // a TCP or UDP socket + sock_inodes.append(new SockInode(fdnum, ino)); + sprintf(buf, "%s socket %lu", + si->proto == Sockinfo::TCP ? "TCP" : "UDP", ino); + fd_files.append(new Fileinfo(fdnum, buf, mode)); + return; + } + else + { + // maybe a unix domain socket? + // read_usockets(); + UnixSocket *us = NULL; + + us = proc->usocks.value(ino, NULL); + if (us) + { + const char *tp = "?", *st = "?"; + switch (us->type) + { + case SOCK_STREAM: + tp = "stream"; + break; + case SOCK_DGRAM: + tp = "dgram"; + break; + } + switch (us->state) + { + case SSFREE: + st = "free"; + break; + case SSUNCONNECTED: + st = "unconn"; + break; + case SSCONNECTING: + st = "connecting"; + break; + case SSCONNECTED: + st = "connected"; + break; + case SSDISCONNECTING: + st = "disconn"; + break; + } + sprintf(buf, "UNIX domain socket %lu (%s, %s) ", ino, tp, + st); + QString s = buf; + s.append(us->name); + fd_files.append(new Fileinfo(fdnum, s, mode)); + return; + } + } + } + // normal filess + // assume fds will be read in increasing order + fd_files.append(new Fileinfo(fdnum, buf, mode)); + } +} + +// Description : +// read /PID/fd opened files +// return true if /proc/PID/fd could be read, false otherwise +// store fileinfo, and also socket inodes separately +// +// called by Detail() +bool Procinfo::read_fds() +{ + char path[128], *p; + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/fd", pid, pid); + else + sprintf(path, "/proc/%d/fd", pid); + + DIR *d = opendir(path); + if (!d) + return false; + + /* + if(!sock_inodes) sock_inodes = new Svec(4); + else sock_inodes->purge(); */ + + strcat(path, "/"); + + fd_files.clear(); // + struct dirent *e; + while ((e = readdir(d)) != 0) + { + char str[128]; + if (e->d_name[0] == '.') + continue; // skip . and .. + int fdnum = atoi(e->d_name); + strcpy(str, path); + strcat(str, e->d_name); + // printf("str=%s\n",str); + read_fd(fdnum, str); + } + // printf("end str=\n"); + closedir(d); + return true; +} + +// tcp, udp +bool Proc::read_socket_list(Sockinfo::proto_t proto, const char *filename) +{ + char path[80]; + sprintf(path, "/proc/net/%s", filename); + FILE *f = fopen(path, "r"); + if (!f) + return false; + + char buf[128 * 3]; + // Header + // sl local_addr rem_addr st tx_queue rx_queue tr tm->when retrnsmt + // uid + // timeout inode + + Sockinfo si; + + printf("read_socket_list()\n"); + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f) != 0) + { + // Sockinfo *si = new Sockinfo; + si.pid = -1; + si.proto = proto; + unsigned local_port, rem_port, st, tr; + sscanf(buf + 6, "%x:%x %x:%x %x %x:%x %x:%x %x %d %d %d", + &si.local_addr, &local_port, &si.rem_addr, &rem_port, &st, + &si.tx_queue, &si.rx_queue, &tr, &si.tm_when, &si.rexmits, + &si.uid, &si.timeout, &si.inode); + // fix fields that aren't sizeof(int) + si.local_port = local_port; + si.rem_port = rem_port; + si.st = st; + si.tr = tr; + + Sockinfo *psi; + psi = socks.value(si.inode, NULL); + if (psi == NULL) + { + printf("inode =%d \n", si.inode); + psi = new Sockinfo; + *psi = si; + socks.insert(si.inode, psi); + } + else + *psi = si; + + // linear_socks.add(si); + } + fclose(f); + return true; +} + +bool Proc::read_usocket_list() +{ + char path[80]; + strcpy(path, PROCDIR); + strcat(path, "/net/unix"); + FILE *f = fopen(path, "r"); + if (!f) + return false; + + char buf[256]; + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f)) + { + if (buf[0]) + buf[strlen(buf) - 1] = '\0'; // chomp newline + // UnixSocket *us = new UnixSocket; + UnixSocket us; + + unsigned q; + unsigned type, state; + int n; + sscanf(buf, "%x: %x %x %x %x %x %ld %n", + // Num refcount protocol flags type state indoe path + &q, &q, &q /*protocol*/, &us.flags, &type, &state, &us.inode, + &n); + us.name = buf + n; + us.type = type; + us.state = state; + + UnixSocket *pus; + pus = usocks.value(us.inode, NULL); + if (pus == NULL) + { + printf("inode =%lu \n", us.inode); + + pus = new UnixSocket; + *pus = us; + usocks.insert(us.inode, pus); + } + else + *pus = us; + } + fclose(f); + return true; +} + +void Proc::read_sockets() +{ + // socks.clear(); + + // memory leak !! + if (!read_socket_list(Sockinfo::TCP, "tcp") || + !read_socket_list(Sockinfo::UDP, "udp")) + return; + // remove no more socket ino + read_usocket_list(); + + socks_current = true; +} + +void Proc::read_usockets() +{ + if (usocks_current) + return; + + usocks.clear(); + if (!read_usocket_list()) + return; + + usocks_current = true; +} + +void Proc::invalidate_sockets() { socks_current = usocks_current = false; } + +// return true if /proc/XX/maps could be read, false otherwise +bool Procinfo::read_maps() +{ + // idea: here we could readlink /proc/XX/exe to identify the executable + // when running 2.0.x + char name[80]; + + if (flag_thread_ok && flag_show_thread) + sprintf(name, "/proc/%d/task/%d/maps", pid, pid); + else + sprintf(name, "/proc/%d/maps", pid); + + FILE *f = fopen(name, "r"); + if (!f) + return false; + + char line[1024]; // lines can be longer , SEGFAULT + + while (fgets(line, sizeof(line), f)) + { + Mapsinfo *mi = new Mapsinfo; + int n; + sscanf(line, "%lx-%lx %4c %lx %x:%x %lu%n", + // sscanf(line, "%lx-%lx %4c %lx %x:%x %n", + &mi->from, &mi->to, mi->perm, &mi->offset, &mi->major, + &mi->minor, &mi->inode, &n); + if (line[n] != '\n') + { + int len = strlen(line); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + while (line[n] == ' ' && line[n]) + n++; + mi->filename = line + n; + } + else if ((mi->major | mi->minor | mi->inode) == 0) + mi->filename = "(anonymous)"; + maps.append(mi); + } + fclose(f); + if (maps.size() == 0) + return false; + + return true; +} + +// DRAFT CODE: +// return true if /proc/777/environ could be read, false otherwise +int fsize(char *fname); +bool Procinfo::read_environ() +{ + int file_size = 0; + int size; + char path[256]; + + environ.clear(); // + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/environ", pid, pid); + else + sprintf(path, "/proc/%d/environ", pid); + + file_size = fsize(path); + if (file_size <= 0) + return false; + + envblock = (char *)malloc(file_size + 1); + size = read_file(path, envblock, file_size + 1); + if (size <= 0) + return false; + + // kernel 2.6.x has a bug + if (envblock[size - 2] == 0) // how to check the bug. + { + char buf[128]; + sprintf(buf, "Kernel Bug= wrong environments ! please, check " + "/proc/%d/environ !", + pid); + size = strlen(buf) + 1; + if (file_size > size) + strcpy(envblock, buf); + } + + int i = 0, n = 0, v = 0; + char ch; + + for (i = 0; i < size; i++) + { + ch = envblock[i]; + if (ch == '=') + { + envblock[i] = 0; + v = i + 1; + } + if (ch == 0) + { + NameValue nv(&envblock[n], &envblock[v]); + environ.append(nv); + // printf("%s %s\n",&envblock[n],&envblock[v]); + n = i + 1; + } + } + if (envblock) + { + free(envblock); // refresh() // INVALID VALGRIND + envblock = 0; + } + return true; +} + +// CWD,ROOT only so... +Cat_dir::Cat_dir(const char *heading, const char *explain, const char *dirname, + QString Procinfo::*member) + : Cat_string(heading, explain), dir(dirname), cache(member) +{ +} + +QString Cat_dir::string(Procinfo *p) +{ + if ((p->*cache).isNull()) + { + char path[128], buf[512]; + + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/%s", p->pid, p->pid, dir); + else + sprintf(path, "/proc/%d/%s", p->pid, dir); + + int n = readlink(path, buf, sizeof(buf) - 1); + if (n < 0) + { + // Either a kernel process, or access denied. + // A hyphen is visually least disturbing here. + p->*cache = "-"; + return p->*cache; + } + else if (buf[0] != '[') + { + // linux >= 2.1.x: path name directly in link + buf[n] = '\0'; + p->*cache = buf; + return p->*cache; + } + + // Either a Linux 2.0 link in [device]:inode form, or a Solaris + // link. + // To resolve it, we just chdir() to it and see where we end up. + // Perhaps we should change back later? + if (chdir(path) < 0) + { + p->*cache = "-"; // Most likely access denied + } + else + { + // getcwd() is fairly expensive, but this is cached + // anyway + if (!getcwd(buf, sizeof(buf))) + { + p->*cache = "(deleted)"; + } + else + p->*cache = buf; + } + } + return p->*cache; +} + +Cat_state::Cat_state(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_state::string(Procinfo *p) +{ + QString s(" "); + int ni = p->nice; + + s[0] = p->state; + s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' '; + s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' '); + return s; +} + +// LINUX +Cat_policy::Cat_policy(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_policy::string(Procinfo *p) +{ + QString s; + switch (p->get_policy()) + { + case SCHED_FIFO: + s = "FI"; + break; // first in, first out + case SCHED_RR: + s = "RR"; + break; // round-robin + case SCHED_OTHER: + s = "TS"; + break; // time-sharing + default: + s = "??"; + break; + } + return s; +} + +int Cat_policy::compare(Procinfo *a, Procinfo *b) +{ + return b->get_policy() - a->get_policy(); +} + +Cat_rtprio::Cat_rtprio(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_rtprio::string(Procinfo *p) +{ + QString s; + s.setNum(p->get_rtprio()); + return s; +} + +int Cat_rtprio::compare(Procinfo *a, Procinfo *b) +{ + return b->get_rtprio() - a->get_rtprio(); +} + +// maybe tms COMMON +Cat_tms::Cat_tms(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_tms::string(Procinfo *p) +{ + QString s; + p->tms = p->get_tms(); + s.sprintf("%.3f", p->tms); + // s.sprintf("%d",p->tms); + return s; +} + +int Cat_tms::compare(Procinfo *a, Procinfo *b) +{ + return (int)((b->get_tms() - a->get_tms()) * 1000); +} + +Cat_affcpu::Cat_affcpu(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_affcpu::string(Procinfo *p) +{ + QString s; + p->affcpu = p->get_affcpu(); + s.sprintf("%lx", p->affcpu); + return s; +} +/* + int Cat_affcpu::compare(Procinfo *a, Procinfo *b) + { + return (int)(b->affcpu - a->affcpu); + } + */ + +// LINUX or COMMON? +Cat_time::Cat_time(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_time::string(Procinfo *p) +{ + QString s; + char buff[64]; + int ticks = p->utime; + int ms; + + int proctick = p->proc->clk_tick; + + if (Procview::flag_cumulative) + ticks += p->cutime; + + int t = ticks / p->proc->clk_tick; // seconds + // COMPLEX CODE + if (t < 10) + { // ex. 9.23s + ms = ticks / (p->proc->clk_tick / 100) % 100; // Need FIX + sprintf(buff, "%1d.%02ds", t, ms); + } + else if (t < 60) + { // ex. 48s + sprintf(buff, "%5ds", t); + } + else if (t < 60 * 10) + { // ex. 8.9m, 9.0m + sprintf(buff, "%2d.%1dm", t / 60, (t % 60) / 6); + } + else if (t < 60 * 60) + { // 58m + sprintf(buff, "%5dm", t / 60); + } + else if (t < 24 * 3600) + { // + int h = t / 3600; // 1hour = 3600 = 60m*60s + t %= 3600; + sprintf(buff, "%2d:%02d", h, t / 60); + } + else if (t < 10 * 24 * 3600) + { // + int d = t / 86400; // 1 day = 24* 3600s + t %= 86400; + sprintf(buff, "%2d.%1dd", d, (t * 10 / (3600 * 24))); + } + else + { + int d = t / 86400; // 1 day = 24* 3600s + sprintf(buff, "%5dd", d); + } + s = buff; + return s; +} + +int Cat_time::compare(Procinfo *a, Procinfo *b) +{ + int at = a->utime, bt = b->utime; + if (Procview::flag_cumulative) + { + at += a->cutime; + bt += b->cutime; + } + return bt - at; +} + +// LINUX ? +Cat_tty::Cat_tty(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_tty::string(Procinfo *p) { return Ttystr::name(p->tty); } + +Proc::Proc() +{ + // Note: + categories.insert(F_PID, + new Cat_int("PID", "Process ID", 6, &Procinfo::pid)); + categories.insert(F_TGID, + new Cat_int("TGID", "Task group ID ( parent of threads )", + 6, &Procinfo::tgid)); + categories.insert( + F_PPID, new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid)); + categories.insert( + F_PGID, new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp)); + categories.insert(F_SID, + new Cat_int("SID", "Session ID", 6, &Procinfo::session)); + categories.insert(F_TTY, new Cat_tty("TTY", "Terminal")); + categories.insert(F_TPGID, + new Cat_int("TPGID", "Process group ID of tty owner", 6, + &Procinfo::tpgid)); + + categories.insert( + F_USER, new Cat_string("USER", "Owner (*=suid root, +=suid a user)", + &Procinfo::username)); + categories.insert(F_GROUP, + new Cat_string("GROUP", "Group name (*=sgid other)", + &Procinfo::groupname)); + + categories.insert(F_UID, + new Cat_int("UID", "Real user ID", 6, &Procinfo::uid)); + categories.insert( + F_EUID, new Cat_int("EUID", "Effective user ID", 6, &Procinfo::euid)); + categories.insert(F_SUID, new Cat_int("SUID", "Saved user ID (Posix)", 6, + &Procinfo::suid)); + categories.insert(F_FSUID, new Cat_int("FSUID", "File system user ID", 6, + &Procinfo::fsuid)); + categories.insert(F_GID, + new Cat_int("GID", "Real group ID", 6, &Procinfo::gid)); + categories.insert( + F_EGID, new Cat_int("EGID", "Effective group ID", 6, &Procinfo::egid)); + categories.insert(F_SGID, new Cat_int("SGID", "Saved group ID (Posix)", 6, + &Procinfo::sgid)); + categories.insert(F_FSGID, new Cat_int("FSGID", "File system group ID", 6, + &Procinfo::fsgid)); + categories.insert( + F_PRI, new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority)); + categories.insert(F_NICE, + new Cat_int("NICE", + "Scheduling favour (higher -> less cpu time)", + 4, &Procinfo::nice)); + categories.insert( + F_NLWP, new Cat_int("NLWP", "Number of tasks(threads) in task group", 5, + &Procinfo::nthreads)); + + categories.insert(F_PLCY, new Cat_policy("PLCY", "Scheduling policy")); + categories.insert( + F_RPRI, + new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)")); + categories.insert(F_TMS, new Cat_tms("TMS", "Time slice in milliseconds")); + categories.insert(F_SLPAVG, + new Cat_int("%SAVG", + "Percentage average sleep time (-1 -> N/A)", + 4, &Procinfo::slpavg)); + categories.insert( + F_AFFCPU, + new Cat_affcpu("CPUSET", + "Affinity CPU mask (0 -> API not supported)")); // ??? + categories.insert(F_MAJFLT, + new Cat_uintl("MAJFLT", + "Number of major faults (disk access)", 8, + &Procinfo::majflt)); + categories.insert(F_MINFLT, + new Cat_uintl("MINFLT", + "Number of minor faults (no disk access)", + 8, &Procinfo::minflt)); + + // Memory + categories.insert(F_SIZE, + new Cat_memory("VSIZE", "Virtual image size of process", + 8, &Procinfo::size)); + categories.insert(F_RSS, new Cat_memory("RSS", "Resident set size", 8, + &Procinfo::resident)); + categories.insert(F_MEM, new Cat_memory("MEM", "memory usage (RSS-SHARE)", + 8, &Procinfo::mem)); + categories.insert(F_TRS, + new Cat_memory("TRS", "Text(code) resident set size", 8, + &Procinfo::trs)); + categories.insert( + F_DRS, + new Cat_memory("DRS", "Data resident set size(malloc+global variable)", + 8, &Procinfo::drs)); + categories.insert( + F_STACK, new Cat_memory("STACK", "Stack size", 8, &Procinfo::stack)); + categories.insert(F_SHARE, + new Cat_memory("SHARE", "Shared memory with other libs", + 8, &Procinfo::share)); + categories.insert(F_SWAP, new Cat_swap("SWAP", "Kbytes on swap device")); + categories.insert( + F_IOR, new Cat_memory("IO_R", "io read (file)", 8, &Procinfo::io_read)); + categories.insert(F_IOW, new Cat_memory("IO_W", "io write (file)", 8, + &Procinfo::io_write)); + + categories.insert(F_DT, + new Cat_uintl("DT", "Number of dirty (non-written) pages", + 7, &Procinfo::dt)); + categories.insert(F_STAT, new Cat_state("STAT", "State of the process ")); + categories.insert(F_FLAGS, new Cat_hex("FLAGS", "Process flags (hex)", 9, + &Procinfo::flags)); + categories.insert( + F_WCHAN, + new Cat_wchan("WCHAN", "Kernel function where process is sleeping")); + categories.insert( + F_WCPU, + new Cat_percent("%WCPU", "Weighted percentage of CPU (30 s average)", 6, + &Procinfo::wcpu)); + categories.insert( + F_CPU, + new Cat_percent("%CPU", "Percentage of CPU used since last update", 6, + &Procinfo::pcpu)); + categories.insert( + F_PMEM, + new Cat_percent("%MEM", "Percentage of memory used (RSS/total mem)", 6, + &Procinfo::pmem)); + categories.insert(F_START, new Cat_start("START", "Time process started")); + categories.insert(F_TIME, + new Cat_time("TIME", "Total CPU time used since start")); + categories.insert( + F_CPUNUM, + new Cat_int("CPU", "CPU the process is executing on (SMP system)", 3, + &Procinfo::which_cpu)); + + categories.insert(F_CMD, new Cat_string("Process Name", "the process name", + &Procinfo::command)); + // categories.insert(F_PROCESSNAME, new Cat_string("Process Name", + //"the + // process name", &Procinfo::command)); + categories.insert(F_CWD, new Cat_dir("CWD", "Current working directory", + "cwd", &Procinfo::cwd)); + categories.insert(F_ROOT, new Cat_dir("ROOT", "Root directory of process", + "root", &Procinfo::root)); + + // command_line="COMMAND_LINE"; //reference to /proc/1234/cmdline + categories.insert(F_CMDLINE, + new Cat_cmdline("COMMAND_LINE", + "Command line that started the process")); + + commonPostInit(); + + socks_current = false; + usocks_current = false; + + Proc::init_static(); +} + +Proc::~Proc() +{ + // killall procinfos +} + +// COMMON for LINUX,SOLARIS +// Polling /proc/PID/* +void Proc::read_proc_all() +{ + DIR *d = opendir("/proc"); + struct dirent *e; + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] >= '0' and e->d_name[0] <= '9') + { // good idea ! + int pid; + Procinfo *pi = NULL; + + // inline int x_atoi(const char *sstr); + // pid=x_atoi(e->d_name); //if(pid<100) continue; + pid = atoi(e->d_name); + + pi = procs.value(pid, NULL); + + if (pi == NULL) // new process + { + pi = new Procinfo(this, pid); + procs.insert(pid, pi); + /* + Procinfo *parent; + parent =procs[pi->ppid]; + if(parent) + parent->children->add(pi); + printf("Qps : parent null + pid[%d]\n",pi->pid); } + */ + } + int ret = pi->readproc(); + if (ret > 0) + { + pi->generation = current_gen; // this process is alive + // printf(" [%s] %d + // %d\n",pi->command.toAscii().data(),pi->generation,current_gen); + + if (flag_show_thread and flag_thread_ok) + read_pid_tasks(pid); // for threads + + // add to History expect thread + if (ret == 2) + { + Procinfo *p = new Procinfo(*pi); // copy + p->clone = true; + hprocs->insert(pid, p); + } + } + else + { + // already gone. /proc/PID dead! + // later remove this process ! not yet + } + } + } + closedir(d); +} + +// COMMON , redesign +Proclist Proc::getHistory(int pos) +{ + Proclist l; + if (pos <= 0) + { + return l; + } + int size = history.size(); + if (size > pos) + l = history[size - pos]->procs; + return l; +} + +void Proc::setHistory(int tick) +{ + return; + if (tick <= 0) + { + mprocs = 0; + return; + } + int size = history.size(); + if (size > tick) + mprocs = &history[size - tick]->procs; + else + mprocs = 0; +} + +bool Procinfo::isThread() +{ + return pid != tgid; // how to check +} + +// LINUX size=64 +int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE, + F_SIZE, F_MEM, F_STAT, F_CPU, + F_START, F_TIME, F_CMDLINE, F_END}; + +// COMMON: basic field +int Procview::basic_fields[] = {F_PID, F_TTY, F_USER, F_CPUNUM, + F_STAT, F_MEM, F_CPU, F_START, + F_TIME, F_CMDLINE, F_END}; + +int Procview::jobs_fields[] = {F_PID, F_TGID, F_PPID, F_PGID, + F_SID, F_TPGID, F_STAT, F_UID, + F_TIME, F_CMDLINE, F_END}; + +int Procview::mem_fields[] = {F_PID, F_TTY, F_MAJFLT, F_MINFLT, F_SIZE, + F_RSS, F_TRS, F_DRS, F_STACK, F_SHARE, + // F_DT, + F_CMDLINE, F_END}; + +int Procview::sched_fields[] = {F_PID, F_TGID, F_NLWP, F_STAT, F_FLAGS, + F_PLCY, F_PRI, F_NICE, F_TMS, F_SLPAVG, + F_RPRI, F_AFFCPU, F_CPU, F_START, F_TIME, + F_CMDLINE, F_END}; + +void Procview::set_fields() +{ + switch (viewfields) + { + case USER: // BASIC FIELD + set_fields_list(basic_fields); + break; + case JOBS: + set_fields_list(jobs_fields); + break; + case MEM: + set_fields_list(mem_fields); + break; + case SCHED: + set_fields_list(sched_fields); + break; + case CUSTOM: + set_fields_list(custom_fields); + break; + default: + printf("Error ? set_fields_list \n"); + } + fieldArrange(); +} + +// LINUX: +// deduce whether the currently selected fields correspond to a field list +void Procview::deduce_fields() +{ + return; // under development (by fasthyun@magicn.com) 2006/05/24 + + if (viewfields != CUSTOM) + return; + + Procview::fieldstates tags[4] = {USER, JOBS, MEM, SCHED}; + int *lists[4] = {basic_fields, jobs_fields, mem_fields, sched_fields}; + for (int i = 0; i < 4; i++) + { + int *l = lists[i]; + int j; + for (j = 0; l[j] != F_END; j++) + if (findCol(l[j]) < 0) + break; + if (l[j] == F_END && j == cats.size()) + { + viewfields = tags[i]; + return; + } + } +} + +// move to Proc.cpp +#include // uname() +int get_kernel_version() +{ + int version = 0; + char *p; + struct utsname uname_info; + if (uname(&uname_info) == 0) // man -S 2 uname + { + // printf("sysname =%s \n",uname_info.sysname); + if (strcasecmp(uname_info.sysname, "linux") == 0) + { + Q_UNUSED(uname_info.release[0]); + } + p = uname_info.release; + char str[32]; + int major, minor, patch; + int result; + + result = sscanf(p, "%d.%d.%d", &major, &minor, &patch); + if (result == 2) + { + // only read two value + patch = 0; // ex) 3.0-ARCH + } + else if (result < 3) + { + fprintf(stderr, "Qps: can't determine version, read %s \n", p); + fprintf(stderr, "please report this bug.\n"); + exit(1); + } + version = major * 10000 + minor * 100 + patch; + // ex) 2.6.17 == 20617 , 2.4.8 == 20408 + printf("DEBUG: version = %d\n", version); + } + else + { + fprintf(stderr, "Qps: uname() failed. (%d) \n", version); + fprintf(stderr, "please report this bug.\n"); + exit(1); + } + return version; +} + +void check_system_requirement() +{ + int kernel_version = 0; + kernel_version = get_kernel_version(); + if (kernel_version < 20600) // less 2.6 + { + printf("Qps: kernel 2.4.x not supported !!!\n\n"); // because of + // 2.4.x SMP + // bugs + exit(0); + } +} diff --git a/src/proc.h b/src/proc.h new file mode 100644 index 0000000..efa7c75 --- /dev/null +++ b/src/proc.h @@ -0,0 +1,873 @@ +// proc.h +// emacs, this is written in -*-c++-*- (who?) +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 +// + +#ifndef PROC_H +#define PROC_H + +#include "config.h" + +#ifdef SOLARIS +#include // kstat_ctl_t +#endif + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#endif + +class Procinfo; +int read_file(char *name, char *buf, int max); +int read_file_addNULL(char *name, void *buf, int max); +void check_system_requirement(); +int get_kernel_version(); + +//#define F_PID 0x00000000 +enum fields +{ + F_PID = 0, +#ifdef LINUX + F_TGID, +#endif + F_PPID, + F_PGID, + F_SID, + F_TTY, +#ifdef LINUX + F_TPGID, +#endif +#ifdef MOSIX + F_MIGR, + F_LOCKED, + F_NMIGS, + F_NOMOVE, + F_RPID, +#endif + F_USER, + F_GROUP, + F_UID, + F_EUID, +#ifdef LINUX + F_SUID, + F_FSUID, +#endif + F_GID, + F_EGID, +#ifdef LINUX + F_SGID, + F_FSGID, +#endif + F_PRI, + F_NICE, + F_PLCY, + F_RPRI, +#ifdef LINUX + F_TMS, + F_AFFCPU, + F_SLPAVG, +#endif + F_NLWP, +#ifdef SOLARIS + F_ARCH, +#endif + F_MAJFLT, + F_MINFLT, +#ifdef LINUX + F_TRS, + F_DRS, + F_STACK, +#endif + F_SIZE, // VSIZE + F_SWAP, // Linux not correct + F_MEM, + F_RSS, +#ifdef LINUX + F_SHARE, + F_DT, + F_IOW, + F_IOR, +#endif + F_STAT, + F_FLAGS, + F_WCHAN, + F_WCPU, + F_CPU, /* %CPU */ + F_PMEM, // F_PMEM: %MEM + F_START, + F_TIME, + F_CPUNUM, + F_CMD, + F_PROCESSNAME, // NEW + F_CWD, + F_ROOT, //? + F_CMDLINE, + F_END = -1 +}; + +class Details; + +#ifdef LINUX + +class Sockinfo +{ + public: + enum proto_t + { + TCP, + UDP + }; + proto_t proto; + unsigned char st; + unsigned char tr; + unsigned local_addr; + unsigned rem_addr; + unsigned short local_port; + unsigned short rem_port; + unsigned tx_queue; + unsigned rx_queue; + unsigned tm_when; + unsigned rexmits; + int uid; //?? + int pid; + int timeout; + int inode; +}; + +class SockInode +{ + public: + SockInode(int descr, unsigned long ino) : fd(descr), inode(ino){}; + int fd; + unsigned long inode; +}; + +class UnixSocket +{ + public: + unsigned long inode; + QString name; + unsigned flags; + unsigned char type; // SOCK_STREAM or SOCK_DGRAM + unsigned char state; // SS_FREE, SS_UNCONNECTED, SS_CONNECTING, + // SS_CONNECTED, SS_DISCONNECTING +}; + +#endif // LINUX + +// COMMON +class Mapsinfo +{ + public: + unsigned long from, to; + unsigned long offset; + unsigned long inode; + QString filename; // null if name unknown + char perm[4]; // "rwx[ps]"; last is private/shared flag + unsigned minor, major; +}; + +#define OPEN_READ 1 +#define OPEN_WRITE 2 + +class Fileinfo +{ + public: + Fileinfo(int descr, QString name, int open_mode = 0) + : fd(descr), filename(name), mode(open_mode){}; + int fd; + QString filename; // "major:minor inode" in Linux 2.0, + // texual description in Solaris 2.6 + unsigned mode; // bits from OPEN_* above (Linux only) +}; + +class NameValue +{ + public: + NameValue(){}; + // NameValue(const NameValue &nv){ name=nv.name; value=nv.value; }; + NameValue(const char *n, const char *val) : name(n), value(val){}; + QString name; + QString value; +}; + +class Category +{ + public: + Category(const char *heading, const char *explain) + : name(heading), help(explain), reversed(false), + flag_int_value(false){}; + virtual ~Category(); + + virtual int alignment() = 0; + virtual QString string(Procinfo *p) = 0; + virtual int width() = 0; + virtual int compare(Procinfo *a, Procinfo *b); + + const char *name; + const char *help; + int index; + int id; + bool reversed; // testing + bool flag_int_value; // testing: for total sum , cat_memory , cat_int +}; + +// COMMON +class Cat_int : public Category +{ + public: + Cat_int(const char *heading, const char *explain, int w, + int Procinfo::*member); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return field_width; }; + virtual int compare(Procinfo *a, Procinfo *b); + + protected: + int Procinfo::*int_member; + int field_width; +}; + +// COMMON for memory usage +class Cat_memory : public Category +{ + public: + Cat_memory(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return field_width; }; + virtual int compare(Procinfo *a, Procinfo *b); + + protected: + unsigned long Procinfo::*uintl_member; + int field_width; +}; + +class Cat_uintl : public Category +{ + public: + Cat_uintl(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return field_width; }; + virtual int compare(Procinfo *a, Procinfo *b); + + protected: + unsigned long Procinfo::*uintl_member; + int field_width; +}; + +class Cat_hex : public Cat_uintl +{ + public: + Cat_hex(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member); + virtual QString string(Procinfo *p); +}; + +class Cat_swap : public Category +{ + public: + Cat_swap(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 8; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +class Cat_string : public Category +{ + public: + Cat_string(const char *heading, const char *explain, + QString Procinfo::*member = 0); + virtual int alignment() { return Qt::AlignLeft; }; + virtual QString string(Procinfo *p); + virtual int width() { return -9; }; + virtual int gap() { return 8; }; + + protected: + QString Procinfo::*str_member; +}; + +class Cat_user : public Cat_string +{ + public: + Cat_user(const char *heading, const char *explain); + virtual QString string(Procinfo *p); +}; + +class Cat_group : public Cat_string +{ + public: + Cat_group(const char *heading, const char *explain); + virtual QString string(Procinfo *p); +}; + +class Cat_wchan : public Cat_string +{ + public: + Cat_wchan(const char *heading, const char *explain); + virtual QString string(Procinfo *p); +}; + +class Cat_dir : public Cat_string +{ + public: + Cat_dir(const char *heading, const char *explain, const char *dirname, + QString Procinfo::*member); + virtual QString string(Procinfo *p); + + protected: + const char *dir; + QString Procinfo::*cache; +}; + +class Cat_cmdline : public Cat_string +{ + public: + Cat_cmdline(const char *heading, const char *explain); + virtual QString string(Procinfo *p); +}; + +class Cat_state : public Category +{ + public: + Cat_state(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignLeft; }; + virtual QString string(Procinfo *p); + virtual int width() { return 6; }; + virtual int gap() { return 8; }; +}; + +class Cat_policy : public Category +{ + public: + Cat_policy(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignLeft; }; + virtual QString string(Procinfo *p); + virtual int width() { return 3; }; + virtual int gap() { return 8; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +class Cat_rtprio : public Category +{ + public: + Cat_rtprio(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 5; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +#ifdef LINUX +class Cat_tms : public Category +{ + public: + Cat_tms(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 5; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +class Cat_affcpu : public Category +{ + public: + Cat_affcpu(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 8; }; + // virtual int compare(Procinfo *a, Procinfo *b); +}; +#endif + +class Cat_time : public Category +{ + public: + Cat_time(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 7; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +class Cat_start : public Category +{ + public: + Cat_start(const char *heading, const char *explain); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return 8; }; + virtual int compare(Procinfo *a, Procinfo *b); +}; + +class Cat_percent : public Category +{ + public: + Cat_percent(const char *heading, const char *explain, int w, + float Procinfo::*member); + virtual int alignment() { return Qt::AlignRight; }; + virtual QString string(Procinfo *p); + virtual int width() { return field_width; }; + virtual int compare(Procinfo *a, Procinfo *b); + + protected: + float Procinfo::*float_member; + int field_width; +}; + +class Cat_tty : public Cat_string +{ + public: + Cat_tty(const char *heading, const char *explain); + virtual QString string(Procinfo *p); +}; + +#define CPU_TIMES(cpu, kind) cpu_times_vec[cpu * CPUTIMES + kind] +class Proc; + +class Procinfo // Process Infomation +{ + public: + Procinfo(Proc *system_proc, int pid, int thread_id = -1); +#ifdef SOLARIS + // Procinfo(int pid); //solaris ! + // Procinfo(int pid, int thread); //solaris ! + int readproc(int pid, int lwp); +#endif + ~Procinfo(); + Procinfo *ref() + { + refcnt++; + return this; + }; + void deref() + { + if (!--refcnt) + delete this; + }; + Proc *proc; + + inline void calculate_cpu(); + + int readproc(); + + bool isThread(); + void read_fd(int fdnum, char *path); + bool read_fds(); + bool read_maps(); + bool read_environ(); +#ifdef SOLARIS + void read_pmap_maps(); +#endif +#ifdef MOSIX + static void check_for_mosix(); + static Svec mosix_nodes(); +#endif + + int get_policy(); + int get_rtprio(); + +#ifdef LINUX + double get_tms(); + unsigned long get_affcpu(); + + QVector sock_inodes; // socket inodes or NULL if not read +#endif + int pid; + bool clone; + + bool first_run; // for optimization + char hashstr[128 * 8]; // cache + int hashlen; + int hashcmp(char *str); + + QString command; // COMMAND + QString cmdline; // COMMAND_LINE + QString username; // + QString groupname; // + QString cwd; // null if not read + QString root; // null if not read + + bool accepted; + int test_stop; // for test + int session; // ??? + + int uid, euid; + int gid, egid; + + char state; + int ppid; // Parent's PID + int pgrp; + dev_t tty; // tty major:minor device + int type; // TESTING X,NETWORK,FILE_OPEN,TERMINAL(tty),THREAD, + + int nthreads; // number of threads : LWP(Solaris), task(Linux) + int tgid; // thread leader's id + +#ifdef LINUX + double tms; // slice time + int slpavg; + unsigned long affcpu; + + int suid, fsuid; + int sgid, fsgid; + int tpgid; + + unsigned long cminflt; + unsigned long cmajflt; +#endif + + unsigned long io_read; // byte, testing + unsigned long io_write; // testing + unsigned long io_read_KBps; // K byte/sec + unsigned long io_write_KBps; // K byte/sec + unsigned long io_read_prev, io_write_prev; + + unsigned long flags; //? + unsigned long minflt; + unsigned long majflt; + + long utime; + long old_utime; // initial value = -1 ; + long cutime; + int priority; + int nice; + unsigned long starttime; // start time since run in epoch? Linux : jiffies + // since boot , solaris + unsigned long wchan; + QString wchan_str; + + // Memory + unsigned long mem; // user Memory define + unsigned long size; // SIZE: total memory (K) + unsigned long resident; // RSS: pages in resident set (non-swapped) (K) +#ifdef LINUX + unsigned long share; // shared memory pages (mmaped) (K) + unsigned long trs; // text resident set size (K) + unsigned long lrs; // shared-lib resident set size (K) + unsigned long drs; // data resident set size (K) + unsigned long + dt; // dirty pages (number of pages, not K), obsolute in Kernel 2.6 + unsigned long stack; // stack size (K) +#endif + +#ifdef SOLARIS + int addr_bits; // address bits (32 or 64) + + char policy_name[2]; // two first letters of scheduling class +#endif + struct timeval tv; // time when the snapshot was taken + struct timeval old_tv; // + + // Posix.1b scheduling + int policy; // -1 = uninitialized + int rtprio; // 0-99, higher can pre-empt lower (-1 = uninitialized) + + // Linux: the cpu used most of the time of the process + // Solaris: the cpu on which the process last ran + int which_cpu; + + // computed %cpu and %mem since last update + float wcpu, old_wcpu; // %WCPUwheight cpu + float pcpu; // %CPU: percent cpu after last update + float pmem; // %MEM + + QVector fd_files; // file names list + QVector maps; // maps list + QVector environ; // environment + char *envblock; // malloc()ed environment data block + +#ifdef SOLARIS + unsigned long env_ofs; +#endif + + Details *detail; // details window or NULL (backlink) + + unsigned int generation; // timestamp + + bool selected : 1; // true if selected in current view + bool hidekids : 1; // true if children are hidden in tree view + bool lastchild : 1; // true if last (visible) child in tree view + + short level; // distance from process root + QVector children; // real child processes + static const int MAX_CMD_LEN = 512; + + char refcnt; + + // virtual child for Table_Tree + QVector table_children; + int table_child_seq; + int clear_gen; + int child_seq_prev; + int parent_row; // virtual parent for tree table + +#ifdef MOSIX + bool isremote; + int from; + int where; + int remotepid; + QString migr; // String explaining migration "node>" or ">node" + int nmigs; + int locked; + QString cantmove; + static bool mosix_running; // true if MOSIX is running +#endif +}; + +typedef QHash Proclist; + +class SysHistory +{ + public: + int idx; + time_t time; // saved time, epoch... + int current_gen; + float load_cpu; // %CPU total ; green + float load_mem; // %mem ; yellow? + float load_io; // %SYS_IO ; BLUE + float load_net; // ;blue? orange? + Proclist procs; + ~SysHistory(); +}; + +// for A System +// cf. Procinfo is for a Process +// +class Proc +{ + public: + Proc(); + ~Proc(); + void commonPostInit(); // COMMON + void read_proc_all(); // test + void refresh(); + + static void init_static(); + int read_system(); + int countCpu(); + void read_loadavg(); + + int read_pid_tasks(int pid); + + Category *cat_by_name(const char *s); + int field_id_by_name(const char *s); + +#ifdef LINUX + /* from /proc/net/{tcp,udp,unix} */ + QHash socks; // tcp/udp sockets + QHash usocks; // unix domain sockets + bool socks_current; // true if the socks list is current + bool usocks_current; // true if the usocks list is current + + bool read_socket_list(Sockinfo::proto_t proto, const char *pseudofile); + void read_sockets(); + bool read_usocket_list(); + void read_usockets(); + void invalidate_sockets(); + +#endif + +#ifdef SOLARIS + static kstat_ctl_t *kc; // NULL if kstat not opened +#endif + QHash categories; + + Proclist procs; // processes indexed by pid + + // TESTING + QString supasswd; // test + int syshistoryMAX; + Proclist *hprocs; // temp_hprocs list + Proclist *mprocs; // + QList history; + + void setHistory(int tick); + Proclist getHistory(int pos); + + int qps_pid; // test + float loadQps; // TEST + static int update_msec; + + // class + int num_cpus; // current number of CPUs + int old_num_cpus; // previous number of CPUs + + long num_network_process; // number of network(socket) process + long num_opened_files; // number of opened normal(not socket) files + int num_process; // number of process + + long dt_total; // + long dt_used; // cpu used time in clktick + + long read_byte; // test + long write_byte; // test + long io_byte; // test file_io + + float load_cpu; // %CPU total + float loadavg[3]; // 1,5,15 minutes load avgs + + unsigned int clk_tick; // the number of clock ticks per second. + unsigned int boot_time; // boot time in seconds since the Epoch + + int mem_total, mem_free; // (Kb) + int swap_total, swap_free; // in kB + + int mem_shared, mem_buffers, mem_cached; // Linux + + // the following are pointers to matrices indexed by kind (above) and + // cpu + unsigned *cpu_times_vec; + unsigned *old_cpu_times_vec; + + // accessors for (old_)cpu_times_vec + unsigned &cpu_times(int cpu, int kind) + { + return cpu_times_vec[cpu * CPUTIMES + kind]; + } + unsigned &old_cpu_times(int cpu, int kind) + { + return old_cpu_times_vec[cpu * CPUTIMES + kind]; + } + +#ifdef LINUX + // from /proc/stat + unsigned long *per_cpu_times; // vector of num_cpus times + unsigned long *old_per_cpu_times; // vector of num_cpus times +#endif + + // Solaris #defines CPU_xxx so we must avoid them + enum + { + CPUTIME_USER, +#ifdef LINUX + CPUTIME_NICE, +#endif + CPUTIME_SYSTEM, +#ifdef SOLARIS + CPUTIME_WAIT, +#endif + CPUTIME_IDLE, + CPUTIMES + }; + + //#define CPU_TIMES(cpu, kind) cpu_times_vec[cpu * CPUTIMES + kind] + + unsigned int current_gen; + int maxSizeHistory; +}; + +class Procview : public Proc +{ + public: + Procview(); + QString filterstr; + + static bool flag_show_file_path; + static bool flag_pcpu_single; + static bool flag_cumulative; + + static int compare(Procinfo *const *a, Procinfo *const *b); + static int compare_backwards(Procinfo *const *a, Procinfo *const *b); + void refresh(); + bool accept_proc(Procinfo *p); // COMMON + void linearize_tree(QVector *ps, int level, int prow, + bool flag_hide = false); + void build_tree(Proclist &); + void rebuild(); + + void set_fields(); + void set_fields_list(int fields[]); + void addField(char *name); // interface + void addField(int FIELD_ID, int where = -1); // base interface + void removeField(int FIELD_ID); + int findCol(int FIELD_ID); + void moveColumn(int col, int place); + void deduce_fields(); + void fieldArrange(); + void update_customfield(); + // + void setSortColumn(int col, bool r = false); + void setTreeMode(bool b); + void saveCOMMANDFIELD(); + +#ifndef GTK + Procinfo *getProcinfoByPID(int pid) { return procs.value(pid, NULL); }; +#endif + + QVector linear_procs; // this is linear_proc_list for viewer + +#ifdef LINUX + QVector linear_socks; // Linux Testing +#endif + + // QList<> tags_kernel; + + // root_procs contains processes without parent; normally only init, but + // we cannot rely on this (Solaris has several parentless processes). + // Also, if the view is restricted, all processes whose parent isn't in + // the table. + QVector root_procs; // table_root_procs; for viewer + QVector cats; // for table + + Category *sortcat; + Category *sortcat_linear; // testing + int sort_column; // index of + bool reversed; // true if sorted backwards + static bool treeview; // true if viewed in tree form + bool enable; // tmp + + enum procstates + { + ALL, + OWNED, + NROOT, + RUNNING, + HIDDEN, + NETWORK + }; + enum fieldstates + { + USER = HIDDEN + 1, + JOBS, + MEM, + SCHED, + CUSTOM + }; + int viewproc; + int viewfields; + + int idxF_CMD; ////Test + QStringList customfields; + static int custom_fields[64]; + // lists of fields to be used for different views, terminated by -1: + static int mini_fields[]; // for mobile + static int basic_fields[]; + static int jobs_fields[]; + static int mem_fields[]; +#ifdef LINUX + static int sched_fields[]; +#endif + +#ifdef MOSIX + static int user_fields_mosix[]; + static int jobs_fields_mosix[]; + static int mem_fields_mosix[]; +#endif + static float avg_factor; // exponential factor for averaging + static const int cpu_avg_time = 30 * 1000; // averaging time for WCPU (ms) + + private: + static Category *static_sortcat; // kludge: to be used by compare +}; + +#endif // PROC_H diff --git a/src/proc_common.cpp b/src/proc_common.cpp new file mode 100644 index 0000000..c55a66d --- /dev/null +++ b/src/proc_common.cpp @@ -0,0 +1,1100 @@ +// for Non-ASCII locale +//#include + +//#include +// extern QTextCodec * codec ; +//#define UniString(str) codec->toUnicode(str) + +int flag_24_ok; // we presume a kernel 2.4.x + +#include +#include +#include +#include +#include +#include +#include + +// COMMON +bool Procview::treeview = 0; // true +bool Procview::flag_show_file_path = false; +bool Procview::flag_cumulative = false; // times cumulative with children's +bool Procview::flag_pcpu_single = false; // %CPU= pcpu/num_cpus + +// COMMON: basic field +int Procview::mini_fields[] = {F_PID, F_STAT, F_MEM, F_CPU, F_CMDLINE, F_END}; + +// COMMON? +// return username from /etc/passwd +char *userName(int uid, int euid) +{ + char buff[128]; + struct passwd *pw = getpwuid(uid); + if (!pw) + { + // dont have name ! + sprintf(buff, "%d", uid); + } + else + strcpy(buff, pw->pw_name); + + if (uid != euid) + strcat(buff, euid == 0 ? "*" : "+"); + + return strdup(buff); +} + +// return group name (possibly numeric) +char *groupName(int gid, int egid) +{ + char *p; + struct group *gr = getgrgid(gid); + if (!gr) + { + p = (char *)malloc(11); + sprintf(p, "%d", gid); + } + else + p = strdup(gr->gr_name); + // s.append("*"); + return p; +} + +Category::~Category() {} + +int Category::compare(Procinfo *a, Procinfo *b) +{ + return string(a).compare(string(b)); +} + +Cat_int::Cat_int(const char *heading, const char *explain, int w, + int Procinfo::*member) + : Category(heading, explain), int_member(member), field_width(w) +{ +} + +QString Cat_int::string(Procinfo *p) +{ + QString s; + s.setNum(p->*int_member); + return s; +} + +int Cat_int::compare(Procinfo *a, Procinfo *b) +{ + // qsort() only cares about the sign of the number returned by the + // comparison function; only a subtraction is necessary + return a->*int_member - b->*int_member; +} + +// COMMON +Cat_percent::Cat_percent(const char *heading, const char *explain, int w, + float Procinfo::*member) + : Category(heading, explain), float_member(member), field_width(w) +{ +} + +QString Cat_percent::string(Procinfo *p) +{ + QString s; + s.sprintf("%01.2f", (double)(p->*float_member)); + return s; +} + +int Cat_percent::compare(Procinfo *a, Procinfo *b) +{ + float at = a->*float_member, bt = b->*float_member; + return at < bt ? 1 : (at > bt ? -1 : 0); +} +// added 2006/05/07 +Cat_memory::Cat_memory(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member) + : Category(heading, explain), uintl_member(member), field_width(w) +{ +} + +QString Cat_memory::string(Procinfo *p) +{ + QString s; + char buff[128]; + + long sizeK, sizeM; + sizeK = p->*uintl_member; + + sizeM = sizeK / 1024; + /* if ( sizeM > 1024 ) + { + sprintf(buff,"%dM",sizeM/1024); + } + else */ + if (sizeM > 0) + { + sprintf(buff, "%ldM", sizeM); + } + else + sprintf(buff, "%ldK", sizeK); + + if (sizeK == 0) + s = "0"; + else + s = buff; + return s; +} + +int Cat_memory::compare(Procinfo *a, Procinfo *b) +{ + int bu = b->*uintl_member, au = a->*uintl_member; + return bu >= au ? (bu == au ? 0 : 1) : -1; +} + +Cat_uintl::Cat_uintl(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member) + : Category(heading, explain), uintl_member(member), field_width(w) +{ +} + +QString Cat_uintl::string(Procinfo *p) +{ + QString s; + s.setNum(p->*uintl_member); + return s; +} + +int Cat_uintl::compare(Procinfo *a, Procinfo *b) +{ + int bu = b->*uintl_member, au = a->*uintl_member; + return bu >= au ? (bu == au ? 0 : 1) : -1; +} + +Cat_hex::Cat_hex(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member) + : Cat_uintl(heading, explain, w, member) +{ +} + +// QString.sprintf 2x speed than glibc.sprintf (by fasthyun@magicn.com) +// but QString structuring & destructring eat more time +QString Cat_hex::string(Procinfo *p) +{ + QString s; + s.sprintf("%8x", (unsigned)(p->*uintl_member)); + return s; +} + +// COMMON, +Cat_swap::Cat_swap(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_swap::string(Procinfo *p) +{ + QString s; + // It can actually happen that size < resident (Sun under Solaris 2.6) + // Possible with Linux ? + + long sizeK, sizeM; + sizeK = p->size > p->resident ? p->size - p->resident : 0; + + sizeM = sizeK / 1024; + + if (sizeM > 0) + s = QString::number(sizeM) + "M"; + else + s = QString::number(sizeK) + "K"; + + if (sizeK == 0) + s = "0"; + return s; +} + +int Cat_swap::compare(Procinfo *a, Procinfo *b) +{ + return (b->size - b->resident) - (a->size - a->resident); +} + +Cat_string::Cat_string(const char *heading, const char *explain, + QString Procinfo::*member) + : Category(heading, explain), str_member(member) +{ +} + +QString Cat_string::string(Procinfo *p) { return p->*str_member; } + +Cat_user::Cat_user(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_user::string(Procinfo *p) +{ + if (p->uid == p->euid) + return Uidstr::userName(p->uid); + else + { + QString s = Uidstr::userName(p->uid); + s.append(p->euid == 0 ? "*" : "+"); + return s; + } +} + +Cat_group::Cat_group(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_group::string(Procinfo *p) +{ + if (p->gid == p->egid) + return Uidstr::groupName(p->gid); + else + { + QString s = Uidstr::groupName(p->gid); + s.append("*"); + return s; + } +} + +Cat_wchan::Cat_wchan(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_wchan::string(Procinfo *p) +{ +#ifdef LINUX + return p->wchan_str; +#else + return Wchan::name(p->wchan); +#endif +} + +Cat_cmdline::Cat_cmdline(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_cmdline::string(Procinfo *p) +{ + if (p->cmdline.isEmpty()) + { + QString s("("); + s.append(p->command); + s.append(")"); + return s; + } + else + { + if (Procview::flag_show_file_path) + return p->cmdline; + else + { + QString s(p->cmdline); + + int i = s.indexOf(' '); + if (i < 0) + i = s.length(); + if (i > 0) + { + i = s.lastIndexOf('/', i - 1); + if (i >= 0) + s.remove(0, i + 1); + } + return s; + } + } +} + +// hmm COMMON? almost same...but Solaris's zombie process... +Cat_start::Cat_start(const char *heading, const char *explain) + : Category(heading, explain) +{ +} +// don't let your confidence be shaken, just learn what you can from this young +// master. +QString Cat_start::string(Procinfo *p) +{ +#ifdef SOLARIS + if (p->state == 'Z') + return "-"; // Solaris zombies have no valid start time +#endif + Proc *proc = p->proc; + // time_t start = boot_time + p->starttime / (unsigned)HZ; + QString s; + // time_t start = proc->boot_time + p->starttime / proc->clk_tick; + time_t start = p->starttime; + char *ct = ctime(&start); // ctime(sec) + time_t sec = p->starttime - proc->boot_time; // secs + // if(p->tv.tv_sec - start < 86400) { + if (sec < 86400) + { // 24hours + ct[16] = '\0'; + s = ct + 11; // Hour:minu + } + else + { + ct[10] = '\0'; + s = ct + 4; // Date + } + return s; +} + +int Cat_start::compare(Procinfo *a, Procinfo *b) +{ + unsigned long bs = b->starttime, as = a->starttime; + return bs >= as ? (bs == as ? 0 : 1) : -1; +} + +/* ======================== Procview ============================ */ +float Procview::avg_factor = 1.0; + +Procview::Procview() +{ + // Proc::Proc(); //call once more ? + cats.clear(); + reversed = false; + viewproc = ALL; + treeview = true; + idxF_CMD = 0; // **** important **** + viewfields = USER; + set_fields(); + + sortcat = 0; // categories[F_PID]; + sort_column = -1; + sortcat_linear = NULL; + enable = true; + maxSizeHistory = 1200; + refresh(); // before reading Watchdog list +} + +// COMMON: +// Description : +// View mode : ALL , OWNER, NO-ROOOT , HIDDEN, NETWORK +bool Procview::accept_proc(Procinfo *p) +{ + QString pid; + static int my_uid = getuid(); + bool result; + + result = true; + + // BAD + if (false and viewproc == NETWORK) + { + /* + p->read_fds(); + if(p->sock_inodes.size()==0) + result=false; + for(int i=0;isock_inodes.size();i++) + { + SockInode *sn=p->sock_inodes[i]; + Sockinfo *si=Procinfo::socks.value(sn->inode,NULL); + if(si) + { + si->pid=p->pid; + linear_socks.append(si); + } + } + */ + } + else if (viewproc == ALL) + result = true; + else if (viewproc == OWNED) + result = (p->uid == my_uid); + else if (viewproc == NROOT) + result = (p->uid != 0); + else if (viewproc == RUNNING) + result = strchr("ORDW", p->state) != 0; + + /* + if ( viewproc == HIDDEN) + { + result=false; + for(int j=0;jcommand) + result=true ; + } + else + { + for(int j=0;jcommand) + result=false; + } */ + + if (result == false) + return false; // dont go further !! for better speed + + /// if(search_box==NULL) return result; + if (filterstr == "") + return result; + + if (filterstr == "*") + return true; + + // Notice: + // 1. some process name maybe different CMDLINE. ex) Xorg + if (p->cmdline.contains(filterstr, + Qt::CaseInsensitive)) // search_box->text() + return true; + if (p->command.contains(filterstr, Qt::CaseInsensitive)) + return true; + if (p->username.contains(filterstr, Qt::CaseInsensitive)) + return true; + + pid = pid.setNum(p->pid); //=QString::number(p->pid); + if (pid.contains(filterstr, Qt::CaseInsensitive)) + return true; + + /// printf("search_Box =%s , %s + /// \n",search_box->text().toAscii().data(),p->command.toAscii().data()); + + return false; +} + +extern "C" { +typedef int (*compare_func)(const void *, const void *); +} + +/* +template +void Svec::sort(int (*compare)(const T *a, const T *b)) +{ + qsort(vect, used, sizeof(T), (compare_func)compare); +} +*/ + +// table view - sort +void Procview::linearize_tree(QVector *ps, int level, int prow, + bool hide) +{ + static_sortcat = sortcat; + // ps->sort(reversed ? compare_backwards : compare); + if (reversed) + qsort(ps->data(), ps->size(), sizeof(Procinfo *), + (compare_func)compare_backwards); + else + qsort(ps->data(), ps->size(), sizeof(Procinfo *), + (compare_func)compare); + + int size = ps->size(); + // printf("level=%d prow=%d size=%d\n",level,prow,size); + for (int i = 0; i < size; i++) + { + Procinfo *p = (*ps)[i]; + // if (p->pid<6 ) printf("pid=%d level=%d + // parent_row=%d\n",p->pid,p->pid,p->parent_row); + p->level = level; + p->lastchild = false; + p->table_child_seq = i; // ************* where using ? -> sequence + p->parent_row = prow; + if (!hide) + linear_procs.append(p); // need !! + if (p->table_children.size()) // and !p->hidekids) + linearize_tree(&p->table_children, level + 1, + linear_procs.size() - 1, hide | p->hidekids); + } + + if (size > 0) + (*ps)[size - 1]->lastchild = true; +} + +/// basic,memory,job fields +void Procview::set_fields_list(int fields[]) +{ + cats.clear(); + for (int i = 0; fields[i] != F_END; i++) + { + if (fields[i] == F_CPUNUM and Proc::num_cpus < 2) + continue; // not correctly work + + Category *c = categories.value(fields[i], NULL); + if (c) + { + // printf("name=%s\n",c->name); + cats.append(c); + } + } + return; +} + +// return the column number of a field, or -1 if not displayed +int Procview::findCol(int field_id) +{ + for (int i = 0; i < cats.size(); i++) + if (cats[i]->id == field_id) + return i; + return -1; +} + +// basis +// called by +// void Procview::fieldArrange(); +// qps(); +// +void Procview::addField(int Fid, int where) +{ + // where=-1; + // printf("Fid =%d where=%d\n",Fid,where); + // where=pstable->clickedColumn(); + if (where == 0) + { + // if (pstable->treeMode()) where=1; + } + + if (Fid == F_PROCESSNAME) + where = 0; + if (Fid == F_CMDLINE) + where = cats.size(); // always should be the last column + + if (where < 0) + { + // this is default + where = cats.size(); + } + + if (where > cats.size()) // CMD_LINE ! ?????? + where = 1; + + Category *newcat = categories[Fid]; + // printf("name =%s where=%d\n",newcat->name,where); + if (cats.indexOf(newcat) < 0) // if not in the list **** + cats.insert(where, newcat); +} + +// add a category to last by name +void Procview::addField(char *name) // interface +{ + // QString str=sl[i]; + int id = field_id_by_name(name); + if (id >= 0) + addField(id); // add to last + + return; + + Category *cat = cat_by_name(name); + if (cat) + cats.append(cat); +} + +void Procview::removeField(int field_id) +{ + for (int i = 0; i < cats.size();) + { + // printf("Fid=%d cats[%d].id=%d + //\n",field_id,i,cats[i]->id); + if (true) + { + if (cats[i]->id == field_id) + { + // if(cats[i]->id==F_CMD and !treeview) + // idxF_CMD=-1; + cats.remove(i); + break; + } + else + i++; + } + } + // printf("Fid=%d cats.size=%d\n",field_id,cats.size()); +} + +// called by +// 1. write_settings() +// DEL? -> not yet +void Procview::update_customfield() +{ + int i; + // printf("update custom_fields\n"); + + customfields.clear(); + + for (i = 0; i < cats.size(); i++) + customfields.append(cats[i]->name); + + // DEL + if (false and treeview) + { + int idx = customfields.indexOf("COMMAND"); // == removeField(F_CMD); + if (idx >= 0) + customfields.removeAt(idx); + + if (idxF_CMD >= 0) + { + /* if(customfields.size()<=idxF_CMD) + customfields.append(categories[F_CMD]->name); + else */ + customfields.insert(idxF_CMD, categories[F_CMD]->name); + } + } + + return; +} + +// Description : FIELD movement by mouse drag +// From col To place +void Procview::moveColumn(int col, int place) +{ + int i; + + int f_size = cats.size(); + if (col < 0 or place < 0 or f_size <= col or f_size <= place) + { + printf("QPS code bugs!! : moveColumn() col=%d place=%d " + "cats.size=%d\n", + col, place, cats.size()); + return; + } + + if (treeview == true) + { + // *** important : F_PROCESSNAME field should be the first in + // TreeMode! + /// if(place==0) return; + if (cats[col]->index == F_PROCESSNAME) + place = 0; + } + + // COMMAND_LINE field should always be the last field + // if(cats[place]->index==F_CMDLINE) return; + if (cats[col]->index == F_CMDLINE) + place = cats.size() - 1; + + Category *cat = cats[col]; // SEGFAULT POSSIBLE! + cats.insert(place, cat); // insert + + if (place < col) + col++; + + cats.remove(col); // remove idx + // refresh(); + /// update_customfield(); +} + +// always called when linear to tree +// DEL +void Procview::saveCOMMANDFIELD() {} + +// call by +// void Pstable::moveCol(int col, int place) +// void Pstable::setTreeMode(bool treemode) +// void Procview::set_fields() +// TODO: checkField(); +void Procview::fieldArrange() +{ + + if (treeview == true) + { + // Tree Mode + // If ProcessName isn't the leftmost column, move it to + // leftmost + // *** important : F_PROCESSNAME field should be the first in + // TreeMode! + if (cats[0]->index != F_PROCESSNAME) + { + // find F_PROCESSNAME + for (int i = 1; i < cats.size(); i++) + { + if (cats[i]->index == F_PROCESSNAME) + moveColumn(i, 0); + } + } + // PID sort for convenience (default) + /* + if(false and cats[i]->index == F_PID ) + { + reversed = false; + sortcat = cats[i]; + //// pstable->setSortedCol(i); + } */ + // Linear_Mode: + } + + if (true) + { + + for (int i = 0; i < cats.size(); i++) + { + + if (cats[i]->index == F_CMDLINE) + { + // COMMAND_LINE field should always be the last + // field + if (i == (cats.size() - 1)) + moveColumn(i, 0); + else + { + } + } + } + } +} + +void Procview::setTreeMode(bool b) +{ + if (treeview == false) + { + if (sortcat_linear == NULL) + sortcat_linear = sortcat; + else + sortcat = sortcat_linear; + } +} + +void Procview::setSortColumn(int col, bool r) +{ + // qDebug("xxx error? col>=cats.size() %d",col); + if (col >= cats.size()) + { + qDebug("hmmm error? col>=cats.size() %d", col); + return; + } + + // if(!procview->treeview) just reverse the lines + if (col == sort_column) + reversed = !reversed; + else + reversed = false; + sortcat = cats[col]; + sort_column = col; +} + +Category *Procview::static_sortcat = 0; +int Procview::compare(Procinfo *const *a, Procinfo *const *b) +{ + if (static_sortcat == 0) + return 0; + int r = static_sortcat->compare(*a, *b); + return (r == 0) ? ((*a)->pid > (*b)->pid ? 1 : -1) : r; +} + +int Procview::compare_backwards(Procinfo *const *a, Procinfo *const *b) +{ + if (static_sortcat == 0) + return 0; + int r = static_sortcat->compare(*b, *a); + return (r == 0) ? ((*b)->pid > (*a)->pid ? 1 : -1) : r; +} + +// COMMON +Category *Proc::cat_by_name(const char *s) +{ + + if (s) + { + // java style + QHashIterator i(categories); + while (i.hasNext()) + { + const char *p; + i.next(); + p = i.value()->name; + if (*p == ' ') + p++; + if (strcmp(p, s) == 0) + return i.value(); + // cout << i.key() << ": " << i.value() << endl; + } + } + return 0; +} + +// COMMON +// call by +int Proc::field_id_by_name(const char *s) +{ + if (s) + { + // STL style + QHash::iterator i = categories.begin(); + while (i != categories.end()) + { + if (strcmp(i.value()->name, s) == 0) + return i.key(); // cout << i.key() << ": " << + // i.value() << endl; + ++i; + } + } + return -1; +} + +// postInit +void Proc::commonPostInit() +{ + // java style + /* + QHashIterator i(categories); // a little suck! + while (i.hasNext()) { + i.next(); + i.value()->index=i.key(); + i.value()->id=i.key(); + }*/ + + // STL style , set id + QHash::iterator i = categories.begin(); + while (i != categories.end()) + { + i.value()->index = i.key(); + i.value()->id = i.key(); + // cout << i.key() << ": " << i.value() << endl; + ++i; + } + + num_opened_files = 0; // test + num_process = 0; // 64bit + num_network_process = 0; // 64bit + + dt_total = 0; // diff system tick + dt_used = 0; // for infobar + + loadavg[0] = loadavg[1] = loadavg[2] = + 0.0; // CPU load avg 1min,5min,15minute + + Proc::num_cpus = 0; + Proc::old_num_cpus = 0; + + Proc::mem_total = 0; + Proc::mem_free = 0; + + Proc::mem_buffers = 0; + Proc::mem_cached = 0; + // mem_shared = 0; // only linux kernel 2.4.x??? + + Proc::swap_total = 0; + Proc::swap_free = 0; + + Proc::qps_pid = -1; + Proc::loadQps = 0.0; + + Proc::cpu_times_vec = 0; // array. + Proc::old_cpu_times_vec = 0; + + Proc::boot_time = 0; + Proc::clk_tick = 100; // for most system + + current_gen = 0; // ! + mprocs = NULL; + hprocs = NULL; + + clk_tick = + sysconf(_SC_CLK_TCK); //****** The number of clock ticks per second. + // + // printf("Qps: hz=%d\n",clk_tick); + + /// Procinfo::init_static(); +} + +// COMMON? +// called by Procview::rebuild() +// using procs, makes root_procs for TABLE_View +void Procview::build_tree(Proclist &procs) +{ + Procinfo *p; + int proc_n = 0; + + // children clear + root_procs.clear(); + + QHash::iterator i; + for (i = procs.begin(); i != procs.end();) + { + Procinfo *p = i.value(); + p->table_children.clear(); + if ((p->accepted = accept_proc(p))) + proc_n++; + ++i; + } + + Proc::num_process = proc_n; // count process + + // find parent of a process + for (i = procs.begin(); i != procs.end(); ++i) + { + p = i.value(); // always not NULL + + if (p->accepted) + { + Procinfo *parent = 0; + int virtual_parent_pid; + + if (p->isThread()) + virtual_parent_pid = p->tgid; // thread's leader ID. + else + virtual_parent_pid = p->ppid; + + if (p->pid < p->ppid) + { + // this occurs !! a reporter mailed to me + } + + parent = procs.value(virtual_parent_pid, + NULL); // if pid not found, then return NULL. + + // printf("thread_leader=0 (%d) + //%s\n",p->tgid,p->command.ascii()); + if (treeview and parent and parent->accepted) + { + // p->table_child_seq=parent->table_children.size(); + parent->table_children.append(p); + } + else + { + // 1.init(pid=1) process + // 2.some process which parent not accepted + // 3.(null) thread has TGID=0,PPID=0 + // 4.when not tree mode + root_procs.append(p); + } + } + } +} + +// COMMON +// BOTTLENECK No.3 0.5% +// re-sort the process list , ::rebuild() for Table +// called by Pstable::refresh() +void Procview::rebuild() +{ + linear_procs.clear(); // Svec procs in Proview +#ifdef LINUX + linear_socks.clear(); +#endif + + // Procinfo *pi = getProcinfoByPID(Procinfo::qps_pid); + // if(pi) Procinfo::loadQps=pi->pcpu; + // printf("rebuild\n"); + + /* if(mprocs) + { + build_tree(*mprocs); + } + else */ + build_tree(Proc::procs); + + linearize_tree(&root_procs, 0, -1); +} + +#include // log() +// COMMON: CORE +// Description: update the process list BottleNeck 1.5% +// read /proc/* +// called by Procview::refresh(), every UPDATE . +void Proc::refresh() +{ + current_gen++; + + // TEST for Process History + SysHistory *s = new SysHistory; + + history.append(s); + hprocs = &(s->procs); + + // init + /// num_opened_files=0; + Proc::num_process = 0; + Proc::num_network_process = 0; + + Proc::read_loadavg(); + Proc::read_system(); // **** should be every refresh !! + // s->load_cpu=(float)Proc::dt_used/Proc::dt_total; //after + // read_system(); + + s->load_cpu = load_cpu; // after read_system(); + s->time = time(NULL); // save current time in seconds since epoch + + // TODO: clean + // Procinfo::read_sockets(); // for future, BottleNect 2% + read_byte = 0; + write_byte = 0; + io_byte = 0; //!!! + /// Proc::read_sockets(); // test + + read_proc_all(); // Linux, Solaris... + + // s->load_io=(float)(read_byte + write_byte ); // (50*1024*1024); + // //dt_total; + if (io_byte != 0) + { + s->load_io = log10(io_byte) * 2; // 9000 -> 3point + // printf("DEBUG: rw=[%d,%d] %d, %f\n",read_byte, + // write_byte,io_byte,s->load_io); + } + else + s->load_io = 0; + + // remove non-existing processes, remove Procinfos of nonexisting + // processes + QHash::iterator i; + for (i = procs.begin(); i != procs.end();) + { + Procinfo *p = i.value(); + if (p->generation != current_gen) + { + // printf("delete %d\n",p->pid); + i = procs.erase(i); + delete p; + } + else + { + // p->table_children.clear(); // for rebuild() + // p->accepted=accept_proc(p); + ++i; + } + } + + // TESTING + while (history.size() >= maxSizeHistory) + { + // if(history.isEmpty()==false) + delete history.takeFirst(); + } +} + +// COMMON +// read new process info +void Procview::refresh() +{ + /****************************************************************/ + /* Procview.procs has the procinfo + */ + /****************************************************************/ + if (enable) + { + // printf("Procview::refresh()\n"); + Proc::refresh(); // read "/proc/*", then update the process list + } +} + +SysHistory::~SysHistory() +{ + Procinfo *p; + foreach (p, procs) + { + delete p; + } + procs.clear(); // remove all (key,val) +} + +/* check +Category *Proc::cat_by_name(const char *s) +{ + if( s ) + { + for( int i = 0; i < categories.size(); i++ ) + if( strcmp(categories[i]->name, s) == 0 ) + return categories[i]; + } + return 0; +} + + +int Proc::field_id_by_name(const char *s) +{ + if( s ) + { + for( int i = 0; i < categories.size(); i++ ) + if( strcmp(categories[i]->name, s) == 0 ) + return i; + } + return -1; +} +*/ diff --git a/src/proc_linux.cpp b/src/proc_linux.cpp new file mode 100644 index 0000000..8f8cd8e --- /dev/null +++ b/src/proc_linux.cpp @@ -0,0 +1,2255 @@ +// proc.cpp for Linux +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 +// Oliver + +/* + LWP (Light Weight Process): just thread, mainly used in Solaris + Task : thread and process in Linux + NPTL(Native POSIX Thread Library) + TGID thread group leader's pid +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // sched_rr_get_interval(pid, &ts); +#include // basename() +#include // sysconf() POSIX.1-2001 + +#include +//#include //HZ defined, no more used. +//#include "misc.h" // x_atoi() , userName() ,groupname() + +#include "proc.h" +#include "uidstr.h" +#include "ttystr.h" +#include "wchan.h" + +#ifdef GTK +#include "detail_gtk.h" +#else +#include "details.h" //qt +#endif + +#include "proc_common.cpp" // COMMON code !!!! + +#define PROCDIR "/proc" // hmmm + +bool flag_SMPsim = false; // SMP simulation + +extern int flag_thread_ok; +extern bool flag_schedstat; +extern bool flag_show_thread; +extern bool flag_devel; + +int pagesize; +int Proc::update_msec = 1024; + +// socket states, from and touched to avoid name collisions +enum +{ + SSFREE = 0, /* not allocated */ + SSUNCONNECTED, /* unconnected to any socket */ + SSCONNECTING, /* in process of connecting */ + SSCONNECTED, /* connected to socket */ + SSDISCONNECTING /* in process of disconnecting */ +}; + +#define QPS_SCHED_AFFINITY ok + +#ifdef QPS_SCHED_AFFINITY +#ifndef SYS_sched_setaffinity +#define SYS_sched_setaffinity 241 +#endif +#ifndef SYS_sched_getaffinity +#define SYS_sched_getaffinity 242 +#endif + +// Needed for some glibc +int qps_sched_setaffinity(pid_t pid, unsigned int len, unsigned long *mask) +{ + return syscall(SYS_sched_setaffinity, pid, len, mask); +}; +int qps_sched_getaffinity(pid_t pid, unsigned int len, unsigned long *mask) +{ + return syscall(SYS_sched_getaffinity, pid, len, mask); +}; +#endif + +/* + Thread Problems. + pthread_exit() + */ + +struct proc_info_ +{ + int proc_id; + char flag; + char type; + int files; +} proc_info; // TESTING + +struct list_files_ +{ + int proc_id; + int flag; + char *filename; // path + filename +} list_files; // TESTING + +// read() the number of bytes read is returned (zero indicates end of file) +// return the number of bytes read if ok, -1 if failed +inline int read_file(char *name, char *buf, int max) +{ + int fd = open(name, O_RDONLY); + if (fd < 0) + return -1; + int r = read(fd, buf, max); + close(fd); + // buf[r]=0; + return r; +} + +// Description : read proc files +// return 0 : if error occurs. +char buffer_proc[1024 * 4]; // enough..maybe +char *read_proc_file(const char *fname, int pid = -1, int tgid = -1, + int *size = NULL) +{ + static int max_size = 0; + char path[256]; + int r; + + if (pid < 0) + sprintf(path, "/proc/%s", fname); + else + { + if (tgid > 0) + sprintf(path, "/proc/%d/task/%d/%s", tgid, pid, fname); + else + sprintf(path, "/proc/%d/%s", pid, fname); + } + + if (strcmp(fname, "exe") == 0) + { + if ((r = readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0) + { + buffer_proc[r] = 0; // safer + return buffer_proc; + } + else + return 0; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + r = read(fd, buffer_proc, sizeof(buffer_proc) - 1); // return 0 , -1 , + if (r < 0) + return 0; + + if (max_size < r) + max_size = r; + + if (size != 0) + *size = r; + + buffer_proc[r] = 0; // safer + + return buffer_proc; + // note: not work fgets(sbuf, sizeof(64), fp) why??? +} + +char *read_proc_file2(char *r_path, const char *fname, int *size = NULL) +{ + static int max_size = 0; + char path[256]; + int r; + + // strcpy(path,r_path); + + sprintf(path, "%s/%s", r_path, fname); + + if (strcmp(fname, "exe") == 0) + { + if ((r = readlink(path, buffer_proc, sizeof(buffer_proc) - 1)) >= 0) + { + buffer_proc[r] = 0; // safer + return buffer_proc; + } + else + return 0; + } + + int fd = open(path, O_RDONLY); + if (fd < 0) + return 0; + r = read(fd, buffer_proc, + sizeof(buffer_proc) - 1); // return 0 , -1 , read_count + if (r < 0) + return 0; + + if (max_size < r) + max_size = r; + + if (size != 0) + *size = r; + + buffer_proc[r] = 0; // safer + close(fd); + + return buffer_proc; + // note: not work fgets(sbuf, sizeof(64), fp) why??? +} + +// TEST CODE , Bottleneck +// Description: read /proc/PID/fd/* check opened file, count opened files +// this fuction will be called when every update. +// Return Value : +int proc_pid_fd(const int pid) +{ + char path[256]; + char buffer[256], fname[256]; + DIR *d; + int fdnum; + int len, path_len; + + sprintf(path, "/proc/%d/fd", pid); + + path_len = strlen(path); + d = opendir(path); + + if (!d) + { + // this happend when the process died already or Zombie process + // printf("Qps : read fail !! /proc/%d/fd !!! kernel bug ? + // \n",pid); + return false; + } + + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip "." and ".." + + path[path_len] = '/'; + path[path_len + 1] = 0; + strcat(path, e->d_name); + + len = readlink(path, fname, sizeof(fname) - 1); + if (len > 0) + { + fname[len] = 0; + // printf("DEBUG: %s[%s]\n",path,fname); + // if (strcmp(fname,"/dev/null")==0 ) continue; + } + + /// num_opened_files++; + // strcpy(p, e->d_name); + // fdnum = atoi(p); + // read_fd(fdnum, path); + } + closedir(d); + return true; +} + +// new process created +Procinfo::Procinfo(Proc *system_proc, int process_id, int thread_id) : refcnt(1) +{ + first_run = true; + clone = false; + + proc = system_proc; + + if (thread_id < 0) // + { + pid = process_id; + tgid = process_id; // thread group leader's id + } + else + { + pid = thread_id; + tgid = process_id; // thread group leader's id + } + + ppid = 0; // no parent + selected = false; + hidekids = false; + envblock = 0; //!! + + table_child_seq = -1; + child_seq_prev = -1; + + lastchild = 0; + generation = -1; + detail = 0; + + /// per_cpu_times = 0; not yet + + size = 0; + resident = 0; + trs = 0; + drs = 0; + stack = 0; + share = 0; + mem = 0; + + io_read_prev = 0; // ** + io_write_prev = 0; + io_read = 0; // ** + io_write = 0; // ** + + // tgid=0; + pcpu = 0; + pmem = 0; + + old_utime = 0; // this must be current utime ! + old_wcpu = 0; + + command = "noname"; + tty = 0; + nice = 0; + starttime = 0; + state = 'Z'; + cutime = utime = 0; + + nthreads = 0; /* number of threads */ + + hashstr[0] = 0; + hashlen = 0; +} + +Procinfo::~Procinfo() +{ + if (!clone) + { + void watchdog_check_if_finish(QString cmd, Procinfo * p); + watchdog_check_if_finish(command, this); + + if (detail) + { + // printf("~Procinfo() : pid=%d\n",pid); + detail->process_gone(); + detail = 0; + } + + // if(environ) delete environ; + if (envblock) + free(envblock); /// double free , SEGFAULT + } + + // fd_files.squeeze(); + // maps.squeeze(); + /* + if(maps) { + maps->purge(); + delete maps; + } + if(fd_files) { + fd_files->purge(); + delete fd_files; + } + */ + + // if(children) + // { children->clear(); delete children; } + /// delete[] per_cpu_times; +} + +// miscellaneous static initializations +void Proc::init_static() +{ + + // socks.setAutoDelete(true); + /// usocks.setAutoDelete(true); + + pagesize = sysconf(_SC_PAGESIZE); // same getpagesize() in + // printf("pagesize=%d, %d\n",getpagesize(), + // sysconf(_SC_PAGESIZE)); //4027 +} + +// tricky function...(by fasthyun@magicn.com) +// Description : +// let's deal thread as normal process! +// read /proc/PID/task/* and add to Proc::procs[] +int Proc::read_pid_tasks(int pid) +{ + char path[256]; + struct dirent *e; + int thread_pid; + int thread_n = 0; + Procinfo *pi = 0; + + sprintf(path, "/proc/%d/task", pid); + + DIR *d = opendir(path); + if (!d) + return -1; // process dead already! + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip "." , ".." + + thread_pid = atoi(e->d_name); + if (pid == thread_pid) + continue; // skip + + pi = procs.value(thread_pid, NULL); + + if (pi == NULL) + { + pi = new Procinfo(this, pid, thread_pid); + procs.insert(thread_pid, pi); + } + if (pi->readproc() >= 0) + { + pi->generation = current_gen; + // if(pid!=thread_pid) + // pi->cmdline="(thread)"; + } + thread_n++; + } + closedir(d); + return thread_n; +} + +// update wcpu,%cpu field +void Procinfo::calculate_cpu() // +{ +} + +// using cache for Speed up +int Procinfo::hashcmp(char *sbuf) +{ + int statlen; + + statlen = strlen(sbuf); + if (statlen > sizeof(hashstr)) + { + // some user reported 265byte. + printf("Qps BUG: hashstr shortage statlen(%d) > hashstr(%lu), " + "report this " + "message to fasthyun@magicn.com \n", + statlen, sizeof(hashstr)); + abort(); + } + else if (statlen == hashlen) + { + if (memcmp(hashstr, sbuf, statlen) == 0) + { + pcpu = 0; + // 1. I am a sleeping process + // printf("[%d] sleep process \n",pid); + return 1; + } + } + memcpy(hashstr, sbuf, statlen); // to back + hashlen = statlen; + return 0; +} + +int mini_sscanf(const char *s, const char *fmt, ...); + +// Description : read /proc/PID/* or read /proc/PID/task/* +// be called every refresh() time. +// return -1 means the process already dead ! +int Procinfo::readproc() +{ + char cmdbuf[MAX_CMD_LEN]; + char path[64]; + int len; + char *sbuf; // should be enough to acommodate /proc/PID/stat + char *buf; + + int x_pid; // just pid + int i_tty; // + long stime, cstime; + + // Note : /proc/PID/* is not same /proc/task/PID/* + if (isThread()) // flag_thread_ok + { + sprintf(path, "/proc/%d/task/%d", tgid, pid); + } + else + sprintf(path, "/proc/%d", pid); + + if (first_run) + { + // Note: COMMAND(?) , TGID, UID , COMMAND_LINE never + // change ! + old_wcpu = wcpu = pcpu = 0.0; + + // read /proc/PID/status + if ((buf = read_proc_file2(path, "status")) == 0) + return -1; + + // Note: Process_name from + // 1.status (15 chars-name) + // 2.stat (15 chars-name, pass) + // 3.cmdline : full name (sometimes have null, Thread + // can't use + // cmdline) + // 4.exe : full name (frequently this does not exist, + // thread can't use + // exe) + // 5.comm : 15 chars ? + // + // Note: + // 1. thread's name_max is 15 chars + + if (mini_sscanf(buf, "Name: %S\n", cmdbuf) == 0) + return -1; + else + { + command = cmdbuf; + if (command.contains("kthread")) + hidekids = true; // kthread, kthreadd , + // ///Procinfo::qps_pid=pid; + } + + if (mini_sscanf(buf, "Tgid: %d ", &tgid) == 0) + return -1; + if (mini_sscanf(buf, "Uid: %d %d %d %d", &uid, &euid, &suid, &fsuid) != + 4) + return -1; + if (mini_sscanf(buf, "Gid: %d %d %d %d", &gid, &egid, &sgid, &fsgid) != + 4) + return -1; + + username = userName(uid, euid); + groupname = groupName(gid, egid); + + int bug = 0; + char cmdline_cmd[4096]; // some cmdline very large! ex)chrome + // read /proc/pid/cmdline + int size; + cmdline_cmd[0] = 0; + + // anyone can read [cmdline] + if ((buf = read_proc_file2(path, "cmdline", &size)) == 0) + return -1; + else + { + // printf("DEBUG: size=%d \n",size); + int cmdlen = strlen(buf); + + if (cmdlen == 0) + { + // 1. kthread + // printf("Qps:debug no_cmdline pid=%d\n",pid ); + cmdline = ""; + } + // for non-ascii locale language + // cmdline = codec->toUnicode(cmdbuf,strlen(cmdbuf)); + else + { + + // change 0x00,0xA to ' ' + for (int i = 0; i < size - 1; i++) // OVERFLOW + if (buf[i] == 0 or buf[i] == 0x0A) + buf[i] = ' '; + cmdline = buf; + strcpy(cmdline_cmd, buf); + } + } + + // VERY COMPLEX CODE + // because Command's MAX_length is only 15, so sometimes + // cmd_name truncated, + // we should guess ... + // + // The solution is... + // 1.check [exe] file ( only owner can read it) + // 2.check [cmdline] ( anyone can read it ) + // 3.check [comm] + // + if (command.size() == 15) + { + // only root & owner can read [exe] link + /* + if((buf= read_proc_file2(path,"exe")) !=0 ) + { + // printf("Qps:debug %s\n",buf ); + if(strlen(basename(buf))>15 and + strncmp(qPrintable(command),basename(buf),15)==0 + ) + command=basename(buf); // no + memory leak ! + else ;// just use command + //printf("Qps:debug %s\n",buf ); + } */ + + if (true) // guess the full name of the command + { + // Use /proc/PID/cmdline, comm, status + // ex) + // /usr/lib/chromium/chromium --option1 + // --option2 + // python /usr/lib/system-service-d + // pam: gdm-password + // hald-addon-input: Listing On /dev~ + // + char *p; + p = strstr(cmdline_cmd, ": "); // cut the options ! + if (p != 0) + *p = 0; + p = strchr(cmdline_cmd, ' '); // cut the options ! + if (p != 0) + *p = 0; + + // printf("Qps:debug %s\n",cmdline_cmd ); + char *pstart = strstr(basename(cmdline_cmd), cmdbuf); + if (pstart != 0) + { + command = pstart; // copy + /// printf("Qps:debug2 + /// %s\n",basename(cmdline_cmd)); + } + } + } + + if (isThread()) + cmdline = command + " (thread)"; + + if (flag_devel and bug) + { + // command.append("^"); + cmdline += " ^ Qps: may be a wrong commandline "; + } + + void watchdog_check_if_start(QString cmd, Procinfo * ps); + watchdog_check_if_start(command, this); // segfault possible. + + first_run = false; + } + + if (flag_schedstat == true) + { + // if no change then return. twice faster ! + // MAX_256 bytes check...? + // 2.6.9 upper only and some system no has + if ((sbuf = read_proc_file2(path, "schedstat")) == 0) + return -1; + + if (hashcmp(sbuf)) + return 1; // no change + } + // read /proc/PID/stat + if ((sbuf = read_proc_file2(path, "stat")) == 0) + return -1; + + if (flag_schedstat == false) // if no change then return. twice faster ! + { + if (hashcmp(sbuf)) + return 1; + } + + /// if (proc_pid_fd(pid)== true) ; // bottleneck !! + + /* + Not all values from /proc/#/stat are interesting; the ones left + out + have been retained in comments to see where they should go, in + case + they are needed again. + + Notes : + 1. man -S 5 proc + 2. man -S 2 times + 3. ppid can be changed when parent dead ! + 4. initial utime maybe 0, so %CPU field NotAnumber !! + utime: user time + stime: kernel mode tick + cutime : The number of jiffies that this process's waited-for + children have been scheduled in user mode. + + #jiffies == tick + */ + unsigned int guest_utime, cguest_utime; +#if 1 + char *p, *p1; + // in odd cases the name can contain spaces and '(' or ')' and numbers, + // so + // this makes + // parsing more difficult. We scan for the outermost '(' ')' to find the + // name. + p = strchr(sbuf, '('); + p1 = strrchr(sbuf, ')'); + if (p == 0 || p1 == 0) + return -1; + p1++; + // we can safely use sscanf() on the rest of the string + sscanf(p1, + " %c %d %d %d %d %d" + " %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %d %d %d %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s " + "%*s %*s %*s %*s %lu %*s %*s %*s %u %*s %*s %*s %u %u", +#else + // some errors will occur ! + mini_sscanf( + sbuf, + "%d (%S) %c %d %d %d %d %d" + "%lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %d %d %d %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s " + "%*s %*s %*s %*s %lu %*s %*s %*s %u %*s %*s %*s %u %u", + &x_pid, &cmdbuf[0], +#endif + &state, &ppid, &pgrp, &session, &i_tty, &tpgid, &flags, &minflt, + &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime, + &priority, &nice, &nthreads /* number of threads v2.6 */, + /* itrealvalue */ + &starttime, /* start time(static) */ // jiffes + /* vsize */ + /* rss */ + /* rlim, startcode, endcode, startstack kstkesp kstkeip, + signal, blocked, sigignore, sigcatch */ + &wchan, + /* 0L, 0L, exit_signal */ + &which_cpu + /* rt_priority, policy, delayacct_blkio_ticks */ + , + &guest_utime, &cguest_utime); + + starttime = proc->boot_time /* secs */ + (starttime / proc->clk_tick); + + tty = (dev_t)i_tty; // hmmm + // if(tty!=0) printf("pid=%d tty =%d\n",pid,tty); + + // if(guest_utime>0 or cguest_utime>0) + // printf("cmd [%s] guest_utime=%d cguest_utime + //=%d\n",qPrintable(command),guest_utime,cguest_utime); + + utime += stime; // we make no user/system time distinction + cutime += cstime; + + if (old_utime > 0) // check.. + { + int dcpu; + dcpu = utime - old_utime; // user_time from proc + if (dcpu < 0) + { + // why.. this occurs ? + // Qps exception:[3230,firefox] dcpu=-22 utime=39268 + // old_utime=39290 why + // occur? + if (flag_devel) + printf("Qps :[%d,%s] dcpu=%d utime=%ld " + "old_utime=%ld why occurs?\n", + pid, qPrintable(command), dcpu, utime, old_utime); + return 1; + } + + // gettimeofday(&tv, 0); //sys/time + if (proc->dt_total > 0) // move to Proc ?? + { + pcpu = 100.0 * dcpu / proc->dt_total; + if (Procview::flag_pcpu_single == true) + pcpu *= proc->num_cpus; // + } + // else too fast read again + // printf("Qps exception: dt_total=%d report to + // fasthyun@magicn.com + // \n",Proc::dt_total); + + if (flag_devel and pcpu > 100) // DEBUG CODE + { + printf("Qps pcpu error: %0.0f%% [%d,%s] dt_total=%ld " + "dcpu=%d utime=%ld " + "old_utime=%ld \n", + pcpu, pid, qPrintable(command), proc->dt_total, dcpu, utime, + old_utime); + pcpu = 99.99; + } + + const float a = Procview::avg_factor; + wcpu = a * old_wcpu + (1 - a) * pcpu; + } + old_tv = tv; + old_wcpu = wcpu; + old_utime = utime; // **** + + // read /proc/%PID/statm - memory usage + if (1) + { + if ((buf = read_proc_file2(path, "statm")) == 0) + return -1; // kernel 2.2 ? + sscanf(buf, "%lu %lu %lu %lu %lu %lu %lu", &size, &resident, &share, + &trs, &lrs, &drs, &dt); + size *= pagesize / 1024; // total memory in kByte + resident *= pagesize / 1024; + share *= pagesize / 1024; // share + // trs ; // text(code) + // lrs ; // zero : lib, awlays zero in + // Kernel 2.6 + // drs ; // data: wrong in kernel 2.6 + // dt ; // zero : in Kernel 2.6 + mem = resident - share; + // pmem = 100.0 * resident / proc->mem_total; + pmem = 100.0 * mem / proc->mem_total; + } + + // read /proc/PID/status check !! + if ((buf = read_proc_file2(path, "status")) == 0) + return -1; + else + { + // slpavg : not supported in kernel 2.4; default value of -1 + if (mini_sscanf(buf, "SleepAVG:%d", &slpavg) == 0) + slpavg = -1; + + if (strstr(buf, "VmSize:")) + { + // mini_sscanf(p, "VmSize: %d",&size); // XXX + // mini_sscanf(p, "VmRSS: %d",&resident); + // mini_sscanf(sbuf, "VmLib: %d",&share); + mini_sscanf(buf, "VmData: %d", &drs); // data in kByte + mini_sscanf(buf, "VmStk: %d", &stack); // stack in kByte + mini_sscanf(buf, "VmExe: %d", &trs); // text + } + } + + /* + generally + shared = RSS - ( CODE + DATA + STACK ) + share= resident - trs -drs -stack; + + // Defines from task_mmu.c of kernel source + total_vm==size + data = mm->total_vm - mm->shared_vm - mm->stack_vm; + swap = p->size - p->resident ; + */ + + // read /proc/PID/file_io + // NOTE: 2.6.11 dont have IO file + // COMPLEX_CODE + if ((buf = read_proc_file2(path, "io")) != 0) + { + // rchar = ... not file maybe sockread + // + mini_sscanf(buf, "read_bytes:%d", &io_read); + mini_sscanf(buf, "write_bytes:%d", &io_write); + + // if(io_read_prev!=0) + { + if (io_read_prev == 0) + io_read_prev = io_read; + if (io_write_prev == 0) + io_write_prev = io_write; + + // NOTE: Kbps right???? + io_read_KBps = (io_read - io_read_prev) / + proc->update_msec; // not accurate.... + io_write_KBps = (io_write - io_write_prev) / proc->update_msec; + + proc->io_byte += io_read_KBps; // test + proc->io_byte += io_write_KBps; + } + + io_read_prev = io_read; + io_write_prev = io_write; + + // io_read>>=10; // divide by 1024 + // io_write>>=10; // divide by 1024 + } + // per_cpu_times = 0; // not yet + + if ((buf = read_proc_file2(path, "wchan")) != 0) + { + wchan_str = buf; + } + + policy = -1; // will get it when needed + rtprio = -1; // ditto + tms = -1; // ditto + + // useless ? if(detail) detail->set_procinfo(this); // BAD !!! + return 2; // return ok. +} + +// just grab the load averages +// called by +void Proc::read_loadavg() +{ + char path[80]; + char buf[512]; + int n; + strcpy(path, "/proc/loadavg"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + fprintf(stderr, "qps: Cannot open /proc/loadavg (make sure " + "/proc is mounted)\n"); + exit(1); + } + buf[n] = '\0'; + sscanf(buf, "%f %f %f", &loadavg[0], &loadavg[1], &loadavg[2]); +} + +int Proc::countCpu() +{ + static bool first_run = true; + char path[80]; + char buf[1024 * 8]; // for SMP + + int num_cpus = 0, n; + // read system status /proc/stat + strcpy(path, "/proc/stat"); + // if((buf= read_proc_file("stat:)) ==0 ) return -1; + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + printf("Qps Error: /proc/stat can't be read ! check it and " + "report to " + "fasthyun@magicn.com\n"); + abort(); // return 0; + } + buf[n] = '\0'; + + // count (current) cpu of system + char *p; + p = strstr(buf, "cpu"); + while (p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) + { + num_cpus++; + if (strncmp(p, "cpu0", 4) == 0) + Proc::num_cpus--; + p = strchr(p, '\n'); + if (p) + p++; + } + + if (flag_devel and flag_SMPsim) + { + // num_cpus=64; + int vals[] = {2, 4, 8, 16, 32}; + int r = rand() % 5; + num_cpus = vals[r]; + } + return num_cpus; +} + +// LINUX +// Description: read common information for all processes +// return value +// -1 : too fast refresh ! +int Proc::read_system() // +{ + static bool first_run = true; + char path[80]; + char buf[1024 * 8]; // for SMP + + char *p; + int n; + + if (first_run) + { + /* Version 2.4.x ? */ + strcpy(path, "/proc/vmstat"); + if (!stat(path, (struct stat *)buf)) + flag_24_ok = false; + else + flag_24_ok = true; + + /* NPTL(Native POSIX Thread Library) */ + strcpy(path, "/proc/1/task"); + if (!stat(path, (struct stat *)buf)) + flag_thread_ok = true; + else + flag_thread_ok = false; + + /* check schedstat */ + strcpy(path, "/proc/1/schedstat"); // some system doesn't have + if (!stat(path, (struct stat *)buf)) + flag_schedstat = true; + else + flag_schedstat = false; + + strcpy(path, "/proc/stat"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return 0; + buf[n] = '\0'; + p = strstr(buf, "btime"); + if (p == NULL) + { + // used + printf("Qps: A bug occurs ! [boot_time] \n"); + // boot_time= current time + } + else + { + p += 6; + // sscanf(p, "%d", &Proc::boot_time); //???? why + // segfault??? + sscanf(p, "%d", &boot_time); + } + + // Max SMP 1024 cpus, MOVETO: COMMON + int max_cpus = 512; + cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; //??? +2 + old_cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; + + // init + for (int cpu = 0; cpu < max_cpus; cpu++) + for (int i = 0; i < CPUTIMES; i++) + { + cpu_times(cpu, i) = 0; + old_cpu_times(cpu, i) = 0; + } + + // first_run=false; // not yet , at the bottom of this function + } + + // read system status /proc/stat + strcpy(path, "/proc/stat"); + // if((buf= read_proc_file("stat:)) ==0 ) return -1; + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + printf("Qps Error: /proc/stat can't be read ! check it and " + "report to " + "fasthyun@magicn.com\n"); + abort(); // return 0; + } + buf[n] = '\0'; + + if (true) + { + // count (current) cpu of system + char *p; + p = strstr(buf, "cpu"); + num_cpus = 0; + while (p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) + { + num_cpus++; + if (strncmp(p, "cpu0", 4) == 0) + Proc::num_cpus--; + p = strchr(p, '\n'); + if (p) + p++; + } + + if (flag_SMPsim) + { + int vals[] = {2, 4, 8, 16, 32}; + int r = rand() % 5; + num_cpus = vals[r]; + num_cpus = 8; + } + + // Hotplugging Detection : save total_cpu + if (Proc::num_cpus != Proc::old_num_cpus) + { + // for(int i = 0; i < CPUTIMES; i++) + // cpu_times(num_cpus, i) = + // cpu_times(Proc::old_num_cpus, + // i); + + Proc::old_num_cpus = Proc::num_cpus; + } + } + + // backup old values : important******* + for (int cpu = 0; cpu < Proc::num_cpus + 1; cpu++) + { + for (int i = 0; i < CPUTIMES; i++) + old_cpu_times(cpu, i) = cpu_times(cpu, i); + } + + /* + /proc/stat + cpu# user nice system idle iowait(2.6) + irq(2.6) sft(2.6) steal(2.6.11) guest(2.6.24) + cpu0 3350 9 535 160879 + 1929 105 326 5 + 1200 + + Q1: kernel 2.4 cpu0 exist ? + */ + + // Total_cpu + int total_cpu = Proc::num_cpus; + unsigned user, nice, system, idle, iowait, irq, sftirq, steal, guest, nflds; + nflds = sscanf(buf, "cpu %u %u %u %u %u %u %u %u %u", &user, &nice, &system, + &idle, &iowait, &irq, &sftirq, &steal, &guest); + if (nflds > 4) + { + // kernel 2.6.x + system += (irq + sftirq); + idle += iowait; + } + if (nflds == 9) + { + system += steal; + system += guest; + } + cpu_times(Proc::num_cpus, CPUTIME_USER) = user; + cpu_times(Proc::num_cpus, CPUTIME_NICE) = nice; + cpu_times(Proc::num_cpus, CPUTIME_SYSTEM) = system; + cpu_times(Proc::num_cpus, CPUTIME_IDLE) = idle; + + // DRAFT! + // num_cpus == total_cpu + // + // dt_total= user + system + nice + idle + // dt_used= user + system; + Proc::dt_used = + user - old_cpu_times(Proc::num_cpus, + CPUTIME_USER); // infobar uses this value + Proc::dt_used += system - old_cpu_times(Proc::num_cpus, CPUTIME_SYSTEM); + Proc::dt_total = dt_used + nice - + old_cpu_times(Proc::num_cpus, CPUTIME_NICE) + idle - + old_cpu_times(Proc::num_cpus, CPUTIME_IDLE); + + load_cpu = (float)Proc::dt_used / Proc::dt_total; // COMMON + + if (first_run) + { + // printf("\n==================== tooo fast + //=================================\n"); + // printf("DEBUG:dt_total=%d + // dt_used=%d\n",Proc::dt_total,Proc::dt_used); + // return -1; // too early refresh again !! + } + if (Proc::dt_total == 0) + { + //????? + printf("Error: dt_total=0 , dt_used=%ld(%u) report to " + "fasthyun@magicn.com\n", + Proc::dt_used, old_cpu_times(Proc::num_cpus, CPUTIME_IDLE)); + dt_total = 500; // more tolerable? + // abort(); // stdlib.h + } + + // void watchdog_syscpu(int ); + // watchdog_syscpu((user-old_cpu_times(num_cpus,CPUTIME_USER))*100/dt_total); + //// test + + // if(flag_devel and flag_SMPsim ) + if (flag_SMPsim) + { + // for Developer only !!! + // printf("user%d nuce%d system%d + // idle%d\n",user,nice,system,idle); + for (int cpu = 0; cpu < num_cpus; cpu++) + { + // stdlib.h, int rand(); + if (dt_used != 0) + cpu_times(cpu, CPUTIME_USER) = + old_cpu_times(cpu, CPUTIME_USER) + rand() % dt_used; + else + cpu_times(cpu, CPUTIME_USER) = 0; + cpu_times(cpu, CPUTIME_NICE) = nice; + cpu_times(cpu, CPUTIME_SYSTEM) = system; + cpu_times(cpu, CPUTIME_IDLE) = idle; + } + } + else + { + // Single-CPU and SMP(Multi-CPU) + for (int cpu = 0; cpu < num_cpus; cpu++) + { + char cpu_buf[10]; + sprintf(cpu_buf, "cpu%d", cpu); + if ((p = strstr(buf, cpu_buf)) != 0) + { + nflds = sscanf(p, "%*s %u %u %u %u %u %u %u %u %u", + &cpu_times(cpu, CPUTIME_USER), + &cpu_times(cpu, CPUTIME_NICE), + &cpu_times(cpu, CPUTIME_SYSTEM), + &cpu_times(cpu, CPUTIME_IDLE), &iowait, &irq, + &sftirq, &steal, &guest); + // cpu_times(cpu, CPUTIME_USER),cpu_times(cpu, + // CPUTIME_NICE), + if (nflds > 4) + { + // kernel 2.6.x + cpu_times(cpu, CPUTIME_SYSTEM) += (irq + sftirq); + cpu_times(cpu, CPUTIME_IDLE) += iowait; + } + if (nflds == 9) + { + cpu_times(cpu, CPUTIME_SYSTEM) += (steal + guest); + } + + // 2.4.27-SMP bug ? + } + else + { + fprintf(stderr, "Qps: Error reading info for " + "cpu%d (/proc/stat)\n", + cpu); + abort(); + } + } + } + + // read memory info + strcpy(path, PROCDIR); + strcat(path, "/meminfo"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return 0; + buf[n] = '\0'; + + // Skip the old /meminfo cruft, making this work in post-2.1.42 kernels + // as well. (values are now in kB) + if ((p = strstr(buf, "MemTotal:"))) + sscanf(p, "MemTotal: %d kB\n", &mem_total); + if ((p = strstr(buf, "MemFree:")) != NULL) + sscanf(p, "MemFree: %d kB\n", &mem_free); + if ((p = strstr(buf, "Buffers:")) != NULL) + sscanf(p, "Buffers: %d kB\n", &mem_buffers); + if ((p = strstr(buf, "Cached:")) != NULL) + sscanf(p, "Cached: %d kB\n", &mem_cached); + + p = strstr(buf, "SwapTotal:"); + sscanf(p, "SwapTotal: %d kB\nSwapFree: %d kB\n", &swap_total, &swap_free); + + first_run = false; + return 0; +} + +int Procinfo::get_policy() +{ + if (policy == -1) + policy = sched_getscheduler(pid); + return policy; +} + +int Procinfo::get_rtprio() +{ + if (rtprio == -1) + { + struct sched_param p; + if (sched_getparam(pid, &p) == 0) + rtprio = p.sched_priority; + } + return rtprio; +} + +double Procinfo::get_tms() +{ + struct timespec ts; + if (sched_rr_get_interval(pid, &ts) == -1) // POSIX + tms = -1; // should not be possible + else + { + tms = ts.tv_nsec; // nano seconds + tms /= 1000000; // mili seconds + tms += ts.tv_sec * 1000; + } + return tms; +} + +unsigned long Procinfo::get_affcpu() +{ +#ifdef QPS_SCHED_AFFINITY + if (qps_sched_getaffinity(pid, sizeof(unsigned long), &affcpu) == -1) + affcpu = (unsigned long)0; +#else + if (sched_getaffinity(pid, sizeof(unsigned long), (cpu_set_t *)&affcpu) == + -1) + affcpu = (unsigned long)0; +#endif + return affcpu; +} + +// Description : read /proc/PID/fd/* (SYMBOLIC LINK NAME) +/* We need to implement support for IPV6 and sctp ? */ +void Procinfo::read_fd(int fdnum, char *path) +{ + int len; + char buf[128]; + struct stat sb; + + // The fd mode is contained in the link permission bits + if (lstat(path, &sb) < 0) + return; + int mode = 0; + if (sb.st_mode & 0400) + mode |= OPEN_READ; + if (sb.st_mode & 0200) + mode |= OPEN_WRITE; + + if ((len = readlink(path, buf, sizeof(buf) - 1)) > 0) + { + buf[len] = '\0'; + unsigned long dev, ino; + + // check socket_fd + if ((buf[0] == '[' and sscanf(buf, "[%lx]:%lu", &dev, &ino) == 2 and + dev == 0) // Linux 2.0 style + || + sscanf(buf, "socket:[%lu]", &ino) > 0) // Linux 2.1 upper + { + Sockinfo *si = NULL; + si = proc->socks.value(ino, NULL); // sock + char buf[80]; + if (si) + { + printf("sock ino=%lu\n", ino); + si->pid = pid; + // a TCP or UDP socket + sock_inodes.append(new SockInode(fdnum, ino)); + sprintf(buf, "%s socket %lu", + si->proto == Sockinfo::TCP ? "TCP" : "UDP", ino); + fd_files.append(new Fileinfo(fdnum, buf, mode)); + return; + } + else + { + // maybe a unix domain socket? + // read_usockets(); + UnixSocket *us = NULL; + + us = proc->usocks.value(ino, NULL); + if (us) + { + const char *tp = "?", *st = "?"; + switch (us->type) + { + case SOCK_STREAM: + tp = "stream"; + break; + case SOCK_DGRAM: + tp = "dgram"; + break; + } + switch (us->state) + { + case SSFREE: + st = "free"; + break; + case SSUNCONNECTED: + st = "unconn"; + break; + case SSCONNECTING: + st = "connecting"; + break; + case SSCONNECTED: + st = "connected"; + break; + case SSDISCONNECTING: + st = "disconn"; + break; + } + sprintf(buf, "UNIX domain socket %lu (%s, %s) ", ino, tp, + st); + QString s = buf; + s.append(us->name); + fd_files.append(new Fileinfo(fdnum, s, mode)); + return; + } + } + } + // normal filess + // assume fds will be read in increasing order + fd_files.append(new Fileinfo(fdnum, buf, mode)); + } +} + +// Description : +// read /PID/fd opened files +// return true if /proc/PID/fd could be read, false otherwise +// store fileinfo, and also socket inodes separately +// +// called by Detail() +bool Procinfo::read_fds() +{ + char path[128], *p; + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/fd", pid, pid); + else + sprintf(path, "/proc/%d/fd", pid); + + DIR *d = opendir(path); + if (!d) + return false; + + /* + if(!sock_inodes) sock_inodes = new Svec(4); + else sock_inodes->purge(); */ + + strcat(path, "/"); + + fd_files.clear(); // + struct dirent *e; + while ((e = readdir(d)) != 0) + { + char str[128]; + if (e->d_name[0] == '.') + continue; // skip . and .. + int fdnum = atoi(e->d_name); + strcpy(str, path); + strcat(str, e->d_name); + // printf("str=%s\n",str); + read_fd(fdnum, str); + } + // printf("end str=\n"); + closedir(d); + return true; +} + +// tcp, udp +bool Proc::read_socket_list(Sockinfo::proto_t proto, const char *filename) +{ + char path[80]; + sprintf(path, "/proc/net/%s", filename); + FILE *f = fopen(path, "r"); + if (!f) + return false; + + char buf[128 * 3]; + // Header + // sl local_addr rem_addr st tx_queue rx_queue tr tm->when retrnsmt + // uid + // timeout inode + + Sockinfo si; + + printf("read_socket_list()\n"); + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f) != 0) + { + // Sockinfo *si = new Sockinfo; + si.pid = -1; + si.proto = proto; + unsigned local_port, rem_port, st, tr; + sscanf(buf + 6, "%x:%x %x:%x %x %x:%x %x:%x %x %d %d %d", + &si.local_addr, &local_port, &si.rem_addr, &rem_port, &st, + &si.tx_queue, &si.rx_queue, &tr, &si.tm_when, &si.rexmits, + &si.uid, &si.timeout, &si.inode); + // fix fields that aren't sizeof(int) + si.local_port = local_port; + si.rem_port = rem_port; + si.st = st; + si.tr = tr; + + Sockinfo *psi; + psi = socks.value(si.inode, NULL); + if (psi == NULL) + { + printf("inode =%d \n", si.inode); + psi = new Sockinfo; + *psi = si; + socks.insert(si.inode, psi); + } + else + *psi = si; + + // linear_socks.add(si); + } + fclose(f); + return true; +} + +bool Proc::read_usocket_list() +{ + char path[80]; + strcpy(path, PROCDIR); + strcat(path, "/net/unix"); + FILE *f = fopen(path, "r"); + if (!f) + return false; + + char buf[256]; + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f)) + { + if (buf[0]) + buf[strlen(buf) - 1] = '\0'; // chomp newline + // UnixSocket *us = new UnixSocket; + UnixSocket us; + + unsigned q; + unsigned type, state; + int n; + sscanf(buf, "%x: %x %x %x %x %x %ld %n", + // Num refcount protocol flags type state indoe path + &q, &q, &q /*protocol*/, &us.flags, &type, &state, &us.inode, + &n); + us.name = buf + n; + us.type = type; + us.state = state; + + UnixSocket *pus; + pus = usocks.value(us.inode, NULL); + if (pus == NULL) + { + printf("inode =%lu \n", us.inode); + + pus = new UnixSocket; + *pus = us; + usocks.insert(us.inode, pus); + } + else + *pus = us; + } + fclose(f); + return true; +} + +void Proc::read_sockets() +{ + // socks.clear(); + + // memory leak !! + if (!read_socket_list(Sockinfo::TCP, "tcp") || + !read_socket_list(Sockinfo::UDP, "udp")) + return; + // remove no more socket ino + read_usocket_list(); + + socks_current = true; +} + +void Proc::read_usockets() +{ + if (usocks_current) + return; + + usocks.clear(); + if (!read_usocket_list()) + return; + + usocks_current = true; +} + +void Proc::invalidate_sockets() { socks_current = usocks_current = false; } + +// return true if /proc/XX/maps could be read, false otherwise +bool Procinfo::read_maps() +{ + // idea: here we could readlink /proc/XX/exe to identify the executable + // when running 2.0.x + char name[80]; + + if (flag_thread_ok && flag_show_thread) + sprintf(name, "/proc/%d/task/%d/maps", pid, pid); + else + sprintf(name, "/proc/%d/maps", pid); + + FILE *f = fopen(name, "r"); + if (!f) + return false; + + char line[1024]; // lines can be longer , SEGFAULT + + while (fgets(line, sizeof(line), f)) + { + Mapsinfo *mi = new Mapsinfo; + int n; + sscanf(line, "%lx-%lx %4c %lx %x:%x %lu%n", + // sscanf(line, "%lx-%lx %4c %lx %x:%x %n", + &mi->from, &mi->to, mi->perm, &mi->offset, &mi->major, + &mi->minor, &mi->inode, &n); + if (line[n] != '\n') + { + int len = strlen(line); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + while (line[n] == ' ' && line[n]) + n++; + mi->filename = line + n; + } + else if ((mi->major | mi->minor | mi->inode) == 0) + mi->filename = "(anonymous)"; + maps.append(mi); + } + fclose(f); + if (maps.size() == 0) + return false; + + return true; +} + +// DRAFT CODE: +// return true if /proc/777/environ could be read, false otherwise +int fsize(char *fname); +bool Procinfo::read_environ() +{ + int file_size = 0; + int size; + char path[256]; + + environ.clear(); // + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/environ", pid, pid); + else + sprintf(path, "/proc/%d/environ", pid); + + file_size = fsize(path); + if (file_size <= 0) + return false; + + envblock = (char *)malloc(file_size + 1); + size = read_file(path, envblock, file_size + 1); + if (size <= 0) + return false; + + // kernel 2.6.x has a bug + if (envblock[size - 2] == 0) // how to check the bug. + { + char buf[128]; + sprintf(buf, "Kernel Bug= wrong environments ! please, check " + "/proc/%d/environ !", + pid); + size = strlen(buf) + 1; + if (file_size > size) + strcpy(envblock, buf); + } + + int i = 0, n = 0, v = 0; + char ch; + + for (i = 0; i < size; i++) + { + ch = envblock[i]; + if (ch == '=') + { + envblock[i] = 0; + v = i + 1; + } + if (ch == 0) + { + NameValue nv(&envblock[n], &envblock[v]); + environ.append(nv); + // printf("%s %s\n",&envblock[n],&envblock[v]); + n = i + 1; + } + } + if (envblock) + { + free(envblock); // refresh() // INVALID VALGRIND + envblock = 0; + } + return true; +} + +// CWD,ROOT only so... +Cat_dir::Cat_dir(const char *heading, const char *explain, const char *dirname, + QString Procinfo::*member) + : Cat_string(heading, explain), dir(dirname), cache(member) +{ +} + +QString Cat_dir::string(Procinfo *p) +{ + if ((p->*cache).isNull()) + { + char path[128], buf[512]; + + if (flag_thread_ok && flag_show_thread) + sprintf(path, "/proc/%d/task/%d/%s", p->pid, p->pid, dir); + else + sprintf(path, "/proc/%d/%s", p->pid, dir); + + int n = readlink(path, buf, sizeof(buf) - 1); + if (n < 0) + { + // Either a kernel process, or access denied. + // A hyphen is visually least disturbing here. + p->*cache = "-"; + return p->*cache; + } + else if (buf[0] != '[') + { + // linux >= 2.1.x: path name directly in link + buf[n] = '\0'; + p->*cache = buf; + return p->*cache; + } + + // Either a Linux 2.0 link in [device]:inode form, or a Solaris + // link. + // To resolve it, we just chdir() to it and see where we end up. + // Perhaps we should change back later? + if (chdir(path) < 0) + { + p->*cache = "-"; // Most likely access denied + } + else + { + // getcwd() is fairly expensive, but this is cached + // anyway + if (!getcwd(buf, sizeof(buf))) + { + p->*cache = "(deleted)"; + } + else + p->*cache = buf; + } + } + return p->*cache; +} + +Cat_state::Cat_state(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_state::string(Procinfo *p) +{ + QString s(" "); + int ni = p->nice; + + s[0] = p->state; + s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' '; + s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' '); + return s; +} + +// LINUX +Cat_policy::Cat_policy(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_policy::string(Procinfo *p) +{ + QString s; + switch (p->get_policy()) + { + case SCHED_FIFO: + s = "FI"; + break; // first in, first out + case SCHED_RR: + s = "RR"; + break; // round-robin + case SCHED_OTHER: + s = "TS"; + break; // time-sharing + default: + s = "??"; + break; + } + return s; +} + +int Cat_policy::compare(Procinfo *a, Procinfo *b) +{ + return b->get_policy() - a->get_policy(); +} + +Cat_rtprio::Cat_rtprio(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_rtprio::string(Procinfo *p) +{ + QString s; + s.setNum(p->get_rtprio()); + return s; +} + +int Cat_rtprio::compare(Procinfo *a, Procinfo *b) +{ + return b->get_rtprio() - a->get_rtprio(); +} + +// maybe tms COMMON +Cat_tms::Cat_tms(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_tms::string(Procinfo *p) +{ + QString s; + p->tms = p->get_tms(); + s.sprintf("%.3f", p->tms); + // s.sprintf("%d",p->tms); + return s; +} + +int Cat_tms::compare(Procinfo *a, Procinfo *b) +{ + return (int)((b->get_tms() - a->get_tms()) * 1000); +} + +Cat_affcpu::Cat_affcpu(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_affcpu::string(Procinfo *p) +{ + QString s; + p->affcpu = p->get_affcpu(); + s.sprintf("%lx", p->affcpu); + return s; +} +/* + int Cat_affcpu::compare(Procinfo *a, Procinfo *b) + { + return (int)(b->affcpu - a->affcpu); + } + */ + +// LINUX or COMMON? +Cat_time::Cat_time(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_time::string(Procinfo *p) +{ + QString s; + char buff[64]; + int ticks = p->utime; + int ms; + + int proctick = p->proc->clk_tick; + + if (Procview::flag_cumulative) + ticks += p->cutime; + + int t = ticks / p->proc->clk_tick; // seconds + // COMPLEX CODE + if (t < 10) + { // ex. 9.23s + ms = ticks / (p->proc->clk_tick / 100) % 100; // Need FIX + sprintf(buff, "%1d.%02ds", t, ms); + } + else if (t < 60) + { // ex. 48s + sprintf(buff, "%5ds", t); + } + else if (t < 60 * 10) + { // ex. 8.9m, 9.0m + sprintf(buff, "%2d.%1dm", t / 60, (t % 60) / 6); + } + else if (t < 60 * 60) + { // 58m + sprintf(buff, "%5dm", t / 60); + } + else if (t < 24 * 3600) + { // + int h = t / 3600; // 1hour = 3600 = 60m*60s + t %= 3600; + sprintf(buff, "%2d:%02d", h, t / 60); + } + else if (t < 10 * 24 * 3600) + { // + int d = t / 86400; // 1 day = 24* 3600s + t %= 86400; + sprintf(buff, "%2d.%1dd", d, (t * 10 / (3600 * 24))); + } + else + { + int d = t / 86400; // 1 day = 24* 3600s + sprintf(buff, "%5dd", d); + } + s = buff; + return s; +} + +int Cat_time::compare(Procinfo *a, Procinfo *b) +{ + int at = a->utime, bt = b->utime; + if (Procview::flag_cumulative) + { + at += a->cutime; + bt += b->cutime; + } + return bt - at; +} + +// LINUX ? +Cat_tty::Cat_tty(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_tty::string(Procinfo *p) { return Ttystr::name(p->tty); } + +Proc::Proc() +{ + // Note: + categories.insert(F_PID, + new Cat_int("PID", "Process ID", 6, &Procinfo::pid)); + categories.insert(F_TGID, + new Cat_int("TGID", "Task group ID ( parent of threads )", + 6, &Procinfo::tgid)); + categories.insert( + F_PPID, new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid)); + categories.insert( + F_PGID, new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp)); + categories.insert(F_SID, + new Cat_int("SID", "Session ID", 6, &Procinfo::session)); + categories.insert(F_TTY, new Cat_tty("TTY", "Terminal")); + categories.insert(F_TPGID, + new Cat_int("TPGID", "Process group ID of tty owner", 6, + &Procinfo::tpgid)); + + categories.insert( + F_USER, new Cat_string("USER", "Owner (*=suid root, +=suid a user)", + &Procinfo::username)); + categories.insert(F_GROUP, + new Cat_string("GROUP", "Group name (*=sgid other)", + &Procinfo::groupname)); + + categories.insert(F_UID, + new Cat_int("UID", "Real user ID", 6, &Procinfo::uid)); + categories.insert( + F_EUID, new Cat_int("EUID", "Effective user ID", 6, &Procinfo::euid)); + categories.insert(F_SUID, new Cat_int("SUID", "Saved user ID (Posix)", 6, + &Procinfo::suid)); + categories.insert(F_FSUID, new Cat_int("FSUID", "File system user ID", 6, + &Procinfo::fsuid)); + categories.insert(F_GID, + new Cat_int("GID", "Real group ID", 6, &Procinfo::gid)); + categories.insert( + F_EGID, new Cat_int("EGID", "Effective group ID", 6, &Procinfo::egid)); + categories.insert(F_SGID, new Cat_int("SGID", "Saved group ID (Posix)", 6, + &Procinfo::sgid)); + categories.insert(F_FSGID, new Cat_int("FSGID", "File system group ID", 6, + &Procinfo::fsgid)); + categories.insert( + F_PRI, new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority)); + categories.insert(F_NICE, + new Cat_int("NICE", + "Scheduling favour (higher -> less cpu time)", + 4, &Procinfo::nice)); + categories.insert( + F_NLWP, new Cat_int("NLWP", "Number of tasks(threads) in task group", 5, + &Procinfo::nthreads)); + + categories.insert(F_PLCY, new Cat_policy("PLCY", "Scheduling policy")); + categories.insert( + F_RPRI, + new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)")); + categories.insert(F_TMS, new Cat_tms("TMS", "Time slice in milliseconds")); + categories.insert(F_SLPAVG, + new Cat_int("%SAVG", + "Percentage average sleep time (-1 -> N/A)", + 4, &Procinfo::slpavg)); + categories.insert( + F_AFFCPU, + new Cat_affcpu("CPUSET", + "Affinity CPU mask (0 -> API not supported)")); // ??? + categories.insert(F_MAJFLT, + new Cat_uintl("MAJFLT", + "Number of major faults (disk access)", 8, + &Procinfo::majflt)); + categories.insert(F_MINFLT, + new Cat_uintl("MINFLT", + "Number of minor faults (no disk access)", + 8, &Procinfo::minflt)); + + // Memory + categories.insert(F_SIZE, + new Cat_memory("VSIZE", "Virtual image size of process", + 8, &Procinfo::size)); + categories.insert(F_RSS, new Cat_memory("RSS", "Resident set size", 8, + &Procinfo::resident)); + categories.insert(F_MEM, new Cat_memory("MEM", "memory usage (RSS-SHARE)", + 8, &Procinfo::mem)); + categories.insert(F_TRS, + new Cat_memory("TRS", "Text(code) resident set size", 8, + &Procinfo::trs)); + categories.insert( + F_DRS, + new Cat_memory("DRS", "Data resident set size(malloc+global variable)", + 8, &Procinfo::drs)); + categories.insert( + F_STACK, new Cat_memory("STACK", "Stack size", 8, &Procinfo::stack)); + categories.insert(F_SHARE, + new Cat_memory("SHARE", "Shared memory with other libs", + 8, &Procinfo::share)); + categories.insert(F_SWAP, new Cat_swap("SWAP", "Kbytes on swap device")); + categories.insert( + F_IOR, new Cat_memory("IO_R", "io read (file)", 8, &Procinfo::io_read)); + categories.insert(F_IOW, new Cat_memory("IO_W", "io write (file)", 8, + &Procinfo::io_write)); + + categories.insert(F_DT, + new Cat_uintl("DT", "Number of dirty (non-written) pages", + 7, &Procinfo::dt)); + categories.insert(F_STAT, new Cat_state("STAT", "State of the process ")); + categories.insert(F_FLAGS, new Cat_hex("FLAGS", "Process flags (hex)", 9, + &Procinfo::flags)); + categories.insert( + F_WCHAN, + new Cat_wchan("WCHAN", "Kernel function where process is sleeping")); + categories.insert( + F_WCPU, + new Cat_percent("%WCPU", "Weighted percentage of CPU (30 s average)", 6, + &Procinfo::wcpu)); + categories.insert( + F_CPU, + new Cat_percent("%CPU", "Percentage of CPU used since last update", 6, + &Procinfo::pcpu)); + categories.insert( + F_PMEM, + new Cat_percent("%MEM", "Percentage of memory used (RSS/total mem)", 6, + &Procinfo::pmem)); + categories.insert(F_START, new Cat_start("START", "Time process started")); + categories.insert(F_TIME, + new Cat_time("TIME", "Total CPU time used since start")); + categories.insert( + F_CPUNUM, + new Cat_int("CPU", "CPU the process is executing on (SMP system)", 3, + &Procinfo::which_cpu)); + + categories.insert(F_CMD, new Cat_string("Process Name", "the process name", + &Procinfo::command)); + // categories.insert(F_PROCESSNAME, new Cat_string("Process Name", + //"the + // process name", &Procinfo::command)); + categories.insert(F_CWD, new Cat_dir("CWD", "Current working directory", + "cwd", &Procinfo::cwd)); + categories.insert(F_ROOT, new Cat_dir("ROOT", "Root directory of process", + "root", &Procinfo::root)); + + // command_line="COMMAND_LINE"; //reference to /proc/1234/cmdline + categories.insert(F_CMDLINE, + new Cat_cmdline("COMMAND_LINE", + "Command line that started the process")); + + commonPostInit(); + + socks_current = false; + usocks_current = false; + + Proc::init_static(); +} + +Proc::~Proc() +{ + // killall procinfos +} + +// COMMON for LINUX,SOLARIS +// Polling /proc/PID/* +void Proc::read_proc_all() +{ + DIR *d = opendir("/proc"); + struct dirent *e; + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] >= '0' and e->d_name[0] <= '9') + { // good idea ! + int pid; + Procinfo *pi = NULL; + + // inline int x_atoi(const char *sstr); + // pid=x_atoi(e->d_name); //if(pid<100) continue; + pid = atoi(e->d_name); + + pi = procs.value(pid, NULL); + + if (pi == NULL) // new process + { + pi = new Procinfo(this, pid); + procs.insert(pid, pi); + /* + Procinfo *parent; + parent =procs[pi->ppid]; + if(parent) + parent->children->add(pi); + printf("Qps : parent null + pid[%d]\n",pi->pid); } + */ + } + int ret = pi->readproc(); + if (ret > 0) + { + pi->generation = current_gen; // this process is alive + // printf(" [%s] %d + // %d\n",pi->command.toAscii().data(),pi->generation,current_gen); + + if (flag_show_thread and flag_thread_ok) + read_pid_tasks(pid); // for threads + + // add to History expect thread + if (ret == 2) + { + Procinfo *p = new Procinfo(*pi); // copy + p->clone = true; + hprocs->insert(pid, p); + } + } + else + { + // already gone. /proc/PID dead! + // later remove this process ! not yet + } + } + } + closedir(d); +} + +// COMMON , redesign +Proclist Proc::getHistory(int pos) +{ + Proclist l; + if (pos <= 0) + { + return l; + } + int size = history.size(); + if (size > pos) + l = history[size - pos]->procs; + return l; +} + +void Proc::setHistory(int tick) +{ + return; + if (tick <= 0) + { + mprocs = 0; + return; + } + int size = history.size(); + if (size > tick) + mprocs = &history[size - tick]->procs; + else + mprocs = 0; +} + +bool Procinfo::isThread() +{ + return pid != tgid; // how to check +} + +// LINUX size=64 +int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE, + F_SIZE, F_MEM, F_STAT, F_CPU, + F_START, F_TIME, F_CMDLINE, F_END}; + +// COMMON: basic field +int Procview::basic_fields[] = {F_PID, F_TTY, F_USER, F_CPUNUM, + F_STAT, F_MEM, F_CPU, F_START, + F_TIME, F_CMDLINE, F_END}; + +int Procview::jobs_fields[] = {F_PID, F_TGID, F_PPID, F_PGID, + F_SID, F_TPGID, F_STAT, F_UID, + F_TIME, F_CMDLINE, F_END}; + +int Procview::mem_fields[] = {F_PID, F_TTY, F_MAJFLT, F_MINFLT, F_SIZE, + F_RSS, F_TRS, F_DRS, F_STACK, F_SHARE, + // F_DT, + F_CMDLINE, F_END}; + +int Procview::sched_fields[] = {F_PID, F_TGID, F_NLWP, F_STAT, F_FLAGS, + F_PLCY, F_PRI, F_NICE, F_TMS, F_SLPAVG, + F_RPRI, F_AFFCPU, F_CPU, F_START, F_TIME, + F_CMDLINE, F_END}; + +void Procview::set_fields() +{ + switch (viewfields) + { + case USER: // BASIC FIELD + set_fields_list(basic_fields); + break; + case JOBS: + set_fields_list(jobs_fields); + break; + case MEM: + set_fields_list(mem_fields); + break; + case SCHED: + set_fields_list(sched_fields); + break; + case CUSTOM: + set_fields_list(custom_fields); + break; + default: + printf("Error ? set_fields_list \n"); + } + fieldArrange(); +} + +// LINUX: +// deduce whether the currently selected fields correspond to a field list +void Procview::deduce_fields() +{ + return; // under development (by fasthyun@magicn.com) 2006/05/24 + + if (viewfields != CUSTOM) + return; + + Procview::fieldstates tags[4] = {USER, JOBS, MEM, SCHED}; + int *lists[4] = {basic_fields, jobs_fields, mem_fields, sched_fields}; + for (int i = 0; i < 4; i++) + { + int *l = lists[i]; + int j; + for (j = 0; l[j] != F_END; j++) + if (findCol(l[j]) < 0) + break; + if (l[j] == F_END && j == cats.size()) + { + viewfields = tags[i]; + return; + } + } +} + +// move to Proc.cpp +#include // uname() +int get_kernel_version() +{ + int version = 0; + char *p; + struct utsname uname_info; + if (uname(&uname_info) == 0) // man -S 2 uname + { + // printf("sysname =%s \n",uname_info.sysname); + if (strcasecmp(uname_info.sysname, "linux") == 0) + { + Q_UNUSED(uname_info.release[0]); + } + p = uname_info.release; + char str[32]; + int major, minor, patch; + int result; + + result = sscanf(p, "%d.%d.%d", &major, &minor, &patch); + if (result == 2) + { + // only read two value + patch = 0; // ex) 3.0-ARCH + } + else if (result < 3) + { + fprintf(stderr, "Qps: can't determine version, read %s \n", p); + fprintf(stderr, "please report this bug.\n"); + exit(1); + } + version = major * 10000 + minor * 100 + patch; + // ex) 2.6.17 == 20617 , 2.4.8 == 20408 + printf("DEBUG: version = %d\n", version); + } + else + { + fprintf(stderr, "Qps: uname() failed. (%d) \n", version); + fprintf(stderr, "please report this bug.\n"); + exit(1); + } + return version; +} + +void check_system_requirement() +{ + int kernel_version = 0; + kernel_version = get_kernel_version(); + if (kernel_version < 20600) // less 2.6 + { + printf("Qps: kernel 2.4.x not supported !!!\n\n"); // because of + // 2.4.x SMP + // bugs + exit(0); + } +} diff --git a/src/proc_mosix.cpp b/src/proc_mosix.cpp new file mode 100644 index 0000000..39eac20 --- /dev/null +++ b/src/proc_mosix.cpp @@ -0,0 +1,2463 @@ +// proc.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias EngdegÃ¥rd, 1997-1999 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qps.h" +#include "proc.h" +#include "svec.cpp" +#include "uidstr.h" +#include "ttystr.h" +#include "wchan.h" +#include "details.h" + +#ifdef SOLARIS +#include +#include +#include +#include +#endif + +#include "proc_common.cpp" + +// socket states, from and touched to avoid name collisions +enum +{ + SSFREE = 0, /* not allocated */ + SSUNCONNECTED, /* unconnected to any socket */ + SSCONNECTING, /* in process of connecting */ + SSCONNECTED, /* connected to socket */ + SSDISCONNECTING /* in process of disconnecting */ +}; + +char procdir[128] = "/proc"; + +int kernel_version = 0; +int Procinfo::page_k_shift; + +Procinfo::Procinfo(int proc_pid) : refcnt(1) +{ + details = 0; + children = 0; + fd_files = 0; + maps = 0; + + environ = 0; + envblock = 0; + if (readproc(proc_pid) < 0) + pid = -1; // invalidate object, will be deleted + + selected = FALSE; + hidekids = FALSE; +} + +Procinfo::~Procinfo() +{ + if (details) + { + details->process_gone(); + details = 0; + } + delete environ; + if (envblock) + free(envblock); + if (maps) + { + maps->purge(); + delete maps; + } + if (fd_files) + { + fd_files->purge(); + delete fd_files; + } + delete children; +} + +// miscellaneous static initializations +void Procinfo::init_static() +{ + + page_k_shift = 0; + for (int j = getpagesize(); j > 1024; j >>= 1) + page_k_shift++; +} + +// return number of bytes read if ok, -1 if failed +int Procinfo::read_file(char *name, void *buf, int max) +{ + int fd = open(name, O_RDONLY); + if (fd < 0) + return -1; + int r = read(fd, buf, max); + close(fd); + return r; +} + +static inline bool isprintable(unsigned char c) +{ + // assume, somewhat naïvely, that all latin-1 characters are printable + return (c >= 0x20 && c < 0x7f) || c >= 0xa0; +} + +// replace unprintables by spaces +static void make_printable(char *s) +{ + while (*s) + { + if (!isprintable(*s)) + *s = ' '; + ++s; + } +} + +#ifdef LINUX + +// read task and ADD to Proc::procs[] !! +// eg) /proc/1234/task/* +int Proc::readTaskByPID(int pid) +{ + char path[256]; + struct dirent *e; + char *p; + int TID; + int thread_n = 0; + + printf("pid=%d :", pid); + sprintf(path, "/proc/%d/task", pid); + + DIR *d = opendir(path); + if (!d) + return FALSE; + + /* + if(!fd_files) + fd_files = new Svec(8); + fd_files->clear(); + + #ifdef LINUX + if(!sock_inodes) + sock_inodes = new Svec(4); + sock_inodes->clear(); + #endif + */ + // p = path + strlen(path) + 1;?? + // p[-1] = '/'; + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip . and .. + + TID = atoi(e->d_name); // only numbre !! + printf("tid=%d ", TID); + + if (pid != TID) + { + sprintf(procdir, "/proc/%d/task", pid, TID); + Procinfo *pi = new Procinfo(TID); + + if (pi->pid == -1) + { + printf("--"); + delete pi; // already gone + } + else + { + pi->generation = current_gen; + newproc(pi); + printf("**"); + } + } + + thread_n++; + // read_fd(fdnum, path); + } + printf("\n"); + closedir(d); + + return thread_n; +} + +int Procinfo::readproc(int proc_pid) +{ + char path[256]; + char buf[256]; + char sbuf[4096]; // should be enough to acommodate /proc/X/stat + char cmdbuf[MAX_CMD_LEN]; + QString tmp_str; + pid = proc_pid; + + // sprintf(path, "%s/%d", "/proc", pid); + sprintf(path, "%s/%d", procdir, proc_pid); + // read /proc/XX/cmdline + strcpy(buf, path); + strcat(buf, "/cmdline"); + int cmdlen; + if ((cmdlen = read_file(buf, cmdbuf, MAX_CMD_LEN - 1)) < 0) + return -1; + if (cmdlen == 0) + { + cmdline = ""; + } + else + { + for (int i = 0; i < cmdlen; i++) + if (!cmdbuf[i]) + cmdbuf[i] = ' '; + int j = cmdlen - 1; + while (j >= 0 && cmdbuf[j] == ' ') + j--; + cmdbuf[j + 1] = '\0'; + make_printable(cmdbuf); + tmp_str = cmdbuf; + cmdline = UniString(tmp_str); // for Non-ascii locale language + } + + // read /proc/XX/stat + strcpy(buf, path); + strcat(buf, "/stat"); + int statlen; + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + return -1; + sbuf[statlen] = '\0'; + char *p = strrchr(sbuf, ')'); + *p = '\0'; // split in two parts + command = strchr(sbuf, '(') + 1; + + // + // Not all values from /proc/#/stat are interesting; the ones left out + // have been retained in comments to see where they should go, in case + // they are needed again. + // + // In Linux 2.2.x, timeout has been removed, and the signal information + // here is obsolete (/proc/#/status has real-time signal info). + // + // There are undocumented values after wchan, unused so far: + // nswap pages swapped out since process started + // cnswap nswap of children + // exit_signal (2.2.x) signal sent to parent when process exits + // (The latter could provide a way to detect cloned processes since + // they usually have exit_signal != SIGCHLD, but I prefer waiting + // for real TIDs before implementing thread support.) + // + long stime, cstime; + int i_tty; + sscanf(p + 2, + "%c %d %d %d %d %d %lu %lu %lu %lu %lu " + "%ld %ld %ld %ld %d %d %*s %*s %lu %*s %*s %*s %*s %*s %*s %*s %*s " + "%*s %*s %*s %*s %lu", + &state, &ppid, &pgrp, &session, &i_tty, &tpgid, &flags, &minflt, + &cminflt, &majflt, &cmajflt, &utime, &stime, &cutime, &cstime, + &priority, &nice, + /* timeout, itrealvalue */ + &starttime, + /* vsize */ + /* rss */ + /* rlim, startcode, endcode, startstack kstkesp kstkeip, + signal, blocked, sigignore, sigcatch */ + &wchan); + + tty = (dev_t)i_tty; + utime += stime; // we make no user/system time distinction + cutime += cstime; + + // read /proc/XX/statm + strcpy(buf, path); + strcat(buf, "/statm"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + { + printf("33"); + return -1; + } + + sbuf[statlen] = '\0'; + sscanf(sbuf, "%lu %lu %lu %lu %lu %lu %lu", &size, &resident, &share, &trs, + &lrs, &drs, &dt); + size <<= page_k_shift; + resident <<= page_k_shift; + share <<= page_k_shift; + trs <<= page_k_shift; + lrs <<= page_k_shift; + drs <<= page_k_shift; + + pmem = 100.0 * resident / mem_total; + +#ifdef MOSIX + if (mosix_running) + { + // Read /proc/XX/where + strcpy(buf, path); + strcat(buf, "/where"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + where = -1; + else + { + sbuf[statlen] = '\0'; + sscanf(sbuf, "%d", &where); + } + // Read /proc/XX/cantmove + strcpy(buf, path); + strcat(buf, "/cantmove"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) > 0) + { + sbuf[statlen] = '\0'; + p = strchr(sbuf, '\n'); + if (p) + { + *p = '\0'; + cantmove = sbuf; + } + } + // Read /proc/XX/nmigs + strcpy(buf, path); + strcat(buf, "/nmigs"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + nmigs = -1; + else + { + sbuf[statlen] = '\0'; + sscanf(sbuf, "%d", &nmigs); + } + // Read /proc/XX/lock + strcpy(buf, path); + strcat(buf, "/lock"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + locked = -1; + else + { + sbuf[statlen] = '\0'; + sscanf(sbuf, "%d", &locked); + } + + // See if this is a remote job + // Read /proc/mosix/remote/xx/statm + char path2[256]; + sprintf(path2, "%s/mosix/remote/%d", procdir, pid); + // There are only two files to read: stats and from + // (statm is identical to /proc/xx/statm). + strcpy(buf, path2); + strcat(buf, "/stats"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) > 0) + { + isremote = TRUE; // This process is a visitor + + // The following variables are available, but are not + // used at the + // moment, so they are merely placeholders right now. + char state2; + long nswap, cnswap, rss, vsize; + if (sscanf(sbuf, "utime=%ld cutime=%ld nice=%d " + "state=%c vsize=%ld" + " rss=%ld nswap=%ld cnswap=%ld", + &utime, &cutime, &nice, &state2, &vsize, &rss, &nswap, + &cnswap) != 8) + return -1; + + // read "from" + strcpy(buf, path2); + strcat(buf, "/from"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + return -1; + sbuf[statlen] = '\0'; + if (sscanf(sbuf, "%d", &from) != 1) + return -1; + + // command is parsed wrong (contains "remote(xx)") + sscanf(command, "remote(%d)", &remotepid); + command = cmdline; // grab comm from there... + } + else + { + isremote = FALSE; + remotepid = -1; + from = -1; + } + if (from > 0) + sprintf(buf, "%d>", from); + else if (where > 0) + sprintf(buf, ">%d", where); + else + strcpy(buf, "-"); + migr = buf; + } +#endif // MOSIX + + // read /proc/XX/status + strcpy(buf, path); // /proc/$PID + strcat(buf, "/status"); + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + return -1; + sbuf[statlen] = '\0'; + if (!(p = strstr(sbuf, "Uid:"))) + return -1; + sscanf(p, "Uid: %d %d %d %d Gid: %d %d %d %d", &uid, &euid, &suid, &fsuid, + &gid, &egid, &sgid, &fsgid); + + which_cpu = 0; + per_cpu_times = 0; + + // only works kernel 2.4.x + if (num_cpus > 1) + { + per_cpu_times = new unsigned long[num_cpus]; + if (per_cpu_times == NULL) + return -1; + if (kernel_version < 20600) + { // less than version 2.6.0 and SMP + strcpy(buf, path); // /proc/$PID + strcat(buf, "/cpu"); // /proc/$PID/cpu + if ((statlen = read_file(buf, sbuf, sizeof(sbuf) - 1)) <= 0) + return -1; + sbuf[statlen] = '\0'; + p = sbuf; + for (unsigned cpu = 0; cpu < num_cpus; cpu++) + { + p = strchr(p, '\n'); + if (!p) + { + for (cpu = 0; cpu < num_cpus; cpu++) + per_cpu_times[cpu] = 0; + break; + } + p++; + unsigned long utime, stime; + sscanf(p, "%*s %lu %lu", &utime, &stime); + per_cpu_times[cpu] = utime + stime; + } + } + } + + gettimeofday(&tv, 0); + policy = -1; // will get it when needed + rtprio = -1; // ditto + + return pid; +} + +#endif // LINUX + +#ifdef SOLARIS +int Procinfo::readproc(int proc_pid) +{ + char path[256]; + + pid = proc_pid; + + sprintf(path, "%s/%d/psinfo", procdir, proc_pid); + psinfo_t psi; + if (read_file(path, (void *)&psi, sizeof(psi)) < (int)sizeof(psi)) + return -1; + + sprintf(path, "%s/%d/usage", procdir, proc_pid); + prusage_t pru; + if (read_file(path, (void *)&pru, sizeof(pru)) < (int)sizeof(pru)) + return -1; + + uid = psi.pr_uid; + euid = psi.pr_euid; + gid = psi.pr_gid; + egid = psi.pr_egid; + + make_printable(psi.pr_psargs); + cmdline = psi.pr_psargs; + + state = psi.pr_lwp.pr_sname; + command = (state == 'Z') ? "" : psi.pr_fname; + ppid = psi.pr_ppid; + pgrp = psi.pr_pgid; + session = psi.pr_sid; + tty = psi.pr_ttydev; // type? + flags = psi.pr_flag; + const int ns_ticks = 1000000000 / HZ; + utime = psi.pr_time.tv_sec * HZ + psi.pr_time.tv_nsec / ns_ticks; + cutime = psi.pr_ctime.tv_sec * HZ + psi.pr_ctime.tv_nsec / ns_ticks; + priority = psi.pr_lwp.pr_pri; + nice = psi.pr_lwp.pr_nice; + if (Qps::normalize_nice) + nice -= NZERO; + starttime = (psi.pr_start.tv_sec - boot_time) * HZ + + psi.pr_start.tv_nsec / ns_ticks; + wchan = psi.pr_lwp.pr_wchan; + minflt = pru.pr_minf; + majflt = pru.pr_majf; + size = psi.pr_size; + resident = psi.pr_rssize; + nthreads = psi.pr_nlwp; + addr_bits = psi.pr_dmodel == PR_MODEL_ILP32 ? 32 : 64; + which_cpu = psi.pr_lwp.pr_onpro; + env_ofs = psi.pr_envp; + + // pr_pctcpu and pr_pctmem are scaled so that 1.0 is stored as 0x8000. + // We rescale pcpu so a CPU-bound process is shown as 100%. (This means + // that wcpu may exceed 100% with several LWPs.) + wcpu = psi.pr_pctcpu * (1 / 327.68) * num_cpus; + pmem = psi.pr_pctmem * (1 / 327.68); + + gettimeofday(&tv, 0); + rtprio = -1; // ditto + policy_name[0] = psi.pr_lwp.pr_clname[0]; + policy_name[1] = psi.pr_lwp.pr_clname[1]; + + return pid; +} +#endif // SOLARIS + +float Procinfo::loadavg[] = {0.0, 0.0, 0.0}; +int Procinfo::mem_total = 0; +int Procinfo::mem_free = 0; +#ifdef LINUX +// int Procinfo::mem_shared = 0; // only linux kernel 2.4.x +int Procinfo::mem_buffers = 0; +int Procinfo::mem_cached = 0; +#endif +int Procinfo::swap_total = 0; +int Procinfo::swap_free = 0; +unsigned *Procinfo::cpu_times_vec = 0; +unsigned *Procinfo::old_cpu_times_vec = 0; +long Procinfo::boot_time = 0; +#ifdef LINUX +Q3IntDict Procinfo::socks(17); +bool Procinfo::socks_current = FALSE; +Q3IntDict Procinfo::usocks(17); +bool Procinfo::usocks_current = FALSE; +#endif +unsigned int Procinfo::num_cpus = 0; +unsigned int Procinfo::old_num_cpus = 0; +#ifdef SOLARIS +kstat_ctl_t *Procinfo::kc = 0; +#endif +#ifdef MOSIX +bool Procinfo::mosix_running; +#endif + +#ifdef LINUX +// just grab the load averages +void Procinfo::read_loadavg() +{ + char path[80]; + char buf[512]; + strcpy(path, procdir); + strcat(path, "/loadavg"); + int n; + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + { + fprintf(stderr, "qps: Cannot open /proc/loadavg" + " (make sure /proc is mounted)\n"); + exit(1); + } + buf[n] = '\0'; + sscanf(buf, "%f %f %f", &loadavg[0], &loadavg[1], &loadavg[2]); +} +#endif // LINUX + +#ifdef SOLARIS +static float getscaled(kstat_t *ks, const char *name) +{ + // load avgs are scaled by 256 + kstat_named_t *kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)name); + return kn ? kn->value.ui32 * (1 / 256.0) : 0.0; +} + +void Procinfo::read_loadavg() +{ + kstat_chain_update(kc); + + kstat_t *ks = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); + if (!ks || kstat_read(kc, ks, 0) == -1) + { + perror("kstat_lookup/read"); + exit(1); + } + + loadavg[0] = getscaled(ks, "avenrun_1min"); + loadavg[1] = getscaled(ks, "avenrun_5min"); + loadavg[2] = getscaled(ks, "avenrun_15min"); + + // we might as well get the boot time too since it's in the same kstat + // (not that it is going to change) + kstat_named_t *kn; + kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)"boot_time"); + if (kn) + boot_time = kn->value.ui32; +} +#endif // SOLARIS + +#ifdef LINUX +// read information common to all processes +// !!! should be simplified !! +void Procinfo::read_common() +{ + char path[80]; + char buf[4096 + 1]; + + char *p; + int n; + + int i1, i2, i3, i4; + // read kernel version + strcpy(path, "/proc/version"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return; + buf[n] = '\0'; + sscanf(buf, "Linux version %d.%d.%d", &i1, &i2, &i3); + kernel_version = i1 * 10000 + i2 * 100 + i3 * 1; + // printf("DEBUG: kernel_version =%d \n",kernel_version); + + // read memory info + strcpy(path, procdir); + strcat(path, "/meminfo"); + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return; + buf[n] = '\0'; + + // Skip the old /meminfo cruft, making this work in post-2.1.42 kernels + // as well. (values are now in kB) + p = strstr(buf, "MemTotal:"); + sscanf(p, "MemTotal: %d kB\nMemFree: %d kB\b\nBuffers: %d kB\nCached: " + "%d kB\n", + &mem_total, &mem_free, &mem_buffers, &mem_cached); + p = strstr(buf, "SwapTotal:"); + sscanf(p, "SwapTotal: %d kB\nSwapFree: %d kB\n", &swap_total, &swap_free); + + // read system status + strcpy(path, procdir); + strcat(path, "/stat"); // /proc/stat + if ((n = read_file(path, buf, sizeof(buf) - 1)) <= 0) + return; + buf[n] = '\0'; + + old_num_cpus = num_cpus; + if (!num_cpus) + { +// count cpus +#ifdef FAKE_SMP + num_cpus = 4; +#else + char *p; + p = strstr(buf, "cpu"); + while (p < buf + sizeof(buf) - 4 && strncmp(p, "cpu", 3) == 0) + { + num_cpus++; + if (strncmp(p, "cpu0", 4) == 0) + num_cpus--; + p = strchr(p, '\n'); + if (p) + p++; + } +#endif + cpu_times_vec = new unsigned[CPUTIMES * num_cpus]; + old_cpu_times_vec = new unsigned[CPUTIMES * num_cpus]; + } + + for (unsigned cpu = 0; cpu < num_cpus; cpu++) + for (int i = 0; i < CPUTIMES; i++) + old_cpu_times(cpu, i) = cpu_times(cpu, i); + + if (num_cpus == 1) + { + unsigned iowait, irq, sftirq; + sscanf(buf, "cpu %u %u %u %u %u %u %u", &cpu_times(0, CPUTIME_USER), + &cpu_times(0, CPUTIME_NICE), &cpu_times(0, CPUTIME_SYSTEM), + &cpu_times(0, CPUTIME_IDLE), &iowait, &irq, &sftirq); + cpu_times(0, CPUTIME_SYSTEM) += (irq + sftirq); + cpu_times(0, CPUTIME_IDLE) += iowait; + } + else + { +#ifdef FAKE_SMP + sscanf(buf, "cpu %u %u %u %u", &cpu_times(0, CPUTIME_USER), + &cpu_times(0, CPUTIME_NICE), &cpu_times(0, CPUTIME_SYSTEM), + &cpu_times(0, CPUTIME_IDLE)); + for (unsigned cpu = 1; cpu < num_cpus; cpu++) + { + for (int i = 0; i < CPUTIMES; i++) + cpu_times(cpu, i) = cpu_times(0, i); + } +#else + // SMP + for (unsigned cpu = 0; cpu < num_cpus; cpu++) + { + char cpu_buf[10]; + sprintf(cpu_buf, "cpu%d", cpu); + if ((p = strstr(buf, cpu_buf)) != 0) + { + sscanf(p, "%*s %u %u %u %u", &cpu_times(cpu, CPUTIME_USER), + &cpu_times(cpu, CPUTIME_NICE), + &cpu_times(cpu, CPUTIME_SYSTEM), + &cpu_times(cpu, CPUTIME_IDLE)); + } + else + { + fprintf(stderr, "Error reading info for cpu %d\n", cpu); + abort(); + } + } +#endif + } + + // 2.0.x kernels (at least up to 2.0.33) have an SMP bug that reports + // cpu_time(CPUTIME_IDLE) incorrectly, since it doesn't take the number + // of + // cpus into account. This is fixed in 2.1.x kernels, and since 2.0.x + // is rather old (and unsuited for SMP anyway) we don't work around it. + + p = strstr(buf, "btime") + 6; + sscanf(p, "%lu", &boot_time); +} +#endif // LINUX + +#ifdef SOLARIS +void Procinfo::read_common() +{ + // memory info: this is easy - just use sysconf + + mem_total = sysconf(_SC_PHYS_PAGES) << page_k_shift; + mem_free = sysconf(_SC_AVPHYS_PAGES) << page_k_shift; + + // get swap info: somewhat trickier - we have to count all swap spaces + + int nswaps = swapctl(SC_GETNSWP, 0); + swaptbl_t *st = + (swaptbl_t *)malloc(sizeof(int) + nswaps * sizeof(swapent_t)); + st->swt_n = nswaps; + // We are not interested in the paths, just the values, so we allocate + // one scratch buffer for all paths to keep swapctl happy. + char path_buf[PATH_MAX + 1]; + for (int i = 0; i < nswaps; i++) + st->swt_ent[i].ste_path = path_buf; + swapctl(SC_LIST, st); + // count the swap spaces + swap_total = swap_free = 0; + for (int i = 0; i < nswaps; i++) + { + swap_total += st->swt_ent[i].ste_pages; + swap_free += st->swt_ent[i].ste_free; + } + swap_total <<= page_k_shift; + swap_free <<= page_k_shift; + free(st); + + if (cpu_times_vec) + { + if (old_cpu_times_vec) + free(old_cpu_times_vec); + old_cpu_times_vec = cpu_times_vec; + cpu_times_vec = + (unsigned *)malloc(sizeof(unsigned) * num_cpus * CPUTIMES); + } + old_num_cpus = num_cpus; + + // cpu states: are stored as kstats named "cpu_statN", where N is the + // cpu number. Unfortunately, the cpu numbers are not guessable so we + // sweep the kstat chain for all of them, assuming (foolishly?) + // that they are in order. + + kstat_chain_update(kc); + int cpu = 0; + for (kstat_t *ks = kc->kc_chain; ks; ks = ks->ks_next) + { + if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) + { + if (kstat_read(kc, ks, NULL) == -1) + { + perror("kstat_read"); + exit(1); + } + cpu_stat_t *cs = (cpu_stat_t *)ks->ks_data; + if (cpu + 1 >= (int)num_cpus) + { + num_cpus = cpu + 1; + cpu_times_vec = (unsigned *)realloc( + cpu_times_vec, num_cpus * CPUTIMES * sizeof(unsigned)); + } + cpu_times(cpu, CPUTIME_USER) = cs->cpu_sysinfo.cpu[CPU_USER]; + cpu_times(cpu, CPUTIME_SYSTEM) = cs->cpu_sysinfo.cpu[CPU_KERNEL]; + cpu_times(cpu, CPUTIME_WAIT) = cs->cpu_sysinfo.cpu[CPU_WAIT]; + cpu_times(cpu, CPUTIME_IDLE) = cs->cpu_sysinfo.cpu[CPU_IDLE]; + cpu++; + } + } +} +#endif // SOLARIS + +#ifdef MOSIX +void Procinfo::check_for_mosix() +{ + char path[256]; + strcpy(path, procdir); + strcat(path, "/mosix"); + DIR *d = opendir(path); + if (d) + { + closedir(d); + mosix_running = TRUE; + return; + } + mosix_running = FALSE; +} + +Svec Procinfo::mosix_nodes() +{ + Svec nodes; + char path[256]; + strcpy(path, procdir); + strcat(path, "/mosix/nodes"); + DIR *d = opendir(path); + if (d) + { + struct dirent *e; + while ((e = readdir(d)) != 0) + { + int num; + if (sscanf(e->d_name, "%d", &num) == 1) + nodes.add(num); + } + closedir(d); + } + return nodes; +} +#endif // MOSIX + +int Procinfo::get_policy() +{ + if (policy == -1) + policy = sched_getscheduler(pid); + return policy; +} + +int Procinfo::get_rtprio() +{ + if (rtprio == -1) + { + struct sched_param p; + if (sched_getparam(pid, &p) == 0) + rtprio = p.sched_priority; + } + return rtprio; +} + +#ifdef LINUX +void Procinfo::read_fd(int fdnum, char *path) +{ + int len; + char buf[80]; + struct stat sb; + + // The fd mode is contained in the link permission bits + if (lstat(path, &sb) < 0) + return; + int mode = 0; + if (sb.st_mode & 0400) + mode |= OPEN_READ; + if (sb.st_mode & 0200) + mode |= OPEN_WRITE; + + if ((len = readlink(path, buf, sizeof(buf) - 1)) > 0) + { + buf[len] = '\0'; + unsigned long dev, ino; + if ((buf[0] == '[' // Linux 2.0 style /proc/fd + && sscanf(buf, "[%lx]:%lu", &dev, &ino) == 2 && dev == 0) || + sscanf(buf, "socket:[%lu]", &ino) > 0) + { // Linux 2.1 + Sockinfo *si = Procinfo::socks[ino]; + char buf[80]; + if (si) + { + // a TCP or UDP socket + sock_inodes->add(SockInode(fdnum, ino)); + sprintf(buf, "%sp socket %lu", + si->proto == Sockinfo::TCP ? "tc" : "ud", ino); + fd_files->add(new Fileinfo(fdnum, buf, mode)); + return; + } + else + { + // maybe a unix domain socket? + read_usockets(); + UnixSocket *us = Procinfo::usocks[ino]; + if (us) + { + char *tp = "?", *st = "?"; + switch (us->type) + { + case SOCK_STREAM: + tp = "stream"; + break; + case SOCK_DGRAM: + tp = "dgram"; + break; + } + switch (us->state) + { + case SSFREE: + st = "free"; + break; + case SSUNCONNECTED: + st = "unconn"; + break; + case SSCONNECTING: + st = "connecting"; + break; + case SSCONNECTED: + st = "connected"; + break; + case SSDISCONNECTING: + st = "disconn"; + break; + } + sprintf(buf, "unix domain socket %lu (%s, %s) ", ino, tp, + st); + QString s = buf; + s.append(us->name); + fd_files->add(new Fileinfo(fdnum, s, mode)); + return; + } + } + } + // assume fds will be read in increasing order + fd_files->add(new Fileinfo(fdnum, buf, mode)); + } +} +#endif // LINUX + +#ifdef SOLARIS +void Procinfo::read_fd(int fdnum, char *path) +{ + struct stat sb; + if (lstat(path, &sb) < 0) + { + // The file has been closed, or we could really not stat it + // despite + // having it open (could be a fd passed from another process). + + fd_files->add(new Fileinfo(fdnum, "(no info available)")); + return; + } + // We could in principle find out more about the fd, such as its mode + // (RDONLY, RDWR etc) and flags, but it's messy. pfiles uses an agent + // lwp + // for this, but I don't know how to do it. + QString s; + const char *n; + switch (sb.st_mode & S_IFMT) + { + case S_IFCHR: + // if it is a tty, we might know its real name + if (sb.st_rdev != (dev_t)-1) + { + QString t = Ttystr::name(sb.st_rdev); + if (t[0] != '?') + { + s = "/dev/"; + s.append(t); + break; + } + } + s.sprintf("char device %u:%u", (unsigned)major(sb.st_rdev), + (unsigned)minor(sb.st_rdev)); + break; + + case S_IFBLK: + s.sprintf("block device %u:%u", (unsigned)major(sb.st_rdev), + (unsigned)minor(sb.st_rdev)); + break; + + case S_IFLNK: + // Directories appear as symlinks in /proc/#/fd; we chdir() to + // it + // and see where we end up. Not efficient though. + // Besides, we change cwd a lot in unpredictable ways. This + // makes + // core dumps hard to find, if they are generated at all. + s = "directory "; + if (chdir(path) >= 0) + { + char buf[512]; + if (getcwd(buf, sizeof(buf)) >= 0) + { + s.append(buf); + break; + } + } + s.append("(unknown)"); + break; + + default: + switch (sb.st_mode & S_IFMT) + { + case S_IFIFO: // fifo or anonymous pipe + n = "pipe"; + break; + case S_IFDIR: // this shouldn't happen + n = "directory"; + break; + case S_IFREG: + n = "file"; + break; + case S_IFSOCK: + n = "unix domain socket"; + break; + case S_IFDOOR: + n = "door"; + break; + default: + n = "unknown"; + break; + } + s.sprintf("%s, dev %u:%u inode %u", n, (unsigned)major(sb.st_dev), + (unsigned)minor(sb.st_dev), (unsigned)sb.st_ino); + break; + } + fd_files->add(new Fileinfo(fdnum, s)); +} +#endif // SOLARIS + +// return TRUE if /proc/PID/fd could be read, FALSE otherwise +// store fileinfo, and also socket inodes separately +bool Procinfo::read_fds() +{ + char path[80], *p; + sprintf(path, "%s/%d/fd", procdir, pid); + + DIR *d = opendir(path); + if (!d) + return FALSE; + + if (!fd_files) + fd_files = new Svec(8); + fd_files->clear(); + +#ifdef LINUX + if (!sock_inodes) + sock_inodes = new Svec(4); + sock_inodes->clear(); +#endif + + p = path + strlen(path) + 1; + p[-1] = '/'; + + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip . and .. + strcpy(p, e->d_name); + int fdnum = atoi(p); + read_fd(fdnum, path); + } + closedir(d); + return TRUE; +} + +#ifdef LINUX + +bool Procinfo::read_socket_list(Sockinfo::proto_t proto, char *pseudofile) +{ + char path[80]; + strcpy(path, procdir); + strcat(path, "/net/"); + strcat(path, pseudofile); + FILE *f = fopen(path, "r"); + if (!f) + return FALSE; + + char buf[256]; + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f) != 0) + { + Sockinfo *si = new Sockinfo; + si->proto = proto; + unsigned local_port, rem_port, st, tr; + sscanf(buf + 6, "%x:%x %x:%x %x %x:%x %x:%x %x %d %d %d", + &si->local_addr, &local_port, &si->rem_addr, &rem_port, &st, + &si->tx_queue, &si->rx_queue, &tr, &si->tm_when, &si->rexmits, + &si->uid, &si->timeout, &si->inode); + // fix fields that aren't sizeof(int) + si->local_port = local_port; + si->rem_port = rem_port; + si->st = st; + si->tr = tr; + + socks.insert(si->inode, si); + if (socks.count() > socks.size() * 3) + socks.resize(socks.count()); + } + fclose(f); + return TRUE; +} + +bool Procinfo::read_usocket_list() +{ + char path[80]; + strcpy(path, procdir); + strcat(path, "/net/unix"); + FILE *f = fopen(path, "r"); + if (!f) + return FALSE; + + char buf[256]; + fgets(buf, sizeof(buf), f); // skip header + while (fgets(buf, sizeof(buf), f)) + { + if (buf[0]) + buf[strlen(buf) - 1] = '\0'; // chomp newline + UnixSocket *us = new UnixSocket; + unsigned q; + unsigned type, state; + int n; + sscanf(buf, "%x: %x %x %x %x %x %ld %n", &q, &q, &q, &us->flags, &type, + &state, &us->inode, &n); + us->name = buf + n; + us->type = type; + us->state = state; + usocks.insert(us->inode, us); + if (usocks.count() > usocks.size() * 3) + usocks.resize(usocks.count()); + } + fclose(f); + return TRUE; +} + +void Procinfo::read_sockets() +{ + if (socks_current) + return; + + socks.clear(); + if (!read_socket_list(Sockinfo::TCP, "tcp") || + !read_socket_list(Sockinfo::UDP, "udp")) + return; + + socks_current = TRUE; +} + +void Procinfo::read_usockets() +{ + if (usocks_current) + return; + + usocks.clear(); + if (!read_usocket_list()) + return; + + usocks_current = TRUE; +} + +void Procinfo::invalidate_sockets() { socks_current = usocks_current = FALSE; } + +// return TRUE if /proc/XX/maps could be read, FALSE otherwise +bool Procinfo::read_maps() +{ + // idea: here we could readlink /proc/XX/exe to identify the executable + // when running 2.0.x + char name[80]; + sprintf(name, "%s/%d/maps", procdir, pid); + FILE *f = fopen(name, "r"); + if (!f) + return FALSE; + char line[1024]; // lines can be this long, or longer + if (!maps) + maps = new Svec; + else + maps->clear(); + + while (fgets(line, sizeof(line), f)) + { + Mapsinfo *mi = new Mapsinfo; + int n; + sscanf(line, "%lx-%lx %4c %lx %x:%x %lu%n", &mi->from, &mi->to, + mi->perm, &mi->offset, &mi->major, &mi->minor, &mi->inode, &n); + if (line[n] != '\n') + { + int len = strlen(line); + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + while (line[n] == ' ' && line[n]) + n++; + mi->filename = line + n; + } + else if ((mi->major | mi->minor | mi->inode) == 0) + mi->filename = "(anonymous)"; + maps->add(mi); + } + fclose(f); + return TRUE; +} + +// return TRUE if /proc/XX/environ could be read, FALSE otherwise +bool Procinfo::read_environ() +{ + int bs = 4096; // good start + if (envblock) + free(envblock); + envblock = (char *)malloc(bs + 1); + char path[128]; + sprintf(path, "%s/%d/environ", procdir, pid); + int fd = open(path, O_RDONLY); + if (fd < 0) + { + free(envblock); + envblock = 0; + return FALSE; + } + int n; + int ofs = 0; + while ((n = read(fd, envblock + ofs, bs - ofs)) == bs - ofs) + { + ofs = bs; + envblock = (char *)realloc(envblock, (bs += 4096) + 1); + } + close(fd); + if (n < 0) + { + free(envblock); + envblock = 0; + return FALSE; + } + n += ofs; + envblock[n] = '\0'; + if (!environ) + environ = new Svec(64); + else + environ->clear(); + for (int i = 0; i < n;) + { + char *p = strchr(envblock + i, '='); + if (p) + *p++ = '\0'; + else // degenerate variable: treat as name with empty value + p = envblock + i + strlen(envblock + i); + make_printable(envblock + i); + make_printable(p); + environ->add(NameValue(envblock + i, p)); + i = p - envblock + strlen(p) + 1; + } + return TRUE; +} + +#endif // LINUX + +#ifdef SOLARIS + +// return TRUE if the process environment could be read, FALSE otherwise +bool Procinfo::read_environ() +{ + int fd; + char file[128]; + sprintf(file, "/proc/%d/as", pid); + if ((fd = open(file, O_RDONLY)) < 0) + return FALSE; + + // Just read the first 8K from the environment. Adaptive code here is + // possible, but not really worth the effort. + int bs = 8192; + if (envblock) + free(envblock); + envblock = (char *)malloc(bs); + if (pread(fd, envblock, bs, env_ofs) < 0) + { + free(envblock); + envblock = 0; + return FALSE; + } + close(fd); + envblock[bs - 1] = '\0'; + + if (!environ) + environ = new Svec(64); + else + environ->clear(); + + for (int i = 0; i * (int)sizeof(char *) < bs && ((char **)envblock)[i]; i++) + { + int b = ((char **)envblock)[i] - (char *)env_ofs; + if (b < 0 || b >= bs) + continue; // outside retrieved memory block + char *val = strchr(envblock + b, '='); + if (val) + *val++ = '\0'; + else + val = (char *)""; // degenerate: treat as name with + // empty value + make_printable(envblock + b); + make_printable(val); + environ->add(NameValue(envblock + b, val)); + } + return TRUE; +} + +// Try using /proc/bin/pmap to add file names to the memory map +void Procinfo::read_pmap_maps() +{ + char buf[256]; + sprintf(buf, "/usr/proc/bin/pmap %d 2>/dev/null", pid); + FILE *pmap = popen(buf, "r"); + if (!pmap) + return; + + // skip first line + if (!fgets(buf, sizeof buf, pmap)) + { + pclose(pmap); + return; + } + + int map_num = 0; + while (fgets(buf, sizeof buf, pmap)) + { + // Each output line from pmap looks like + //
K + // We use
and only to match with previously + // read + // map info, and only use here. + + unsigned long addr; + unsigned len; + int next; + char *p; + if (sscanf(buf, "%lx %dK%n", &addr, &len, &next) != 2) + continue; + + // Correlate this with info already gathered. Assume they are in + // the + // same order (ascending by address). + Mapsinfo *mi; + int i = map_num; + while (i < maps->size() && (mi = (*maps)[i])->from != addr) + i++; + // Mismatches can happen since changes can have taken place + // since + // we read the maps. If so, skip this mapping and try the next. + if (mi->to != addr + ((unsigned long)len << 10) || i == maps->size()) + continue; + map_num = i + 1; + + while (buf[next] == ' ') + next++; + while (buf[next] && buf[next] != ' ') + next++; + while (buf[next] == ' ') + next++; + // At this point we are looking at a file name, or at a + // suitable + // designator like [ heap ], [ anon ] or [ stack ]. Use it right + // away + // (after peeling off the newline) + int l = strlen(buf + next); + if (buf[next + l - 1] == '\n') + buf[next + l - 1] = '\0'; + mi->filename = buf + next; + } + + pclose(pmap); + return; +} + +// return TRUE if /proc/XX/map could be read, FALSE otherwise +bool Procinfo::read_maps() +{ + char name[128]; + sprintf(name, "%s/%d/map", procdir, pid); + FILE *f = fopen(name, "r"); + if (!f) + return FALSE; + if (!maps) + maps = new Svec; + else + maps->clear(); + + prmap_t pm; + while (fread(&pm, sizeof(pm), 1, f) == 1) + { + Mapsinfo *mi = new Mapsinfo; + mi->from = pm.pr_vaddr; + mi->to = pm.pr_vaddr + pm.pr_size; + mi->offset = pm.pr_offset; + mi->perm[0] = pm.pr_mflags & MA_READ ? 'r' : '-'; + mi->perm[1] = pm.pr_mflags & MA_WRITE ? 'w' : '-'; + mi->perm[2] = pm.pr_mflags & MA_EXEC ? 'x' : '-'; + mi->perm[3] = pm.pr_mflags & MA_SHARED ? 's' : 'p'; + + if (pm.pr_mapname[0]) + { + // To find device/inode, stat the file in + // /proc/#/object: + char obj[128]; + sprintf(obj, "%s/%d/object/%s", procdir, pid, pm.pr_mapname); + struct stat sb; + if (lstat(obj, &sb) < 0) + { + delete mi; + continue; + } + mi->major = major(sb.st_dev); + mi->minor = minor(sb.st_dev); + mi->inode = sb.st_ino; + if (strcmp(pm.pr_mapname, "a.out") == 0) + mi->filename = "(executable)"; + } + else + { + mi->major = mi->minor = mi->inode = 0; + mi->filename = "(anonymous)"; + } + + maps->add(mi); + } + fclose(f); + + // If desired and possible, use /usr/proc/bin/pmap to get the + // names of the mapped files + static int myeuid = geteuid(); + if (Qps::use_pmap && (myeuid == 0 || myeuid == euid)) + read_pmap_maps(); + + return TRUE; +} + +#endif // SOLARIS + +Category::~Category() {} + +int Category::compare(Procinfo *a, Procinfo *b) +{ +#if QT_VERSION < 200 + return strcmp(string(a), string(b)); +#else + return string(a).compare(string(b)); +#endif +} + +Cat_int::Cat_int(const char *heading, const char *explain, int w, + int Procinfo::*member) + : Category(heading, explain), int_member(member), field_width(w) +{ +} + +QString Cat_int::string(Procinfo *p) +{ + QString s; + s.setNum(p->*int_member); + return s; +} + +int Cat_int::compare(Procinfo *a, Procinfo *b) +{ + // qsort() only cares about the sign of the number returned by the + // comparison function; only a subtraction is necessary + return a->*int_member - b->*int_member; +} + +Cat_uintl::Cat_uintl(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member) + : Category(heading, explain), uintl_member(member), field_width(w) +{ +} + +QString Cat_uintl::string(Procinfo *p) +{ + QString s; + s.setNum(p->*uintl_member); + return s; +} + +int Cat_uintl::compare(Procinfo *a, Procinfo *b) +{ + int bu = b->*uintl_member, au = a->*uintl_member; + return bu >= au ? (bu == au ? 0 : 1) : -1; +} + +Cat_hex::Cat_hex(const char *heading, const char *explain, int w, + unsigned long Procinfo::*member) + : Cat_uintl(heading, explain, w, member) +{ +} + +QString Cat_hex::string(Procinfo *p) +{ + QString s; + s.sprintf("%8x", (unsigned)(p->*uintl_member)); + return s; +} + +Cat_swap::Cat_swap(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_swap::string(Procinfo *p) +{ + QString s; + // It can actually happen that size < resident (XSun under Solaris 2.6) + s.setNum(p->size > p->resident ? p->size - p->resident : 0); + return s; +} + +int Cat_swap::compare(Procinfo *a, Procinfo *b) +{ + return (b->size - b->resident) - (a->size - a->resident); +} + +Cat_string::Cat_string(const char *heading, const char *explain, + QString Procinfo::*member) + : Category(heading, explain), str_member(member) +{ +} + +QString Cat_string::string(Procinfo *p) { return p->*str_member; } + +Cat_user::Cat_user(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_user::string(Procinfo *p) +{ +#ifdef MOSIX + if (p->isremote) + return "-mosix-"; +#endif + if (p->uid == p->euid) + return Uidstr::userName(p->uid); + else + { + QString s = Uidstr::userName(p->uid); +#if QT_VERSION < 200 + s.detach(); +#endif + s.append(p->euid == 0 ? "*" : "+"); + return s; + } +} + +Cat_group::Cat_group(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_group::string(Procinfo *p) +{ + if (p->gid == p->egid) + return Uidstr::groupName(p->gid); + else + { + QString s = Uidstr::groupName(p->gid); +#if QT_VERSION < 200 + s.detach(); +#endif + s.append("*"); + return s; + } +} + +Cat_wchan::Cat_wchan(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_wchan::string(Procinfo *p) { return Wchan::name(p->wchan); } + +Cat_cmdline::Cat_cmdline(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_cmdline::string(Procinfo *p) +{ + if (p->cmdline.isEmpty()) + { + QString s("("); + s.append(p->command); + s.append(")"); + return s; + } + else + { + if (Qps::show_file_path) + return p->cmdline; + else + { + QString s(p->cmdline); +#if QT_VERSION < 200 + s.detach(); +#endif + int i = s.find(' '); + if (i < 0) + i = s.length(); + if (i > 0) + { + i = s.findRev('/', i - 1); + if (i >= 0) + s.remove(0, i + 1); + } + return s; + } + } +} + +Cat_dir::Cat_dir(const char *heading, const char *explain, const char *dirname, + QString Procinfo::*member) + : Cat_string(heading, explain), dir(dirname), cache(member) +{ +} + +QString Cat_dir::string(Procinfo *p) +{ + if ((p->*cache).isNull()) + { + char path[128], buf[512]; + sprintf(path, "%s/%d/%s", procdir, p->pid, dir); + +#ifdef LINUX + int n = readlink(path, buf, sizeof(buf) - 1); + if (n < 0) + { + // Either a kernel process, or access denied. + // A hyphen is visually least disturbing here. + p->*cache = "-"; + return p->*cache; + } + else if (buf[0] != '[') + { + // linux >= 2.1.x: path name directly in link + buf[n] = '\0'; + p->*cache = buf; + return p->*cache; + } +#endif + + // Either a Linux 2.0 link in [device]:inode form, or a Solaris + // link. + // To resolve it, we just chdir() to it and see where we end up. + // Perhaps we should change back later? + if (chdir(path) < 0) + { + p->*cache = "-"; // Most likely access denied + } + else + { + // getcwd() is fairly expensive, but this is cached + // anyway + if (!getcwd(buf, sizeof(buf))) + { + p->*cache = "(deleted)"; + } + else + p->*cache = buf; + } + } + return p->*cache; +} + +Cat_state::Cat_state(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_state::string(Procinfo *p) +{ + QString s(" "); + s[0] = p->state; +#ifdef SOLARIS + if (p->state == 'Z') + return s; +#endif + s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' '; + int ni = p->nice; +#ifdef SOLARIS + if (!Qps::normalize_nice) + ni -= NZERO; +#endif + s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' '); + return s; +} + +Cat_policy::Cat_policy(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_policy::string(Procinfo *p) +{ + QString s; +#ifdef LINUX + switch (p->get_policy()) + { + case SCHED_FIFO: + s = "FI"; + break; // first in, first out + case SCHED_RR: + s = "RR"; + break; // round-robin + case SCHED_OTHER: + s = "TS"; + break; // time-sharing + default: + s = "??"; + break; + } +#endif +#ifdef SOLARIS + s = " "; + s[0] = p->policy_name[0]; + s[1] = p->policy_name[1]; +#endif + return s; +} + +int Cat_policy::compare(Procinfo *a, Procinfo *b) +{ +#ifdef LINUX + return b->get_policy() - a->get_policy(); +#endif +#ifdef SOLARIS + int r = b->policy_name[0] - a->policy_name[0]; + return r ? r : b->policy_name[1] - a->policy_name[1]; +#endif +} + +Cat_rtprio::Cat_rtprio(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_rtprio::string(Procinfo *p) +{ + QString s; + s.setNum(p->get_rtprio()); + return s; +} + +int Cat_rtprio::compare(Procinfo *a, Procinfo *b) +{ + return b->get_rtprio() - a->get_rtprio(); +} + +Cat_time::Cat_time(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_time::string(Procinfo *p) +{ + QString s; + int ticks = p->utime; + if (Qps::cumulative) + ticks += p->cutime; + int t = ticks / HZ; // seconds + if (t < 10) + { + int hundreds = ticks / (HZ / 100) % 100; + s.sprintf("%1d.%02ds", t, hundreds); + } + else if (t < 100 * 60) + { + s.sprintf("%2d:%02d", t / 60, t % 60); + } + else if (t < 100 * 3600) + { + int h = t / 3600; + t %= 3600; + s.sprintf("%2d:%02dh", h, t / 60); + } + else + { + int d = t / 86400; + t %= 86400; + s.sprintf("%dd%dh", d, t / 3600); + } + return s; +} + +int Cat_time::compare(Procinfo *a, Procinfo *b) +{ + int at = a->utime, bt = b->utime; + if (Qps::cumulative) + { + at += a->cutime; + bt += b->cutime; + } + return bt - at; +} + +Cat_start::Cat_start(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_start::string(Procinfo *p) +{ +#ifdef SOLARIS + if (p->state == 'Z') + return "-"; // Solaris zombies have no valid start time +#endif + time_t start = p->boot_time + p->starttime / (unsigned)HZ; + QString s; + char *ct = ctime(&start); + if (p->tv.tv_sec - start < 86400) + { + ct[16] = '\0'; + s = ct + 11; + } + else + { + ct[10] = '\0'; + s = ct + 4; + } + return s; +} + +int Cat_start::compare(Procinfo *a, Procinfo *b) +{ + unsigned long bs = b->starttime, as = a->starttime; + return bs >= as ? (bs == as ? 0 : 1) : -1; +} + +Cat_percent::Cat_percent(const char *heading, const char *explain, int w, + float Procinfo::*member) + : Category(heading, explain), float_member(member), field_width(w) +{ +} + +QString Cat_percent::string(Procinfo *p) +{ + QString s; + s.sprintf("%01.2f", (double)(p->*float_member)); + return s; +} + +int Cat_percent::compare(Procinfo *a, Procinfo *b) +{ + float at = a->*float_member, bt = b->*float_member; + return at < bt ? 1 : (at > bt ? -1 : 0); +} + +Cat_tty::Cat_tty(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_tty::string(Procinfo *p) { return Ttystr::name(p->tty); } + +Proc::Proc() +{ + // Note: When adding/removing/changing the fields, the save file + // version must be increased! + char *command, *command_line; + allcats.set(F_PID, new Cat_int("PID", "Process ID", 6, &Procinfo::pid)); + allcats.set(F_PPID, + new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid)); + allcats.set(F_PGID, + new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp)); + allcats.set(F_SID, new Cat_int("SID", "Session ID", 6, &Procinfo::session)); + allcats.set(F_TTY, new Cat_tty("TTY", "Controlling tty")); +#ifdef LINUX + allcats.set(F_TPGID, new Cat_int("TPGID", "Process group ID of tty owner", + 6, &Procinfo::tpgid)); +#endif +#ifdef MOSIX + allcats.set(F_MIGR, new Cat_string("MIGR", "Process migrated to (>node) " + "or from (node>)", + &Procinfo::migr)); + allcats.set(F_LOCKED, + new Cat_int("LOCK", "Whether this process is locked to" + " this node", + 6, &Procinfo::locked)); + allcats.set(F_NMIGS, new Cat_int("NMIGS", "How many times this process" + " has migrated", + 6, &Procinfo::nmigs)); + allcats.set(F_NOMOVE, + new Cat_string("NOMOVE", "Reason for why process can't move", + &Procinfo::cantmove)); + allcats.set(F_RPID, new Cat_int("RPID", "Process ID on home node", 6, + &Procinfo::remotepid)); +#endif + + allcats.set(F_USER, + new Cat_user("USER", "Owner (*=suid root, +=suid other user" +#ifdef MOSIX + " -mosix-=immigrated from other node" +#endif + ")")); + allcats.set(F_GROUP, new Cat_group("GROUP", "Group name (*=sgid other)")); + allcats.set(F_UID, new Cat_int("UID", "Real user ID", 6, &Procinfo::uid)); + allcats.set(F_EUID, + new Cat_int("EUID", "Effective user ID", 6, &Procinfo::euid)); +#ifdef LINUX + allcats.set(F_SUID, new Cat_int("SUID", "Saved user ID (Posix)", 6, + &Procinfo::suid)); + allcats.set(F_FSUID, new Cat_int("FSUID", "File system user ID", 6, + &Procinfo::fsuid)); +#endif + allcats.set(F_GID, new Cat_int("GID", "Real group ID", 6, &Procinfo::gid)); + allcats.set(F_EGID, + new Cat_int("EGID", "Effective group ID", 6, &Procinfo::egid)); +#ifdef LINUX + allcats.set(F_SGID, new Cat_int("SGID", "Saved group ID (Posix)", 6, + &Procinfo::sgid)); + allcats.set(F_FSGID, new Cat_int("FSGID", "File system group ID", 6, + &Procinfo::fsgid)); +#endif + allcats.set(F_PRI, + new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority)); + allcats.set(F_NICE, + new Cat_int("NICE", + "Scheduling favour (higher -> less cpu time)", 4, + &Procinfo::nice)); + allcats.set(F_PLCY, new Cat_policy("PLCY", "Scheduling policy")); + allcats.set( + F_RPRI, + new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)")); +#ifdef SOLARIS + allcats.set(F_NLWP, new Cat_int("NLWP", "Number of threads in process", 5, + &Procinfo::nthreads)); + allcats.set(F_ARCH, new Cat_int("ARCH", "Architecture (address bits)", 2, + &Procinfo::addr_bits)); +#endif + allcats.set(F_MAJFLT, + new Cat_uintl("MAJFLT", "Number of major faults (disk access)", + 8, &Procinfo::majflt)); + allcats.set(F_MINFLT, + new Cat_uintl("MINFLT", + "Number of minor faults (no disk access)", 8, + &Procinfo::minflt)); +#ifdef LINUX + allcats.set(F_TRS, new Cat_uintl("TRS", "Text resident set size in Kbytes", + 8, &Procinfo::trs)); + allcats.set(F_DRS, new Cat_uintl("DRS", "Data resident set size in Kbytes", + 8, &Procinfo::drs)); +#endif + allcats.set(F_SIZE, + new Cat_uintl("SIZE", "Virtual image size of process in Kbytes", + 8, &Procinfo::size)); + allcats.set(F_SWAP, new Cat_swap("SWAP", "Kbytes on swap device")); + allcats.set(F_RSS, + new Cat_uintl("RSS", "Resident set size; Kbytes of program " + "in memory", + 8, &Procinfo::resident)); +#ifdef LINUX + allcats.set(F_SHARE, new Cat_uintl("SHARE", "Shared memory in Kbytes", 8, + &Procinfo::share)); + allcats.set(F_DT, new Cat_uintl("DT", "Number of dirty (non-written) pages", + 7, &Procinfo::dt)); +#endif + allcats.set(F_STAT, new Cat_state("STAT", "State of the process")); + allcats.set(F_FLAGS, new Cat_hex("FLAGS", "Process flags (hex)", 9, + &Procinfo::flags)); + allcats.set( + F_WCHAN, + new Cat_wchan("WCHAN", "Kernel function where process is sleeping")); + allcats.set(F_WCPU, + new Cat_percent("%WCPU", + "Weighted percentage of CPU (30 s average)", 6, + &Procinfo::wcpu)); + allcats.set(F_CPU, new Cat_percent( + "%CPU", "Percentage of CPU used since last update", + 6, &Procinfo::pcpu)); + allcats.set(F_MEM, new Cat_percent( + "%MEM", "Percentage of memory used (RSS/total mem)", + 6, &Procinfo::pmem)); + allcats.set(F_START, new Cat_start("START", "Time process started")); + allcats.set(F_TIME, + new Cat_time("TIME", "Total CPU time used since start")); + allcats.set(F_CPUNUM, new Cat_int("CPU", "CPU the process is executing on", + 3, &Procinfo::which_cpu)); +#ifdef OLD_LABEL + command = "COMM"; // label in linux source code --> /proc/XXX/stat +#else + command = "COMMAND"; +#endif + allcats.set(F_COMM, + new Cat_string(command, "Command that started the process", + &Procinfo::comm)); + allcats.set(F_CWD, new Cat_dir("CWD", "Current working directory", "cwd", + &Procinfo::cwd)); + allcats.set(F_ROOT, new Cat_dir("ROOT", "Root directory of process", "root", + &Procinfo::root)); +#ifdef OLD_LABEL + command_line = "CMDLINE"; // reference to /proc/XXX/cmdline +#else + command_line = "COMMAND_LINE"; +#endif + allcats.set( + F_CMDLINE, + new Cat_cmdline(command_line, "Command line that started the process")); + + for (int i = 0; i < allcats.size(); i++) + allcats[i]->index = i; + + Procinfo::init_static(); + + current_gen = 0; // ! +} + +void Proc::newproc(Procinfo *p) +{ + Procinfo *oldp = procs[p->pid]; + if (oldp) + { + // calculate pcpu (and wcpu for Linux) from previous procinfo + int dt = (p->tv.tv_usec - oldp->tv.tv_usec) / (1000000 / HZ) + + (p->tv.tv_sec - oldp->tv.tv_sec) * HZ; + int dcpu = p->utime - oldp->utime; + p->pcpu = 100.0 * dcpu / dt; + if (p->pcpu > 99.99) + p->pcpu = 99.99; + +#if defined(LINUX) + const float a = Procview::avg_factor; + p->wcpu = a * oldp->wcpu + (1 - a) * p->pcpu; +#endif + + // propagate some fields to new incarnation + p->selected = oldp->selected; + p->details = oldp->details; + p->hidekids = oldp->hidekids; + oldp->details = 0; + if (p->details) + p->details->set_procinfo(p); + +#ifdef LINUX + if (Procinfo::num_cpus > 1) + { + // SMP: see which processor was used the most + int best_cpu = -1; + unsigned long most = 0; + for (unsigned cpu = 0; cpu < Procinfo::num_cpus; cpu++) + { + unsigned long delta = + p->per_cpu_times[cpu] - oldp->per_cpu_times[cpu]; + if (delta > most) + { + most = delta; + best_cpu = cpu; + } + // if no cpu time has been spent, use previous + // value + p->which_cpu = (best_cpu >= 0) ? best_cpu : oldp->which_cpu; + } + } +#endif + oldp->deref(); + } + else + { +// New process +#ifdef LINUX + // %cpu first time = (cpu time since start) / (time since start) + int jiffies_since_boot = + p->tv.tv_usec / (1000000 / HZ) + (p->tv.tv_sec - p->boot_time) * HZ; + int dt = jiffies_since_boot - p->starttime; + int dcpu = p->utime; + p->pcpu = 100.0 * dcpu / dt; + if (dt == 0 || p->pcpu > 99.99 || p->pcpu < 0) + p->pcpu = 0.0; + p->selected = FALSE; +#endif + p->wcpu = p->pcpu; // just a start + +#ifdef LINUX + if (Procinfo::num_cpus > 1) + { + // first tick: count times from 0 + unsigned long most = 0; + for (unsigned cpu = 0; cpu < Procinfo::num_cpus; cpu++) + { + unsigned long t = p->per_cpu_times[cpu]; + if (t > most) + { + most = t; + p->which_cpu = cpu; + } + } + } +#endif + } + procs.replace(p->pid, p); + if (procs.count() > procs.size()) + procs.resize(procs.count() * 2 - 1); +} + +// update the process list +void Proc::refresh() +{ + current_gen = !current_gen; // 1,2,3,4.... + + Procinfo::read_common(); + + int pid; + + DIR *d = opendir(procdir); + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] >= '0' && e->d_name[0] <= '9') + { // good idea ! + + pid = atoi(e->d_name); + Procinfo *pi = new Procinfo(pid); + + if (pi->pid == -1) + delete pi; // already gone + else + { + pi->generation = current_gen; + newproc(pi); + } + } + } + closedir(d); + +#ifdef MOSIX + if (Procinfo::mosix_running) + { + char path[256]; + strcpy(path, procdir); + strcat(path, "/mosix/remote"); + d = opendir(path); + if (d) + { + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] >= '0' && e->d_name[0] <= '9') + { + Procinfo *pi = new Procinfo(atoi(e->d_name)); + if (pi->pid == -1) + delete pi; // already gone + else + { + pi->generation = current_gen; + newproc(pi); + } + } + } + closedir(d); + } + } +#endif // MOSIX + + // remove Procinfos of nonexisting processes + for (Q3IntDictIterator it(procs); it.current();) + { + Procinfo *p = it.current(); + if (p->generation != current_gen) + { + procs.remove(p->pid); + p->deref(); + } + else + ++it; + } +} + +Category *Proc::cat_by_name(const char *s) +{ + if (s) + { + for (int i = 0; i < allcats.size(); i++) + if (strcmp(allcats[i]->name, s) == 0) + return allcats[i]; + } + return 0; +} + +int Proc::field_id_by_name(const char *s) +{ + if (s) + { + for (int i = 0; i < allcats.size(); i++) + if (strcmp(allcats[i]->name, s) == 0) + return i; + } + return -1; +} + +int Procview::custom_fields[18] = {F_PID, F_TTY, F_USER, F_NICE, +#ifdef SOLARIS + F_NLWP, +#endif + F_SIZE, F_RSS, F_STAT, F_CPU, + F_START, F_TIME, F_CMDLINE, F_END}; + +int Procview::user_fields[] = {F_PID, F_TTY, F_USER, F_NICE, +#ifdef SOLARIS + F_NLWP, +#endif + F_SIZE, F_RSS, F_STAT, F_CPU, + F_START, F_TIME, F_CMDLINE, F_END}; + +#ifdef MOSIX +int Procview::user_fields_mosix[] = {F_PID, F_TTY, F_USER, F_NICE, F_MIGR, + F_NMIGS, F_SIZE, F_RSS, F_STAT, F_CPU, + F_START, F_TIME, F_CMDLINE, F_END}; +#endif + +int Procview::jobs_fields[] = {F_PID, F_PPID, F_PGID, F_SID, F_TTY, +#ifdef LINUX + F_TPGID, +#endif + F_STAT, F_UID, F_TIME, F_CMDLINE, F_END}; +#ifdef MOSIX +int Procview::jobs_fields_mosix[] = { + F_PID, F_PPID, F_PGID, F_SID, F_MIGR, F_NMIGS, F_LOCKED, F_NOMOVE, + F_TTY, F_TPGID, F_STAT, F_UID, F_TIME, F_CMDLINE, F_END}; +#endif + +int Procview::mem_fields[] = {F_PID, F_TTY, F_MAJFLT, F_MINFLT, +#ifdef LINUX + F_TRS, F_DRS, +#endif + F_SIZE, F_SWAP, F_RSS, +#ifdef LINUX + F_SHARE, F_DT, +#endif + F_CMDLINE, F_END}; +#ifdef MOSIX +int Procview::mem_fields_mosix[] = { + F_PID, F_MIGR, F_TTY, F_MAJFLT, F_MINFLT, F_TRS, F_DRS, + F_SIZE, F_SWAP, F_RSS, F_SHARE, F_DT, F_CMDLINE, F_END}; +#endif + +float Procview::avg_factor = 1.0; + +Procview::Procview(Proc *p) : proc(p) +{ + sortcat = p->allcats[F_WCPU]; + reversed = FALSE; + viewproc = ALL; + viewfields = USER; + treeview = TRUE; // init_mode by fasthyun + set_fields(); +} + +// read new process info +void Procview::refresh() +{ + for (int i = 0; i < old_procs.size(); i++) + old_procs[i]->deref(); + old_procs = procs; + procs.clear(); + proc->refresh(); // update the process list ,read "/proc" + rebuild(); +} + +bool Procview::accept_proc(Procinfo *p) +{ + static int my_uid = getuid(); + return viewproc == ALL || viewproc == OWNED && p->uid == my_uid || + viewproc == NROOT && p->uid != 0 || + viewproc == RUNNING && strchr("ORDW", p->state) != 0 +#ifdef MOSIX + || viewproc == RUNNING && p->where > 0 +#endif + ; +} + +// be called by Procview::rebuild() +void Procview::build_tree() +{ + if (root_procs.size() > 0) + { + Procinfo *p; + for (Q3IntDictIterator it(proc->procs); (p = it.current()); + ++it) + if (p->children) + p->children->clear(); + root_procs.clear(); + } + Procinfo *p; + for (Q3IntDictIterator it(proc->procs); (p = it.current()); ++it) + { + if (accept_proc(p)) + { + Procinfo *parent = 0; + + if (p->ppid && (parent = proc->procs[p->ppid]) && + accept_proc(parent)) + { + if (!parent->children) + parent->children = new Svec(4); + parent->children->add(p); + } + else + { + root_procs.add(p); + } + } + else + p->selected = FALSE; + } +} + +// re-sort the process info +void Procview::rebuild() +{ + for (int i = 0; i < procs.size(); i++) + procs[i]->deref(); // delete procs ? + procs.clear(); + if (treeview) + { + build_tree(); + parent_rows.clear(); + linearize_tree(&root_procs, 0, -1); + } + else + { + for (Q3IntDictIterator it(proc->procs); it.current(); ++it) + { + Procinfo *p = it.current(); + if (accept_proc(p)) + procs.add(p->ref()); + else + p->selected = FALSE; + } + static_sortcat = sortcat; + procs.sort(reversed ? compare_backwards : compare); + } +} + +// ???? +void Procview::linearize_tree(Svec *ps, int level, int prow) +{ + static_sortcat = sortcat; + ps->sort(reversed ? compare_backwards : compare); + for (int i = 0; i < ps->size(); i++) + { + Procinfo *p = (*ps)[i]; + p->level = level; + p->lastchild = FALSE; + procs.add(p->ref()); + parent_rows.add(prow); + if (p->children && !p->hidekids) + linearize_tree(p->children, level + 1, procs.size() - 1); + } + if (ps->size() > 0) + (*ps)[ps->size() - 1]->lastchild = TRUE; +} + +void Procview::set_fields_list(int fields[]) +{ + cats.clear(); + for (int i = 0; fields[i] != F_END; i++) + { + cats.add(proc->allcats[fields[i]]); + // printf("DEBUG: %d %s \n", i,proc->allcats[fields[i]]->name); + } +} + +void Procview::set_fields() +{ + switch (viewfields) + { + case USER: +#ifdef MOSIX + if (Procinfo::mosix_running) + { + set_fields_list(user_fields_mosix); + break; + } +#endif + set_fields_list(user_fields); + break; + case JOBS: +#ifdef MOSIX + if (Procinfo::mosix_running) + { + set_fields_list(jobs_fields_mosix); + break; + } +#endif + set_fields_list(jobs_fields); + break; + case MEM: +#ifdef MOSIX + if (Procinfo::mosix_running) + { + set_fields_list(mem_fields_mosix); + break; + } +#endif + set_fields_list(mem_fields); + break; + case CUSTOM: + set_fields_list(custom_fields); + break; + } +} + +// return the column number of a field, or -1 if not displayed +int Procview::findCol(int field) +{ + for (int i = 0; i < cats.size(); i++) + if (cats[i] == proc->allcats[field]) + return i; + return -1; +} + +// add a category (last) +void Procview::add_cat(Category *c) +// void Procview::add_category(Category *c) +{ + cats.add(c); +} + +void Procview::remove_cat(int index) { cats.remove(index); } + +// deduce whether the currently selected fields correspond to a field list +void Procview::deduce_fields() +{ + return; + + if (viewfields != CUSTOM) + return; + Procview::fieldstates tags[3] = {USER, JOBS, MEM}; + int *lists[3] = {user_fields, jobs_fields, mem_fields}; +#ifdef MOSIX + if (Procinfo::mosix_running) + { + lists[0] = user_fields_mosix; + lists[1] = jobs_fields_mosix; + lists[2] = mem_fields_mosix; + } +#endif + for (int i = 0; i < 3; i++) + { + int *l = lists[i]; + int j; + for (j = 0; l[j] != F_END; j++) + if (findCol(l[j]) < 0) + break; + if (l[j] == F_END && j == cats.size()) + { + viewfields = tags[i]; + return; + } + } +} + +Category *Procview::static_sortcat = 0; + +int Procview::compare(Procinfo *const *a, Procinfo *const *b) +{ + int r = static_sortcat->compare(*a, *b); + return (r == 0) ? ((*a)->pid > (*b)->pid ? 1 : -1) : r; +} + +int Procview::compare_backwards(Procinfo *const *a, Procinfo *const *b) +{ + int r = static_sortcat->compare(*b, *a); + return (r == 0) ? ((*b)->pid > (*a)->pid ? 1 : -1) : r; +} diff --git a/src/proc_solaris.cpp b/src/proc_solaris.cpp new file mode 100644 index 0000000..a06f963 --- /dev/null +++ b/src/proc_solaris.cpp @@ -0,0 +1,1328 @@ +// proc.cpp for Solaris (SunOS) +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 +// José Luis Sánchez, 2005 + +#include +#include +#include //HZ +#include +#include +#include +#include +#include +#include +#include + +#include "qps.h" +#include "proc.h" +#include "uidstr.h" +#include "ttystr.h" +#include "wchan.h" +#include "details.h" + +// Solaris... +#include +#include +#include +#include + +//#include +#define _STRUCTURED_PROC 1 +#include // psinfo_t , + /* + * + If you want an "#ifdef solaris", the portable way is + + #if defined (__SVR4) && defined (__sun) + + This should work on gcc, sun cc, and lots o other compilers, on both sparc and + intel. + If for some reason, you want to know that Sun forte CC (c++) compiler is being + used, something that seems to work is + + #if defined(__SUNPRO_CC) + + Whereas for forte cc (regular C), you can use + + #if defined(__SUNPRO_C) + + */ +#include "proc_common.cpp" + +char procdir[128] = "/proc"; + +int page_k_shift; // + +extern int flag_thread_ok; +extern bool flag_show_thread; +extern bool flag_devel; + +kstat_ctl_t *Proc::kc = 0; + +int proc_PID_fd(const int pid) +{ + char path[128]; + int numfd; + + sprintf(path, "/proc/%d/fd", pid); + + QDir qdir(path); + numfd = qdir.count(); + + /// num_opened_files += numfd; + // printf("PID %d: %d opened files\n", pid, numfd); + return TRUE; +} + +Procinfo::Procinfo(Proc *system_proc, int process_id, int thread_id) : refcnt(1) +{ + proc = system_proc; // + first_run = true; + clone = false; + + if (thread_id < 0) + { + pid = process_id; + tgid = process_id; + } + else + { + pid = thread_id; + tgid = process_id; + } + + detail = NULL; + /// children = 0; + + envblock = 0; + // if( readproc(proc_pid) < 0 ) pid = -1; // + // invalidate object, + // will be deleted + + ppid = 0; // no parent important!! + selected = false; + hidekids = false; + envblock = 0; //!! + + table_child_seq = -1; + child_seq_prev = -1; + + lastchild = 0; + generation = -1; + detail = 0; + + size = 0; + resident = 0; + // trs=0; + // drs=0; + // stack=0; + // share=0; + mem = 0; + + io_read_prev = 0; + io_write_prev = 0; + + // tgid=0; + pcpu = 0; + pmem = 0; + + command = "noname"; + tty = 0; + nice = 0; + starttime = 0; + state = 'Z'; + cutime = utime = 0; + + old_utime = 0; +} + +// COMMON? +Procinfo::~Procinfo() +{ + if (detail) + { + detail->process_gone(); + detail = 0; + } + + /* + if( envblock ) + free(envblock); + if( maps ) + { + // maps->purge(); + delete maps; + } + if( fd_files ) + { + // fd_files->purge(); + delete fd_files; + } */ + /// delete children; +} + +// miscellaneous static initializations +void Proc::init_static() +{ + if (!kc) + { + kc = kstat_open(); + if (!kc) + { + perror("kstat_open"); + exit(1); + } + } + + int pagesize = sysconf(_SC_PAGESIZE); // same getpagesize() in + printf("pagesize=%d, %d\n ", getpagesize(), sysconf(_SC_PAGESIZE)); // 4027 + + page_k_shift = 0; + for (int j = getpagesize(); j > 1024; j >>= 1) + page_k_shift++; +} + +// return number of bytes read if ok, -1 if failed +int read_file(char *name, void *buf, int max) +{ + int fd = open(name, O_RDONLY); + if (fd < 0) + return -1; + int r = read(fd, buf, max); + close(fd); + return r; +} + +// SOLARIS +// Description : read /proc/1234/task/* tasks(thread,LWP) +// add to Proc::procs[] +int Proc::read_pid_tasks(int pid) +{ + char path[256]; + char *p; + struct dirent *e; + int thread_pid; + int thread_n = 0; + + // printf("DEBUG:pid=%d :",pid); + sprintf(path, "%s/%d/lwp", procdir, pid); + + DIR *d = opendir(path); + if (!d) + return -1; + + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip "." , ".." + + Procinfo *pi; + thread_pid = atoi(e->d_name); // only number !! + + if (pid == thread_pid) + continue; // skip + + /* + Procinfo *pi = new Procinfo(this,pid, thread_id); + + if(pi->pid == -1) + delete pi; // already gone + + else { + pi->generation = current_gen; + pi->cmdline="(thread)"; + // newproc(pi); + } + */ + pi = procs.value(thread_pid, NULL); + + if (pi == NULL) + { + pi = new Procinfo(this, pid, thread_pid); + procs.insert(thread_pid, pi); + } + if (pi->readproc() >= 0) + { + pi->generation = current_gen; + // if(pid!=thread_pid) + // pi->cmdline="(thread)"; + } + + thread_n++; + } + /// printf("\n"); + closedir(d); + return thread_n; +} + +// DEL ( because of 2byte language) +static inline bool isprintable(unsigned char c) +{ + // assume, somewhat na.vely, that all latin-1 characters are printable + return (c >= 0x20 && c < 0x7f) || c >= 0xa0; +} + +// DEL replace unprintables by spaces +static void make_printable(char *s) +{ + while (*s) + { + if (!isprintable(*s)) + *s = ' '; + ++s; + } +} + +// what this fuck..??? move to readproc +/* void Proc::newproc(Procinfo *p) +{ + Procinfo *oldp = procs[p->pid]; + + //if(oldp && flag_thread_ok && (previous_flag_show_thread != +flag_show_thread) ){ + // oldp->deref(); + // oldp =NULL; + // } + { + // New process + p->wcpu = p->pcpu; // just a start + } +} */ + +// read proc/PID Solaris 10, not tested in Solaris 9 +int Procinfo::readproc() +{ + char path[256]; + + // Procinfo:pid; + proc_PID_fd(pid); // check!! + sprintf(path, "%s/%d/psinfo", procdir, pid); + + psinfo_t psi; + if (read_file(path, (void *)&psi, sizeof(psi)) < (int)sizeof(psi)) + return -1; + + sprintf(path, "%s/%d/usage", procdir, pid); + prusage_t pru; + if (read_file(path, (void *)&pru, sizeof(pru)) < (int)sizeof(pru)) + return -1; + + if (first_run) + { + + first_run = false; + } + minflt = pru.pr_minf; + majflt = pru.pr_majf; + + uid = psi.pr_uid; // ok + euid = psi.pr_euid; // ok + gid = psi.pr_gid; // ok + egid = psi.pr_egid; // ok + + // make_printable(psi.pr_psargs); + cmdline = psi.pr_psargs; // ok + command = psi.pr_fname; + + ppid = psi.pr_ppid; // ok + pgrp = psi.pr_pgid; // ok + session = psi.pr_sid; // ok + tty = psi.pr_ttydev; // ok type? + + nthreads = psi.pr_nlwp; // num of threads + + //------- no checked + const int ns_ticks = 1000000000 / HZ; // HZ=100, clk_tick + // printf("ns_ticks=%d\n",ns_ticks);// 10000000 + // starttime = (psi.pr_start.tv_sec) * HZ + + // psi.pr_start.tv_nsec / + // ns_ticks; + starttime = psi.pr_start.tv_sec; // discard tv_nsec + env_ofs = psi.pr_envp; + addr_bits = psi.pr_dmodel == PR_MODEL_ILP32 ? 32 : 64; + + gettimeofday(&tv, 0); // current_time, sys/time , tv.tv_sec, tv.tv_usec + + // god dam!!!! if that dont reel in¿ the ladies then nothing will!!!! + + // No + state = psi.pr_lwp.pr_sname; // no + command = (state == 'Z') ? "" : psi.pr_fname; + flags = psi.pr_flag; // no + + // No + utime = psi.pr_time.tv_sec * HZ + psi.pr_time.tv_nsec / ns_ticks; //?? + cutime = psi.pr_ctime.tv_sec * HZ + psi.pr_ctime.tv_nsec / ns_ticks; + + // printf("[%d] utime=%d\n",pid,utime); + + int dcpu; + if (old_utime == 0 and utime == 0) + { + pcpu = 0; + } + else + { + dcpu = utime - old_utime; + + // calculate pcpu (and wcpu for Linux) from previous procinfo + int dt = (tv.tv_usec - old_tv.tv_usec) / (1000000 / HZ) + + (tv.tv_sec - old_tv.tv_sec) * HZ; + + if (dt == 0) + pcpu = 0; + else + pcpu = 100.0 * dcpu / dt; + + // if( pcpu > 99.99 ) pcpu = 99.99; + + old_utime = utime; + old_tv = tv; + } + + priority = psi.pr_lwp.pr_pri; + nice = psi.pr_lwp.pr_nice; + if (Qps::normalize_nice) //??? + nice -= NZERO; + + wchan = psi.pr_lwp.pr_wchan; + + // MEM + size = psi.pr_size; + resident = psi.pr_rssize; + which_cpu = psi.pr_lwp.pr_onpro; + + // pr_pctcpu and pr_pctmem are scaled so that 1.0 is stored as 0x8000. + // We rescale pcpu so a CPU-bound process is shown as 100%. (This means + // that wcpu may exceed 100% with several LWPs.) + wcpu = psi.pr_pctcpu * (1 / 327.68) * proc->num_cpus; + pmem = psi.pr_pctmem * (1 / 327.68); + + rtprio = -1; // ditto + policy_name[0] = psi.pr_lwp.pr_clname[0]; + policy_name[1] = psi.pr_lwp.pr_clname[1]; + + if (dcpu == 0) + return 1; + return 2; // this process consumed jiffi of cpu +} + +// thread Solaris 10 +int Procinfo::readproc(int proc_pid, int thread) +{ + char ppath[256]; + char upath[256]; + + pid = proc_pid; + + proc_PID_fd(pid); + + if (!flag_thread_ok || !flag_show_thread) + { + return readproc(); + } + // get the process stuff + // readproc(); + + pid = thread; //***** + + sprintf(ppath, "%s/%d/lwp/%d/lwpsinfo", procdir, proc_pid, thread); + sprintf(upath, "%s/%d/lwp/%d/lwpusage", procdir, proc_pid, thread); + + lwpsinfo_t psi; + if (read_file(ppath, (void *)&psi, sizeof(psi)) < (int)sizeof(psi)) + { + return -1; + } + + prusage_t pru; + if (read_file(upath, (void *)&pru, sizeof(pru)) < (int)sizeof(pru)) + { + return -1; + } + + state = psi.pr_sname; + flags = psi.pr_flag; + const int ns_ticks = 1000000000 / HZ; + utime = psi.pr_time.tv_sec * HZ + psi.pr_time.tv_nsec / ns_ticks; + priority = psi.pr_pri; + nice = psi.pr_nice; + if (Qps::normalize_nice) + nice -= NZERO; + starttime = (psi.pr_start.tv_sec - proc->boot_time) * HZ + + psi.pr_start.tv_nsec / ns_ticks; + wchan = psi.pr_wchan; + + minflt = pru.pr_minf; + majflt = pru.pr_majf; + which_cpu = psi.pr_onpro; + + // pr_pctcpu and pr_pctmem are scaled so that 1.0 is stored as 0x8000. + // We rescale pcpu so a CPU-bound process is shown as 100%. (This means + // that wcpu may exceed 100% with several LWPs.) + if (psi.pr_pctcpu) + { + wcpu = (psi.pr_pctcpu / 327.68) * proc->num_cpus; + } + else + { + wcpu = 0.0; + } + gettimeofday(&tv, 0); // + rtprio = -1; // ditto + policy_name[0] = psi.pr_clname[0]; + policy_name[1] = psi.pr_clname[1]; + /// num_process++; + return pid; + // thread Solaris10 +} + +static float getscaled(kstat_t *ks, const char *name) +{ + // load avgs are scaled by 256 + kstat_named_t *kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)name); + return kn ? kn->value.ui32 * (1 / 256.0) : 0.0; +} + +void Proc::read_loadavg() +{ + kstat_chain_update(kc); + + kstat_t *ks = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); + if (!ks || kstat_read(kc, ks, 0) == -1) + { + perror("kstat_lookup/read"); + exit(1); + } + + loadavg[0] = getscaled(ks, "avenrun_1min"); + loadavg[1] = getscaled(ks, "avenrun_5min"); + loadavg[2] = getscaled(ks, "avenrun_15min"); + + // we might as well get the boot time too since it's in the same kstat + // (not that it is going to change) + kstat_named_t *kn; + kn = (kstat_named_t *)kstat_data_lookup(ks, (char *)"boot_time"); + if (kn) + boot_time = kn->value.ui32; +} + +// read /proc +// called by Proc::fresh(); +int Proc::read_system() +{ + static bool first_run = true; + if (first_run) + { + flag_thread_ok = true; + + // memory info: this is easy - just use sysconf + mem_total = sysconf(_SC_PHYS_PAGES) << page_k_shift; + mem_free = sysconf(_SC_AVPHYS_PAGES) << page_k_shift; + + // Max SMP 1024 cpus . COMMON + int max_cpus = 1024; + // cpu_times_vec = (unsigned *)malloc(sizeof(unsigned) * + // num_cpus * + // CPUTIMES); + cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; + old_cpu_times_vec = new unsigned[CPUTIMES * max_cpus]; + + // init + for (int cpu = 0; cpu < max_cpus; cpu++) + for (int i = 0; i < CPUTIMES; i++) + { + cpu_times(cpu, i) = 0; + old_cpu_times(cpu, i) = 0; + } + + /* + if(cpu_times_vec) + { + if(old_cpu_times_vec) free(old_cpu_times_vec); + old_cpu_times_vec = cpu_times_vec; + cpu_times_vec = (unsigned + *)malloc(sizeof(unsigned) * + num_cpus * CPUTIMES); + } + old_num_cpus = num_cpus; + */ + first_run = 0; + } + + // get swap info: somewhat trickier - we have to count all swap spaces + int nswaps = swapctl(SC_GETNSWP, 0); + swaptbl_t *st = + (swaptbl_t *)malloc(sizeof(int) + nswaps * sizeof(swapent_t)); + st->swt_n = nswaps; + // We are not interested in the paths, just the values, so we allocate + // one scratch buffer for all paths to keep swapctl happy. + char path_buf[PATH_MAX + 1]; + + for (int i = 0; i < nswaps; i++) + st->swt_ent[i].ste_path = path_buf; + swapctl(SC_LIST, st); + + // count the swap spaces + swap_total = swap_free = 0; + for (int i = 0; i < nswaps; i++) + { + swap_total += st->swt_ent[i].ste_pages; + swap_free += st->swt_ent[i].ste_free; + } + swap_total <<= page_k_shift; + swap_free <<= page_k_shift; + free(st); + + // cpu states: are stored as kstats named "cpu_statN", where N is the + // cpu number. Unfortunately, the cpu numbers are not guessable so we + // sweep the kstat chain for all of them, assuming (foolishly?) + // that they are in order. + + // kstat_chain_update(kc); + int cpu = 0; + + for (kstat_t *ks = kc->kc_chain; ks; ks = ks->ks_next) + { + // printf("kstat name :%s\n",ks->ks_name); + + if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) + { + if (kstat_read(kc, ks, NULL) == -1) + { + perror("kstat_read"); + exit(1); + } + cpu_stat_t *cs = (cpu_stat_t *)ks->ks_data; + cpu_times(cpu, CPUTIME_USER) = cs->cpu_sysinfo.cpu[CPU_USER]; + cpu_times(cpu, CPUTIME_SYSTEM) = cs->cpu_sysinfo.cpu[CPU_KERNEL]; + cpu_times(cpu, CPUTIME_WAIT) = cs->cpu_sysinfo.cpu[CPU_WAIT]; + cpu_times(cpu, CPUTIME_IDLE) = cs->cpu_sysinfo.cpu[CPU_IDLE]; + + cpu++; + } + } + Proc::num_cpus = cpu; + + // exit(1); + dt_total = 0; + dt_used = 0; + + // dt_used= user + system; + // dt_total= user + system + nice + idle + // dt_used+=cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu, + // CPUTIME_SYSTEM); + // dt_total=dt_used + + cpu_times(num_cpus, CPUTIME_USER) = 0; + cpu_times(num_cpus, CPUTIME_SYSTEM) = 0; + cpu_times(num_cpus, CPUTIME_WAIT) = 0; + cpu_times(num_cpus, CPUTIME_IDLE) = 0; + for (int cpu = 0; cpu < num_cpus; cpu++) + { + cpu_times(num_cpus, CPUTIME_USER) += cpu_times(cpu, CPUTIME_USER); + cpu_times(num_cpus, CPUTIME_SYSTEM) += cpu_times(cpu, CPUTIME_SYSTEM); + cpu_times(num_cpus, CPUTIME_WAIT) += cpu_times(cpu, CPUTIME_WAIT); + cpu_times(num_cpus, CPUTIME_IDLE) += cpu_times(cpu, CPUTIME_IDLE); + } + + cpu = num_cpus; + dt_used += + cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu, CPUTIME_SYSTEM); // Kernel + dt_total += cpu_times(cpu, CPUTIME_USER) + cpu_times(cpu, CPUTIME_SYSTEM) + + cpu_times(cpu, CPUTIME_WAIT) + cpu_times(cpu, CPUTIME_IDLE); + + load_cpu = (float)Proc::dt_used / Proc::dt_total; + + // Hotplugging Detection : save total_cpu + if (Proc::num_cpus != Proc::old_num_cpus) + { + // for(int i = 0; i < CPUTIMES; i++) + // cpu_times(num_cpus, i) = + // cpu_times(Proc::old_num_cpus, i); + // Proc::old_num_cpus=Proc::num_cpus; + } +} + +int Procinfo::get_policy() +{ + if (policy == -1) + policy = sched_getscheduler(pid); + return policy; +} + +int Procinfo::get_rtprio() +{ + if (rtprio == -1) + { + struct sched_param p; + if (sched_getparam(pid, &p) == 0) + rtprio = p.sched_priority; + } + return rtprio; +} + +// called by bool Procinfo::read_fds() +void Procinfo::read_fd(int fdnum, char *path) +{ + struct stat sb; + + if (lstat(path, &sb) < 0) + { + // The file has been closed, or we could really not stat it + // despite + // having it open (could be a fd passed from another process). + fd_files.append(new Fileinfo(fdnum, "(no info available)")); + return; + } + // We could in principle find out more about the fd, such as its mode + // (RDONLY, RDWR etc) and flags, but it's messy. pfiles uses an agent + // lwp + // for this, but I don't know how to do it. + QString s; + const char *n; + switch (sb.st_mode & S_IFMT) + { + case S_IFCHR: + // if it is a tty, we might know its real name + if (sb.st_rdev != (dev_t)-1) + { + QString t = Ttystr::name(sb.st_rdev); + if (t[0] != '?') + { + s = "/dev/"; + s.append(t); + break; + } + } + s.sprintf("char device %u:%u", (unsigned)major(sb.st_rdev), + (unsigned)minor(sb.st_rdev)); + break; + + case S_IFBLK: + s.sprintf("block device %u:%u", (unsigned)major(sb.st_rdev), + (unsigned)minor(sb.st_rdev)); + break; + + case S_IFLNK: + // Directories appear as symlinks in /proc/#/fd; we chdir() to + // it + // and see where we end up. Not efficient though. + // Besides, we change cwd a lot in unpredictable ways. This + // makes + // core dumps hard to find, if they are generated at all. + s = "directory "; + if (chdir(path) >= 0) + { + char buf[512]; + if (getcwd(buf, sizeof(buf)) >= 0) + { + s.append(buf); + break; + } + } + s.append("(unknown)"); + break; + + default: + switch (sb.st_mode & S_IFMT) + { + case S_IFIFO: // fifo or anonymous pipe + n = "pipe"; + break; + case S_IFDIR: // this shouldn't happen + n = "directory"; + break; + case S_IFREG: + n = "file"; + break; + case S_IFSOCK: + n = "unix domain socket"; + break; + case S_IFDOOR: + n = "door"; + break; + default: + n = "unknown"; + break; + } + s.sprintf("%s, dev %u:%u inode %u", n, (unsigned)major(sb.st_dev), + (unsigned)minor(sb.st_dev), (unsigned)sb.st_ino); + break; + } + fd_files.append(new Fileinfo(fdnum, s)); +} + +// return TRUE if /proc/PID/fd could be read, FALSE otherwise +// store fileinfo, and also socket inodes separately +bool Procinfo::read_fds() +{ + char path[80], *p; + + sprintf(path, "%s/%d/fd", procdir, pid); + + DIR *d = opendir(path); + if (!d) + return FALSE; + + p = path + strlen(path) + 1; + p[-1] = '/'; + + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] == '.') + continue; // skip . and .. + strcpy(p, e->d_name); + int fdnum = atoi(p); + read_fd(fdnum, path); + } + closedir(d); + return TRUE; +} + +// return TRUE if the process environment could be read, FALSE otherwise +bool Procinfo::read_environ() +{ + int fd; + char file[128]; + + return 0; + + sprintf(file, "/proc/%d/as", pid); + if ((fd = open(file, O_RDONLY)) < 0) + return FALSE; + + // Just read the first 8K from the environment. Adaptive code here is + // possible, but not really worth the effort. + int bs = 8192; + if (envblock) + free(envblock); + envblock = (char *)malloc(bs); + if (pread(fd, envblock, bs, env_ofs) < 0) + { + free(envblock); + envblock = 0; + return FALSE; + } + close(fd); + envblock[bs - 1] = '\0'; + + /// environ.purge(); + + for (int i = 0; i * (int)sizeof(char *) < bs && ((char **)envblock)[i]; i++) + { + int b = ((char **)envblock)[i] - (char *)env_ofs; + if (b < 0 || b >= bs) + continue; // outside retrieved memory block + char *val = strchr(envblock + b, '='); + if (val) + *val++ = '\0'; + else + val = (char *)""; // degenerate: treat as name with + // empty value + make_printable(envblock + b); + make_printable(val); + environ.append(NameValue(envblock + b, val)); // ????? + } + return TRUE; +} + +// Try using /proc/bin/pmap to add file names to the memory map +void Procinfo::read_pmap_maps() +{ + char buf[256]; + + sprintf(buf, "/usr/proc/bin/pmap %d 2>/dev/null", pid); + + FILE *pmap = popen(buf, "r"); + if (!pmap) + return; + + // skip first line + if (!fgets(buf, sizeof buf, pmap)) + { + pclose(pmap); + return; + } + + int map_num = 0; + while (fgets(buf, sizeof buf, pmap)) + { + // Each output line from pmap looks like + //
K + // We use
and only to match with previously + // read + // map info, and only use here. + + unsigned long addr; + unsigned len; + int next; + char *p; + + if (sscanf(buf, "%lx %dK%n", &addr, &len, &next) != 2) + continue; + + // Correlate this with info already gathered. Assume they are in + // the + // same order (ascending by address). + Mapsinfo *mi; + int i = map_num; + while (i < maps.size() && (mi = maps[i])->from != addr) + i++; + // Mismatches can happen since changes can have taken place + // since + // we read the maps. If so, skip this mapping and try the next. + if (mi->to != addr + ((unsigned long)len << 10) || i == maps.size()) + continue; + map_num = i + 1; + + while (buf[next] == ' ') + next++; + while (buf[next] && buf[next] != ' ') + next++; + while (buf[next] == ' ') + next++; + // At this point we are looking at a file name, or at a + // suitable + // designator like [ heap ], [ anon ] or [ stack ]. Use it right + // away + // (after peeling off the newline) + int l = strlen(buf + next); + if (buf[next + l - 1] == '\n') + buf[next + l - 1] = '\0'; + mi->filename = buf + next; + } + + pclose(pmap); + return; +} + +// return TRUE if /proc/XX/map could be read, FALSE otherwise +bool Procinfo::read_maps() +{ + char name[128]; + sprintf(name, "%s/%d/map", procdir, pid); + + FILE *f = fopen(name, "r"); + if (!f) + return FALSE; + + prmap_t pm; + while (fread(&pm, sizeof(pm), 1, f) == 1) + { + Mapsinfo *mi = new Mapsinfo; + mi->from = pm.pr_vaddr; + mi->to = pm.pr_vaddr + pm.pr_size; + mi->offset = pm.pr_offset; + mi->perm[0] = pm.pr_mflags & MA_READ ? 'r' : '-'; + mi->perm[1] = pm.pr_mflags & MA_WRITE ? 'w' : '-'; + mi->perm[2] = pm.pr_mflags & MA_EXEC ? 'x' : '-'; + mi->perm[3] = pm.pr_mflags & MA_SHARED ? 's' : 'p'; + + if (pm.pr_mapname[0]) + { + // To find device/inode, stat the file in + // /proc/#/object: + char obj[128]; + sprintf(obj, "%s/%d/object/%s", procdir, pid, pm.pr_mapname); + struct stat sb; + if (lstat(obj, &sb) < 0) + { + delete mi; + continue; + } + mi->major = major(sb.st_dev); + mi->minor = minor(sb.st_dev); + mi->inode = sb.st_ino; + if (strcmp(pm.pr_mapname, "a.out") == 0) + mi->filename = "(executable)"; + } + else + { + mi->major = mi->minor = mi->inode = 0; + mi->filename = "(anonymous)"; + } + + maps.append(mi); + } + fclose(f); + + // If desired and possible, use /usr/proc/bin/pmap to get the + // names of the mapped files + static int myeuid = geteuid(); + if (Qps::use_pmap && (myeuid == 0 || myeuid == euid)) + read_pmap_maps(); + + return TRUE; +} + +Cat_dir::Cat_dir(const char *heading, const char *explain, const char *dirname, + QString Procinfo::*member) + : Cat_string(heading, explain), dir(dirname), cache(member) +{ +} + +QString Cat_dir::string(Procinfo *p) +{ + if ((p->*cache).isNull()) + { + char path[128], buf[512]; + sprintf(path, "/proc/%d/%s", p->pid, dir); + + // Either a Linux 2.0 link in [device]:inode form, or a Solaris + // link. + // To resolve it, we just chdir() to it and see where we end up. + // Perhaps we should change back later? + if (chdir(path) < 0) + { + p->*cache = "-"; // Most likely access denied + } + else + { + // getcwd() is fairly expensive, but this is cached + // anyway + if (!getcwd(buf, sizeof(buf))) + { + p->*cache = "(deleted)"; + } + else + p->*cache = buf; + } + } + return p->*cache; +} + +Cat_state::Cat_state(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_state::string(Procinfo *p) +{ + QString s(" "); + s[0] = p->state; + if (p->state == 'Z') + return s; + s[1] = (p->resident == 0 && p->state != 'Z') ? 'W' : ' '; + int ni = p->nice; + if (!Qps::normalize_nice) + ni -= NZERO; + s[2] = (ni > 0) ? 'N' : ((ni < 0) ? '<' : ' '); + return s; +} + +Cat_policy::Cat_policy(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_policy::string(Procinfo *p) +{ + QString s; + s = " "; + s[0] = p->policy_name[0]; + s[1] = p->policy_name[1]; + return s; +} + +int Cat_policy::compare(Procinfo *a, Procinfo *b) +{ + int r = b->policy_name[0] - a->policy_name[0]; + return r ? r : b->policy_name[1] - a->policy_name[1]; +} + +Cat_rtprio::Cat_rtprio(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_rtprio::string(Procinfo *p) +{ + QString s; + s.setNum(p->get_rtprio()); + return s; +} + +int Cat_rtprio::compare(Procinfo *a, Procinfo *b) +{ + return b->get_rtprio() - a->get_rtprio(); +} + +Cat_time::Cat_time(const char *heading, const char *explain) + : Category(heading, explain) +{ +} + +QString Cat_time::string(Procinfo *p) +{ + QString s; + int ticks = p->utime; + if (Procview::flag_cumulative) + ticks += p->cutime; + int t = ticks / HZ; // seconds + if (t < 10) + { + int hundreds = ticks / (HZ / 100) % 100; + s.sprintf("%1d.%02ds", t, hundreds); + } + else if (t < 100 * 60) + { + s.sprintf("%2d:%02d", t / 60, t % 60); + } + else if (t < 100 * 3600) + { + int h = t / 3600; + t %= 3600; + s.sprintf("%2d:%02dh", h, t / 60); + } + else + { + int d = t / 86400; + t %= 86400; + s.sprintf("%dd%dh", d, t / 3600); + } + return s; +} + +int Cat_time::compare(Procinfo *a, Procinfo *b) +{ + int at = a->utime, bt = b->utime; + if (Procview::flag_cumulative) + { + at += a->cutime; + bt += b->cutime; + } + return bt - at; +} + +Cat_tty::Cat_tty(const char *heading, const char *explain) + : Cat_string(heading, explain) +{ +} + +QString Cat_tty::string(Procinfo *p) { return Ttystr::name(p->tty); } + +Proc::Proc() +{ + + categories.insert(F_PID, + new Cat_int("PID", "Process ID", 6, &Procinfo::pid)); + categories.insert( + F_PPID, new Cat_int("PPID", "Parent process ID", 6, &Procinfo::ppid)); + categories.insert( + F_PGID, new Cat_int("PGID", "Process group ID", 6, &Procinfo::pgrp)); + categories.insert(F_SID, + new Cat_int("SID", "Session ID", 6, &Procinfo::session)); + categories.insert(F_TTY, new Cat_tty("TTY", "Controlling tty")); + categories.insert( + F_USER, new Cat_user("USER", "Owner (*=suid root, +=suid other user)")); + categories.insert(F_GROUP, + new Cat_group("GROUP", "Group name (*=sgid other)")); + categories.insert(F_UID, + new Cat_int("UID", "Real user ID", 6, &Procinfo::uid)); + categories.insert( + F_EUID, new Cat_int("EUID", "Effective user ID", 6, &Procinfo::euid)); + categories.insert(F_GID, + new Cat_int("GID", "Real group ID", 6, &Procinfo::gid)); + categories.insert( + F_EGID, new Cat_int("EGID", "Effective group ID", 6, &Procinfo::egid)); + categories.insert( + F_PRI, new Cat_int("PRI", "Dynamic priority", 4, &Procinfo::priority)); + categories.insert(F_NICE, + new Cat_int("NICE", + "Scheduling favour (higher -> less cpu time)", + 4, &Procinfo::nice)); + categories.insert(F_PLCY, new Cat_policy("PLCY", "Scheduling policy")); + categories.insert( + F_RPRI, + new Cat_rtprio("RPRI", "Realtime priority (0-99, more is better)")); + categories.insert(F_NLWP, + new Cat_int("NLWP", "Number of threads in process", 5, + &Procinfo::nthreads)); + categories.insert(F_ARCH, new Cat_int("ARCH", "Architecture (address bits)", + 2, &Procinfo::addr_bits)); + categories.insert(F_MAJFLT, + new Cat_uintl("MAJFLT", + "Number of major faults (disk access)", 8, + &Procinfo::majflt)); + categories.insert(F_MINFLT, + new Cat_uintl("MINFLT", + "Number of minor faults (no disk access)", + 8, &Procinfo::minflt)); + + // Memory + categories.insert(F_SIZE, + new Cat_memory("SIZE", + "Virtual image size of process in Kbytes", + 8, &Procinfo::size)); + categories.insert(F_SWAP, new Cat_swap("SWAP", "Kbytes on swap device")); + categories.insert( + F_RSS, + new Cat_memory("RSS", "Resident set size; Kbytes of program in memory", + 8, &Procinfo::resident)); + + categories.insert(F_STAT, new Cat_state("STAT", "State of the process")); + categories.insert(F_FLAGS, new Cat_hex("FLAGS", "Process flags (hex)", 9, + &Procinfo::flags)); + categories.insert( + F_WCHAN, + new Cat_wchan("WCHAN", "Kernel function where process is sleeping")); + + categories.insert( + F_WCPU, + new Cat_percent("%WCPU", "Weighted percentage of CPU (30 s average)", 6, + &Procinfo::wcpu)); + categories.insert( + F_CPU, + new Cat_percent(" %CPU", "Percentage of CPU used since last update", 6, + &Procinfo::pcpu)); + categories.insert( + F_MEM, + new Cat_percent(" %MEM", "Percentage of memory used (RSS/total mem)", 6, + &Procinfo::pmem)); + categories.insert(F_START, new Cat_start("START", "Time process started")); + categories.insert(F_TIME, + new Cat_time("TIME", "Total CPU time used since start")); + categories.insert(F_CPUNUM, + new Cat_int("CPU", "CPU the process is executing on", 3, + &Procinfo::which_cpu)); + categories.insert(F_CMD, new Cat_string("COMMAND", + "Command that started the process", + &Procinfo::command)); + categories.insert(F_CWD, new Cat_dir("CWD", "Current working directory", + "cwd", &Procinfo::cwd)); + categories.insert(F_ROOT, new Cat_dir("ROOT", "Root directory of process", + "root", &Procinfo::root)); + categories.insert(F_CMDLINE, + new Cat_cmdline("COMMAND_LINE", + "Command line that started the process")); + + commonPostInit(); + + Proc::init_static(); +} + +// update the process list +// called by void Proc::refresh() +void Proc::read_proc_all() +{ + + DIR *d = opendir(procdir); + struct dirent *e; + while ((e = readdir(d)) != 0) + { + if (e->d_name[0] >= '0' && e->d_name[0] <= '9') // good idea! + { + + /* if(flag_thread_ok && flag_show_thread) + read_pid_tasks(pid); + + */ + + Procinfo *pi; + int pid; + + pid = atoi(e->d_name); + + pi = procs.value(pid, NULL); // if not found pid then, return Null + + if (pi == NULL) // new process + { + pi = new Procinfo(this, pid); + procs.insert(pid, pi); + } + int ret = pi->readproc(); + if (ret > 0) + { + pi->generation = current_gen; // this process is alive + + // if(flag_show_thread and flag_thread_ok ) + // read_pid_tasks(pid); //for threads + + // add to History /// COMMONZ + if (ret == 2) + { + Procinfo *p = new Procinfo(*pi); // copy + p->clone = true; + hprocs->insert(pid, p); + } + } + else + { + // already gone. /proc/PID dead! + // later remove this process ! not yet + } + } + } + closedir(d); +} + +int Procview::custom_fields[] = {F_PID, F_TTY, F_USER, F_NICE, F_NLWP, + F_SIZE, F_RSS, F_STAT, F_CPU, F_START, + F_TIME, F_CMDLINE, F_END}; + +int Procview::basic_fields[] = {F_PID, F_TTY, F_USER, F_NICE, F_NLWP, + F_SIZE, F_RSS, F_STAT, F_CPU, F_START, + F_TIME, F_CMDLINE, F_END}; +int Procview::jobs_fields[] = {F_PID, F_PPID, F_PGID, F_SID, F_TTY, + F_STAT, F_UID, F_TIME, F_CMDLINE, F_END}; + +int Procview::mem_fields[] = {F_PID, F_TTY, F_MAJFLT, F_MINFLT, F_SIZE, + F_SWAP, F_RSS, F_CMDLINE, F_END}; + +void Procview::set_fields() +{ + switch (viewfields) + { + case USER: + set_fields_list(basic_fields); + break; + case JOBS: + set_fields_list(jobs_fields); + break; + case MEM: + set_fields_list(mem_fields); + break; + case CUSTOM: + set_fields_list(custom_fields); + break; + } +} + +// SOLARIS: +// deduce whether the currently selected fields correspond to a field list +void Procview::deduce_fields() +{ + return; + /* + if( viewfields != CUSTOM ) + return; + + Procview::fieldstates tags[3] = {USER, JOBS, MEM}; + int *lists[3] = {user_fields, jobs_fields, mem_fields}; + for( int i = 0; i < 3; i++ ) + { + int *l = lists[i]; + int j; + for( j = 0; l[j] != F_END; j++ ) + if( findCol(l[j]) < 0 ) + break; + if( l[j] == F_END && j == cats.size() ) + { + viewfields = tags[i]; + return; + } + } */ +} + +void check_system_requirement() {} + +bool Procinfo::isThread() +{ + return pid != tgid; // not a thread ! +} + +Proc::~Proc() {} diff --git a/src/pstable.cpp b/src/pstable.cpp new file mode 100644 index 0000000..55a6d60 --- /dev/null +++ b/src/pstable.cpp @@ -0,0 +1,586 @@ +// pstable.cpp +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 + +// ** toooo complex,so need new Design ... should be more simple.. (by +// fasthyun@magicn.com) + +#include "proc.h" +#include "pstable.h" +#include "misc.h" + +///#include +#include +#include +#include + +// When a subtree is folded away, selections inside it disappear to prevent +// unexpected behaviour +static void clear_subtree_selections(Procinfo *p) +{ + for (int i = 0; i < p->table_children.size(); i++) + { + Procinfo *c = p->table_children[i]; + c->selected = false; + clear_subtree_selections(c); + } +} + +// SLOT : +// connect(this, SIGNAL(titleClicked(int)), SLOT(setSortColumn(int))); +void Pstable::setSortColumn(int col) +{ + procview->setSortColumn(col, false); + setSortedCol(col); // void HeadedTable::setSortedCol(int col) + refresh(); // rebuild table +} + +// AWKWARD +// sync Procview and HeadedTable set sorted column of table to procview->sortcol +// called by 1. Pstable::moveCol(int col, int place) +void Pstable::set_sortcol() +{ + for (int i = 0; i < procview->cats.size(); i++) + { + if (procview->cats[i] == procview->sortcat) + { + HeadedTable::setSortedCol(i); // HeadedTable + return; + } + } + setSortedCol(-1); +} + +// for Optimization +int Pstable::sizeHintForColumn(int col) const +{ + // int aw=fontMetrics().averageCharWidth(); + int aw = fontMetrics().width("0"); + int cat_idx = procview->cats[col]->index; + // if(col==0) printf("col.idx=%d + // %s\n",cat_idx,procview->cats[col]->name); + switch (cat_idx) + { + // only COMMON Field + // case F_PID : + case F_RSS: + case F_SIZE: + case F_TTY: + return aw * 6; + case F_MEM: + case F_START: + case F_CPU: // PCPU + return aw * 6 + 2; + case F_TIME: + return aw * 5; + case F_WCPU: + case F_NICE: + return aw * 5; + case F_CMD: + return -(aw * 20); + + case F_USER: + // case F_USER : return aw*10; + case F_CMDLINE: + default: + return -1; + } + return -1; +} + +// inner +QString Pstable::title(int col) +{ + if (col >= procview->cats.size()) + { + qDebug("Qps Bug!: title() over col !! %d\n", col); + return ""; + } + return procview->cats[col]->name; +} + +// TESTING +void Pstable::overpaintCell(QPainter *p, int row, int col, int xpos) +{ + if (col != 0) + return; + if (procview->cats[col]->id != F_CMD) + return; + + int w; + + Procinfo *pi = procview->linear_procs[row]; + + int n = pi->nthreads; + if (n == 1) + return; +#ifdef LINUX + if (pi->pid != pi->tgid) + return; // LINUX + +#endif + w = p->fontMetrics().width(text(row, col)); + + QFont font = p->font(); + int size = font.pointSize(); // point size + + if (size <= 0) + return; // saver! + + int h = body->cellHeight(); + h = p->fontMetrics().height(); // return pixel + + int msize = h * 3.0 / 8.0; + // printf("DEBUG: height=%d, msize=%d\n",h,msize); + + if (h <= 11) + return; // saver! + + if (msize < 6) + msize = 6; + + // font.setPointSize(msize); // not pixel! + font.setPixelSize(msize); // not pixel! + p->setFont(font); + p->drawText(xpos + w + 2, msize + msize / 3, QString::number(n)); + font.setPointSize(size); + p->setFont(font); +} +// +QString Pstable::text(int row, int col) +{ + if (col >= procview->cats.size()) + { + qDebug("Qps Bug!: text() over col !! %d\n", col); + return ""; + } + return procview->cats[col]->string(procview->linear_procs[row]); +} + +int Pstable::totalRow() //??? +{ + return procview->linear_procs.size(); +} + +int Pstable::colWidth(int col) +{ + // this is -1 for variable width fields, htable keeps track of it + if (col >= procview->cats.size()) + { + qDebug("Qps Bug!: colWidth() over col !! %d\n", col); + return -1; + } + return procview->cats[col]->width(); +} + +int Pstable::alignment(int col) +{ + // qDebug("debug:alignment()\n"); + Category *cat = procview->cats[col]; + return cat->alignment(); +} + +// virtual of HeadedTable +void Pstable::setSelected(int row, bool flag) +{ + /// qDebug("debug:Pstable::setSelected()\n"); + Procinfo *pi = procview->linear_procs[row]; + if (pi->selected != flag) + { + pi->selected = flag; + // body->repaintRow(row); // TEMP trick.. + } +} + +// virtual +bool Pstable::isSelected(int row) +{ + Procinfo *pi = procview->linear_procs[row]; + return pi->selected; // VALGRIND +} + +int Pstable::rowDepth(int row) +{ + Procinfo *pi = procview->linear_procs[row]; + if (pi) + return pi->level; // VALGRIND segfault + + qDebug("Qps bug: over row %d!!!\n", row); + return 0; +} + +bool Pstable::lastChild(int row) +{ + Procinfo *pi = procview->linear_procs[row]; + if (pi) + return pi->lastchild; + qDebug("Qps bug: over row %d!!!\n", row); + return 0; +} + +// Segfault !!!! +// return not PPID ! just virtual parent in tree +int Pstable::parentRow(int row) +{ + return procview->linear_procs[row]->parent_row; + /* int ppid= procview->linear_procs[row]->ppid; + Procinfo *pi= procview->getProcinfoByPID(ppid); + if(pi) return ppid; + else return -1; */ +} + +// DRAFT tooltip +QString Pstable::tipText(int col) +{ + Category *cat = procview->cats[col]; + QString s(cat->help); + // trick + if (cat->index == F_STAT) + s.append("\n(R =Running, S =Sleeping, T =sTopped, Z=Zombie)"); + if (cat->index == F_PLCY) + s.append("\n(TS =Time Sharing)"); + // if(cat->index == F_RSS); + // s.append("\nRSS = CODE + DATA + SHARE\n" + // "RSS = TRS + DRS + SHARE\n"); + /// s.append("\n(RSS = TRS + DRS)"); + if (cat == procview->sortcat) + s.append(procview->reversed ? "\n(sorted backwards)" : "\n(sorted)"); + + // printf("size=%d\n",s.capacity()); + return s; +} + +void Pstable::showTip(QPoint p, int idx) +{ + QToolTip::showText(p, tipText(idx)); +} + +extern TFrame *infobox; // testing + +// SLOT +void Pstable::mouseOutOfCell() { infobox->hide(); } + +// TEST +void Pstable::mouseOnCell(int row, int col) +{ + // printf("cursor on (%d,%d)\n",row,col); + + Category *cat = procview->cats[col]; + QString s = ""; + + return; + + // below TEST code! + if (cat->index == F_USER) + { + Procinfo *pi = procview->linear_procs[row]; + s = pi->username; + } + else + { + infobox->hide(); + return; + } + + if (infobox != NULL) + { + // p.setY(p.y() + geometry().y()); + // p.setY(QCursor::pos().y() - geometry().y()); + // p.setY(QCursor::pos().y() - frameGeometry().y()); + // p.setX(QCursor::pos().x() - frameGeometry().x()); + // QPoint p=QCursor::pos(); + // if(htable->numSelected()>=2) + + infobox->show(); + infobox->setPos(); + if (s > 0) + { + infobox->setText(s); + } + else + infobox->hide(); + } + + // char *str=htable->total_selectedRow(col); +} + +void Pstable::leaveEvent(QEvent *) {} + +// DRAFT who call? +// void TableBody::mouseMoveEvent(QMouseEvent *e) +char *Pstable::total_selectedRow(int col) +{ + static char buff[48]; + char mem_str[48]; + std::string name; + int index; + + if (procview->cats.size() <= col or col < 0) + return 0; // col == -1 + index = procview->cats[col]->index; + + switch (index) + { + case F_SIZE: + name = "total SIZE: "; + break; + case F_RSS: + name = "total RSS: "; + break; +#ifdef LINUX + case F_TRS: + name = "total Text: "; + break; + case F_DRS: + name = "total Data: "; + break; + case F_STACK: + name = "total STACK: "; + break; +#endif + default: + return 0; + } + + int total = 0; + int rows = procview->linear_procs.size(); + for (int i = 0; i < rows; i++) + if (procview->linear_procs[i]->selected) + { + switch (index) + { + case F_SIZE: + total += procview->linear_procs[i]->size; + break; + case F_RSS: + total += procview->linear_procs[i]->resident; + break; +#ifdef LINUX + case F_TRS: + total += procview->linear_procs[i]->trs; + break; + case F_DRS: + total += procview->linear_procs[i]->drs; + break; + case F_STACK: + total += procview->linear_procs[i]->stack; + break; +#endif + } + } + + mem_string(total, mem_str); // misc.h + strcpy(buff, name.c_str()); + strcat(buff, mem_str); + // sqDebug(buff,"total:%s",total); + return buff; +} + +// col +// colMoved(col, place); +Pstable::Pstable(QWidget *parent, Procview *pv) : HeadedTable(parent, 0) +{ + procview = pv; + // connect(this, SIGNAL(selectionChanged(const Svec + //*)),SLOT(selection_update(const Svec *))); //DEL? + // connect(this, + // SIGNAL(selectionChanged()),SLOT(selection_update())); // + // when procinfo clicked + connect(this, SIGNAL(titleClicked(int)), SLOT(setSortColumn(int))); + connect(this, SIGNAL(foldSubTree(int)), SLOT(subtree_folded(int))); + connect(head, SIGNAL(toolTip(QPoint, int)), this, + SLOT(showTip(QPoint, int))); + connect(this, SIGNAL(flyOnCell(int, int)), SLOT(mouseOnCell(int, int))); + connect(this, SIGNAL(outOfCell()), SLOT(mouseOutOfCell())); +} + +// who call this ? : from qps.cpp +void Pstable::setProcview(Procview *pv) { procview = pv; } + +HeadedTable::NodeState Pstable::folded(int row) +{ + Procinfo *p = procview->linear_procs[row]; + return (p->table_children.size() > 0) ? (p->hidekids ? Closed : Open) + : Leaf; +} + +// slot: called when selection changes +// called by +// 1.void HeadedTable::selectionNotify() +/* +void Pstable::selection_update(const Svec *rows) +{ + //CLIPBOARD + qDebug("debug:selection_update()\n"); + for(int i = 0; i < rows->size(); i++) { + int row = (*rows)[i]; + procview->linear_procs[row]->selected = isSelected(row); // +WRONG !!! + } + + qps->update_menu_selection_status(); + + if(numSelected() > 0 && qps->pids_to_selection) { + // set the X11 selection to "PID1 PID2 PID3 ..." + QString s, num; + int n = numRows(); + for(int i = 0; i < n; i++) { + if(isSelected(i)) { + num.setNum(procview->linear_procs[i]->pid); + s.append(num); + if(i < n - 1) + s.append(" "); + } + } + + // important: this mustn't be called non-interactively since Qt +uses + // the selection time of the last mouse or keyboard event +//// QApplication::clipboard()->setText(s); + } + +} */ + +// call by +// 1.void Qps::set_table_mode(bool treemode) +// slot: changes table mode +void Pstable::setTreeMode(bool treemode) +{ + // qDebug("Pstable::setTreeMode() %d , procview.treeview + // =%d\n",treemode,procview->treeview); + // no more HeadedTable::setTreeMode(treemode); + HeadedTable::setTreeMode(treemode); + procview->treeview = treemode; + procview->fieldArrange(); + set_sortcol(); + refresh(); //==rebuild(); +} + +// +bool Pstable::columnMovable(int col) +{ + if (treemode) + { + if (col == 0) + return false; + } + if (procview->cats[col]->index == F_CMDLINE) + return false; + + return true; +} + +// called by HeadedTable +// Description : FIELD movement by mouse drag to place From col +// virtual HeadedTable::moveCol(col,place); +void Pstable::moveCol(int col, int place) +{ + // qDebug("Pstable::moveCol\n"); + procview->moveColumn(col, place); + set_sortcol(); //??? + procview->fieldArrange(); + // update(); + refresh(); // width size changed ,... + return; + // updateColWidth(place); updateColWidth(col);// TEMP +} + +// NEED Check !! +// Slot: called when a subtree is opened or closed +// row = row number of sheet +void Pstable::subtree_folded(int row) +{ + + Procinfo *p = procview->linear_procs[row]; + p->hidekids = !p->hidekids; + + if (p->hidekids) + clear_subtree_selections(p); // *** important + + refresh(); // tree rebuild only + return; + // ??? + Procinfo *nextp = + (row < numRows() - 1) ? procview->linear_procs[row + 1] : 0; + if (!p->hidekids) + { + // Show as much as possible of the opened subtree + int r = row + 1; + while (r < numRows() && procview->linear_procs[r] != nextp) + r++; + // setAutoUpdate(FALSE); + showRange(row, r - 1); + // setAutoUpdate(TRUE); + } + // This is a stopgap solution; it would be better to have htable + // take care of the hiding of subtrees and repaint only the rows under + // the line hidden +} + +// DRAFT CODE: +// 1.procview->refresh: proc.refresh, rebuild +// 2.resetwidth +// 3.repaint + +// called by +// 1.void Qps::refresh() +void STATUSBAR_SETCOUNT(int n); + +static int kgen = 123; +void Pstable::checkTableModel() +{ + // int size=procview->linear_procs.size(); + // if(size!=numRows()) + { + // printf("Warnning: this is the bug.. wrong! %d + //%d\n",size,numRows()); + } + if (procview->current_gen != kgen) + { + // printf("Warnning: maybe this is the bug.. wrong! + //%d + //%d\n",kgen,procview->current_gen); + // setAutoUpdate(false); // ********** important for + // GUI-thread + // HeadedTable::setTreeMode(procview->treeview); + procview->rebuild(); // for Table + setNumRows(procview->linear_procs.size()); // 1. + // qDebug("Pstable setTreeMode =%d , procview.treeview + //=%d\n",treemode,procview->treeview); + setNumCols(procview->cats.size()); // 2. resetWidths() + kgen = procview->current_gen; + // setAutoUpdate(true); + } +} + +// paintEvent(); + +void Pstable::refresh() +{ + // qDebug("Pstable:refresh()"); + // procview->refresh(); -> move to Qps::refresh() + // qDebug("catsize=%d\n",procview->cats.size()); + procview->rebuild(); // for Table + setNumRows(procview->linear_procs.size()); // 1. + setNumCols(procview->cats.size()); // 2. resetWidths() UNINITIAL + kgen = procview->current_gen; + // checkTableModel(); + repaint_changed(); + + /* + if(true) + { + int count=0,trow=0; + for(int row = 0; row < nrows; row++) + if(isSelected(row)) + { + count++; + trow=row; + } + if (count == 1) + centerVertically(trow); + } */ + + STATUSBAR_SETCOUNT(procview->num_process); +} diff --git a/src/pstable.h b/src/pstable.h new file mode 100644 index 0000000..6d69dc0 --- /dev/null +++ b/src/pstable.h @@ -0,0 +1,64 @@ +// pstable.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef PSTABLE_H +#define PSTABLE_H + +#include "proc.h" +#include "htable.h" + +class Pstable : public HeadedTable +{ + Q_OBJECT + public: + Pstable(QWidget *parent, Procview *pv); + + void set_sortcol(); + void setProcview(Procview *pv); + virtual void moveCol(int col, int place); + void refresh(); + + // called by super + bool hasSelection() { return 0; }; + + virtual bool isSelected(int row); + virtual void setSelected(int row, bool sel); + virtual int totalRow(); + virtual void checkTableModel(); + + public slots: + // void selection_update(const Svec *row); + void setSortColumn(int col); + void subtree_folded(int row); + void showTip(QPoint p, int index); + void setTreeMode(bool treemode); + void mouseOnCell(int row, int col); + void mouseOutOfCell(); + + protected: + // implementation of the interface to HeadedTable + virtual QString title(int col); + virtual QString text(int row, int col); + virtual int colWidth(int col); + virtual int alignment(int col); + virtual QString tipText(int col); + virtual int rowDepth(int row); + virtual NodeState folded(int row); + virtual int parentRow(int row); + virtual bool lastChild(int row); + virtual char *total_selectedRow(int col); + virtual int sizeHintForColumn(int col) const; + virtual bool columnMovable(int col); + + virtual void overpaintCell(QPainter *p, int row, int col, int xpos); + // virtual bool hasChildren(int row); + + virtual void leaveEvent(QEvent *); + + private: + Procview *procview; +}; + +#endif // PSTABLE_H diff --git a/src/pstable2.cpp b/src/pstable2.cpp new file mode 100644 index 0000000..2cb932c --- /dev/null +++ b/src/pstable2.cpp @@ -0,0 +1,491 @@ + +#include "pstable2.h" +int flag_x = 0; +PstableModel::PstableModel(QObject *parent, Procview *pv) : HtableModel(parent) +{ + procview = pv; +} + +QModelIndex PstableModel::index(int row, int col, + const QModelIndex &parent) const +{ + if (flag_x) + printf("index(): row=%d col=%d parent(%d %d)\n", row, col, + parent.row(), parent.column()); + // if(row<0 or column<0) // root? + // return QModelIndex(); + Procinfo *pi; + + if (col < 0 or col >= procview->cats.size()) + return QModelIndex(); + // qFatal("Qps::Pstable2::sizeHintForColumn() Bug!: over col !! + // %d\n",col); + if (parent == QModelIndex()) + { + if (row >= 0 and row < procview->root_procs.size()) + { + Procinfo *pi = procview->root_procs[row]; // NULL possible!! + return createIndex(row, col, pi); + } + else + printf("out of row %d", row); // ??????????????????? + } + + if (parent.isValid()) + { + + Procinfo *ppi = static_cast(parent.internalPointer()); + + if (ppi and row < ppi->table_children.size() and row >= 0) + { + Procinfo *pi = ppi->table_children[row]; + return createIndex(row, col, pi); + } + } + printf(" indeX() error\n"); + // qFatal("Error : index() row=%d ",row); + return QModelIndex(); +} + +// Pure Virtual +QModelIndex PstableModel::parent(const QModelIndex &child) const +{ + // wrong parent makes segfault + // return QModelIndex(); + // printf("parent() ! %d %d\n",child.row(),child.column()); + if (child.isValid()) + { + Procinfo *pi = static_cast(child.internalPointer()); + // if (pi==NULL or pi->level==0 )//or pi->parent_row<0) + if (pi->level < 1) // or pi->parent_row<0) + { + // printf("root , pid=%d ppid=%d + //\n",pi->pid,pi->ppid); + return QModelIndex(); + } + + // parent_row == -1 segfault + Procinfo *ppi = procview->procs.value(pi->ppid, 0); /// + if (ppi == NULL) + printf("error xxxxxxxxxxxx\n"); + // if(pi->ppid==2608) printf("parent_row=%d + // ppid=%d\n",pi->parent_row,pi->ppid); + // Procinfo *ppi=procview->linear_procs[pi->parent_row]; + // if (pi->ppid==0 or ppi==NULL)// or !procview->treeview) + ////if(flag_x) printf("parent : pid=%d (row=%d + /// ppid=%d)\n",pi->pid,ppi->table_child_seq,ppi->pid); + return createIndex(ppi->table_child_seq, 0, ppi); + // return createIndex(0 , 0 ,ppi); + } + printf("parent() Invalud\n"); + return QModelIndex(); // no parent! + + // if(row>=0 and col>=0 and rownrows) + // return createIndex(htable->parentRow(row),col,NULL); +} + +int PstableModel::columnCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + // printf("columnCount() size=%d\n" ,procview->cats.size()); + return procview->cats.size(); +} + +// pure virtual: return chilren count +int PstableModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + int count = 0; + if (parent == QModelIndex()) + { + count = procview->root_procs.size(); + // printf("rowCount() root_procs size=%d\n" + //,count); + } + else + { + Procinfo *pi = static_cast(parent.internalPointer()); + count = pi->table_children.size(); + } + // printf("rowCount (%d %d), + //%d\n",parent.row(),parent.column(),count); + // if(pi->table_children.size()>0) + // htable->setExpanded(index,false); + // if(count>0) htable->setExpanded(parent,false); + // if(count>0) htable->expand(parent); + return count; +} + +bool PstableModel::hasChildren(const QModelIndex &parent) const +{ + int count; + if (parent.column() > 0) + return false; + return rowCount(parent); +} + +QVariant PstableModel::data(const QModelIndex &index, int role) const +{ + // printf("data\n"); + Procinfo *pi = static_cast(index.internalPointer()); + int col = index.column(); + + if (role == Qt::DisplayRole) + { + // if(pi->child_seq_prev>=0 and pi->child_seq!=index.row()) + // printf("========= not equal row =====\n"); + return procview->cats[index.column()]->string(pi); + // returhtable->text(index.row(),index.column()); + } + else if (role == Qt::DecorationRole) + { + } + else if (role == Qt::EditRole) + { + } + else if (role == Qt::CheckStateRole) + { + // if(item->enable) + // return Qt::Checked; + // else + // return Qt::Unchecked; + } + else if (role == Qt::TextAlignmentRole) + { + Category *cat = procview->cats[index.column()]; + return cat->alignment(); + } + if (role == Qt::EditRole) + { + } + else if (role == Qt::BackgroundColorRole) + { + } + else if (role == Qt::SizeHintRole) + { + // return QSize(18,18); + } + return QVariant(); +} +// MOVE TO Procview +// slot: called when a title is clicked + +// void Pstable::set_sortcol() +// setSortedCol(i); +// connect(header(), SIGNAL(sectionPressed( int )),SLOT(setSortColumn(int ))); +void Pstable2::setSortColumn(int col) +{ + // col=-1 no section + if (col < 0 or col >= procview->cats.size()) + return; + // qFatal("Qps::Pstable2::setSortColumn() Bug!: over col !! %d\n",col); + // printf("setSortColumn()\n"); + procview->setSortColumn(col, false); + setSortedCol(col); // void HeadedTable::setSortedCol(int col) + refresh(); // no redraw + model->update(); +} + +// sync Procview and HeadedTable +// set sorted column of table to procview->sortcol +void Pstable2::set_sortcol() +{ + for (int i = 0; i < procview->cats.size(); i++) + { + if (procview->cats[i] == procview->sortcat) + { + setSortedCol(i); // HeadedTable + return; + } + } + setSortedCol(-1); +} + +// for Speed Optimize +int Pstable2::sizeHintForColumn(int col) const +{ + if (col < 0 or col >= procview->cats.size()) + qFatal("Qps::Pstable2::sizeHintForColumn() Bug!: over col !! %d\n", + col); + // int aw=fontMetrics().averageCharWidth(); + int aw = fontMetrics().width("0"); + // int title_witdh=fontMetrics().width(procview->cats[col]->name); + // static int z5=fontMetrics ().width("00000"); + // static int z6=fontMetrics ().width("000000"); + int cat_id = procview->cats[col]->index; + switch (cat_id) + { + // only COMMON Field + // case F_PID : + case F_RSS: + case F_NICE: + case F_SIZE: + case F_TIME: + return aw * 6; + case F_TTY: + case F_MEM: + case F_CPU: + // return aw*7; + return fontMetrics().width("%MEM") + 10; + case F_WCPU: + return fontMetrics().width("%WCPU") + 10; + // return aw*4; + case F_USER: + case F_CMD: + // case F_CMDLINE: return 300; + default: + return QTreeView::sizeHintForColumn(col); + // return HeadedTable::sizeHintForColumn(col); + // QTreeView::resizeColumnToContents (col); + return -1; + ; + } + return -1; +} + +// inner +QString Pstable2::title(int col) +{ + if (col < 0 or col >= procview->cats.size()) + { + qFatal("Qps::Pstable2::title(): Bug! over col !! %d\n", col); + return ""; + } + return procview->cats[col]->name; +} + +QString Pstable2::text(int row, int col) // not called +{ + if (col < 0 or col >= procview->cats.size()) + qFatal("Qps::Pstable2::text() Bug!: over col !! %d\n", col); + + return procview->cats[col]->string(procview->linear_procs[row]); +} + +int Pstable2::totalRow() //??? +{ + return procview->linear_procs.size(); +} + +int Pstable2::colWidth(int col) +{ + if (col < 0 or col >= procview->cats.size()) + qFatal("Qps::Pstable2::colWidth() Bug!: over col !! %d\n", col); + /// return 30; + // this is -1 for variable width fields, htable keeps track of it + return procview->cats[col]->width(); +} + +int Pstable2::alignment(int col) +{ + if (col < 0 or col >= procview->cats.size()) + qFatal("Qps::Pstable2::alignment() Bug!: over col !! %d\n", col); + // printf("debug:alignment()\n"); + Category *cat = procview->cats[col]; + return cat->alignment(); +} + +// virtual of HeadedTable +void Pstable2::setSelected(int row, bool sel) +{ + /// printf("debug:setSelected()\n"); + Procinfo *pi = procview->linear_procs[row]; + if (pi->selected != sel) + { + pi->selected = sel; + // body->repaintRow(row); // TEMP trick.. + } +} + +// virtual +bool Pstable2::isSelected(int row) +{ + Procinfo *pi = procview->linear_procs[row]; + return pi->selected; +} + +int Pstable2::rowDepth(int row) +{ + // if(row < procview->linear_procs.size()) + Procinfo *pi = procview->linear_procs[row]; + if (pi) + return pi->level; + + printf("Qps bug: over row %d!!!\n", row); + return 0; +} + +bool Pstable2::lastChild(int row) +{ + Procinfo *pi = procview->linear_procs[row]; + if (pi) + return pi->lastchild; + printf("Qps bug: over row %d!!!\n", row); + return 0; +} + +/* +int Pstable2::childCount(int row) +{ + return procview->linear_procs[row]->table_children.size(); + //and !p->hidekids) +} */ + +// Segfault !!!! +// return not PPID ! just virtual parent in tree +int Pstable2::parentRow(int row) +{ + return procview->linear_procs[row]->parent_row; + /* int ppid= procview->linear_procs[row]->ppid; + Procinfo *pi= procview->getProcinfoByPID(ppid); + if(pi) return ppid; + else return -1; */ +} + +// DRAFT +QString Pstable2::tipText(int col) +{ + Category *cat = procview->cats[col]; + QString s(cat->help); + + // trick + if (cat->index == F_STAT) + s.append("\n(R =Running, S =Sleeping, T =sTopped, Z=Zombie)"); + if (cat->index == F_PLCY) + s.append("\n(TS =Time Sharing)"); + if (cat->index == F_RSS) + ; + // s.append("\nRSS = CODE + DATA + SHARE\n" + // "RSS = TRS + DRS + SHARE\n"); + /// s.append("\n(RSS = TRS + DRS)"); + if (cat == procview->sortcat) + s.append(procview->reversed ? "\n(sorted backwards)" : "\n(sorted)"); + return s; +} + +// DRAFT who call? +char *Pstable2::total_selectedRow(int col) { return 0; } + +Pstable2::Pstable2(QWidget *parent, Procview *pv) : HeadedTable2(parent, 0) +{ + printf("read 3-2\n"); + procview = pv; + model = new PstableModel(this, pv); + setModel(model); + // activate() double click + // pressed() one click + // entered() drag + // connect(this, SIGNAL(activated ( const QModelIndex & + // )),SLOT(selection_update(const QModelIndex&))); + connect(this, SIGNAL(pressed(const QModelIndex &)), + SLOT(selection_update(const QModelIndex &))); + // connect(header(), SIGNAL(sectionClicked( int + //)),SLOT(setSortColumn(int + //))); + connect(header(), SIGNAL(sectionPressed(int)), SLOT(setSortColumn(int))); + // connect(this, SIGNAL(titleClicked(int)), + // SLOT(setSortColumn(int))); + + printf("read 3-3\n"); + // modelIterate(rootIndex()); + // modelIterate(QModelIndex()); + // update(QModelIndex()); + // reset(); + // expandAll(); + // reset(); + printf("read 3-4\n"); +} + +// who call this ? : from qps.cpp +void Pstable2::setProcview(Procview *pv) { procview = pv; } + +HeadedTable2::NodeState Pstable2::folded(int row) +{ + Procinfo *p = procview->linear_procs[row]; + return (p->table_children.size() > 0) ? (p->hidekids ? Closed : Open) + : Leaf; +} + +// slot: called when selection changes +// called by +void Pstable2::selection_update(const QModelIndex &idx) +{ + QModelIndexList list = selectionModel()->selectedRows(); + printf("debug:selection_update()\n"); + // qps->update_menu_selection_status(); +} + +// slot: changes table mode +// call by +// 1.void Qps::set_table_mode(bool treemode) +void Pstable2::setTreeMode(bool treemode) +{ + /// printf("Pstable setTreeMode\n"); + procview->treeview = treemode; + procview->fieldArrange(); + set_sortcol(); + HeadedTable2::setTreeMode(treemode); + Pstable2::refresh(); + if (treemode) + setRootIsDecorated(true); + else + setRootIsDecorated(false); + + reset(); + expandAll(); + // model->update(); //SEGFAULT + // rebuild(); +} + +bool Pstable2::columnMovable(int col) +{ + if (treemode) + { + if (col == 0) + return false; + } + if (procview->cats[col]->index == F_CMDLINE) + return false; + + return true; +} + +// called by HeadedTable +// Description : FIELD movement by mouse drag +// To place From col +// virtual HeadedTable::moveCol(col,place); +void Pstable2::moveCol(int col, int place) +{ + // printf("Pstable::moveCol\n"); + procview->moveColumn(col, place); + set_sortcol(); + // update(); + refresh(); // width size changed ,... +} + +// DRAFT CODE: +// 1.procview->refresh: proc.refresh, rebuild +// 2.resetwidth +// 3.repaint + +// called by +// 1.void Qps::refresh() +void Pstable2::refresh() +{ + printf("Pstable2:refresh()\n"); + procview->rebuild(); // for Table + setNumCols(procview->cats.size()); // 2. resetWidths() + setNumRows(procview->linear_procs.size()); // 1. + // printf("catsize=%d + // row=%d\n",procview->cats.size(),procview->linear_procs.size()); + // update(); + // update(QModelIndex()); + // repaint(); + // repaint_changed(); + // model->revert(); //temp + // model->submit(); //temp + // model->update(); +} diff --git a/src/pstable2.h b/src/pstable2.h new file mode 100644 index 0000000..4eaac40 --- /dev/null +++ b/src/pstable2.h @@ -0,0 +1,71 @@ + +#include "proc.h" +#include "htable2.h" +#include + +class PstableModel : public HtableModel +{ + Q_OBJECT + public: + PstableModel(QObject *parent, Procview *procview); + ~PstableModel(){}; + virtual QModelIndex index(int row, int column, + const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &child) const; // pure virtual + virtual int rowCount(const QModelIndex &parent) const; + virtual int columnCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual bool hasChildren(const QModelIndex &parent = QModelIndex()) const; + // int columnCount(const QModelIndex &parent) const; + // HeadedTable2 *htable; + Procview *procview; +}; + +class Pstable2 : public HeadedTable2 +{ + Q_OBJECT + public: + Pstable2(QWidget *parent, Procview *pv); + + void set_sortcol(); + void setProcview(Procview *pv); + void setTreeMode(bool treemode); + virtual void moveCol(int col, int place); + void refresh(); + + // called by super + bool hasSelection() { return selectionModel()->hasSelection(); }; + + virtual bool isSelected(int row); // hmm + virtual void setSelected(int row, bool sel); + virtual int totalRow(); // DEL? + + public slots: + void selection_update(const QModelIndex &); // hmm + void setSortColumn(int col); + +signals: + void selection_changed(); + + protected: + // implementation of the interface to HeadedTable + virtual QString title(int col); // ok + virtual QString text(int row, int col); // ok + virtual int colWidth(int col); + virtual int alignment(int col); // ok + virtual QString tipText(int col); // ok + + virtual int rowDepth(int row); + virtual NodeState folded(int row); + virtual int parentRow(int row); + virtual bool lastChild(int row); + virtual char *total_selectedRow(int col); + virtual int sizeHintForColumn(int col) const; + virtual bool columnMovable(int col); + + // virtual void drawCellContents(int row, int col, int w, int h, + // QPainter *p); + + private: + Procview *procview; +}; diff --git a/src/qps.cpp b/src/qps.cpp new file mode 100644 index 0000000..ea66c41 --- /dev/null +++ b/src/qps.cpp @@ -0,0 +1,2553 @@ +// qps -- Qt-based visual process status monitor +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdeg?rd, 1997-1999 +// fasthyun@magicn.com 2005-2012 +// daehyun.yang@gmail.com 2015- + +// TODO & MEMO +/* + * + * For easy maintance + * 1. F_CMDLINE should be rightmost field + * 2. F_PROCESSNAME shoud be leftmost field and NEVER removed! + * + * + * + */ +// . Memory Hole or Qt's string allocate bug? +// . clipboard copy [CMDLINE,PID,USERNAME...] +// . [klog] table cache. +// . save sort of tree, linear +// . watchdog showmsg substitude +// . history system +// . cmd [w] analsys: [IDLE] [WHAT] +// . ExecWindow - [running] finish. +// . UNIX Domain SOCKET +// . COLOR : orange FF5d00 +// . P_MEM -> P_%MEM or P_PMEM + +#include "../icon/icon.xpm" + +#include +#include +#include +#include +#include +#include +#include // uname +#include +#include +#include +#include //for sleep + +#include "qps.h" +#include "dialogs.h" +#include "lookup.h" +#include "misc.h" + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +/* --------------------- Global Variable START ---------------------- */ +QList commands; + +bool previous_flag_show_thread_prev = false; // previous state +bool flag_show_thread = false; // to see informations at the thread level +int flag_thread_ok = true; // we presume a kernel 2.6.x using NPTL +bool flag_session_start = false; +bool flag_start_mini = false; // Need for Xorg.Session +bool flag_refresh = true; // DEL +bool flag_xcompmgr = false; // DEL test.compiz.. +bool flag_devel = false; +bool flag_schedstat = false; +bool flag_smallscreen = false; +bool flag_firstRun = true; // test + +Qps *qps; +// ControlBar *controlbar=NULL; +SearchBox *search_box = NULL; +TFrame *infobox = NULL; // testing +// DEL Screenshot *screenshot=NULL; +QFontComboBox *font_cb = NULL; +WatchdogDialog *watchdogDialog = NULL; + +/// PDisplay *pdisplay; + +QList proclist; + +#include "trayicon.h" +TrayIcon *trayicon = NULL; + +// for Non-ASCII Languages (CJK:chinese,japanese,korean, arabic ...) +#include +QTextCodec *codec = NULL; +#define UniString(str) str // codec->toUnicode(str) +/* ------------------------ END global variables -------------------------- */ + +// default values of settings, overridden by $HOME/.qpsrc if present +bool Qps::flag_show = + true; // window state of last run : mini(iconic) or normal window +bool Qps::flag_exit = true; +bool Qps::flag_qps_hide = true; // TEST +bool Qps::flag_useTabView = false; +bool Qps::show_file_path = false; +bool Qps::show_cmd_path = true; +bool Qps::show_infobar = true; +bool Qps::show_ctrlbar = true; +bool Qps::show_cpu_bar = true; +bool Qps::show_load_graph = true; // DEL +bool Qps::show_statusbar = true; +bool Qps::load_in_icon = true; +bool Qps::auto_save_options = true; +#ifdef LINUX +bool Qps::hostname_lookup = true; +bool Qps::service_lookup = true; +#endif +bool Qps::pids_to_selection = true; +bool Qps::vertical_cpu_bar = false; // not used +#ifdef SOLARIS +bool Qps::normalize_nice = true; +bool Qps::use_pmap = true; +#endif +bool Qps::tree_gadgets = true; +bool Qps::tree_lines = true; +int Qps::swaplimit = 5; // default: warn when less than 10% swap left +bool Qps::swaplim_percent = true; + +QThread *thread_main = 0; + +Qps::Qps() +{ + setObjectName("qps_main_window"); + timer_id = 0; + field_win = 0; + prefs_win = 0; + command_win = 0; + default_icon = 0; + default_icon_set = false; + + setIconSize(24, 24); // Important!! + // setMouseTracking(true); // no need? + + if (flag_devel) + { + thread_main = thread(); // Test + printf("qps thread =%p , qApp thread =%p\n", thread(), + QApplication::instance()->thread()); + } + + // watchdogDialog = new WatchdogDialog; //Memory Leak + + // font_cb=new QFontComboBox(this); // preload + // font_cb->setWritingSystem ( QFontDatabase::Latin ); + // font_cb->hide(); + + make_signal_popup_menu(); + + // MOVETO Pstable !! + m_headpopup = new QMenu("header_popup", this); + m_headpopup->addAction("Remove Field", this, SLOT(menu_remove_field())); + m_fields = new QMenu("Add Field", this); + m_headpopup->addMenu(m_fields); + // connect(m_fields, SIGNAL(activated(int)), + // SLOT(add_fields_menu(int))); + // m_headpopup->addAction("Select Field", this, SLOT(menu_custom()) ); + + m_command = new QMenu("Command", this); // filled in later + + QAction *act; + + m_view = new QMenu("View", this); + act = m_view->addAction("Process"); // act->setData(Procview::CUSTOM); + act = m_view->addAction("Log"); // act->setData(Procview::CUSTOM); + // m_view->hide(); + + m_field = new QMenu("Field", this); + act = m_field->addAction("Custom Fields"); + act->setData(Procview::CUSTOM); + act = m_field->addAction("Basic Fields "); + act->setData(Procview::USER); + act = m_field->addAction("Jobs Fields "); + act->setData(Procview::JOBS); + act = m_field->addAction("Memory Fields "); + act->setData(Procview::MEM); +#ifdef LINUX + act = m_field->addAction("Scheduling Fields "); + act->setData(Procview::SCHED); +#endif + + m_field->addSeparator(); + // act=m_field->addAction(QtIconLoader::icon("edit-find-replace"),"Save + // Current as Custom Fields",this, SLOT(menu_custom())); + act = m_field->addAction(QtIconLoader::icon("edit-find-replace"), + "Select Custom Fields...", this, + SLOT(menu_custom())); + act->setData(MENU_CUSTOM); + + connect(m_field, SIGNAL(triggered(QAction *)), this, + SLOT(view_menu(QAction *))); + connect(m_field, SIGNAL(aboutToShow()), SLOT(update_menu_status())); + + /// connect(m_view, SIGNAL(triggered(QAction *)),this, + /// SLOT(view_menu(QAction + /// *))); + /// connect(m_view, SIGNAL(aboutToShow ()), SLOT(update_menu_status())); + + m_options = new QMenu("Option", this); + m_options->addAction("Update Period...", this, SLOT(menu_update())); + m_options->addSeparator(); + act = m_options->addAction("", /* MENU_PATH */ this, + SLOT(menu_toggle_path())); + act->setData(QVariant(MENU_PATH)); + act = m_options->addAction("", this, SLOT(menu_toggle_infobar())); + act->setData(QVariant(MENU_INFOBAR)); + act = m_options->addAction("", this, SLOT(menu_toggle_ctrlbar())); + act->setData(QVariant(MENU_CTRLBAR)); + act = m_options->addAction("Show Status bar", this, + SLOT(menu_toggle_statusbar())); + act->setData(QVariant(MENU_STATUS)); + act = m_options->addAction("", this, SLOT(menu_toggle_cumul())); + act->setData(QVariant(MENU_CUMUL)); + + m_options->addSeparator(); + // m_options->addAction(QtIconLoader::icon("gtk-preferences" /* + // gnome-settings + // */),"Preferences...", this, SLOT(menu_prefs())); //MENU_PREFS + m_options->addAction(QIcon(":icon/preferences-system.png"), + "Preferences...", this, + SLOT(menu_prefs())); // MENU_PREFS + + connect(m_options, SIGNAL(aboutToShow()), SLOT(update_menu_status())); + + QMenu *m_help = new QMenu("Help", this); + // m_help->addAction("FAQ", this, SLOT(license())); + // m_help->addAction("About", this, SLOT(about())); //gtk-help,help + m_help->addAction(QtIconLoader::icon("help-about"), "Feedback", this, + SLOT(about())); + + // menu = new QMenuBar(this); + menubar = new QMenuBar; + menubar->addMenu(m_command); + // am_view=menubar->addMenu(m_view); + menubar->addMenu(m_field); + menubar->addMenu(m_options); + menubar->addSeparator(); + menubar->addMenu(m_help); + + ctrlbar = new ControlBar(this); + // controlbar=ctrlbar; + + connect(ctrlbar, SIGNAL(need_refresh()), SLOT(refresh())); + connect(ctrlbar, SIGNAL(viewChange(QAction *)), + SLOT(view_menu(QAction *))); //????? + + context_col = -1; + + procview = new Procview(); // refresh() only not rebuild() +/// procview = new Procview("localhost"); // refresh() only not rebuild() +/// procview->refresh(); // TODO +/// proclist.append(procview); +/// procview->start(); // thread start + +#ifdef HTABLE2 + pstable = new Pstable2(this, procview); // +#else + pstable = new Pstable(this, procview); // no refresh() +#endif + PDisplay *display = new PDisplay(this); + infobar = display->addSystem(procview); + //. infobar = new Infobar(this,procview); // graph_bar + // infobar = new Infobar(procview); // graph_bar + statusBar = new StatusBar(this); + + if (!read_settings()) + { + // printf("default value\n"); + set_update_period(1300); // default + resize(640, 370); // default initial size + } + + set_table_mode(procview->treeview); // Pstable::refresh() occur + make_command_menu(); // after qpsrc reading + + // misc. accelerators + QShortcut *c1 = + new QShortcut(Qt::CTRL + Qt::Key_Q, this, SLOT(save_quit())); + QShortcut *c3 = new QShortcut(Qt::CTRL + Qt::Key_Space, + ctrlbar->pauseButton, SLOT(click())); + QShortcut *c2 = + new QShortcut(Qt::CTRL + Qt::Key_L, pstable, SLOT(repaintAll())); + + // MOVETO : pstable better? hmmm... + // where is leftClick? + connect(pstable, SIGNAL(doubleClicked(int)), SLOT(open_details(int))); + connect(pstable, SIGNAL(rightClicked(QPoint)), this, + SLOT(show_popup_menu(QPoint))); + connect(pstable->header(), SIGNAL(rightClicked(QPoint, int)), this, + SLOT(context_heading_menu(QPoint, int))); + // connect(netable, SIGNAL(rightClicked(QPoint)), this, + // SLOT(context_row_menu(QPoint))); + + selection_items_enabled = true; // ???? + update_load_time = 0; + //// update_menu_status(); + + QVBoxLayout *vlayout = new QVBoxLayout; + vlayout->setMargin(0); + if (flag_useTabView) + vlayout->setSpacing(0); + else + vlayout->setSpacing(1); + + // vlayout->addWidget(menu); + vlayout->setMenuBar(menubar); + vlayout->addWidget(display); + // vlayout->addWidget( infobar); + vlayout->addWidget(ctrlbar); + + /// vlayout->addSpacing(5); + + // if(flag_devel){ + if (0) + { + if (0) + { + QSplitter *splitter = new QSplitter(Qt::Vertical); + QDockWidget *dock = new QDockWidget(tr("Detail"), this); + dock->setFeatures(QDockWidget::DockWidgetClosable); + // dock->setWidget(); + // dock->setAllowedAreas(Qt::LeftDockWidgetArea | + // Qt::RightDockWidgetArea); + // customerList = new QListWidget(dock); + // vlayout->addStretch(); + splitter->addWidget(pstable); + splitter->addWidget(dock); + vlayout->addWidget(splitter); + vlayout->addWidget(statusBar); + setLayout(vlayout); + } + } + + logbox = new QTextEdit(this); + logbox->setReadOnly(true); + + if (0) // if(flag_smallscreen==false) + { + // + } + else + { + vlayout->addWidget(pstable); + vlayout->addWidget(logbox); + logbox->hide(); + } + + vlayout->addWidget(statusBar); + setLayout(vlayout); + + infobox = new TFrame(this); + // setAttribute(Qt::WA_ShowWithoutActivating); + if (update_period != eternity) + Qps::update_timer(); // startTimer(update_period); + /// setFocusPolicy (Qt::WheelFocus); + + bar_visibility(); // need + + // testing + popupx = new QMenu("test", this); + popupx->addAction("Copied to Clipboard"); +} + +// explicit destructor needed for gcc +Qps::~Qps() {} +void Qps::tabChanged(int idx) +{ + /* + for(int i; i<1024;i++) + { + QFont font; + if(font_cb) + font=font_cb->currentFont(); + //font=font_cb->currentFont(); + QApplication::setFont(font); + } + + printf("tab\n"); + */ + + if (idx == 0) + { + procview->viewproc = Procview::ALL; + pstable->refresh(); + // pstable->update(); + } + else if (idx == 1) + { + // procview->viewproc=Procview::NETWORK; + // netable->refresh(); + // netable->update(); + } +} +// return true if all selected processes are stopped +bool Qps::all_selected_stopped() +{ + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected && p->state != 'T') + return false; + } + return true; +} + +// Adjust menu to contain Stop or Continue +void Qps::adjust_popup_menu(QMenu *m, bool cont) +{ + // int idx = m->indexOf(MENU_DETAILS); + if (procview->treeview == true) + { + // m->setItemVisible (MENU_PARENT,false); + // m->setItemVisible (MENU_CHILD,false); + // m->setItemVisible (MENU_DYNASTY,false); + } + else + { + // m->setItemVisible (MENU_PARENT,true); + // m->setItemVisible (MENU_CHILD,true); + // m->setItemVisible (MENU_DYNASTY,true); + } + + if (procview->viewproc == Procview::HIDDEN) + { + // m->setItemVisible (MENU_ADD_HIDDEN,false); + // m->setItemVisible (MENU_REMOVE_HIDDEN,true); + } + else + { + // m->setItemVisible (MENU_ADD_HIDDEN,true); + // m->setItemVisible (MENU_REMOVE_HIDDEN,false); + } +} + +// +void Qps::adjust_popup_menu() +{ + bool allstop = all_selected_stopped(); + QList list = m_popup->actions(); + // printf("adjust_popup_menu () size=%d \n",list.size()); + + if (allstop) + for (int i = 0; i < list.size() - 1; i++) + { + QAction *act = list[i]; + int id = act->data().toInt(); + if (id == MENU_SIGCONT) + act->setVisible(true); + if (id == MENU_SIGSTOP) + act->setVisible(false); + } + else + for (int i = 0; i < list.size() - 1; i++) + { + QAction *act = list[i]; + int id = act->data().toInt(); + if (id == MENU_SIGCONT) + act->setVisible(false); + if (id == MENU_SIGSTOP) + act->setVisible(true); + } +} + +// build signal menu (used in two places) +QMenu *Qps::make_signal_popup_menu() +{ + + // move to pstable? + QAction *act; // show_popup_menu() callback + m_popup = new QMenu("context popup", this); + m_popup->addAction("Renice...", this, SLOT(menu_renice())); + m_popup->addAction("Scheduling...", this, SLOT(menu_sched())); + m_popup->addSeparator(); + m_popup->addAction("Terminate", this, SLOT(sig_term()), + Qt::Key_Delete); // better + m_popup->addAction("Hangup", this, SLOT(sig_hup()), Qt::ALT + Qt::Key_H); + m_popup->addAction("Kill", this, SLOT(sig_kill()), Qt::ALT + Qt::Key_K); + act = m_popup->addAction("Stop", this, SLOT(sig_stop())); + act->setData(MENU_SIGSTOP); + act = m_popup->addAction("Continue", this, SLOT(sig_cont())); + act->setData(MENU_SIGCONT); + + // connect(m_popup, SIGNAL(aboutToShow ()),this, + // SLOT(adjust_popup_menu())); + /// m_popup->addAction("Find Parent", this, + /// SLOT(menu_parent()),0,MENU_PARENT); + /// m_popup->addAction("Find Children", this, + /// SLOT(menu_children()),0,MENU_CHILD); + /// m_popup->addAction("Find Descendants", this, + /// SLOT(menu_dynasty()),0,MENU_DYNASTY); + + QMenu *m = new QMenu("Other Signals"); + act = m->addAction("SIGINT (interrupt)"); + act->setData(SIGINT); + act = m->addAction("SIGCONT (continue)"); + act->setData(SIGCONT); + act = m->addAction("SIGSTOP (stop)"); + act->setData(SIGSTOP); + act = m->addAction("SIGQUIT (quit)"); + act->setData(SIGQUIT); + act = m->addAction("SIGILL (illegal instruction)"); + act->setData(SIGILL); + act = m->addAction("SIGABRT (abort)"); + act->setData(SIGABRT); + act = m->addAction("SIGFPE (floating point exception)"); + act->setData(SIGFPE); + act = m->addAction("SIGSEGV (segmentation violation)"); + act->setData(SIGSEGV); + act = m->addAction("SIGPIPE (broken pipe)"); + act->setData(SIGPIPE); + act = m->addAction("SIGALRM (timer signal)"); + act->setData(SIGALRM); + act = m->addAction("SIGUSR1 (user-defined 1)"); + act->setData(SIGUSR1); + act = m->addAction("SIGUSR2 (user-defined 2)"); + act->setData(SIGUSR2); + act = m->addAction("SIGCHLD (child death)"); + act->setData(SIGCHLD); + act = m->addAction("SIGTSTP (stop from tty)"); + act->setData(SIGTSTP); + act = m->addAction("SIGTTIN (tty input)"); + act->setData(SIGTTIN); + act = m->addAction("SIGTTOU (tty output)"); + act->setData(SIGTTOU); + + connect(m, SIGNAL(triggered(QAction *)), SLOT(signal_menu(QAction *))); + + m_popup->addMenu(m); + m_popup->addSeparator(); + m_popup->addAction("View Details", this, SLOT(Action_Detail())); + + return m; +} + +void Qps::signal_menu(QAction *act) +{ + int signal = act->data().toInt(); + send_to_selected(signal); +} + +#ifdef MOSIX + +// build migrate menu +static int intcmp(const int *a, const int *b) { return *a - *b; } + +QMenu *Qps::make_migrate_menu() +{ + QString buf; + QMenu *m = new QMenu; + Svec lst = Procinfo::mosix_nodes(); + lst.sort(intcmp); /// sort + m->insertItem("Home", 1); + m->insertItem("Find Best", 0); + for (int i = 0; i < lst.size(); i++) + { + buf.sprintf("to node %d", lst[i]); + m->insertItem(buf, lst[i] + 1); + } + return m; +} +#endif // MOSIX + +// update the visibility of the {info, control, status} bar +void Qps::bar_visibility() +{ + + if (show_infobar) + infobar->show(); + else + infobar->hide(); + + if (show_ctrlbar) + ctrlbar->show(); + else + ctrlbar->hide(); + + if (show_statusbar) + statusBar->show(); + else + statusBar->hide(); + + if (flag_useTabView) + { + // DEL tbar->showTab(true); + /// am_view->setVisible(false); + } + else + { + // DEL tbar->showTab(false); + // am_view->setVisible(true); + } +} + +void Qps::timerEvent(QTimerEvent *e) { Qps::refresh(); } +// +// dialogs.cpp: qps->update_timer(); +void Qps::update_timer() +{ + if (timer_id) + killTimer(timer_id); + timer_id = startTimer(update_period); // *** important !!! +} + +// change the update period, recomputing the averaging factor +void Qps::set_update_period(int milliseconds) +{ + if (milliseconds == 0) + { + qDebug("DEBUG: set_update_period(): update= %dms \n", milliseconds); + milliseconds = 1000; + } + update_period = milliseconds; + Proc::update_msec = milliseconds; // testing transfer... + Procview::avg_factor = exp(-(float)update_period / Procview::cpu_avg_time); +} + +void Qps::refresh() +{ + if (flag_refresh == false) + return; + + procview->refresh(); // important + // infobar->update_load(); // add_point and drawGraphOnPixmap(); + infobar->updatePixmap(); // add_point and drawGraphOnPixmap(); + + if (isVisible()) + { // infobar->isVisible() + // printf("paint\n"); + // if(search_box and !search_box->hasFocus()) + // search_box->setFocus(Qt::OtherFocusReason); + pstable->refresh(); + // if(netable and netable->isVisible())netable->refresh(); + if (show_infobar) // if(Qps::show_load_graph) + infobar->update(); // TODO: thread update or + // infobar->repaint(); + //==> pdisplay->update(); + // DEL update_menu_selection_status(); // update pop menu + } + + if (trayicon) + update_icon(); // make icon for systray + refresh_details(); +} + +// make next timer_refresh happen a little earlier to remove processes that +// might have died after a signal +void Qps::earlier_refresh() +{ + const int delay = 500; // wait no more than this period (ms) + if (update_period > delay && update_period != eternity) + { + } +} + +void Qps::resizeEvent(QResizeEvent *e) +{ + // DEBUG("Qps::resize() w=%d\n",e->size().width()); + // QWidget::resizeEvent(event); + + int w; + return; + + // search_box->isVisible(true); // imidiate + w = ctrlbar->sizeHint().width(); + if (!search_box->isVisible()) + w += search_box->sizeHint().width(); + + if (e->size().width() < w) + { + // ctrlbar-> + search_box->hide(); + } + // else search_box->show(); + + return; + if (e->size().width() < 639) + { + // tbar->setDrawBase (false); + flag_smallscreen = true; + statusBar->hide(); + // void QTabWidget::setTabEnabled ( int index, bool enable + //) + } + else + { + flag_smallscreen = false; + statusBar->show(); + } +} + +/* + Description : + 1. called when visible() or hide() state. QT4 + 2. called when clicked WINDOW_EXIT_X_BUTTON + 3. NOT called when user logout (session manager) +*/ +void Qps::closeEvent(QCloseEvent *e) +{ + // DEBUG("DEBUG: closeEvent()....\n"); + // sleep(2); + if ((Qps::flag_exit == false) and trayicon->hasSysTray()) + { + e->ignore(); // dont close window! + hide(); + return; + } + e->accept(); // ok. I will be exit Now !! + save_quit(); +} + +// call by void Qps::make_command_menu() +// void signal_handler(int sig) +void Qps::save_quit() // will be removed ! +{ + // printf("DEBUG: save_quit()....\n"); + close(); // if another window exists, then no exit. // occurs + // QCoseEvent! + save_settings(); + qApp->quit(); // MACRO +} + +// NEW Version ! +// : write geometry, visible fields and other settings to $HOME/.qpsrc +void Qps::save_settings() +{ + if (Qps::auto_save_options) + write_settings(); +} + +void Qps::leaveEvent(QEvent *event) +{ + // printf("out!xx\n"); //works well +} + +void Qps::enterEvent(QEvent *event) +{ + // printf("in!\n"); // works well +} + +void Qps::showEvent(QShowEvent *event) +{ + // printf("showEvent()\n"); + // event->accept(); +} +void Qps::mouseMoveEvent(QMouseEvent *event) +{ + // printf("Qps::mouseMoveEvent\n"); //work in child surface without + // setMouseTracking(true); +} + +// setFocusPolicy() should on +void Qps::focusInEvent(QFocusEvent *event) +{ + // printf("focusIn\n"); // not work in compiz,Metacity + // infobar->showup(); + // event->accept(); +} + +void Qps::focusOutEvent(QFocusEvent *event) +{ + // printf("focusOut\n"); // not work in compiz,Metacity + // event->accept(); +} + +void Qps::keyPressEvent(QKeyEvent *event) +{ + // printf("Qps::key\n"); // not work in compiz + if (search_box) + { + if (!search_box->hasFocus()) + search_box->setFocus(Qt::OtherFocusReason); + search_box->keyPressEvent(event); + } + // search_box->setFocusProxy(pstable); +} +void Qps::moveEvent(QMoveEvent *event) +{ + ////infobar->refresh(); + // printf("move\n"); +} +void Qps::paintEvent(QPaintEvent *event) +{ + // printf("paintEvent : Qps\n"); + // update_icon(); +} +void Qps::update_icon() +{ + if (load_in_icon) + set_load_icon(); // always true + else + set_default_icon(); +} +// called by update_icon() +void Qps::set_default_icon() +{ + if (!default_icon_set) + { + if (!default_icon) + default_icon = new QPixmap((const char **)icon_xpm); + setWindowIcon(*default_icon); + default_icon_set = true; + } +} + +void Qps::set_load_icon() +{ + QPixmap *pm = infobar->load_icon(icon_width, icon_height); + if (!pm->mask()) + { + // avoid a fvwm/Qt 1.30 problem and create a filled mask for the + // icon + // (without mask, Qt would attempt to use a heuristically + // created mask) + QBitmap bm(pm->size()); + bm.fill(Qt::color1); + pm->setMask(bm); + } + // QApplication::setWindowIcon(*pm); + if (trayicon->hasSysTray()) + { + trayicon->setIcon(*pm); + // str.sprintf("Qps %2.2f%",Procinfo::loadQps); + // trayicon->setToolTip(str); + // trayicon->showMessage("str",str); + } + // else + setWindowIcon(*pm); + // QApplication::setWindowIcon(*pm); + default_icon_set = false; +} + +QPixmap *Qps::get_load_icon() +{ + return infobar->load_icon(icon_width, icon_height); +} + +// +void Qps::refresh_details() +{ +//// details.first(); +////Details d; +#ifdef LINUX +// Proc::invalidate_sockets(); +#endif + + for (int i = 0; i < details.size(); ++i) + { + /////Details *d=details[i] + ////if(details[i].isVisible()) + /////details[i].refresh(); + } + /* + while((d = details.current()) != 0) { + if(d->isVisible()) + d->refresh(); + details.next(); + } */ +} + +// called by +// 1.void Qps::field_removed(int index) +// ====> QMenu::aboutToShow () +void Qps::update_menu_status() +{ + // printf("update_menu_status\n"); + update_menu_selection_status(); + // ctrlbar->view->setCurrentItem (procview->viewproc); //??? + + // Field Menu + QList list = m_field->actions(); + for (int i = 0; i < list.size() - 1; i++) + { + QAction *act = list[i]; + act->setCheckable(true); + int id = act->data().toInt(); + if (id == procview->viewfields) + act->setChecked(true); + else + act->setChecked(false); + } + + // View Menu temporary + /* + QString str=tbar->tabText(tbar->currentIndex()); + list=m_view->actions(); + for(int i =0 ;i< list.size() ;i++) + { + QAction *act=list[i]; + act->setCheckable(true); + + if(act->text()==str) + { + act->setChecked(true); + } + else + act->setChecked(false); + } */ + + // Option Menu + list = m_options->actions(); + for (int i = 0; i < list.size() - 1; i++) + { + QAction *act = list[i]; + int id = act->data().toInt(); + + if (id == MENU_PATH) + { + act->setCheckable(true); + act->setChecked(Procview::flag_show_file_path); + act->setText("Show File Path"); + // act->setText(Procview::flag_show_file_path? + //"Hide File Path" : + //"Show File Path"); + } + else if (id == MENU_INFOBAR) + { + act->setCheckable(true); + act->setChecked(show_infobar); + act->setText("Show Graph"); + // act->setText(show_infobar ? "Hide Graph" : "Show + // Graph"); + } + else if (id == MENU_CTRLBAR) + { + act->setCheckable(true); + act->setChecked(show_ctrlbar); + act->setText("Show Control Bar"); + // act->setText(show_ctrlbar ? "Hide Control bar" : + // "Show Control + // Bar"); + } + else if (id == MENU_STATUS) + { + act->setCheckable(true); + act->setChecked(show_statusbar); + } + else if (id == MENU_CUMUL) + { + act->setCheckable(true); + act->setChecked(Procview::flag_cumulative); + act->setText("Include Child Times"); + // act->setText(Procview::flag_cumulative ? + // "Exclude Child Times" : + // "Include Child Times"); + } + } +} + +// need to change name , redesign +void Qps::view_menu(QAction *act) +{ + int id = act->data().toInt(); + if (act->text() == "Process") + { + // tbar->setCurrentIndex(0); + return; + } + if (act->text() == "Log") + { + // tbar->setCurrentIndex(1); + return; + } + + int state = id; + if (id >= Procview::ALL && id <= Procview::HIDDEN) + { + if (procview->viewproc != state) + { + procview->viewproc = state; + } + } + + if (id >= Procview::USER && id <= Procview::CUSTOM) + { + if (procview->viewfields != state) + { + procview->viewfields = state; + procview->set_fields(); + } + } + pstable->refresh(); // layout + /// update_menu_status(); +} + +// called when selection changed & update time +// void Qps::update_menu_status() +// void Qps::make_command_menu() +// DEL. +// -> show_popup_menu() +void Qps::update_menu_selection_status() +{ + + // bool enabled = (pstable->numSelected() > 0); + // if(enabled) + if (0) + { + bool cont = all_selected_stopped(); + adjust_popup_menu(m_popup, cont); + } + + for (int i = 0; i < commands.size(); i++) + { + /* if (commands[i]->IsNeedProc()==false) + m_command->setItemEnabled(MENU_FIRST_COMMAND + i, true); + else m_command->setItemEnabled(MENU_FIRST_COMMAND + i, enabled); + */} +} + +// call by SearchBox::keyPressEvent + +// slot +void Qps::sig_term() { send_to_selected(SIGTERM); } + +void Qps::sig_hup() { send_to_selected(SIGHUP); } + +// need +void Qps::sig_stop() +{ + send_to_selected(SIGSTOP); + + // test + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + { + p->test_stop = 1; // who ?? + // sendsig(p, sig); + } + } +} + +void Qps::sig_cont() +{ + send_to_selected(SIGCONT); + + // test + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + p->test_stop = 0; + } +} + +void Qps::sig_kill() { send_to_selected(SIGKILL); } + +void Qps::menu_custom() +{ + //// view_menu(Procview::CUSTOM); // should !! + if (field_win) + { + field_win->show(); + field_win->raise(); + } + else + { + field_win = new FieldSelect(procview); + setWindowGroup(field_win); + field_win->show(); + connect(field_win, SIGNAL(added_field(int)), this, + SLOT(field_added(int))); + connect(field_win, SIGNAL(removed_field(int)), this, + SLOT(field_removed(int))); + } +} + +// MOVE TO PSTABlE ? hmmm wait... +// Interface for reading_setting() +// SLOT: +// +// called by +// 1.click Tree_checkbox +// 2.void Qps::view_menu(int id) +void Qps::set_table_mode(bool treemode) +{ + // qDebug("set_table_mode()\n"); + ctrlbar->setMode(treemode); // toggle checkbox + pstable->setTreeMode(treemode); // first pstable->refresh(); +} + +// Slot: +// SEGFAULT CODE: MOVETO Pstable +// called by +// 1. void Qps::add_fields_menu(int id) +// 2. field_win +void Qps::field_added(int field_id) +{ + int where = -1; + where = pstable->clickedColumn(); + procview->addField(field_id, where); + pstable->refresh(); // pstable->update(); // repaint + // update_menu_status(); +} + +// MOVETO Proview +// what this hell? SEGFAULT! +// call by +// 1. FieldSelect +// 2. void Qps::menu_remove_field() +void Qps::field_removed(int index) +{ + /// printf("field_removed() index=%d\n",index); + procview->removeField(index); + if (procview->treeview and index == F_CMD) + set_table_mode(false); + // should be changed to linear mode !! + + /// update_menu_status(); // ??? + + // pstable->update(); // no + pstable->refresh(); + context_col = -1; // *** important **** : right clicked column removed + return; +} + +// moveto command? +void Qps::menu_edit_cmd() +{ + if (command_win) + { + command_win->show(); + command_win->raise(); + } + else + { + command_win = new CommandDialog(); + setWindowGroup(command_win); + command_win->show(); + connect(command_win, SIGNAL(command_change()), + SLOT(make_command_menu())); + } +} + +// init command menu +// callback by CommandDialog::add_new() +void Qps::make_command_menu() +{ + // should clear SIGNAL!!! + QAction *act; + m_command->clear(); + m_command->disconnect(); + + if (flag_devel) + { + m_command->addAction("WatchDog", watchdogDialog, + SLOT(show())); //, m_event); + // m_command->addAction("ScreenShot", screenshot, + // SLOT(show()) ); + act = m_command->addAction("Edit Commands...", this, + SLOT(menu_edit_cmd())); + // act->setEnabled(false); + } + + // if(commands.size()) m_command->addSeparator(); + add_default_command(); + + for (int i = 0; i < commands.size(); i++) + { + // commands[i]->menu = m_command->insertItem(commands[i]->name, + // MENU_FIRST_COMMAND + i); + // commands[i]->menu = + act = m_command->addAction(commands[i]->name); + // connect(act, SIGNAL(triggered(bool )), + // SLOT(signal_menu(bool))); + } + connect(m_command, SIGNAL(triggered(QAction *)), + SLOT(run_command(QAction *))); + + update_menu_selection_status(); // DEL + //#ifdef SOLARIS + /* Solaris CDE don't have a tray, so we need a method to terminate */ + m_command->addSeparator(); + // m_command->addAction(QtIconLoader::icon("application-exit"), "&Quit", + // this, + // SLOT(save_quit()), Qt::ALT + Qt::Key_Q); + m_command->addAction(QtIconLoader::icon("gtk-quit"), "&Quit", this, + SLOT(save_quit()), Qt::ALT + Qt::Key_Q); + //#endif +} + +// run by MENU_ID ? qt slot? +// void Qps::run_command(int command_id) +void Qps::run_command(QAction *act) +{ + int command_id; + int i, j, idx = -1; + // FUNC_START + + // printf("%s\n",qPrintable(act->text())); + AddLog(act->text()); + + // find_command + for (i = 0; i < commands.size(); i++) + { + // if(commands[i]->menu==command_id) + if (commands[i]->name == act->text()) + { + if (commands[idx]->IsNeedProc() == false) + { + commands[idx]->call(NULL); + return; + } + + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + commands[idx]->call(p); + } + break; + } + } + + return; +} + +// detail +void Qps::Action_Detail() +{ + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + open_details(i); + } +} + +// MOVETO : pstable.cpp +void Qps::open_details(int row) +{ + Procinfo *p = procview->linear_procs[row]; + if (p->detail) + { + // p->detail->raise(); + p->detail->show(); + printf("show detail \n"); + } + else + { + Details *d = new Details(p, procview); + // details.append(*d); + // setWindowGroup(d); + d->show(); + /// connect(d, SIGNAL(closed(Details *)), this, + /// SLOT(details_closed(Details + ///*))); + } +} + +void Qps::details_closed(Details *d) +{ + // printf("details_closed()\n"); + // disconnect + + // This is potentially dangerous, since this is called in response to a + // signal sent by the widget that is about to be deleted here. Better + // hope + // that nobody references the object down the call chain! + // what ?????? + + //////details.removeAt(*d); // deletes window + // details.removeAt(*d); // deletes window +} + +// find parents of selected processes +void Qps::menu_parent() { locate_relatives(&Procinfo::ppid, &Procinfo::pid); } + +void Qps::menu_children() { locate_relatives(&Procinfo::pid, &Procinfo::ppid); } + +// Find processes whose attribute b is equal to the attribute a of +// selected processes. Center around topmost found. +// This is quadratic in worst case (shouldn't be a problem) +// called by +// 1.menu_children() +// 2.menu_parent() +void Qps::locate_relatives(int Procinfo::*a, int Procinfo::*b) +{ + QList relatives; + const int infinity = 2000000000; + int topmost = infinity; + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + { + pstable->setSelected(i, false); + for (int j = 0; j < procview->linear_procs.size(); j++) + { + Procinfo *q = procview->linear_procs[j]; + if (p->*a == q->*b) + { + relatives.append(j); + if (j < topmost) + topmost = j; + } + } + } + } + for (int i = 0; i < relatives.size(); i++) + pstable->setSelected(relatives[i], true); + /// if(topmost < infinity) pstable->centerVertically(topmost); + //// pstable->selectionNotify(); +} + +// select all (direct and indirect) offsprings of currently selected +// processes, without deselecting them +void Qps::menu_dynasty() +{ + QList family; + for (int i = 0; i < procview->linear_procs.size(); i++) + if (pstable->isSelected(i)) + family.append(i); + for (int i = 0, j = family.size(); i < j;) + { + for (int k = 0; k < procview->linear_procs.size(); k++) + { + Procinfo *p = procview->linear_procs[k]; + for (int m = i; m < j; m++) + { + Procinfo *q = procview->linear_procs[family[m]]; + if (q->pid == p->ppid) + family.append(k); + } + } + i = j; + j = family.size(); + } + const int infinity = 2000000000; + int topmost = infinity; + for (int i = 0; i < family.size(); i++) + { + pstable->setSelected(family[i], true); + if (family[i] < topmost) + topmost = family[i]; + } + ////if(topmost < infinity) pstable->centerVertically(topmost); + ////pstable->selectionNotify(); +} + +// CALLBACK: called when right button is clicked in table +void Qps::show_popup_menu(QPoint p) +{ +#ifdef MOSIX + bool may_migrate = false; + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected && p->cantmove.isEmpty()) + { + may_migrate = true; + break; + } + } + m_popup->setItemEnabled(POPUP_MIGRATE, may_migrate); +#endif + adjust_popup_menu(); + m_popup->popup(p); +} + +// called when right button is clicked in heading +// +void Qps::context_heading_menu(QPoint p, int col) +{ + context_col = col; // **** + ////printf("context_col=%d\n",col); + static int init = 0; + // rebuild the submenu: only include non-displayed fields + int ncats = procview->categories.size(); + + if (init == 0) + { + /* #ifdef SHash + SHash::const_iterator it=procview->categories.begin(); + QList keys; + for ( ; it != procview->categories.end(); it++ ) + keys.append((*it).first); + #else + */ QList keys = procview->categories.keys(); + // #endif + for (int i = 0; i < ncats; i++) + { + Category *cat = procview->categories[keys.takeFirst()]; + QAction *act = m_fields->addAction(cat->name); + act->setData(cat->id); // index==key + } + connect(m_fields, SIGNAL(triggered(QAction *)), this, + SLOT(add_fields_menu(QAction *))); + init = 1; + } + + QBitArray displayed(64); // MAX_FIELDS + displayed.fill(false); + // printf("categories.size=%d cats.size=%d\n",ncats, + // procview->cats.size()); + + for (int i = 0; i < procview->cats.size(); i++) + { + // printf("cat id=%d \n",procview->cats[i]->id); + displayed.setBit(procview->cats[i]->id); + } + + QList la = m_fields->actions(); + for (int j = 0; j < la.size(); j++) + { + QAction *act = la[j]; + int id = act->data().toInt(); + if (!displayed.testBit(id)) + act->setVisible(true); + else + act->setVisible(false); + } + + m_headpopup->popup(p); +} + +void Qps::add_fields_menu(QAction *act) +{ + // act->text(); + int id = act->data().toInt(); + field_added(id); + // add_fields_menu(id); +} + +// called when field is added from heading context menu +// called by 1. context_heading_menu +void Qps::add_fields_menu(int cat_id) +{ + field_added(cat_id); + context_col = -1; + // if(field_win) field_win->update_boxes(); // change to showEvent. !!!! +} + +// +void Qps::menu_remove_field() +{ + if (context_col < 0) + return; + /// printf("cats.size=%d , context_col=%d + /// \n",procview->cats.size(),context_col); + field_removed(procview->cats[context_col]->index); +} + +void Qps::menu_update() +{ + + QString txt; + if (update_period == eternity) + txt = "1 s"; + else if (update_period % 1000 == 0) + txt.sprintf("%d s", update_period / 1000); + else + txt.sprintf("%d ms", update_period); + IntervalDialog id(txt.toUtf8().data(), update_period != eternity); + id.exec(); + /// save_settings(); + return; +} + +void Qps::menu_toggle_path() +{ + Procview::flag_show_file_path = !Procview::flag_show_file_path; + /// table refresh() ! +} + +void Qps::menu_toggle_infobar() +{ + show_infobar = !show_infobar; + bar_visibility(); +} + +void Qps::menu_toggle_ctrlbar() +{ + show_ctrlbar = !show_ctrlbar; + bar_visibility(); +} + +void Qps::menu_toggle_statusbar() +{ + show_statusbar = !show_statusbar; + bar_visibility(); +} + +void Qps::menu_toggle_cumul() +{ + Procview::flag_cumulative = !Procview::flag_cumulative; + // refresh() !! +} + +void Qps::menu_prefs() +{ + if (prefs_win) + { + prefs_win->show(); + prefs_win->raise(); + } + else + { + prefs_win = new Preferences(); + setWindowGroup(prefs_win); + prefs_win->show(); + prefs_win->raise(); + + connect(prefs_win, SIGNAL(prefs_change()), this, SLOT(config_change())); + /// connect(infobar, SIGNAL(config_change()), prefs_win, + /// SLOT(update_boxes())); // doesnt need !! --> aboutToShow() + } +} + +// if "Preferences" changed +void Qps::config_change() +{ + + write_settings(); + bar_visibility(); + // DEL infobar->configure(); + /// resizeEvent(0); // in case it caused geometry change + + for (int i = 0; i < details.size(); ++i) + { + ///// details[i].config_change(); + } + /* while((d = details.current()) != 0) { + d->config_change(); + details.next(); + } */ +} + +void Qps::menu_renice() +{ + // if(pstable->hasSelection() == 0) return; + + int defnice = -1000; + + // use nice of first selected process as default, and check permission + bool possible = true; + int euid = geteuid(); + Procinfo *p = 0; + + for (int i = 0; i < procview->linear_procs.size(); i++) + { + p = procview->linear_procs[i]; + if (p->selected) + { + if (defnice == -1000) + defnice = p->nice; + if (euid != 0 && euid != p->uid && euid != p->euid) + possible = false; + } + } + + if (!possible) + { + QString s; + s.sprintf("You do not have permission to renice the\n" + "selected process%s.\n" + "Only the process owner and the super-user\n" + "are allowed to do that.", + (pstable->hasSelection() == 1) ? "" : "es"); + QMessageBox::warning(this, "Permission denied", s); + return; + } + + int new_nice; + for (;;) + { + SliderDialog sd(defnice, -20, 19); // Linux kernel : -20 ~ 19 + if (!sd.exec()) + return; + bool ok; + new_nice = sd.ed_result.toInt(&ok); + if (ok && new_nice >= -20 && new_nice <= 19) + break; + } + int nicecol = procview->findCol(F_NICE); + int statcol = procview->findCol(F_STAT); +#ifdef LINUX + int tmscol = procview->findCol(F_TMS); +#endif + + // do the actual renicing + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + { + if (setpriority(PRIO_PROCESS, p->pid, new_nice) < 0) + { + QString s; + switch (errno) + { + case EPERM: + // this shouldn't happen, but (e)uid + // could be changed... + s.sprintf("You do not have permission " + "to renice" + " process %d (", + p->pid); + s.append(p->command); + s.append(").\n" + "Only the process owner and " + "the super-user are" + " allowed to do that."); + QMessageBox::warning(this, "Permission denied", s); + break; + case EACCES: + QMessageBox::warning(this, "Permission denied", + "Only the super-user may lower" + " the nice value of a process."); + return; + } + } + else + { + p->nice = new_nice; // don't wait for update +#ifdef LINUX + p->tms = p->get_tms(); // ditto +#endif + } + } + } +} + +void Qps::menu_sched() +{ + // if(pstable->hasSelection()==false) return; + if (geteuid() != 0) + { + QMessageBox::warning(this, "Permission denied", + "Only the super-user may change the\n" + "scheduling policy and static priority."); + return; + } + + // provide reasonable defaults (first selected process) + Procinfo *p = 0; + for (int i = 0; i < procview->linear_procs.size(); i++) + { + p = procview->linear_procs[i]; + if (p->selected) + break; + } + int pol = p->get_policy(); + int pri = p->get_rtprio(); + SchedDialog sd(pol, pri); + if (!sd.exec()) + return; + + if (sd.out_policy == SCHED_OTHER) + sd.out_prio = 0; // only allowed value + int plcycol = procview->findCol(F_PLCY); + int rpricol = procview->findCol(F_RPRI); +#ifdef LINUX + int tmscol = procview->findCol(F_TMS); +#endif + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + { + struct sched_param sp; + sp.sched_priority = sd.out_prio; + if (sched_setscheduler(p->pid, sd.out_policy, &sp) < 0) + { + QString s; + if (errno == EPERM) + { + s.sprintf("You do not have permission to " + "change the\n" + "scheduling and/or priority of" + " process %d (", + p->pid); + s.append(p->command); + s.append(").\n" + "Only the super-user may do that."); + QMessageBox::warning(this, "Permission denied", s); + break; + } + } + else + { + p->policy = sd.out_policy; // don't wait for update + p->rtprio = sd.out_prio; +#ifdef LINUX + p->tms = p->get_tms(); +#endif + } + } + } +} + +#ifdef MOSIX + +void Qps::mig_menu(int id) { migrate_selected(id - 1); } + +void Qps::migrate_selected(int migto) +{ + // User wants to migrate a process somewhere + // Write destination into /proc/XX/goto + int warnremote = 0; + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + { + if (p->isremote) + ++warnremote; + char buf[80]; + sprintf(buf, "/proc/%d/goto", p->pid); + FILE *f = fopen(buf, "w"); + if (f) + { + fprintf(f, "%d", migto); + fclose(f); + } + } + } + if (warnremote) + QMessageBox::warning(this, "Remote migration attempt", + "You can only migrate an immigrated process " + "using qps on the home node."); + earlier_refresh(); +} +#else + +// Since this is a slot, at least a stub must be defined even when it isn't +// used (moc ignores preprocessor directives) +void Qps::mig_menu(int) {} + +#endif // MOSIX + +void Qps::send_to_selected(int sig) +{ + for (int i = 0; i < procview->linear_procs.size(); i++) + { + Procinfo *p = procview->linear_procs[i]; + if (p->selected) + sendsig(p, sig); + } + earlier_refresh(); // in case we killed one +} + +void Qps::sendsig(Procinfo *p, int sig) +{ + if (kill(p->pid, sig) < 0) + { + // if the process is gone, do nothing - no need to alert the + // user + if (errno == EPERM) + { + QString s; + s.sprintf("You do not have permission to send a signal to" + " process %d (", + p->pid); + s.append(p->command); + s.append(").\n" + "Only the super-user and the owner of the process" + " may send signals to it."); + QMessageBox::warning(this, "Permission denied", s); + // PermissionDialog *pd = new + // PermissionDialog(s,supasswd); + // pd->exec(); + } + } +} + +// write geometry, visible fields and other settings to $HOME/.qps-settings +#define SETTINGS_FILE ".qpsrc" +// If the file format is changed in any way (including adding new +// viewable fields), QPS_FILE_VERSION must be incremented to prevent +// version mismatches and core dumps + +#ifdef LINUX +#define QPS_FILE_VERSION 40 // version of .qps-linux file format +#endif + +#ifdef SOLARIS +#define QPS_FILE_VERSION 24 // version of .qps-solaris file format +#endif + +struct Sflagvar +{ + const char *name; + bool *var; +}; +static Sflagvar flagvars[] = {{"ExitOnClose", &Qps::flag_exit}, + {"TabView", &Qps::flag_useTabView}, + {"SingleCPU", &Procview::flag_pcpu_single}, + {"devel", &flag_devel}, + {"cmdpath", &Procview::flag_show_file_path}, + {"infobar", &Qps::show_infobar}, + {"ctrlbar", &Qps::show_ctrlbar}, + {"statbar", &Qps::show_statusbar}, + {"autosave", &Qps::auto_save_options}, + {"cpubar", &Qps::show_cpu_bar}, + {"loadgraph", &Qps::show_load_graph}, + {"loadicon", &Qps::load_in_icon}, + {"selectpids", &Qps::pids_to_selection}, + {"tree", &Procview::treeview}, + {"cumulative", &Procview::flag_cumulative}, +#ifdef LINUX + {"hostname", &Qps::hostname_lookup}, + {"service", &Qps::service_lookup} +#endif +#ifdef SOLARIS + {"normalize", &Qps::normalize_nice}, + {"pmap", &Qps::use_pmap} +#endif + , + {0, 0}}; + +char *settings_path(char *name) +{ + char *home = getenv("HOME"); + if (!home) + return 0; + strcpy(name, home); + strcat(name, "/" SETTINGS_FILE); + return name; +} + +#include +extern QList watchlist; +// 1.Procview should be contstructed ! +// 2. +bool Qps::read_settings() +{ + char path[512]; + if (settings_path(path) == NULL) + { + return false; + } + QSettings set(path, QSettings::NativeFormat); + + int v = set.value("version", 0).toInt(); + if (v == 0) + return false; + + int x, y, w, h; + x = set.value("geometry/x").toInt(); + y = set.value("geometry/y").toInt(); + w = set.value("geometry/width").toInt(); + h = set.value("geometry/height").toInt(); + // set.value("geometry/visiable",isVisible()); + Qps::flag_show = true; + setGeometry(x, y, w, h); + + if (set.value("font/name") != QVariant() and + set.value("font/size") != QVariant()) + { + QString fname = set.value("font/name").toString(); + int fsize = set.value("font/size").toInt(); // if not exist then 0 + QFont font; + font.setFamily(fname); + font.setPointSize(fsize); + QApplication::setFont(font); + } + + // Field + procview->cats.clear(); + QStringList sl = set.value("field").toStringList(); + for (int i = 0; i < sl.size(); i++) + { + QString str = sl[i]; + procview->addField(str.toUtf8().data()); + } + /// procview->saveCOMMANDFIELD(); // TMP + + // procview->idxF_CMD=set.value("F_CMD").toInt(); + + if (sl.size() == + 0) // *** for safe , if there is no field. this happen when no qpsrc + { + procview->viewfields = Procview::USER; + procview->set_fields(); /// + } + else + { + procview->viewfields = Procview::CUSTOM; + } + + QString str = set.value("sort/field").toString(); // procview->sortcat->name + int fid = procview->field_id_by_name(str.toUtf8().data()); + int col = procview->findCol(fid); + if (col >= 0) + // pstable->setSortColumn(col); // Pstable::refresh() + procview->setSortColumn(col); // Pstable::refresh() + // pstable -> procview + procview->reversed = set.value("sort/reversed").toBool(); // tmp + + sl = set.value("flags").toStringList(); + // for(int i=0;iname; fs++) + { + if (sl.contains(fs->name)) + *(fs->var) = true; + else + *(fs->var) = false; + } + // procview->treeview=set.value("treeview").toBool(); + + int i = set.value("interval").toInt(); + set_update_period(i); + i = set.value("viewproc").toInt(); + procview->viewproc = i; + ctrlbar->view->setCurrentIndex( + i); // procview->viewproc=set.value("viewproc").toInt(); + + int size = set.beginReadArray("watchdog"); + for (int i = 0; i < size; i++) + { + set.setArrayIndex(i); + watchCond *w = new watchCond; + w->putstring(set.value("cat").toString()); + watchlist.append(w); + } + set.endArray(); + size = set.beginReadArray("commands"); + for (int i = 0; i < size; i++) + { + set.setArrayIndex(i); + Command *c = new Command; + c->putString(set.value("cmd").toString()); + commands.append(c); + } + set.endArray(); + + return true; + /* + } else if(strcmp(buf, "swaplim") == 0) { + swaplimit = atoi(p); + swaplim_percent = false; + if(swaplimit < 0) { + swaplimit = -swaplimit; + swaplim_percent = true; + } + fclose(f); */ + return true; +} + +// USING : +// write geometry, visible fields and other settings to $HOME/.qps-settings +void Qps::write_settings() // save setting +{ + char path[512]; + if (settings_path(path) == NULL) + { + return; + } + + QSettings set(path, QSettings::NativeFormat); + + set.setValue("version", QPS_FILE_VERSION); + // set.beginGroup(""); + // set.endGroup(); + set.setValue("geometry/x", + geometry().x()); // if hide then, wrong value saved!! + set.setValue("geometry/y", geometry().y()); + set.setValue("geometry/width", geometry().width()); + set.setValue("geometry/height", geometry().height()); + set.setValue("geometry/visiable", + isVisible()); // isVisible() ? "show":"hide"); + set.setValue("viewproc", + procview->viewproc); // All, your , running process... + /// set.setValue("treeview",procview->treeview); + + QStringList sl; + // printf("procview cats.size=%d\n",procview->cats.size()); + procview->update_customfield(); + set.setValue("field", procview->customfields); + // set.setValue("F_CMD",procview->idxF_CMD); + + if (procview->sortcat) // sometimes, sortcat==NULL. + { + set.setValue("sort/field", procview->sortcat->name); + set.setValue("sort/reversed", procview->reversed); + } + // return; + sl.clear(); + for (Sflagvar *fs = flagvars; fs->name; fs++) + { + if (*(fs->var)) + sl.append(fs->name); // fprintf(f, " %s", fs->name); + } + set.setValue("flags", sl); + + // fprintf(f, "swaplim: %d\n" "interval: %d\n", + // swaplim_percent ? -swaplimit : + // swaplimit, + // update_period); + + sl.clear(); + set.setValue("font/name", QApplication::font().family()); + set.setValue("font/size", QApplication::font().pointSize()); + + set.setValue("interval", update_period); + + set.beginWriteArray("watchdog"); + for (int i = 0; i < watchlist.size(); i++) + { + set.setArrayIndex(i); + set.setValue("cat", watchlist[i]->getstring()); + } + set.endArray(); + + set.beginWriteArray("commands"); + for (int i = 0; i < commands.size(); i++) + { + set.setArrayIndex(i); + set.setValue("cmd", commands[i]->getString()); + } + set.endArray(); + + /* + fprintf(f, "hidden_process:"); + for(int i = 0; i < hidden_process.size(); i++) { + if(i!=0) fprintf(f,","); + fprintf(f,"%s",(const char *)hidden_process[i]); + } + */ + + /// printf("Qps: setting saved !\n"); +} + +// set the window_group hint to that of the main (qps) window +// DELETE ? -> need function , why? +void Qps::setWindowGroup(QWidget *w) +{ + /* + XWMHints wmh; + + wmh.flags = WindowGroupHint; + wmh.window_group = handle(); + XSetWMHints(w->x11Display(), w->handle(), &wmh); + */ +} + +// DEL +void Qps::setCommand(int argc, char **argv) +{ + // bug: argv[0] should really be frobbed into an absolute path name here + /// XSetCommand(x11Display(), handle(), argv, argc); +} + +// return host name with domain stripped +QString short_hostname() +{ + //// int gethostname(char *name, size_t len); // hyun? + struct utsname u; + uname(&u); + char *p = strchr(u.nodename, '.'); + if (p) + *p = '\0'; + QString s(u.nodename); + return s; +} + +bool opt_eq(const char *arg, const char *opt) +{ + if (arg[0] == '-') + arg++; + if (arg[0] == '-') + arg++; + + return strncmp(arg, opt, strlen(opt)) == 0; +} + +// print some help to stdout and exit +void print_help(char *cmdname) +{ + fprintf(stderr, "Usage: qps [options]\n" + "Options:\n" + " -version\t\tversion\n" + " -mini \t\tstart Minimized\n"); +} + +/// #include +QByteArray geo; +void Qps::clicked_trayicon() +{ + // QT 4.5 bug : hide(), then show() window's pos will be changed a + // little. + if (isMinimized() or (isVisible() == false)) // if hidden + { + // printf("showNormal\n"); + // show(); // works.. + showNormal(); // works.. + return; + } + + // METACITY- focus stealing prevention : XRaiseWindow() not work. + // Compiz works well !! + if (isActiveWindow() == false) // if lower than other windows + { + raise(); + activateWindow(); // for WindowMaker + return; + } + hide(); +} + +void Qps::clicked_trayicon(QSystemTrayIcon::ActivationReason r) +{ + // printf("ActivationReason = %d\n",r); + if (r == QSystemTrayIcon::Trigger) + { + if (!isHidden()) + { + hide(); + } + else + { + showNormal(); + } + } + if (r == QSystemTrayIcon::Context) + { + } +} + +// +void Qps::hideEvent(QHideEvent *event) +{ + // printf("hideEvent()\n"); + if (trayicon->hasSysTray()) + { + // event->accept(); + } + // geo=saveGeometry(); +} + +// MOD!!!: For systray update. +// this trick very suck, but I can't find a better solution. +class QpsApp : public QApplication +{ + public: + QpsApp(int &argc, char **argv) : QApplication(argc, argv){}; + void commitData(QSessionManager &sm); + void saveState(QSessionManager &manager); + + /* + virtual bool x11EventFilter ( XEvent *xev ){ + // catch X11 event for systray_update !! which event? + ///if(trayicon!=NULL) return + trayicon->checkNewTrayEvent(xev); + return false; // events to qt. + }; */ +}; + +#include +void QpsApp::saveState(QSessionManager &manager) +{ + // printf("saveState()\n"); + // manager.setRestartHint(QSessionManager::RestartIfRunning); + // manager.release(); +} + +// this is called when X Logout +// closeEvent() never called !!! +void QpsApp::commitData(QSessionManager &manager) +{ + /* + printf("commitData()\n"); + manager.setRestartHint(QSessionManager::RestartIfRunning); + qps->flag_exit=true; // ready to Logout + qps->save_settings() ; + manager.release(); + sleep(2); + return; + if (manager.allowsInteraction()) { + int ret = QMessageBox::warning( + qps, + tr("My Application"), + tr("Save changes to document?"), + QMessageBox::Save | QMessageBox::Discard | + QMessageBox::Cancel); + + switch (ret) { + case QMessageBox::Save: + manager.release(); + // if (!saveDocument()) manager.cancel(); + break; + case QMessageBox::Discard: + break; + case QMessageBox::Cancel: + default: + manager.cancel(); + } + } else { + + manager.release(); + + // we did not get permission to interact, then + // do something reasonable instead + } + */ + /* + //DEL sm.release(); + qDebug("Qps: Session saved\n"); + // sm.cancel(); + //sm.setRestartHint (QSessionManager::RestartIfRunning); + QApplication::commitData(sm); + */ +} + +#include +void signal_handler(int sig) +{ + // if(sig==SIGINT) printf("DEBUG: catched SIGINT \n"); + // if(sig==SIGTERM)printf("DEBUG: catched SIGTERM \n"); + qps->save_quit(); + // printf("Qps: suiciding.... wait \n"); + printf("Qps: terminating...\n"); +} + +int main(int argc, char **argv, char **envp) +{ + signal(SIGTERM, signal_handler); + signal(SIGINT, signal_handler); + signal(SIGKILL, signal_handler); + + Lookup::initproctitle(argv, envp); // will be REMOVEd + + for (int i = 1; i < argc; i++) + { + if (opt_eq(argv[i], "version")) + { + fprintf(stderr, + "qps version " QPS_VERSION ", using Qt library %s\n", + qVersion()); + exit(1); + } + else if (opt_eq(argv[i], "help") || opt_eq(argv[i], "h")) + { + print_help(argv[0]); + exit(1); + } + else if (opt_eq(argv[i], "session") or opt_eq(argv[i], "sm")) + { + flag_session_start = true; + sleep(2); // **** important !! maybe systray runs later + } + else if (opt_eq(argv[i], "min")) // mini + { + flag_start_mini = true; + } + else if (strstr(argv[i], "screen")) + { + // int screenshot_main(int argc, char **argv); + // screenshot_main(argc,argv); + // return 1; + } + } + // codec = QTextCodec::codecForLocale(); // for Local locale + check_system_requirement(); // check kernel version.. etc in proc.cpp + check_qps_running(); // check already qps running. in misc.cpp + + QpsApp app(argc, argv); + + init_misc(0); // init misc, some test code runs ... + qps = new Qps(); + // sleep(20); + + QString caption = ""; + caption.append(getenv("USER")); + caption.append("@"); + caption.append(short_hostname()); // geteuid() + + qps->setWindowTitle(UniString(caption)); + qps->setWindowIcon(QPixmap((const char **)icon_xpm)); + + // MOVETO Systray + QMenu *menu = new QMenu(qps); + /// menu->addAction( UniString("About"), qps, SLOT(about()) ); + menu->addAction("Show", qps, SLOT(showNormal())); + menu->addAction("Hide", qps, SLOT(hide())); + menu->addSeparator(); + if (flag_devel) + menu->addAction("ScreenShot", qps, SLOT(start_screenshot())); + menu->addAction("&Quit", qps, SLOT(save_quit()), Qt::ALT + Qt::Key_Q); + + trayicon = new TrayIcon(QPixmap((const char **)icon_xpm /* init icon */), + "qps", menu); + QObject::connect(trayicon, SIGNAL(clicked(const QPoint &)), qps, + SLOT(clicked_trayicon())); + QObject::connect(trayicon, SIGNAL(doubleClicked(const QPoint &)), qps, + SLOT(clicked_trayicon())); + QObject::connect(trayicon, + SIGNAL(activated(QSystemTrayIcon::ActivationReason)), qps, + SLOT(clicked_trayicon(QSystemTrayIcon::ActivationReason))); + + trayicon->sysInstall(); // ok + + if (flag_session_start or flag_start_mini) + { + if (trayicon->hasSysTray()) + { + qps->hide(); // qps->setHidden(true); + } + else + { + qps->showMinimized(); + } + } + else + { + qps->show(); + } + + // Testing + // app.setStyleSheet(" QToolTip { opacity: 100;" "border-width: + // 1px; + // border-style: solid; border-color: rgb(50,110,80); border-radius: + // 4px ;" + //"background-color : rgba(0,0,0); padding: 3px; color: rgb(0,255,150); + //}"); + QString str; + str.append("Qps "); + str.append(QPS_VERSION); + str.append(" launched."); + AddLog(str); + + return app.exec(); +} + +#include +#include +#include +#include +#include + +void Qps::test_popup(const QUrl &link) +{ + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(link.toString(), QClipboard::Clipboard); + popupx->popup(QCursor::pos()); +} + +void Qps::about() +{ + QDialog *diag = new QDialog(this); + diag->setWindowTitle("About"); + QVBoxLayout *lay = new QVBoxLayout(diag); + + QLabel *label = new QLabel(diag); + // label->setMinimumSize( 400,200 ); + label->setOpenExternalLinks(true); + // QLabel::setOpenExternalLinks ( bool open ); + QTextBrowser *browser = new QTextBrowser(diag); + browser->setOpenExternalLinks(false); + browser->setOpenLinks(false); + + connect(browser, SIGNAL(anchorClicked(const QUrl &)), this, + SLOT(test_popup(const QUrl &))); + + lay->addWidget(label); + lay->addWidget(browser); + + // QDesktopServices::openUrl(QUrl("file:///", QUrl::TolerantMode)); + // + + QString str("

qps " QPS_VERSION " - A Visual Process Manager

" +#ifdef SOLARIS +#ifdef _LP64 + "64-bit " +#else + "32-bit " +#endif + "Solaris version " +#endif // SOLARIS + "using Qt library "); + + str.append(qVersion()); + // s.append(url); + str.append("" + "
" + "http://kldp.net/projects/" + "qps"); + label->setText(str); + + str = ""; + str.append("
Bug, Complains, Opinion, Patch to
" + "
" + "daehyun.yang@gmail.com
" + "
" + "
Contributors
" + "
Olivier.Daudel@u-paris10.fr
" + "
jsanchez@todounix.homeip.net
" + "
\ + Vince Schiavoni (hlingler@verizon.net)\ +
" + "
" + "
Original Qps by
" + "
" + " Mattias Engdeg?rd\n" //"(f91-men@nada.kth.se)\n" + "
"); + + // QPushButton *contribut = new QPushButton(tr("&More")); + + // moreButton->setCheckable(true); + // moreButton->setAutoDefault(false); + + browser->setText(str); + + QDialogButtonBox *bbox = + new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, diag); + + lay->addWidget(bbox); + + connect(bbox, SIGNAL(accepted()), diag, SLOT(accept())); + + diag->exec(); + + // mb.setIconPixmap(QPixmap((const char **)icon_xpm)); +} + +void Qps::license() // -> help() +{ + + QDialog *diag = new QDialog(this); + diag->setWindowTitle("Quick Help"); + // diag->setSizeGripEnabled(true) ; + QHBoxLayout *lay = new QHBoxLayout(diag); + + QTextBrowser *browser = new QTextBrowser(diag); + browser->setMinimumSize(400, 200); + + lay->addWidget(browser); + + // QDesktopServices::openUrl(QUrl("file:///", QUrl::TolerantMode)); + // QLabel::setOpenExternalLinks ( bool open ); + browser->setOpenExternalLinks(true); + browser->setOpenLinks(true); + browser->setText( + "

QPS Help

" + "Updated: May 24 2005
" + "http://kldp.net/projects/" + "qps
" + + "" + " " + " " + " Quit" + " " + " " + " " + " " + " Update" + " " + " " + " " + " " + "" + " " + " " + "
  CTRL + q , CTRL + x" + "
 Space , Enter " + "
process Terminate ALT + T , DELETE
process Kill ALT + K
"); + diag->exec(); +} + +// slot +void Qps::start_screenshot() {} + +// MOVETO qps::keyPressEvent() +void SearchBox::keyPressEvent(QKeyEvent *e) +{ + if (e->key() == Qt::Key_Delete) + { + qps->sig_term(); + return; + } + + if (e->key() == Qt::Key_Escape) + { + + clear(); // clear searchbox + + qps->pstable->clearAllSelections(); // TEMP + + // xb->setDown(true); + // event_xbutton_clicked(); + // xb->setDown(false); + } + else + QLineEdit::keyPressEvent(e); + + qps->procview->filterstr = text(); + qps->pstable->refresh(); /// qps->refresh(); // better ? +} + +void STATUSBAR_COUNT_UPDATE() {} + +void STATUSBAR_SETCOUNT(int n) +{ + if (qps) + qps->statusBar->update(n); +} + +void PSTABLE_SETTREEMODE(bool mode) +{ + if (qps) + qps->pstable->setTreeMode(mode); +} + +void QPS_SHOW() +{ + if (qps) + qps->showNormal(); +} + +// TESTING +int QPS_PROCVIEW_CPU_NUM() +{ + if (qps) + return qps->procview->num_cpus; + else + return 0; +} + +void AddLog(QString str) +{ + qDebug() << qps; + if (qps && qps->logbox) + { + qps->logbox->append(str); + } +} diff --git a/src/qps.h b/src/qps.h new file mode 100644 index 0000000..fa13d3f --- /dev/null +++ b/src/qps.h @@ -0,0 +1,318 @@ +// qps.h emacs, this is written in -*-c++-*- +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegrd, 1997-1999 + +#ifndef QPS_H +#define QPS_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#if QT_VERSION >= 0x040200 +#else +//#error Qt library version 4.2 or higher is needed for this version of qps +#endif + +#define HTABLE1 +#include "misc.h" +#ifdef HTABLE2 +#include "pstable2.h" +#else +#include "pstable.h" +#endif +#include "proc.h" +#include "infobar.h" +#include "fieldsel.h" +#include "details.h" +#include "prefs.h" +#include "command.h" + +#define DEBUG printf + +#define FUNC_START +#define FUNC_END + +#include +class Qps : public QWidget +{ + Q_OBJECT + public: + Qps(); + ~Qps(); + + void set_update_period(int milliseconds); + void setWindowGroup(QWidget *w); + void setCommand(int argc, char **argv); + void setIconSize(int w, int h) { icon_width = w, icon_height = h; } + void write_settings(); + void save_settings(); + + QPixmap *get_load_icon(); + + // settings + static bool show_file_path; // shows command/file map path + static bool show_cmd_path; // DEL CMDLINE shows command path + static bool show_infobar; // information bar is shown + static bool show_ctrlbar; // control bar is shown + static bool show_statusbar; // + static bool show_mem_bar; // memory is shown as a bar + static bool show_swap_bar; // swap is shown as a bar + static bool show_cpu_bar; // cpu states are shown as a bar + static bool show_load_graph; // load is shown as a graph + static bool load_in_icon; // load graph in icon + static bool auto_save_options; // settings saved on exit +#ifdef LINUX + static bool hostname_lookup; // do a name lookup on socket addresses + static bool service_lookup; // map port numbers to service names +#endif + static bool pids_to_selection; // selected processes' PIDS to X11 sel + static bool vertical_cpu_bar; // save space with SMP + + static int swaplimit; // limit of swap redline, in K or % + static bool swaplim_percent; // true if swaplimit is in % +#ifdef SOLARIS + static bool + normalize_nice; // whether nice should be normalized (normal nice + // is 0 etc) + static bool use_pmap; // use /proc/bin/pmap for map names +#endif + static bool tree_gadgets; // use open/close gadgets (triangles) + static bool tree_lines; // draw tree branch lines + + // static bool comm_is_magic; //DEL, auto-remove COMM + // when + // switching + static bool flag_systray; // trayicon , sytemtray + static bool flag_exit; + static bool flag_show; // last qps-main_window state + static bool flag_useTabView; + static bool flag_qps_hide; + + // colors which may be set by the user + enum + { + COLOR_CPU_USER, +#ifdef LINUX + COLOR_CPU_NICE, +#endif + COLOR_CPU_SYS, +#ifdef SOLARIS + COLOR_CPU_WAIT, +#endif + COLOR_CPU_IDLE, + COLOR_MEM_USED, + COLOR_MEM_BUFF, + COLOR_MEM_CACHE, + COLOR_MEM_FREE, + COLOR_SWAP_USED, + COLOR_SWAP_FREE, + COLOR_SWAP_WARN, + COLOR_LOAD_BG, + COLOR_LOAD_FG, + COLOR_LOAD_LINES, + COLOR_SELECTION_BG, + COLOR_SELECTION_FG, + NUM_COLORS + }; + + enum menuid + { + MENU_SIGQUIT, + MENU_SIGILL, + MENU_SIGABRT, + MENU_SIGFPE, + MENU_SIGSEGV, + MENU_SIGPIPE, + MENU_SIGALRM, + MENU_SIGUSR1, + MENU_SIGUSR2, + MENU_SIGCHLD, + MENU_SIGCONT, + MENU_SIGSTOP, + MENU_SIGTSTP, + MENU_SIGTTIN, + MENU_SIGTTOU, + MENU_RENICE, + MENU_SCHED, + MENU_DETAILS, + MENU_PARENT, + MENU_CHILD, + MENU_DYNASTY, + MENU_SIGTERM, + MENU_SIGHUP, + MENU_SIGINT, + MENU_SIGKILL, + MENU_OTHERS, + MENU_PROCS, + MENU_FIELDS, + MENU_CUSTOM, + MENU_PATH, + MENU_INFOBAR, + MENU_CTRLBAR, + MENU_CUMUL, + MENU_PREFS, + MENU_ADD_HIDDEN, + MENU_REMOVE_HIDDEN, + MENU_STATUS, +#ifdef MOSIX + POPUP_MIGRATE, +#endif + MENU_FIRST_COMMAND // must be last + }; + + public slots: + void start_screenshot(); + void clicked_trayicon(QSystemTrayIcon::ActivationReason); + void clicked_trayicon(); + void sig_term(); + void sig_hup(); + void sig_stop(); + void sig_cont(); + void sig_kill(); + void signal_menu(QAction *); + void run_command(QAction *); + void about(); + void license(); + void menu_update(); + void menu_toggle_path(); + void menu_toggle_infobar(); + void menu_toggle_ctrlbar(); + void menu_toggle_statusbar(); + void menu_toggle_cumul(); + void menu_prefs(); + void menu_renice(); + void menu_sched(); + void Action_Detail(); + void menu_parent(); + void menu_children(); + void menu_dynasty(); + void menu_custom(); + void menu_remove_field(); + void menu_edit_cmd(); + void mig_menu(int id); // MOSIX only + void make_command_menu(); + void view_menu(QAction *); + void save_quit(); + void add_fields_menu(int id); + void add_fields_menu(QAction *act); + void tabChanged(int i); + + void show_popup_menu(QPoint p); + void context_heading_menu(QPoint p, int col); + + void update_menu_selection_status(); + void field_added(int index); + void field_removed(int index); + void set_table_mode(bool treemode); // hmmm + + void details_closed(Details *d); + void open_details(int row); + + void config_change(); + + void update_timer(); + void refresh(); + void refresh_details(); + void test_popup(const QUrl &link); + void update_menu_status(); + + protected: + // reimplementation of QWidget methods + virtual void timerEvent(QTimerEvent *); + virtual void closeEvent(QCloseEvent *); + virtual void moveEvent(QMoveEvent *event); + virtual void mouseMoveEvent(QMouseEvent *event); + virtual void hideEvent(QHideEvent *event); + virtual void resizeEvent(QResizeEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void enterEvent(QEvent *event); + virtual void leaveEvent(QEvent *event); + virtual void showEvent(QShowEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + // bool event(QEvent *e); + void transfer_selection(); + void adjust_popup_menu(QMenu *m, bool cont); + void adjust_popup_menu(); + bool all_selected_stopped(); + + void locate_relatives(int Procinfo::*a, int Procinfo::*b); + QMenu *make_signal_popup_menu(); +#ifdef MOSIX + QMenu *make_migrate_menu(); + void migrate_selected(int sig); +#endif + void send_to_selected(int sig); + void sendsig(Procinfo *p, int sig); + void earlier_refresh(); + bool read_settings(); + + bool load_settings(); + + void bar_visibility(); + void set_default_icon(); + void set_load_icon(); + + public: +#ifdef HTABLE2 + Pstable2 *pstable; // test +#else + Pstable *pstable; +#endif + Procview *procview; + StatusBar *statusBar; + // Netable *netable; + QTextEdit *logbox; + void update_icon(); + + private: + QMenuBar *menubar; + QMenu *m_field; // Field Menu + QMenu *m_view; // Field Menu + QAction *am_view; + QMenu *m_command; + QMenu *m_options; + QMenu *m_popup; + QMenu *m_headpopup; + QMenu *m_fields; + QMenu *popupx; // testing + + bool selection_items_enabled; // state of certain menu items (???) + + Infobar2 *infobar; // tmp + ControlBar *ctrlbar; + FieldSelect *field_win; + Preferences *prefs_win; + CommandDialog *command_win; + + QList
details; + + int update_period; // in mili second + static const int eternity = 0x7fffffff; // turns off automatic update + // the Linux kernel updates its load average counters every 5 seconds + // (kernel 2.0.29), so there is no point in reading it too often + static const int load_update_period = 4800; // in ms + int update_load_time; // time left to update load + + // size of our icon + int icon_width; + int icon_height; + int timer_id; + + QPixmap *default_icon; + bool default_icon_set; // true if default icon is current icon + int context_col; // where heading context menu was clicked +}; + +#endif // QPS_H diff --git a/src/qps.qrc b/src/qps.qrc new file mode 100644 index 0000000..944525c --- /dev/null +++ b/src/qps.qrc @@ -0,0 +1,11 @@ + + + icon/letters.png + icon/vista.png + icon/vpointer.png + icon/vcross.png + icon/pause.png + icon/superman.png + icon/preferences-system.png + + diff --git a/src/qrc_qps.cpp b/src/qrc_qps.cpp new file mode 100644 index 0000000..ad9944e --- /dev/null +++ b/src/qrc_qps.cpp @@ -0,0 +1,1205 @@ +/**************************************************************************** +** Resource object code +** +** Created by: The Resource Compiler for Qt version 4.8.6 +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include + +static const unsigned char qt_resource_data[] = { + // /home/hyun/qps/icon/vcross.png + 0x0, 0x0, 0x2, 0x90, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, + 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, + 0x61, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, + 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, + 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, + 0x45, 0x7, 0xd8, 0x9, 0x5, 0xc, 0x1e, 0x19, 0xb1, 0x47, 0xc8, 0xda, + 0x0, 0x0, 0x2, 0x10, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0xb5, 0x93, + 0x4d, 0x68, 0x13, 0x51, 0x14, 0x85, 0xbf, 0x99, 0x69, 0xfe, 0x25, 0x49, + 0xdb, 0xa4, 0xc6, 0x46, 0x44, 0x13, 0x3, 0x65, 0x28, 0x26, 0x6d, 0x35, + 0x20, 0xa5, 0x34, 0x56, 0xb3, 0xd0, 0xa0, 0x2d, 0x6e, 0xec, 0xce, 0x85, + 0x20, 0x5, 0xdd, 0x15, 0x17, 0x82, 0xd8, 0x4d, 0x6b, 0x95, 0xe8, 0x46, + 0x37, 0x6e, 0x74, 0x23, 0x82, 0x9b, 0xe2, 0x42, 0x5b, 0x23, 0x2d, 0x45, + 0x41, 0x4, 0x5d, 0x74, 0xa1, 0x42, 0xc8, 0xc6, 0x14, 0x2c, 0xa1, 0xa5, + 0x51, 0x26, 0x94, 0xb4, 0xf9, 0x99, 0xcc, 0xb8, 0xc9, 0xe8, 0x88, 0xa, + 0x1, 0xf1, 0xc2, 0xe5, 0x3d, 0x78, 0xef, 0x9e, 0x7b, 0x39, 0xf7, 0x1c, + 0xf8, 0x5f, 0x11, 0x6d, 0x2c, 0x4a, 0xad, 0xfc, 0x13, 0x4d, 0xa7, 0x91, + 0x16, 0x0, 0xb5, 0x58, 0x72, 0x2, 0x82, 0x39, 0xb3, 0x7e, 0x8f, 0x35, + 0xeb, 0xf7, 0x58, 0x4d, 0xf5, 0x6d, 0x3f, 0x80, 0x62, 0x99, 0xd9, 0x5a, + 0xf4, 0xe5, 0xcd, 0x6, 0xd0, 0x7, 0xec, 0x36, 0x77, 0xd1, 0xfd, 0x1e, + 0x9, 0x20, 0xba, 0x78, 0x4b, 0x95, 0x97, 0x6f, 0x37, 0xae, 0xb8, 0xec, + 0x5d, 0x46, 0x9d, 0x31, 0x81, 0x5e, 0xb1, 0x5b, 0x25, 0xbb, 0xb2, 0xb5, + 0x2, 0xec, 0x1, 0x8e, 0x9a, 0x1, 0x84, 0xcd, 0x52, 0x43, 0xf7, 0x7b, + 0x9c, 0xb6, 0xed, 0xea, 0x27, 0x41, 0xd3, 0xf4, 0x74, 0xb9, 0x92, 0x0, + 0xf6, 0x3, 0x9a, 0x31, 0x82, 0xa0, 0xeb, 0xc8, 0x7, 0xce, 0xa7, 0xb7, + 0x5d, 0xf, 0x26, 0x97, 0x94, 0x40, 0x7b, 0x78, 0x25, 0x75, 0x6d, 0x18, + 0xf8, 0x2, 0x28, 0xf2, 0x72, 0x5a, 0x19, 0xd9, 0x2c, 0xa9, 0xef, 0x47, + 0xaf, 0xfb, 0xf6, 0x8a, 0xe2, 0x9, 0xa0, 0x1, 0x78, 0xcd, 0x1c, 0x68, + 0xb9, 0x63, 0x93, 0xb9, 0x27, 0x95, 0xda, 0xfa, 0xb7, 0xa0, 0x2f, 0x54, + 0xde, 0xe5, 0x10, 0xdc, 0xe7, 0x86, 0xbb, 0x81, 0x2e, 0x20, 0xa2, 0xb6, + 0x49, 0x94, 0x1d, 0x56, 0x1, 0x88, 0xaf, 0x69, 0x5a, 0xd, 0x58, 0x5, + 0xa, 0x6, 0x9, 0x22, 0xa0, 0x35, 0x99, 0xd7, 0x75, 0x70, 0xe4, 0xa4, + 0xa4, 0xe3, 0xc8, 0xf3, 0x69, 0xa5, 0x16, 0xe, 0xce, 0x49, 0x43, 0xbd, + 0x67, 0xf3, 0x8f, 0x96, 0x52, 0x8d, 0x87, 0x99, 0x2a, 0x50, 0x2, 0x3e, + 0x3, 0xa, 0xa0, 0xff, 0x69, 0x75, 0xa2, 0x5c, 0xcd, 0x58, 0x1, 0x71, + 0x22, 0x12, 0x8c, 0xf, 0x3e, 0x9d, 0x52, 0x7, 0xba, 0x3b, 0x92, 0xc0, + 0x19, 0x20, 0xe6, 0x8c, 0xf7, 0xb8, 0x5b, 0xd2, 0x40, 0xd6, 0xef, 0x71, + 0xd, 0x64, 0x66, 0xeb, 0x89, 0x8b, 0xa7, 0xd2, 0xf2, 0xc2, 0xcc, 0x6a, + 0x40, 0x14, 0x2e, 0x0, 0x72, 0x4b, 0xc5, 0xb1, 0x17, 0x37, 0xd4, 0xa1, + 0xb9, 0x29, 0x35, 0xe2, 0x73, 0x1f, 0x7, 0xc6, 0xbd, 0x13, 0xa7, 0x3b, + 0x4f, 0xde, 0xbb, 0xfc, 0xb6, 0x7f, 0x7e, 0xba, 0xa, 0x8c, 0x1, 0x1d, + 0x26, 0xee, 0x7e, 0x5e, 0x8c, 0x88, 0xe4, 0xd7, 0x3f, 0xaa, 0x82, 0xa0, + 0x15, 0xa2, 0x61, 0x19, 0x78, 0xa3, 0xdc, 0x7f, 0xf6, 0x75, 0x23, 0x14, + 0x90, 0x4b, 0x6e, 0x97, 0x5, 0x28, 0x36, 0x57, 0x7c, 0xf0, 0xb7, 0xce, + 0xfd, 0xb, 0x33, 0x6a, 0xf2, 0xf1, 0xd5, 0x77, 0x40, 0x10, 0x18, 0x7, + 0x12, 0xe6, 0xf7, 0x94, 0xcd, 0x12, 0x1a, 0xb9, 0x7b, 0xa9, 0x10, 0x79, + 0x7d, 0x67, 0x47, 0x70, 0xda, 0x46, 0x81, 0x43, 0xbf, 0x4c, 0x60, 0xaf, + 0xd6, 0xb5, 0x62, 0xa7, 0xfb, 0x30, 0x10, 0x2, 0x76, 0x80, 0x57, 0x66, + 0x80, 0xf9, 0x6a, 0x3d, 0xbf, 0xd1, 0xb3, 0xaf, 0xdd, 0x56, 0xa9, 0x59, + 0x9c, 0x83, 0xbd, 0x5e, 0xc0, 0x43, 0x53, 0xe3, 0xe6, 0xe8, 0x6b, 0x2a, + 0x71, 0xd, 0xf8, 0xf0, 0x37, 0x9f, 0x35, 0xfd, 0x92, 0x3, 0xb6, 0xfe, + 0xd9, 0xb5, 0xdf, 0x1, 0x18, 0x93, 0xa5, 0x78, 0x64, 0xd8, 0xcb, 0x95, + 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82, + // /home/hyun/qps/icon/vpointer.png + 0x0, 0x0, 0x2, 0x94, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x10, + 0x0, 0x0, 0x0, 0x10, 0x8, 0x6, 0x0, 0x0, 0x0, 0x1f, 0xf3, 0xff, + 0x61, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, + 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, + 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, + 0x45, 0x7, 0xd8, 0x7, 0x10, 0x13, 0x18, 0x20, 0x77, 0x95, 0x1a, 0xd5, + 0x0, 0x0, 0x2, 0x14, 0x49, 0x44, 0x41, 0x54, 0x38, 0xcb, 0x75, 0x93, + 0xbf, 0x6b, 0x14, 0x41, 0x14, 0xc7, 0x3f, 0x33, 0x3b, 0x7b, 0x7b, 0xc6, + 0xe4, 0xbc, 0xf3, 0x7, 0xa2, 0x46, 0x90, 0x44, 0x44, 0xb4, 0x8, 0x36, + 0xa6, 0xb6, 0x4a, 0x63, 0x2b, 0x39, 0x82, 0xc5, 0xd9, 0x88, 0x6, 0x3d, + 0xb4, 0x10, 0x11, 0xfc, 0x17, 0xd2, 0x5, 0x44, 0xb4, 0x38, 0xb1, 0xd0, + 0x10, 0xb8, 0x42, 0x2c, 0x22, 0x91, 0x13, 0x2c, 0xd, 0xd8, 0x44, 0x5, + 0x51, 0x24, 0xc9, 0x36, 0xb7, 0x7a, 0x4a, 0x2e, 0xa7, 0xc9, 0x5d, 0x32, + 0xbb, 0x63, 0xe1, 0x4c, 0xb2, 0x2e, 0xfa, 0x60, 0x19, 0xf6, 0xbd, 0xef, + 0xfb, 0xbe, 0xef, 0xbc, 0xf7, 0x46, 0xf0, 0xc7, 0x24, 0x3b, 0xe6, 0x1, + 0x7b, 0x80, 0x0, 0x50, 0x36, 0x16, 0xdb, 0xaf, 0x3, 0xfc, 0x2, 0x8c, + 0xf5, 0xb, 0x65, 0x93, 0x84, 0x75, 0x1a, 0xb, 0x94, 0x80, 0x2c, 0x97, + 0xcb, 0x67, 0x85, 0x10, 0x26, 0x8a, 0xa2, 0xa8, 0xd1, 0x68, 0x2c, 0xdb, + 0x98, 0xc9, 0xe4, 0x6c, 0x2b, 0xf0, 0xec, 0xe9, 0x3, 0x7, 0xd5, 0xd0, + 0xa1, 0xa3, 0x23, 0xf1, 0xbc, 0x19, 0x89, 0xe7, 0x4d, 0xe5, 0xce, 0xcd, + 0x6b, 0xc0, 0x20, 0xd0, 0xef, 0xc8, 0xad, 0x42, 0x3f, 0x2d, 0x9d, 0x14, + 0x63, 0xb2, 0xeb, 0xc4, 0xe0, 0x80, 0x73, 0xf6, 0x96, 0x9a, 0x4d, 0x20, + 0x9f, 0xb9, 0x6a, 0x2, 0x24, 0x2a, 0x93, 0xbc, 0x2d, 0xab, 0x33, 0xb7, + 0xd0, 0x2d, 0x8e, 0x4f, 0x9f, 0x3b, 0xec, 0xf7, 0xed, 0xd7, 0x5a, 0x2b, + 0xab, 0x50, 0x67, 0xf0, 0x46, 0x66, 0x58, 0xdd, 0xdd, 0x0, 0xf4, 0xea, + 0x93, 0xc9, 0x57, 0x1f, 0x1e, 0x55, 0x66, 0xdd, 0x7f, 0x2a, 0xe6, 0x14, + 0x18, 0xc7, 0xac, 0x52, 0x4e, 0x47, 0x98, 0x43, 0x4a, 0x88, 0x13, 0xa4, + 0x94, 0xce, 0xdf, 0xef, 0x12, 0x1d, 0x4e, 0x1, 0xaa, 0x54, 0x2a, 0xe5, + 0xb5, 0xd6, 0xb9, 0x4e, 0xa7, 0x93, 0x73, 0xe3, 0x1, 0x36, 0x4f, 0x4e, + 0x3c, 0x1c, 0xd7, 0x5a, 0xab, 0x81, 0x52, 0x69, 0xaf, 0xc5, 0xee, 0xb3, + 0x24, 0xb1, 0xc5, 0xf4, 0x14, 0x90, 0x8c, 0x8d, 0x8d, 0x15, 0x5a, 0xad, + 0xd6, 0x81, 0x6f, 0x2f, 0x6e, 0x2f, 0x0, 0x8c, 0x5c, 0x7a, 0x3c, 0xd1, + 0x6c, 0x36, 0x97, 0xbf, 0x3c, 0xa8, 0xcc, 0xe8, 0x9c, 0xe4, 0xcc, 0xe4, + 0xcc, 0x24, 0xc0, 0xb1, 0xb5, 0xe7, 0xef, 0x3, 0xdf, 0xe3, 0xfc, 0xdd, + 0x97, 0x37, 0xa6, 0xa6, 0xa6, 0xea, 0x40, 0x57, 0x1, 0x5b, 0x8b, 0x8b, + 0x8b, 0x5f, 0x57, 0x56, 0x56, 0x18, 0x2, 0xb6, 0x74, 0x8c, 0x10, 0xc2, + 0xb, 0xc3, 0xf0, 0xe7, 0x6e, 0x29, 0xf0, 0x62, 0x83, 0x31, 0x46, 0x0, + 0x4, 0xbe, 0x47, 0x3e, 0xa7, 0x68, 0xb5, 0x5a, 0x4b, 0xc0, 0x26, 0xd0, + 0x15, 0xa9, 0x3d, 0x28, 0xd9, 0xd, 0x14, 0xa7, 0x7a, 0x73, 0x9f, 0x7d, + 0xe5, 0x31, 0x7c, 0xe1, 0xde, 0xc5, 0x42, 0xa1, 0x50, 0x7c, 0x73, 0xbf, + 0x3c, 0x6d, 0xc, 0x8c, 0x5e, 0x79, 0x7a, 0xbd, 0x56, 0xab, 0x3d, 0xb3, + 0xf2, 0x7f, 0x0, 0x1b, 0x32, 0xd5, 0xbc, 0x36, 0xb0, 0xa, 0x74, 0x85, + 0x10, 0xac, 0xf7, 0xb6, 0xa8, 0xd7, 0xeb, 0xef, 0x3c, 0xcf, 0x4b, 0x12, + 0x63, 0xc8, 0xf9, 0x1e, 0x61, 0x18, 0xbe, 0xb6, 0x95, 0x23, 0x60, 0xdd, + 0xf6, 0xe2, 0x2f, 0xeb, 0x3, 0x8e, 0x0, 0xc3, 0xc0, 0x69, 0xb7, 0x89, + 0xd5, 0x6a, 0xf5, 0x16, 0x70, 0xdc, 0xc6, 0x8a, 0xe9, 0xd1, 0xab, 0xc, + 0x41, 0xf, 0xf8, 0xe, 0x14, 0xec, 0x78, 0x1, 0x68, 0xb7, 0xdb, 0x1f, + 0xed, 0x23, 0xda, 0xb0, 0x95, 0xcd, 0xff, 0x8, 0x9c, 0xa4, 0x36, 0x20, + 0x47, 0xaf, 0xce, 0x5e, 0xe, 0x82, 0xa0, 0xf8, 0x29, 0x8a, 0xde, 0x2, + 0x6b, 0x56, 0xbe, 0x4e, 0x13, 0x8, 0xfe, 0x6d, 0x22, 0xd5, 0xd4, 0x75, + 0x5b, 0x59, 0xdb, 0xd3, 0xa4, 0x81, 0xbf, 0x1, 0x8e, 0x94, 0xc6, 0xce, + 0x19, 0xec, 0xf4, 0x7f, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82, + // /home/hyun/qps/icon/letters.png + 0x0, 0x0, 0x2, 0xd0, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x2, 0x3a, + 0x0, 0x0, 0x0, 0x9, 0x4, 0x3, 0x0, 0x0, 0x0, 0xc9, 0x17, 0x9b, + 0xd9, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x12, 0x50, 0x4c, 0x54, 0x45, 0x0, 0x0, + 0x0, 0x0, 0x47, 0x3e, 0x0, 0x7d, 0x71, 0x51, 0xec, 0xb7, 0xff, 0xff, + 0xff, 0x0, 0xa3, 0x90, 0xc4, 0xf9, 0x56, 0xb4, 0x0, 0x0, 0x0, 0x1, + 0x74, 0x52, 0x4e, 0x53, 0x0, 0x40, 0xe6, 0xd8, 0x66, 0x0, 0x0, 0x0, + 0x1, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x88, 0x5, 0x1d, 0x48, 0x0, 0x0, + 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, + 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, + 0x49, 0x4d, 0x45, 0x7, 0xd9, 0x2, 0xb, 0xe, 0x3, 0x38, 0x5d, 0x95, + 0xf5, 0x71, 0x0, 0x0, 0x2, 0x2a, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, + 0xed, 0x56, 0x41, 0x8e, 0xe4, 0x20, 0xc, 0xb4, 0xb1, 0xb8, 0x13, 0x50, + 0xee, 0xad, 0xdd, 0xf, 0x40, 0xa3, 0xfc, 0x63, 0x46, 0xfb, 0xff, 0xbf, + 0xac, 0x6d, 0x30, 0x21, 0xe9, 0x9e, 0x4c, 0xee, 0x33, 0xc8, 0x4a, 0x13, + 0xc7, 0x14, 0xa6, 0xe2, 0x8a, 0x1b, 0xe0, 0x77, 0x5c, 0x8d, 0x25, 0xb8, + 0xfa, 0x34, 0xcb, 0xee, 0x21, 0x1e, 0x58, 0x1e, 0x50, 0xb3, 0x98, 0x3, + 0x88, 0x41, 0x3d, 0x66, 0xec, 0x5c, 0x6d, 0xc9, 0x12, 0xa7, 0xb5, 0x62, + 0x47, 0x34, 0x73, 0xd6, 0x4c, 0xf5, 0x3, 0x1b, 0xfe, 0xf0, 0x2f, 0x11, + 0x12, 0x3, 0x46, 0x1, 0x59, 0x2, 0x1, 0x11, 0x38, 0xf6, 0x93, 0x3e, + 0xf5, 0x16, 0xe9, 0x97, 0xb5, 0x19, 0x87, 0xc9, 0x55, 0x9c, 0x53, 0x26, + 0x96, 0x95, 0xef, 0x93, 0xd5, 0x76, 0x54, 0xfc, 0x10, 0x74, 0x55, 0xf4, + 0x53, 0xfe, 0x2d, 0x80, 0xe4, 0x74, 0xbc, 0xcb, 0x66, 0xe0, 0xb6, 0x8b, + 0x1e, 0x81, 0xf, 0xd8, 0xe3, 0x11, 0x90, 0xd4, 0x90, 0xf3, 0xe3, 0x4, + 0x33, 0xb1, 0x7, 0xdd, 0x27, 0x9, 0x73, 0xec, 0x20, 0x24, 0x14, 0x4f, + 0xb7, 0xe1, 0xa1, 0x36, 0xdf, 0xd7, 0xda, 0x23, 0xb2, 0xf9, 0x8b, 0xe1, + 0x88, 0x6f, 0xf3, 0x86, 0xd3, 0x9d, 0xb9, 0xd2, 0xe3, 0x1c, 0xff, 0x8a, + 0x86, 0xfb, 0x8e, 0xe8, 0xc1, 0x8f, 0xc4, 0x68, 0x4f, 0x6f, 0xc2, 0xdf, + 0x13, 0xc3, 0x29, 0xf2, 0x1b, 0x7c, 0x54, 0x7f, 0xb, 0x3e, 0xb1, 0x83, + 0x0, 0xd9, 0x50, 0x84, 0x1d, 0xd8, 0xf2, 0x91, 0x9d, 0x42, 0x50, 0xaf, + 0xd8, 0x71, 0xfd, 0xd1, 0xb0, 0x1b, 0xec, 0x14, 0xb9, 0xe5, 0xe1, 0xae, + 0xd8, 0xc1, 0x5c, 0x28, 0x24, 0x4d, 0x0, 0x31, 0xf7, 0x73, 0x5a, 0x56, + 0xfe, 0x3d, 0x3b, 0x13, 0xfe, 0xb7, 0xec, 0xc, 0xfc, 0x3, 0x3b, 0xa3, + 0xd8, 0x40, 0x25, 0xc0, 0x84, 0xf4, 0xa2, 0x6a, 0xe3, 0xdf, 0x7, 0x3d, + 0x2b, 0xdf, 0xb6, 0x2b, 0x97, 0x9c, 0x4f, 0x6b, 0x53, 0x16, 0x1c, 0x4, + 0x15, 0x87, 0xb2, 0x20, 0x3e, 0x67, 0x73, 0x49, 0xb, 0x98, 0x95, 0xb2, + 0x4, 0x55, 0x62, 0x16, 0x4b, 0x1a, 0x1c, 0x55, 0x59, 0x35, 0xf4, 0x47, + 0x49, 0x54, 0xe6, 0x19, 0xa7, 0x44, 0x56, 0x62, 0xb, 0x70, 0x5d, 0x7a, + 0x51, 0x12, 0xe3, 0xf4, 0x92, 0x4, 0x4b, 0x2, 0xec, 0xd9, 0xc5, 0x5e, + 0x7d, 0x2d, 0xb3, 0x96, 0x3d, 0x59, 0x32, 0x82, 0x2f, 0xca, 0xf5, 0x65, + 0xa3, 0x4d, 0xc4, 0xe8, 0x3a, 0xbe, 0x7e, 0x37, 0xde, 0xe1, 0xcb, 0xaa, + 0x30, 0x29, 0xb, 0xad, 0xa8, 0xe4, 0x37, 0xc3, 0x55, 0xed, 0xf0, 0x5d, + 0x6e, 0xf2, 0xc1, 0xb9, 0x3a, 0xc6, 0x55, 0x6a, 0x87, 0xe, 0x46, 0x54, + 0xde, 0xd5, 0xe, 0x9d, 0x6b, 0x47, 0x26, 0xb5, 0x7, 0x90, 0xa1, 0x8d, + 0x77, 0x4b, 0xf2, 0x6e, 0x25, 0x3e, 0xb7, 0xad, 0xb, 0xbe, 0x29, 0x87, + 0x2f, 0xf0, 0xc9, 0x6a, 0x13, 0xca, 0x1d, 0xfc, 0xb3, 0xb2, 0x66, 0x76, + 0x8, 0x9e, 0x33, 0x3b, 0x7d, 0xf, 0xb7, 0xcb, 0xdb, 0x3e, 0x2e, 0x5f, + 0xb0, 0x73, 0x90, 0xe1, 0xbe, 0xe4, 0x92, 0x1d, 0xa2, 0x63, 0xc1, 0x37, + 0x9a, 0x46, 0xf6, 0x52, 0xf3, 0xb9, 0x8e, 0x7d, 0x6d, 0xeb, 0xdb, 0xec, + 0xec, 0x8c, 0x94, 0x3b, 0xf8, 0x67, 0x65, 0xf9, 0xf1, 0x9d, 0xe7, 0x2b, + 0xfc, 0xd9, 0x3b, 0xc2, 0xe8, 0x59, 0x7f, 0xd5, 0x93, 0xcc, 0x9f, 0xc2, + 0x8b, 0xa0, 0x26, 0x65, 0xa5, 0x83, 0xb5, 0xbe, 0xd3, 0x7a, 0x16, 0x69, + 0x61, 0x53, 0xd3, 0xb2, 0xf6, 0x2c, 0x6b, 0x7c, 0x45, 0x62, 0x96, 0xa4, + 0xc1, 0x7d, 0x89, 0xa0, 0xad, 0xa1, 0x89, 0x9d, 0xbf, 0x8, 0xec, 0xe1, + 0x78, 0x3f, 0xf7, 0xac, 0xbd, 0x3f, 0x6e, 0xad, 0xc7, 0x61, 0xad, 0xd, + 0x1f, 0xea, 0x36, 0xf0, 0xbd, 0xe2, 0xd3, 0x9a, 0x68, 0x91, 0x13, 0xd, + 0x7c, 0xaf, 0xcd, 0x17, 0x6b, 0x11, 0xfc, 0x45, 0xf1, 0x29, 0x9c, 0x7b, + 0xd6, 0xad, 0x41, 0x3f, 0xf4, 0xdf, 0xce, 0x7f, 0x17, 0x96, 0x58, 0xb5, + 0xdd, 0x76, 0x3, 0xb9, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82, + // /home/hyun/qps/icon/superman.png + 0x0, 0x0, 0x11, 0x91, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x40, + 0x0, 0x0, 0x0, 0x30, 0x8, 0x6, 0x0, 0x0, 0x0, 0xa1, 0x4b, 0x7c, + 0x1f, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, + 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, + 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, + 0x45, 0x7, 0xd8, 0xb, 0x15, 0xb, 0x17, 0x8, 0x25, 0xa3, 0x41, 0x1b, + 0x0, 0x0, 0x11, 0x11, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, 0x9a, + 0x69, 0x74, 0x5d, 0xd5, 0x75, 0xc7, 0x7f, 0xfb, 0xdc, 0xfb, 0xee, 0x9b, + 0xf4, 0x34, 0x59, 0x96, 0x65, 0x63, 0xe6, 0x21, 0x18, 0x6c, 0xc0, 0x24, + 0x61, 0xb0, 0x31, 0x96, 0x8d, 0x2c, 0xdb, 0x18, 0x50, 0x70, 0xc, 0xb4, + 0xa1, 0x4d, 0x33, 0xb5, 0x5d, 0x49, 0x3a, 0xa5, 0x9, 0x69, 0xc3, 0xa, + 0x6d, 0x17, 0x4d, 0x83, 0xcd, 0x10, 0x8, 0x26, 0xd3, 0x4a, 0x88, 0x57, + 0x42, 0x9a, 0xa1, 0x19, 0x80, 0x66, 0x0, 0xa, 0x8e, 0x1, 0xf, 0xc, + 0xc5, 0x36, 0x1e, 0x18, 0x6c, 0x2c, 0x59, 0x9e, 0x25, 0x21, 0x6b, 0x78, + 0xd2, 0x1b, 0xef, 0xbd, 0x67, 0xf7, 0xc3, 0x7d, 0x92, 0x85, 0xd, 0x4, + 0x88, 0x4d, 0x3f, 0x24, 0x67, 0xad, 0xb3, 0xee, 0x7d, 0xf7, 0xbd, 0x7b, + 0xce, 0xde, 0xff, 0xb3, 0xcf, 0xde, 0xff, 0xbd, 0xcf, 0x83, 0x3f, 0xb6, + 0x3f, 0xec, 0x26, 0x6f, 0xf5, 0x87, 0x2b, 0x5a, 0x2e, 0x6d, 0x3e, 0xd8, + 0xf9, 0x7c, 0x73, 0x4d, 0x6d, 0x88, 0x63, 0x4, 0x15, 0x50, 0x1, 0xa3, + 0x6f, 0x6f, 0x42, 0x3b, 0x32, 0xa3, 0xa, 0xa0, 0x18, 0x81, 0xb1, 0x43, + 0x88, 0xbe, 0x73, 0x65, 0xac, 0x5a, 0x8a, 0x85, 0x84, 0x3d, 0xf7, 0x63, + 0x9f, 0xf9, 0xd6, 0xac, 0xcf, 0xdc, 0xd4, 0x7d, 0xd4, 0x0, 0x58, 0x7e, + 0xe1, 0xd4, 0xe9, 0xa6, 0x6b, 0xc7, 0xea, 0xb9, 0x4d, 0xc5, 0x74, 0x3a, + 0x23, 0x88, 0xd1, 0xd7, 0x4a, 0xfd, 0x36, 0x9a, 0x2a, 0x60, 0x89, 0xde, + 0x1f, 0x99, 0xdd, 0x1c, 0x92, 0x46, 0xde, 0xee, 0x78, 0xaf, 0xf9, 0x20, + 0x84, 0x25, 0x65, 0xf3, 0x60, 0xd5, 0x53, 0xe3, 0xff, 0xea, 0x5f, 0xe7, + 0xcf, 0xf8, 0x9b, 0x1b, 0x86, 0x7e, 0x6f, 0x0, 0xee, 0xbd, 0xbc, 0xf9, + 0x82, 0xd2, 0x8b, 0xcf, 0x3c, 0x3c, 0xa7, 0xa1, 0x50, 0x97, 0x42, 0xf0, + 0x87, 0x81, 0x30, 0x9a, 0xda, 0x2, 0xe, 0x82, 0x15, 0x10, 0x19, 0xab, + 0x21, 0xa8, 0xa, 0x22, 0x11, 0x50, 0x8a, 0x20, 0x80, 0xaa, 0x46, 0xd7, + 0xca, 0xc4, 0xa, 0xa8, 0x8, 0xe, 0x8a, 0x45, 0x10, 0x14, 0x15, 0xa1, + 0xf2, 0x1a, 0xa2, 0x1a, 0x59, 0x99, 0x8, 0x8a, 0x56, 0x9e, 0xc9, 0xa8, + 0xd2, 0xd1, 0xd3, 0x68, 0xec, 0x11, 0x4d, 0x4c, 0x52, 0x20, 0x69, 0xd9, + 0x94, 0xcb, 0x3c, 0x7a, 0xf2, 0xe7, 0x6e, 0x5d, 0x72, 0xde, 0x5f, 0x7c, + 0x32, 0xfb, 0x8e, 0x1, 0xf8, 0xf1, 0xf5, 0x4b, 0x16, 0x1c, 0x5c, 0xfb, + 0x9b, 0x9f, 0x5f, 0x36, 0xbe, 0x90, 0x4a, 0x28, 0x94, 0xfb, 0xa0, 0x2b, + 0xe, 0x43, 0x25, 0x8d, 0x56, 0x4b, 0x1, 0x11, 0x54, 0xe1, 0xd4, 0xda, + 0x18, 0x5e, 0xc9, 0xa7, 0x9c, 0x3, 0xac, 0xa2, 0x6, 0x8c, 0x56, 0x4, + 0xf7, 0x5c, 0xa, 0xe9, 0x4, 0xc3, 0x6a, 0xc9, 0x17, 0x3, 0xca, 0xc5, + 0x90, 0x20, 0xb0, 0xa0, 0x8a, 0xe3, 0x1a, 0xbc, 0x84, 0x4b, 0x32, 0xe1, + 0x52, 0x65, 0xc, 0xe9, 0x62, 0x9, 0xa7, 0xe0, 0x63, 0xc7, 0xac, 0xb0, + 0x31, 0xe0, 0xa4, 0x20, 0x1b, 0x8b, 0xb3, 0x2f, 0xeb, 0xe3, 0x8a, 0x8e, + 0x82, 0xa0, 0x28, 0x19, 0xcf, 0xd0, 0x54, 0xb0, 0x58, 0x8d, 0x16, 0x22, + 0x96, 0x1, 0xd2, 0xb0, 0xbe, 0x58, 0xfb, 0xf4, 0x94, 0x2f, 0xde, 0xdd, + 0x36, 0x75, 0xc9, 0x87, 0x7b, 0xde, 0x36, 0x0, 0xdf, 0xbd, 0x7c, 0xf6, + 0xfb, 0xa, 0x2f, 0x6f, 0x58, 0x37, 0xb7, 0x7e, 0x38, 0x96, 0x54, 0xa5, + 0x3c, 0x0, 0xaf, 0x24, 0xc, 0x5b, 0xa, 0x4a, 0xed, 0x24, 0x83, 0xd, + 0x85, 0xfd, 0x2f, 0x58, 0xaa, 0x33, 0x86, 0x39, 0xa7, 0xc2, 0x44, 0xd, + 0x28, 0x1e, 0x14, 0x82, 0x7c, 0x24, 0xda, 0x50, 0x4d, 0x8a, 0xce, 0x7c, + 0x40, 0x97, 0x3a, 0x1c, 0xec, 0x2f, 0x62, 0x43, 0xc8, 0x34, 0xa6, 0x48, + 0xd6, 0xa7, 0xf0, 0xd2, 0x31, 0x9c, 0x98, 0x8b, 0x5a, 0x8, 0xca, 0x1, + 0xa5, 0x6c, 0x91, 0xe1, 0xee, 0x21, 0xa, 0x59, 0x1f, 0x37, 0x6, 0xb5, + 0xf5, 0x29, 0x26, 0x2, 0x27, 0xa6, 0x61, 0xdc, 0x70, 0x89, 0xd0, 0x86, + 0xc4, 0xe2, 0x6, 0xb7, 0x4e, 0xd9, 0x25, 0xe, 0x6b, 0x76, 0x58, 0x32, + 0xc7, 0x7b, 0xc4, 0xd2, 0x4a, 0xdf, 0x2e, 0x9f, 0xa6, 0x2a, 0x65, 0x6e, + 0x8, 0xa5, 0xa2, 0xa2, 0x1a, 0x1, 0x66, 0x32, 0x20, 0x29, 0xd8, 0xc8, + 0xc4, 0x17, 0xa7, 0xdd, 0x74, 0xd7, 0x9c, 0xf7, 0x2c, 0xba, 0xae, 0xe7, + 0x2d, 0x3, 0xf0, 0x83, 0x25, 0x57, 0xce, 0x18, 0xdc, 0xb0, 0xf2, 0xe1, + 0x39, 0xe3, 0xa, 0x99, 0xa4, 0x55, 0x82, 0x7e, 0x61, 0x7b, 0x12, 0xb6, + 0x1b, 0xe1, 0xe3, 0xdf, 0xac, 0xc3, 0x8d, 0x27, 0xf8, 0xc5, 0xcd, 0xfd, + 0x4, 0x3b, 0x7c, 0x9a, 0x1b, 0x2d, 0x52, 0xb0, 0x94, 0x7, 0x14, 0x2d, + 0x41, 0x5f, 0xca, 0x63, 0x43, 0x51, 0xe8, 0xc9, 0xfa, 0x9c, 0x3c, 0x63, + 0x32, 0xa7, 0x5f, 0x7c, 0x1c, 0x93, 0xdf, 0x7b, 0x1c, 0xe3, 0x4f, 0x6c, + 0xc0, 0xcb, 0x24, 0x10, 0x63, 0x10, 0xdc, 0x51, 0x3, 0x1e, 0x59, 0x47, + 0x1b, 0x84, 0x64, 0x7b, 0x86, 0xe8, 0xde, 0xd6, 0x43, 0xfb, 0xda, 0x4e, + 0x3a, 0x9e, 0xde, 0x43, 0xf7, 0xb6, 0x7e, 0xea, 0xeb, 0x62, 0x9c, 0x5b, + 0xed, 0x70, 0x42, 0xd1, 0x47, 0x24, 0xc0, 0xab, 0x13, 0x86, 0x3d, 0x87, + 0x17, 0xeb, 0x53, 0x9c, 0xff, 0xa1, 0xf1, 0xdc, 0xff, 0x2f, 0x1d, 0xb4, + 0x34, 0x3a, 0xd4, 0xe, 0x85, 0x14, 0x7, 0x23, 0xeb, 0xa3, 0xb2, 0x25, + 0x63, 0xd5, 0x6, 0x4d, 0x5a, 0x36, 0x68, 0xd3, 0x9a, 0xf7, 0xde, 0xbe, + 0xe2, 0x8a, 0x53, 0x2e, 0x5d, 0x38, 0xf8, 0x3b, 0x1, 0xf8, 0xe1, 0x35, + 0x8b, 0x5a, 0xfa, 0x36, 0xae, 0x7e, 0xf8, 0xb2, 0xda, 0x21, 0x27, 0x81, + 0xe2, 0xf7, 0x41, 0x7b, 0x52, 0xd8, 0x66, 0xe0, 0xe3, 0xdf, 0xca, 0xa0, + 0xa4, 0x58, 0xf1, 0xa9, 0x5e, 0x4e, 0x8d, 0x9, 0xd3, 0x93, 0x21, 0xfe, + 0x80, 0x52, 0x1a, 0x56, 0x8, 0x61, 0x5b, 0x26, 0xcd, 0xd6, 0xfe, 0x32, + 0x17, 0x7e, 0x64, 0xa, 0x17, 0x5c, 0x37, 0x95, 0xf4, 0xb8, 0xc, 0x48, + 0xac, 0xd2, 0x9d, 0x48, 0x71, 0x31, 0x51, 0x1f, 0xf5, 0x7c, 0x15, 0x43, + 0xd7, 0x10, 0x54, 0x51, 0x42, 0xd0, 0x0, 0xb5, 0x1, 0x7, 0xb6, 0x75, + 0xb1, 0xee, 0x3b, 0xeb, 0xd9, 0xfa, 0x9b, 0x76, 0xea, 0x6b, 0x3d, 0xe6, + 0x54, 0x19, 0x92, 0xf9, 0x22, 0x5e, 0xd, 0x64, 0x93, 0xe, 0xbf, 0xd9, + 0xad, 0x2c, 0x3c, 0xc5, 0xa1, 0xae, 0x10, 0x52, 0xec, 0xb7, 0xd8, 0xf2, + 0x21, 0x8d, 0x6, 0x1b, 0xc, 0xe3, 0xfa, 0x5, 0x53, 0x15, 0x12, 0xa6, + 0xe0, 0x79, 0xe7, 0xf8, 0xd5, 0x17, 0xdd, 0xf1, 0x83, 0xb6, 0xe3, 0x2f, + 0x9a, 0xdd, 0xff, 0x86, 0x0, 0xfc, 0xe0, 0x4f, 0xdb, 0xe6, 0xf, 0x3e, + 0xf7, 0xf8, 0xaf, 0xe7, 0xd4, 0x64, 0x9d, 0xa4, 0x2a, 0xe5, 0x7e, 0xa1, + 0x33, 0xa9, 0xbc, 0x4, 0x7c, 0xf4, 0x9b, 0x29, 0x4c, 0x2c, 0xc9, 0x8a, + 0xbf, 0x1e, 0xe4, 0xcc, 0xb8, 0x61, 0x8a, 0xe3, 0x53, 0x1a, 0x84, 0x20, + 0xaf, 0x88, 0x85, 0x9d, 0x99, 0x24, 0xdb, 0xe3, 0x71, 0xfe, 0xec, 0xeb, + 0xcd, 0xd4, 0x4e, 0x1e, 0x37, 0x46, 0xf1, 0x18, 0x52, 0x1, 0x0, 0x71, + 0x1, 0x17, 0x15, 0x53, 0xf1, 0x82, 0xa6, 0xe2, 0x48, 0x14, 0xd1, 0x8, + 0x4, 0x25, 0x0, 0x6b, 0x11, 0xeb, 0x3, 0x3e, 0x10, 0xb0, 0xef, 0x85, + 0x7d, 0x3c, 0x70, 0xe3, 0x13, 0xf4, 0x76, 0xe, 0x30, 0xbf, 0xc9, 0xa3, + 0x21, 0x57, 0xc4, 0x1b, 0x27, 0x68, 0xbd, 0xc1, 0xc, 0x84, 0x14, 0xf, + 0xa, 0x94, 0xc1, 0x8e, 0x89, 0xa1, 0xab, 0x92, 0xc2, 0x29, 0x9e, 0x70, + 0xea, 0x30, 0x98, 0x8c, 0xc5, 0xc6, 0x85, 0x8d, 0xc9, 0x53, 0x36, 0xcf, + 0xbc, 0xeb, 0x87, 0xb3, 0x27, 0x4d, 0xbf, 0x70, 0xe0, 0x8, 0x0, 0x56, + 0xb4, 0xb5, 0x34, 0x17, 0x5e, 0x7a, 0x6e, 0xd5, 0xec, 0xea, 0x1, 0x52, + 0xa, 0x7e, 0x3f, 0x74, 0x26, 0x60, 0x2b, 0xf0, 0x91, 0x6f, 0xc4, 0xa8, + 0xaa, 0x4e, 0xf2, 0xdd, 0x4f, 0xd, 0x33, 0xb9, 0x2c, 0x9c, 0xe3, 0x86, + 0xf8, 0xfd, 0x86, 0xa0, 0x6c, 0xc1, 0x82, 0x9f, 0x72, 0x79, 0x38, 0x70, + 0xf9, 0xcb, 0x9f, 0x2c, 0xa0, 0x7a, 0x42, 0x2d, 0x18, 0xf, 0x25, 0x1, + 0xe2, 0x22, 0xe2, 0x81, 0x89, 0x55, 0x94, 0x1f, 0x1, 0xc1, 0xa0, 0x98, + 0x43, 0xde, 0x5b, 0x89, 0x42, 0x8b, 0x86, 0x15, 0xb, 0xf0, 0x11, 0x1b, + 0xa0, 0x4, 0x88, 0xfa, 0xa8, 0x16, 0x29, 0xe7, 0xb, 0x3c, 0x78, 0xd3, + 0x2a, 0x5e, 0x7e, 0x6c, 0xf, 0x6d, 0x4d, 0x1e, 0xa9, 0x5c, 0x11, 0x13, + 0x13, 0xac, 0xaf, 0x58, 0x5b, 0x19, 0x48, 0xa3, 0xf8, 0x22, 0x28, 0xab, + 0x52, 0xc2, 0xde, 0x7d, 0x30, 0xeb, 0x44, 0xe1, 0xd4, 0x1c, 0x38, 0xd5, + 0x4a, 0x39, 0xae, 0x6c, 0x4a, 0x9e, 0xb6, 0xb1, 0xe5, 0xdb, 0xf, 0x36, + 0xd7, 0x9f, 0x7e, 0x76, 0x76, 0x34, 0x2, 0xaf, 0x68, 0x6b, 0x6d, 0xce, + 0xbd, 0xb4, 0x7e, 0xd5, 0xa5, 0x99, 0x1, 0xd2, 0x56, 0xf0, 0xfb, 0x85, + 0xdd, 0x9, 0xd8, 0xac, 0xc2, 0x47, 0xef, 0x31, 0xd4, 0x36, 0xba, 0x3c, + 0xb2, 0x3c, 0x8f, 0xdb, 0x2b, 0x9c, 0x63, 0x2c, 0xe5, 0x41, 0xc1, 0x2f, + 0x59, 0xb0, 0x82, 0x1a, 0xe8, 0x36, 0x86, 0x73, 0xda, 0x4e, 0xa2, 0x7a, + 0x42, 0x6, 0xf0, 0x80, 0x78, 0xc5, 0xd4, 0x47, 0x94, 0x1f, 0xe9, 0x1e, + 0x2a, 0x1e, 0x6a, 0xbc, 0x8, 0x24, 0x33, 0xe6, 0xb3, 0x78, 0x60, 0xe2, + 0x88, 0x78, 0x88, 0x78, 0xa8, 0x13, 0x47, 0x8c, 0x1b, 0x81, 0x48, 0x1c, + 0x2f, 0x95, 0x62, 0xf1, 0x97, 0x9b, 0x69, 0x9a, 0x52, 0xcf, 0xca, 0x41, + 0xb, 0xae, 0x4b, 0x58, 0x50, 0x6c, 0x38, 0x62, 0x44, 0x3a, 0xca, 0xa4, + 0x14, 0xc8, 0xd4, 0x2b, 0x73, 0x3e, 0x1, 0xab, 0x3b, 0x95, 0x9d, 0x29, + 0x8, 0xb3, 0x42, 0xac, 0x8, 0xe7, 0xe5, 0x77, 0x4c, 0x5f, 0xf9, 0xc9, + 0x6b, 0x1f, 0x1f, 0xdc, 0xdd, 0x5e, 0xd, 0x60, 0xee, 0xbb, 0x66, 0x51, + 0x73, 0xff, 0xa6, 0xe7, 0x56, 0xcd, 0xa4, 0x9f, 0xb4, 0x5, 0x3f, 0xab, + 0xec, 0x4d, 0xc2, 0x66, 0x85, 0x8f, 0x2f, 0x87, 0x9a, 0x26, 0xd8, 0xf7, + 0x82, 0x65, 0xd3, 0xaf, 0x42, 0x66, 0xd5, 0x82, 0x9f, 0x53, 0x6c, 0xb1, + 0x12, 0xeb, 0x45, 0x11, 0x23, 0xe4, 0x8b, 0x3e, 0x27, 0x5d, 0x38, 0x11, + 0x88, 0x81, 0x71, 0x51, 0x71, 0x10, 0x89, 0x23, 0xc6, 0x89, 0x14, 0x27, + 0x76, 0x98, 0x82, 0x71, 0x64, 0x54, 0xe9, 0x4a, 0x97, 0x78, 0x4, 0x86, + 0x44, 0xf7, 0xe0, 0x56, 0xc0, 0x74, 0x51, 0xe3, 0x22, 0x18, 0x9c, 0x78, + 0x9c, 0xab, 0xbf, 0x34, 0x83, 0xc1, 0x6c, 0xc0, 0x6e, 0x2f, 0x16, 0x91, + 0xc9, 0x23, 0xc8, 0x10, 0xa3, 0x3c, 0x64, 0xce, 0x27, 0x94, 0xe6, 0x4f, + 0xc0, 0x13, 0x7b, 0x94, 0x5d, 0x29, 0xb0, 0x43, 0x6, 0xb7, 0x4, 0xef, + 0xd9, 0xfb, 0xe2, 0xf4, 0xff, 0xbe, 0x7e, 0xf1, 0xfd, 0x3, 0x5d, 0xfb, + 0xc6, 0x39, 0xf3, 0x1a, 0x6a, 0x1f, 0x3a, 0xb7, 0xb7, 0xbd, 0xa1, 0x8e, + 0x88, 0x60, 0xc, 0x66, 0x84, 0xa7, 0xa, 0xca, 0x47, 0xbe, 0x6, 0xb5, + 0x13, 0x23, 0x87, 0xf5, 0xab, 0xdb, 0x94, 0x49, 0x45, 0x98, 0xe4, 0x2b, + 0x7e, 0x11, 0xd4, 0x1e, 0x9a, 0xcf, 0xf5, 0xa0, 0xaa, 0x29, 0xce, 0xb, + 0xfb, 0x3, 0xce, 0x98, 0x7b, 0xa, 0xc6, 0xa9, 0xac, 0xa0, 0x71, 0x23, + 0x5, 0xc5, 0x43, 0x24, 0x86, 0x8a, 0xb, 0xc6, 0x45, 0xc4, 0x8d, 0xfc, + 0x1, 0x2e, 0x52, 0x71, 0x8c, 0x11, 0x95, 0x91, 0xe8, 0x2a, 0x32, 0x72, + 0x57, 0x59, 0x51, 0x8b, 0xa8, 0x8d, 0x28, 0x8f, 0x42, 0xba, 0x2e, 0xce, + 0x81, 0x6d, 0x3d, 0x74, 0xee, 0xcc, 0x22, 0x1e, 0xf4, 0x89, 0xd0, 0x67, + 0x94, 0x3e, 0x43, 0xa5, 0xb, 0x7, 0x1d, 0x61, 0xc0, 0x81, 0xf3, 0x3f, + 0x28, 0x9c, 0x3c, 0x1d, 0xc2, 0x50, 0x79, 0xe2, 0x9, 0xe5, 0xa4, 0xe3, + 0xc, 0x5e, 0x5e, 0x89, 0x95, 0x5, 0xaf, 0xb7, 0xe7, 0xc4, 0x97, 0xfb, + 0xb3, 0xcf, 0x38, 0xff, 0x7e, 0xf7, 0xf2, 0x9f, 0x3e, 0xfb, 0xe8, 0x6f, + 0x17, 0x4d, 0xc, 0xf3, 0xe3, 0x63, 0x40, 0x55, 0x1c, 0xa, 0x29, 0x43, + 0xe3, 0xf9, 0x50, 0xd3, 0xa8, 0x14, 0x73, 0x86, 0x7, 0x97, 0x6, 0x5c, + 0xd4, 0x20, 0xb8, 0x79, 0x45, 0x83, 0xc8, 0xda, 0xb4, 0xb2, 0xe5, 0x14, + 0x48, 0xb9, 0x21, 0x4e, 0xb1, 0xc0, 0xaa, 0xfb, 0x77, 0xd2, 0xbd, 0x33, + 0x4b, 0x18, 0x2, 0xc6, 0x23, 0xe6, 0xc5, 0x11, 0xc7, 0x43, 0x8c, 0x8b, + 0x98, 0x18, 0x62, 0x46, 0xb6, 0x81, 0x1b, 0x59, 0x86, 0x71, 0x10, 0x71, + 0x50, 0x71, 0x40, 0xc, 0x22, 0x52, 0x59, 0x6, 0x5, 0xb5, 0xa8, 0x44, + 0x57, 0xa9, 0xd0, 0x22, 0xc1, 0x7, 0x55, 0xaa, 0x27, 0x25, 0xd9, 0xbf, + 0x73, 0x80, 0x5c, 0x5d, 0x82, 0x5c, 0x7d, 0x9c, 0x5c, 0x7d, 0x8c, 0x7c, + 0x7d, 0x8c, 0x7c, 0x9d, 0x43, 0xae, 0x5e, 0xc8, 0xd7, 0xc2, 0x9, 0x17, + 0xc0, 0xa9, 0xef, 0x35, 0xa0, 0xca, 0xf6, 0xa7, 0xc0, 0x74, 0xc3, 0xd4, + 0xa4, 0x10, 0xe6, 0x94, 0xbc, 0x85, 0xee, 0x69, 0x17, 0xfd, 0xd3, 0x55, + 0xf7, 0x3d, 0x78, 0xaf, 0x0, 0xfc, 0xc3, 0xe2, 0xb6, 0xc9, 0x93, 0x9e, + 0x7b, 0xe2, 0xd9, 0x16, 0x1d, 0x9c, 0x98, 0xf2, 0x14, 0x37, 0x3, 0xeb, + 0x8d, 0x30, 0xf3, 0x46, 0xc1, 0x62, 0xf8, 0xfe, 0xa7, 0x3, 0xae, 0xa9, + 0x8e, 0x1c, 0xce, 0x88, 0xd2, 0x63, 0xc3, 0x87, 0x71, 0xc, 0x5e, 0xad, + 0xc5, 0xad, 0x11, 0x86, 0xe2, 0x69, 0xba, 0xb3, 0x3e, 0xd9, 0x3c, 0x94, + 0xdd, 0x24, 0x36, 0x91, 0x0, 0xcf, 0x43, 0xdd, 0x18, 0xe2, 0xc6, 0xc0, + 0x71, 0xa2, 0x90, 0x58, 0xa1, 0xb7, 0xa8, 0x1e, 0xe2, 0xd1, 0x15, 0xd3, + 0x52, 0x6c, 0x14, 0x20, 0x5c, 0x70, 0x12, 0xe, 0xe9, 0xfa, 0x38, 0xd5, + 0xe3, 0x93, 0x8c, 0x3b, 0xad, 0x86, 0xa6, 0xd3, 0xab, 0xa9, 0x6d, 0x74, + 0x81, 0x22, 0x68, 0x11, 0x6c, 0x1, 0xb4, 0x0, 0xb6, 0x58, 0xb9, 0x16, + 0x10, 0xcd, 0xa3, 0x9a, 0x47, 0x6c, 0x8e, 0x87, 0xbf, 0x66, 0xd9, 0xfd, + 0xa8, 0xd2, 0x5a, 0x6b, 0x28, 0xf7, 0x5b, 0xa, 0x65, 0xc3, 0xea, 0xa6, + 0xf7, 0xdc, 0xf7, 0xb7, 0xcf, 0xbe, 0xf4, 0xe1, 0xd7, 0x44, 0x81, 0xbf, + 0x6b, 0xfb, 0xc0, 0x19, 0xc7, 0x6f, 0x7c, 0x7c, 0xd5, 0x5c, 0x1d, 0x98, + 0x94, 0x8e, 0x81, 0x53, 0x3, 0xeb, 0x45, 0x38, 0xe1, 0x3, 0xc2, 0x9a, + 0xef, 0x2a, 0x1f, 0x40, 0x29, 0xdb, 0x23, 0x73, 0x20, 0x19, 0x73, 0x63, + 0x5c, 0x70, 0x22, 0x37, 0x10, 0x6d, 0x6f, 0x37, 0xd2, 0x4d, 0x4d, 0xb4, + 0xb2, 0xf2, 0x16, 0x92, 0x1d, 0x15, 0x46, 0x89, 0x8c, 0x88, 0x40, 0xcc, + 0xa1, 0x10, 0x4f, 0x91, 0xb, 0x95, 0x81, 0x6c, 0x89, 0xbe, 0x9c, 0x21, + 0x57, 0x97, 0x22, 0x73, 0x52, 0x35, 0x67, 0xcf, 0x9b, 0xc0, 0x29, 0x17, + 0xd6, 0xe0, 0x9a, 0x22, 0x4a, 0x1e, 0x9, 0x8b, 0x28, 0xb9, 0xa, 0x28, + 0x39, 0x1e, 0xb9, 0x27, 0xc7, 0xae, 0x87, 0x94, 0x96, 0x6, 0x8, 0xfa, + 0xa0, 0x58, 0x82, 0xd5, 0x13, 0xa7, 0xfc, 0x62, 0xf7, 0x9c, 0x2b, 0x3e, + 0x74, 0xdb, 0xb2, 0x5b, 0x4b, 0x47, 0xf0, 0x80, 0xbf, 0xbf, 0xf2, 0x8a, + 0x53, 0x4e, 0x78, 0x7e, 0xcd, 0x6f, 0x67, 0x33, 0x78, 0x62, 0x32, 0x6, + 0x6e, 0x2d, 0x3c, 0x93, 0x83, 0xdd, 0x83, 0xb0, 0x38, 0xad, 0x84, 0xfe, + 0x58, 0xf6, 0xf6, 0x6, 0xa9, 0xd9, 0x61, 0x1a, 0x8e, 0x2e, 0xae, 0xbe, + 0x8d, 0x4, 0x7d, 0x4, 0xa8, 0xa, 0x8, 0x62, 0x14, 0xe3, 0x8, 0x26, + 0x2e, 0x38, 0x71, 0x8b, 0x9b, 0x80, 0x52, 0xd2, 0xa3, 0x73, 0x50, 0xe9, + 0x76, 0x62, 0x9c, 0xd4, 0xd2, 0xc8, 0x45, 0xd7, 0x36, 0xe0, 0x7a, 0x25, + 0xd0, 0xc8, 0x2, 0x1e, 0xfd, 0x7a, 0x2f, 0xed, 0xbf, 0xf4, 0x69, 0x19, + 0x6f, 0xf0, 0xf, 0x86, 0x14, 0x7d, 0x58, 0xd3, 0x74, 0xd6, 0x23, 0xfb, + 0x9b, 0xdb, 0x96, 0x2c, 0xbd, 0xf5, 0x96, 0xe1, 0x91, 0xe9, 0x9c, 0xb1, + 0x73, 0x3f, 0xb3, 0x7d, 0x7b, 0xff, 0xb4, 0xb9, 0xb, 0x56, 0xf6, 0x76, + 0x1d, 0xb8, 0x6a, 0x62, 0x58, 0xac, 0x36, 0x65, 0x38, 0xa1, 0x5a, 0x18, + 0x40, 0xa8, 0x4e, 0x9, 0xa6, 0x78, 0x48, 0xcf, 0x37, 0xec, 0x95, 0xfc, + 0x7e, 0xd4, 0x4f, 0x8c, 0xfd, 0xee, 0xf5, 0x9e, 0xbd, 0x5e, 0xaf, 0xec, + 0xc, 0xb5, 0x60, 0x2b, 0x3d, 0xf4, 0x21, 0x2c, 0x2a, 0xe5, 0x1c, 0x94, + 0x87, 0x4, 0xb2, 0x21, 0xf5, 0x46, 0x39, 0x39, 0xee, 0x93, 0xef, 0x1c, + 0xe6, 0xc9, 0x5f, 0xf6, 0xd3, 0x38, 0xc5, 0x23, 0xd3, 0xa0, 0xac, 0xfc, + 0xf6, 0x41, 0xb6, 0xdd, 0x5f, 0x62, 0x5e, 0xa3, 0xa1, 0xdc, 0x6b, 0xc9, + 0xfb, 0xc2, 0xd3, 0x13, 0xa6, 0x3c, 0xb9, 0x67, 0xce, 0x95, 0xd7, 0x2d, + 0xbb, 0x75, 0xd9, 0xe0, 0xef, 0xcc, 0x5, 0x3e, 0xb7, 0x70, 0xe1, 0x94, + 0xa6, 0xad, 0x6b, 0x57, 0x5e, 0xa2, 0xd9, 0x89, 0x89, 0x58, 0x94, 0x84, + 0x64, 0x13, 0x1e, 0xa9, 0xae, 0x32, 0xd6, 0xd7, 0xb7, 0x64, 0x0, 0x3a, + 0xc2, 0x70, 0x4d, 0x74, 0x35, 0x6f, 0x60, 0x21, 0xaf, 0x57, 0x2f, 0x90, + 0xc3, 0xc2, 0x9b, 0x54, 0xa, 0x29, 0xa6, 0xb2, 0x5, 0x45, 0xa2, 0x92, + 0x82, 0x41, 0x30, 0x2e, 0xb8, 0x69, 0xc1, 0xad, 0xb3, 0x6c, 0xc8, 0x19, + 0xe2, 0x67, 0xa6, 0xd8, 0xb7, 0x36, 0x4f, 0xeb, 0x4, 0xa1, 0xd8, 0x1b, + 0x52, 0xf6, 0xe1, 0x7f, 0xc7, 0x9f, 0xb1, 0x72, 0x4f, 0xcb, 0xd5, 0x8b, + 0x97, 0xde, 0xba, 0x2c, 0xfb, 0x96, 0xb3, 0xc1, 0x1b, 0xae, 0xba, 0xe2, + 0xfc, 0x9, 0x1b, 0x9e, 0xf8, 0x9f, 0x8b, 0x75, 0x68, 0x5c, 0xd2, 0x83, + 0x78, 0xad, 0x41, 0x4b, 0x4a, 0x61, 0x58, 0x51, 0x3b, 0x9a, 0xb, 0x57, + 0x24, 0x8e, 0x72, 0x7e, 0x33, 0xb2, 0x41, 0xf4, 0xd0, 0x7e, 0x1e, 0xf9, + 0xd9, 0x8, 0xe3, 0x1b, 0x5b, 0xf1, 0xd1, 0xc3, 0xa4, 0x18, 0xa9, 0x3, + 0xbc, 0x29, 0x48, 0x6a, 0xe, 0xaf, 0x3, 0x45, 0x89, 0x8f, 0x27, 0x78, + 0xf5, 0xf0, 0x92, 0x15, 0xa6, 0xc4, 0xc, 0xc5, 0x9e, 0x80, 0x52, 0x59, + 0x58, 0x3f, 0xe1, 0xf4, 0x27, 0xf6, 0xce, 0x5b, 0xb2, 0x78, 0xe9, 0xd2, + 0x2f, 0xf7, 0xbd, 0xed, 0x7a, 0xc0, 0x67, 0x17, 0x2e, 0x3c, 0x6f, 0xe2, + 0xd6, 0x35, 0x8f, 0x5c, 0xa0, 0x43, 0x8d, 0x71, 0x3, 0x6e, 0x42, 0xd0, + 0x2a, 0x8f, 0x30, 0x17, 0x62, 0x73, 0x21, 0xa2, 0x95, 0x12, 0xd7, 0x48, + 0xa1, 0xe3, 0x35, 0x35, 0x2, 0xad, 0x8c, 0x6e, 0x46, 0x8d, 0x5b, 0x5e, + 0x8f, 0xb4, 0xc8, 0x5b, 0x75, 0xc, 0x87, 0xa, 0x24, 0x47, 0xe, 0x11, + 0x8d, 0xec, 0x7a, 0x10, 0x4b, 0x40, 0x90, 0x87, 0x52, 0x20, 0x3c, 0x3f, + 0xe1, 0x8c, 0x6d, 0xfb, 0x5a, 0x97, 0xcc, 0xbd, 0xe5, 0x96, 0xff, 0xd8, + 0xff, 0x46, 0x23, 0x3b, 0x6f, 0x36, 0xed, 0x53, 0x3b, 0x76, 0x74, 0x9d, + 0xdb, 0xdc, 0xba, 0x66, 0xb0, 0xab, 0xeb, 0x9a, 0x86, 0xb0, 0x9c, 0x10, + 0xb, 0xea, 0x5a, 0xb6, 0x5a, 0xa5, 0xd7, 0xf5, 0x8, 0x3d, 0x4b, 0xb2, + 0x2a, 0x89, 0x5b, 0xe, 0xa9, 0xb0, 0x78, 0xec, 0xd8, 0x2e, 0x82, 0x55, + 0x1d, 0xad, 0x80, 0x8d, 0x7c, 0xaf, 0x3a, 0x52, 0x15, 0x13, 0xac, 0x1e, + 0xfe, 0xe, 0x47, 0x3e, 0x1b, 0xf3, 0xee, 0xe1, 0x5d, 0x47, 0xde, 0xb1, + 0x10, 0x86, 0x50, 0x2a, 0x83, 0x1f, 0x8, 0x5b, 0x1a, 0x4f, 0x7b, 0x61, + 0x6f, 0xcb, 0xd5, 0x57, 0x2c, 0x5d, 0x76, 0xcb, 0xee, 0xdf, 0xbb, 0x26, + 0xf8, 0xb9, 0xb6, 0x2b, 0xe6, 0x35, 0xad, 0x7f, 0xf2, 0xe7, 0xe7, 0x85, + 0xd9, 0x8c, 0xe7, 0x40, 0xac, 0xca, 0xb0, 0xb1, 0x4, 0x5d, 0xfd, 0x96, + 0x30, 0x84, 0x74, 0xc6, 0xa1, 0xba, 0x2a, 0x4e, 0x95, 0x1a, 0xaa, 0xd4, + 0x27, 0x19, 0x8f, 0x91, 0x88, 0x39, 0x18, 0xb, 0x4e, 0x68, 0x31, 0x81, + 0x5, 0x3f, 0x80, 0x30, 0x44, 0xd5, 0x1e, 0xda, 0x6, 0x8e, 0xb, 0x5e, + 0xc, 0x75, 0x5c, 0x42, 0x23, 0x11, 0x37, 0xa8, 0x38, 0xb, 0x51, 0x90, + 0x8a, 0x56, 0xa6, 0xec, 0x83, 0xef, 0x63, 0xc3, 0x8, 0xe4, 0x37, 0xf7, + 0x21, 0xc2, 0xb, 0x4d, 0xa7, 0x6d, 0xdf, 0xbf, 0x60, 0xf1, 0xbc, 0x5b, + 0x6e, 0x59, 0xb6, 0xfb, 0xa8, 0x55, 0x85, 0x3f, 0xbb, 0x68, 0xc1, 0x8c, + 0x49, 0xcf, 0xaf, 0x7d, 0x78, 0x6a, 0x38, 0x94, 0x71, 0x1c, 0xf0, 0xaa, + 0x5c, 0x3a, 0x52, 0x2e, 0x17, 0xfe, 0xe3, 0xd9, 0xc, 0xf7, 0x6, 0x74, + 0x6f, 0x2f, 0x30, 0xd0, 0x5d, 0x64, 0xa8, 0xa7, 0xc8, 0x60, 0xd7, 0x30, + 0x85, 0xbe, 0x3c, 0xa1, 0x1f, 0xed, 0x9, 0x11, 0x30, 0x46, 0x10, 0xc7, + 0xe0, 0x38, 0xe, 0x62, 0x4, 0xd, 0x95, 0x20, 0x8, 0xd0, 0x50, 0xb1, + 0xf6, 0x90, 0x3f, 0x11, 0x73, 0x28, 0x12, 0x44, 0x61, 0x54, 0x70, 0x1d, + 0x48, 0x54, 0xc5, 0xc9, 0x24, 0x1d, 0x32, 0x36, 0xa4, 0x31, 0x29, 0xa4, + 0x6, 0x7c, 0xca, 0x41, 0x58, 0x61, 0x8b, 0x23, 0xa4, 0xa, 0x5e, 0x99, + 0x70, 0xf2, 0xf6, 0x3d, 0xf3, 0xaf, 0x9d, 0xbf, 0xf4, 0xd6, 0x65, 0x9d, + 0x47, 0xb5, 0x2c, 0x1e, 0xf9, 0x84, 0xf9, 0xb3, 0x27, 0x6e, 0x5e, 0xf7, + 0xd0, 0x59, 0x61, 0x2e, 0x69, 0xc4, 0xe2, 0x55, 0x39, 0xec, 0x1d, 0x97, + 0xe4, 0xca, 0xaf, 0xce, 0xa4, 0x66, 0x62, 0xd, 0x82, 0x87, 0x9a, 0x4, + 0x8a, 0x47, 0x50, 0x36, 0x94, 0xf2, 0x82, 0x5f, 0x54, 0xfc, 0xb2, 0xc5, + 0xf, 0xc, 0xf8, 0x86, 0x90, 0xa8, 0xe8, 0x29, 0xc6, 0xc5, 0xb8, 0x6, + 0x37, 0x11, 0xc3, 0x8b, 0xc7, 0x30, 0xae, 0x62, 0x5c, 0xa2, 0x32, 0xb9, + 0xfa, 0x68, 0x50, 0x26, 0x28, 0x15, 0xf0, 0xb, 0x5, 0xa, 0x83, 0xc3, + 0xc, 0x1e, 0xe8, 0xe3, 0xc0, 0xe6, 0x2e, 0x3a, 0x37, 0x76, 0xb3, 0x7f, + 0x53, 0x2f, 0x67, 0x4d, 0x4e, 0xd0, 0x38, 0x58, 0xa4, 0x1c, 0x48, 0xc5, + 0x15, 0x2a, 0x1d, 0xd, 0x27, 0xbe, 0xb2, 0x6b, 0xe1, 0xb5, 0x73, 0x6e, + 0xbb, 0xed, 0xb6, 0x7d, 0x47, 0xfd, 0x5c, 0x60, 0xa4, 0x7d, 0x7e, 0x61, + 0xeb, 0x65, 0x8d, 0x5b, 0xd6, 0xfd, 0xfa, 0x8c, 0x60, 0x38, 0x6e, 0x0, + 0x2f, 0x1d, 0xe3, 0x40, 0x63, 0x92, 0xb6, 0xe5, 0x33, 0xa9, 0x9e, 0x50, + 0x4b, 0xdf, 0x9e, 0x12, 0x1d, 0xcf, 0xf6, 0xd0, 0xb5, 0x3d, 0x4b, 0xbe, + 0xaf, 0x84, 0xd, 0x42, 0x8c, 0x71, 0x31, 0x8e, 0x21, 0x5e, 0x93, 0x66, + 0xc2, 0x19, 0x4d, 0x9c, 0x36, 0xf7, 0x2c, 0xea, 0x27, 0x8f, 0xe3, 0x50, + 0xbd, 0x98, 0x4a, 0x5d, 0xd8, 0xa2, 0xea, 0x23, 0xea, 0x47, 0x15, 0x22, + 0xeb, 0xa3, 0x1a, 0x20, 0x94, 0xc1, 0x96, 0x41, 0x8b, 0xec, 0x5a, 0xbf, + 0x97, 0x1f, 0x7d, 0xe6, 0x29, 0xa6, 0x57, 0x1, 0x7, 0x4b, 0xf8, 0x95, + 0xd0, 0xb8, 0xb7, 0x61, 0xf2, 0xcb, 0x3b, 0x5a, 0xae, 0xb9, 0xec, 0x2b, + 0x77, 0xdd, 0xb9, 0xff, 0x98, 0x1c, 0x8c, 0xbc, 0x6, 0x84, 0x5, 0xf3, + 0x97, 0x34, 0x6e, 0x7d, 0xea, 0xa7, 0xa7, 0xfa, 0x59, 0x44, 0xc0, 0x4b, + 0xb9, 0xf4, 0x36, 0xa5, 0x91, 0x86, 0x24, 0x3, 0x5b, 0x7, 0x98, 0xe8, + 0x42, 0x6d, 0x22, 0x86, 0x53, 0x2c, 0x61, 0x8b, 0x1, 0x81, 0x5a, 0xc4, + 0x18, 0xdc, 0x44, 0x92, 0x7c, 0xcc, 0xa1, 0xcf, 0xb7, 0x8c, 0x5f, 0x74, + 0x1e, 0xad, 0x37, 0xb4, 0x62, 0x5c, 0x19, 0x73, 0x58, 0x60, 0x51, 0x8d, + 0x4a, 0x62, 0x68, 0x88, 0xa8, 0x4f, 0x39, 0x9f, 0xe7, 0xd5, 0x9d, 0xaf, + 0xd2, 0xbe, 0x76, 0x17, 0x2f, 0x3d, 0xb6, 0xb, 0xba, 0xf3, 0x9c, 0x16, + 0x57, 0xca, 0x7d, 0x3e, 0x7e, 0x68, 0x51, 0x60, 0x5f, 0xdd, 0x71, 0x2f, + 0xee, 0x98, 0x7f, 0xdd, 0x65, 0x77, 0x7e, 0xf5, 0x2b, 0x5d, 0xc7, 0xec, + 0x64, 0xe8, 0x48, 0x10, 0x5a, 0x97, 0x34, 0x6c, 0x7d, 0xfa, 0xa7, 0xa7, + 0x95, 0xb3, 0x20, 0x82, 0x97, 0x72, 0xf1, 0x93, 0xe, 0xf1, 0x41, 0x9f, + 0x52, 0x18, 0x46, 0x79, 0x8d, 0xbc, 0xce, 0xc, 0x95, 0x38, 0x66, 0x6a, + 0xe2, 0x98, 0x4b, 0xcf, 0x64, 0xda, 0x92, 0x73, 0xf1, 0x8b, 0x1, 0xfe, + 0x70, 0x89, 0xc2, 0x60, 0x8e, 0xe1, 0x9e, 0x21, 0x86, 0xf, 0xe6, 0x19, + 0xe8, 0xca, 0x52, 0x7c, 0x35, 0x4b, 0xd0, 0x9b, 0x27, 0x51, 0xa, 0xc9, + 0x98, 0x80, 0x6a, 0xe3, 0x50, 0xca, 0x16, 0x29, 0x87, 0x32, 0x7a, 0x2e, + 0xd1, 0x55, 0x37, 0xa9, 0x73, 0xe7, 0xbc, 0xf, 0x5e, 0x72, 0xc7, 0xf2, + 0xe5, 0xfb, 0xde, 0x89, 0x1e, 0xef, 0x18, 0x0, 0x80, 0x1b, 0x16, 0x5d, + 0x7e, 0xfd, 0x84, 0x8d, 0x4f, 0xae, 0x38, 0x31, 0xc8, 0xc5, 0x54, 0x14, + 0x47, 0xc7, 0xe4, 0xa, 0x72, 0x44, 0xd4, 0x1f, 0x5, 0x40, 0x25, 0xe2, + 0x4, 0xb6, 0x26, 0x41, 0x21, 0x5f, 0x46, 0x1c, 0x83, 0xeb, 0xba, 0xc4, + 0x62, 0xe, 0x5e, 0xcc, 0xe0, 0xaa, 0xe2, 0x16, 0x3, 0x82, 0x62, 0x89, + 0x52, 0x10, 0x6d, 0xe, 0xd5, 0x88, 0x7, 0x30, 0x72, 0x94, 0xa6, 0x42, + 0x77, 0x5d, 0xe3, 0x2b, 0xed, 0xad, 0xd7, 0xcc, 0xbf, 0x73, 0xf9, 0x3d, + 0x3b, 0x8f, 0xf9, 0xd9, 0xe0, 0x1b, 0xb5, 0x7f, 0xbe, 0x7c, 0xc1, 0x95, + 0x8d, 0x1b, 0x57, 0xff, 0xe7, 0xf1, 0x61, 0x2e, 0x33, 0x9a, 0xc4, 0xe8, + 0xe8, 0x42, 0x8f, 0x2d, 0x6d, 0x8c, 0x1e, 0x64, 0x30, 0x72, 0xc2, 0x23, + 0x52, 0x31, 0xfd, 0xd7, 0xa, 0x32, 0x7a, 0x92, 0x54, 0x19, 0x68, 0xf4, + 0x84, 0x49, 0xa4, 0x42, 0x7c, 0x94, 0xde, 0xea, 0xc6, 0xad, 0xed, 0xb, + 0xff, 0xa4, 0xed, 0xf6, 0xbb, 0xef, 0xee, 0x78, 0x57, 0xe, 0x47, 0xdf, + 0xac, 0xdd, 0xd4, 0x3a, 0x7b, 0x46, 0xfd, 0xd6, 0xd, 0x8f, 0x4d, 0xe, + 0x87, 0x92, 0x23, 0xa, 0x5b, 0xa9, 0xc4, 0x72, 0xde, 0x98, 0xb9, 0xe9, + 0xd8, 0xa2, 0xa8, 0x1e, 0x32, 0x9a, 0xc3, 0x99, 0xb6, 0x11, 0xc1, 0xa2, + 0x98, 0xca, 0x81, 0x6a, 0x4f, 0x5d, 0x63, 0x7b, 0x47, 0xcb, 0xb5, 0xad, + 0xb7, 0x7f, 0xed, 0x9e, 0x8e, 0xdf, 0x57, 0x76, 0xe7, 0x68, 0x0, 0xf0, + 0x64, 0xfb, 0xae, 0x3d, 0x33, 0x2e, 0x9e, 0xf5, 0x4c, 0xe9, 0xe0, 0xab, + 0x1f, 0xae, 0x9, 0xcb, 0xa8, 0x8, 0x6, 0x48, 0xd7, 0xc7, 0x31, 0x55, + 0x1e, 0x71, 0x4, 0xd, 0xf4, 0x8, 0x24, 0x46, 0xc0, 0x92, 0xca, 0x96, + 0x89, 0x98, 0x73, 0x54, 0x24, 0x8b, 0xb9, 0x82, 0x57, 0x17, 0x27, 0x9e, + 0xf6, 0x90, 0x62, 0x48, 0x50, 0x41, 0xa8, 0xb7, 0x66, 0xfc, 0xee, 0xf6, + 0xb9, 0x8b, 0x5b, 0xef, 0xf8, 0xc6, 0x37, 0xda, 0x8f, 0x86, 0xec, 0x47, + 0x5, 0x0, 0x80, 0xc7, 0xdb, 0x77, 0x76, 0x5c, 0x30, 0xa7, 0x75, 0x47, + 0xb9, 0x6b, 0xdf, 0xd5, 0x19, 0x1b, 0x88, 0x11, 0x45, 0x8d, 0xb0, 0x63, + 0x58, 0xe9, 0x18, 0xa, 0x28, 0xd5, 0xc4, 0x28, 0xa5, 0xe3, 0x94, 0x63, + 0x2e, 0xbe, 0x31, 0x84, 0x2e, 0x84, 0x9, 0x8, 0x3d, 0xc1, 0x77, 0x84, + 0xa2, 0xe3, 0x50, 0x4c, 0x7a, 0x14, 0x92, 0x1e, 0xfd, 0x49, 0x87, 0xdd, + 0x45, 0x4b, 0xbf, 0xf, 0x4d, 0x8e, 0x50, 0x28, 0x45, 0xec, 0xb1, 0xbf, + 0xba, 0xbe, 0x73, 0x47, 0xcb, 0x92, 0xe6, 0x3b, 0xbe, 0xf9, 0xad, 0x8e, + 0xa3, 0x25, 0xb7, 0x70, 0x94, 0xdb, 0x17, 0x16, 0xb4, 0x7c, 0x6c, 0xfc, + 0xa6, 0x75, 0xdf, 0x3e, 0xde, 0x16, 0x8c, 0xa0, 0xb8, 0xe9, 0x18, 0x9d, + 0xbe, 0x72, 0xe6, 0x92, 0x89, 0x4, 0x39, 0xc8, 0x67, 0x43, 0x4a, 0x85, + 0x80, 0xb0, 0x14, 0x62, 0x47, 0xfe, 0x23, 0xe0, 0x8, 0xae, 0x67, 0x48, + 0xa4, 0x1c, 0xd2, 0x75, 0x31, 0x76, 0x6e, 0xec, 0x25, 0xd7, 0x59, 0x66, + 0x5a, 0xda, 0x61, 0x38, 0x5b, 0x2, 0xa0, 0xaf, 0x6a, 0xdc, 0xee, 0x6d, + 0x73, 0xae, 0x9a, 0x7b, 0xc7, 0x77, 0x56, 0xb4, 0x1f, 0x4d, 0x79, 0x8f, + 0x3a, 0x0, 0x0, 0x37, 0xce, 0x6f, 0xf9, 0x50, 0xe3, 0xe6, 0x75, 0xdf, + 0x9f, 0x64, 0xf3, 0x8e, 0x51, 0x70, 0xd3, 0x2e, 0xd9, 0xe3, 0x52, 0x5c, + 0x7d, 0xd7, 0xd9, 0xa4, 0xeb, 0x13, 0x51, 0x99, 0x4c, 0xcd, 0x18, 0x7, + 0x79, 0xa8, 0x16, 0xb8, 0xe1, 0x81, 0xfd, 0x3c, 0xfe, 0x95, 0xe, 0xa6, + 0x26, 0xd, 0xc3, 0x83, 0x3e, 0x56, 0x60, 0xb0, 0x7a, 0xdc, 0x81, 0x6d, + 0x97, 0xb6, 0xcd, 0xbd, 0xfd, 0xde, 0x7b, 0x5f, 0x3e, 0xda, 0xb2, 0x3a, + 0xc7, 0x2, 0x80, 0xd5, 0xed, 0x1d, 0x5b, 0xce, 0xbd, 0xf8, 0x92, 0x76, + 0xba, 0xbb, 0xae, 0xca, 0x48, 0x60, 0x42, 0x5f, 0x49, 0x15, 0x3, 0x9e, + 0x5f, 0xd7, 0xcf, 0xe9, 0xb3, 0xab, 0xf0, 0x12, 0x51, 0x7e, 0x27, 0x84, + 0x28, 0x7e, 0x44, 0x7c, 0xa4, 0xcc, 0x86, 0x7, 0xf6, 0xf1, 0xf8, 0x9d, + 0x9d, 0x4c, 0x4b, 0x1a, 0x86, 0x6, 0x7d, 0x40, 0x18, 0xc8, 0x8c, 0x7b, + 0xb5, 0x7d, 0xe6, 0xfc, 0x79, 0xb7, 0xad, 0xf8, 0xde, 0x8b, 0xc7, 0x42, + 0xd6, 0x63, 0x2, 0x0, 0xc0, 0xda, 0x8e, 0xce, 0x2d, 0xd3, 0x67, 0x5c, + 0xd2, 0x4e, 0x4f, 0x77, 0x5b, 0x35, 0xbe, 0x9, 0x2, 0x48, 0x15, 0x7c, + 0x36, 0x3e, 0x35, 0xc0, 0xe9, 0x97, 0xa6, 0x89, 0xc5, 0x15, 0xc1, 0x47, + 0x34, 0x4, 0x29, 0xf3, 0xfc, 0x2f, 0x7b, 0x58, 0x79, 0xfb, 0x1e, 0xa6, + 0xa5, 0xc, 0x43, 0x83, 0x1, 0x2a, 0x30, 0x50, 0x5d, 0xd7, 0xf5, 0xca, + 0xc5, 0xad, 0x73, 0x6e, 0xbd, 0xef, 0x47, 0x2f, 0x1c, 0x2b, 0x39, 0x8f, + 0x19, 0x0, 0x0, 0x6b, 0x3a, 0x3a, 0xb7, 0x9c, 0x33, 0x6b, 0x76, 0x8f, + 0xe9, 0xea, 0x5a, 0x58, 0x6d, 0x7d, 0x63, 0x3, 0x21, 0x99, 0xf7, 0xd9, + 0xf4, 0xec, 0x10, 0xa7, 0x37, 0x7b, 0xb8, 0x9e, 0x5, 0x7c, 0xb6, 0x3c, + 0xd2, 0xcb, 0x63, 0xb7, 0x75, 0x31, 0x2d, 0xe5, 0x90, 0x1b, 0xc, 0x11, + 0x11, 0x6, 0xd2, 0x75, 0x7, 0x3a, 0x2f, 0x58, 0x38, 0x73, 0xd9, 0xf, + 0x7f, 0xf4, 0xca, 0xb1, 0x94, 0xf1, 0x98, 0x2, 0x0, 0xb0, 0x76, 0xc7, + 0xce, 0xf5, 0xe7, 0xcc, 0x9a, 0xd3, 0x27, 0xdd, 0x7, 0x2e, 0xcf, 0xe0, + 0x43, 0x20, 0xc4, 0xa, 0x21, 0x5b, 0x9e, 0xc9, 0x73, 0xc6, 0x6c, 0x87, + 0x6d, 0xab, 0xb3, 0x3c, 0xb2, 0xec, 0x20, 0xd3, 0x12, 0x30, 0x9c, 0xd, + 0x1, 0x21, 0x9b, 0xae, 0xe9, 0xd9, 0x7e, 0xc1, 0xbc, 0x59, 0x4b, 0x7f, + 0xfc, 0x93, 0x8e, 0x63, 0x2d, 0xdf, 0x31, 0x7, 0x20, 0x2, 0xa1, 0xe3, + 0xd9, 0xf3, 0x66, 0xb7, 0x94, 0x65, 0xff, 0x9e, 0xb9, 0x55, 0x84, 0x42, + 0xa0, 0xc4, 0x72, 0x21, 0xeb, 0x57, 0xe7, 0xd9, 0xfc, 0x50, 0x9e, 0x69, + 0x71, 0x87, 0x5c, 0x36, 0x44, 0x81, 0xe1, 0x54, 0x4d, 0x77, 0xfb, 0xfb, + 0x5b, 0xe6, 0x2e, 0xfd, 0xc9, 0xcf, 0xb6, 0xbd, 0x1b, 0xb2, 0xbd, 0x2b, + 0x0, 0x0, 0x7c, 0xec, 0xb, 0x5f, 0x5c, 0xbb, 0xb3, 0x3f, 0x7b, 0xd0, + 0x39, 0xb0, 0x6f, 0x7e, 0x95, 0x86, 0x86, 0x40, 0x48, 0x94, 0x2d, 0xe3, + 0xd, 0x94, 0x86, 0x2d, 0x56, 0x84, 0x5c, 0xaa, 0xe6, 0xd5, 0xce, 0xf7, + 0xb7, 0x2e, 0xfa, 0xd2, 0x7f, 0xfd, 0x6c, 0xd3, 0xbb, 0x25, 0x97, 0xf0, + 0x2e, 0xb7, 0x1b, 0x2f, 0xbd, 0xf8, 0xcf, 0x27, 0x6d, 0xdf, 0xf0, 0xfd, + 0x26, 0x5b, 0x8a, 0xca, 0xa4, 0x95, 0xfc, 0x61, 0x28, 0x99, 0x39, 0xb8, + 0xf3, 0x7d, 0x73, 0xaf, 0xba, 0xf9, 0x67, 0xf, 0xac, 0x7b, 0x37, 0xe5, + 0x71, 0xde, 0x6d, 0x0, 0x56, 0xef, 0xda, 0xbb, 0xf9, 0x9c, 0x8b, 0x66, + 0xd, 0x39, 0xaf, 0x1e, 0x98, 0x5f, 0x45, 0x8, 0x28, 0xb9, 0x64, 0xf5, + 0xc1, 0x9d, 0x53, 0x67, 0xb6, 0xde, 0xfc, 0xc0, 0xaf, 0x9e, 0xe5, 0xf, + 0xa5, 0x7d, 0xa1, 0x79, 0xe6, 0xa7, 0xbf, 0x3e, 0xc1, 0xd3, 0xef, 0x9d, + 0x94, 0xe9, 0xfe, 0xb7, 0x5, 0xf3, 0xce, 0xe5, 0xf, 0xb1, 0xdd, 0xd8, + 0x7c, 0xd1, 0xf5, 0x9f, 0x5f, 0x38, 0x6f, 0x2a, 0x7f, 0x6c, 0x7f, 0x6c, + 0xff, 0x6f, 0xed, 0xff, 0x0, 0x2b, 0xde, 0x6, 0xb0, 0x47, 0x2, 0xc3, + 0xc6, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, + 0x82, + + // /home/hyun/qps/icon/vista.png + 0x0, 0x0, 0xc, 0x6a, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x81, + 0x0, 0x0, 0x0, 0x1c, 0x8, 0x6, 0x0, 0x0, 0x0, 0x46, 0x58, 0xdc, + 0xcd, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0x24, + 0x0, 0x24, 0x0, 0x24, 0xce, 0x45, 0x38, 0x56, 0x0, 0x0, 0x0, 0x9, + 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x13, 0x0, 0x0, 0xb, 0x13, + 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, + 0x45, 0x7, 0xd8, 0xc, 0x1d, 0xa, 0x25, 0x8, 0xbe, 0x85, 0x8b, 0xa2, + 0x0, 0x0, 0x0, 0x19, 0x74, 0x45, 0x58, 0x74, 0x43, 0x6f, 0x6d, 0x6d, + 0x65, 0x6e, 0x74, 0x0, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x20, + 0x77, 0x69, 0x74, 0x68, 0x20, 0x47, 0x49, 0x4d, 0x50, 0x57, 0x81, 0xe, + 0x17, 0x0, 0x0, 0xb, 0xc5, 0x49, 0x44, 0x41, 0x54, 0x68, 0xde, 0xed, + 0x5b, 0xdb, 0x92, 0x2b, 0x37, 0xe, 0x23, 0x14, 0x7f, 0x69, 0xfe, 0xbf, + 0x6a, 0x93, 0xf7, 0x98, 0xd8, 0x7, 0xf1, 0x2, 0x4a, 0x9a, 0xc9, 0x7, + 0xec, 0x4e, 0x9d, 0x3a, 0xe3, 0xb1, 0xbb, 0xd5, 0x12, 0x45, 0x11, 0x20, + 0x48, 0xe3, 0xcf, 0x3f, 0xff, 0xa4, 0xfd, 0xff, 0xe7, 0x7f, 0xfa, 0xe7, + 0x63, 0x66, 0x66, 0x34, 0x33, 0xd0, 0x48, 0x33, 0x83, 0xe5, 0x7f, 0xfb, + 0x7d, 0xa3, 0x19, 0x60, 0xc6, 0xfd, 0x1b, 0xe4, 0x7e, 0xdb, 0xb0, 0xff, + 0x91, 0x46, 0x98, 0x19, 0xf3, 0xef, 0xb8, 0x3d, 0x6, 0x23, 0xe2, 0x6a, + 0xc6, 0x70, 0x35, 0x3e, 0xf7, 0x7b, 0xc8, 0xcf, 0xe2, 0x99, 0x46, 0x3, + 0x60, 0xb4, 0xbc, 0xa1, 0x2e, 0x8f, 0xd7, 0x31, 0x17, 0x33, 0x43, 0x3d, + 0xd0, 0x64, 0xee, 0x66, 0x60, 0xdc, 0xbf, 0xaf, 0x30, 0x1a, 0xd, 0x96, + 0xef, 0xcd, 0x21, 0x73, 0x3a, 0x24, 0xc, 0x31, 0xf9, 0xfd, 0xec, 0xb8, + 0x2, 0x32, 0x1e, 0xe4, 0x2e, 0xce, 0xb1, 0xf2, 0x87, 0x98, 0xb6, 0x4b, + 0xd3, 0xc8, 0x42, 0xc7, 0xcf, 0x5e, 0xeb, 0x7e, 0x6a, 0xda, 0xd0, 0x48, + 0xbb, 0x4e, 0x26, 0xca, 0x6a, 0x6, 0xee, 0x79, 0xd5, 0xf6, 0xe4, 0xac, + 0x7a, 0x31, 0xf5, 0x4c, 0xc4, 0xbc, 0x19, 0xf6, 0xb6, 0x71, 0xed, 0xfe, + 0xe3, 0xe3, 0xee, 0xdb, 0xae, 0x6e, 0xa6, 0xe, 0x31, 0x66, 0x41, 0xf6, + 0x3e, 0xe4, 0x4, 0xb8, 0x27, 0xbd, 0x2f, 0x67, 0x3d, 0xc4, 0x61, 0xf1, + 0x1f, 0xdb, 0x8, 0xb6, 0x8d, 0x5c, 0xe3, 0xc6, 0xec, 0x19, 0xd7, 0x6e, + 0x3, 0xa7, 0x71, 0xcd, 0x58, 0x93, 0x91, 0xfd, 0xcf, 0x5, 0x89, 0x81, + 0x28, 0xf3, 0x8b, 0x1d, 0x8, 0x67, 0xe6, 0xe1, 0x3b, 0x34, 0x4f, 0x43, + 0xc6, 0x67, 0x3e, 0x96, 0x17, 0xae, 0xc2, 0x9a, 0x5c, 0xb8, 0x42, 0x18, + 0xb4, 0x36, 0xde, 0xf7, 0xa6, 0x91, 0xe2, 0x52, 0xd3, 0xb9, 0xb6, 0x73, + 0xf7, 0x7b, 0x34, 0xf1, 0x36, 0x4f, 0xc7, 0x90, 0xcf, 0xd3, 0x16, 0x79, + 0x1, 0xf, 0x5f, 0x8f, 0x8d, 0x85, 0xe7, 0x1a, 0xe2, 0x4e, 0x57, 0xc7, + 0x8b, 0xdf, 0xe7, 0xf8, 0xb4, 0xf8, 0xcd, 0x32, 0x66, 0x99, 0x4a, 0xe, + 0xd8, 0x87, 0xa4, 0x3a, 0xf5, 0x61, 0xdd, 0xe3, 0xad, 0xdc, 0x0, 0xce, + 0xb, 0xc7, 0xa6, 0xcc, 0xb, 0x64, 0x6c, 0x5e, 0xf7, 0x8d, 0x6b, 0x6b, + 0x65, 0x78, 0x3c, 0x37, 0xd, 0xa5, 0xef, 0xe2, 0x18, 0xd3, 0xae, 0xbf, + 0x65, 0xfb, 0xea, 0xb3, 0x73, 0x69, 0x9e, 0xa7, 0xf0, 0x1a, 0xb9, 0xce, + 0x67, 0xdf, 0x6f, 0x56, 0xe, 0x46, 0x9d, 0xc3, 0x31, 0x5, 0x97, 0xb8, + 0xf6, 0x30, 0xc4, 0xe5, 0x34, 0x26, 0x87, 0xeb, 0xba, 0x3c, 0xec, 0xc2, + 0x11, 0x9d, 0x9f, 0x9b, 0x23, 0x33, 0xe5, 0xb5, 0xf, 0xc7, 0x14, 0xc6, + 0xbd, 0x1f, 0xbe, 0x3c, 0x20, 0xe2, 0x5, 0xc3, 0xab, 0xcf, 0x15, 0x65, + 0xb0, 0xe8, 0x78, 0x3e, 0x3f, 0xbd, 0x3, 0xf1, 0x9c, 0x64, 0xff, 0xc5, + 0x81, 0x38, 0xe3, 0xec, 0x32, 0x43, 0x60, 0x6c, 0x1e, 0x50, 0x5b, 0xb2, + 0x91, 0x89, 0x2, 0x39, 0x31, 0xf2, 0x39, 0x27, 0xf6, 0x29, 0xb1, 0xd7, + 0xa6, 0xc6, 0x75, 0xcc, 0x3d, 0x48, 0x68, 0x29, 0xb7, 0xe0, 0x6b, 0x9b, + 0x2f, 0x87, 0x23, 0xe6, 0xc8, 0x66, 0x66, 0x1e, 0xe3, 0xe5, 0xb9, 0xd4, + 0xa8, 0x3, 0x50, 0x62, 0xb2, 0xdc, 0xe7, 0x6d, 0x41, 0x0, 0xcf, 0x93, + 0xc9, 0xb2, 0xd5, 0x74, 0x4f, 0xd8, 0xfb, 0x10, 0x3d, 0x76, 0x6e, 0x3a, + 0x1d, 0x61, 0x1f, 0xd2, 0x77, 0x58, 0x2f, 0x78, 0xe5, 0x36, 0x38, 0xbd, + 0x42, 0x38, 0x5d, 0x68, 0x41, 0x62, 0x4c, 0x58, 0x8e, 0x38, 0x62, 0x4b, + 0x45, 0x3e, 0xd6, 0xc4, 0xdc, 0x10, 0x48, 0x40, 0xf1, 0x55, 0x79, 0x66, + 0xc1, 0xcb, 0x3c, 0x61, 0x19, 0xd6, 0x18, 0x56, 0x25, 0x24, 0x2a, 0x70, + 0xe3, 0xa9, 0xe5, 0x7d, 0x61, 0x70, 0x73, 0x33, 0xc0, 0x3, 0x56, 0xf6, + 0x73, 0x5d, 0x8c, 0xb8, 0xd7, 0x2d, 0xd1, 0xa7, 0xad, 0x6a, 0xee, 0x6c, + 0x8e, 0x70, 0x60, 0xf4, 0x1, 0xcc, 0x46, 0x34, 0xc6, 0x16, 0xdc, 0x24, + 0xc, 0xc5, 0xe6, 0x3a, 0x13, 0x9f, 0x63, 0x2d, 0xe9, 0x18, 0x89, 0x3a, + 0x65, 0x58, 0x17, 0xf3, 0x21, 0x3e, 0xf2, 0xe0, 0x3c, 0x3d, 0x45, 0x8a, + 0xf3, 0x16, 0xc, 0x23, 0x81, 0x44, 0xb9, 0x12, 0xc5, 0xc7, 0x82, 0x97, + 0xe5, 0xa1, 0xd2, 0xad, 0xf2, 0x7d, 0xdd, 0x87, 0xce, 0x3a, 0xf1, 0xac, + 0x9, 0x86, 0xe1, 0xe3, 0x54, 0xe5, 0xa4, 0x6b, 0x43, 0xe, 0xcc, 0xad, + 0xf0, 0x49, 0x36, 0xf4, 0x59, 0x46, 0x13, 0x33, 0x30, 0x1c, 0x1, 0x61, + 0x18, 0xec, 0xf7, 0x8c, 0x5, 0x93, 0x8d, 0x83, 0x82, 0x61, 0x14, 0xef, + 0xc5, 0x3, 0xcc, 0x3c, 0x26, 0x6, 0x20, 0x16, 0xb4, 0x5f, 0x7b, 0xd9, + 0xd4, 0x65, 0x2e, 0x7a, 0x78, 0x93, 0x94, 0xc6, 0x6f, 0x19, 0x93, 0x9, + 0xae, 0x71, 0xba, 0xdd, 0x51, 0x76, 0x80, 0x79, 0xe3, 0xb4, 0x8b, 0x33, + 0x79, 0xb8, 0x6b, 0x12, 0xe7, 0x7a, 0x8e, 0x37, 0x29, 0x96, 0xcd, 0x1f, + 0x94, 0x92, 0x3e, 0xe, 0x2a, 0x2, 0xf3, 0x11, 0xb0, 0x52, 0xa1, 0x86, + 0x1d, 0x3b, 0xcb, 0x2e, 0x71, 0xfb, 0x88, 0x5a, 0x38, 0x78, 0x6, 0xd9, + 0x64, 0x9e, 0x48, 0x77, 0x6d, 0x9f, 0x4b, 0x38, 0xe0, 0xc0, 0x91, 0x33, + 0xe2, 0xf1, 0x5f, 0xa0, 0xe8, 0xa2, 0x45, 0xf, 0xfc, 0xec, 0xd3, 0x6b, + 0xa0, 0xc1, 0xcd, 0x1e, 0xd4, 0xcf, 0x2e, 0x68, 0x92, 0x4c, 0x83, 0x2f, + 0xfc, 0xaf, 0x10, 0xfb, 0xc3, 0xeb, 0x6b, 0xf7, 0x39, 0x23, 0x39, 0x8f, + 0x6b, 0xf4, 0x2f, 0xea, 0xc9, 0xcb, 0xec, 0x49, 0xc6, 0x82, 0x19, 0x84, + 0xd4, 0x8e, 0xc3, 0x63, 0xf, 0xce, 0x24, 0xfb, 0x79, 0x71, 0xa3, 0x13, + 0xb7, 0x6b, 0x23, 0x33, 0xc, 0xb0, 0x36, 0xf6, 0x84, 0x86, 0x7b, 0x89, + 0xca, 0x97, 0x38, 0xcd, 0x8, 0x79, 0x5e, 0xcc, 0x1d, 0xa0, 0x7d, 0x9c, + 0x3c, 0xe1, 0xfa, 0xc0, 0x6c, 0x74, 0xda, 0xa7, 0x33, 0x5, 0x7e, 0xc1, + 0x9d, 0x37, 0xc, 0x9d, 0xdc, 0xad, 0x71, 0xf8, 0x31, 0x1, 0xeb, 0x6c, + 0xa1, 0x49, 0x13, 0xec, 0xf7, 0xc1, 0x4f, 0xdc, 0x33, 0x89, 0x7f, 0xe7, + 0xfb, 0xe7, 0x7a, 0x7a, 0x5d, 0xe5, 0xdc, 0xc0, 0x38, 0x31, 0x7, 0xeb, + 0x6c, 0x82, 0x47, 0xe5, 0x49, 0x7, 0xd9, 0xc5, 0xcd, 0x65, 0x9f, 0x46, + 0x91, 0xb, 0x59, 0x99, 0x10, 0xec, 0x17, 0x52, 0x62, 0x37, 0x37, 0xfd, + 0x81, 0xac, 0xda, 0x8b, 0x27, 0x7a, 0xf1, 0xab, 0x8f, 0x47, 0xba, 0xb3, + 0xe3, 0x88, 0xf2, 0x3c, 0x8e, 0xf4, 0xaa, 0x9d, 0x28, 0x3e, 0x75, 0x4d, + 0x97, 0x22, 0x2, 0xc8, 0xba, 0x90, 0x46, 0xe4, 0x99, 0xea, 0x99, 0xe4, + 0xe3, 0x1d, 0x56, 0x4f, 0x1a, 0x69, 0x7, 0x7, 0xb7, 0xf3, 0x24, 0x24, + 0xd6, 0x29, 0xf5, 0xa4, 0xa4, 0x81, 0x49, 0xca, 0x18, 0x46, 0xcd, 0x41, + 0xe, 0xfe, 0xa2, 0x4, 0x69, 0xbf, 0x14, 0x72, 0x95, 0x3c, 0x64, 0x60, + 0x31, 0x25, 0xb7, 0xd7, 0x60, 0x75, 0x64, 0x2c, 0x23, 0x8f, 0x87, 0x66, + 0x69, 0x63, 0x13, 0x86, 0xa3, 0xb2, 0x49, 0x26, 0x83, 0x6f, 0x15, 0x97, + 0x8a, 0xf5, 0x5e, 0x34, 0xdb, 0x9b, 0xfc, 0x8e, 0x75, 0xb, 0x48, 0xe0, + 0x41, 0x22, 0x13, 0xa6, 0x9, 0xd8, 0xc7, 0x7c, 0xe3, 0x75, 0xe7, 0xf0, + 0x31, 0x88, 0x84, 0xbf, 0x1c, 0x86, 0x10, 0x32, 0x42, 0x98, 0xd3, 0x9b, + 0xb0, 0xf0, 0x3a, 0x28, 0xe5, 0x20, 0x95, 0xae, 0xe5, 0xaa, 0x1c, 0x66, + 0x70, 0x3b, 0xed, 0x66, 0xa4, 0x39, 0x32, 0x23, 0x61, 0xe7, 0xf0, 0x50, + 0x22, 0x28, 0x51, 0x8a, 0x34, 0xc0, 0xca, 0x49, 0x2b, 0xdb, 0xe, 0xab, + 0xec, 0x45, 0x46, 0x68, 0x57, 0xb1, 0xb, 0xb0, 0x4d, 0x1c, 0xca, 0x7d, + 0x8f, 0xb4, 0x4c, 0x13, 0xc4, 0xdc, 0xae, 0x26, 0x5e, 0x5b, 0xc7, 0x40, + 0x18, 0xbf, 0x51, 0x56, 0xb9, 0x45, 0x3a, 0x5f, 0x1d, 0x26, 0xb0, 0xe0, + 0x83, 0x25, 0x12, 0x5, 0xb9, 0x24, 0x7, 0xf4, 0x50, 0x8c, 0xa2, 0xaa, + 0x8, 0xe5, 0x19, 0x0, 0x36, 0xf9, 0x4e, 0xf2, 0x39, 0x42, 0x4e, 0xf0, + 0x9, 0x8a, 0x3c, 0x91, 0x50, 0x7, 0x46, 0xe6, 0x62, 0x5, 0x61, 0xc1, + 0x9, 0x68, 0x50, 0x54, 0x0, 0x83, 0x5c, 0xf1, 0xca, 0x6b, 0x37, 0xe3, + 0xe6, 0x7e, 0x88, 0x12, 0x5b, 0x50, 0x4, 0x3e, 0x9, 0xa9, 0x69, 0x10, + 0xa4, 0xe8, 0xb4, 0x27, 0x6f, 0x7e, 0xa7, 0x82, 0xac, 0x53, 0xe3, 0x2, + 0x9e, 0xad, 0x63, 0xf8, 0xc0, 0xfb, 0x74, 0x28, 0xd1, 0x3, 0x72, 0x6f, + 0xd5, 0x98, 0x22, 0x0, 0xa9, 0xce, 0x31, 0x28, 0x16, 0x61, 0x80, 0x7, + 0x2b, 0x87, 0x30, 0x1b, 0x88, 0x40, 0x46, 0x39, 0xcd, 0x49, 0xb0, 0x3a, + 0xf4, 0x96, 0x40, 0x44, 0x1b, 0x2a, 0x3, 0x2b, 0x83, 0xe9, 0xe7, 0x25, + 0x33, 0x57, 0xcd, 0x7, 0x6a, 0xe7, 0xca, 0x3c, 0x78, 0x69, 0x30, 0x90, + 0x3, 0x76, 0x5e, 0xc1, 0x12, 0xea, 0xdc, 0x3a, 0x71, 0x92, 0x48, 0xad, + 0x5a, 0x89, 0x44, 0xf6, 0x8f, 0xf3, 0x16, 0x7c, 0x70, 0x32, 0x58, 0x53, + 0xe9, 0x57, 0x36, 0x5b, 0x88, 0x15, 0x2f, 0x31, 0xa4, 0x65, 0xd7, 0xfa, + 0x8c, 0x37, 0xf9, 0x9b, 0x24, 0x4c, 0x3f, 0xff, 0xa5, 0xa4, 0x21, 0x37, + 0xf1, 0x57, 0x31, 0xe4, 0x67, 0x30, 0x3d, 0x9, 0x1c, 0x9f, 0x2, 0x18, + 0x87, 0x96, 0xd3, 0x76, 0xbc, 0xdf, 0xe4, 0xb, 0x87, 0x65, 0xb3, 0xed, + 0x67, 0x4d, 0xeb, 0x9e, 0x11, 0x7f, 0x9a, 0x39, 0x64, 0x53, 0xdf, 0x1c, + 0x21, 0xaf, 0xb8, 0x9, 0xf2, 0x54, 0xd, 0x75, 0xe6, 0x95, 0x1d, 0xe0, + 0x9c, 0x0, 0x44, 0xb8, 0x18, 0xd3, 0xc4, 0x73, 0xb3, 0x5f, 0x52, 0xd0, + 0x89, 0x77, 0x6f, 0x1a, 0xf4, 0xef, 0x54, 0xef, 0x7a, 0x9f, 0xa7, 0x68, + 0xf5, 0x1e, 0x13, 0xe7, 0x34, 0xf0, 0x18, 0xef, 0xa9, 0x77, 0x35, 0xf1, + 0x7d, 0xd9, 0x66, 0x5a, 0x0, 0x66, 0x3f, 0x49, 0x34, 0xbf, 0x10, 0x3a, + 0xe5, 0x5e, 0xf6, 0xae, 0x68, 0xfc, 0x40, 0x20, 0x69, 0x3e, 0x98, 0xd3, + 0x14, 0xc1, 0x4e, 0x3d, 0xf3, 0x91, 0x68, 0x5d, 0xd3, 0xfb, 0xfc, 0xfd, + 0xf7, 0x5f, 0xa2, 0x58, 0x76, 0xa1, 0x4, 0x87, 0xb7, 0x20, 0x8b, 0x2c, + 0x99, 0x6a, 0x50, 0x70, 0xad, 0x84, 0x90, 0x43, 0x23, 0xd4, 0x27, 0x43, + 0x9f, 0x91, 0x1a, 0xbe, 0x14, 0x98, 0x6, 0x79, 0xe2, 0x95, 0xfa, 0x40, + 0x8b, 0x33, 0xf, 0x41, 0x9, 0xbc, 0xa9, 0x65, 0x69, 0x97, 0x10, 0x72, + 0x98, 0xf8, 0xa9, 0xaa, 0x1d, 0x45, 0x49, 0x51, 0x11, 0x4a, 0x89, 0xb0, + 0x14, 0x93, 0x4e, 0xbe, 0xd2, 0xc6, 0x45, 0xc3, 0x22, 0xe5, 0xb9, 0x53, + 0x8, 0x10, 0x61, 0x73, 0x66, 0x2c, 0xca, 0xc3, 0x1a, 0x76, 0xc4, 0x9e, + 0x98, 0xaa, 0x28, 0x8e, 0xa2, 0x9f, 0xaa, 0xb8, 0xc3, 0x12, 0x12, 0xda, + 0x15, 0x1c, 0x72, 0xed, 0x9f, 0xff, 0xfc, 0xe7, 0xaf, 0xa1, 0x48, 0xe1, + 0x87, 0xe8, 0xab, 0x72, 0x13, 0x65, 0x3, 0xf4, 0x1a, 0x2c, 0x94, 0xea, + 0x83, 0x63, 0xa4, 0x96, 0x41, 0x61, 0xb, 0xa1, 0x16, 0xa2, 0xd3, 0x4f, + 0xc8, 0xc9, 0x5b, 0xf1, 0x2c, 0x60, 0x99, 0x99, 0xd9, 0x5a, 0x7f, 0x18, + 0xf2, 0x7a, 0x98, 0xad, 0xb5, 0xc, 0x58, 0xc3, 0xbb, 0x81, 0xf7, 0xe4, + 0x1, 0x33, 0xd8, 0x92, 0x22, 0xa4, 0xd6, 0x14, 0x31, 0xab, 0xa0, 0x45, + 0x9e, 0x8e, 0x13, 0x93, 0xbc, 0xe9, 0x11, 0x4a, 0x36, 0xbf, 0x69, 0x7e, + 0xf0, 0x75, 0x1a, 0xdd, 0x8d, 0x1, 0x3, 0x5f, 0xba, 0xf0, 0x12, 0xcc, + 0xc2, 0x28, 0xbd, 0x45, 0x1c, 0x11, 0x8d, 0x8a, 0x78, 0x5e, 0x18, 0x47, + 0x73, 0x3f, 0x34, 0x98, 0x23, 0x5b, 0x1f, 0x15, 0xd0, 0x2b, 0x8c, 0x41, + 0x20, 0xa1, 0xc7, 0xfd, 0xfc, 0xf3, 0xfd, 0xf6, 0x75, 0x7a, 0x40, 0x94, + 0x4c, 0x0, 0x3f, 0x2b, 0xd2, 0x75, 0xc9, 0x32, 0x23, 0x6b, 0x3, 0x73, + 0x30, 0xbd, 0x73, 0xad, 0xbd, 0x53, 0x1e, 0x94, 0x5e, 0xa5, 0x4d, 0xd, + 0x86, 0x5e, 0x4e, 0x45, 0xc3, 0x5a, 0xc6, 0xef, 0x3f, 0x6, 0x6c, 0xe7, + 0x31, 0xc0, 0xbe, 0x4e, 0x5b, 0x8, 0xce, 0x8e, 0x59, 0x82, 0xd6, 0xd3, + 0xda, 0xf2, 0x82, 0x87, 0xb1, 0x56, 0x33, 0xe4, 0xcc, 0x1e, 0x32, 0xc2, + 0x88, 0xe4, 0xea, 0x2f, 0x2d, 0x20, 0xd, 0x17, 0x93, 0x74, 0xf7, 0xe9, + 0x24, 0x1, 0xb4, 0x6e, 0xbe, 0x55, 0xbc, 0xd8, 0x78, 0xa7, 0x17, 0x69, + 0xcc, 0xea, 0xa3, 0x66, 0x1c, 0xfb, 0x7d, 0xef, 0xc, 0xca, 0x93, 0xa3, + 0xf8, 0x21, 0x80, 0x29, 0xf4, 0x8, 0x71, 0x27, 0x8f, 0x8a, 0xe8, 0xe1, + 0xa5, 0x52, 0xca, 0x3f, 0x6b, 0x7f, 0xa9, 0x98, 0x7e, 0x86, 0xca, 0x45, + 0xbb, 0xca, 0xb4, 0x18, 0xf5, 0xed, 0x37, 0x25, 0x81, 0xc1, 0x0, 0xbb, + 0x2a, 0x58, 0x55, 0xd2, 0x84, 0xd9, 0xc2, 0xea, 0x88, 0x43, 0x81, 0x13, + 0xde, 0xd5, 0xc8, 0xb5, 0x56, 0x11, 0x13, 0x7a, 0x17, 0x2e, 0x7c, 0xfd, + 0x91, 0xc9, 0xaa, 0x39, 0x57, 0xe9, 0xd, 0x55, 0x97, 0x8f, 0xec, 0x43, + 0x75, 0xfd, 0x3e, 0xe0, 0x9d, 0x6d, 0x38, 0xf1, 0x4b, 0x85, 0x6f, 0xa6, + 0x7a, 0x76, 0x62, 0x29, 0xf7, 0x69, 0xa4, 0xa8, 0x98, 0xe5, 0x4, 0x76, + 0xbf, 0x6e, 0x68, 0xe4, 0x35, 0x3e, 0x1f, 0xc2, 0xda, 0xc9, 0x6b, 0x7a, + 0x2e, 0xcb, 0x2c, 0x9d, 0x96, 0x67, 0x34, 0x78, 0xd1, 0xcc, 0xa1, 0x29, + 0x1b, 0x5f, 0x27, 0x37, 0xb6, 0x2a, 0xa, 0x48, 0x52, 0x1c, 0xc9, 0x80, + 0xc, 0x93, 0x9a, 0x74, 0x1e, 0xee, 0x59, 0xa5, 0xc2, 0xca, 0x18, 0x8c, + 0x9b, 0xf5, 0xc7, 0x6, 0x2f, 0x69, 0x38, 0x31, 0xeb, 0x62, 0x54, 0x87, + 0x24, 0x89, 0x14, 0x71, 0xd2, 0x6b, 0x29, 0xfe, 0xdd, 0x91, 0x20, 0x6e, + 0xa2, 0x7f, 0x6d, 0x2d, 0xc4, 0xc9, 0xda, 0x1a, 0xc0, 0xc2, 0xda, 0xbf, + 0x1f, 0xc4, 0x6b, 0x19, 0xaa, 0x6a, 0x47, 0xa5, 0xec, 0x19, 0xa1, 0x3a, + 0x3f, 0x9d, 0xc4, 0xaa, 0x1a, 0x49, 0x24, 0x3a, 0xd1, 0x7b, 0xc3, 0xd3, + 0xf0, 0xec, 0x14, 0xb6, 0xdf, 0xf7, 0xd2, 0xa4, 0x70, 0x46, 0x90, 0x74, + 0x6a, 0x9, 0xfb, 0x9c, 0x4c, 0xa9, 0xa, 0x6f, 0xb0, 0xad, 0xc3, 0x4c, + 0x38, 0xba, 0xcb, 0xf1, 0x69, 0x77, 0x2d, 0x7e, 0xf5, 0x29, 0xbf, 0x25, + 0x7f, 0xc6, 0x3e, 0x96, 0x7b, 0xb7, 0x62, 0xd8, 0x9e, 0x51, 0xc4, 0x2f, + 0xf3, 0xef, 0x30, 0xc8, 0x59, 0x77, 0xc7, 0x42, 0xd4, 0x4b, 0x53, 0x57, + 0x40, 0x9c, 0xf8, 0xf6, 0xfc, 0x5, 0x81, 0x94, 0xf0, 0xb5, 0x25, 0xc4, + 0x29, 0x17, 0x93, 0x1c, 0xa1, 0x46, 0x77, 0x8f, 0xd0, 0xbd, 0x8b, 0x44, + 0xa0, 0x99, 0xad, 0xfd, 0xac, 0x84, 0x5f, 0xc4, 0xc8, 0xdf, 0xd8, 0x48, + 0x2c, 0x54, 0x1, 0x27, 0x21, 0xc6, 0x63, 0x2d, 0xb, 0xb8, 0xb2, 0x0, + 0xad, 0xe4, 0x2a, 0xb1, 0x4c, 0xc3, 0x38, 0xbc, 0x4, 0x31, 0x56, 0x51, + 0x88, 0x72, 0xba, 0x13, 0x9f, 0x7d, 0x34, 0x6a, 0x24, 0x77, 0x28, 0x55, + 0x54, 0x1a, 0x5e, 0x70, 0x34, 0xc2, 0xd4, 0x48, 0x87, 0x58, 0x94, 0x5c, + 0x3, 0x29, 0x48, 0xa5, 0x2a, 0xeb, 0xd6, 0x75, 0x1e, 0x49, 0xe3, 0x48, + 0xcf, 0x19, 0x59, 0xaa, 0x7a, 0xac, 0xb4, 0xf7, 0x91, 0x87, 0x79, 0x8f, + 0xb1, 0x15, 0xc3, 0xb, 0xf7, 0xc4, 0x30, 0x52, 0x32, 0xee, 0x54, 0xab, + 0xab, 0x76, 0xad, 0xb3, 0xc7, 0x66, 0xc6, 0xa2, 0x11, 0x47, 0x7d, 0x14, + 0x34, 0x56, 0xe6, 0x1e, 0x6b, 0xf2, 0xb, 0xd8, 0xd0, 0xc4, 0x56, 0xd5, + 0xe3, 0xbd, 0xf1, 0xbe, 0x42, 0x87, 0xc0, 0xd, 0x66, 0x45, 0xfd, 0xeb, + 0xe8, 0xc8, 0xc4, 0xde, 0x7c, 0xba, 0xd9, 0xd7, 0x83, 0x54, 0x26, 0x16, + 0x7, 0x59, 0x74, 0xeb, 0xe, 0xa1, 0xa, 0xad, 0xfe, 0x95, 0x46, 0x20, + 0x1e, 0x9d, 0x38, 0x8d, 0xc1, 0x1b, 0xeb, 0x6d, 0x94, 0x90, 0x4d, 0xfa, + 0x28, 0xf6, 0x3f, 0x1f, 0x85, 0x12, 0x37, 0xdc, 0xf9, 0xfe, 0x20, 0x78, + 0xa1, 0x78, 0xba, 0x10, 0xc4, 0x80, 0x50, 0x17, 0x16, 0xdc, 0xd7, 0x70, + 0xf0, 0x2f, 0xe3, 0x8e, 0x34, 0xdd, 0x4a, 0x16, 0xab, 0xc0, 0x2f, 0x29, + 0x6b, 0x2a, 0x86, 0x33, 0x71, 0x54, 0xa1, 0xf1, 0x28, 0xba, 0x64, 0xb3, + 0x49, 0x6e, 0xb4, 0x56, 0xa9, 0x10, 0xa7, 0xb6, 0x3e, 0x97, 0xae, 0xa6, + 0x95, 0x5d, 0x16, 0x6b, 0x28, 0x6f, 0xda, 0xbf, 0x87, 0xed, 0x42, 0xe6, + 0x64, 0x45, 0x13, 0x85, 0x29, 0xe, 0x43, 0x33, 0x8c, 0x2, 0x5b, 0x4b, + 0x20, 0xe0, 0xcb, 0xda, 0x6c, 0x3, 0xec, 0x8b, 0x3, 0x93, 0xa1, 0xa9, + 0xe8, 0xf7, 0x61, 0xf, 0x9a, 0x5a, 0xe0, 0x55, 0x9d, 0xdc, 0xe2, 0x5a, + 0x94, 0xc1, 0x7f, 0xa8, 0x5e, 0x9e, 0xe6, 0xa4, 0xf7, 0x46, 0xed, 0xd7, + 0x2f, 0x2c, 0x6f, 0x52, 0x78, 0x8e, 0x91, 0xf7, 0x4f, 0xae, 0xb1, 0x9, + 0x2d, 0x33, 0xfb, 0xc0, 0xec, 0xc1, 0xa4, 0x49, 0x9b, 0xdd, 0x55, 0x2d, + 0x3d, 0x1a, 0x4d, 0xbd, 0x9a, 0x17, 0x8e, 0x9c, 0x20, 0x59, 0xb6, 0x2a, + 0x84, 0xa9, 0xb, 0x4, 0xcb, 0xe6, 0x51, 0x81, 0x83, 0x6d, 0xfc, 0xde, + 0x38, 0xce, 0x4a, 0xe9, 0x1a, 0x77, 0x29, 0xc2, 0x15, 0x8e, 0x3a, 0x8e, + 0x57, 0x16, 0xe2, 0xcc, 0x4c, 0xc2, 0x24, 0xdc, 0x62, 0xe6, 0x7d, 0x71, + 0x8f, 0x7f, 0x3, 0x41, 0x57, 0x53, 0x1a, 0x8f, 0x1a, 0xc7, 0xe6, 0xb, + 0x3c, 0xa, 0x8a, 0x1c, 0xd, 0xa1, 0x75, 0x1a, 0xd5, 0xa9, 0x39, 0xd3, + 0xa8, 0xaa, 0xef, 0xfb, 0xb7, 0xe, 0xc1, 0x4c, 0x1f, 0xb3, 0x78, 0x83, + 0x43, 0x7d, 0x54, 0x8d, 0x85, 0xcd, 0x15, 0xf0, 0x70, 0x30, 0x1e, 0x1c, + 0xec, 0x24, 0x91, 0xfc, 0x8e, 0xd4, 0x8f, 0xda, 0xaf, 0x39, 0x7a, 0x18, + 0x24, 0x72, 0xc5, 0xde, 0xb4, 0x68, 0xe9, 0xdd, 0x87, 0x29, 0x7b, 0xf0, + 0xb9, 0x18, 0x3d, 0x36, 0x96, 0xee, 0x93, 0xfb, 0x60, 0xdf, 0x48, 0x96, + 0xaa, 0x69, 0x1d, 0xa2, 0x26, 0xe0, 0xc1, 0xec, 0x25, 0x79, 0xa5, 0x14, + 0x86, 0x2, 0x22, 0x56, 0x85, 0xf2, 0xae, 0x42, 0x26, 0xf4, 0xa4, 0xe3, + 0x38, 0x39, 0xc9, 0x9e, 0x74, 0x36, 0x79, 0xf0, 0x88, 0x21, 0xb, 0x65, + 0xa4, 0xa9, 0x28, 0x6, 0xfb, 0xf2, 0x5b, 0x27, 0x70, 0xd9, 0x8a, 0x8d, + 0xa7, 0x68, 0xf7, 0xc1, 0x45, 0xa8, 0xb5, 0x77, 0xc1, 0x1a, 0x77, 0xa9, + 0x8b, 0xf5, 0x66, 0xce, 0xca, 0x61, 0xc3, 0x41, 0x1d, 0x28, 0x33, 0xa9, + 0xf8, 0x69, 0xaf, 0xc2, 0x76, 0x56, 0xf7, 0xce, 0x5e, 0x5c, 0xda, 0x55, + 0x2b, 0x95, 0x8f, 0xea, 0x25, 0xb5, 0xc1, 0x37, 0xf9, 0x91, 0xcc, 0x9f, + 0x7, 0xa3, 0x3d, 0xf9, 0x5, 0xa9, 0xdd, 0xd3, 0x8f, 0xb6, 0xb8, 0x78, + 0xf1, 0x69, 0x6c, 0x3a, 0x19, 0x64, 0x8b, 0xe, 0x59, 0xbd, 0xb3, 0x5, + 0x9, 0xab, 0xe9, 0x7d, 0x28, 0xe3, 0x60, 0x61, 0xb6, 0x7d, 0x1d, 0xd9, + 0x19, 0x2d, 0xcb, 0xc8, 0xab, 0xc3, 0x5a, 0x42, 0x4, 0x6c, 0x74, 0xf0, + 0x36, 0x86, 0x66, 0x67, 0xd1, 0x2e, 0x7b, 0x42, 0xd5, 0x4c, 0x4c, 0xb1, + 0x35, 0xcb, 0xaf, 0x6b, 0x94, 0x9e, 0xd9, 0x3d, 0x46, 0x9, 0x21, 0x23, + 0x7d, 0x3c, 0xba, 0x83, 0x19, 0x39, 0xfa, 0xe8, 0x5b, 0x3c, 0xea, 0x29, + 0xd4, 0x12, 0xb3, 0x14, 0x94, 0x20, 0xfd, 0x8f, 0x6e, 0xcd, 0x39, 0xb4, + 0x47, 0x21, 0xe0, 0xae, 0x49, 0x66, 0x6f, 0x58, 0xb5, 0xd1, 0x9, 0x2a, + 0x38, 0xf5, 0x20, 0x56, 0x2d, 0xaa, 0xda, 0x8b, 0x67, 0xeb, 0x1d, 0xec, + 0xa8, 0xca, 0x95, 0x9e, 0x51, 0xfc, 0x4e, 0x78, 0x45, 0x15, 0x90, 0xe8, + 0xbc, 0xc2, 0xc8, 0x60, 0xcd, 0xe8, 0x8d, 0x37, 0xdf, 0x9b, 0xd6, 0x61, + 0x72, 0x63, 0xb2, 0xa6, 0x42, 0x48, 0x66, 0x2b, 0x92, 0xb2, 0x15, 0x63, + 0xd7, 0xeb, 0xb2, 0x61, 0xc2, 0xa5, 0xb5, 0x1b, 0xd5, 0x64, 0x72, 0x70, + 0xe9, 0x71, 0xfa, 0x36, 0x1c, 0xb5, 0xa7, 0x9f, 0xe2, 0xbe, 0xd7, 0x77, + 0x8, 0x6e, 0xc1, 0x1c, 0xe4, 0xcd, 0x6, 0xf0, 0x2e, 0x62, 0x9c, 0xd9, + 0x44, 0xe2, 0xfb, 0x6c, 0x19, 0xe7, 0xa5, 0xfb, 0xd3, 0x3b, 0x5c, 0xab, + 0xea, 0x77, 0x6a, 0x9, 0x90, 0x36, 0x47, 0x6d, 0x7, 0x87, 0x54, 0x4c, + 0xcb, 0xfe, 0xc7, 0xf7, 0x1c, 0xfc, 0x1c, 0xd3, 0x39, 0xca, 0xe2, 0x3d, + 0x58, 0x90, 0xc5, 0x83, 0x1f, 0x28, 0xe4, 0x7c, 0xf8, 0x68, 0xf2, 0xf6, + 0x4a, 0x6b, 0xb3, 0xd7, 0xbe, 0x31, 0xbe, 0xd5, 0xad, 0x26, 0x65, 0xf4, + 0x26, 0x5d, 0x9b, 0xfc, 0xa4, 0xec, 0xab, 0xd6, 0x5d, 0xb3, 0x69, 0x92, + 0x6e, 0xe0, 0xaa, 0xf0, 0xb7, 0x99, 0x7d, 0x5a, 0x3, 0x8d, 0xa5, 0xa9, + 0x44, 0xc, 0xfd, 0xc0, 0xd, 0xeb, 0x51, 0x62, 0x85, 0x34, 0x53, 0x88, + 0xbe, 0x31, 0xee, 0xb5, 0x77, 0x11, 0xe9, 0x59, 0xf5, 0xf1, 0x80, 0xb, + 0xd7, 0x2a, 0x68, 0x5a, 0xf2, 0xec, 0x5e, 0x3e, 0x26, 0x63, 0x53, 0xa, + 0x7e, 0x11, 0x50, 0x6a, 0xf6, 0xe1, 0x92, 0x5, 0x9c, 0x4a, 0xe4, 0xa8, + 0xc0, 0x66, 0x7f, 0x2, 0xca, 0x21, 0x5b, 0x4f, 0xf0, 0x51, 0xe4, 0xcb, + 0xd4, 0x51, 0x79, 0x8c, 0x5f, 0x49, 0x23, 0xfb, 0x7b, 0x7, 0xa5, 0xc, + 0x16, 0xbb, 0xe, 0x1e, 0xb1, 0xf2, 0x24, 0xa3, 0x1b, 0x33, 0xb4, 0xe3, + 0xd8, 0x44, 0xfd, 0x83, 0x7e, 0x73, 0x7, 0x1d, 0x9e, 0xb1, 0xca, 0x73, + 0x17, 0x32, 0xaf, 0x5d, 0x85, 0xed, 0x1a, 0x1d, 0xfa, 0x8b, 0x1d, 0x68, + 0xf2, 0x2, 0xb, 0xe5, 0xb0, 0x9d, 0x29, 0x1d, 0x7, 0x95, 0x57, 0x47, + 0xf3, 0xaa, 0xb4, 0xa0, 0x43, 0x8c, 0x89, 0x47, 0x3b, 0x1c, 0x9e, 0x64, + 0xf9, 0xa8, 0x92, 0x1e, 0xe, 0x52, 0x51, 0xc0, 0xa5, 0x51, 0x55, 0xf8, + 0x8e, 0xd6, 0xe2, 0xf3, 0x50, 0xb8, 0xbf, 0xdb, 0xee, 0x58, 0x4, 0x8f, + 0xfd, 0x2d, 0x28, 0xd1, 0x69, 0xce, 0x1a, 0x41, 0xf5, 0x6f, 0xc4, 0xe7, + 0x90, 0xb5, 0x79, 0x8, 0x28, 0x9a, 0x7d, 0xb8, 0x1c, 0xb8, 0x76, 0xe0, + 0xd9, 0xe1, 0x46, 0xa3, 0xfd, 0x17, 0x66, 0x56, 0x9d, 0xf1, 0xd6, 0xf2, + 0xa7, 0xd0, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82, + // /home/hyun/qps/icon/pause.png + 0x0, 0x0, 0x1, 0xf6, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x18, + 0x0, 0x0, 0x0, 0x18, 0x8, 0x6, 0x0, 0x0, 0x0, 0xe0, 0x77, 0x3d, + 0xf8, 0x0, 0x0, 0x0, 0x1, 0x73, 0x52, 0x47, 0x42, 0x0, 0xae, 0xce, + 0x1c, 0xe9, 0x0, 0x0, 0x0, 0x6, 0x62, 0x4b, 0x47, 0x44, 0x0, 0xff, + 0x0, 0xff, 0x0, 0xff, 0xa0, 0xbd, 0xa7, 0x93, 0x0, 0x0, 0x0, 0x9, + 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, 0x12, 0x0, 0x0, 0xb, 0x12, + 0x1, 0xd2, 0xdd, 0x7e, 0xfc, 0x0, 0x0, 0x0, 0x7, 0x74, 0x49, 0x4d, + 0x45, 0x7, 0xd8, 0x8, 0x4, 0x0, 0x22, 0x3, 0xb3, 0x10, 0xff, 0xee, + 0x0, 0x0, 0x1, 0x76, 0x49, 0x44, 0x41, 0x54, 0x48, 0xc7, 0xed, 0x55, + 0x4d, 0x4f, 0xc2, 0x40, 0x10, 0x7d, 0x33, 0x6d, 0x6d, 0xe4, 0x42, 0x89, + 0xb, 0x9, 0x11, 0x68, 0x9, 0xf5, 0x22, 0x21, 0x31, 0xd0, 0xfe, 0xff, + 0x4, 0x8f, 0x5e, 0x45, 0x3c, 0x90, 0x88, 0x9, 0x1c, 0x30, 0x4b, 0x53, + 0x83, 0xc0, 0x85, 0x38, 0x1e, 0x4, 0x23, 0x9f, 0xda, 0xea, 0x81, 0x3, + 0x2f, 0x99, 0x64, 0x77, 0x66, 0x76, 0xdf, 0xbc, 0xfd, 0x4, 0x4e, 0x38, + 0x56, 0x98, 0x9, 0x72, 0xcf, 0x52, 0x31, 0x84, 0x61, 0x28, 0x0, 0xbe, + 0x2c, 0xc, 0x43, 0x11, 0x91, 0x15, 0xb1, 0x78, 0x9e, 0x27, 0xbe, 0xef, + 0x8b, 0xef, 0xfb, 0xab, 0x9c, 0x44, 0x95, 0xb2, 0xd6, 0x1a, 0xad, 0x66, + 0x13, 0xe5, 0x72, 0x19, 0xfd, 0x7e, 0x1f, 0x5a, 0x6b, 0x10, 0xd1, 0x2, + 0x0, 0x94, 0x52, 0xb3, 0x7c, 0x3e, 0x9f, 0x71, 0x2b, 0x15, 0xc, 0x86, + 0x43, 0xf4, 0x7a, 0xbd, 0xbd, 0x85, 0xf2, 0x21, 0x15, 0x8d, 0x46, 0x3, + 0x2a, 0xaf, 0xd0, 0xa, 0x5a, 0x6b, 0x7e, 0xad, 0x75, 0x26, 0x8, 0x2, + 0xf8, 0x57, 0x3e, 0xea, 0xf5, 0xeb, 0x83, 0x2b, 0xc1, 0x3f, 0x2d, 0x95, + 0xba, 0x50, 0x30, 0xcd, 0x3d, 0x42, 0x5, 0x98, 0xcf, 0xe6, 0x28, 0x95, + 0x4a, 0x89, 0x9, 0xde, 0x1, 0x0, 0x44, 0x18, 0x8f, 0xc7, 0x60, 0xda, + 0x4e, 0x33, 0x98, 0xc1, 0x4c, 0xc8, 0x66, 0xb3, 0xa9, 0x14, 0x30, 0x33, + 0x47, 0xcc, 0x8c, 0x42, 0xa1, 0x0, 0xc3, 0x30, 0x36, 0xe3, 0x77, 0x96, + 0x65, 0x82, 0x88, 0x77, 0xc5, 0x12, 0x28, 0xf8, 0x87, 0x63, 0xbc, 0x77, + 0xf, 0x98, 0xf9, 0xed, 0xc0, 0x38, 0xfa, 0xf3, 0x85, 0x5a, 0x2c, 0x16, + 0x15, 0x83, 0x19, 0x96, 0x65, 0x80, 0x79, 0xab, 0x8e, 0x1b, 0xdb, 0xb6, + 0x11, 0x45, 0x63, 0x4c, 0xa7, 0xb3, 0xbf, 0x9d, 0x22, 0x0, 0x88, 0xe3, + 0x78, 0xd3, 0xf5, 0xb4, 0x6a, 0x38, 0x8e, 0x93, 0x9e, 0x80, 0x88, 0x40, + 0xc4, 0xc8, 0x6d, 0x4f, 0xa2, 0x3f, 0x37, 0x97, 0xf0, 0x1a, 0xc7, 0x18, + 0xc, 0x6, 0x29, 0x15, 0x10, 0xf0, 0x32, 0x1a, 0x1, 0x44, 0x3b, 0xc9, + 0x99, 0x0, 0x27, 0x97, 0x4b, 0xaf, 0xa0, 0xdb, 0x7d, 0x4, 0x1b, 0x6, + 0xee, 0x3b, 0x9d, 0x35, 0xbf, 0xeb, 0xba, 0x97, 0xed, 0xf6, 0xed, 0xce, + 0xd8, 0xaf, 0x51, 0xad, 0x56, 0xd7, 0x1e, 0xbb, 0x65, 0x1f, 0x0, 0xe0, + 0x79, 0xde, 0x43, 0xad, 0x56, 0x8b, 0x0, 0x3c, 0x7f, 0xcb, 0x49, 0x6, + 0x11, 0x51, 0x22, 0x42, 0xcb, 0x76, 0x51, 0x44, 0x8a, 0x93, 0xc9, 0xc4, + 0x16, 0x11, 0x12, 0x11, 0x6b, 0x69, 0x45, 0x11, 0xc9, 0x88, 0xc8, 0xf9, + 0xe9, 0x87, 0x3b, 0x5e, 0x7c, 0x0, 0x8d, 0xf8, 0x80, 0x7e, 0x5c, 0x10, + 0xaa, 0xf, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, + 0x60, 0x82, + // /home/hyun/qps/icon/preferences-system.png + 0x0, 0x0, 0xb, 0x34, 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa, + 0x0, 0x0, 0x0, 0xd, 0x49, 0x48, 0x44, 0x52, 0x0, 0x0, 0x0, 0x30, + 0x0, 0x0, 0x0, 0x30, 0x8, 0x6, 0x0, 0x0, 0x1, 0x20, 0x5, 0xc9, + 0x11, 0x0, 0x0, 0x0, 0x9, 0x70, 0x48, 0x59, 0x73, 0x0, 0x0, 0xb, + 0x13, 0x0, 0x0, 0xb, 0x13, 0x1, 0x0, 0x9a, 0x9c, 0x18, 0x0, 0x0, + 0x0, 0x4, 0x67, 0x41, 0x4d, 0x41, 0x0, 0x0, 0xd8, 0xeb, 0xf5, 0x1c, + 0x14, 0xaa, 0x0, 0x0, 0x0, 0x20, 0x63, 0x48, 0x52, 0x4d, 0x0, 0x0, + 0x6e, 0x35, 0x0, 0x0, 0x75, 0x37, 0x0, 0x0, 0xf6, 0xb7, 0x0, 0x0, + 0x87, 0x2a, 0x0, 0x0, 0x6d, 0x69, 0x0, 0x0, 0xe8, 0xa3, 0x0, 0x0, + 0x32, 0x2, 0x0, 0x0, 0x18, 0x93, 0x17, 0x8d, 0xd2, 0x53, 0x0, 0x0, + 0xa, 0xaa, 0x49, 0x44, 0x41, 0x54, 0x78, 0xda, 0x5c, 0xc9, 0xb1, 0xd, + 0xc2, 0x30, 0x10, 0x40, 0xd1, 0x7f, 0x16, 0x42, 0x69, 0x2e, 0xd, 0x23, + 0x0, 0x25, 0x4b, 0xb0, 0x47, 0x6, 0x4a, 0xc1, 0x3c, 0xec, 0xe2, 0x32, + 0x4d, 0x1a, 0x24, 0x64, 0x91, 0x98, 0x8b, 0xcf, 0x92, 0x29, 0xa0, 0xe2, + 0xb5, 0x4f, 0x5a, 0x6b, 0x88, 0xc8, 0x95, 0x3f, 0x2, 0x1c, 0x2e, 0xc3, + 0xf0, 0x0, 0xb8, 0x8f, 0x37, 0xba, 0x6e, 0x8f, 0x59, 0x21, 0x0, 0x3c, + 0xa7, 0x89, 0x64, 0x86, 0x6d, 0x5, 0xf7, 0x8a, 0x6d, 0x85, 0x1d, 0xc0, + 0x6b, 0x59, 0x90, 0xbe, 0xc7, 0xbd, 0x12, 0x63, 0xc4, 0xec, 0x17, 0x39, + 0x67, 0x48, 0x89, 0x10, 0x2, 0xc7, 0xd3, 0x99, 0x75, 0x7d, 0x7f, 0xc3, + 0x6b, 0x85, 0x79, 0x46, 0x55, 0x1, 0x50, 0x55, 0x3e, 0x0, 0x0, 0x0, + 0xff, 0xff, 0x54, 0x8e, 0xb1, 0xa, 0x80, 0x20, 0x14, 0x0, 0x4f, 0xe9, + 0x43, 0xac, 0xe6, 0x3e, 0xc1, 0xa1, 0x6f, 0x17, 0x1a, 0x5a, 0x1b, 0x5d, + 0xfd, 0x0, 0xb, 0x2, 0x2b, 0xe1, 0x35, 0x54, 0x92, 0xb7, 0x1e, 0x7, + 0xa7, 0x80, 0xf1, 0x9d, 0xf8, 0x93, 0x15, 0xc6, 0xc8, 0x60, 0x2d, 0x8b, + 0x73, 0x84, 0x69, 0x2e, 0xa6, 0x1, 0x58, 0x53, 0x2, 0xad, 0x49, 0xc7, + 0x59, 0x8b, 0x2d, 0x4, 0x10, 0xc1, 0x7b, 0xf, 0x80, 0x31, 0xed, 0x23, + 0xf6, 0x18, 0x41, 0x84, 0xae, 0xeb, 0xeb, 0xe2, 0xca, 0xb9, 0xdc, 0x7c, + 0xdc, 0x0, 0x0, 0x0, 0xff, 0xff, 0x8c, 0x90, 0x4d, 0xa, 0x82, 0x40, + 0x0, 0x85, 0x3f, 0xb1, 0xb2, 0xd1, 0x72, 0x31, 0x50, 0xfb, 0x4e, 0x51, + 0xc7, 0xf1, 0x14, 0x1e, 0x2c, 0xe8, 0xc, 0xed, 0xdd, 0x87, 0xb6, 0xd3, + 0xc2, 0x28, 0x2c, 0xc7, 0x51, 0x27, 0x5b, 0x8, 0x89, 0xd0, 0xa2, 0xb7, + 0x7c, 0xbc, 0xc7, 0xfb, 0xb1, 0x0, 0xb9, 0xd9, 0xee, 0x72, 0xb9, 0x5e, + 0x7d, 0xc9, 0xdb, 0xe5, 0xca, 0x71, 0x7f, 0xe0, 0x17, 0x26, 0x80, 0x15, + 0x67, 0x29, 0x53, 0x7f, 0x9, 0x52, 0x2, 0x10, 0x67, 0x29, 0x4a, 0x6b, + 0x0, 0x5c, 0x31, 0xa7, 0x54, 0xd5, 0x38, 0x1a, 0xe0, 0xde, 0x34, 0x78, + 0x41, 0xd0, 0xc7, 0x46, 0x11, 0x4d, 0xdd, 0xd2, 0x1a, 0x83, 0x52, 0x7a, + 0x10, 0xdb, 0xf6, 0x60, 0x28, 0xf2, 0x9c, 0x22, 0xc, 0x1, 0xe8, 0x94, + 0xa2, 0x35, 0x6, 0x80, 0x85, 0xe7, 0xf2, 0x7c, 0x95, 0xfd, 0x7b, 0xc6, + 0xc, 0x86, 0x4a, 0xeb, 0x51, 0x57, 0x21, 0x4, 0x0, 0x8e, 0x33, 0xc3, + 0xbc, 0xbb, 0xd1, 0x68, 0xbf, 0x36, 0xdd, 0x83, 0x3f, 0x70, 0x4e, 0x92, + 0xd3, 0x7, 0x0, 0x0, 0xff, 0xff, 0x8c, 0x93, 0x4f, 0x4a, 0xc3, 0x60, + 0x10, 0xc5, 0x7f, 0xf9, 0xd3, 0x64, 0xd3, 0x45, 0xb1, 0x29, 0xba, 0xab, + 0x17, 0x70, 0x91, 0x13, 0x88, 0x88, 0x17, 0x50, 0x11, 0x97, 0x15, 0x84, + 0xde, 0x46, 0xa, 0x45, 0x71, 0x21, 0xd4, 0x22, 0x1e, 0xc1, 0x55, 0x36, + 0x1e, 0x43, 0x5c, 0x25, 0x17, 0xd0, 0x45, 0xaa, 0xb4, 0xf3, 0xe5, 0x9b, + 0xcf, 0x45, 0x34, 0x6d, 0xc0, 0x45, 0x7, 0x6, 0xe6, 0xd, 0x6f, 0xe0, + 0xd, 0xf3, 0xc6, 0x3, 0xe, 0x80, 0x1, 0xdb, 0xc5, 0xab, 0xb7, 0xf8, + 0x5a, 0xb9, 0xc3, 0xcb, 0xf3, 0x56, 0xf7, 0xe5, 0xe1, 0xf1, 0x5f, 0xf6, + 0xde, 0x6e, 0x7f, 0xe4, 0x57, 0xd6, 0x22, 0xaa, 0x48, 0xaf, 0x57, 0xa7, + 0x2a, 0x95, 0xb5, 0x4d, 0xce, 0xe7, 0xb3, 0xa6, 0x6, 0xfc, 0xd0, 0x54, + 0x15, 0x2b, 0x11, 0x90, 0x5f, 0x63, 0x88, 0x34, 0x17, 0x6, 0xb0, 0xaa, + 0x2d, 0x1c, 0x5a, 0xab, 0x7c, 0x2f, 0x97, 0x78, 0x65, 0x89, 0xdf, 0xed, + 0xa2, 0x9d, 0xe, 0xd6, 0x6a, 0x43, 0x50, 0x75, 0x2d, 0xec, 0xab, 0x55, + 0x56, 0x22, 0xb8, 0x34, 0xc5, 0x1f, 0x8f, 0x71, 0x69, 0x8a, 0x5a, 0xe5, + 0xf9, 0x69, 0x8e, 0x88, 0xe1, 0xf4, 0xec, 0x2, 0x11, 0xc3, 0xa2, 0x2c, + 0xeb, 0x1, 0xab, 0x8a, 0x18, 0x43, 0x99, 0x65, 0x7c, 0x4e, 0x26, 0x94, + 0x59, 0x86, 0x55, 0xe5, 0xe8, 0xf8, 0x84, 0xa2, 0xc8, 0xb9, 0xbf, 0x9b, + 0x52, 0x14, 0x39, 0x78, 0x7e, 0x2d, 0xc9, 0x39, 0x87, 0x18, 0x3, 0xc6, + 0x40, 0x9e, 0xd7, 0x97, 0x76, 0x8e, 0x24, 0x19, 0x90, 0x24, 0x3, 0x76, + 0xfa, 0x9, 0xc3, 0xe1, 0xfe, 0xa6, 0x3d, 0x2, 0xaa, 0x8d, 0xa5, 0x0, + 0x82, 0x20, 0x58, 0x6b, 0xf6, 0xfd, 0x16, 0xe, 0xa3, 0x28, 0x6a, 0x7c, + 0xf3, 0x17, 0x51, 0x14, 0xad, 0x9, 0x61, 0xd0, 0xc6, 0x71, 0x1c, 0xe3, + 0x9c, 0x6b, 0xd, 0xc4, 0x71, 0xdc, 0xd4, 0xa3, 0xab, 0xeb, 0xf6, 0x3f, + 0xdc, 0x4e, 0x6f, 0x66, 0xf2, 0xf6, 0x7e, 0xb5, 0xa5, 0x35, 0x3e, 0x7e, + 0x58, 0x2d, 0x9f, 0x95, 0x46, 0x82, 0x20, 0x8c, 0xff, 0x3a, 0xdd, 0xd1, + 0x19, 0x27, 0x2a, 0x92, 0xec, 0xfa, 0x6f, 0xef, 0x22, 0x2c, 0x88, 0xe0, + 0x63, 0x78, 0x56, 0x54, 0x3c, 0xc9, 0xee, 0x7a, 0x5f, 0xa2, 0xe2, 0xc1, + 0xab, 0x6f, 0x20, 0x7b, 0xd8, 0x78, 0x58, 0x76, 0x5f, 0x61, 0xf5, 0x11, + 0x82, 0x6, 0x24, 0x57, 0x11, 0xff, 0x40, 0x1c, 0x14, 0x44, 0x4f, 0xc1, + 0xcd, 0xa4, 0x7b, 0xb2, 0x87, 0x99, 0x69, 0x67, 0x4c, 0xe2, 0xc9, 0x82, + 0xba, 0x74, 0x7d, 0x5d, 0x5f, 0x4d, 0x77, 0x7f, 0x55, 0x23, 0x80, 0xc1, + 0xcb, 0xab, 0xeb, 0xba, 0x57, 0x18, 0x99, 0xe1, 0x1d, 0xcd, 0x71, 0x1c, + 0x46, 0x87, 0x87, 0x16, 0x15, 0x50, 0x28, 0x7d, 0x18, 0x9f, 0x19, 0xf9, + 0x3c, 0xcb, 0xfc, 0xdc, 0x1c, 0x8c, 0x8d, 0x75, 0xa3, 0x9f, 0x9e, 0x38, + 0xab, 0xd7, 0xf1, 0xab, 0xa7, 0xdd, 0x89, 0x6, 0x7, 0x62, 0x39, 0x5, + 0x99, 0xf5, 0x66, 0xb3, 0x9, 0xf0, 0x51, 0x1, 0x68, 0x1d, 0x1d, 0x6a, + 0x2b, 0x8, 0xe0, 0xfe, 0xbe, 0x6f, 0x55, 0x9, 0x2e, 0x6d, 0x46, 0x85, + 0x54, 0x2a, 0x3f, 0x59, 0x5d, 0x5b, 0xef, 0xdb, 0x63, 0xec, 0xc6, 0xe7, + 0x7f, 0x71, 0x2f, 0x29, 0x16, 0x5f, 0x9d, 0xe4, 0x63, 0x7f, 0x2, 0x13, + 0x62, 0x4c, 0xd8, 0x33, 0x66, 0x9, 0x4c, 0x18, 0xbd, 0xf5, 0xe7, 0x56, + 0xb, 0xe1, 0x38, 0xa8, 0xe9, 0x69, 0xd4, 0xc2, 0x2, 0x42, 0x29, 0x3a, + 0x5a, 0xa3, 0x6b, 0x35, 0x84, 0xef, 0x5b, 0xdc, 0xf1, 0xd1, 0x5f, 0x36, + 0xbf, 0x6e, 0xd8, 0x24, 0x5b, 0xe5, 0xef, 0x99, 0xa4, 0x97, 0x37, 0xb7, + 0xe4, 0x72, 0xb9, 0x14, 0x41, 0x2c, 0xa6, 0x20, 0x8, 0xa0, 0xd3, 0x41, + 0xfb, 0x3e, 0xb2, 0x56, 0x3, 0xa5, 0x40, 0x6b, 0x8c, 0xef, 0xd3, 0x31, + 0xc6, 0xe2, 0x4e, 0x4f, 0xaa, 0xfc, 0xf9, 0xfd, 0xb, 0xa5, 0x24, 0x52, + 0x4a, 0x6e, 0x1b, 0xd, 0x26, 0x26, 0xa7, 0x8, 0xc3, 0x10, 0x63, 0x34, + 0x7, 0x3f, 0x2a, 0x78, 0x9e, 0xf7, 0x42, 0x10, 0xc6, 0x95, 0x59, 0x9, + 0x9d, 0x9f, 0x47, 0x5e, 0x2a, 0x45, 0x25, 0x3d, 0x3c, 0x40, 0xbb, 0x6d, + 0x71, 0x3b, 0xbb, 0x7b, 0xb6, 0xda, 0x82, 0x37, 0xc4, 0xe1, 0x61, 0x85, + 0xa5, 0xe5, 0x95, 0xcc, 0x57, 0x24, 0x58, 0x5, 0x20, 0x44, 0x34, 0xad, + 0xda, 0xaf, 0x24, 0xc7, 0xdd, 0x5d, 0x44, 0x12, 0xaf, 0x27, 0xb8, 0xcc, + 0xb0, 0x15, 0x89, 0x8b, 0xfe, 0x77, 0x90, 0xcf, 0xe7, 0x6d, 0x1b, 0xef, + 0xb2, 0xd4, 0xab, 0x4a, 0x70, 0x99, 0x4, 0x4a, 0x21, 0xa5, 0xec, 0x19, + 0xb3, 0x4, 0x89, 0xb6, 0x8d, 0x31, 0x6f, 0x8a, 0x27, 0xdd, 0x3, 0xd2, + 0x4, 0x4a, 0xaa, 0x9e, 0x31, 0x20, 0xfa, 0x6b, 0x70, 0x5d, 0x87, 0xfd, + 0xf2, 0xf6, 0x9b, 0xc9, 0xf7, 0xcb, 0xdb, 0xb8, 0xae, 0xdb, 0xe5, 0x8, + 0xc9, 0xc6, 0x97, 0x6f, 0xbd, 0x63, 0xf1, 0x9c, 0xca, 0x1, 0x9f, 0x80, + 0x49, 0x40, 0xbe, 0x63, 0xb7, 0x68, 0x2, 0x17, 0xff, 0x59, 0x2f, 0x7f, + 0xd7, 0x28, 0x82, 0x28, 0x8e, 0x7f, 0x66, 0x76, 0x2f, 0xcd, 0x41, 0x2, + 0xe6, 0xc7, 0x21, 0x39, 0xd, 0xf1, 0xe4, 0x84, 0x44, 0xb0, 0xb3, 0xf2, + 0xf, 0x30, 0x5a, 0x58, 0x24, 0x95, 0x85, 0xbf, 0x9a, 0xfc, 0x9, 0xf6, + 0x5a, 0x8, 0x8a, 0xbd, 0x18, 0xc4, 0xbf, 0x20, 0x85, 0x68, 0xb0, 0x16, + 0x3b, 0xbb, 0xe0, 0xa5, 0x48, 0x82, 0x69, 0xc, 0x46, 0xb8, 0xf3, 0xc7, + 0x19, 0xbd, 0x84, 0xdc, 0xce, 0x7b, 0x16, 0xb7, 0x3b, 0xb7, 0x97, 0xdd, + 0xd, 0xa, 0x37, 0x30, 0x2c, 0xb3, 0xef, 0xed, 0x7c, 0x77, 0xde, 0xbc, + 0xf9, 0xbe, 0xef, 0x24, 0x0, 0x57, 0xd2, 0x75, 0x7a, 0x8, 0x4d, 0x81, + 0x96, 0xaa, 0x7e, 0xc, 0x81, 0x85, 0xf6, 0x7e, 0xe7, 0xcd, 0xe1, 0xe1, + 0xe1, 0x30, 0xb9, 0x8e, 0x3f, 0xbf, 0x7f, 0x6d, 0x1a, 0x63, 0x2e, 0x85, + 0xc0, 0xc4, 0x41, 0xe7, 0x80, 0xcb, 0x37, 0xae, 0x33, 0x39, 0x99, 0x5f, + 0xa0, 0x9b, 0xcd, 0x26, 0x1f, 0x5e, 0xad, 0xfd, 0x17, 0xc0, 0xd4, 0xd4, + 0xe9, 0xb, 0x40, 0x39, 0x4, 0xac, 0xa8, 0x30, 0x5e, 0xa9, 0x20, 0xb5, + 0x5a, 0xae, 0xf3, 0xb8, 0xb5, 0x88, 0x4a, 0x3e, 0x2d, 0x17, 0xb0, 0x69, + 0xec, 0xdf, 0x2b, 0x85, 0x22, 0x4a, 0x14, 0x45, 0x44, 0x22, 0xf9, 0x3d, + 0x8a, 0x10, 0xd1, 0xdc, 0xbe, 0xb2, 0xb2, 0x82, 0x68, 0xbe, 0x6d, 0x80, + 0x2a, 0x9c, 0x8, 0x6c, 0x6e, 0x16, 0x2e, 0x39, 0x39, 0xfa, 0x99, 0xdd, + 0x54, 0x45, 0x45, 0x33, 0xf6, 0x1, 0xaa, 0x70, 0xf1, 0x5f, 0x2, 0x50, + 0xaf, 0x67, 0x67, 0xd9, 0xda, 0xf2, 0x4c, 0x9a, 0x1, 0x56, 0x45, 0x54, + 0x33, 0x76, 0x97, 0x6, 0x50, 0xd1, 0x3e, 0xf, 0xe5, 0x9d, 0xe6, 0x28, + 0x42, 0x45, 0xf3, 0x57, 0x20, 0xea, 0xfb, 0xf1, 0xf7, 0xfd, 0x10, 0x69, + 0xa, 0x60, 0x7b, 0x1b, 0x3b, 0x3a, 0x8a, 0x89, 0xb9, 0x45, 0xbb, 0x5d, + 0xc4, 0x5a, 0x44, 0xb5, 0x30, 0xe1, 0x15, 0xcd, 0xd8, 0x93, 0x71, 0x98, + 0xc4, 0x31, 0x72, 0xe, 0x8c, 0x21, 0x18, 0x1b, 0xe3, 0x68, 0x75, 0x75, + 0x50, 0xc8, 0x2c, 0x2e, 0x7a, 0xe9, 0x21, 0x22, 0x9c, 0x9b, 0x99, 0xf6, + 0xb6, 0xfb, 0x71, 0xb1, 0x99, 0xe8, 0x9, 0x6e, 0x9e, 0x3d, 0x7f, 0xc1, + 0xd5, 0x85, 0x6b, 0xde, 0xdf, 0x87, 0xc8, 0x39, 0x7, 0xd6, 0x62, 0x4a, + 0x25, 0x8e, 0x7, 0xc9, 0x94, 0x4a, 0x7e, 0xc9, 0x7, 0x9d, 0xe, 0xdb, + 0x3b, 0x9f, 0x8b, 0x75, 0xca, 0xb7, 0xd6, 0x40, 0xc8, 0x7c, 0x88, 0x9c, + 0x73, 0x20, 0x82, 0xb4, 0x5a, 0x84, 0xcb, 0xcb, 0x98, 0xf8, 0x36, 0xa1, + 0xfb, 0xfb, 0x1c, 0xb5, 0x5a, 0x7e, 0xc9, 0x4e, 0x94, 0xbb, 0xb7, 0x6f, + 0x12, 0x4, 0x21, 0xd6, 0x5a, 0xbe, 0xee, 0x7d, 0x61, 0xba, 0x5a, 0xc5, + 0x39, 0x47, 0x14, 0x39, 0xea, 0xf5, 0xba, 0xdf, 0xf8, 0x81, 0x10, 0xf9, + 0x2c, 0x68, 0xb7, 0x89, 0xd6, 0xd7, 0x31, 0x73, 0x73, 0xf1, 0x66, 0x9, + 0xb4, 0xdb, 0x7e, 0xc9, 0x41, 0x10, 0xf0, 0xe0, 0xe1, 0x23, 0x3f, 0xc1, + 0xdb, 0xb5, 0xd7, 0x2c, 0x2e, 0x2d, 0xd1, 0x89, 0xef, 0x13, 0xe5, 0x72, + 0xb9, 0x97, 0xba, 0x19, 0x80, 0x24, 0x7b, 0x9c, 0xeb, 0x95, 0xcd, 0x34, + 0x37, 0x75, 0xbb, 0xfe, 0x83, 0x91, 0x91, 0x11, 0xce, 0x9c, 0x9d, 0xf1, + 0xa6, 0x53, 0xe3, 0x13, 0xcc, 0xce, 0xd6, 0xfc, 0xdd, 0xc3, 0x9f, 0x8d, + 0x34, 0x40, 0x3a, 0x6f, 0x7d, 0x6b, 0x34, 0x86, 0x42, 0x7a, 0xbe, 0x26, + 0xe7, 0x9e, 0xd4, 0xf9, 0xf9, 0xde, 0x73, 0x63, 0xa3, 0xb0, 0xe6, 0x16, + 0xd5, 0xe4, 0x64, 0x1c, 0x26, 0x1a, 0x3a, 0x17, 0x20, 0x79, 0x27, 0xe2, + 0x75, 0x4e, 0x16, 0xc0, 0x62, 0xac, 0xcd, 0xd8, 0x7, 0x74, 0x51, 0x10, + 0x4, 0xf9, 0x7, 0x29, 0x15, 0xa6, 0xb4, 0x88, 0x1f, 0x9c, 0xc8, 0x10, + 0x1c, 0x13, 0xf9, 0x69, 0xff, 0x3e, 0x40, 0x1, 0xd7, 0x70, 0xf1, 0x22, + 0x34, 0x1a, 0x27, 0x0, 0xd8, 0xcc, 0x2d, 0x22, 0x17, 0x40, 0xb, 0xa8, + 0x0, 0x55, 0x50, 0x2d, 0x6, 0x30, 0xff, 0x0, 0x10, 0x86, 0x27, 0x94, + 0xe3, 0x38, 0x4c, 0x45, 0x3e, 0xb7, 0xee, 0xdc, 0xa3, 0x1b, 0x49, 0xc6, + 0x9e, 0x8c, 0xbd, 0xf0, 0xaa, 0x56, 0x2a, 0xec, 0x16, 0x48, 0xf7, 0x6a, + 0xa5, 0x52, 0x28, 0xac, 0x8a, 0x5a, 0xa9, 0xd4, 0x7, 0xf8, 0xfe, 0xf3, + 0x47, 0xf3, 0xd3, 0xce, 0xbb, 0xf7, 0xb5, 0x61, 0x16, 0xfd, 0xa7, 0x4f, + 0x1e, 0xbf, 0x4, 0xba, 0x6, 0x28, 0x3, 0xe7, 0xe3, 0xe7, 0xb0, 0x9a, + 0x3, 0xf6, 0x80, 0xdd, 0xbf, 0xcc, 0x9a, 0xdd, 0x6b, 0x1c, 0x55, 0x18, + 0xc6, 0x7f, 0x67, 0xbe, 0x76, 0x93, 0x4c, 0x82, 0x35, 0x9b, 0xaf, 0x4d, + 0x9, 0x6d, 0xac, 0x35, 0xa1, 0x52, 0x6d, 0xc1, 0xb, 0xaf, 0x84, 0x6, + 0xe9, 0x85, 0x9a, 0x5c, 0xa, 0x8a, 0x60, 0x3, 0x5e, 0x78, 0x53, 0xaa, + 0xd7, 0x85, 0xb6, 0xd0, 0xff, 0xc1, 0xb, 0x21, 0x15, 0x44, 0xc1, 0xcb, + 0x54, 0x7b, 0x11, 0x21, 0xf5, 0xce, 0xf, 0x84, 0x46, 0x5b, 0x4a, 0xd2, + 0x34, 0x69, 0x29, 0xd, 0x49, 0xf3, 0x45, 0x9b, 0x66, 0xb2, 0xd9, 0xec, + 0xec, 0xcc, 0x78, 0x31, 0xb3, 0x93, 0x33, 0x3b, 0xb3, 0x9b, 0xd9, 0x42, + 0x6c, 0xe, 0xc, 0xef, 0xe, 0xcc, 0x39, 0xbc, 0xef, 0xf9, 0x78, 0xdf, + 0xe7, 0x79, 0xce, 0x56, 0x70, 0xd1, 0xc9, 0xb1, 0x6b, 0xdf, 0x7d, 0x3c, + 0x34, 0x34, 0x34, 0x92, 0xcf, 0xe7, 0x7, 0x39, 0xc0, 0x6d, 0x71, 0x71, + 0x71, 0x7a, 0x72, 0x72, 0x72, 0x7c, 0xf4, 0xdc, 0xe7, 0x3f, 0x1, 0xb7, + 0x5, 0xd0, 0x6, 0x7c, 0x50, 0xd8, 0x2e, 0xfd, 0xf8, 0x7c, 0x73, 0x93, + 0x6f, 0xae, 0x8d, 0xf1, 0xed, 0xf, 0xdf, 0xb3, 0x58, 0x87, 0xaa, 0xd5, + 0x6a, 0xf9, 0xae, 0x2e, 0xbe, 0xf8, 0xf4, 0x33, 0xbe, 0x3c, 0x37, 0xba, + 0xaf, 0x41, 0xb4, 0xb5, 0xb6, 0xd2, 0xdc, 0x64, 0x7c, 0x2, 0xdc, 0x10, + 0x40, 0x3b, 0x30, 0xbc, 0xb9, 0x55, 0x1c, 0xb3, 0x2c, 0x8b, 0x9e, 0x77, + 0x4e, 0x73, 0xfa, 0xd4, 0x29, 0x3f, 0x7f, 0x35, 0x8c, 0x47, 0x3d, 0x6e, + 0x4d, 0x4d, 0xb1, 0xf4, 0xf7, 0xad, 0x7d, 0xd, 0xc0, 0x34, 0x4d, 0x5a, + 0x5b, 0xb2, 0xa3, 0xc0, 0x75, 0x4d, 0x96, 0x55, 0x5c, 0xd7, 0x3, 0x45, + 0xd9, 0x2d, 0xc, 0xc7, 0x8e, 0xc1, 0xfa, 0xba, 0xcf, 0x69, 0xf7, 0xb2, + 0x73, 0x73, 0x95, 0xcc, 0x14, 0x42, 0xba, 0x54, 0x72, 0x40, 0xc6, 0xe0, + 0xca, 0x95, 0xcb, 0x0, 0x5c, 0xba, 0x74, 0x39, 0x86, 0x3f, 0x93, 0x11, + 0xa0, 0x17, 0xd7, 0xed, 0x5c, 0xd7, 0xd, 0xb3, 0xa9, 0x23, 0xd5, 0x80, + 0x86, 0x9e, 0x3d, 0x20, 0x66, 0x2d, 0x54, 0x98, 0xcf, 0xf7, 0xee, 0x22, + 0xc4, 0x14, 0x7d, 0xe5, 0x6f, 0x22, 0xe5, 0xb8, 0xe2, 0x78, 0x8, 0x51, + 0x67, 0x66, 0x2a, 0xdc, 0x20, 0x9d, 0xad, 0x55, 0xda, 0xf7, 0x70, 0xc6, + 0x95, 0xf0, 0x5c, 0x9a, 0xbe, 0x4e, 0x52, 0x0, 0x32, 0xe, 0x8b, 0x51, + 0xf1, 0xe3, 0xc7, 0x7d, 0x9e, 0x9f, 0xcb, 0xd5, 0xb6, 0xb3, 0xb3, 0x31, + 0xc8, 0x9b, 0x9a, 0xc7, 0x55, 0xca, 0x9c, 0x97, 0xae, 0xaf, 0x97, 0xb8, + 0x85, 0x24, 0x9c, 0x17, 0xa3, 0xfa, 0x95, 0x88, 0x85, 0x40, 0x68, 0x1a, + 0x28, 0x8a, 0xaf, 0x90, 0x4, 0xd6, 0xab, 0x1c, 0x78, 0xd7, 0x5, 0x21, + 0x6a, 0x42, 0xf0, 0x5a, 0x5b, 0x28, 0x44, 0xd4, 0x9e, 0x97, 0xaa, 0xaf, + 0xfc, 0x8d, 0xb4, 0x2, 0xae, 0x8f, 0x3f, 0x1, 0xdb, 0xb6, 0x77, 0xd1, + 0x94, 0xa6, 0x21, 0x1e, 0x3f, 0x46, 0x3d, 0x74, 0xc8, 0x7, 0x51, 0xbd, + 0xbd, 0xa0, 0xaa, 0x60, 0x9a, 0x15, 0x9, 0x1a, 0xb6, 0xb7, 0xf1, 0xba, + 0xbb, 0x71, 0x9e, 0x3e, 0xf5, 0x3, 0x4a, 0xd8, 0x6, 0xb7, 0xff, 0xfd, + 0x87, 0xb5, 0xb5, 0x55, 0x54, 0x55, 0xc3, 0xc8, 0x18, 0x68, 0xaa, 0x6, + 0x42, 0xd0, 0x94, 0x35, 0x78, 0xf8, 0xf0, 0x1, 0x0, 0x7f, 0xfd, 0xf9, + 0x3b, 0xdb, 0xc5, 0x40, 0x4a, 0x72, 0xca, 0x94, 0x76, 0x4a, 0x38, 0x4e, + 0x99, 0x5c, 0xae, 0x83, 0x93, 0x6f, 0xbd, 0x8d, 0xec, 0x6b, 0xba, 0x15, + 0x50, 0x14, 0x84, 0x10, 0xa8, 0x6d, 0x6d, 0x78, 0x47, 0x8e, 0x50, 0xbc, + 0x7a, 0x15, 0x73, 0x62, 0x2, 0xeb, 0xec, 0x59, 0xcc, 0x9b, 0x37, 0xb1, + 0xce, 0x9c, 0x9, 0xdf, 0x33, 0x17, 0x2f, 0xe2, 0x39, 0xe, 0x42, 0xd3, + 0x62, 0xb3, 0xb8, 0xb3, 0xb3, 0xc3, 0xdc, 0xfc, 0x3c, 0x1f, 0x7e, 0x34, + 0x92, 0x38, 0xa3, 0x5f, 0xf, 0xbc, 0x59, 0x77, 0xc6, 0x7f, 0xf9, 0x79, + 0x9c, 0xd7, 0xdf, 0x18, 0x8, 0x65, 0x56, 0x79, 0x7c, 0x25, 0x9, 0x68, + 0x3b, 0x8e, 0x13, 0x79, 0xca, 0x85, 0x2, 0xe5, 0x8d, 0xd, 0x1c, 0xa0, + 0xb4, 0xbc, 0xec, 0xdb, 0x27, 0x4f, 0x22, 0xef, 0xe5, 0x8d, 0xd, 0xca, + 0x85, 0x2, 0x4, 0xe3, 0xc8, 0x8f, 0x6d, 0xdb, 0xdc, 0x9f, 0xbd, 0xf7, + 0xc2, 0x79, 0xff, 0xfe, 0xec, 0x3d, 0xec, 0x80, 0x38, 0xc8, 0x7e, 0x86, + 0x77, 0x4e, 0xc0, 0xf0, 0xc2, 0xd2, 0xfa, 0x98, 0x65, 0x59, 0xc, 0xbc, + 0xff, 0x1e, 0xaf, 0xc8, 0x5a, 0x66, 0xb0, 0xcf, 0x45, 0x36, 0x8b, 0x30, + 0xc, 0xd0, 0xf5, 0x68, 0x91, 0xf3, 0x3c, 0x9f, 0x95, 0x94, 0x4a, 0x78, + 0xc5, 0x22, 0xcf, 0x96, 0x97, 0x99, 0x99, 0xf8, 0x2d, 0xe2, 0x40, 0xa1, + 0xb0, 0xc5, 0xda, 0xea, 0x2a, 0x96, 0x65, 0xc5, 0x70, 0x69, 0x53, 0x53, + 0x86, 0xeb, 0xe3, 0xe3, 0x0, 0xc, 0x8f, 0x8c, 0x44, 0xae, 0x5b, 0x2a, + 0xd8, 0xdf, 0x34, 0x4d, 0x72, 0x1d, 0x1d, 0x34, 0x37, 0xb7, 0x84, 0x85, + 0xec, 0x70, 0x4f, 0x7b, 0xb4, 0x90, 0xc9, 0x91, 0x45, 0x72, 0xb1, 0xeb, + 0xfa, 0x42, 0xa0, 0x7c, 0xfb, 0x32, 0x38, 0xe8, 0xa7, 0xce, 0x8e, 0x8e, + 0x5d, 0x3b, 0x3d, 0xed, 0x7, 0xe3, 0xba, 0x9, 0xa, 0x7b, 0x96, 0xce, + 0xae, 0x6e, 0xda, 0x73, 0x65, 0xaa, 0xcf, 0x68, 0x4b, 0x73, 0x96, 0x57, + 0x83, 0x9, 0x3b, 0x7a, 0xb4, 0x9f, 0xad, 0x42, 0x31, 0x46, 0x6a, 0x54, + 0x55, 0x43, 0xd7, 0xf5, 0x70, 0x5c, 0x2f, 0xf1, 0x10, 0x57, 0x6d, 0xa1, + 0x3d, 0x92, 0xb7, 0x1f, 0x54, 0xb5, 0x95, 0xc6, 0xaa, 0x9e, 0x45, 0x5f, + 0x27, 0x8c, 0x6b, 0x85, 0x2d, 0x2d, 0xcd, 0x18, 0x46, 0x26, 0xf8, 0x6d, + 0xe2, 0xa1, 0xd4, 0x41, 0x2a, 0x75, 0x2, 0x68, 0xa8, 0x10, 0xdd, 0xb9, + 0xe3, 0xdb, 0x95, 0x95, 0xa8, 0x7d, 0x9, 0x4d, 0xab, 0x26, 0x57, 0xd, + 0x41, 0x81, 0x13, 0x27, 0x7c, 0xe7, 0x3b, 0x3b, 0xe1, 0xee, 0xdd, 0x18, + 0xa5, 0x4c, 0xd3, 0x14, 0x45, 0xa0, 0x4, 0x67, 0x4a, 0x51, 0x44, 0xaa, + 0xbe, 0xf2, 0x37, 0x9a, 0x4c, 0xd2, 0x2a, 0x44, 0x2d, 0x75, 0x0, 0xc1, + 0x9e, 0xf, 0xed, 0x1e, 0x14, 0xb7, 0x96, 0x33, 0x22, 0x70, 0x28, 0x89, + 0x3c, 0x26, 0xb5, 0xc8, 0x3d, 0x58, 0xe2, 0xa, 0xa4, 0xad, 0xa4, 0xd5, + 0x5b, 0xe9, 0x5, 0x56, 0x40, 0x8, 0x25, 0x5c, 0x1, 0x21, 0x94, 0xff, + 0x79, 0x5, 0xaa, 0xb8, 0xff, 0x4b, 0x5d, 0x1, 0x4d, 0xd3, 0xea, 0xd3, + 0xfb, 0x46, 0xe, 0x56, 0x3, 0xe3, 0xa8, 0xaa, 0xc2, 0x57, 0x17, 0xce, + 0xfb, 0xea, 0x79, 0xa1, 0x98, 0xaa, 0xaf, 0xfc, 0x8d, 0x26, 0xdf, 0xc5, + 0xe8, 0xba, 0x4e, 0x7f, 0x5f, 0x1f, 0xf, 0x96, 0x96, 0x1a, 0xf3, 0x58, + 0x9a, 0xfd, 0xfe, 0xbe, 0xbe, 0x86, 0x24, 0x6, 0xc7, 0x85, 0xe7, 0x56, + 0xb1, 0xee, 0x9d, 0x50, 0x5c, 0x92, 0xd0, 0x23, 0x1, 0x78, 0x80, 0xbb, + 0xb0, 0xf0, 0x68, 0xbe, 0xa7, 0xe7, 0xf0, 0x6b, 0x53, 0x37, 0x7e, 0x45, + 0xd7, 0x95, 0x9a, 0x6a, 0x59, 0xfd, 0x33, 0xed, 0x61, 0xdb, 0x2e, 0x76, + 0xd9, 0xd9, 0xb7, 0xb4, 0x69, 0x18, 0x6, 0xb, 0xb, 0x8f, 0xe6, 0x1, + 0x17, 0xf0, 0x4, 0x90, 0x1, 0xba, 0x80, 0x77, 0x81, 0xce, 0xe0, 0x4a, + 0x48, 0x70, 0x30, 0x9b, 0x17, 0x48, 0x2a, 0x2b, 0xc0, 0x1f, 0xc0, 0xb2, + 0x8, 0xfe, 0x98, 0xa3, 0x0, 0x7a, 0x10, 0x8c, 0x76, 0xc0, 0x3, 0x28, + 0x3, 0x3b, 0x80, 0xd, 0xb8, 0xff, 0xd, 0x0, 0x62, 0x73, 0xb1, 0x8a, + 0xf9, 0x27, 0xe, 0x8a, 0x0, 0x0, 0x0, 0x0, 0x49, 0x45, 0x4e, 0x44, + 0xae, 0x42, 0x60, 0x82, + +}; + +static const unsigned char qt_resource_name[] = { + // icon + 0x0, 0x4, 0x0, 0x6, 0xfa, 0x5e, 0x0, 0x69, 0x0, 0x63, 0x0, 0x6f, + 0x0, 0x6e, + // vcross.png + 0x0, 0xa, 0x6, 0xad, 0xc2, 0x67, 0x0, 0x76, 0x0, 0x63, 0x0, 0x72, + 0x0, 0x6f, 0x0, 0x73, 0x0, 0x73, 0x0, 0x2e, 0x0, 0x70, 0x0, 0x6e, + 0x0, 0x67, + // vpointer.png + 0x0, 0xc, 0x5, 0x6b, 0x97, 0xe7, 0x0, 0x76, 0x0, 0x70, 0x0, 0x6f, + 0x0, 0x69, 0x0, 0x6e, 0x0, 0x74, 0x0, 0x65, 0x0, 0x72, 0x0, 0x2e, + 0x0, 0x70, 0x0, 0x6e, 0x0, 0x67, + // letters.png + 0x0, 0xb, 0xc, 0x73, 0xc0, 0x7, 0x0, 0x6c, 0x0, 0x65, 0x0, 0x74, + 0x0, 0x74, 0x0, 0x65, 0x0, 0x72, 0x0, 0x73, 0x0, 0x2e, 0x0, 0x70, + 0x0, 0x6e, 0x0, 0x67, + // superman.png + 0x0, 0xc, 0xc, 0x59, 0x8e, 0x67, 0x0, 0x73, 0x0, 0x75, 0x0, 0x70, + 0x0, 0x65, 0x0, 0x72, 0x0, 0x6d, 0x0, 0x61, 0x0, 0x6e, 0x0, 0x2e, + 0x0, 0x70, 0x0, 0x6e, 0x0, 0x67, + // vista.png + 0x0, 0x9, 0xa, 0xa4, 0xad, 0x47, 0x0, 0x76, 0x0, 0x69, 0x0, 0x73, + 0x0, 0x74, 0x0, 0x61, 0x0, 0x2e, 0x0, 0x70, 0x0, 0x6e, 0x0, 0x67, + // pause.png + 0x0, 0x9, 0xc, 0x98, 0xba, 0x47, 0x0, 0x70, 0x0, 0x61, 0x0, 0x75, + 0x0, 0x73, 0x0, 0x65, 0x0, 0x2e, 0x0, 0x70, 0x0, 0x6e, 0x0, 0x67, + // preferences-system.png + 0x0, 0x16, 0x1, 0x70, 0xe1, 0x87, 0x0, 0x70, 0x0, 0x72, 0x0, 0x65, + 0x0, 0x66, 0x0, 0x65, 0x0, 0x72, 0x0, 0x65, 0x0, 0x6e, 0x0, 0x63, + 0x0, 0x65, 0x0, 0x73, 0x0, 0x2d, 0x0, 0x73, 0x0, 0x79, 0x0, 0x73, + 0x0, 0x74, 0x0, 0x65, 0x0, 0x6d, 0x0, 0x2e, 0x0, 0x70, 0x0, 0x6e, + 0x0, 0x67, + +}; + +static const unsigned char qt_resource_struct[] = { + // : + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, + // :/icon + 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x2, + // :/icon/preferences-system.png + 0x0, 0x0, 0x0, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x27, 0xfd, + // :/icon/vpointer.png + 0x0, 0x0, 0x0, 0x28, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x2, 0x94, + // :/icon/vcross.png + 0x0, 0x0, 0x0, 0xe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x0, + // :/icon/vista.png + 0x0, 0x0, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x19, 0x95, + // :/icon/superman.png + 0x0, 0x0, 0x0, 0x62, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x8, 0x0, + // :/icon/letters.png + 0x0, 0x0, 0x0, 0x46, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x5, 0x2c, + // :/icon/pause.png + 0x0, 0x0, 0x0, 0x98, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x26, 0x3, + +}; + +QT_BEGIN_NAMESPACE + +extern Q_CORE_EXPORT bool qRegisterResourceData(int, const unsigned char *, + const unsigned char *, + const unsigned char *); + +extern Q_CORE_EXPORT bool qUnregisterResourceData(int, const unsigned char *, + const unsigned char *, + const unsigned char *); + +QT_END_NAMESPACE + +int QT_MANGLE_NAMESPACE(qInitResources_qps)() +{ + QT_PREPEND_NAMESPACE(qRegisterResourceData) + (0x01, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +Q_CONSTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qInitResources_qps)) + +int QT_MANGLE_NAMESPACE(qCleanupResources_qps)() +{ + QT_PREPEND_NAMESPACE(qUnregisterResourceData) + (0x01, qt_resource_struct, qt_resource_name, qt_resource_data); + return 1; +} + +Q_DESTRUCTOR_FUNCTION(QT_MANGLE_NAMESPACE(qCleanupResources_qps)) diff --git a/src/qticonloader.cpp b/src/qticonloader.cpp new file mode 100644 index 0000000..743b7e2 --- /dev/null +++ b/src/qticonloader.cpp @@ -0,0 +1,389 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +#include "qticonloader.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_WS_X11 + +class QIconTheme +{ + public: + QIconTheme(QHash dirList, QStringList parents) + : _dirList(dirList), _parents(parents), _valid(true) + { + } + QIconTheme() : _valid(false) {} + QHash dirList() { return _dirList; } + QStringList parents() { return _parents; } + bool isValid() { return _valid; } + + private: + QHash _dirList; + QStringList _parents; + bool _valid; +}; + +class QtIconLoaderImplementation +{ + public: + QtIconLoaderImplementation(); + QPixmap findIcon(int size, const QString &name) const; + + private: + QIconTheme parseIndexFile(const QString &themeName) const; + void lookupIconTheme() const; + QPixmap findIconHelper(int size, const QString &themeName, + const QString &iconName, QStringList &visited) const; + mutable QString themeName; + mutable QStringList iconDirs; + mutable QHash themeList; +}; + +Q_GLOBAL_STATIC(QtIconLoaderImplementation, iconLoaderInstance) +#endif + +/*! + + Returns the standard icon for the given icon /a name + as specified in the freedesktop icon spec + http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html + + /a fallback is an optional argument to specify the icon to be used if + no icon is found on the platform. This is particularily useful for + crossplatform code. + +*/ +QIcon QtIconLoader::icon(const QString &name, const QIcon &fallback) +{ + QIcon icon; +#ifdef Q_WS_X11 + QString pngExtension(QLatin1String(".png")); + QList iconSizes; + iconSizes << 16 << 24 << 32 << 48 << 64; + Q_FOREACH (int size, iconSizes) + { + icon.addPixmap( + iconLoaderInstance()->findIcon(size, name + pngExtension)); + // icon.addPixmap(iconLoaderInstance()->findIcon(size, + // name)); + } +#endif + if (icon.isNull()) + icon = fallback; + Q_UNUSED(name); + return icon; +} + +#ifdef Q_WS_X11 + +QtIconLoaderImplementation::QtIconLoaderImplementation() { lookupIconTheme(); } + +extern "C" { +struct GConfClient; +struct GError; +typedef void (*Ptr_g_type_init)(); +typedef GConfClient *(*Ptr_gconf_client_get_default)(); +typedef char *(*Ptr_gconf_client_get_string)(GConfClient *, const char *, + GError **); +typedef void (*Ptr_g_object_unref)(void *); +typedef void (*Ptr_g_error_free)(GError *); +typedef void (*Ptr_g_free)(void *); +static Ptr_g_type_init p_g_type_init = 0; +static Ptr_gconf_client_get_default p_gconf_client_get_default = 0; +static Ptr_gconf_client_get_string p_gconf_client_get_string = 0; +static Ptr_g_object_unref p_g_object_unref = 0; +static Ptr_g_error_free p_g_error_free = 0; +static Ptr_g_free p_g_free = 0; +} + +static int kdeVersion() +{ + static int version = qgetenv("KDE_SESSION_VERSION").toInt(); + return version; +} + +static QString kdeHome() +{ + static QString kdeHomePath; + if (kdeHomePath.isEmpty()) + { + kdeHomePath = QFile::decodeName(qgetenv("KDEHOME")); + if (kdeHomePath.isEmpty()) + { + int kdeSessionVersion = kdeVersion(); + QDir homeDir(QDir::homePath()); + QString kdeConfDir(QLatin1String("/.kde")); + if (4 == kdeSessionVersion && + homeDir.exists(QLatin1String(".kde4"))) + kdeConfDir = QLatin1String("/.kde4"); + kdeHomePath = QDir::homePath() + kdeConfDir; + } + } + return kdeHomePath; +} + +void QtIconLoaderImplementation::lookupIconTheme() const +{ + +#ifdef Q_WS_X11 + QString dataDirs = QFile::decodeName(getenv("XDG_DATA_DIRS")); + if (dataDirs.isEmpty()) + dataDirs = QLatin1String("/usr/local/share/:/usr/share/"); + + dataDirs.prepend(QDir::homePath() + QLatin1String("/:")); + iconDirs = dataDirs.split(QLatin1Char(':')); + + // If we are running GNOME we resolve and use GConf. In all other + // cases we currently use the KDE icon theme + + if (qgetenv("DESKTOP_SESSION") == "gnome" || + !qgetenv("GNOME_DESKTOP_SESSION_ID").isEmpty()) + { + + if (themeName.isEmpty()) + { + // Resolve glib and gconf + + p_g_type_init = (Ptr_g_type_init)QLibrary::resolve( + QLatin1String("gobject-2.0"), 0, "g_type_init"); + p_gconf_client_get_default = + (Ptr_gconf_client_get_default)QLibrary::resolve( + QLatin1String("gconf-2"), 4, "gconf_client_get_default"); + p_gconf_client_get_string = + (Ptr_gconf_client_get_string)QLibrary::resolve( + QLatin1String("gconf-2"), 4, "gconf_client_get_string"); + p_g_object_unref = (Ptr_g_object_unref)QLibrary::resolve( + QLatin1String("gobject-2.0"), 0, "g_object_unref"); + p_g_error_free = (Ptr_g_error_free)QLibrary::resolve( + QLatin1String("glib-2.0"), 0, "g_error_free"); + p_g_free = (Ptr_g_free)QLibrary::resolve(QLatin1String("glib-2.0"), + 0, "g_free"); + + if (p_g_type_init && p_gconf_client_get_default && + p_gconf_client_get_string && p_g_object_unref && + p_g_error_free && p_g_free) + { + + p_g_type_init(); + GConfClient *client = p_gconf_client_get_default(); + GError *err = 0; + + char *str = p_gconf_client_get_string( + client, "/desktop/gnome/interface/icon_theme", &err); + if (!err) + { + themeName = QString::fromUtf8(str); + p_g_free(str); + } + + p_g_object_unref(client); + if (err) + p_g_error_free(err); + } + if (themeName.isEmpty()) + themeName = QLatin1String("gnome"); + } + + if (!themeName.isEmpty()) + return; + } + + // KDE (and others) + if (dataDirs.isEmpty()) + dataDirs = QLatin1String("/usr/local/share/:/usr/share/"); + + dataDirs += QLatin1Char(':') + kdeHome() + QLatin1String("/share"); + dataDirs.prepend(QDir::homePath() + QLatin1String("/:")); + QStringList kdeDirs = + QFile::decodeName(getenv("KDEDIRS")).split(QLatin1Char(':')); + Q_FOREACH (const QString dirName, kdeDirs) + dataDirs.append(QLatin1Char(':') + dirName + QLatin1String("/share")); + iconDirs = dataDirs.split(QLatin1Char(':')); + + QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde")); + QDir dir(fileInfo.canonicalFilePath()); + QString kdeDefault = kdeVersion() >= 4 ? QString::fromLatin1("oxygen") + : QString::fromLatin1("crystalsvg"); + QString defaultTheme = fileInfo.exists() ? dir.dirName() : kdeDefault; + QSettings settings(kdeHome() + QLatin1String("/share/config/kdeglobals"), + QSettings::IniFormat); + settings.beginGroup(QLatin1String("Icons")); + themeName = settings.value(QLatin1String("Theme"), defaultTheme).toString(); +#endif +} + +QIconTheme +QtIconLoaderImplementation::parseIndexFile(const QString &themeName) const +{ + QIconTheme theme; + QFile themeIndex; + QStringList parents; + QHash dirList; + + for (int i = 0; i < iconDirs.size() && !themeIndex.exists(); ++i) + { + const QString &contentDir = QLatin1String( + iconDirs[i].startsWith(QDir::homePath()) ? "/.icons/" : "/icons/"); + themeIndex.setFileName(iconDirs[i] + contentDir + themeName + + QLatin1String("/index.theme")); + } + + if (themeIndex.exists()) + { + QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat); + Q_FOREACH (const QString &key, indexReader.allKeys()) + { + if (key.endsWith("/Size")) + { + if (int size = indexReader.value(key).toInt()) + dirList.insertMulti(size, key.left(key.size() - 5)); + } + } + + // Parent themes provide fallbacks for missing icons + parents = indexReader.value(QLatin1String("Icon Theme/Inherits")) + .toStringList(); + } + + if (kdeVersion() >= 3) + { + QFileInfo fileInfo(QLatin1String("/usr/share/icons/default.kde")); + QDir dir(fileInfo.canonicalFilePath()); + QString defaultKDETheme = + dir.exists() ? dir.dirName() + : kdeVersion() == 3 ? QString::fromLatin1("crystalsvg") + : QString::fromLatin1("oxygen"); + if (!parents.contains(defaultKDETheme) && themeName != defaultKDETheme) + parents.append(defaultKDETheme); + } + else if (parents.isEmpty() && themeName != QLatin1String("hicolor")) + { + parents.append(QLatin1String("hicolor")); + } + + theme = QIconTheme(dirList, parents); + return theme; +} + +QPixmap QtIconLoaderImplementation::findIconHelper(int size, + const QString &themeName, + const QString &iconName, + QStringList &visited) const +{ + QPixmap pixmap; + + if (!themeName.isEmpty()) + { + visited << themeName; + QIconTheme theme = themeList.value(themeName); + + if (!theme.isValid()) + { + theme = parseIndexFile(themeName); + themeList.insert(themeName, theme); + } + + if (!theme.isValid()) + return QPixmap(); + + QList subDirs = theme.dirList().values(size); + + for (int i = 0; i < iconDirs.size(); ++i) + { + for (int j = 0; j < subDirs.size(); ++j) + { + QString contentDir = (iconDirs[i].startsWith(QDir::homePath())) + ? QLatin1String("/.icons/") + : QLatin1String("/icons/"); + QString fileName = iconDirs[i] + contentDir + themeName + + QLatin1Char('/') + subDirs[j] + + QLatin1Char('/') + iconName; + QFile file(fileName); + if (file.exists()) + pixmap.load(fileName); + if (!pixmap.isNull()) + break; + } + } + + if (pixmap.isNull()) + { + QStringList parents = theme.parents(); + // search recursively through inherited themes + for (int i = 0; pixmap.isNull() && i < parents.size(); ++i) + { + QString parentTheme = parents[i].trimmed(); + if (!visited.contains(parentTheme)) // guard + // against + // endless + // recursion + pixmap = + findIconHelper(size, parentTheme, iconName, visited); + } + } + } + return pixmap; +} + +QPixmap QtIconLoaderImplementation::findIcon(int size, + const QString &name) const +{ + QPixmap pixmap; + QString pixmapName = QLatin1String("$qt") + name + QString::number(size); + if (QPixmapCache::find(pixmapName, pixmap)) + return pixmap; + + if (!themeName.isEmpty()) + { + QStringList visited; + pixmap = findIconHelper(size, themeName, name, visited); + } + QPixmapCache::insert(pixmapName, pixmap); + return pixmap; +} +#endif // Q_WS_X11 diff --git a/src/qticonloader.h b/src/qticonloader.h new file mode 100644 index 0000000..aba2449 --- /dev/null +++ b/src/qticonloader.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** 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, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +#ifndef QTICONLOADER_H +#define QTICONLOADER_H + +#include + +// This is the QtIconLoader +// Version 0.1 +// + +class QtIconLoader +{ + public: + static QIcon icon(const QString &name, const QIcon &fallback = QIcon()); +}; + +#endif // QTICONLOADER_H diff --git a/src/qttableview.cpp b/src/qttableview.cpp new file mode 100644 index 0000000..647edd2 --- /dev/null +++ b/src/qttableview.cpp @@ -0,0 +1,1793 @@ +/********************************************************************** +** $Id: qttableview.cpp,v 1.115 2011/08/27 00:13:41 fasthyun Exp $ +** Implementation of QtTableView class +** Created : 941115 +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** This file contains a class moved out of the Qt GUI Toolkit API. It +** may be used, distributed and modified without limitation. +**********************************************************************/ + +#include "qttableview.h" + +enum ScrollBarDirtyFlags +{ + verSteps = 0x02, + verRange = 0x04, + verValue = 0x08, + horSteps = 0x20, + horRange = 0x40, + horValue = 0x80, + verMask = 0x0F, + horMask = 0xF0 +}; + +QtTableView::QtTableView(QWidget *parent, const char *name) + : QAbstractScrollArea(parent) +{ + nRows = nCols = 0; // zero rows/cols + + xCellOffs = yCellOffs = 0; // zero offset + xCellDelta = yCellDelta = 0; // zero cell offset + xOffs = yOffs = 0; // zero total pixel offset + cellH = cellW = 0; // user defined cell size + vScrollBar = hScrollBar = 0; // no scroll bars + tFlags = 0; + sbDirty = 0; + verSliding = false; + verSnappingOff = false; + horSliding = false; + horSnappingOff = false; + inSbUpdate = false; + flag_view = 0; + view = viewport(); + enablePaint = true; + test = false; + + // setSizePolicy(QSizePolicy + // (QSizePolicy::Expanding,QSizePolicy::Expanding)); + // setFocusPolicy(Qt::NoFocus); + + verticalScrollBar(); // created + horizontalScrollBar(); // created + // setFocusPolicy(Qt::NoFocus); + // setFrameShape (QFrame::NoFrame); + // setStyle(new QCleanlooksStyle); // compiz bug + // setStyle(new QWindowsStyle); // repaint bug +} +/* + Destroys the table view. +*/ +QtTableView::~QtTableView() +{ + delete vScrollBar; + delete hScrollBar; +} + +/* + Returns a pointer to the vertical scroll bar mainly so you can + connect() to its signals. Note that the scroll bar works in pixel + values; use findRow() to translate to cell numbers. +*/ + +QScrollBar *QtTableView::verticalScrollBar() const +{ + QtTableView *that = (QtTableView *)this; // semantic const + if (!vScrollBar) + { + QScrollBar *sb = QAbstractScrollArea::verticalScrollBar(); + sb->setFocusPolicy(Qt::NoFocus); + connect(sb, SIGNAL(valueChanged(int)), SLOT(verSbValue(int))); + connect(sb, SIGNAL(sliderMoved(int)), SLOT(verSbSliding(int))); + connect(sb, SIGNAL(sliderReleased()), SLOT(verSbSlidingDone())); + that->vScrollBar = sb; + // vScrollBar = sb; + return sb; + } + return vScrollBar; +} + +QScrollBar *QtTableView::horizontalScrollBar() const +{ + QtTableView *that = (QtTableView *)this; // semantic const + if (!hScrollBar) + { + QScrollBar *sb = QAbstractScrollArea::horizontalScrollBar(); + sb->setFocusPolicy(Qt::NoFocus); + // sb->setTracking( true ); // bar cause heads moving + connect(sb, SIGNAL(valueChanged(int)), SLOT(horSbValue(int))); + connect(sb, SIGNAL(sliderMoved(int)), SLOT(horSbSliding(int))); + connect(sb, SIGNAL(sliderReleased()), SLOT(horSbSlidingDone())); + that->hScrollBar = sb; + return sb; + } + return hScrollBar; +} + +/* + Sets the number of rows of the table to \a rows (must be non-negative). + Does not change topCell(). + The table repaints itself automatically if updatesEnabled() is set. + + numCols(), setNumCols(), numRows() +*/ + +void QtTableView::setNumRows(int rows) +{ + /// view->setMaximumHeight(rows*cellH); + if (rows < 0) + { + qWarning("QtTableView::setNumRows: (%s) Negative argument %d.", + "unnamed", rows); + return; + } + if (nRows == rows) + return; + + if (isVisible()) + { + int oldLastVisible = lastRowVisible(); + int oldTopCell = topCell(); + } + nRows = rows; + updateScrollBars(verRange); +} + +void QtTableView::setNumCols(int cols) +{ + if (cols < 0) + { + qWarning("QtTableView::setNumCols: (%s) Negative argument %d.", + "unnamed", cols); + return; + } + if (nCols == cols) + return; + int oldCols = nCols; + nCols = cols; + updateScrollBars(horRange); // int maxCol = lastColVisible(); +} + +/* + \fn int QtTableView::topCell() const + Returns the index of the first row in the table that is visible in + the view. The index of the first row is 0. + \sa leftCell(), setTopCell() + Scrolls the table so that \a row becomes the top row. + The index of the very first row is 0. + \sa setYOffset(), setTopLeftCell(), setLeftCell() +*/ + +void QtTableView::setTopCell(int row) +{ + setTopLeftCell(row, -1); + return; +} + +/* + \fn int QtTableView::leftCell() const + Returns the index of the first column in the table that is visible in + the view. The index of the very leftmost column is 0. + \sa topCell(), setLeftCell() +*/ + +/* + Scrolls the table so that \a col becomes the leftmost + column. The index of the leftmost column is 0. + \sa setXOffset(), setTopLeftCell(), setTopCell() +*/ + +void QtTableView::setLeftCell(int col) +{ + setTopLeftCell(-1, col); + return; +} + +/* + Scrolls the table so that the cell at row \a row and colum \a + col becomes the top-left cell in the view. The cell at the extreme + top left of the table is at position (0,0). + \sa setLeftCell(), setTopCell(), setOffset() +*/ + +void QtTableView::setTopLeftCell(int row, int col) +{ + int newX = xOffs; + int newY = yOffs; + + if (col >= 0) + { + if (cellW) + { + newX = col * cellW; + if (newX > maxXOffset()) + newX = maxXOffset(); + } + else + { + newX = 0; + while (col) + newX += cellWidth(--col); // optimize using current! ### + } + } + if (row >= 0) + { + if (cellH) + { + newY = row * cellH; + if (newY > maxYOffset()) + newY = maxYOffset(); + } + else + { + newY = 0; + while (row) + newY += cellHeight(--row); // optimize using current! ### + } + } + setOffset(newX, newY); +} + +/* + \fn int QtTableView::xOffset() const + + Returns the x coordinate in \e table coordinates of the pixel that is + currently on the left edge of the view. + + \sa setXOffset(), yOffset(), leftCell() */ + +/* + Scrolls the table so that \a x becomes the leftmost pixel in the view. + The \a x parameter is in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapToHGrid + \endlink is tricky. + + \sa xOffset(), setYOffset(), setOffset(), setLeftCell() +*/ + +void QtTableView::setXOffset(int x) { setOffset(x, yOffset()); } + +/* + int QtTableView::yOffset() const + + Returns the y coordinate in \e table coordinates of the pixel that is + currently on the top edge of the view. + + :setYOffset(), xOffset(), topCell() +*/ + +// SCROLL Code called by wheel +void QtTableView::setYOffset(int y) { setOffset(xOffset(), y, true); } + +/* + Scrolls the table so that \a (x,y) becomes the top-left pixel + in the view. Parameters \a (x,y) are in \e table coordinates. + + The interaction with \link setTableFlags() Tbl_snapTo*Grid \endlink + is tricky. If \a updateScrBars is true, the scroll bars are + updated. + + \sa xOffset(), yOffset(), setXOffset(), setYOffset(), setTopLeftCell() +*/ + +void QtTableView::setOffset(int x, int y, bool updateScrBars) +{ + if ((!testTableFlags(Tbl_snapToHGrid) || xCellDelta == 0) && + (!testTableFlags(Tbl_snapToVGrid) || yCellDelta == 0) && + (x == xOffs && y == yOffs)) + return; + + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + if (cellW) + { + if (x > maxXOffset()) + x = maxXOffset(); + xCellOffs = x / cellW; + if (!testTableFlags(Tbl_snapToHGrid)) + { + xCellDelta = (short)(x % cellW); + } + else + { + x = xCellOffs * cellW; + xCellDelta = 0; + } + } + else + { + int xn = 0, xcd = 0, col = 0; + while (col < nCols - 1 && x >= xn + (xcd = cellWidth(col))) + { + xn += xcd; + col++; + } + xCellOffs = col; //!!! + if (testTableFlags(Tbl_snapToHGrid)) + { + xCellDelta = 0; + x = xn; + } + else + { + xCellDelta = (short)(x - xn); + } + } + if (cellH) + { // same cellHegiht + if (y > maxYOffset()) + y = maxYOffset(); + yCellOffs = y / cellH; //******** + if (!testTableFlags(Tbl_snapToVGrid)) + { + yCellDelta = (short)(y % cellH); + } + else + { + y = yCellOffs * cellH; + yCellDelta = 0; + } + } + else + { + } + // printf("y=%d yOffs=%d ", y,yOffs); + int dx = (xOffs - x); + int dy = (yOffs - y); // + xOffs = x; + yOffs = y; + if (updatesEnabled() && isVisible()) + { + // *** scroll() call update( desposal region ) !! + // printf("scroll: xpixel=%d , ypixel=%d \n",xPixels,yPixels); + scrollTrigger(dx, dy); + view->scroll(dx, dy); + } + if (updateScrBars) + updateScrollBars(verValue | horValue); +} + +void QtTableView::scrollContentsBy(int dx, int dy) +{ + // view->update() //default + // printf("scroll: dx=%d , dy=%d \n",dx,dy); +} +/* + Moves the visible area of the table right by \a xPixels and + down by \a yPixels pixels. Both may be negative. + \warning You might find that QScrollView offers a higher-level of + functionality than using QtTableView and this function. + setXOffset(), setYOffset(), setOffset(), setTopCell(), setLeftCell() +*/ + +/* + \overload int QtTableView::cellWidth() const + + Returns the column width in pixels. Returns 0 if the columns have + variable widths. + + \sa setCellWidth(), cellHeight() + Returns the width of column \a col in pixels. + + setCellWidth(), cellHeight(), totalWidth(), updateTableSize() +*/ + +// virtual +int QtTableView::cellWidth(int col) { return cellW; } + +/* + Sets the width in pixels of the table cells to \a cellWidth. + + Setting it to 0 means that the column width is variable. When + set to 0 (this is the default) QtTableView calls the virtual function + cellWidth() to get the width. + + \sa cellWidth(), setCellHeight(), totalWidth(), numCols() +*/ + +void QtTableView::setCellWidth(int cellWidth) +{ + if (cellW == cellWidth) + return; +#if defined(QT_CHECK_RANGE) + if (cellWidth < 0 || cellWidth > SHRT_MAX) + { + qWarning("QtTableView::setCellWidth: (%s) Argument out of " + "range (%d)", + name("unnamed"), cellWidth); + return; + } +#endif + cellW = (short)cellWidth; + + updateScrollBars(horSteps | horRange); + if (updatesEnabled() && isVisible()) + update(); +} + +/* + \overload int QtTableView::cellHeight() const + + Returns the row height, in pixels. Returns 0 if the rows have + variable heights. + + \sa setCellHeight(), cellWidth() + + Returns the height of row \a row in pixels. + + This function is virtual and must be reimplemented by subclasses that + have variable cell heights. Note that if the total table height + changes, updateTableSize() must be called. + + \sa setCellHeight(), cellWidth(), totalHeight() +*/ + +int QtTableView::cellHeight(int) { return cellH; } + +/* + Sets the height in pixels of the table cells to \a cellHeight. + + Setting it to 0 means that the row height is variable. When set + to 0 (this is the default), QtTableView calls the virtual function + cellHeight() to get the height. + + cellHeight(), setCellWidth(), totalHeight(), numRows() +*/ +// call by htable:: fontChanged +void QtTableView::setCellHeight(int cellHeight) +{ + + if (cellH == cellHeight) + return; +#if defined(QT_CHECK_RANGE) + if (cellHeight < 0 || cellHeight > SHRT_MAX) + { + qWarning("QtTableView::setCellHeight: (%s) Argument out of " + "range (%d)", + name("unnamed"), cellHeight); + return; + } +#endif + cellH = (short)cellHeight; + if (updatesEnabled() && isVisible()) + update(); + updateScrollBars(verSteps | verRange); +} + +// using +int QtTableView::totalWidth() +{ + if (cellW) + { + return cellW * nCols; + } + else + { + int tw = 0; + for (int i = 0; i < nCols; i++) + tw += cellWidth(i); + return tw; + } +} + +// using +int QtTableView::totalHeight() +{ + if (cellH) + { + return cellH * nRows; + } + else + { + int th = 0; + for (int i = 0; i < nRows; i++) + th += cellHeight(i); + return th; + } +} + +/* + \fn uint QtTableView::tableFlags() const + + Returns the union of the table flags that are currently set. + + \sa setTableFlags(), clearTableFlags(), testTableFlags() +*/ + +/* + \fn bool QtTableView::testTableFlags( uint f ) const + + Returns true if any of the table flags in \a f are currently set, + otherwise false. + + \sa setTableFlags(), clearTableFlags(), tableFlags() +*/ + +/* + Sets the table flags to \a f. + + If a flag setting changes the appearance of the table, the table is + repainted if - and only if - updatesEnabled() is true. + + The table flags are mostly single bits, though there are some multibit + flags for convenience. Here is a complete list: + + Tbl_vScrollBar - The table has a vertical scroll bar. + Tbl_hScrollBar
- The table has a horizontal scroll bar. + Tbl_autoVScrollBar
- The table has a vertical scroll bar if + - and only if - the table is taller than the view. + Tbl_autoHScrollBar
The table has a horizontal scroll bar if + - and only if - the table is wider than the view. + Tbl_autoScrollBars
- The union of the previous two flags. + Tbl_clipCellPainting
- The table uses QPainter::setClipRect() to + make sure that paintCell() will not draw outside the cell + boundaries. + Tbl_cutCellsV
- The table will never show part of a + cell at the bottom of the table; if there is not space for all of + a cell, the space is left blank. +
Tbl_cutCellsH
- The table will never show part of a + cell at the right side of the table; if there is not space for all of + a cell, the space is left blank. +
Tbl_cutCells
- The union of the previous two flags. +
Tbl_scrollLastHCell
- When the user scrolls horizontally, + let him/her scroll the last cell left until it is at the left + edge of the view. If this flag is not set, the user can only scroll + to the point where the last cell is completely visible. +
Tbl_scrollLastVCell
- When the user scrolls vertically, let + him/her scroll the last cell up until it is at the top edge of + the view. If this flag is not set, the user can only scroll to the + point where the last cell is completely visible. +
Tbl_scrollLastCell
- The union of the previous two flags. +
Tbl_smoothHScrolling
- The table scrolls as smoothly as + possible when the user scrolls horizontally. When this flag is not + set, scrolling is done one cell at a time. +
Tbl_smoothVScrolling
- The table scrolls as smoothly as + possible when scrolling vertically. When this flag is not set, + scrolling is done one cell at a time. +
Tbl_smoothScrolling
- The union of the previous two flags. +
Tbl_snapToHGrid
- Except when the user is actually scrolling, + the leftmost column shown snaps to the leftmost edge of the view. +
Tbl_snapToVGrid
- Except when the user is actually + scrolling, the top row snaps to the top edge of the view. +
Tbl_snapToGrid
- The union of the previous two flags. + + + You can specify more than one flag at a time using bitwise OR. + + Example: + setTableFlags( Tbl_smoothScrolling | Tbl_autoScrollBars ); + + \warning The cutCells options (\c Tbl_cutCells, \c Tbl_cutCellsH and + Tbl_cutCellsV) may cause painting problems when scrollbars are + enabled. Do not combine cutCells and scrollbars. + clearTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::setTableFlags(uint f) +{ + f = (f ^ tFlags) & f; // clear flags already set + tFlags |= f; + + bool updateOn = updatesEnabled(); + setAutoUpdate(false); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if (f & Tbl_autoVScrollBar) + { + updateScrollBars(verRange); + } + if (f & Tbl_autoHScrollBar) + { + updateScrollBars(horRange); + } + if (f & Tbl_scrollLastHCell) + { + updateScrollBars(horRange); + } + if (f & Tbl_scrollLastVCell) + { + updateScrollBars(verRange); + } + if (f & Tbl_snapToHGrid) + { + updateScrollBars(horRange); + } + if (f & Tbl_snapToVGrid) + { + updateScrollBars(verRange); + } + if (f & Tbl_snapToGrid) + { // Note: checks for 2 flags + if (((f & Tbl_snapToHGrid) != 0 && + xCellDelta != 0) || // have to scroll? + ((f & Tbl_snapToVGrid) != 0 && yCellDelta != 0)) + { + repaintMask |= Tbl_snapToGrid; // repaint table + } + } + + if (updateOn) + { + setAutoUpdate(true); + updateScrollBars(); + if (isVisible() && (f & repaintMask)) + update(); + } +} + +/* + Clears the \link setTableFlags() table flags\endlink that are set + in \a f. + + Example (clears a single flag): + \code + clearTableFlags( Tbl_snapToGrid ); + \endcode + + The default argument clears all flags. + + \sa setTableFlags(), testTableFlags(), tableFlags() +*/ + +void QtTableView::clearTableFlags(uint f) +{ + f = (f ^ ~tFlags) & f; // clear flags that are already 0 + tFlags &= ~f; + + bool updateOn = updatesEnabled(); + setAutoUpdate(false); + + uint repaintMask = Tbl_cutCellsV | Tbl_cutCellsH; + + if (f & Tbl_scrollLastHCell) + { + int maxX = maxXOffset(); + if (xOffs > maxX) + { + setOffset(maxX, yOffs); + repaintMask |= Tbl_scrollLastHCell; + } + updateScrollBars(horRange); + } + if (f & Tbl_scrollLastVCell) + { + int maxY = maxYOffset(); + if (yOffs > maxY) + { + setOffset(xOffs, maxY); + repaintMask |= Tbl_scrollLastVCell; + } + updateScrollBars(verRange); + } + if (f & Tbl_smoothScrolling) + { // Note: checks for 2 flags + if (((f & Tbl_smoothHScrolling) != 0 && + xCellDelta != 0) || // must scroll? + ((f & Tbl_smoothVScrolling) != 0 && yCellDelta != 0)) + { + repaintMask |= Tbl_smoothScrolling; // repaint table + } + } + if (f & Tbl_snapToHGrid) + { + updateScrollBars(horRange); + } + if (f & Tbl_snapToVGrid) + { + updateScrollBars(verRange); + } + if (updateOn) + { + setAutoUpdate(true); + updateScrollBars(); // returns immediately if nothing to do + if (isVisible() && (f & repaintMask)) + update(); // repaint(); + } +} + +/* + Sets the auto-update option of the table view to enable. + + If enable is true (this is the default), the view updates itself + automatically whenever it has changed in some way (for example, when a + setTableFlags() flag\endlink is changed). +*/ + +void QtTableView::setAutoUpdate(bool enable) +{ + enablePaint = enable; + // updatesEnabled= enable ; + + // setAttribute(Qt::WA_ForceUpdatesDisabled, !enable); + //// setAttribute(Qt::WA_UpdatesDisabled, !enable); + // d->setUpdatesEnabled_helper(enable); + + return; + + if (updatesEnabled() == enable) + return; + setUpdatesEnabled(enable); // update() call + if (enable) + { + // updateScrollBars(); + } +} + +/* + Returns the index of the last (bottom) row in the view. + The index of the first row is 0. + + If no rows are visible it returns -1. This can happen if the + view is too small for the first row and Tbl_cutCellsV is set. + + \sa lastColVisible() + */ + +int QtTableView::lastRowVisible() const +{ + int cellMaxY; + int row = findRawRow(maxViewY(), &cellMaxY); + if (row == -1 || row >= nRows) + { // maxViewY() past end? + row = nRows - 1; // yes: return last row + } + else + { + if (testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY()) + { + if (row == yCellOffs) // cut by right margin? + return -1; // yes, nothing in the view + else + row = row - 1; // cut by margin, one back + } + } + return row; +} + +/* + Returns the index of the last (right) column in the view. + The index of the first column is 0. + + If no columns are visible it returns -1. This can happen if the + view is too narrow for the first column and Tbl_cutCellsH is set. + + \sa lastRowVisible() +*/ + +/* +int QtTableView::lastVisibleCol() const +{ + int cellMaxX; + int col = findRawCol( maxViewX(), &cellMaxX ); + if ( col == -1 || col >= nCols ) { // maxViewX() past end? + col = nCols - 1; // yes: return last col + } else { + col = col - 1; // cell by margin, one back + } + return col; +} */ + +int QtTableView::lastColVisible() const +{ + int cellMaxX; + int col = findRawCol(maxViewX(), &cellMaxX); + if (col == -1 || col >= nCols) + { // maxViewX() past end? + col = nCols - 1; // yes: return last col + } + else + { + if (testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX()) + { + if (col == xCellOffs) // cut by bottom margin? + return -1; // yes, nothing in the view + else + col = col - 1; // cell by margin, one back + } + } + return col; +} + +/* + Returns true if \a row is at least partially visible. + \sa colIsVisible() +*/ + +bool QtTableView::rowIsVisible(int row) const { return rowYPos(row, 0); } + +/* + Returns true if \a col is at least partially visible. + \sa rowIsVisible() +*/ + +bool QtTableView::colIsVisible(int col) const { return colXPos(col, 0); } + +/* + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table horizontally to offset \a val without updating the + scroll bar. +*/ + +void QtTableView::horSbValue(int val) +{ + if (horSliding) + { + horSliding = false; + if (horSnappingOff) + { + horSnappingOff = false; + tFlags |= Tbl_snapToHGrid; + } + } + setOffset(val, yOffs, false); +} + +/* + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly horizontally even if \c Tbl_snapToHGrid is set. +*/ + +void QtTableView::horSbSliding(int val) +{ + if (testTableFlags(Tbl_snapToHGrid) && testTableFlags(Tbl_smoothHScrolling)) + { + tFlags &= ~Tbl_snapToHGrid; // turn off snapping while sliding + setOffset(val, yOffs, false); + tFlags |= Tbl_snapToHGrid; // turn on snapping again + } + else + { + setOffset(val, yOffs, false); + } +} + +/* + \internal + This internal slot is connected to the horizontal scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::horSbSlidingDone() +{ + if (testTableFlags(Tbl_snapToHGrid) && testTableFlags(Tbl_smoothHScrolling)) + ; // snapToGrid( true, false ); +} + +/* + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::valueChanged() signal. + + Moves the table vertically to offset \a val without updating the + scroll bar. + */ + +void QtTableView::verSbValue(int val) +{ + if (verSliding) + { + verSliding = false; + if (verSnappingOff) + { + verSnappingOff = false; + tFlags |= Tbl_snapToVGrid; + } + } + setOffset(xOffs, val, false); +} + +/* + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderMoved() signal. + + Scrolls the table smoothly vertically even if \c Tbl_snapToVGrid is set. +*/ + +void QtTableView::verSbSliding(int val) +{ + if (testTableFlags(Tbl_snapToVGrid) && testTableFlags(Tbl_smoothVScrolling)) + { + tFlags &= ~Tbl_snapToVGrid; // turn off snapping while sliding + setOffset(xOffs, val, false); + tFlags |= Tbl_snapToVGrid; // turn on snapping again + } + else + { + setOffset(xOffs, val, false); + } +} + +/* + \internal + This internal slot is connected to the vertical scroll bar's + QScrollBar::sliderReleased() signal. +*/ + +void QtTableView::verSbSlidingDone() +{ + if (testTableFlags(Tbl_snapToVGrid) && testTableFlags(Tbl_smoothVScrolling)) + ; // snapToGrid( false, true ); +} + +/* + This virtual function is called before painting of table cells + is started. It can be reimplemented by subclasses that want to + to set up the painter in a special way and that do not want to + do so for each cell. +*/ + +/* + Handles paint events, for the table view. + Calls paintCell() for the cells that needs to be repainted. +*/ +// work? +void QtTableView::repaintCell(int row, int col, bool usecache) // false +{ + // + static int c = 0; + int xPos, yPos; + if (!colXPos(col, &xPos)) + return; + if (!rowYPos(row, &yPos)) + return; + + QRect uR = QRect(xPos, yPos, cellWidth(col), cellHeight(row)); + // printf("repaintCell() %d, + // [%d,%d,%d,%d]\n",c++,uR.x(),uR.y(),uR.width(),uR.height() ); + view->repaint(uR.intersected(viewRect())); // slow + // view->update( uR.intersect(viewRect())); +} + +// using +void QtTableView::repaintRow(int row) +{ + int y; + if (rowYPos(row, &y)) + { + // view->repaint(minViewX(),y,viewWidth(),cellHeight()); + view->update(minViewX(), y, viewWidth(), cellHeight()); + }; +} + +extern QThread *thread_main; +void QtTableView::paintEvent(QPaintEvent *e) +{ + static int count = 0; + + ////if(thread_main!=thread()) + /// printf("Error : main_thread(%X) != paint_thread(%X) report this + /// message!!!\n",thread_main,thread()); + + /// if ( !isVisible() or !enablePaint ) return; + checkProfile(); // check cache, current_get + + if (!isVisible()) + return; + + QRect viewR = viewRect(); + bool flag_fullpainting = false; + QRect updateR = e->rect(); // update rectangle + + QPainter paint(viewport()); + // printf("Qps: %s::paintEvent() + // count=%d\n",objectName().toAscii().data(),count++); + // printf("%s + //[%d,%d,%d,%d];\n",objectName().toAscii().data(),updateR.x(),updateR.y(),updateR.width(),updateR.height()); + + /* possible? + int maxVX = maxXOffset(), maxVY = maxYOffset(); + if ( updateR.right() > maxVX ) updateR.setRight( maxVX ); + if ( updateR.bottom() > maxVY ) updateR.setBottom( maxVY ); + */ + + if (updateR.width() < viewR.width() or updateR.height() < viewR.height()) + { + // when: Scroll_up,down and Selection + // printf("partial: %d!=%d, + // %d!=%d\n",updateR.width(),viewR.width(),updateR.height(),viewR.height()); + // printf("viewR (%d,%d,%d,%d) + // \n",viewR.x(),viewR.y(),viewR.width(),viewR.height()); + } + else + { + flag_fullpainting = true; + /// printf("%s::paintEvent() fullpainting + ///%d\n",objectName().toAscii().data(),count++); + } + + int firstRow = findRow(updateR.y()); + int firstCol = findCol(updateR.x()); + + int xStart, yStart; + + if (!colXPos(firstCol, &xStart)) + { + // right empty area of table + // paint.eraseRect( updateR ); // erase area outside cells but + // in view + // printf("colXPos null\n"); + eraseRight(&paint, updateR); + return; + } + + // if ( !rowYPos( firstRow, &yStart ) || !colXPos( firstCol, &xStart ) ) + if (!rowYPos(firstRow, &yStart)) + { // get firstRow + // printf("eraseRect()\n"); + paint.eraseRect(updateR); + return; + } + + int maxX = updateR.right(); // x2 + int maxY = updateR.bottom(); // y2 + int row = firstRow; + int col; + int yPos = yStart; + int xPos = maxX + 1; // in case the while() is empty + int nextX; + int nextY; + + // printf("frow=%d,fcol=%d\n",firstRow,firstCol); + // void (QtTableView::*painT)( QPainter *, int row, int col + // ,bool + // update); + // painT=&QtTableView::paintCell; + // paint.setClipRect( 0,0,viewR.width()-30,viewR.height() ); //enable, + // font + // not clip + + paint.setClipRect(viewR); // enable, font not clip (less Qt-4.3.x) + // paint.setClipRect(updateR); //enable, font not clip + + while (yPos <= maxY and row < nRows) + { // row=...5,6,7.... + nextY = yPos + cellHeight(); + col = firstCol; + xPos = xStart; + while (xPos < maxX and col < nCols) + { + QRect cell; + int width = cellWidth(col); + nextX = xPos + width; + cell.setRect(xPos, yPos, width, cellH); + tmp_size = viewR.intersected(cell).size(); + tmp_x = xPos; + { + paint.translate(xPos, yPos); // (0,0) // for subclass + //(*this.*painT)( &paint, row, col , + // flag_use_cache); + paintCell(&paint, row, col, false); + paint.translate(-xPos, -yPos); // paint.translate(0,0); + } + col++; + xPos = nextX; + } + row++; + yPos = nextY; + } + + // printf("%s: xoff=%d ,yoff=%d + //\n",objectName().toAscii().data(),xOffs, + // yOffs); + + // while painting we have to erase any areas in the view that + // are not covered by cells but are covered by the paint event + // rectangle these must be erased. We know that xPos is the last + // x pixel updated + 1 and that yPos is the last y pixel updated + 1. + if (xPos <= maxX) + { + QRect r = viewR; + r.setLeft(xPos); + r.setBottom(yPos < maxY ? yPos : maxY); + + // QRect ir=r.intersect( updateR ); + eraseRight(&paint, r); //???????? + } + + if (yPos <= maxY) + { + QRect r = viewR; + r.setTop(yPos); + paint.eraseRect(r.intersected(updateR)); + } +} + +void QtTableView::repaintChanged() // only fullpainting +{ + + if (0) + { + // printf("repaintChanged()\n"); + test = true; + // viewport()->setAutoFillBackground (false); + viewport()->setAttribute(Qt::WA_OpaquePaintEvent); + viewport()->repaint(); + viewport()->setAttribute(Qt::WA_OpaquePaintEvent, false); + test = false; + } + + if (!isVisible()) + return; + bool flag_fullpainting = false; + + QRect updateR = viewRect(); + QRect viewR = viewRect(); + + flag_fullpainting = true; + + int firstRow = findRow(updateR.y()); + int firstCol = findCol(updateR.x()); + + int xStart, yStart; + + if (!colXPos(firstCol, &xStart)) + { + // right empty area of table + printf("b\n"); + view->update(updateR); + return; + } + + // if ( !rowYPos( firstRow, &yStart ) || !colXPos( firstCol, &xStart ) ) + if (!rowYPos(firstRow, &yStart)) + { // get firstRow + /// printf("a\n"); + view->update(updateR); + return; + } + + int maxX = updateR.right(); // x2 + int maxY = updateR.bottom(); // y2 + int row = firstRow; + int col; + int yPos = yStart; + int xPos = maxX + 1; // in case the while() is empty + int nextX; + int nextY; + + while (yPos <= maxY and row < nRows) + { // row=...5,6,7.... + nextY = yPos + cellHeight(); + col = firstCol; + xPos = xStart; + while (xPos < maxX and col < nCols) + { + QRect cell; + int width = cellWidth(col); + nextX = xPos + width; + cell.setRect(xPos, yPos, width, cellH); + int ctl = 0; + tmp_size = viewR.intersected(cell).size(); + + if (isCellChanged(row, col)) + { + if (col == firstCol) + { + repaintRow(row); // speed up! update()... + // view->update(minViewX(),y,viewWidth(),cellHeight()); + /// printf("row %d\n",row); + break; + } + + // printf("row %d col=%d\n",row,col); + repaintCell(row, col, false); + } + col++; + xPos = nextX; + } + row++; + yPos = nextY; + } + + if (xPos <= maxX) + { + QRect r = viewR; + r.setLeft(xPos); + r.setBottom(yPos < maxY ? yPos : maxY); + view->repaint(r.intersected(updateR)); + } + if (yPos <= maxY) + { + QRect r = viewR; + r.setTop(yPos); + view->repaint(r.intersected(updateR)); // why? CPU +2~3% -> rect.unite() + } + + return; + + int rows = numRows(); + int cols = numCols(); + int left = leftCell(), right = lastColVisible(); + int top = topCell(), bottom = lastRowVisible(); + + if (right >= cols) + right = cols - 1; //??? + if (bottom >= rows) + bottom = rows - 1; + // if width[col] be changed ,then the right of [col] should be repainted + // ! + // repaintColumns(c,-1); + // printf("left=%d \n",left); + + for (int r = top; r <= bottom; r++) + { + for (int c = left; c <= right; c++) + { + // if(isCellChanged(r,c)) repaintCell(r, c,false); + } + } +} + +void QtTableView::resizeEvent(QResizeEvent *e) +{ + // printf("QtTableView::resize [%d,%d] \n",width(),height()); + // QAbstractScrollArea::resizeEvent(e); + updateScrollBars(horValue | verValue | horSteps | horRange | verSteps | + verRange); + return; +} + +// BOTTLENECK? +int QtTableView::findRawRow(int yPos, int *cellMaxY, int *cellMinY, + bool goOutsideView) const +{ + int r = -1; + if (nRows == 0) + return r; + if (goOutsideView || (yPos >= minViewY() && yPos <= maxViewY())) + { + if (yPos < minViewY()) + { +#if defined(QT_CHECK_RANGE) + qWarning("QtTableView::findRawRow: (%s) internal error: " + "yPos < minViewY() && goOutsideView " + "not supported. (%d,%d)", + name("unnamed"), yPos, yOffs); +#endif + return -1; + } + if (cellH) + { // uniform cell height + r = (yPos - minViewY() + yCellDelta) / cellH; // cell offs from top + if (cellMaxY) + *cellMaxY = (r + 1) * cellH + minViewY() - yCellDelta - 1; + if (cellMinY) + *cellMinY = r *cellH + minViewY() - yCellDelta; + r += yCellOffs; // absolute cell index + } + else + { // variable cell height + QtTableView *tw = (QtTableView *)this; + r = yCellOffs; + int h = minViewY() - yCellDelta; //##arnt3 + int oldH = h; + Q_ASSERT(r < nRows); + while (r < nRows) + { + oldH = h; + h += tw->cellHeight(r); // Start of next cell + if (yPos < h) + break; + r++; + } + if (cellMaxY) + *cellMaxY = h - 1; + if (cellMinY) + *cellMinY = oldH; + } + } + return r; +} + +// return vitrual col. +int QtTableView::findRawCol(int xPos, int *cellMaxX, int *cellMinX, + bool goOutsideView) const +{ + int c = -1; + if (nCols == 0) + return c; + if (goOutsideView || (xPos >= minViewX() && xPos <= maxViewX())) + { + if (xPos < minViewX()) + { +#if defined(QT_CHECK_RANGE) + qWarning("QtTableView::findRawCol: (%s) internal error: " + "xPos < minViewX() && goOutsideView " + "not supported. (%d,%d)", + name("unnamed"), xPos, xOffs); +#endif + return -1; + } + if (cellW) + { // uniform cell width + c = (xPos - minViewX() + xCellDelta) / cellW; // cell offs from left + if (cellMaxX) + *cellMaxX = (c + 1) * cellW + minViewX() - xCellDelta - 1; + if (cellMinX) + *cellMinX = c *cellW + minViewX() - xCellDelta; + c += xCellOffs; // absolute cell index + } + else + { // variable cell width + QtTableView *tw = (QtTableView *)this; + c = xCellOffs; + int w = minViewX() - xCellDelta; //##arnt3 + int oldW = w; + Q_ASSERT(c < nCols); + while (c < nCols) + { + oldW = w; + w += tw->cellWidth(c); // Start of next cell + if (xPos < w) + break; + c++; + } + if (cellMaxX) + *cellMaxX = w - 1; + if (cellMinX) + *cellMinX = oldW; + } + } + return c; +} + +/* + Returns the index of the row at position \a yPos, where \a yPos is in + \e widget coordinates. Returns -1 if \a yPos is outside the valid + range. + + \sa findCol(), rowYPos() +*/ + +int QtTableView::findRow(int yPos) const +{ + int cellMaxY; + int row = findRawRow(yPos, &cellMaxY); + if (testTableFlags(Tbl_cutCellsV) && cellMaxY > maxViewY()) + row = -1; // cell cut by bottom margin + if (row >= nRows) + row = -1; + return row; +} + +/* + Returns the index of the column at position \a xPos, where \a xPos is + in \e widget coordinates. Returns -1 if \a xPos is outside the valid + range. + + \sa findRow(), colXPos() +*/ + +int QtTableView::findCol(int xPos) const +{ + int cellMaxX; + int col = findRawCol(xPos, &cellMaxX); + if (testTableFlags(Tbl_cutCellsH) && cellMaxX > maxViewX()) + col = -1; // cell cut by right margin + if (col >= nCols) + col = -1; + return col; +} + +// testing +int QtTableView::findColNoMinus(int xPos) const +{ + int col = findCol(xPos); + + if (col < 0) + { + if (xPos < 0) + col = 0; + else + col = nCols - 1; + } + return col; +} +/* + Computes the position in the widget of row \a row. + + Returns true and stores the result in \a *yPos (in \e widget + coordinates) if the row is visible. Returns false and does not modify + \a *yPos if \a row is invisible or invalid. + + \sa colXPos(), findRow() +*/ + +// return : false means out of bound +bool QtTableView::rowYPos(int row, int *yPos) const +{ + int y; + if (row >= yCellOffs) + { + if (cellH) + { + int lastVisible = lastRowVisible(); + if (row > lastVisible || lastVisible == -1) + return false; + y = (row - yCellOffs) * cellH + minViewY() - yCellDelta; + } + else + { + //##arnt3 + y = minViewY() - yCellDelta; // y of leftmost cell in view + int r = yCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxY = maxViewY(); + while (r < row && y <= maxY) + y += tw->cellHeight(r++); + if (y > maxY) + return false; + } + } + else + { + return false; + } + if (yPos) + *yPos = y; + return true; +} + +/* + Computes the position in the widget of column col. + + Returns true and stores the result in \a *xPos (in \e widget + coordinates) if the column is visible. Returns false and does not + modify \a *xPos if \a col is invisible or invalid. + + \sa rowYPos(), findCol() +*/ + +bool QtTableView::colXPos(int col, int *xPos) const +{ + int x; + if (col >= xCellOffs) + { + if (cellW) + { + int lastVisible = lastColVisible(); + if (col > lastVisible || lastVisible == -1) + return false; + x = (col - xCellOffs) * cellW + minViewX() - xCellDelta; + } + else + { + //##arnt3 + x = minViewX() - xCellDelta; // x of uppermost cell in view + int c = xCellOffs; + QtTableView *tw = (QtTableView *)this; + int maxX = maxViewX(); + while (c < col && x <= maxX) + x += tw->cellWidth(c++); + if (x > maxX) + return false; + } + } + else + { + return false; + } + if (xPos) + *xPos = x; + return true; +} + +QRect QtTableView::viewRect() const +{ + return viewport()->rect(); + // return QRect( 0, 0, viewWidth(), viewHeight() ); +} + +int QtTableView::minViewX() const +{ + return 0; // frameWidth(); +} + +int QtTableView::minViewY() const +{ + return 0; // view->frameWidth(); +} + +/* + Returns the rightmost pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels to the right of the visible table data. + + \sa maxViewY(), viewWidth(), +*/ + +int QtTableView::maxViewX() const { return viewport()->width(); } + +/* + Returns the bottom pixel of the table view in \e view + coordinates. This excludes the frame and any scroll bar, but + includes blank pixels below the visible table data. + + \sa maxViewX(), viewHeight(), +*/ + +int QtTableView::maxViewY() const { return viewport()->height(); } + +/* + Returns the width of the table view, as such, in \e view + coordinates. This does not include any header, scroll bar or frame, + but it does include background pixels to the right of the table data. + + minViewX() maxViewX(), viewHeight(),viewRect() +*/ + +int QtTableView::viewWidth() const { return maxViewX(); } + +int QtTableView::viewHeight() const +{ + return maxViewY(); /// - minViewY() + 1; +} + +/* + \fn void QtTableView::updateScrollBars() + Updates the scroll bars' contents and presence to match the table's + state. Generally, you should not need to call this. + \sa setTableFlags() + Updates the scroll bars' contents and presence to match the table's + state \c or \a f. + \sa setTableFlags() +*/ + +void QtTableView::updateScrollBars(uint f) +{ + sbDirty = sbDirty | f; + // if ( inSbUpdate ) return; + // inSbUpdate = true; + + if (yOffset() > 0 && testTableFlags(Tbl_autoVScrollBar) && + !testTableFlags(Tbl_vScrollBar)) + { + setYOffset(0); //???????? + } + if (xOffset() > 0 && testTableFlags(Tbl_autoHScrollBar) && + !testTableFlags(Tbl_hScrollBar)) + { + setXOffset(0); //????? + } + if (!isVisible()) + { + inSbUpdate = false; + return; + } + + if (testTableFlags(Tbl_hScrollBar) && (sbDirty & horMask) != 0) + { + if (sbDirty & horSteps) + { + if (cellW) + hScrollBar->setSingleStep(qMin((int)cellW, viewWidth() / 2)); + else + hScrollBar->setSingleStep(16); + hScrollBar->setPageStep(viewWidth()); + } + + if (sbDirty & horRange) + { + hScrollBar->setRange(0, maxXOffset()); + } + if (sbDirty & horValue) + hScrollBar->setValue(xOffs); + } + + if (testTableFlags(Tbl_vScrollBar) && (sbDirty & verMask) != 0) + { + if (sbDirty & verSteps) + { + if (cellH) + vScrollBar->setSingleStep(qMin((int)cellH, viewHeight() / 2)); + else + vScrollBar->setSingleStep(16); // fttb! ### + vScrollBar->setPageStep(viewHeight()); + } + + if (sbDirty & verRange) + vScrollBar->setRange(0, maxYOffset()); + + if (sbDirty & verValue) + vScrollBar->setValue(yOffs); + } + + sbDirty = 0; + inSbUpdate = false; +} + +/* + Updates the scroll bars and internal state. + + Call this function when the table view's total size is changed; + typically because the result of cellHeight() or cellWidth() have changed. + This function does not repaint the widget. +*/ +//-> updateViewSize(); +void QtTableView::updateTableSize() +{ + updateScrollBars(horSteps | horRange | verSteps | verRange); + return; + bool updateOn = updatesEnabled(); + setAutoUpdate(false); + int xofs = xOffset(); + xOffs++; // so that setOffset will not return immediately + setOffset(xofs, yOffset(), false); // to calculate internal state correctly + setAutoUpdate(updateOn); +} + +/* + Returns the maximum horizontal offset within the table of the + view's left edge in \e table coordinates. + + This is used mainly to set the horizontal scroll bar's range. + + \sa maxColOffset(), maxYOffset(), totalWidth() +*/ + +int QtTableView::maxXOffset() +{ + int tw = totalWidth(); + int maxOffs; + if (testTableFlags(Tbl_scrollLastHCell)) + { + if (nCols != 1) + maxOffs = tw - (cellW ? cellW : cellWidth(nCols - 1)); + else + maxOffs = tw - viewWidth(); + } + else + { + if (testTableFlags(Tbl_snapToHGrid)) + { + if (cellW) + { + maxOffs = tw - (viewWidth() / cellW) * cellW; + } + else + { + int goal = tw - viewWidth(); + int pos = tw; + int nextCol = nCols - 1; + int nextCellWidth = cellWidth(nextCol); + while (nextCol > 0 && pos > goal + nextCellWidth) + { + pos -= nextCellWidth; + nextCellWidth = cellWidth(--nextCol); + } + if (goal + nextCellWidth == pos) + maxOffs = goal; + else if (goal < pos) + maxOffs = pos; + else + maxOffs = 0; + } + } + else + { + maxOffs = tw - viewWidth(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + +/* + Returns the maximum vertical offset within the table of the + view's top edge in \e table coordinates. + + This is used mainly to set the vertical scroll bar's range. + \sa maxRowOffset(), maxXOffset(), totalHeight() +*/ + +int QtTableView::maxYOffset() +{ + int th = totalHeight(); + int maxOffs; + if (testTableFlags(Tbl_scrollLastVCell)) + { + if (nRows != 1) + maxOffs = th - (cellH ? cellH : cellHeight(nRows - 1)); + else + maxOffs = th - viewHeight(); + } + else + { + if (testTableFlags(Tbl_snapToVGrid)) + { + if (cellH) + { + maxOffs = th - (viewHeight() / cellH) * cellH; + } + else + { + int goal = th - viewHeight(); + int pos = th; + int nextRow = nRows - 1; + int nextCellHeight = cellHeight(nextRow); + while (nextRow > 0 && pos > goal + nextCellHeight) + { + pos -= nextCellHeight; + nextCellHeight = cellHeight(--nextRow); + } + if (goal + nextCellHeight == pos) + maxOffs = goal; + else if (goal < pos) + maxOffs = pos; + else + maxOffs = 0; + } + } + else + { + maxOffs = th - viewHeight(); + } + } + return maxOffs > 0 ? maxOffs : 0; +} + +/* + Returns the index of the last column, which may be at the left edge + of the view. + + Depending on the \link setTableFlags() Tbl_scrollLastHCell\endlink flag, + this may or may not be the last column. + + \sa maxXOffset(), maxRowOffset() +*/ + +int QtTableView::maxColOffset() +{ + int mx = maxXOffset(); + if (cellW) + return mx / cellW; + else + { + int xcd = 0, col = 0; + while (col < nCols && mx > (xcd = cellWidth(col))) + { + mx -= xcd; + col++; + } + return col; + } +} + +/* + Returns the index of the last row, which may be at the top edge of + the view. + Depending on the \link setTableFlags() Tbl_scrollLastVCell\endlink flag, + this may or may not be the last row. + + \sa maxYOffset(), maxColOffset() +*/ + +int QtTableView::maxRowOffset() +{ + int my = maxYOffset(); + if (cellH) + return my / cellH; + else + { + int ycd = 0, row = 0; + while (row < nRows && my > (ycd = cellHeight(row))) + { + my -= ycd; + row++; + } + return row; + } +} + +// DEL +void QtTableView::showOrHideScrollBars() { return; } diff --git a/src/qttableview.h b/src/qttableview.h new file mode 100644 index 0000000..897d68e --- /dev/null +++ b/src/qttableview.h @@ -0,0 +1,219 @@ +/********************************************************************** +** $Id: qttableview.h,v 1.46 2011/08/27 00:13:41 fasthyun Exp $ +** +** Definition of QtTableView class +** +** Created : 941115 +** +** Copyright (C) 1992-2000 Trolltech AS. All rights reserved. +** +** This file contains a class moved out of the Qt GUI Toolkit API. It +** may be used, distributed and modified without limitation. +** +**********************************************************************/ + +#ifndef QTTABLEVIEW_H +#define QTTABLEVIEW_H + +#ifndef USING_PCH +#include +#include +#include +#include +#include +#endif + +class QScrollBar; +class QtTableView : public QAbstractScrollArea +{ + Q_OBJECT + public: + QWidget *view; + int flag_view; + int cellWidth() const; + int cellHeight() const; + void repaintRow(int row); // paintRow(); + void coverCornerSquare(bool); + void clearCache() {} + void repaintChanged(); + virtual bool isCellChanged(int r, int c) { return true; }; + virtual void eraseRight(QPainter *, QRect &r) { return; } + virtual void checkProfile(){}; + QSize tmp_size; // testing. + int tmp_x; + bool test; + QColor backColor; + + protected: + QtTableView(QWidget *parent = 0, const char *name = 0); + ~QtTableView(); + + int numRows() const; + int numCols() const; + void setNumRows(int); + void setNumCols(int); + + int topCell() const; + int leftCell() const; + void setTopCell(int row); + void setLeftCell(int col); + void setTopLeftCell(int row, int col); + + int xOffset() const; + int yOffset() const; + virtual void setXOffset(int); + virtual void setYOffset(int); + virtual void setOffset(int x, int y, bool updateScrBars = true); + virtual void scrollTrigger(int x, int y){}; // tmp + + virtual int cellWidth(int col); + int cellHeight(int row); + virtual void setCellWidth(int); + virtual void setCellHeight(int); + + int totalWidth(); + int totalHeight(); + + uint tableFlags() const; + // bool testTableFlags( uint f ) const; + virtual void setTableFlags(uint f); + void clearTableFlags(uint f = ~0); + + void setAutoUpdate(bool); + + void repaintCell(int row, int column, bool usecache = false); + + QRect cellUpdateRect() const; + QRect viewRect() const; + + int lastRowVisible() const; + int lastColVisible() const; + + bool rowIsVisible(int row) const; + bool colIsVisible(int col) const; + + QScrollBar *verticalScrollBar() const; + QScrollBar *horizontalScrollBar() const; + + private slots: + void horSbValue(int); + void horSbSliding(int); + void horSbSlidingDone(); + void verSbValue(int); + void verSbSliding(int); + void verSbSlidingDone(); + + protected: + virtual void paintCell(QPainter *, int row, int col, bool update) = 0; + virtual void paintEvent(QPaintEvent *); + virtual void resizeEvent(QResizeEvent *); + virtual void scrollContentsBy(int dx, int dy); + + int findRow(int yPos) const; + int findCol(int xPos) const; + int findColNoMinus(int xPos) const; + + bool rowYPos(int row, int *yPos) const; + bool colXPos(int col, int *xPos) const; + + int maxXOffset(); + int maxYOffset(); + int maxColOffset(); + int maxRowOffset(); + + int minViewX() const; + int minViewY() const; + int maxViewX() const; + int maxViewY() const; + int viewWidth() const; + int viewHeight() const; + + void updateScrollBars(); + void updateTableSize(); + + QRect cellUpdateR; + + private: + int findRawRow(int yPos, int *cellMaxY, int *cellMinY = 0, + bool goOutsideView = false) const; + int findRawCol(int xPos, int *cellMaxX, int *cellMinX = 0, + bool goOutsideView = false) const; + int maxColsVisible() const; + + void updateScrollBars(uint); + void showOrHideScrollBars(); + + int nRows; + int nCols; + int xOffs, yOffs; + int xCellOffs, yCellOffs; + short xCellDelta, yCellDelta; + short cellH, cellW; // + + uint eraseInPaint : 1; + uint verSliding : 1; + uint verSnappingOff : 1; + uint horSliding : 1; + uint horSnappingOff : 1; + uint coveringCornerSquare : 1; + uint sbDirty : 8; + uint inSbUpdate : 1; + + bool enablePaint; + + uint tFlags; + + QScrollBar *vScrollBar; + QScrollBar *hScrollBar; +}; + +const uint Tbl_vScrollBar = 0x00000001; +const uint Tbl_hScrollBar = 0x00000002; +const uint Tbl_autoVScrollBar = 0x00000004; +const uint Tbl_autoHScrollBar = 0x00000008; +const uint Tbl_autoScrollBars = 0x0000000C; + +const uint Tbl_clipCellPainting = 0x00000100; +const uint Tbl_cutCellsV = 0x00000200; +const uint Tbl_cutCellsH = 0x00000400; +const uint Tbl_cutCells = 0x00000600; + +const uint Tbl_scrollLastHCell = 0x00000800; +const uint Tbl_scrollLastVCell = 0x00001000; +const uint Tbl_scrollLastCell = 0x00001800; + +const uint Tbl_smoothHScrolling = 0x00002000; +const uint Tbl_smoothVScrolling = 0x00004000; +const uint Tbl_smoothScrolling = 0x00006000; + +const uint Tbl_snapToHGrid = 0x00008000; +const uint Tbl_snapToVGrid = 0x00010000; +const uint Tbl_snapToGrid = 0x00018000; + +inline int QtTableView::numRows() const { return nRows; } + +inline int QtTableView::numCols() const { return nCols; } + +inline int QtTableView::topCell() const { return yCellOffs; } + +inline int QtTableView::leftCell() const { return xCellOffs; } + +inline int QtTableView::xOffset() const { return xOffs; } + +inline int QtTableView::yOffset() const { return yOffs; } + +inline int QtTableView::cellHeight() const { return cellH; } + +inline int QtTableView::cellWidth() const { return cellW; } + +inline uint QtTableView::tableFlags() const { return tFlags; } + +#define testTableFlags(f) ((tFlags & f) != 0) +// inline bool QtTableView::testTableFlags( uint f ) const{ return (tFlags & f) +// != 0; } + +inline QRect QtTableView::cellUpdateRect() const { return cellUpdateR; } + +inline void QtTableView::updateScrollBars() { updateScrollBars(0); } + +#endif // QTTABLEVIEW_H diff --git a/src/screenshot.cpp b/src/screenshot.cpp new file mode 100644 index 0000000..6853b63 --- /dev/null +++ b/src/screenshot.cpp @@ -0,0 +1,425 @@ +#include +#include +#include "screenshot.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* +SizeGrip::SizeGrip() +{ + + setWindowFlags( Qt::FramelessWindowHint); +} */ + +QSizeGrip *sg; +int window_width = 320; +int window_height = 240; + +TitleBar::TitleBar(QWidget *w) : QWidget(w) +{ + // setWindowFlags( Qt::FramelessWindowHint); + // resize(window_width,32); + setMinimumHeight(32); +} + +void TitleBar::mousePressEvent(QMouseEvent *) +{ + // press_pos = e->pos(); + // printf("x=%d y=%d\n",press_pos.x(),press_pos.y()); +} + +void TitleBar::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) // Button state ) + { + // move(e->globalX()-e->x(), e->globalY()-e->y());// + // coe->x(),e->y()); + // move(e->globalX() - press_pos.x(), e->globalY()- + // press_pos.y());// + // coe->x(),e->y()); + // sg->move(x()-100,y()-100); + } +} + +ShotArea::ShotArea() +{ + setLineWidth(3); + setFrameStyle(QFrame::Panel | QFrame::Raised); + setAttribute(Qt::WA_OpaquePaintEvent); + setAttribute(Qt::WA_NoSystemBackground); + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); +} + +void ShotArea::resizeEvent(QResizeEvent * /* event */) +{ + // printf("ShotArea::resizeEvent()\n"); + // QPixmap pixmap = QPixmap(screenshotLabel->size()); +} + +void ShotArea::paintEvent(QPaintEvent *event) +{ + // printf("ShotArea::paintEvent\n"); + // QPainter painter(this); + // painter.setOpacity(1.0); + // painter.fillRect(10, 10, 100, 80, QColor(255,255,255,120)); +} + +void ShotArea::mousePressEvent(QMouseEvent *e) +{ + press_pos = e->pos(); + /// printf("x=%d y=%d\n",press_pos.x(),press_pos.y()); +} + +void ShotArea::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) // Button state ) + { + // move(e->globalX()-e->x(), e->globalY()-e->y());// + // coe->x(),e->y()); + // move(e->globalX() - press_pos.x(), e->globalY()- + // press_pos.y());// + // coe->x(),e->y()); + // sg->move(x()-100,y()-100); + } +} + +#include +Screenshot::Screenshot(QWidget *p) : QWidget(p) +{ + // Qt::WindowTitleHint + // Qt::CustomizeWindowHint ); + // setWindowTitle(tr("Screenshot")); + // TitleBar *titlebar=new TitleBar(this); + QStatusBar *statusbar = new QStatusBar(this); + shotarea = new ShotArea(); + // setWindowFlags(Qt::WindowStaysOnTopHint); + setWindowFlags(Qt::FramelessWindowHint); + // setWindowFlags(Qt::X11BypassWindowManagerHint); + setStyleSheet( // padding: 2px + "QWidget { background-color : rgba(20,20,20,70%); color: " + "rgb(0,255,150);}" + "QPushButton,QToolButton,QLineEdit { border-width: 1px; " + "padding:3px; " + "border-style: solid; border-color: rgb(210,50,130); " + "border-radius: 5px " + ";}" + "QPushButton:hover,QToolButton:hover { color:rgb(40,255,190); " + "padding:3px; border-width: 1px; border-style: solid; " + "border-color: " + "rgb(230,80,170); border-radius: 5px ;}" + "QPushButton:pressed,QToolButton:pressed { color:rgb(0,255,150); " + "padding:3px; border-width: 2px; border-style: solid; " + "border-color: " + "rgb(210,50,130); border-radius: 5px ;}" + "QComboBox { border-width: 1px; padding:3px; border-style: solid; " + "border-color: rgb(50,200,130); border-radius: 5px ;}" + "QLabel { border-width: 1px; padding:3px; border-style: solid; " + "border-color: rgb(65,16,40); border-radius: 5px ;}" + "QComboBox:drop-down { border-width: 2px; padding:0px; " + "border-style: " + "solid; border-color: rgb(50,200,130); border-radius: 3px ;}"); + +// sg=new QSizeGrip(NULL); +// sg->show(); +// sg->move(100,100); +// QStyle::PE_Frame +// createOptionsGroupBox(); +// createButtonsLayout(); + +#if QT_VERSION > 0x040500 + +#endif + + mainLayout = new QVBoxLayout; +#if QT_VERSION < 0x040300 + mainLayout->setMargin(3); // qt-4.2 +#else + mainLayout->setContentsMargins(3, 3, 3, 3); // qt-4.3 +#endif + mainLayout->setSpacing(0); + + // mainLayout->addWidget(titlebar); + mainLayout->addWidget(shotarea); + // mainLayout->addLayout(buttonsLayout); + mainLayout->addWidget(statusbar); + + // saveScreenshotButton = createButton(tr("Save"),this, + // SLOT(saveScreenshot())); + saveScreenshotButton = new QPushButton("Save"); + connect(saveScreenshotButton, SIGNAL(clicked()), this, + SLOT(saveScreenshot())); + + statusbar->addWidget(saveScreenshotButton); + // statusbar->addWidget( quitScreenshotButton); + statusbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); + setLayout(mainLayout); + // delaySpinBox->setValue(5); + + setMouseTracking(true); + + // if (QApplication::desktop()->winId() < 0) + // printf("Qps: Null Desktop\n"); + resize(300, 240); +} + +static QPoint init_pos; +void Screenshot::mousePressEvent(QMouseEvent *e) +{ + + press_pos = e->globalPos(); + init_pos = pos(); + // printf("x=%d y=%d\n",press_pos.x(),press_pos.y()); +} + +void Screenshot::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::LeftButton) // Button state ) + { + move(init_pos + e->globalPos() - press_pos); + // move(x() + e->globalX() - press_pos.x(), y()+e->globalY()- + // press_pos.y());// coe->x(),e->y()); + // sg->move(x()-100,y()-100); + } +} + +extern bool flag_xcompmgr; +void Screenshot::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + // printf("Screenshot::paintEvent\n"); + // setAttribute(Qt::WA_NoSystemBackground); + if (0) // if(flag_xcompmgr)//setWindowOpacity (0.9); + // //_NET_WM_WINDOW_OPACITY + { + setAttribute(Qt::WA_OpaquePaintEvent); + // setAttribute(Qt::WA_); + // p.setOpacity(0.1); + p.setCompositionMode(QPainter::CompositionMode_Source); // need! + p.fillRect(rect(), Qt::transparent); // clear! + // QRect rectR(shotarea->x(),shotarea->y(), + // shotarea->width(),shotarea->height()); + // p.fillRect(rectR,QColor(0,255,0,10)); + // p.fillRect(rect(),QColor(255,255,255,255)); + } + + return; + QStyleOptionFrame opt; + QRect rectR(shotarea->x() - 1, shotarea->y() - 1, shotarea->width() + 2, + shotarea->height() + 2); + opt.rect = rectR; + opt.lineWidth = 2; + opt.midLineWidth = 1; + opt.state = opt.state | QStyle::State_Sunken; + /// style()->drawControl(QStyle::CE_Header, &opt, p, this); + style()->drawPrimitive(QStyle::PE_Frame, &opt, &p, 0); +} + +void Screenshot::resizeEvent(QResizeEvent *e /* event */) +{ + + // printf("Screenshot::resizeEvent()\n"); + // QWidget::resizeEvent(e); + // return; + if (flag_xcompmgr == false) + { + QPixmap pixmap = QPixmap(size()); + QPainter painter(&pixmap); + // painter.eraseRect(0,0,width(),height()); + painter.fillRect(0, 0, width(), height(), QColor(0, 0, 0)); + painter.fillRect(shotarea->x() + 1, shotarea->y() + 1, + shotarea->width() - 2, shotarea->height() - 2 + // painter.fillRect(shotarea->geometry() + , + QColor(255, 255, 255)); +#if QT_VERSION >= 0x040300 + setMask( + pixmap.createMaskFromColor(QColor(255, 255, 255), Qt::MaskInColor)); +// setMask(pixmap.mask()); // with alpha channel ,notwork +#else +// #error Qt library version 4.2 or higher is needed for this version of +// qps +#endif + } + + // QSize scaledSize = originalPixmap.size(); + // scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio); + // if (!screenshotLabel->pixmap()|| scaledSize != + // screenshotLabel->pixmap()->size()) + // updateScreenshotLabel(); +} + +void Screenshot::newScreenshot() +{ + // newScreenshotButton->setDisabled(true); + // shootScreen(); + // QTimer::singleShot(delaySpinBox->value() * 1000, this, + // SLOT(shootScreen())); +} + +void Screenshot::saveScreenshot() +{ + shootScreen(); + QString format = "png"; + QString path = QDir::homePath() + "/Desktop"; // *** ooooooo + if (QFile::exists(path)) + path = path + tr("/untitled.") + format; + else + path = QDir::homePath(); + + QString fileName = QFileDialog::getSaveFileName( + this, tr("Save As"), path, + tr("%1 Files (*.%2);;All Files (*)").arg(format.toUpper()).arg(format)); + if (!fileName.isEmpty()) + originalPixmap.save(fileName, format.toLatin1()); + + // setWindowFlags(Qt::X11BypassWindowManagerHint); +} + +void Screenshot::shootScreen() +{ + + // if (delaySpinBox->value() != 0) qApp->beep(); + originalPixmap = QPixmap::grabWindow( + QApplication::desktop()->winId(), geometry().x() + shotarea->x(), + geometry().y() + shotarea->y(), shotarea->width(), shotarea->height()); + /// updateScreenshotLabel(); +} + +void Screenshot::updateCheckBox() +{ + if (delaySpinBox->value() == 0) + hideThisWindowCheckBox->setDisabled(true); + else + hideThisWindowCheckBox->setDisabled(false); +} + +void Screenshot::createOptionsGroupBox() +{ + optionsGroupBox = new QGroupBox(tr("Options")); + + delaySpinBox = new QSpinBox; + delaySpinBox->setSuffix(tr(" s")); + delaySpinBox->setMaximum(60); + connect(delaySpinBox, SIGNAL(valueChanged(int)), this, + SLOT(updateCheckBox())); + + delaySpinBoxLabel = new QLabel(tr("Screenshot Delay:")); + + hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window")); + + optionsGroupBoxLayout = new QGridLayout; + optionsGroupBoxLayout->addWidget(delaySpinBoxLabel, 0, 0); + optionsGroupBoxLayout->addWidget(delaySpinBox, 0, 1); + optionsGroupBoxLayout->addWidget(hideThisWindowCheckBox, 1, 0, 1, 2); + optionsGroupBox->setLayout(optionsGroupBoxLayout); +} + +void Screenshot::createButtonsLayout() +{ + newScreenshotButton = + createButton(tr("New Screenshot"), this, SLOT(newScreenshot())); + quitScreenshotButton = createButton(tr("Quit"), this, SLOT(close())); + + buttonsLayout = new QHBoxLayout; + buttonsLayout->addStretch(); + buttonsLayout->addWidget(newScreenshotButton); + buttonsLayout->addWidget(saveScreenshotButton); + buttonsLayout->addWidget(quitScreenshotButton); +} + +QPushButton *Screenshot::createButton(const QString &text, QWidget *receiver, + const char *member) +{ + QPushButton *button = new QPushButton(text); + button->connect(button, SIGNAL(clicked()), receiver, member); + return button; +} + +// preview +void Screenshot::updateScreenshotLabel() +{ + screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(), + Qt::KeepAspectRatio, + Qt::SmoothTransformation)); +} + +//#include "widget.h" + +#include +#include + +int screenshot_main(int argc, char **argv) +// int screenshot_main() +{ + // qWarning("Please make sure you're running a composition manager!"); + bool argbVisual = false; + Display *dpy = QX11Info::display(); // open default display + if (!dpy) + { + qWarning("Cannot connect to the X server"); + exit(1); + } + + int screen = DefaultScreen(dpy); + Colormap colormap = 0; + Visual *visual = 0; + int eventBase, errorBase; + + if (0 and XRenderQueryExtension(dpy, &eventBase, &errorBase)) + { + int nvi; + XVisualInfo templ; + templ.screen = screen; + templ.depth = 32; + templ.c_class = TrueColor; + XVisualInfo *xvi = XGetVisualInfo( + dpy, VisualScreenMask | VisualDepthMask | VisualClassMask, &templ, + &nvi); + + printf("nvi=%d\n", nvi); + for (int i = 0; i < nvi; ++i) + { + XRenderPictFormat *format = + XRenderFindVisualFormat(dpy, xvi[i].visual); + if (format->type == PictTypeDirect && format->direct.alphaMask > 0) + { + visual = xvi[i].visual; + colormap = XCreateColormap(dpy, RootWindow(dpy, screen), visual, + AllocNone); + argbVisual = true; + break; + } + } + } + if (argbVisual == true) + { + qWarning("Found ARGB visual. Starting app..."); + // QApplication app(dpy, argc, argv, Qt::HANDLE(visual), + // Qt::HANDLE(colormap)); + } + else + { + // qWarning("Couldn't find ARGB visual... Exiting."); + // QApplication app(dpy, argc, argv); + } + + // QApplication app(dpy, argc, argv, Qt::HANDLE(visual), + // Qt::HANDLE(colormap)); + QApplication app(argc, argv); + + Display *dsp = QX11Info::display(); // get the display(X server?) + + Screenshot *w = new Screenshot(); + w->show(); + // Widget w; + // w.resize(400, 300); + // w.show(); + + return app.exec(); +} diff --git a/src/screenshot.h b/src/screenshot.h new file mode 100644 index 0000000..c135ceb --- /dev/null +++ b/src/screenshot.h @@ -0,0 +1,110 @@ +#ifndef SCREENSHOT_H +#define SCREENSHOT_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TitleBar : public QWidget +{ + Q_OBJECT + public: + TitleBar(QWidget *w); + + protected: + // void resizeEvent(QResizeEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *e); + private slots: + private: + QPoint press_pos; +}; + +class ShotArea : public QFrame +{ + Q_OBJECT + public: + ShotArea(); + + protected: + // void resizeEvent(QResizeEvent *event); + + virtual void resizeEvent(QResizeEvent *e); + virtual void paintEvent(QPaintEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *e); + private slots: + private: + QPoint press_pos; +}; + +/* +class SizeGrip : public QWidget +{ + Q_OBJECT +public: + SizeGrip(); +protected: +// void resizeEvent(QResizeEvent *event); + void mouseMoveEvent ( QMouseEvent * event ); + void mousePressEvent(QMouseEvent *e); +private slots: +private: + QPoint press_pos; +}; +*/ + +class Screenshot : public QWidget +{ + Q_OBJECT + + public: + Screenshot(QWidget *p = 0); + + protected: + void resizeEvent(QResizeEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *e); + virtual void paintEvent(QPaintEvent *event); + + private slots: + void newScreenshot(); + void saveScreenshot(); + void shootScreen(); + void updateCheckBox(); + + private: + void createOptionsGroupBox(); + void createButtonsLayout(); + QPushButton *createButton(const QString &text, QWidget *receiver, + const char *member); + void updateScreenshotLabel(); + + QPoint press_pos; + QPixmap originalPixmap; + + QLabel *screenshotLabel; + QGroupBox *optionsGroupBox; + QSpinBox *delaySpinBox; + QLabel *delaySpinBoxLabel; + QCheckBox *hideThisWindowCheckBox; + QPushButton *newScreenshotButton; + QPushButton *saveScreenshotButton; + QPushButton *quitScreenshotButton; + + QVBoxLayout *mainLayout; + QGridLayout *optionsGroupBoxLayout; + QHBoxLayout *buttonsLayout; + ShotArea *shotarea; +}; + +#endif diff --git a/src/stable.h b/src/stable.h new file mode 100644 index 0000000..c6a4b05 --- /dev/null +++ b/src/stable.h @@ -0,0 +1,88 @@ +// for Precompiled Header ! +#include +#include +#include +#include + +#if defined __cplusplus + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "svec.h" +#endif diff --git a/src/svec.cpp b/src/svec.cpp new file mode 100644 index 0000000..d738cc6 --- /dev/null +++ b/src/svec.cpp @@ -0,0 +1,145 @@ +// svec.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +// implement a stretchy vector class: +// An Svec grows automatically (doubles when full), so that adding +// elements to the end has an amortized cost of O(1). +// For now, only use this for types not requiring a con/destructor. + +#ifndef SVEC_C +#define SVEC_C + +#include "svec.h" +////#define fatal(str) { printf("fatal error: %s\n",str); exit(0);} +#define fatal printf + +template Svec::Svec(const Svec &s) +{ + int n = s.size(); + if (n < 8) + n = 8; + vect = (T *)malloc(n * sizeof(T)); + alloced = n; + used = s.size(); + for (int i = 0; i < used; i++) + vect[i] = s[i]; +} + +template Svec &Svec::operator=(const Svec &s) +{ + if (this != &s) + { + if (alloced < s.size()) + { + alloced = s.size(); + vect = (T *)realloc(vect, alloced * sizeof(T)); + } + for (int i = 0; i < s.size(); i++) + { + vect[i] = s.vect[i]; + } + used = s.size(); + } + return *this; +} + +/* +template +void Svec::indexerr(int index) const +{ + fatal("Svec: index out of range (%d, valid is 0..%d)", index, used - 1); +} +*/ + +template void Svec::setSize(int newsize) +{ + while (newsize > alloced) + grow(); + used = newsize; +} + +template void Svec::setextend(int index, T value) +{ +#ifdef CHECK_INDICES + if (index < 0) + fatal("const Svec: negative index"); +#endif + while (index >= alloced) + grow(); + if (index >= used) + used = index + 1; + vect[index] = value; +} + +template void Svec::insert(int index, T value) +{ +#ifdef CHECK_INDICES + if (index < 0 || index > used) + fatal("Svec: index out of range"); +#endif + if ((used + 1) > alloced) + grow(); + + /*int i; + T v; + v=vect[i+1]; + for(i = index; j < =used ; i++) + vect[i+1]=v; + vect[i+1]=vect[i]; //vect[index+1]=vect[index]; + */ + + // old + for (int j = used - 1; j >= index; j--) + vect[j + 1] = vect[j]; + + vect[index] = value; + + used++; +} + +// for int,float type , no delete +template void Svec::remove(int index) +{ +#ifdef CHECK_INDICES + if (index < 0 || index >= used) + fatal("Svec: index out of range"); +#endif + for (int j = index; j < used - 1; j++) + vect[j] = vect[j + 1]; + used--; +} + +// for class type +template void Svec::Delete(int index) +{ +#ifdef CHECK_INDICES + if (index < 0 || index >= used) + fatal("Svec: index out of range"); +#endif + delete vect[index]; + for (int j = index; j < used - 1; j++) + vect[j] = vect[j + 1]; + used--; +} + +// Assuming T is "pointer to U", delete all contents. +// Warning: duplicated objects will be deleted twice --- doubleplusungood +template void Svec::purge() +{ + for (int i = 0; i < used; i++) + delete vect[i]; + used = 0; +} + +extern "C" { +typedef int (*compare_func)(const void *, const void *); +} + +template void Svec::sort(int (*compare)(const T *a, const T *b)) +{ + qsort(vect, used, sizeof(T), (compare_func)compare); +} + +#endif // SVEC_C diff --git a/src/svec.h b/src/svec.h new file mode 100644 index 0000000..948ec9e --- /dev/null +++ b/src/svec.h @@ -0,0 +1,138 @@ +// svec.h emacs, this is a -*-c++-*- file +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef SVEC_H +#define SVEC_H + +#include +#include + +template class SHash : public std::map +{ + public: + T value(Key k, T r) + { + using namespace std; + typename map::const_iterator it; + it = map::find(k); + if (it == map::end()) + return r; + return (*it).second; + } + void insert(Key k, T r) + { + using namespace std; + map::insert(pair(k, r)); + } + bool contains(Key k) + { + if (value(k, NULL) == NULL) + return false; + else + return true; + } +}; + +#include + +//#ifndef NDEBUG +//#define CHECK_INDICES // Check invalid vector indices (the default) +//#endif + +template class Svec +{ + public: + Svec(int max = 16); + Svec(const Svec &s); + ~Svec(); + + Svec &operator=(const Svec &s); + int size() const; + void setSize(int newsize); + T &operator[](int i); + T operator[](int i) const; + void set(int i, T val); + void sort(int (*compare)(const T *a, const T *b)); + void add(T x); + void insert(int index, T val); + void remove(int index); + void Delete(int index); + void clear(); + void purge(); // like clear() but deletes all contents + + private: + void grow(); + void setextend(int index, T value); + void indexerr(int index) const; + + T *vect; + int alloced; // # of entries allocated + int used; // # of entries actually used (size) +}; + +template inline Svec::Svec(int max) : alloced(max), used(0) +{ + vect = (T *)malloc(max * sizeof(T)); +} + +template inline Svec::~Svec() { free(vect); } + +template inline int Svec::size() const { return used; } + +template inline T &Svec::operator[](int i) +{ +#ifdef CHECK_INDICES + if (i < 0 || i >= used) + indexerr(i); +#endif + return vect[i]; +} + +template inline T Svec::operator[](int i) const +{ +#ifdef CHECK_INDICES + if (i < 0 || i >= used) + indexerr(i); +#endif + return vect[i]; +} + +template inline void Svec::set(int i, T val) +{ + if (i < 0 || i >= used) + setextend(i, val); + else + vect[i] = val; +} + +template inline void Svec::add(T x) +{ + if (++used > alloced) + grow(); + vect[used - 1] = x; +} + +/* +template +inline void Svec::add(T x) +{ + if(++used > alloced) + { + + } + + vect[used - 1] = x; +} +*/ + +template inline void Svec::clear() { used = 0; } + +template inline void Svec::grow() +{ + // printf("size=%d\n",sizeof(T)); + vect = (T *)realloc(vect, (alloced *= 2) * sizeof(T)); +} + +#endif // SVEC_H diff --git a/src/translations/qps.ts b/src/translations/qps.ts new file mode 100644 index 0000000..8c61fac --- /dev/null +++ b/src/translations/qps.ts @@ -0,0 +1,170 @@ + + + + + ControlBar + + + Pause (Ctrl+Space) + + + + + EventDialog + + + Watchdog 0.1 alpha + + + + + Eventcat + + + + + Select condition + + + + + labelDescrition + + + + + Enable + + + + + process name + + + + + cpu + + + + + % + + + + + include already running process + + + + + run command + + + + + show Message + + + + + Help (Not yet. just concept) + + + + + %p : pid +%c : command + + + + + New + + + + + Add + + + + + Delete + + + + + Close + + + + + ExecWindow + + + Qps + + + + + Ok + + + + + Qps + + + Detail + + + + + Screenshot + + + /untitled. + + + + + Save As + + + + + %1 Files (*.%2);;All Files (*) + + + + + Options + + + + + s + + + + + Screenshot Delay: + + + + + Hide This Window + + + + + New Screenshot + + + + + Quit + + + + diff --git a/src/trayicon.cpp b/src/trayicon.cpp new file mode 100644 index 0000000..ce3c441 --- /dev/null +++ b/src/trayicon.cpp @@ -0,0 +1,240 @@ +/* + really sucking codes.. sorry.. + from psi , modified and cleand by fasthyun@magicn.com + trayicon_x11.cpp - X11 trayicon (for use with KDE and GNOME) + Copyright (C) 2003 Justin Karneges + GNOME2 Notification Area support: Tomasz Sterna + + */ + +#include // *****should be First!!! +#include "global.h" // Qps *qps; +#include "trayicon.h" + +#include +#include +#include +#include + +// QIcon icon = iconComboBox->itemIcon(index); +// trayIcon->setIcon(icon); + +extern bool flag_session_start; +//---------------------------------------------------------------------------- +// common stuff +//---------------------------------------------------------------------------- +// for Gnome2 Notification Area +static int dock_xerror = 0; +extern bool flag_xcompmgr; + +#define SYSTEM_TRAY_REQUEST_DOCK 0 +#define SYSTEM_TRAY_BEGIN_MESSAGE 1 +#define SYSTEM_TRAY_CANCEL_MESSAGE 2 + +TrayIcon::TrayIcon(const QPixmap &icon, const QString &tooltip, QMenu *popup, + QWidget *parent, const char *name) + : QSystemTrayIcon(0 /* shoud be NULL!! */), pop(popup), pm(icon), + tip(tooltip) +{ + flag_show_tip = false; + flag_systray_ready = false; + inTray = false; + isWMDock = false; + + QSystemTrayIcon::setIcon(icon); + QSystemTrayIcon::setContextMenu(popup); + + if (!pm.width() || !pm.height()) + pm = QPixmap(45, 45); +} + +/*! + Removes the icon from the system tray and frees all allocated resources. +*/ +TrayIcon::~TrayIcon() { sysRemove(); } + +/*! + Sets the context menu to \a popup. The context menu will pop up when the + user clicks the system tray entry with the right mouse button. +*/ +void TrayIcon::setPopup(QMenu *popup) { pop = popup; } + +/*! + Returns the current popup menu. +*/ +QMenu *TrayIcon::popup() const { return pop; } + +QPixmap TrayIcon::icon() const { return pm; } + +//---------------------------------------------------------------------------- +// TrayIcon +//---------------------------------------------------------------------------- +// DRAFT Code (by fasthyun@magicn.com) +void TrayIcon::init_TrayIconFreeDesktop() {} + +void TrayIcon::init_WindowMakerDock() {} + +void TrayIcon::sysInstall() +{ + /// boolSysTray=hasSysTray(); + + if (hasSysTray() == true) + { + show(); + } + /* + if(hasSysTray()==false) + { + setFixedSize(46,46); // setIconSize + init_WindowMakerDock(); + setSysTray(true); + } */ +} + +bool TrayIcon::hasSysTray() { return QSystemTrayIcon::isSystemTrayAvailable(); } + +void TrayIcon::sysRemove() +{ + // printf("Qps: sysRemove\n"); + inTray = false; + /// boolSysTray=isWMDock=false; + hide(); +} + +/*! + update systray icon + called by qps::refresh() +*/ +void TrayIcon::setIcon(const QPixmap &pix) +{ + QSystemTrayIcon::setIcon(pix); + if (isVisible() == false) + return; + pm = pix; +} + +/*! + \property TrayIcon::toolTip + \brief the tooltip for the system tray entry + + On some systems, the tooltip's length is limited and will be truncated as + necessary. +*/ +void TrayIcon::setToolTip(const QString &tooltip) +{ + if (pop->isVisible()) + flag_show_tip = false; + if (flag_show_tip) + QToolTip::showText(tip_pos, tooltip); + else + QToolTip::hideText(); + tip = tooltip; +} + +void TrayIcon::leaveEvent(QEvent *) { flag_show_tip = false; } + +void TrayIcon::mouseMoveEvent(QMouseEvent *e) +{ + // printf("move\n"); + // QToolTip::showText(QPoint(0,0),"" ); + // QToolTip::showText(e->globalPos(),tip ); + tip_pos = e->globalPos(); + flag_show_tip = true; + QToolTip::showText(tip_pos, tip); + e->accept(); +} + +void TrayIcon::mousePressEvent(QMouseEvent *e) +{ + // test_xapp(); + // This is for X11, menus appear on mouse press + // I'm not sure whether Mac should be here or below.. Somebody check? + switch (e->button()) + { + case Qt::RightButton: + if (pop) + { + pop->popup(e->globalPos()); + e->accept(); + } + break; + case Qt::LeftButton: + case Qt::MidButton: + // emit clicked( e->globalPos(), e->button() ); + emit clicked(e->globalPos()); + default: + break; + } + e->ignore(); +} + +void TrayIcon::mouseReleaseEvent(QMouseEvent *e) +{ + // printf("mouseReleaseEvent\n"); + switch (e->button()) + { + case Qt::RightButton: + if (pop) + { + // Necessary to make keyboard focus + // and menu closing work on Windows. + // e->accept(); + } + break; + case Qt::LeftButton: + case Qt::MidButton: + // emit clicked( e->globalPos(), e->button() ); + // emit clicked( e->globalPos()); + break; + default: + break; + } + e->ignore(); +} + +void TrayIcon::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) + emit doubleClicked(e->globalPos()); + e->accept(); +} + +// this will be called before shown !! +void TrayIcon::paintEvent(QPaintEvent *) +{ + /* + //printf("paintEvent()\n"); + QPainter p(this); + int w,h; + if(isVisible()==false) // ** important to prevent X11 Error !! + { + // printf("paintEvent(): hidden\n"); + return; + } + w=width()/2 ; + h=height()/2; + p.drawPixmap(w - pm.width()/2, h - pm.height()/2, pm); */ +} + +// for session logout ?? never called +void TrayIcon::closeEvent(QCloseEvent *e) +{ + printf("TrayIcon::closeEvent()\n"); + e->accept(); +} + +// called after size changed +void TrayIcon::resizeEvent(QResizeEvent *e) +{ + int w, h; + // printf("TrayIcon::resizeEvent(): w=%d,h=%d\n",width(),height()); + // if(isVisible()==false) return; // X11 error !! + /// w=width();h=height(); + if (isWMDock) + { + /// w=w-14;h=h-14; + } + // qps->setIconSize(w,h); +} + +void QPS_SHOW(); diff --git a/src/trayicon.h b/src/trayicon.h new file mode 100644 index 0000000..d8cb7fe --- /dev/null +++ b/src/trayicon.h @@ -0,0 +1,98 @@ +/* + * trayicon.h - system-independent trayicon class (adapted from Qt example) + * Copyright (C) 2003 Justin Karneges + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef CS_TRAYICON_H +#define CS_TRAYICON_H + +#include +#include + +#include + +//---------------------------------------------------------------------------- +// TrayIconPrivate +//---------------------------------------------------------------------------- + +class TrayIcon : public QSystemTrayIcon +{ + Q_OBJECT + public: + TrayIcon(const QPixmap &, const QString &, QMenu *popup = 0, + QWidget *parent = 0, const char *name = 0); + ~TrayIcon(); + + // use WindowMaker dock mode. ignored on non-X11 platforms + void setWMDock(bool use) { isWMDock = use; } + bool checkWMDock() { return isWMDock; } + bool hasSysTray(); + void setSysTray(bool val) + { // boolSysTray=val; + } + + // Set a popup menu to handle RMB + void setPopup(QMenu *); + QMenu *popup() const; + + QPixmap icon() const; + + void sysInstall(); + void sysRemove(); + + void init_TrayIconFreeDesktop(); + void init_WindowMakerDock(); + + public slots: + void setIcon(const QPixmap &icon); + void setToolTip(const QString &tip); +signals: + void clicked(const QPoint &); + void clicked(const QPoint &, int); + void doubleClicked(const QPoint &); + void closed(); + + protected: + // bool event( QEvent *e ); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void leaveEvent(QEvent *); + + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *e); + // void hideEvent ( QHideEvent *e ); // called after hide() + void closeEvent(QCloseEvent *e); + + private: + QMenu *pop; + QPixmap pm; + QString tip; + QPoint tip_pos; + + bool isWMDock; + // bool boolSysTray; //DEL? + bool inTray; + bool flag_systray_ready; + bool flag_show_tip; + + // DEL void sysUpdateToolTip(); +}; + +#endif // CS_TRAYICON_H diff --git a/src/ttystr.cpp b/src/ttystr.cpp new file mode 100644 index 0000000..ae86919 --- /dev/null +++ b/src/ttystr.cpp @@ -0,0 +1,211 @@ +// ttystr.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include +#include +#include +#include //major() minor() +#include +#include +#include +#include +#include +#include "ttystr.h" + +#ifdef LINUX + +// Table of various tty devices (mostly serial boards) + +struct Ttytab +{ + short dev_maj; // major number of tty + char prefix; + char callout; // 1 if callout device, 0 if tty +}; + +static Ttytab ttytab[] = {{19, 'C', 0}, // Cyclades + {20, 'b', 1}, + {22, 'D', 0}, // Digiboard + {23, 'd', 1}, + {24, 'E', 0}, // Stallion + {25, 'e', 1}, + {32, 'X', 0}, // Specialix + {33, 'x', 1}, + {46, 'R', 0}, // Comtrol Rocketport + {47, 'r', 1}, + {48, 'L', 0}, // SDL RISCom + {49, 'l', 1}, + {57, 'P', 0}, // Hayes ESP + {58, 'p', 1}, + {71, 'F', 0}, // Computone IntelliPort II + {72, 'f', 1}, + {75, 'W', 0}, // Specialix IO8+ + {76, 'w', 1}, + {78, 'M', 0}, // PAM Software's multimodem boards + {79, 'm', 1}, + {105, 'V', 0}, // Comtrol VS-1000 + {106, 'v', 1}, + {0, 0, 0}}; + +// Return tty name of a device. Remove leading "tty" if any. +// Return "-" if devnr is zero, "?" if unknown. +QString Ttystr::name(dev_t devnr) +{ + if (devnr == 0) + return "-"; + + int dmin = minor(devnr); + int dmaj = major(devnr); + char name[40]; + switch (dmaj) + { + case 3: + // BSD-style pty slaves + name[0] = "pqrstuvwxyzabcde"[(dmin >> 4) & 0xf]; + name[1] = "0123456789abcdef"[dmin & 0xf]; + name[2] = '\0'; + break; + + case 4: + if (dmin < 64) + { + // virtual console 0..63 + sprintf(name, "%d", dmin); + } + else if (dmin < 128) + { + // serial port 0..63 + sprintf(name, "S%d", dmin - 64); + } + else if (dmin >= 192 && dmin < 256) + { + // obsolete tty devices + name[0] = "pqrs"[(dmin >> 4) & 0x3]; + name[1] = "0123456789abcdef"[dmin & 0xf]; + name[2] = '\0'; + } + else + strcpy(name, "?"); + break; + + case 5: + // alternate tty devices + switch (dmin) + { + case 0: + strcpy(name, "tty"); + break; + + case 1: + strcpy(name, "console"); + break; + + case 2: + strcpy(name, "ptmx"); + break; + + default: + if (dmin >= 64 && dmin < 128) + { + // callout devices 0..63 + sprintf(name, "cua%d", dmin - 64); + } + else + strcpy(name, "?"); + } + + default: + if (dmaj >= 136 && dmaj < 144) + { + // Unix98 pty slaves + sprintf(name, "pts/%d", ((dmaj - 136) << 8) + dmin); + break; + } + + // Various serial tty devices + for (int i = 0; ttytab[i].dev_maj; i++) + { + if (dmaj == ttytab[i].dev_maj) + { + sprintf(name, "%s%c%d", ttytab[i].callout ? "cu" : "", + ttytab[i].prefix, dmin); + break; + } + } + strcpy(name, "?"); + } + return name; +} + +#endif // LINUX + +#ifdef SOLARIS + +QHash Ttystr::dict; +bool Ttystr::scanned = FALSE; + +// return tty name, '-' if no tty, "??" if unidentifiable tty +QString Ttystr::name(dev_t devnr) +{ + if (!scanned) + { + read_devs(); + scanned = TRUE; // just scan /dev once + } + QString s; + if (devnr == (dev_t)-1) + s = "-"; + else + { + char *ts = dict.value(devnr, NULL); + + if (ts) + s = ts; + else + s = "??"; + } + return s; +} + +// scan device directory or subdirectory thereof +void Ttystr::scandevdir(const char *prefix) +{ + char dirname[80] = "/dev"; + strcat(dirname, prefix); + DIR *d = opendir(dirname); + if (!d) + return; + struct dirent *e; + struct stat sb; + char name[80]; + while ((e = readdir(d)) != 0) + { + sprintf(name, "/dev%s/%s", prefix, e->d_name); + if (stat(name, &sb) >= 0) + { + if (S_ISCHR(sb.st_mode)) + { + dev_t d = sb.st_rdev; + if (!dict.value(d, NULL)) + { + dict.insert(sb.st_rdev, strdup(name + 5)); // skip "/dev/" + } + } + } + } + closedir(d); +} + +void Ttystr::read_devs() +{ + // scan /dev and various subdirectories. In case of duplicates, pick + // the first name encountered + scandevdir("/term"); + scandevdir(""); + scandevdir("/pts"); + scandevdir("/cua"); +} + +#endif // SOLARIS diff --git a/src/ttystr.h b/src/ttystr.h new file mode 100644 index 0000000..45d8189 --- /dev/null +++ b/src/ttystr.h @@ -0,0 +1,35 @@ +// ttystr.h +// +// This program is free software. See the file COPYING for details. +// Author: Mattias EngdegÃ¥rd, 1997-1999 + +#ifndef TTYSTR_H +#define TTYSTR_H + +#include "config.h" +#include + +#ifdef LINUX +#include +#endif + +#ifdef SOLARIS +#include +#endif + +class Ttystr +{ + public: + static QString name(dev_t devnr); + +#ifdef SOLARIS + private: + static void read_devs(); + static void scandevdir(const char *prefix); + + static QHash dict; + static bool scanned; +#endif +}; + +#endif // TTYSTR_H diff --git a/src/uidstr.cpp b/src/uidstr.cpp new file mode 100644 index 0000000..01b49aa --- /dev/null +++ b/src/uidstr.cpp @@ -0,0 +1,61 @@ +// uidstr.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include +#include +#include +#include +#include +#include + +#include "uidstr.h" + +QHash Uidstr::udict; +QHash Uidstr::gdict; + +// return user name (possibly numeric) +QString Uidstr::userName(int uid) +{ + char *p = udict.value(uid, NULL); + if (!p) + { + struct passwd *pw = getpwuid(uid); + if (!pw) + { + p = (char *)malloc(11); + sprintf(p, "%d", uid); + } + else + p = strdup(pw->pw_name); + udict.insert(uid, p); + // if(udict.count() > udict.size() * 3) + // udict.resize(udict.count()); + } + QString s(p); + return s; +} + +// return group name (possibly numeric) + +QString Uidstr::groupName(int gid) +{ + char *p = gdict[gid]; + if (!p) + { + struct group *gr = getgrgid(gid); + if (!gr) + { + p = (char *)malloc(11); + sprintf(p, "%d", gid); + } + else + p = strdup(gr->gr_name); + gdict.insert(gid, p); + // if(gdict.count() > gdict.size() * 3) + // gdict.resize(gdict.count()); + } + QString s(p); + return s; +} diff --git a/src/uidstr.h b/src/uidstr.h new file mode 100644 index 0000000..7773491 --- /dev/null +++ b/src/uidstr.h @@ -0,0 +1,23 @@ +// uidstr.h +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef UIDSTR_H +#define UIDSTR_H + +#include +#include + +class Uidstr +{ + public: + static QString userName(int uid); + static QString groupName(int gid); + + private: + static QHash udict; + static QHash gdict; +}; + +#endif // UIDSTR_H diff --git a/src/watchdog.cpp b/src/watchdog.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/watchdog.h b/src/watchdog.h new file mode 100644 index 0000000..e69de29 diff --git a/src/watchdog.ui b/src/watchdog.ui new file mode 100644 index 0000000..00583a0 --- /dev/null +++ b/src/watchdog.ui @@ -0,0 +1,458 @@ + + + EventDialog + + + + 0 + 0 + 680 + 533 + + + + + 0 + 0 + + + + Qt::WheelFocus + + + Watchdog 0.1 alpha + + + QPushButtonX { + color: rgb(244, 244, 244); + border-image: url(:/icon/vista.png); +} +QWidget { +} +QFrame, QLabel, QToolTip { + } + + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 8 + 1 + + + + + 16777215 + 51 + + + + + + + + true + + + + 0 + 0 + + + + Qt::NoFocus + + + Qt::NoContextMenu + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + false + + + Qt::NoPen + + + + + + + + 0 + 0 + + + + Eventcat + + + + 6 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + Select condition + + + + + + + + labelDescrition + + + + + + + Qt::Horizontal + + + + 36 + 20 + + + + + + + + false + + + Enable + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + process name + + + + + + + + + + cpu + + + + + + + + + + + + + + + + + + + % + + + + + + 90 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + + + include already running process + + + true + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + run command + + + + + + + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + show Message + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + Help (Not yet. just concept) + + + + 4 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + %p : pid +%c : command + + + + + + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + New + + + + + + + Add + + + + + + + Delete + + + + + + + Close + + + + + + + + + + + + diff --git a/src/wchan.cpp b/src/wchan.cpp new file mode 100644 index 0000000..ae7d227 --- /dev/null +++ b/src/wchan.cpp @@ -0,0 +1,185 @@ +// wchan.C +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "config.h" + +#include "wchan.h" + +#ifdef LINUX +QHash Wchan::dict; +char *Wchan::sysmap = 0; +bool Wchan::sysmap_inited = false; +int Wchan::sysmap_size = 0; + +// return malloc:ed hex representation of x +static char *hexstr(unsigned long x) +{ + char *p = (char *)malloc(sizeof(long) * 2 + 1); + sprintf(p, sizeof(long) == 8 ? "%016lx" : "%08lx", x); + return p; +} +#endif + +// called by ???? +// return wchan symbol (possibly numeric, and empty string if addr=0) + +QString Wchan::name(unsigned long addr) +{ +#ifdef LINUX + return ""; + +#endif + +#ifdef SOLARIS + char buf[sizeof(long) * 2 + 1]; + sprintf(buf, sizeof(long) == 8 ? "%016lx" : "%08lx", addr); + return QString(buf); +#endif +} + +#ifdef LINUX + +// return true if open succeeds +bool Wchan::open_sysmap() +{ + // common places to look for a valid System.map + static const char *paths[] = { + "/boot/System.map-%s", "/boot/System.map", "/lib/modules/%s/System.map", + "/usr/src/linux-%s/System.map", "/usr/src/linux/System.map", + "/usr/local/src/linux-%s/System.map", "/usr/local/src/linux/System.map", + 0}; + sysmap_inited = true; // don't try again + for (const char **p = paths; *p; p++) + { + char buf[80]; + struct utsname ub; + uname(&ub); + int major, minor, lvl; + if (sscanf(ub.release, "%d.%d.%d", &major, &minor, &lvl) != 3) + major = -1; // non-standard release, silently accept it + sprintf(buf, *p, ub.release); + if (try_sysmap(buf)) + { // try_sysmap + if (major >= 0) + { + char vstr[40]; + sprintf(vstr, "Version_%d", (major << 16) + (minor << 8) + lvl); + // map a zero page at the end to terminate + // string + int ps = getpagesize(); + + mmap(sysmap + ((sysmap_size + ps - 1) & ~(ps - 1)), ps, + PROT_READ, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if (!strstr(sysmap, vstr)) + { + fprintf(stderr, "qps warning: %s does " + "not match current " + "kernel\n", + buf); + munmap(sysmap, sysmap_size + ps); + sysmap = 0; + continue; // search the list for a + // better file + } + } + return true; + } + } + return false; +} + +// try mapping System.map from path, return true if success +bool Wchan::try_sysmap(const char *path) +{ + int fd = open(path, O_RDONLY); + struct stat sbuf; + if (fd >= 0) + { + if (fstat(fd, &sbuf) == 0) + { + sysmap_size = sbuf.st_size; + // make room for a zero page after the sysmap + sysmap = (char *)mmap(0, sysmap_size + getpagesize(), PROT_READ, + MAP_SHARED, fd, 0); + close(fd); + if (sysmap != (char *)-1) + return true; + sysmap = 0; + return false; + } + close(fd); + } + return false; +} + +inline int Wchan::beginning_of_line(int ofs) +{ + // seek backwards to beginning of line + while (ofs >= 0 && sysmap[ofs] != '\n') + ofs--; + return ofs + 1; +} + +char *Wchan::find_sym(unsigned long addr) +{ + // use binary search to find symbol; return malloced string + int l = 0, r = sysmap_size; + for (;;) + { + unsigned long a; + char buf[80]; + int m = (l + r) / 2; + m = beginning_of_line(m); + if (m == l) + { + // see if there is a line further down + while (m < r - 1 && sysmap[m] != '\n') + m++; + if (m < r - 1) + { + m++; + } + else + { + if (r == sysmap_size) + { + // after last item, probably in a + // module. give hex addr + return hexstr(addr); + } + m = l; + sscanf(sysmap + m, "%lx %*c %s", &a, buf); + // strip leading sys_ or do_ to reduce field + // width + char *p = buf; + if (strncmp(buf, "do_", 3) == 0) + p += 3; + if (strncmp(buf, "sys_", 4) == 0) + p += 4; + return strdup(p); + } + } + sscanf(sysmap + m, "%lx %*c %s", &a, buf); + if (addr < a) + { + r = m; + } + else + { + l = m; + } + } +} + +#endif // LINUX diff --git a/src/wchan.h b/src/wchan.h new file mode 100644 index 0000000..ad9058f --- /dev/null +++ b/src/wchan.h @@ -0,0 +1,31 @@ +// wchan.h +// +// This program is free software. See the file COPYING for details. +// Author: Mattias Engdegård, 1997-1999 + +#ifndef WCHAN_H +#define WCHAN_H + +#include +#include + +class Wchan +{ + public: + static QString name(unsigned long addr); + +#ifdef LINUX + private: + static bool open_sysmap(); + static bool try_sysmap(const char *path); + static char *find_sym(unsigned long addr); + static inline int beginning_of_line(int ofs); + + static QHash dict; + static char *sysmap; // pointer to mmap()ed System.map + static bool sysmap_inited; // if an attempt to read sysmap has been made + static int sysmap_size; +#endif +}; + +#endif // WCHAN_H