process_viewer-0.2.6/.gitignore010066400017500001750000000000311353320622000147570ustar0000000000000000*target **.dll **-config process_viewer-0.2.6/.travis.yml010066400017500001750000000023321353320622000151060ustar0000000000000000dist: xenial language: rust matrix: include: - os: linux env: TARGET=armv7-unknown-linux-gnueabihf rust: stable - os: linux env: TARGET=x86_64-apple-darwin rust: stable - os: linux env: TARGET=armv7-unknown-linux-gnueabihf rust: nightly - os: linux env: TARGET=x86_64-apple-darwin rust: nightly - os: osx rust: stable - os: osx rust: nightly sudo: true -env: global: - LD_LIBRARY_PATH=/usr/local/lib addons: apt: packages: - libgtk-3-dev - libmount-dev before_install: - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew unlink python; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install gtk+3 cairo atk; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:/opt/X11/lib/pkgconfig:/usr/local/opt/libffi/lib/pkgconfig; fi script: - rustc --version - if [ "$TRAVIS_RUST_VERSION" == "nightly" ]; then rustup component add clippy-preview || touch cargo_failed; fi - RUST_BACKTRACE=1 cargo build - if [ "$TRAVIS_RUST_VERSION" == "nightly" ] && [ ! -f "cargo_failed" ]; then cargo clippy; fi process_viewer-0.2.6/Cargo.toml.orig010066400017500001750000000011521353375016100156730ustar0000000000000000[package] name = "process_viewer" version = "0.2.6" authors = ["Guillaume Gomez "] description = "A process viewer GUI" repository = "https://github.com/GuillaumeGomez/process-viewer" license = "MIT" homepage = "https://github.com/GuillaumeGomez/process-viewer" readme = "README.md" keywords = ["GUI", "process", "viewer", "gtk"] [dependencies] cairo-rs = "0.7" gdk = "0.11" gdk-pixbuf = "0.7" gio = "0.7" glib = "0.8" gtk = "0.7" pango = "0.7" sysinfo = "0.9" libc = "0.2" serde = "1.0" serde_derive = "1.0" toml = "0.5" [[bin]] name = "process_viewer" path = "src/process_viewer.rs" process_viewer-0.2.6/Cargo.toml0000644000000026030000000000000121360ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "process_viewer" version = "0.2.6" authors = ["Guillaume Gomez "] description = "A process viewer GUI" homepage = "https://github.com/GuillaumeGomez/process-viewer" readme = "README.md" keywords = ["GUI", "process", "viewer", "gtk"] license = "MIT" repository = "https://github.com/GuillaumeGomez/process-viewer" [[bin]] name = "process_viewer" path = "src/process_viewer.rs" [dependencies.cairo-rs] version = "0.7" [dependencies.gdk] version = "0.11" [dependencies.gdk-pixbuf] version = "0.7" [dependencies.gio] version = "0.7" [dependencies.glib] version = "0.8" [dependencies.gtk] version = "0.7" [dependencies.libc] version = "0.2" [dependencies.pango] version = "0.7" [dependencies.serde] version = "1.0" [dependencies.serde_derive] version = "1.0" [dependencies.sysinfo] version = "0.9" [dependencies.toml] version = "0.5" process_viewer-0.2.6/LICENSE010066400017500001750000000020731353320622000140040ustar0000000000000000The MIT License (MIT) Copyright (c) 2015 Guillaume Gomez Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. process_viewer-0.2.6/README.md010066400017500001750000000033741353320622000142630ustar0000000000000000# process-viewer [![Build Status](https://travis-ci.org/GuillaumeGomez/process-viewer.png?branch=master)](https://travis-ci.org/GuillaumeGomez/process-viewer) A process viewer GUI in rust. It provides current status of your processes (cpu and memory usage) and your system (usage of every core and of your RAM, and the temperature of your components if this information is available). It can be run on the following platforms: * Linux * Raspberry * Mac OSX * Windows Please run it in release mode to have good performance: ```bash cargo run --release ``` or to install it as binary ```bash cargo install process_viewer ``` ### Building/running on Linux, MacOS and Ubuntu-based Distros Running ```process-viewer``` on Gnome-based Ubuntu (>=17.10) should work out of the box. For Debian, Ubuntu-derivatives, Fedora and MacOS refer to the [gtk-rs installation guide](http://gtk-rs.org/docs/requirements.html). ### Building/running on Windows You'll need to follow the [gtk-rs installation guide](http://gtk-rs.org/docs/requirements.html#windows). If you still have issues to run the generated binary, just copy the `.dll`s into the executable's folder. ### Running on Raspberry It'll be difficult to build on Raspberry pi directly. A good way-around is to be build on Linux before sending it to your Raspberry pi: ```bash rustup target add armv7-unknown-linux-gnueabihf cargo build --target=armv7-unknown-linux-gnueabihf ``` ## Donations If you appreciate my work and want to support me, you can do it here: [![Become a patron](https://c5.patreon.com/external/logo/become_a_patron_button.png)](https://www.patreon.com/GuillaumeGomez) ## Screenshots ![screenshot](http://guillaume-gomez.fr/image/screen3.png) ![screenshot](http://guillaume-gomez.fr/image/screen4.png) process_viewer-0.2.6/appveyor.yml010066400017500001750000000010301353320622000153570ustar0000000000000000environment: matrix: - RUST: stable BITS: 32 - RUST: stable BITS: 64 install: - IF "%BITS%" == "32" SET ARCH=i686 - IF "%BITS%" == "64" SET ARCH=x86_64 - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe --default-host "%ARCH%-pc-windows-gnu" --default-toolchain %RUST% -y - SET PATH=C:\Users\appveyor\.cargo\bin;C:\msys64\mingw%BITS%\bin;%PATH%;C:\msys64\usr\bin - rustc -Vv - cargo -Vv - pacman --noconfirm -S mingw-w64-%ARCH%-gtk3 build_script: - cargo build test: false process_viewer-0.2.6/assets/eye.png010066400017500001750000001325051353320622000155750ustar0000000000000000PNG  IHDRݾPIDATxtdKw-Z=[k4@ 0c| d z*2##Yzd=Egˊq;{C~+/?λ-C*q.4nkiEqVũcR\)&t o'PO\=S1N_/7M]U<M<}c~0Ctl6͆;]0A<GƆ~m|ǁuhf3]o6<~Nlq:%2q~N/7ey-NS1[h/i=s9H<뻴rqtC MzS^[Jtr KƏAPeH_7x-_\աQ`B\H"3IEƘo%Ert23K%8V0(_ O:X) uS1}o_E70:=oMpzv:b%"S];S`0*2]66X+U Smr#\dpL't3$ tuv]֕i_Tmږ2>NU4!iV*z55I·XMݵ=/[,4A19:B"ZEJCmlM͹8UG+0}|#QϘw ,::>SU =W(떌uX3iփ |dbhlJu|^]ZWBm\P9}Dpfu 2^EHuMj&a&I,-빼'v7#E&4,yF8 nd]Uw]G%D2&=%\&b\UA̹7ieTǩ#t3Tӕb\\@z){Dao UbhDk$r`Jf0Sq& $ 64WԚ' M5ZdWJjY`f^H'm::)+_y,@˜O黉7 F tj$m(U*-Bj*DAzs3W 5(؆ODW<9$쉖W]OzU%A>{rG7;GG般 7|{df'!1/5a8i[B7,CǃW02[2NJT%h]ӶPaل$Ѵ?tDԉTNL߆ fb&e QMi%]Hg+Xtt<Ѧ0w+VUqOb;YitJ>]VLC ]DxbQ*,դ-oҵ.<` SxA\ !\`Jd֓jiS'40@t*2#K{@'>(*umaS7c첶35!"1D'‚Cj|0MKGzĘ"L `2F<٣80TM(RL DT$qy]qj(sD|Wx %ʓ3K-› <Ŏ\5W:vKHuf3xox](.ٌ Q24{X{1[͓/S9wKK̞^4[-5թaBWCed#c"8*uwʿoEa|UWR?/_"+HF!5^֮<6l;AF.zNr! |$EV U%ɸ%6i%ũ+ < HLWȹcڬp$AEYij0Oas`2׳aИT31 3iWPewYq= 5;XrD}Vz!TpU-f K-~ =$c}CA|V[ĝ~gs|fJy Y(l1 \>Rӹ> =UTo_*FO毎N6Y2Y |o'g:c+8y $g*T@`&EG9g#3& V+-gbLGv ~owYWLTյΪ2g n7ו(RƧ qLq/)g ”%*MXpA*xYԯďJUl\@Hž񟹣*s :%&Wp8T#e|ѴN]r4C߳0sSfCbF +R0#-tc p M t |Zuk`jZ3|P Q7Q`1T ގ'Ǻpj VfS<̰cdȪߖ֫,a#ȱ HtAUe舻2|vOȃ.%QI6 4"M'TL]20MpL9:%,Lj4@DP[-ȅcDrdw&CS[9xсEVC]]j8ӰE$:g j@gŬ[zF!-Wr`箨%P#X %,R'JtFH!rc'g/5a|R5Bg_$* PR~J'M +4﹌،)K dW#,u=! F*( Yb.3)_$EFKBglȣ$L< љeRs:&ĕf  ԅe}ATʍ+iVN5n[7-C&@ؙҺ43 z4J*{5+2b&T%.9~Jᝏ=6V@bIS>+lJ5#@ 5w8IGNXII$l6"58q*Oc%3$j΂8r,W1&˳meZ2gaSpd2+Wj Z`ܺk*<,cRFa},љ8@j2׵< 묤@]j_>m6K%aF#:aXG&a=3 pPb 9΄20dK,aQ(illk=HbTSձi`3RL>ɍ,sC(%]^0+.|^bM$WPTg]Ʌ=erV'Cn8;NW %Kר1K\|so>N ?{pPqAԿˮ-]PXRS㛑Α%\wq9W^1;J-йHzV#.Sɒ \2@Ac2#CA!m:|c^!|t^OV8esJp He! ($"Ts),'PQp4,oZŽU>H MϬ~NU&fL2xR/ĉLN:c.Tjy}|V [w Ce)4@O(a8V 7ju9QU|a]+gJ:8*9Cޯ*ZgR1\`)DnhNM1WHUj:ۉhBu2E\;{(%$3-nIwJBVS,Lk6dQxO2GHDTuһqi7rQmB|Jjɲ8<hK('l ec{?RfnIWѼ}l|9U/N宓Y*JTwNZ`?|xp ByZ}OGRwF`=C󱰵9I*rV ,cf=3k+ IB :Bi`Ax8q E ɰ<:v,{3KCTO>Kw/E x"ea.2i;MV[Xz[r\lQeR MfME_&Z_50%"A2Я%T\V^+<?.!']%wI>2 TIhF+ MJs$1'",$XrAʢbndW?=>:.9`s)2Qw*OULq><AByMD>YwBZy9&2p2\VGo<Y⨌K՚4" +?%C(OzJq-0 {WY*uytMJS^:=g$CǝF"MbʹUln۲7 {~!t{7j^u Pӗɖ;HfBO_u݆8@C EK:=x'4_E340x0GbcAgY ő/]_8UJJvY&ȘXRIp_GX$-@=Ĵ5/+xjN"Еc@^I8%'.&vw>~wS<=~Z t5zEt)mzL/0$83{c)Ě**D5_ Ԕ)9 ֞ xmF #eO51\tʴX?JxT?r|Zgq Y4éq ]:f jp P ʈf^\Rah"9!F34#hh(PI DěpXjVwM3\B,͑r<_GEIX=YE>ʹ5rRb1j-w#Tκ~x-T8zMy#UqB*|QgHqκIq}yRX3bSbVm[ *)ǠO~L\JgiI2sTJUU G"vSDd] }Or4HuM]oiE@$mytpu6BgtjRLlJd}(+[%pިB\i_S}RH/ddyjg0XSdH>WAFT'5_YYHJxsQI1%p)+4vp99%w$@Y$Mb@Eo6l^)䷐AC2Rp>6`vMGJkN(@svr:3` M?/}SD]L3&PǶ ; =Daf$qR'JƩ _vZ)EGӘj(&r($ Ni% b"W <-~M%#Q)w6/RV$RzpǙD#$l&Ӫ,W+bM䭅94(Hlv7#Ww-|a@~gYqYfYtt_G6!2\.̴~jwB*M#UsE}˗jwѮ0fuqݠ }埛qQ)ҷ_pI8J n10|aVuݢEM$ [[JMu__~/w2=?_z˯ݿw?>~M {Ɂ5R )6=HѠڨ͎ƀDԦnﶻ$d\ä' j#YLKh_NVt[nud1-R1Z0x&f49*n.WltNX=XxoBaqQHP *$kr#`Yd!$M=PݪM{2'o~a{v'e6OO?Ӥjjs!o`Ѕ_ $L|&vm=wA3=~ uG+7~~ʫr٥ěwfɨ@UAp5ׇϓ5Y*J%a-zS>}~WW8!bs#IȞuՔJY8q\CxdeW''֞03dh!knG/--PҠymo7_~ӿ-q_ˎK?^[ZO?_Fw>Ӈdyf= vQri Y%2BIJD5R' v̧S 8,&o9T*u 5's~ZVc/an, 9p$"q!1eUFKkO,,]{LPc⼘8cSQ΋f[PvE̓'C VMմ}jir01L_|{z8cwwWÇ~.knۧA 2 zn_/bsDz'aM8Nt2Y$nV?ΑU׸"K\6Ha%+U ,;Ɩ4ˍeg@V^?fiƲUk%<'b!V%re{*kqiԚqtF{JLhI dx `FZc*ת7þF+bWOGh#J&O_}qo0?}MI0A߮Ï0ГߠoK>` r%H@CQ+]J ~)l2![*%Ö] rWN+aYWrkkR>{kx1 %I'jSݑna '҃GP QKy'қeoGyDF>sҷHJR&\ՅZՅjkԭx kֆ k,<־e8Wʌ5[mK"e3s%&`B bo֐7"`4c'$LD6] Ddv7_bA/2|0pO];e BxN2eL/޾)"AH(A ϳs#E.Ιmh3f7J6ad>>~>vs~T[+wCYE5 ^LaFYfZb[2S R@zc,4 M@l)h[xM5u^/_W1ĭ'0N׶ h9z]06yݶm-Mǟ~![S74WdO?6p=^a'(sv&zTd..!v^%۹ǨWXI[VXW],Γ2(]PD\]A\V+aL{iPn^t=8uII9v)SӐa2DHOݖ FF?>|ik &XN}ݻwׯ_BeiK|J,:H5TEd~fG%垬4^%;]+^vU$y1-ԗ+ A^{Tq:u{zmI+rWʞRR+"$J[ZD&}9f0fv?& &-VA7Cv{w \`y 9tۻM3i@OϤ4qqD'|=E: S/vz>Ջpy8/?9EE>4wgr} vgb+6G֞}6/ $^_}A/5Uxf&+|r 5[MggcpQ^n[%OJV"U "-\}Ol [I?c?ͮؒuM >t|TuBErOdjybbӷ~.8GtK%FRiFFLᾼQN!bRc\u(x;'FOp̸Ylt"W'TLm2ltX  yߗ, 7 ǿQ'L?X>fϓy噐\}-]4lm4-fh"e6!/nFoAzǧ?!7o"O_8I/GZO$T&{diiD><^HUDU׃+2Y)L^2AQ{+_A˖B؉%9*"wUs-}:>0W )gFdSvQʟt+_{- ۤ# Db'Q$H$F fUɟ jql:_l_GD;7 bKH1a}}>F'oLIm)TJ|Mxt8veX ]L (mRm)aVVB[EI)1U' o+Z9]$sy>u)ڄg3 (S-&.f2xEs9MNgMQAɦ%{klvDL5v/DuAvi7^ aCƠhJLEĉZR_Li^`RBj3.Uf_ag9/%1f_D3W2ߗsu )JdEIE"T Lg;c<*1t?/g0s6697%NRngxmq,e%@~`> #/uw*F"`n믿onooIZo72[$S$u6p rdX61e^Z)lji w\HJk_IM6o_׎X\Zh\[woj@'A 3Rŭ*Y^級g6 (b*G޺ddZh +)Tv]( -TA\!EHYyِyUHWIry_~ q&nDӼvӧO>>ɲowts=9sc VFCrhZTz0JU0&GhjL@QtlHڢ#ԜX.-7#Ηbh0FwӄWB6%K|V $٘r.*ޒ[tA~Ur@6HHTZO'Zqdh1'A wvslz|!Cͦ}uwsOSK^8Gm+b,K{6 j{(뎲?}oG;YHegBBrӓ~>ל." ;䕒>ІRKTA#MV^vފQ5a$H%MU!Jr2}2jWiaWR)&"H#nZt {rBs' &lhj rSq R^N1SpI;+dlESX,*9teَ0sny5zv,Rw,f(]ljTItq!q)yɶN B}<>ǿw 0/v]KiAGL8˳Aҍ푘CmvSK17* h"N_ ia1ج {=Ivذm*aٴ*h kG4S@vQ굳g-DP0a};}FmKˆV)VwZ=hDkZsnl$Yfmi r Pf9 _- ,c\G2C4,b')I MGP??Ya"wEE{\*%f_T&.:))xʼnyž2I I|k$K7-h0*H lK@&w-@gIoAۚ~@#Ba &ʯ+CK يҼbg.2)}&X$S"S=Sr8Ec ЪdI:vIϫcTlNI9.].qæ 2Io44G?-MV"Bee7 lDi'YETBͿ (|Dw'V}Pc; s{P:Mduc׹o(+B"-&?wGDoR˼ъPB@:+/s?3q4!;׃Ywʅ4,^9AG-l>8DKѡ-O:斮OYI-1°W)5q4q[onnnx< x5Zvs{RYC;7aﴯ}c`yҳ,ҜOgZ&wA]))ClKY%9ʎdt91U)t]V b+LVuova#m4\*R][0'䒌p5 HDwm+b0C tvU|ƞz6-0.jar#zl$e d W)Z*{| v$4\"^Hl1 Vy%f^FC&v ۷_94icVx2p4)'$u(&&Am3 /9}[wXg@RvFz~ȩ3&3_Pvoۛ-ldFШZ_!M‡eNQԓ~H#Uuڴ\4!;;Z 4tҰ7yvQCs֜%J6X}BpE07TJ.Gܲ)/#›3f{-[ѐ!X6 N91Yu3bVUi!R@0a l(ta;/`'R׆]Jv,F}""*Sbv$m#&B#}m]Z"BoI$ϰtMl;v}/N k, sNxh[Յb>{%;ːSbf,W y 4XˍԫXKsLvT E1Bju{Ë~z,z^Txb\Xjx q̝1ΏpCz-`HTmܽ~';U;}Qov4&1IMDĎuuh7Վ%sUmHM^MqN|t¶2АC39珄Cr_`(ĔbH!%D25/+NBQ75A^uc/i ց]sE3rEVXl]x_0&D 2XCqjh4LVb{[NnH8N'on͛v׈Yiddus@<Tvp WkH9 -藙!= Bܒơ©f\ V UwoG2Fbcx%hnRLNj$FqG|nJr 8*o2<"&T4M7H$\βՔEH!$G>`>F"\tYX eƦh, +np>ڄpW&l ̓Go,me5̄&*?u%:ƑQGmsZX>'<^ fgjkmk?6?I&htnNa!1l;uw_PZ3].WuWfu3f2jJOڪz|#Uv6l9\''W$')\9C?i^aIV͋y+T1Կ-sP Hh>4XN'N| 0t~zv%# dבmnjb#0)k3'i.Bw s{գk8"ЙP8%#S.nOy߶vOo-MƧOah:l+: לL8 +qp{=1m?ϒc\Ar5@MMxu@ MoOyljS:}?8YURPOT&fۺ+x# S@h# dr vsT@5N#[hjgAyA#z;LC.[( f qGĝӌFHʸXLun}Eg]i &?NCnW|nO<ۇfl7 =8ϧax:iFS"v?l~jvup1Db8S ?P}=}S7w2C}vXD]7,͢׆U*Ugy ߁ǼoAтX9K[d~|qND3q$}^ 3Lb >b!,i-8HX zJƆFGM{[U+}nOTHݮ}%?o?g|;L.붛}Z7ϗ4N#=їy/8%Yr#F7Amsa~ݜ=کG TBMի'2<{׼#O!ǻ`}EbDDȞR9$lxWMɗlL >FӍ2)2a-{L3b Imxm"`EuFnI#ι;a$[@ޢ.(ҋ v˾K59NJg!Ӯr"C|@v:;ϱ'fG݁?eC~oK۞lbv78}$9OeElt?n7{7/no-:i̾9\C[Z|P뾸 ?ߴz 4mu!:S[Ҧ E,AB;L_bSiGa!CHJ zMYNiI+H%R k%杍%h7%=M%.%cd Rݫׯ޼%aODKǁo"JF3෻ %n/h*ֻDsa?iLwɦ:t7Ud`8ϗi8tUĎ+K|iZvۡRԆ3 AͫfʒaVnԞN z@-M0IR˹+\xSJSVpڪR"K0œ7Y}z1#لfiQph(: vsʋP!PW? ]eY҃lV`4®ݾz}.8߾~II×R<߼ko|f;t!ҽ/stjQ`/n\"TGW)`mNљ$6Gbh"mڻ-Uo lc:~u<=-zﱯ5?~<;QSe; ˎ V]=TzOWUWWHf&Yi\tJOIlI!Ƭn),ISUT$"57V@RA"AΆX}#z.7 T܎u7=69(nG%9-/_oBIm=ȤϏqpZI{FAv#2֖97O$^ö^.T1PyWcw]Ohra>CYP,9iXK%IlPǔSֱbY+>î! .'>8,ȡA!Wnj6K BG n)㴚+ RygMW$ \&#)G<ݶ:(x|B HoAzw?4Cr-(B.N#@=p~KClWE#qv''/ۏ {>!4qN 18qbl~S;%A0RʣD{{S?ө_FK:qtHs p"#m,*-y$*!]Jw^+ )!Yj;UW,kn_ itG-0엔ώJӁ3JKyw x2!ϼ.z-lQ6'ꈜ wMAE8HoHmm&d;]O0Avl:5v?3UvoRe u =/\䝭k)*aM(a@p)Ų־&VԛJŊ/}c8>? /n>=S0|a񸭑M?g1u@KzI$>>|DeO#q]#ouL4HNI^zg#귋M\!&#{&!N皣kq1!~8ut1(QZ4Hj|~|>Įih?iKs#/KZ/VXl\gL}a8i ?y&f bvgx_,e6 xja=JKn[xWVSRP&1 [a_.'Og\F A .c|"tdg= <7dÎW ŴƁ5K@GYbުg'Qeفw&UU-E@`s)׮noCqf3Q)룆w*0J@ +%f`͜]e[ ŵGXꯀǙDc'Yű}S~a ex9I Cshx|<=.EwdtH]CjCj>d)gұ敂 ('8Kyw;Tt#cpBk*ډx剔WWUnYtJ9p0X0H9 eM5d/5 fFس\GT D|5"*1%fkń 0m@WqʀV17g'7Y=h5ɜH~ݒ9\~ī 66+=Zb e[Mc.IHF*5CDŽx*{`Y5o"1%woMGZC mD&;I]ܫQw^`!-uWn`h#+dA<ޓKږ~a⎗E~Vg`7"gY6B9̸Zwj%u37uܶ$c!t$25)GF&*/CXʉADyt@y4E&b `LN:HߘGJf {npqCO2݅e͕ Zhp~VؑmíW-V4dab TSRc󩫶4{dn-*ӹ7벴>,]<7P:>=?=H ,aUP2}ɕ巚A! 1nCڶo)9%EP7zy V?a!]GR.d*f_AAs*T0Vk^hIЂ̶g|T vS.rn<wpD$~®(h@;:P'06x ugXi]IDW"~6qDRo~Pd[ @?Ѕ_pEM`77 ?_.ϗ82#v/?K>A)Wv" "ڨ10ܶ ӲIy>cB9NdDr4mv *-9\petLȥ++44<^tI;+>4~qϮJ}YF6G~wJGupp~苦Mefn#eW; ktK0ad^ր8Hh??}Mn]67=4;ېi#q^pQ\33VT#?.[&fV d 㤰o}]utCҍe kWYiݰ IDMM!|zYW y lfsI Zb[tҍRK ;. 6\.suQ)cЉ<%Ďmzׯ{3gڇhjHnBW~AE6dQd]8KA$$'p/Kd_þow(hbd w_9yĝ))~!gULVS)։E3:T*4 hOsEx^ {M%-#HÜ{~ ﵤx )4iz{ɭbR K.q{x{l kM?0G 'i_yKH FCalCrxIG$3mi5.x"iq[V*}]W8wo8=SO "1YCjvTeYmD`}[t}lۊdLƇϴ|m660!T+nbpKlEP$;)w%z>9C晎vNJH k%8άla6j5o]ix^  {~힟w݆>'lyyUU6EsLO^%=XYm$i7=a\Г(WSW| .]yrKH3Ho+oM"`Ni0?Mm3=("߁THlדDuY.JK7KVȣftŕlΕwXeɉD%,~r:pU|^}fV{z5qz3ɤ5 M/\8ߒ?gM "!te S?J)E> gf?>tMgqܩ_|syAL |ڇß?^ti9< "̷Gx,7߳VGK Yn Dpڴȿ:4_g{mOKYIde*+"vxm_k5`șREa:X=-aXL5j b4_!61',$.SlND4S./WI1PR\I9Oe4/󹠪p?Гv-riG9FI^ w|/|S&< RLk=`C#G=a ZSUϿôZ ._1wSq~ǩ;`g{5,iC\׵ɯO//?1}>1_V%z1yya&D|X:yPeoYg-ͺʨY {k0΄:W6&{ý)\Q*dlͅ=^(O|=5eqBhgD˥,:Zl]?$nh|v@R:e{-sB=X )z EȄ=bn_8t W!(%_y@5O%=\ffd "I>$ ퟟgItv]n&ݩL1eIpVr%n̍dY،gsi_6{f* s0 "bk`[&wE>.֓9nkJgeշ lHډ/_=ǧ!7[\;MJnS"O1*t<3?[A,_qYbj/n vN0֗[uIJU+k= r>r_ʝ4iu{Hr1Q܋H}'-xߧ&]PY#-[]-x@SgxqObdtρ؜v **ѫ<~y^f>. \uџruLjpT dZNF>>RTU`%^o 0bbEc<PK@[֏)Hwo+E}_<4',g _Q<%v]>nXkXVؿr4?_ϒ>P[GfiockeŽYaX:̒CPs$!|jXaӫ*Юvsz OֽJk}5OԈl?S^~FN!a-'@e,.Ga C}y$uK*F\m[G??s Hyxt TA r} ß㋘&D$(^? _xz>-s:x$LK`lh؈yT [g|;b dݝ9;7׿qt? `=_l lJ~nJQ#)L+5۞Bऺ:xB>HqItɓW| /6ݡ"|P8rzzk^Z*.p q9.EB _ұp fh')Das7>w4EPur!2pPk{$FS! QaZg\WP}W~*ќPr1y!L֫\;j.?D,O>5 ٕUkC۱aɘo JۿVQy<>49ФjoxVZt;v3}WmSJ<0ׅ#轔%7`3fF|)ܭ^nq~O~Dهdab$_Eic& ; 1Xk}l/cǝ8q`<`2(p<|k4T{.KOx_9}wn Ѩ=H*J|wfصfTa-;Z]I=QL)/s3bx1SjI}h`|Ow->ކqJ@QtfIUZJ{Ū|AG#JDR.| T*JHQ9ʦlFyqL9"_<.a1[>34Rfhr9|=iCw 6d;V?bcMjIUnhK6w]N}^Ap@_4$%,-!5\ u:lKN_4>]kO]FL^4 ݈MwO/;9)ڽŵ4@u2݀^;ll&H8URD ACP2e#U1Nr#0b] 9C?1B56 < J1>JXG[(B0kdd" x=8=V%~4|ܧIRa^r]6_r`̽,LjE-N'YRעyURX?u?-7˝װuoW"?G Z:0̀T=ΖV-bhs2uaM1={7#[ )ay4ŀ0厱:pLݼpUJZmIrχ9*G 1( llˢpbH^=/Tn|}0AWt<qX!ӓRR,1@=a[s9GIDP#X%F j<{F|bvǃ]Ȋj('GA\}:sH.uG׽.S9r{rJe]{>c-7*q |<\2u7ymozsdBդLP < ׎- piAm Egb7fBs0Af XfDQ0m-ngi<6؏^n9i^< aĉ}dzَmhډ!0W/I49o~~h# [&\6|r}# KNqOL[tĠ3Yda cAҷU,cKN T |i֛ ޏ;o4;c8k,ۺ~ RO ӮĀ** j^+p3g7lI#0r:d3u^;&Bj*$`FÄaqP[z( zGG^ľ6!4b6qsQU ~J[:(g޵qL;p:3:GKkfxLJdԒ.":)yPjIA0%Z%?F䎗2a ~RNAՇҶ=_ $qx HH{ qw3d}P.#m栒ES9u ' h4hS5|G^KsGojSZXvC0pb^x'0C'>fƾz笣?Wv#rv{%.UtC'J S|T$jbRa\< Ρے PuM㖧 f}>c!(`xxwf1(gFF&my$IbPoE[8m?(G~C9[N߸YOJD[wNjtJ;&,fX$5ki :F 7:֕;1}p㒹e;&!SCgj'q]8X<͘hCFWYJ}CU5ڴJ KF훀c [\G TSE;Q@KYYn4&c <룼>-n1S vjY $FLUgcw-<( DZ)ԸY<<$؍X 3%_`oo=UW1d3}sD IM&Frmɯ,unYo;QWPQ@H]SB&"eL(l):(.ZIk7r/yKvX ne4 Yŀ(KÀns偢JeYPtX] |'= (QޙzZLbϾW;^f.EM=ˊv빶ڤrz-b@G^ZO=mcLCB\J\C/8Z~XOnoo⇏?iyqm,"Qb%grUC[V;/իt~/ GTP#t$s'A$O+'&Héq=)Kʜ O-s- F𵜖̊B*9h| Čojt3эŬt=r.(et7piOGcgl{&n1 [:=gEy-< -ǃPGq!C?O톙yCΉz8cc@jۦU00ćv]] k<r*5" U`h2Hod}glJ_A5&N0D r4}Iĭ sr<ڞ8A =˥}.ʂ`ypʴ|HFmWUJaaXo/ d#r ڰmCkklAo6c3d"u62T[V7wK<۫vz%gX9жwX]y2GRyx6#U‰\E=;]>u]ǽV-Vw|*c՝o>*re~2B~, G=^a%Md}D} H-  0Qn'50dXhY?f1*v([i8,Ofy)L0xjO:*pEP|1ցȁ+ʰ*Ώ{00 x~~ aPŪ`KB ;(8Cg~LI=,$ ] T;1%`U sl6i X$6sv~mӪf񜵐?U\cz -FwrxŊhv$ 5G/FG5AAfԶ} F^zE<.} OOO_6I64#1+`f'X@i<_HRu,_56FiKe~ ?t:W;34 KplK&U+92| zOM>B9Q%)ˏ*~1e*w:e{̚5G jJ[ ` x"]-ϋ\@6&hFSf bXo:A$g+æ/ϯȞ>؋JPvv֎Q) 0Q ˗/0??2YBt6I^^GNG0L?$QNA'd`ҍcv1bQ5]2Mt0s{B8}=G5IDAT qf6ndKmStbS֯>(R7 #(]lZEcW%ʓr(g9h@Uq\lYM /u,Y!5yn2 m͠xݣ-pF&F-10U+&Ӌp+r2] '/#B8TZ9g lh|>\y 5mlM8ZSt%8c:QN_XB+4/?z߻:k]pGؚ':(0zlBUY$0 EVJWo5pՈ]Qٔ͊F2 .- 6J^M\mXBؐNa59osk(JYB '~ЮM68Fhy%Fd-o/[ҐZ55"]v:*Ts/USNkNX<[j^|'!aQV+cKJ2R;`"͊TzY,E HL` f&'6ϤS*\۴b.3Hu#y~$SQf;6@Ѳl쒷]vaPa[Ag h!G l:<0(q6(PUے@Z.\S>r`|KH`o/To?r63 ZЯń|biq;۴E*؈&6yRIoIdWȚjۘ[7 Ėe۟@g+C^Géx>AFĚoY4-(hG1#'=X, 'VS_UȲSjO gС?̮V< B/f*$$ L>*JFX&^L֑/u?PԶۀP҅ngQ7{զ֖z?Ce(VS ;DTm2h&Vi.=U~c;&ԝӴg̒GBɹ O4$ut~>vNY/;ݛ5L V'tUlhNA.MrZ#8,vMH/x-ɒt>=?^~[kьtzNZ3\K@C/Վʑa3i&!4[s#$7$j=6ׁ^Fs#i*VbSXۧh9mkK73pOf 8h% =%rXPZ&RMֺ֫7ٲiZa\$Vqc&tz##O=qW"5E;8HrY Y 4LR*#9=F{0I_ۑscUAQX_)S~zA 9⌜5iVw,9f~}Aafq]Yq*^x;'6s1u󛶹G-el7ԥ|3Ӟm ʵHavEўɎhѭBo HR wYyI ͊]l(XpLaZYǏ߿MTwݒx*[9FjgdEv v#Ԃ* ݮzs?v>+!F~jmi⋛km5.3*i-v_VڷK {iQ3ւx ͋$a˜>t.H ?m \"7wAYL;_{1M`G;&Տ-KOܮO<2VKaаϰL-=ɬ z?1ݮK3 d܈rwh/nUz&.]YS" l]3-lv3PoSh`]jX0KnF!Z65TaZ Ws-QOE*u4?t:t%- q[PؓHdN۪_4v@PyZG-mbS *&9KmzV}i@LXfH"XҼ#Q7  .B!URz5!c/!ME䛼4. D`XR db瑚;Ql>ܘ n4 65cĭ:yM h<|RWhQ""ޛ bO,,H@v\>>X}R߁t+i[L뇑g2/kldZlآnek{যl>MtjhDup`G3{ɷԠDTjP:<%*{n?$<]~j8*1vy#S rz:=2ĭ]/@KC$W'Ar7 !hV~FOڊ`)d&m-5z]2uYQ^bdWWR'G+|@N~?>%MEMҺrQ:LI+q0gN⫯8?-<َ2֔։vS/t=jwMM 4^Dէt{u*#Y:OL*7B52b߿ݯWt nUX Hnx\/o߾=Xu=c&|ځ#E 7f E鱍qF^sc_\ҊzCYwC0=M&՞k"0Д)A[+U>?/-ʘCΠ U/=^@ndt@܇77 y@3l\!C`Za|h`6^pUi>8JZq] U^W*A\/Gʍi<霃n*$8tݮ߾S|B7E<<1 b0wyhۊVv꙽XLH!ɲl9;vJϧIS=7q}dR4WmtBrq!?ȝɺǃ0Q;x_/ 9-kW^Q؂x/&rG|oZg3Hw"sy~Kf kf =UѤCgw e#|kk`:X@D%l~.m` >3e&54jy3)D؃i?%n*tvh4Ie{fJj6h.ۀ3R_J}{{?q^p?hOxԃ<:a=pn]sV`&M X~a]xVܒt3n,D~ ?mBt wTKׯ_^~kLi?~([)L7i,ΣO4M1`MD+rFQ]%_yKzR-1T%x4pg%I!HsXL1MTh,\^N>ζT;bS#G#`]t ayI~dU9׻1L=b*xJ(EtfAUUAzd,+HKѧ{!mq_~yOW%I|q׊CPm3wK}TPUk)8*QZ㦓!&K4xcYDYmsU)5j1C^vnBa zJvpר.6 ,F6Xb#lz7$J#$mp] ʃh>On׆ ey] !8tU|[^a]?G #Rq]RXȳPU@U HzlGM)4mZ,5|E*/9W'j?b*>0V ,}բEhg,rSs3"X@QLmKLrKW- bPbqffZݚ9-]Rw$FTI~Qc ;}X%HL"F|}co2RzN{Ј3gIDǞ.o+A:Zk;լ+$8?PaW>Qܻ{U, aWRQ]ͯ^-&oj}%R6qh 59X+ <*;8ؤAxZ[ΐ:ND ^T-j)qǥ3nB`,eIRHo-f(bPQF}C9N1lEЄ7:$Yc -=o͡jǚaIsDVS0hy֡n=e#2u7S^#r[dHr=aJbsQ -p!-IhkٽJ #3 ;uMBCk7Ɨ罿DMғ3bgFo\.Z3?`ΤS.:O\ψFk  +I,zdN,ȣe>Jhp}¥?l8t>v~x>TqhbkMWtX`=hy5ԝXb0U յQՖh^]|PՓ;WFU>f:fMx*KSNlޟuC%8tA6_,' DѱW>ߑD>YCu4!Ġy6S 3!:JӔۏ p{~yP)DtNbLǼ(ԅ^^^j$j GHnp¢.bs *pa

,<%h-jrΝ9)ke|Ƅ@,`TaUQktVͰ*Ɍh-J:`¹0YOi@flt4l)xFe;^ʼࣁK"k[. u@]1mYdh#uL49Ǵ+W]2[ J⡯Rc#䥦;eya]  8O0o[lFtÍ.=VOח oLP bOtTx&덬䤔1rSp'caCk)8FUHWX$3 ÁQ%N"eSd+N1Z5|["=8uPi9ї? i pMAo 5E>ō*{hLA_;q@ #CǣN2ǾkO IA#f(; ɥN|MpSG]HedBF;`ܷwQRu.GskYJo!!jrKViudVߘ@FϠ-#5(A5eq=PrX;0k++moK`jSd]A90z>C?{O5mPRl&Wv {.3#gq(}w-[%h:h N!鮹a\/UG 8$^.02UIY[lD6 cF&eSg$E*%s!P`nδ9VZڪUThZ4sy [in)n(t,9a !9wb'+`"Jw% *ِ:?:1oc RGH%gWZP@KJTV<4XjN7{v찄Ozۨى[Ўl̮77O^M<+dnLϠlW ^3Aϊ>) [MOHOmM -j~ B1 Ò{7rVOqAn]@W' ϥ\NK,@mPu8X.$𥋵o@Adb@T,";H*ww.-Ovbz=6ʜ\Z~+FL*ybeξ!MZoL{*IIj$OK+6MZ!M3p&ݖ v˭URvbsX%ڬ]Ġ0AG{n(*/#I zwSjg\U&%Ƭ`55Q֞XqA  Z 姁3.?C. Sf^mPЖez>a5:El{n8NNk [m!Jt6)ӸNSUP6NVl̻V#5̪DRl/7=CDueVaMEٷ(4@ S}Iz Trn ڦA ̫&sS:yd0-pBPAmJY?ʵ 6ΡФJG{p zoN soYmF9!ϖEJjjbvEИD ! ϋI_jϴCOqf0(M;!x_epj'Z'rޱvg`Um*\eWM>V뢪VK(EU@&۳нZL0XnNzm/t액=?ldVj{) ;#=m36`qsSEA}g }{sZجGo5}-h* O9Cq3VtƼqpg#P5c kv$VC!l$$qWDYD]),TU͞W66j,+dy-H͢ }gJ.5l#%[)Jhtn[Ֆ:E5{lޝC(ѫvðv6NOm{5"y>z2Z"Fgv賫bKe7a;Ը!ӧK[7H \zn\SZ, X zv#({-[ta0ʥ9GG37Y0嚕flQEtɥ!Ufd ѡor T0xfd/0f~@M N;}9E3[0Ҙ{C1>yo7 Sr%cU'P-c߳q^r;p#yxo(,h!pR·U[@Lz̹nw̰,&ظuB]Z^reFFNq8 Eaf.4U &e"rk+ZC6FU]  Q:A- Vod+иZs]fZLBIr2U|wCq$k"m/}PjLU-16MS@WCVval8Xsi]*fq({ g~%o'|p>?NqJ4ПVMݠ6kƵz4WY6GA؅aYsG\ * V&+Iƈ] V[WB J!1]`B;e` xcPZϧ8+j+YW@WK*K[թZjF9cpT2Љ>(w +(c$2 rXWEQ Qb|2Sq%<9HS4 Pc=o4`l2` ُwLBgmaWpglÌ@fب6mLuU`DgDp3CXW=i9ME>`W'SOO/qHYע*G jpm*jBm 4>$yj":q#5tA-/h*AШ'4BcۡUk 9xz@ZL1&MBs3[H)PU S4dLmUŃ: ̊&r[k4G⥠B-6[ 22*]V?Y+1CHu~O Iײe@.$rcjSWnޚroͫzs~BExchhs-E& m ,*Rr,qjM#@dݺ"g4 N \Qpc Mm1 ="XfYl0Yq< BiLF$J hrqz^h+~ɍ V2b_Ԥ&8g pMK%ŝWOO_; v (x܌SaO5O@=K3(MŃEvI usRlwo?F;l1FZ=(m&TT6DTm@Q+ eZ6beikSW.{֡;eKQv(z)1֎qH伃1jk"!N1oD2>?uČZj{qg!]r XHOa&ư[yz6t~ʼn|vd,,a q4sZmvEdEB X9p,DsJ&*ꐀ'ӧ` #7ƜϟX;njQ7qtK)(pq*Y[tK޾xoFz6PZ٥!5x"_k`+'OŴTTt&l1=BJ=kܠ%l[ $aG1GԧCz}R%5fUBQkG"}$r@<-\[N{c n,kXw!pӨ| 7(T/cƚ vb'/M,!4kTq omz&7z%ֺ7KϽ@F輀`ؽPRO%:Y̴[LDŽh}0$nw%ǒ! ?dZ:Q`.#1l35u!l@[A1mԻ[ק؟vmɨC-:᭪ҏѩ=ڒ ]SCGu8JmD֗+ZVYvZxU`/PEl8XhlĢ"Kق6,RF-, Ni]Uᤗˁ8oRSXQ/ꆤ ]8??;Cu-dPGe= >{Fm6|= /zY5xBϢ?y=Oٝ&WH(6 ^ W67Uh,?^5hCE\jcoRWB}SJOb.z&s_}90~$ %=v[]gլC.i8 g1kv%N7NJIX-iMkELqƆOӢJvvpo3!*.ڬCp|u"lP l^ƒ$ʶ= 0K9˂[S-eQ} Ih%?d s^¯WV'dBՏrJ`†-b|vQtQO&EwCTs}'oLa*]] #MEY^ &0]6v_UFQi}Rk1sT a{g7_Au&82jܵ ƶY2?xk>Ə,r%[$.w7\Od\hOA^C;ZbZv9a@Cƺd1XR[M'Ƹُ_apVD1i ;vgZ[nҾssxv ۟Hcr2 ]Oy3I9l9qq\\[8z_g{kЏՊh{z6t]nn1 {wu7ot|CA^wIENDB`process_viewer-0.2.6/assets/magnifier.png010066400017500001750000000077661353320622000167660ustar0000000000000000PNG  IHDR>asBIT|d pHYs  IDATxkt\u{ߙȖ%kƏ$҆G qClVx[RSRZZi-^) +D<Ad@Q!7l,if4gƒνW}<{>@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@d.ZY9TUe%@|$2]+H4,)&GPT; MQoom%JuMWVS`C[CLԝtuNw/vgBU \pM\n&"Ž{7wn+?[̬ Q3sxIoNB?knw `β) O`y贈צG7 ~ŗ4_ u0yқ:'j[燠$A -ʺnXA©~9vKFUT`ٹDT+ӈT3W>kPeB~1~f;.? l->ҩ6EHdX_9",X>g-#nV3.n',x4Ri]u\q\5@/ɦvX6](f,m=Crr+1e%ws* )(`n] 0xUZb(ModrVb5]@˿9-GUtz-~/ Q`-o+pY<ġ|1 tOz79\f_Q%{@PQ<_;؄.\v aD)|w.RÄw. o" S jػcЎmǞ!`ʱڱ57e@|0n*``PÞ_/g bhǶ+=E1i֝>r;P6n,F 70csǜ2_m#A>T{Zvہ&2nrvMgT I i|O478k*w{v9zŴ n 0 ]?^v(I ?6DvE4]}z̾RP 1%^AXcq|`n 6w<+l'XpV,2Ai,ƀ2&?:] -qҗIw?0HF5&<JY=7Ig^/ lAƃf59%] dB7; &BVca[퐊rLIuD"-}!F3xvm,"B8&y-) &CnS GPh;lk}dQ9>)tDrK ("Mt1 O"&@n4?D"z”n2?/pDYdHL d&v,plBB?+bR1& j Ԇnt="8&tv,/m1WbMT+:M?t;o'y[.qڟEgsxΏ/ȭI9MO47<o ,˺iY }7o#"vp0n4eξn3E72!I 2gy6]T_6L;c fQ޽@Ժ~O>?jO tɨ+CY&Od{4Զ]rQ9%Znhɨ?LU<4ĄoqgfDCXF"wx&S\$T[:U=K$; : R; ! ֬"(pg̚r?ˬ-s$dW79O\76_п {{5.4xA3Y"3-޷y7ɺ>4;cIkYȷ ko3K^o9g, ?XC eFUYzujS3n'I+_J יwaza3My+g3%-E̤?Lm| ga[8/gFu|P)JE$ǫe=;x7*NuY T:RYEb~S[\ӚY.kn~zc/^7!9*+{;~éHljKܴ^9&Wշ͈^b\5I 5-m; &yK@ZBR䘭-v #űP L68ʪMaۇ=Mw<*po 4߻.p|#OM.%}e#cV/i,(\^A]'1&oKZb-H٢VfiRC- Xm\SGTv|o㞻Ús3M Z$R O]@U٤X.}q# hGΝ˧sT)^``4#NO'"ǀ1?_ĐT1[žSN6Vjo<īyT劣Uf۹Տj*ksCVPX=}7FQhav\xU}Fq[K>&tI5nU(}a[Y|qX@AZT3?1gYĜ!4A9<{-cpܬy'=PXDG#' /{TmopUFZ*A/W op̚M7+tOGW~woYSVM?_넏0k"e99L$Hmbͅ^+C7IENDB`process_viewer-0.2.6/src/color.rs010066400017500001750000000026631353320622000152570ustar0000000000000000use gdk; pub struct Color { pub r: f64, pub g: f64, pub b: f64, } fn convert(v: u8) -> f64 { f64::from(v) / 255.0 } fn apply(i: isize) -> u8 { let mut value = i - 1; let mut v = 0; for _ in 0..8 { v |= value & 1; v <<= 1; value >>= 1; } v >>= 1; v as u8 } impl Color { pub fn new(r: u8, g: u8, b: u8) -> Color { Color { r: convert(r), g: convert(g), b: convert(b), } } pub fn generate(index: usize) -> Color { let n = (index as f64).cbrt() as isize; let mut index = index as isize - (n * n * n); let p = &mut [n, n, n]; if index == 0 { return Color::new(apply(p[0]), apply(p[1]), apply(p[2])); } index -= 1; let v = (index % 3) as usize; index /= 3; if index < n { p[v] = index % n; return Color::new(apply(p[0]), apply(p[1]), apply(p[2])); } index -= n; p[v] = index / n; p[(v + 1) % 3] = index % n; Color::new(apply(p[0]), apply(p[1]), apply(p[2])) } /*pub fn to_int(&self) -> usize { 0xFF << 24 | (self.r as usize) << 16 | (self.g as usize) << 8 | (self.b as usize) }*/ pub fn to_gdk(&self) -> gdk::RGBA { gdk::RGBA { red: self.r, green: self.g, blue: self.b, alpha: 1.0, } } } process_viewer-0.2.6/src/disk_info.rs010066400017500001750000000057041353320622000161050ustar0000000000000000use std::cell::RefCell; use std::rc::Rc; use notebook::NoteBook; use utils::format_number; use gtk::{self, BoxExt, ButtonExt, ContainerExt, GridExt, LabelExt, ProgressBarExt, WidgetExt}; use sysinfo::{self, DiskExt, SystemExt}; fn update_disk(label: >k::Label, p: >k::ProgressBar, disk: &sysinfo::Disk) { label.set_text(format!("{} mounted on \"{}\"", disk.get_name() .to_str() .unwrap_or_else(|| ""), disk.get_mount_point() .to_str() .unwrap_or_else(|| "")).as_str()); p.set_text(Some(format!("{} / {}", format_number(disk.get_total_space() - disk.get_available_space()), format_number(disk.get_total_space())).as_str())); p.set_fraction( (disk.get_total_space() - disk.get_available_space()) as f64 / disk.get_total_space() as f64); } fn refresh_disks(grid: >k::Grid, disks: &[sysinfo::Disk], grid_elems: &mut Vec<(gtk::Label, gtk::ProgressBar)>) { let mut done = 0; for (pos, disk) in disks.iter().enumerate() { if pos <= grid_elems.len() { let name = gtk::Label::new(None); let p = gtk::ProgressBar::new(); p.set_show_text(true); grid.attach(&name, 0, pos as i32, 1, 1); grid.attach(&p, 1, pos as i32, 2, 1); grid_elems.push((name, p)); } update_disk(&grid_elems[pos].0, &grid_elems[pos].1, disk); done += 1; } // A disk was removed so we need to remove it from the list. while grid_elems.len() > done { if let Some(elem) = grid_elems.pop() { grid.remove(&elem.0); grid.remove(&elem.1); } else { break } } } pub fn create_disk_info(sys: &Rc>, note: &mut NoteBook) { let grid_elems: Rc>> = Rc::new(RefCell::new(Vec::new())); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let scroll = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); let grid = gtk::Grid::new(); grid.set_column_homogeneous(true); grid.set_margin_end(5); grid.set_margin_start(5); grid.set_margin_top(10); grid.set_margin_bottom(5); let refresh_but = gtk::Button::new_with_label("Refresh"); refresh_but.connect_clicked(clone!(sys, grid, grid_elems => move |_| { sys.borrow_mut().refresh_disks(); refresh_disks(&grid, sys.borrow().get_disks(), &mut *grid_elems.borrow_mut()); })); scroll.add(&grid); vertical_layout.pack_start(&scroll, true, true, 0); vertical_layout.pack_start(&refresh_but, false, true, 0); note.create_tab("Disk information", &vertical_layout); refresh_disks(&grid, sys.borrow().get_disks(), &mut *grid_elems.borrow_mut()); } process_viewer-0.2.6/src/display_sysinfo.rs010066400017500001750000000513011353320622000173510ustar0000000000000000use gdk; use glib::object::Cast; use gtk::{ self, AdjustmentExt, BoxExt, ContainerExt, GridExt, LabelExt, ProgressBarExt, ScrolledWindowExt, ToggleButtonExt, WidgetExt, WidgetExtManual, GtkWindowExt, }; use sysinfo::{self, ComponentExt, NetworkExt, ProcessorExt, SystemExt}; use std::cell::RefCell; use std::iter; use std::rc::Rc; use graph::Graph; use notebook::NoteBook; use settings::Settings; use utils::{connect_graph, format_number, RotateVec}; fn create_header( label_text: &str, parent_layout: >k::Box, display_graph: bool, ) -> gtk::CheckButton { let check_box = gtk::CheckButton::new_with_label("Graph view"); check_box.set_active(display_graph); let label = gtk::Label::new(Some(label_text)); let empty = gtk::Label::new(None); let grid = gtk::Grid::new(); let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0); horizontal_layout.pack_start(>k::Label::new(None), true, true, 0); horizontal_layout.pack_start(&check_box, false, false, 0); grid.attach(&empty, 0, 0, 3, 1); grid.attach_next_to(&label, Some(&empty), gtk::PositionType::Right, 3, 1); grid.attach_next_to(&horizontal_layout, Some(&label), gtk::PositionType::Right, 3, 1); grid.set_column_homogeneous(true); parent_layout.pack_start(&grid, false, false, 15); check_box } pub fn create_progress_bar(non_graph_layout: >k::Grid, line: i32, label: &str, text: &str) -> gtk::ProgressBar { let p = gtk::ProgressBar::new(); let l = gtk::Label::new(Some(label)); p.set_text(Some(text)); p.set_show_text(true); non_graph_layout.attach(&l, 0, line, 1, 1); non_graph_layout.attach(&p, 1, line, 11, 1); p } #[allow(dead_code)] pub struct DisplaySysInfo { procs: Rc>>, ram: gtk::ProgressBar, swap: gtk::ProgressBar, vertical_layout: gtk::Box, // network in usage in_usage: gtk::Label, // network out usage out_usage: gtk::Label, components: Vec, cpu_usage_history: Rc>, // 0 = RAM // 1 = SWAP ram_usage_history: Rc>, temperature_usage_history: Rc>, network_history: Rc>, pub ram_check_box: gtk::CheckButton, pub swap_check_box: gtk::CheckButton, pub network_check_box: gtk::CheckButton, pub temperature_check_box: Option, } impl DisplaySysInfo { pub fn new( sys: &Rc>, note: &mut NoteBook, win: >k::ApplicationWindow, settings: &Settings, ) -> DisplaySysInfo { let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let mut procs = Vec::new(); let scroll = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); let mut components = vec!(); // CPU let mut cpu_usage_history = Graph::new(None, false); cpu_usage_history.set_label_callbacks(Some(Box::new(|_| { ["100".to_string(), "50".to_string(), "0".to_string(), "%".to_string()] }))); // RAM let mut ram_usage_history = Graph::new(Some(sys.borrow().get_total_memory() as f64), true); ram_usage_history.set_label_callbacks(Some(Box::new(|v| { if v < 100_000. { [v.to_string(), format!("{}", v / 2.), "0".to_string(), "kB".to_string()] } else if v < 10_000_000. { [format!("{:.1}", v / 1_024f64), format!("{:.1}", v / 2_048f64), "0".to_string(), "MB".to_string()] } else if v < 10_000_000_000. { [format!("{:.1}", v / 1_048_576f64), format!("{:.1}", v / 2_097_152f64), "0".to_string(), "GB".to_string()] } else { [format!("{:.1}", v / 1_073_741_824f64), format!("{:.1}", v / 1_073_741_824f64), "0".to_string(), "TB".to_string()] } }))); ram_usage_history.set_labels_width(70); // TEMPERATURE let mut temperature_usage_history = Graph::new(Some(1.), false); temperature_usage_history.set_label_callbacks(Some(Box::new(|v| { [format!("{:.1}", v), format!("{:.1}", v / 2.), "0".to_string(), "°C".to_string()] }))); temperature_usage_history.set_labels_width(70); // NETWORK let mut network_history = Graph::new(Some(1.), false); network_history.set_label_callbacks(Some(Box::new(|v| { let v = v as u64; if v < 1000 { return [v.to_string(), (v >> 1).to_string(), "0".to_string(), "B/sec".to_string()]; } let nb = v >> 10; // / 1_024 if nb < 100_000 { [nb.to_string(), (nb >> 1).to_string(), "0".to_string(), "kB/sec".to_string()] } else if nb < 10_000_000 { [(nb >> 10).to_string(), (nb >> 11).to_string(), "0".to_string(), "MB/sec".to_string()] } else if nb < 10_000_000_000 { [(nb >> 20).to_string(), (nb >> 21).to_string(), "0".to_string(), "GB/sec".to_string()] } else { [(nb >> 30).to_string(), (nb >> 31).to_string(), "0".to_string(), "TB/sec".to_string()] } }))); network_history.set_labels_width(70); let mut check_box3 = None; vertical_layout.set_spacing(5); vertical_layout.set_margin_top(10); vertical_layout.set_margin_bottom(10); let non_graph_layout = gtk::Grid::new(); non_graph_layout.set_column_homogeneous(true); non_graph_layout.set_margin_end(5); let non_graph_layout2 = gtk::Grid::new(); non_graph_layout2.set_column_homogeneous(true); non_graph_layout2.set_margin_start(5); let non_graph_layout3 = gtk::Box::new(gtk::Orientation::Vertical, 0); let non_graph_layout4 = gtk::Box::new(gtk::Orientation::Vertical, 0); vertical_layout.pack_start(>k::Label::new(Some("Total CPU usage")), false, false, 7); procs.push(gtk::ProgressBar::new()); { let p: >k::ProgressBar = &procs[0]; let s = sys.borrow(); p.set_margin_end(5); p.set_margin_start(5); p.set_show_text(true); let processor_list = s.get_processor_list(); if !processor_list.is_empty() { let pro = &processor_list[0]; p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.))); p.set_fraction(f64::from(pro.get_cpu_usage())); } else { p.set_text(Some("0.0 %")); p.set_fraction(0.); } vertical_layout.add(p); } // // PROCESS PART // let check_box = create_header("Process usage", &vertical_layout, settings.display_graph); for (i, pro) in sys.borrow().get_processor_list().iter().skip(1).enumerate() { let i = i + 1; procs.push(gtk::ProgressBar::new()); let p: >k::ProgressBar = &procs[i]; let l = gtk::Label::new(Some(&format!("{}", i))); p.set_text(Some(&format!("{:.2} %", pro.get_cpu_usage() * 100.))); p.set_show_text(true); p.set_fraction(f64::from(pro.get_cpu_usage())); non_graph_layout.attach(&l, 0, i as i32 - 1, 1, 1); non_graph_layout.attach(p, 1, i as i32 - 1, 11, 1); cpu_usage_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), &format!("process {}", i), None); } vertical_layout.add(&non_graph_layout); cpu_usage_history.attach_to(&vertical_layout); // // MEMORY PART // let check_box2 = create_header("Memory usage", &vertical_layout, settings.display_graph); let ram = create_progress_bar(&non_graph_layout2, 0, "RAM", ""); let swap = create_progress_bar(&non_graph_layout2, 1, "Swap", ""); vertical_layout.pack_start(&non_graph_layout2, false, false, 15); //vertical_layout.add(&non_graph_layout2); ram_usage_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "RAM", Some(4)); ram_usage_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "Swap", Some(2)); ram_usage_history.attach_to(&vertical_layout); // // TEMPERATURES PART // if !sys.borrow().get_components_list().is_empty() { check_box3 = Some(create_header("Components' temperature", &vertical_layout, settings.display_graph)); for component in sys.borrow().get_components_list() { let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 10); // TODO: add max and critical temperatures as well let temp = gtk::Label::new(Some(&format!("{:.1} °C", component.get_temperature()))); horizontal_layout.pack_start(>k::Label::new(Some(component.get_label())), true, false, 0); horizontal_layout.pack_start(&temp, true, false, 0); horizontal_layout.set_homogeneous(true); non_graph_layout3.add(&horizontal_layout); components.push(temp); temperature_usage_history.push(RotateVec::new(iter::repeat(0f64) .take(61) .collect()), component.get_label(), None); } vertical_layout.add(&non_graph_layout3); temperature_usage_history.attach_to(&vertical_layout); } // // NETWORK PART // let check_box4 = create_header("Network usage", &vertical_layout, settings.display_graph); // input data let in_usage = gtk::Label::new(Some(&format_number(0))); let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 10); horizontal_layout.pack_start(>k::Label::new(Some("Input data")), true, false, 0); horizontal_layout.pack_start(&in_usage, true, false, 0); horizontal_layout.set_homogeneous(true); non_graph_layout4.add(&horizontal_layout); network_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "Input data", None); // output data let out_usage = gtk::Label::new(Some(&format_number(0))); let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 10); horizontal_layout.pack_start(>k::Label::new(Some("Output data")), true, false, 0); horizontal_layout.pack_start(&out_usage, true, false, 0); horizontal_layout.set_homogeneous(true); non_graph_layout4.add(&horizontal_layout); network_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "Output data", None); vertical_layout.add(&non_graph_layout4); network_history.attach_to(&vertical_layout); network_history.area.set_margin_bottom(20); // // Putting everyting into places now. // let area = cpu_usage_history.area.clone(); let area2 = ram_usage_history.area.clone(); let area3 = temperature_usage_history.area.clone(); let area4 = network_history.area.clone(); let cpu_usage_history = connect_graph(cpu_usage_history); let ram_usage_history = connect_graph(ram_usage_history); let temperature_usage_history = connect_graph(temperature_usage_history); let network_history = connect_graph(network_history); scroll.add(&vertical_layout); note.create_tab("System usage", &scroll); // It greatly improves the scrolling on the system information tab. No more clipping. if let Some(adjustment) = scroll.get_vadjustment() { adjustment.connect_value_changed( clone!(cpu_usage_history, ram_usage_history, temperature_usage_history, network_history => move |_| { cpu_usage_history.borrow().invalidate(); ram_usage_history.borrow().invalidate(); temperature_usage_history.borrow().invalidate(); network_history.borrow().invalidate(); })); } let mut tmp = DisplaySysInfo { procs: Rc::new(RefCell::new(procs)), ram: ram.clone(), swap: swap.clone(), out_usage: out_usage.clone(), in_usage: in_usage.clone(), vertical_layout, components, cpu_usage_history: Rc::clone(&cpu_usage_history), ram_usage_history: Rc::clone(&ram_usage_history), ram_check_box: check_box.clone(), swap_check_box: check_box2.clone(), temperature_usage_history: Rc::clone(&temperature_usage_history), temperature_check_box: check_box3.clone(), network_history: Rc::clone(&network_history), network_check_box: check_box4.clone(), }; tmp.update_system_info(&sys.borrow(), settings.display_fahrenheit); win.add_events(gdk::EventMask::STRUCTURE_MASK); // TODO: ugly way to resize drawing area, I should find a better way win.connect_configure_event(move |w, _| { // To silence the annoying warning: // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without // calling gtk_widget_get_preferred_width/height(). How does the code know the size to // allocate?" w.get_preferred_width(); let w = w.clone().upcast::().get_size().0 - 130; area.set_size_request(w, 200); area2.set_size_request(w, 200); area3.set_size_request(w, 200); area4.set_size_request(w, 200); false }); check_box.clone().upcast::() .connect_toggled(clone!(non_graph_layout, cpu_usage_history => move |c| { show_if_necessary(c, &cpu_usage_history.borrow(), &non_graph_layout); })); check_box2.clone().upcast::() .connect_toggled(clone!(non_graph_layout2, ram_usage_history => move |c| { show_if_necessary(c, &ram_usage_history.borrow(), &non_graph_layout2); })); if let Some(ref check_box3) = check_box3 { check_box3.clone().upcast::() .connect_toggled( clone!(non_graph_layout3, temperature_usage_history => move |c| { show_if_necessary(c, &temperature_usage_history.borrow(), &non_graph_layout3); })); } check_box4.clone().upcast::() .connect_toggled(clone!(non_graph_layout4, network_history => move |c| { show_if_necessary(c, &network_history.borrow(), &non_graph_layout4); })); scroll.connect_show(clone!(cpu_usage_history, ram_usage_history => move |_| { show_if_necessary(&check_box.clone().upcast::(), &cpu_usage_history.borrow(), &non_graph_layout); show_if_necessary(&check_box2.clone().upcast::(), &ram_usage_history.borrow(), &non_graph_layout2); if let Some(ref check_box3) = check_box3 { show_if_necessary(&check_box3.clone().upcast::(), &temperature_usage_history.borrow(), &non_graph_layout3); } show_if_necessary(&check_box4.clone().upcast::(), &network_history.borrow(), &non_graph_layout4); })); tmp } pub fn update_system_info(&mut self, sys: &sysinfo::System, display_fahrenheit: bool) { let disp = |total, used| { if total < 100_000 { format!("{} / {} kB", used, total) } else if total < 10_000_000 { format!("{:.2} / {} MB", used as f64 / 1_024f64, total >> 10) // / 1024 } else if total < 10_000_000_000 { format!("{:.2} / {} GB", used as f64 / 1_048_576f64, total >> 20) // / 1_048_576 } else { format!("{:.2} / {} TB", used as f64 / 1_073_741_824f64, total >> 30) // / 1_073_741_824 } }; let total_ram = sys.get_total_memory(); let used = sys.get_used_memory(); self.ram.set_text(Some(&disp(total_ram, used))); if total_ram != 0 { self.ram.set_fraction(used as f64 / total_ram as f64); } else { self.ram.set_fraction(0.0); } { let mut r = self.ram_usage_history.borrow_mut(); r.data[0].move_start(); if let Some(p) = r.data[0].get_mut(0) { *p = used as f64; } } let total = ::std::cmp::max(sys.get_total_swap(), total_ram); let used = sys.get_used_swap(); self.swap.set_text(Some(&disp(sys.get_total_swap(), used))); let mut fraction = if total != 0 { used as f64 / total as f64 } else { 0f64 }; if fraction.is_nan() { fraction = 0f64; } self.swap.set_fraction(fraction); { let mut r = self.ram_usage_history.borrow_mut(); r.data[1].move_start(); if let Some(p) = r.data[1].get_mut(0) { *p = used as f64; } } // temperature part let mut t = self.temperature_usage_history.borrow_mut(); for (pos, (component, label)) in sys.get_components_list() .iter() .zip(self.components.iter()) .enumerate() { t.data[pos].move_start(); if let Some(t) = t.data[pos].get_mut(0) { *t = f64::from(component.get_temperature()); } if let Some(t) = t.data[pos].get_mut(0) { *t = f64::from(component.get_temperature()); } if display_fahrenheit { label.set_text(&format!("{:.1} °F", component.get_temperature() * 1.8 + 32.)); } else { label.set_text(&format!("{:.1} °C", component.get_temperature())); } } } pub fn update_network(&mut self, sys: &sysinfo::System) { let mut t = self.network_history.borrow_mut(); self.in_usage.set_text(format_number(sys.get_network().get_income()).as_str()); self.out_usage.set_text(format_number(sys.get_network().get_outcome()).as_str()); t.data[0].move_start(); *t.data[0].get_mut(0).expect("cannot get data 0") = sys.get_network().get_income() as f64; t.data[1].move_start(); *t.data[1].get_mut(0).expect("cannot get data 1") = sys.get_network().get_outcome() as f64; } pub fn update_system_info_display(&mut self, sys: &sysinfo::System) { let v = &*self.procs.borrow_mut(); let h = &mut *self.cpu_usage_history.borrow_mut(); for (i, pro) in sys.get_processor_list().iter().enumerate() { v[i].set_text(Some(&format!("{:.1} %", pro.get_cpu_usage() * 100.))); v[i].set_show_text(true); v[i].set_fraction(f64::from(pro.get_cpu_usage())); if i > 0 { h.data[i - 1].move_start(); if let Some(h) = h.data[i - 1].get_mut(0) { *h = f64::from(pro.get_cpu_usage()); } } } h.invalidate(); self.ram_usage_history.borrow().invalidate(); self.temperature_usage_history.borrow().invalidate(); self.network_history.borrow().invalidate(); } } fn show_if_necessary(check_box: >k::ToggleButton, proc_horizontal_layout: &Graph, non_graph_layout: &T) { if check_box.get_active() { proc_horizontal_layout.show_all(); non_graph_layout.hide(); } else { non_graph_layout.show_all(); proc_horizontal_layout.hide(); } } process_viewer-0.2.6/src/graph.rs010066400017500001750000000253011353320622000152340ustar0000000000000000use cairo; use gtk::{self, BoxExt, ContainerExt, DrawingArea, ScrolledWindowExt, StateFlags, WidgetExt}; use std::cell::RefCell; use gdk::{self, WindowExt}; use std::rc::Rc; use std::time::Instant; use color::Color; use utils::RotateVec; const LEFT_WIDTH: f64 = 31.; pub struct Graph { elapsed: Instant, colors: Vec, pub data: Vec>, vertical_layout: gtk::Box, scroll_layout: gtk::ScrolledWindow, horizontal_layout: gtk::Box, pub area: DrawingArea, max: Option>, keep_max: bool, display_labels: RefCell, initial_diff: Option, label_callbacks: Option [String; 4]>>, labels_layout_width: i32, } impl Graph { // If `max` is `None`, the graph will expect values between 0 and 1. // // If `keep_max` is set to `true`, then this value will never go down, meaning that graphs // won't rescale down. It is not taken into account if `max` is `None`. pub fn new(max: Option, keep_max: bool) -> Graph { let g = Graph { elapsed: Instant::now(), colors: vec!(), data: vec!(), vertical_layout: gtk::Box::new(gtk::Orientation::Vertical, 0), scroll_layout: gtk::ScrolledWindow::new( None::<>k::Adjustment>, None::<>k::Adjustment>, ), horizontal_layout: gtk::Box::new(gtk::Orientation::Horizontal, 0), area: DrawingArea::new(), max: if let Some(max) = max { Some(RefCell::new(max)) } else { None }, keep_max, display_labels: RefCell::new(true), initial_diff: None, label_callbacks: None, labels_layout_width: 80, }; g.scroll_layout.set_min_content_width(g.labels_layout_width); g.scroll_layout.add(&g.vertical_layout); g.horizontal_layout.pack_start(&g.area, true, true, 0); g.horizontal_layout.pack_start(&g.scroll_layout, false, true, 10); g.horizontal_layout.set_margin_start(5); g } /// Changes the size of the layout containing labels (the one on the right). pub fn set_labels_width(&mut self, labels_layout_width: u32) { self.scroll_layout.set_min_content_width(labels_layout_width as i32); self.labels_layout_width = labels_layout_width as i32; } pub fn set_label_callbacks( &mut self, label_callbacks: Option [String; 4]>>, ) { self.label_callbacks = label_callbacks; } pub fn set_display_labels(&self, display_labels: bool) { *self.display_labels.borrow_mut() = display_labels; if display_labels { self.scroll_layout.show_all(); } else { self.scroll_layout.hide(); } self.invalidate(); } pub fn hide(&self) { self.horizontal_layout.hide(); } pub fn show_all(&self) { self.horizontal_layout.show_all(); if !*self.display_labels.borrow() { self.scroll_layout.hide(); } } pub fn attach_to(&self, to: >k::Box) { to.add(&self.horizontal_layout); } pub fn push(&mut self, d: RotateVec, s: &str, override_color: Option) { let c = if let Some(over) = override_color { Color::generate(over) } else { Color::generate(self.data.len() + 11) }; let l = gtk::Label::new(Some(s)); l.override_color(StateFlags::from_bits(0).expect("from_bits failed"), Some(&c.to_gdk())); self.vertical_layout.add(&l); self.colors.push(c); self.data.push(d); } fn draw_labels(&self, c: &cairo::Context, max: f64, height: f64) { if let Some(ref call) = self.label_callbacks { let entries = call(max); let font_size = 8.; c.set_source_rgb(0., 0., 0.); c.set_font_size(font_size); c.move_to(LEFT_WIDTH - 4. - entries[0].len() as f64 * 4., font_size); c.show_text(entries[0].as_str()); c.move_to(LEFT_WIDTH - 4. - entries[1].len() as f64 * 4., height / 2.); c.show_text(entries[1].as_str()); c.move_to(LEFT_WIDTH - 4. - entries[2].len() as f64 * 4., height - 2.); c.show_text(entries[2].as_str()); c.move_to(font_size - 1., height / 2. + 4. * (entries[3].len() >> 1) as f64); c.rotate(-::std::f64::consts::FRAC_PI_2); c.show_text(entries[3].as_str()); } } pub fn draw(&self, c: &cairo::Context, width: f64, height: f64) { let x_start = if self.label_callbacks.is_some() { LEFT_WIDTH } else { 1.0 }; c.set_source_rgb(0.95, 0.95, 0.95); c.rectangle(x_start, 1.0, width - 1.0, height - 2.0); c.fill(); c.set_source_rgb(0.0, 0.0, 0.0); c.set_line_width(1.0); c.move_to(x_start, 0.0); c.line_to(x_start, height); c.move_to(width, 0.0); c.line_to(width, height); c.move_to(x_start, 0.0); c.line_to(width, 0.0); c.move_to(x_start, height); c.line_to(width, height); // For now it's always 60 seconds. let time = 60.; let elapsed = self.elapsed.elapsed().as_secs() % 5; let x_step = (width - 2.0 - x_start) * 5.0 / (time as f64); let mut current = width - elapsed as f64 * (x_step / 5.0) - 1.0; if x_step < 0.1 { c.stroke(); return; } while current > x_start { c.move_to(current, 0.0); c.line_to(current, height); current -= x_step; } let step = height / 10.0; current = step - 1.0; while current < height - 1. { c.move_to(x_start, current); c.line_to(width - 1.0, current); current += step; } c.stroke(); if let Some(ref self_max) = self.max { let mut max = if self.keep_max { *self_max.borrow() } else { 1. }; let len = self.data[0].len() - 1; for x in 0..len { for entry in &self.data { if entry[x] > max { max = entry[x]; } } } if !self.data.is_empty() && !self.data[0].is_empty() { let len = self.data[0].len() - 1; let step = (width - 2.0 - x_start) / len as f64; current = x_start + 1.0; let mut index = len; while current > x_start && index > 0 { for (entry, color) in self.data.iter().zip(self.colors.iter()) { c.set_source_rgb(color.r, color.g, color.b); c.move_to(current + step, height - entry[index - 1] / max * (height - 1.0)); c.line_to(current, height - entry[index] / max * (height - 1.0)); c.stroke(); } current += step; index -= 1; } } if max > *self_max.borrow() || !self.keep_max { *self_max.borrow_mut() = max; } self.draw_labels(c, max, height); } else if !self.data.is_empty() && !self.data[0].is_empty() { let len = self.data[0].len() - 1; let step = (width - 2.0 - x_start) / (len as f64); current = x_start + 1.0; let mut index = len; while current > x_start && index > 0 { for (entry, color) in self.data.iter().zip(self.colors.iter()) { c.set_source_rgb(color.r, color.g, color.b); c.move_to(current + step, height - entry[index - 1] * (height - 1.0)); c.line_to(current, height - entry[index] * (height - 1.0)); c.stroke(); } current += step; index -= 1; } // To be called in last to avoid having to restore state (rotation). self.draw_labels(c, 100., height); } } pub fn invalidate(&self) { if let Some(t_win) = self.area.get_window() { let (x, y) = self.area.translate_coordinates(&self.area, 0, 0) .expect("translate_coordinates failed"); let rect = gdk::Rectangle { x, y, width: self.area.get_allocated_width(), height: self.area.get_allocated_height() }; t_win.invalidate_rect(Some(&rect), true); } } pub fn send_size_request(&self, width: Option) { let mut width = match width { Some(w) => w, None => { if let Some(parent) = self.area.get_parent() { parent.get_allocation().width - parent.get_margin_start() - parent.get_margin_end() } else { eprintln!(" A parent is required if no width is \ provided..."); return; } } }; // This condition is to avoid having a graph with a bigger width than the window. if let Some(top) = self.area.get_toplevel() { let max_width = top.get_allocation().width; if width > max_width { width = max_width; } } self.area.set_size_request( if *self.display_labels.borrow() { width - if width >= self.labels_layout_width { self.labels_layout_width } else { width } } else { width }, 200); } } pub trait Connecter { fn connect_to_window_events(&self); } impl Connecter for Rc> { fn connect_to_window_events(&self) { let s = self.clone(); if let Some(parent) = self.borrow().horizontal_layout.get_toplevel() { // TODO: ugly way to resize drawing area, I should find a better way parent.connect_configure_event(move |w, _| { let need_diff = s.borrow().initial_diff.is_none(); if need_diff { let mut s = s.borrow_mut(); let parent_width = if let Some(p) = s.area.get_parent() { p.get_allocation().width } else { 0 }; s.initial_diff = Some(w.get_allocation().width - parent_width); } s.borrow().send_size_request(None); false }); } else { eprintln!("This method needs to be called *after* it has been put inside a window"); } } } process_viewer-0.2.6/src/macros.rs010066400017500001750000000015471353320622000154250ustar0000000000000000// // Process viewer // // Copyright (c) 2019 Guillaume Gomez // #[macro_export] macro_rules! clone { (@param _) => ( _ ); (@param $x:ident) => ( $x ); ($($n:ident),+ => move || $body:expr) => ( { $( let $n = $n.clone(); )+ move || $body } ); ($($n:ident),+ => move |$($p:tt),+| $body:expr) => ( { $( let $n = $n.clone(); )+ move |$(clone!(@param $p),)+| $body } ); } // Macro for upgrading a weak reference or returning the given value // // This works for glib/gtk objects as well as anything else providing an upgrade method #[macro_export] macro_rules! upgrade_weak { ($x:ident, $r:expr) => {{ match $x.upgrade() { Some(o) => o, None => return $r, } }}; ($x:ident) => { upgrade_weak!($x, ()) }; } process_viewer-0.2.6/src/notebook.rs010066400017500001750000000013361353320622000157550ustar0000000000000000use gtk::{self, IsA}; use gtk::prelude::{BoxExt, NotebookExtManual, WidgetExt}; pub struct NoteBook { pub notebook: gtk::Notebook, pub tabs: Vec, } impl NoteBook { pub fn new() -> NoteBook { NoteBook { notebook: gtk::Notebook::new(), tabs: Vec::new(), } } pub fn create_tab>(&mut self, title: &str, widget: &T) -> Option { let label = gtk::Label::new(Some(title)); let tab = gtk::Box::new(gtk::Orientation::Horizontal, 0); tab.pack_start(&label, true, true, 0); tab.show_all(); let index = self.notebook.append_page(widget, Some(&tab)); self.tabs.push(tab); Some(index) } } process_viewer-0.2.6/src/process_dialog.rs010066400017500001750000000241571353374764200171620ustar0000000000000000use gtk::{ self, AdjustmentExt, BoxExt, ButtonExt, ContainerExt, LabelExt, ScrolledWindowExt }; use gtk::{WidgetExt, GtkWindowExt}; use pango; use sysinfo::{self, Pid, ProcessExt}; use std::cell::RefCell; use std::fmt; use std::iter; use std::rc::Rc; use graph::{Connecter, Graph}; use notebook::NoteBook; use utils::{get_main_window, connect_graph, format_number, RotateVec}; #[allow(dead_code)] pub struct ProcDialog { working_directory: gtk::Label, memory_usage: gtk::Label, cpu_usage: gtk::Label, run_time: gtk::Label, pub popup: gtk::Window, pub pid: Pid, notebook: NoteBook, ram_usage_history: Rc>, cpu_usage_history: Rc>, memory_peak: RefCell, memory_peak_label: gtk::Label, } impl fmt::Debug for ProcDialog { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ProcDialog {{ pid: {} }}", self.pid) } } impl ProcDialog { pub fn update(&self, process: &sysinfo::Process, start_time: u64) { self.working_directory.set_text(&process.cwd().display().to_string()); let memory = process.memory() << 10; // * 1024 let memory_s = format_number(memory); self.memory_usage.set_text(&memory_s); if memory > *self.memory_peak.borrow() { *self.memory_peak.borrow_mut() = memory; self.memory_peak_label.set_text(&memory_s); } self.cpu_usage.set_text(&format!("{:.1}%", process.cpu_usage())); let running_since = compute_running_since(process, start_time); self.run_time.set_text(&format_time(running_since)); let mut t = self.ram_usage_history.borrow_mut(); t.data[0].move_start(); *t.data[0].get_mut(0).expect("cannot get data 0") = process.memory() as f64; t.invalidate(); let mut t = self.cpu_usage_history.borrow_mut(); t.data[0].move_start(); *t.data[0].get_mut(0).expect("cannot get data 0") = f64::from(process.cpu_usage()); t.invalidate(); } } fn format_time(t: u64) -> String { format!("{}{}{}{}s", { let days = t / 86_400; if days > 0 { format!("{}d ", days) } else { "".to_owned() } }, { let hours = t / 3_600 % 24; if hours > 0 { format!("{}h ", hours) } else { "".to_owned() } }, { let minutes = t / 60 % 60; if minutes > 0 { format!("{}m ", minutes) } else { "".to_owned() } }, t % 60) } fn create_and_add_new_label(scroll: >k::Box, title: &str, text: &str) -> gtk::Label { let horizontal_layout = gtk::Box::new(gtk::Orientation::Horizontal, 0); horizontal_layout.set_margin_top(5); horizontal_layout.set_margin_bottom(5); horizontal_layout.set_margin_end(5); horizontal_layout.set_margin_start(5); let label = gtk::Label::new(None); label.set_justify(gtk::Justification::Left); label.set_markup(&format!("{}: ", title)); let text = gtk::Label::new(Some(text)); text.set_selectable(true); text.set_justify(gtk::Justification::Left); text.set_line_wrap(true); text.set_line_wrap_mode(pango::WrapMode::Char); horizontal_layout.add(&label); horizontal_layout.add(&text); scroll.add(&horizontal_layout); text } fn compute_running_since( process: &sysinfo::Process, running_since: u64, ) -> u64 { if running_since > process.start_time() { running_since - process.start_time() } else { process.start_time() - running_since } } pub fn create_process_dialog( process: &sysinfo::Process, start_time: u64, total_memory: u64, ) -> ProcDialog { let mut notebook = NoteBook::new(); let popup = gtk::Window::new(gtk::WindowType::Toplevel); popup.set_title(&format!("Information about {}", process.name())); popup.set_transient_for(get_main_window().as_ref()); popup.set_destroy_with_parent(true); // // PROCESS INFO TAB // let scroll = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); let close_button = gtk::Button::new_with_label("Close"); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); scroll.set_policy(gtk::PolicyType::Automatic, gtk::PolicyType::Automatic); let running_since = compute_running_since(process, start_time); let labels = gtk::Box::new(gtk::Orientation::Vertical, 0); create_and_add_new_label(&labels, "name", process.name()); create_and_add_new_label(&labels, "pid", &process.pid().to_string()); let memory_peak = process.memory() << 10; // * 1024 let memory_usage = create_and_add_new_label(&labels, "memory usage", &format_number(memory_peak)); let memory_peak_label = create_and_add_new_label(&labels, "memory usage peak", &format_number(memory_peak)); let cpu_usage = create_and_add_new_label(&labels, "cpu usage", &format!("{:.1}%", process.cpu_usage())); let run_time = create_and_add_new_label(&labels, "Running since", &format_time(running_since)); create_and_add_new_label(&labels, "command", &format!("{:?}", process.cmd())); create_and_add_new_label(&labels, "executable path", &process.exe().display().to_string()); let working_directory = create_and_add_new_label(&labels, "current working directory", &process.cwd().display().to_string()); create_and_add_new_label(&labels, "root directory", &process.root().display().to_string()); let mut text = String::with_capacity(100); for env in process.environ() { text.push_str(&format!("\n{:?}", env)); } create_and_add_new_label(&labels, "environment", &text); scroll.add(&labels); vertical_layout.pack_start(&scroll, true, true, 0); vertical_layout.pack_start(&close_button, false, true, 0); notebook.create_tab("Information", &vertical_layout); // // GRAPH TAB // let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); vertical_layout.set_spacing(5); vertical_layout.set_margin_top(10); vertical_layout.set_margin_bottom(10); vertical_layout.set_margin_start(5); vertical_layout.set_margin_end(5); let scroll = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); let mut cpu_usage_history = Graph::new(Some(100.), false); // In case a process uses more than 100% let mut ram_usage_history = Graph::new(Some(total_memory as f64), false); cpu_usage_history.set_display_labels(false); ram_usage_history.set_display_labels(false); cpu_usage_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "", None); cpu_usage_history.set_label_callbacks(Some(Box::new(|v| { if v > 100. { let nb = v.ceil() as u64; [nb.to_string(), (nb / 2).to_string(), "0".to_string(), "%".to_string()] } else { ["100".to_string(), "50".to_string(), "0".to_string(), "%".to_string()] } }))); vertical_layout.add(>k::Label::new(Some("Process usage"))); cpu_usage_history.attach_to(&vertical_layout); cpu_usage_history.invalidate(); let cpu_usage_history = connect_graph(cpu_usage_history); ram_usage_history.push(RotateVec::new(iter::repeat(0f64).take(61).collect()), "", None); ram_usage_history.set_label_callbacks(Some(Box::new(|v| { if v < 100_000. { [v.to_string(), format!("{}", v / 2.), "0".to_string(), "kB".to_string()] } else if v < 10_000_000. { [format!("{:.1}", v / 1_024f64), format!("{:.1}", v / 2_048f64), "0".to_string(), "MB".to_string()] } else if v < 10_000_000_000. { [format!("{:.1}", v / 1_048_576f64), format!("{:.1}", v / 2_097_152f64), "0".to_string(), "GB".to_string()] } else { [format!("{:.1}", v / 1_073_741_824f64), format!("{:.1}", v / 1_073_741_824f64), "0".to_string(), "TB".to_string()] } }))); vertical_layout.add(>k::Label::new(Some("Memory usage"))); ram_usage_history.attach_to(&vertical_layout); ram_usage_history.invalidate(); let ram_usage_history = connect_graph(ram_usage_history); scroll.add(&vertical_layout); scroll.connect_show(clone!(ram_usage_history, cpu_usage_history => move |_| { ram_usage_history.borrow().show_all(); cpu_usage_history.borrow().show_all(); })); notebook.create_tab("Resources usage", &scroll); popup.add(¬ebook.notebook); // To silence the annoying warning: // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without // calling gtk_widget_get_preferred_width/height(). How does the code know the size to // allocate?" popup.get_preferred_width(); popup.set_size_request(500, 600); close_button.connect_clicked(clone!(popup => move |_| { popup.destroy(); })); popup.set_resizable(true); popup.show_all(); if let Some(adjust) = scroll.get_vadjustment() { adjust.set_value(0.); scroll.set_vadjustment(&adjust); } ram_usage_history.connect_to_window_events(); cpu_usage_history.connect_to_window_events(); ProcDialog { working_directory, memory_usage, cpu_usage, run_time, popup, pid: process.pid(), notebook, ram_usage_history, cpu_usage_history, memory_peak: RefCell::new(memory_peak), memory_peak_label, } } process_viewer-0.2.6/src/process_viewer.rs010077500017500001750000000524101353374470100172110ustar0000000000000000// // Process viewer // // Copyright (c) 2017 Guillaume Gomez // #![crate_type = "bin"] extern crate cairo; extern crate gdk; extern crate gdk_pixbuf; extern crate gio; extern crate glib; extern crate gtk; extern crate libc; extern crate pango; extern crate sysinfo; extern crate toml; #[macro_use] extern crate serde_derive; use sysinfo::*; use gdk_pixbuf::Pixbuf; use gio::{ActionExt, ActionMapExt, ApplicationExt, ApplicationExtManual, MemoryInputStream}; use glib::{Bytes, Cast, IsA, ToVariant}; use gtk::{AboutDialog, Dialog, EditableSignals, Entry, Inhibit, MessageDialog}; use gtk::{ AboutDialogExt, BoxExt, ButtonBoxExt, ButtonExt, ContainerExt, DialogExt, EntryExt, GtkApplicationExt, GtkListStoreExt, GtkListStoreExtManual, GtkWindowExt, GtkWindowExtManual, NotebookExtManual, SearchBarExt, ToggleButtonExt, TreeModelExt, TreeSortableExtManual, TreeViewExt, WidgetExt, }; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::env::args; #[cfg(unix)] use std::os::unix::process::CommandExt; use std::process::{Command, Stdio}; use std::rc::Rc; use std::time::SystemTime; use display_sysinfo::DisplaySysInfo; use notebook::NoteBook; use procs::{create_and_fill_model, Procs}; use settings::Settings; #[macro_use] mod macros; mod color; mod disk_info; mod display_sysinfo; mod graph; mod notebook; mod utils; mod process_dialog; mod procs; mod settings; pub const APPLICATION_NAME: &str = "com.github.GuillaumeGomez.process-viewer"; fn update_system_info(system: &Rc>, info: &mut DisplaySysInfo, display_fahrenheit: bool) { let mut system = system.borrow_mut(); system.refresh_system(); info.update_system_info(&system, display_fahrenheit); info.update_system_info_display(&system); } fn update_system_network(system: &Rc>, info: &mut DisplaySysInfo) { let mut system = system.borrow_mut(); system.refresh_network(); info.update_network(&system); } fn update_window(list: >k::ListStore, system: &Rc>) { let mut system = system.borrow_mut(); system.refresh_processes(); let entries: &HashMap = system.get_process_list(); let mut seen: HashSet = HashSet::new(); if let Some(iter) = list.get_iter_first() { let mut valid = true; while valid { if let Some(pid) = list.get_value(&iter, 0).get::().map(|x| x as Pid) { if let Some(p) = entries.get(&(pid)) { list.set(&iter, &[2, 3, 5], &[&format!("{:.1}", p.cpu_usage()), &p.memory(), &p.cpu_usage()]); valid = list.iter_next(&iter); seen.insert(pid); } else { valid = list.remove(&iter); } } } } for (pid, pro) in entries.iter() { if !seen.contains(pid) { create_and_fill_model(list, pro.pid().as_u32(), &format!("{:?}", &pro.cmd()), &pro.name(), pro.cpu_usage(), pro.memory()); } } } fn parse_quote(line: &str, quote: char) -> Vec { let args = line.split(quote).collect::>(); let mut out_args = vec!(); for (num, arg) in args.iter().enumerate() { if num != 1 { out_args.extend_from_slice(&parse_entry(arg)); } else { out_args.push((*arg).to_owned()); } } out_args } // super simple parsing fn parse_entry(line: &str) -> Vec { match (line.find('\''), line.find('"')) { (Some(x), Some(y)) => { if x < y { parse_quote(line, '\'') } else { parse_quote(line, '"') } } (Some(_), None) => parse_quote(line, '\''), (None, Some(_)) => parse_quote(line, '"'), (None, None) => line.split(' ').map(::std::borrow::ToOwned::to_owned) .collect::>(), } } #[cfg(unix)] fn build_command(c: &mut Command) -> &mut Command { unsafe { c.pre_exec(|| { libc::setsid(); Ok(()) }) } } #[cfg(windows)] fn build_command(c: &mut Command) -> &mut Command { c } fn start_detached_process(line: &str) -> Option { let args = parse_entry(line); let command = args[0].clone(); let cmd = build_command(Command::new(&command).args(&args)).stdin(Stdio::null()) .stderr(Stdio::null()) .stdout(Stdio::null()) .spawn(); if cmd.is_err() { Some(format!("Failed to start '{}'", &command)) } else { None } } fn run_command>(input: &Entry, window: &T, d: &Dialog) { if let Some(text) = input.get_text() { let x = if let Some(x) = start_detached_process(&text) { x } else { "The command started successfully".to_owned() }; d.destroy(); let m = MessageDialog::new(Some(window), gtk::DialogFlags::DESTROY_WITH_PARENT, gtk::MessageType::Info, gtk::ButtonsType::Ok, &x); m.set_modal(true); m.connect_response(|dialog, response| { if response == gtk::ResponseType::DeleteEvent || response == gtk::ResponseType::Close || response == gtk::ResponseType::Ok { dialog.destroy(); } }); m.show_all(); } } fn create_new_proc_diag( process_dialogs: &Rc>>, pid: Pid, sys: &sysinfo::System, starting_time: u64, ) { if let Some(ref proc_diag) = process_dialogs.borrow().get(&pid) { proc_diag.popup.present(); return; } let total_memory = sys.get_total_memory(); if let Some(process) = sys.get_process(pid) { let diag = process_dialog::create_process_dialog(process, starting_time, total_memory); diag.popup.connect_destroy(clone!(process_dialogs => move |_| { process_dialogs.borrow_mut().remove(&pid); })); process_dialogs.borrow_mut().insert(pid, diag); } } pub struct RequiredForSettings { current_source: Option, current_network_source: Option, current_system_source: Option, sys: Rc>, process_dialogs: Rc>>, list_store: gtk::ListStore, display_tab: Rc>, } pub fn setup_timeout( refresh_time: u32, rfs: &Rc>, ) { let ret = { let mut rfs = rfs.borrow_mut(); rfs.current_source.take().map(glib::Source::remove); let sys = &rfs.sys; let process_dialogs = &rfs.process_dialogs; let list_store = &rfs.list_store; Some( gtk::timeout_add(refresh_time, clone!(sys, process_dialogs, list_store => move || { // first part, deactivate sorting let sorted = TreeSortableExtManual::get_sort_column_id(&list_store); list_store.set_unsorted(); // we update the tree view update_window(&list_store, &sys); // we re-enable the sorting if let Some((col, order)) = sorted { list_store.set_sort_column_id(col, order); } let dialogs = process_dialogs.borrow(); let start_time = get_now(); for dialog in dialogs.values() { // TODO: handle dead process? if let Some(process) = sys.borrow().get_process(dialog.pid) { dialog.update(process, start_time); } } glib::Continue(true) })) ) }; rfs.borrow_mut().current_source = ret; } pub fn setup_network_timeout( refresh_time: u32, rfs: &Rc>, ) { let ret = { let mut rfs = rfs.borrow_mut(); rfs.current_network_source.take().map(glib::Source::remove); let sys = &rfs.sys; let display_tab = &rfs.display_tab; Some( gtk::timeout_add(refresh_time, clone!(sys, display_tab => move || { update_system_network(&sys, &mut display_tab.borrow_mut()); glib::Continue(true) })) ) }; rfs.borrow_mut().current_network_source = ret; } pub fn setup_system_timeout( refresh_time: u32, rfs: &Rc>, settings: &Rc>, ) { let ret = { let mut rfs = rfs.borrow_mut(); rfs.current_system_source.take().map(glib::Source::remove); let sys = &rfs.sys; let display_tab = &rfs.display_tab; Some( gtk::timeout_add(refresh_time, clone!(sys, display_tab, settings => move || { update_system_info(&sys, &mut display_tab.borrow_mut(), settings.borrow().display_fahrenheit); glib::Continue(true) })) ) }; rfs.borrow_mut().current_system_source = ret; } fn get_now() -> u64 { SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) .expect("couldn't get start time") .as_secs() } fn build_ui(application: >k::Application) { let settings = Settings::load(); let menu = gio::Menu::new(); let menu_bar = gio::Menu::new(); let more_menu = gio::Menu::new(); let settings_menu = gio::Menu::new(); menu.append(Some("Launch new executable"), Some("app.new-task")); menu.append(Some("Quit"), Some("app.quit")); settings_menu.append(Some("Display temperature in °F"), Some("app.temperature")); settings_menu.append(Some("Display graphs"), Some("app.graphs")); settings_menu.append(Some("More settings..."), Some("app.settings")); menu_bar.append_submenu(Some("_Settings"), &settings_menu); more_menu.append(Some("About"), Some("app.about")); menu_bar.append_submenu(Some("?"), &more_menu); application.set_app_menu(Some(&menu)); application.set_menubar(Some(&menu_bar)); let window = gtk::ApplicationWindow::new(application); let sys = sysinfo::System::new(); let start_time = get_now(); let sys = Rc::new(RefCell::new(sys)); let mut note = NoteBook::new(); let procs = Procs::new(sys.borrow().get_process_list(), &mut note); let current_pid = Rc::clone(&procs.current_pid); let info_button = procs.info_button.clone(); window.set_title("Process viewer"); window.set_position(gtk::WindowPosition::Center); // To silence the annying warning: // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without // calling gtk_widget_get_preferred_width/height(). How does the code know the size to // allocate?" window.get_preferred_width(); window.set_default_size(600, 700); window.connect_delete_event(|w, _| { w.destroy(); Inhibit(false) }); sys.borrow_mut().refresh_all(); procs.kill_button.connect_clicked(clone!(current_pid, sys => move |_| { let sys = sys.borrow(); if let Some(process) = current_pid.get().and_then(|pid| sys.get_process(pid)) { process.kill(Signal::Kill); } })); let display_tab = DisplaySysInfo::new(&sys, &mut note, &window, &settings); disk_info::create_disk_info(&sys, &mut note); let v_box = gtk::Box::new(gtk::Orientation::Vertical, 0); let ram_check_box = display_tab.ram_check_box.clone(); let swap_check_box = display_tab.swap_check_box.clone(); let network_check_box = display_tab.network_check_box.clone(); let temperature_check_box = display_tab.temperature_check_box.clone(); let display_tab = Rc::new(RefCell::new(display_tab)); // I think it's now useless to have this one... v_box.pack_start(¬e.notebook, true, true, 0); window.add(&v_box); let process_dialogs: Rc>> = Rc::new(RefCell::new(HashMap::new())); let list_store = procs.list_store.clone(); let rfs = Rc::new(RefCell::new(RequiredForSettings { current_source: None, current_network_source: None, current_system_source: None, sys: sys.clone(), process_dialogs: process_dialogs.clone(), list_store, display_tab, })); let refresh_processes_rate = settings.refresh_processes_rate; let refresh_system_rate = settings.refresh_system_rate; let refresh_network_rate = settings.refresh_network_rate; let settings = Rc::new(RefCell::new(settings)); setup_timeout(refresh_processes_rate, &rfs); setup_network_timeout(refresh_network_rate, &rfs); setup_system_timeout(refresh_system_rate, &rfs, &settings); let settings_action = gio::SimpleAction::new("settings", None); settings_action.connect_activate(clone!(settings => move |_, _| { settings::show_settings_dialog(&settings, &rfs); })); info_button.connect_clicked( clone!(current_pid, process_dialogs, sys => move |_| { if let Some(pid) = current_pid.get() { create_new_proc_diag(&process_dialogs, pid, &*sys.borrow(), start_time); } } )); procs.left_tree.connect_row_activated( clone!(sys => move |tree_view, path, _| { let model = tree_view.get_model().expect("couldn't get model"); let iter = model.get_iter(path).expect("couldn't get iter"); let pid = model.get_value(&iter, 0) .get::() .map(|x| x as Pid) .expect("failed to get value from model"); create_new_proc_diag(&process_dialogs, pid, &*sys.borrow(), start_time); } )); let quit = gio::SimpleAction::new("quit", None); quit.connect_activate(clone!(window => move |_, _| { window.destroy(); })); let about = gio::SimpleAction::new("about", None); about.connect_activate(clone!(window => move |_, _| { let p = AboutDialog::new(); p.set_authors(&["Guillaume Gomez"]); p.set_website_label(Some("my website")); p.set_website(Some("https://guillaume-gomez.fr/")); p.set_comments(Some("A process viewer GUI wrote with gtk-rs")); p.set_copyright(Some("This is under MIT license")); p.set_transient_for(Some(&window)); p.set_program_name("process-viewer"); let memory_stream = MemoryInputStream::new_from_bytes( &Bytes::from_static(include_bytes!("../assets/eye.png"))); let logo = Pixbuf::new_from_stream(&memory_stream, None::<&gio::Cancellable>); if let Ok(logo) = logo { p.set_logo(Some(&logo)); } p.set_modal(true); p.connect_response(|dialog, response| { if response == gtk::ResponseType::DeleteEvent || response == gtk::ResponseType::Close { dialog.destroy(); } }); p.show_all(); })); let new_task = gio::SimpleAction::new("new-task", None); new_task.connect_activate(clone!(window => move |_, _| { let dialog = gtk::Dialog::new_with_buttons( Some("Launch new executable"), Some(&window), gtk::DialogFlags::USE_HEADER_BAR, &[("Run", gtk::ResponseType::Other(0)), ("Cancel", gtk::ResponseType::Close)], ); let input = Entry::new(); // To set "run" button disabled by default. dialog.set_response_sensitive(gtk::ResponseType::Other(0), false); // To make "run" and "cancel" button take all spaces. if let Some(run) = dialog.get_widget_for_response(gtk::ResponseType::Other(0)) { if let Some(parent) = run.get_parent() { let parent = parent.downcast::().unwrap(); parent.set_property_layout_style(gtk::ButtonBoxStyle::Expand); } } input.connect_changed(clone!(dialog => move |input| { match input.get_text() { Some(ref x) if !x.is_empty() => { dialog.set_response_sensitive(gtk::ResponseType::Other(0), true); } _ => dialog.set_response_sensitive(gtk::ResponseType::Other(0), false), } })); input.connect_activate(clone!(window, dialog => move |input| { run_command(input, &window, &dialog); })); dialog.connect_response(clone!(input, window => move |dialog, response| { match response { gtk::ResponseType::Close => { dialog.destroy(); } gtk::ResponseType::Other(0) => { run_command(&input, &window, &dialog); } _ => {} } })); dialog.get_content_area().add(&input); // To silence the annying warning: // "(.:2257): Gtk-WARNING **: Allocating size to GtkWindow 0x7f8a31038290 without // calling gtk_widget_get_preferred_width/height(). How does the code know the size to // allocate?" dialog.get_preferred_width(); dialog.set_size_request(400, 70); dialog.show_all(); })); let graphs = gio::SimpleAction::new_stateful("graphs", None, &settings.borrow().display_graph.to_variant()); graphs.connect_activate(clone!(settings => move |g, _| { let mut is_active = false; if let Some(g) = g.get_state() { is_active = g.get().expect("couldn't get bool"); ram_check_box.set_active(!is_active); swap_check_box.set_active(!is_active); network_check_box.set_active(!is_active); if let Some(ref temperature_check_box) = temperature_check_box { temperature_check_box.set_active(!is_active); } } // We need to change the toggle state ourselves. `gio` dark magic. g.change_state(&(!is_active).to_variant()); // We update the setting and save it! settings.borrow_mut().display_graph = !is_active; settings.borrow().save(); })); let temperature = gio::SimpleAction::new_stateful("temperature", None, &settings.borrow() .display_fahrenheit .to_variant()); temperature.connect_activate(move |g, _| { let mut is_active = false; if let Some(g) = g.get_state() { is_active = g.get().expect("couldn't get graph state"); } // We need to change the toggle state ourselves. `gio` dark magic. g.change_state(&(!is_active).to_variant()); // We update the setting and save it! settings.borrow_mut().display_fahrenheit = !is_active; settings.borrow().save(); }); application.add_action(&about); application.add_action(&graphs); application.add_action(&temperature); application.add_action(&settings_action); application.add_action(&new_task); application.add_action(&quit); let filter_entry = procs.filter_entry.clone(); let notebook = note.notebook.clone(); procs.filter_button.connect_clicked(clone!(filter_entry, window => move |_| { if filter_entry.get_visible() { filter_entry.hide(); } else { filter_entry.show_all(); window.set_focus(Some(&filter_entry)); } })); window.connect_key_press_event(move |win, key| { if notebook.get_current_page() == Some(0) { // the process list if key.get_keyval() == gdk::enums::key::Escape { procs.hide_filter(); } else { let ret = procs.search_bar.handle_event(key); match procs.filter_entry.get_text() { Some(ref s) if s.len() > 0 => { procs.filter_entry.show_all(); if win.get_focus() != Some(procs.filter_entry.clone().upcast::()) { win.set_focus(Some(&procs.filter_entry)); } } _ => {} } return Inhibit(ret); } } Inhibit(false) }); window.set_name(utils::MAIN_WINDOW_NAME); application.connect_activate(move |_| { window.show_all(); filter_entry.hide(); window.present(); }); } fn main() { let application = gtk::Application::new(Some(APPLICATION_NAME), gio::ApplicationFlags::empty()) .expect("Initialization failed..."); application.connect_startup(move |app| { build_ui(app); }); glib::set_application_name("process-viewer"); application.run(&args().collect::>()); } process_viewer-0.2.6/src/procs.rs010066400017500001750000000217721353320622000152710ustar0000000000000000use gio::MemoryInputStream; use glib::Bytes; use glib::object::Cast; use gtk::{self, Type}; use gtk::{ BoxExt, ButtonExt, CellLayoutExt, CellRendererExt, ContainerExt, EntryExt, GridExt, GtkListStoreExtManual, OverlayExt, SearchBarExt, TreeModelExt, TreeModelFilterExt, TreeSelectionExt, TreeViewColumnExt, TreeViewExt, WidgetExt, }; use gdk_pixbuf::Pixbuf; use sysinfo::*; use notebook::NoteBook; use std::cell::Cell; use std::collections::HashMap; use std::rc::Rc; #[allow(dead_code)] pub struct Procs { pub left_tree: gtk::TreeView, pub scroll: gtk::ScrolledWindow, pub current_pid: Rc>>, pub kill_button: gtk::Button, pub info_button: gtk::Button, pub vertical_layout: gtk::Box, pub list_store: gtk::ListStore, pub columns: Vec, pub filter_entry: gtk::Entry, pub search_bar: gtk::SearchBar, pub filter_button: gtk::Button, } impl Procs { pub fn new(proc_list: &HashMap, note: &mut NoteBook) -> Procs { let left_tree = gtk::TreeView::new(); let scroll = gtk::ScrolledWindow::new(None::<>k::Adjustment>, None::<>k::Adjustment>); let current_pid = Rc::new(Cell::new(None)); let kill_button = gtk::Button::new_with_label("End task"); let info_button = gtk::Button::new_with_label("More information"); let filter_button = gtk::Button::new(); let memory_stream = MemoryInputStream::new_from_bytes( &Bytes::from_static(include_bytes!("../assets/magnifier.png"))); let image = Pixbuf::new_from_stream_at_scale(&memory_stream, 32, 32, true, None::<&gio::Cancellable>); if let Ok(image) = image { let image = gtk::Image::new_from_pixbuf(Some(&image)); filter_button.set_image(Some(&image)); filter_button.set_always_show_image(true); } else { filter_button.set_label("Filter"); } // TODO: maybe add an 'X' button to close search as well? let overlay = gtk::Overlay::new(); let filter_entry = gtk::Entry::new(); let search_bar = gtk::SearchBar::new(); // We put the filter entry at the right bottom. filter_entry.set_halign(gtk::Align::End); filter_entry.set_valign(gtk::Align::End); filter_entry.hide(); // By default, we don't show it. search_bar.connect_entry(&filter_entry); search_bar.set_show_close_button(true); overlay.add_overlay(&filter_entry); let mut columns: Vec = Vec::new(); let list_store = gtk::ListStore::new(&[ // The first four columns of the model are going to be visible in the view. Type::U32, // pid Type::String, // name Type::String, // CPU Type::U64, // mem // These two will serve as keys when sorting by process name and CPU usage. Type::String, // name_lowercase Type::F32, // CPU_f32 ]); for pro in proc_list.values() { create_and_fill_model(&list_store, pro.pid().as_u32(), &format!("{:?}", &pro.cmd()), &pro.name(), pro.cpu_usage(), pro.memory()); } left_tree.set_headers_visible(true); scroll.add(&left_tree); overlay.add(&scroll); let vertical_layout = gtk::Box::new(gtk::Orientation::Vertical, 0); let horizontal_layout = gtk::Grid::new(); left_tree.connect_cursor_changed( clone!(current_pid, kill_button, info_button => move |tree_view| { let selection = tree_view.get_selection(); let (pid, ret) = if let Some((model, iter)) = selection.get_selected() { if let Some(x) = model.get_value(&iter, 0).get::().map(|x| x as Pid) { (Some(x), true) } else { (None, false) } } else { (None, false) }; current_pid.set(pid); kill_button.set_sensitive(ret); info_button.set_sensitive(ret); } )); kill_button.set_sensitive(false); info_button.set_sensitive(false); vertical_layout.pack_start(&overlay, true, true, 0); horizontal_layout.attach(&info_button, 0, 0, 4, 1); horizontal_layout.attach_next_to(&kill_button, Some(&info_button), gtk::PositionType::Right, 4, 1); horizontal_layout.attach_next_to(&filter_button, Some(&kill_button), gtk::PositionType::Right, 1, 1); horizontal_layout.set_column_homogeneous(true); vertical_layout.pack_start(&horizontal_layout, false, true, 0); // The filter part. let filter_model = gtk::TreeModelFilter::new(&list_store, None); filter_model.set_visible_func(clone!(filter_entry => move |model, iter| { if !filter_entry.get_visible() || filter_entry.get_text_length() < 1 { return true; } if let Some(text) = filter_entry.get_text() { if text.is_empty() { return true; } let text: &str = text.as_ref(); // TODO: Maybe add an option to make searches case sensitive? let pid = model.get_value(iter, 0) .get::() .map(|p| p.to_string()) .unwrap_or_else(String::new); let name = model.get_value(iter, 1) .get::() .map(|s| s.to_lowercase()) .unwrap_or_else(String::new); pid.contains(text) || text.contains(&pid) || name.contains(text) || text.contains(&name) } else { true } })); // For the filtering to be taken into account, we need to add it directly into the // "global" model. let sort_model = gtk::TreeModelSort::new(&filter_model); left_tree.set_model(Some(&sort_model)); append_column("pid", &mut columns, &left_tree, None); append_column("process name", &mut columns, &left_tree, Some(200)); append_column("cpu usage", &mut columns, &left_tree, None); append_column("memory usage (in kB)", &mut columns, &left_tree, None); // When we click the "name" column the order is defined by the // "name_lowercase" effectively making the built-in comparator ignore case. columns[1].set_sort_column_id(4); // Likewise clicking the "CPU" column sorts by the "CPU_f32" one because // we want the order to be numerical not lexicographical. columns[2].set_sort_column_id(5); filter_entry.connect_property_text_length_notify(move |_| { filter_model.refilter(); }); note.create_tab("Process list", &vertical_layout); Procs { left_tree, scroll, current_pid, kill_button, info_button, vertical_layout: vertical_layout.downcast::().expect("downcast failed"), list_store, columns, filter_entry, search_bar, filter_button, } } pub fn hide_filter(&self) { self.filter_entry.hide(); self.filter_entry.set_text(""); self.search_bar.set_search_mode(false); } } fn append_column(title: &str, v: &mut Vec, left_tree: >k::TreeView, max_width: Option) { let id = v.len() as i32; let renderer = gtk::CellRendererText::new(); if title != "process name" { renderer.set_property_xalign(1.0); } let column = gtk::TreeViewColumn::new(); column.set_title(title); column.set_resizable(true); if let Some(max_width) = max_width { column.set_max_width(max_width); column.set_expand(true); } column.set_min_width(10); column.pack_start(&renderer, true); column.add_attribute(&renderer, "text", id); column.set_clickable(true); column.set_sort_column_id(id); left_tree.append_column(&column); v.push(column); } pub fn create_and_fill_model(list_store: >k::ListStore, pid: u32, cmdline: &str, name: &str, cpu: f32, memory: u64) { if cmdline.is_empty() || name.is_empty() { return; } list_store.insert_with_values(None, &[0, 1, 2, 3, 4, 5], &[&pid, &name, &format!("{:.1}", cpu), &memory, &name.to_lowercase(), &cpu ]); } process_viewer-0.2.6/src/settings.rs010066400017500001750000000164521353374506300160170ustar0000000000000000// // Process viewer // // Copyright (c) 2019 Guillaume Gomez // use glib; use gtk; use gio::ApplicationExt; use gtk::{ BoxExt, ContainerExt, DialogExt, GridExt, GtkWindowExt, SpinButtonExt, SpinButtonSignals, WidgetExt, }; use std::cell::RefCell; use std::fs::{create_dir_all, File}; use std::io::Read; use std::path::PathBuf; use std::rc::Rc; use APPLICATION_NAME; use RequiredForSettings; use {setup_timeout, setup_network_timeout, setup_system_timeout}; use utils::{get_app, get_main_window}; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct Settings { pub display_fahrenheit: bool, pub display_graph: bool, // Timer length in milliseconds (500 minimum!). pub refresh_processes_rate: u32, // Timer length in milliseconds (500 minimum!). pub refresh_system_rate: u32, // Timer length in milliseconds (500 minimum!). pub refresh_network_rate: u32, } impl Default for Settings { fn default() -> Settings { Settings { display_fahrenheit: false, display_graph: false, refresh_processes_rate: 1500, refresh_system_rate: 2000, refresh_network_rate: 1500, } } } impl Settings { fn load_from_file(p: &PathBuf)->Result { let mut input = String::new(); let mut file = File::open(p). map_err(|e| format!("Error while opening '{}': {}", p.display(), e))?; file.read_to_string(&mut input). map_err(|e|format!("Error while opening '{}': {}", p.display(), e))?; toml::from_str(&input). map_err(|e| format!("Error while opening '{}': {}", p.display(), e)) } pub fn load() -> Settings { let s = Self::get_settings_file_path(); if s.exists() && s.is_file() { match Self::load_from_file(&s) { Ok(settings) => { settings } Err(e) => { show_error_dialog( false, &e ); Settings::default() } } } else { Settings::default() } } pub fn get_settings_file_path() -> PathBuf { let mut path = glib::get_user_config_dir().unwrap_or_else(|| PathBuf::from(".")); path.push(APPLICATION_NAME); path.push("settings.toml"); path } pub fn save(&self) { let s = Self::get_settings_file_path(); if !s.exists() { if let Some(parent_dir) = s.parent() { if !parent_dir.exists() { if let Err(e) = create_dir_all(parent_dir) { show_error_dialog( false, format!( "Error while trying to build settings snapshot_directory '{}': {}", parent_dir.display(), e ).as_str(), ); return; } } } } match toml::to_string_pretty(&self) { Ok(output) => { if let Err(e) = std::fs::write(&s, output) { show_error_dialog( false, format!("Error while trying to save file: {}", e).as_str(), ); } } Err(e) => { show_error_dialog( false, format!("Error while trying to save file: {}", e).as_str(), ); } } } } fn show_error_dialog(fatal: bool, text: &str) { let dialog = gtk::MessageDialog::new( get_main_window().as_ref(), gtk::DialogFlags::MODAL, gtk::MessageType::Error, gtk::ButtonsType::Ok, text, ); dialog.connect_response(move |dialog, _| { dialog.destroy(); if fatal { get_app().quit(); } }); dialog.set_resizable(false); dialog.show_all(); } pub fn build_spin( label: &str, grid: >k::Grid, top: i32, refresh: u32, ) -> gtk::SpinButton { // Refresh rate. let refresh_label = gtk::Label::new(Some(label)); // We allow 0.5 to 5 seconds, in 0.1 second steps. let refresh_entry = gtk::SpinButton::new_with_range(0.5, 5., 0.1); refresh_label.set_halign(gtk::Align::Start); refresh_entry.set_hexpand(true); refresh_entry.set_value(f64::from(refresh) / 1000.); grid.attach(&refresh_label, 0, top, 1, 1); grid.attach(&refresh_entry, 1, top, 3, 1); refresh_entry } pub fn show_settings_dialog( settings: &Rc>, rfs: &Rc>, ) { let bsettings = &*settings.borrow(); // Create an empty dialog with close button. let dialog = gtk::Dialog::new_with_buttons( Some("Process Viewer settings"), get_main_window().as_ref(), gtk::DialogFlags::MODAL, &[("Close", gtk::ResponseType::Close)], ); // All the UI widgets are going to be stored in a grid. let grid = gtk::Grid::new(); grid.set_column_spacing(4); grid.set_row_spacing(4); grid.set_margin_bottom(12); let refresh_procs = build_spin("Processes refresh rate (in seconds)", &grid, 0, bsettings.refresh_processes_rate); let refresh_network = build_spin("System network refresh rate (in seconds)", &grid, 1, bsettings.refresh_network_rate); let refresh_sys = build_spin("System information refresh rate (in seconds)", &grid, 2, bsettings.refresh_system_rate); // Put the grid into the dialog's content area. let content_area = dialog.get_content_area(); content_area.pack_start(&grid, true, true, 0); content_area.set_border_width(10); // Finally connect to all kinds of change notification signals for the different UI widgets. // Whenever something is changing we directly save the configuration file with the new values. refresh_procs.connect_value_changed(clone!(settings, rfs => move |entry| { let mut settings = settings.borrow_mut(); let refresh_processes_rate = settings.refresh_processes_rate; setup_timeout(refresh_processes_rate, &rfs); settings.refresh_processes_rate = (entry.get_value() * 1000.) as u32; settings.save(); })); refresh_network.connect_value_changed(clone!(settings, rfs => move |entry| { let mut settings = settings.borrow_mut(); let refresh_network_rate = settings.refresh_network_rate; setup_network_timeout(refresh_network_rate, &rfs); settings.refresh_network_rate = (entry.get_value() * 1000.) as u32; settings.save(); })); refresh_sys.connect_value_changed(clone!(settings, rfs => move |entry| { let refresh_system_rate = settings.borrow().refresh_system_rate; setup_system_timeout(refresh_system_rate, &rfs, &settings); let mut settings = settings.borrow_mut(); settings.refresh_system_rate = (entry.get_value() * 1000.) as u32; settings.save(); })); dialog.connect_response(move |dialog, _| { dialog.destroy(); }); dialog.set_resizable(false); dialog.show_all(); } process_viewer-0.2.6/src/utils.rs010066400017500001750000000051411353374474600153170ustar0000000000000000use graph::Graph; use gio; use glib::Cast; use gtk::{GtkApplicationExt, Inhibit, WidgetExt}; use std::cell::RefCell; use std::ops::Index; use std::rc::Rc; pub const MAIN_WINDOW_NAME: &str = "main-window"; #[derive(Debug)] pub struct RotateVec { data: Vec, start: usize, } impl RotateVec { pub fn new(d: Vec) -> RotateVec { RotateVec { data: d, start: 0, } } pub fn len(&self) -> usize { self.data.len() } pub fn is_empty(&self) -> bool { self.data.is_empty() } pub fn move_start(&mut self) { if self.start > 0 { self.start -= 1; } else { self.start = self.data.len() - 1; } } /*pub fn get(&self, index: usize) -> Option<&T> { self.data.get(self.get_real_pos(index)) }*/ pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { let pos = self.get_real_pos(index); self.data.get_mut(pos) } fn get_real_pos(&self, index: usize) -> usize { if self.start + index >= self.data.len() { self.start + index - self.data.len() } else { index + self.start } } } pub fn format_number(mut nb: u64) -> String { if nb < 1000 { return format!("{} B", nb); } nb >>= 10; // / 1_024 if nb < 100_000 { format!("{} kB", nb) } else if nb < 10_000_000 { format!("{} MB", nb >> 10) // / 1_024 } else if nb < 10_000_000_000 { format!("{} GB", nb >> 20) // / 1_048_576 } else { format!("{} TB", nb >> 30) // / 1_073_741_824 } } pub fn connect_graph(graph: Graph) -> Rc> { let area = graph.area.clone(); let graph = Rc::new(RefCell::new(graph)); area.connect_draw(clone!(graph => move |w, c| { graph.borrow() .draw(c, f64::from(w.get_allocated_width()), f64::from(w.get_allocated_height())); Inhibit(false) })); graph } impl Index for RotateVec { type Output = T; fn index(&self, index: usize) -> &T { &self.data[self.get_real_pos(index)] } } pub fn get_app() -> gtk::Application { gio::Application::get_default() .expect("No default application") .downcast::() .expect("Default application has wrong type") } pub fn get_main_window() -> Option { for window in get_app().get_windows() { if window.get_name().as_ref().map(|ref s| s.as_str()) == Some(MAIN_WINDOW_NAME) { return Some(window); } } None } process_viewer-0.2.6/.cargo_vcs_info.json0000644000000001120000000000000141310ustar00{ "git": { "sha1": "998acca74e151a4223c1f1edaab7fff18af962da" } } process_viewer-0.2.6/Cargo.lock0000644000000624640000000000000121260ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "arrayvec" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atk" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "atk-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cairo-rs" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cairo-sys-rs" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cc" version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "crossbeam-deque" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-epoch" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-queue" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crossbeam-utils" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "doc-comment" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "either" version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "fragile" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "gdk" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-pixbuf" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-pixbuf-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gdk-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gio" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gio-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glib" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glib-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gobject-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gtk" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gtk-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "memoffset" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pango" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pango-sys" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "pkg-config" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "process_viewer" version = "0.2.6" dependencies = [ "cairo-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", "sysinfo 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rayon-core" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scopeguard" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" version = "0.15.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sysinfo" version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "toml" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum atk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "86b7499272acf036bb5820c6e346bbfb5acc5dceb104bc2c4fd7e6e33dfcde6a" "checksum atk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067531f752c01027f004032bb676e715aba74b75e904a7340a61ce3fb0b61b0" "checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" "checksum cairo-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac3ead6489c4d701dabeb6e20795654e7c574f6de400a80d922328664beb95e" "checksum cairo-sys-rs 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "90a1ec04603a78c111886a385edcec396dbfbc57ea26b9e74aeea6a1fe55dcca" "checksum cc 1.0.37 (registry+https://github.com/rust-lang/crates.io-index)" = "39f75544d7bbaf57560d2168f28fd649ff9c76153874db88bdbdfd839b1a7e7d" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" "checksum doc-comment 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "923dea538cea0aa3025e8685b20d6ee21ef99c4f77e954a30febbaac5ec73a97" "checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" "checksum fragile 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f8140122fa0d5dcb9fc8627cfce2b37cc1500f752636d46ea28bc26785c2f9" "checksum gdk 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6243e995f41f3a61a31847e54cc719edce93dd9140c89dca3b9919be1cfe22d5" "checksum gdk-pixbuf 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9726408ee1bbada83094326a99b9c68fea275f9dbb515de242a69e72051f4fcc" "checksum gdk-pixbuf-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1d6778abf5764b9080a9345a16c5d16289426a3b3edd808a29a9061d431c465" "checksum gdk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ebe06357212127f50575b535bdb04638f5d375bb41062287abc6c94e5b8067b" "checksum gio 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6261b5d34c30c2d59f879e643704cf54cb44731f3a2038000b68790c03e360e3" "checksum gio-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "778b856a70a32e2cc5dd5cc7fa1b0c4b6df924fdf5c82984bc28f30565657cfe" "checksum glib 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d70d737019da0473a7cd6d9240571cf58c6897dcb10edf32b90774f4ba237c1b" "checksum glib-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4b86a9169fbc9cf9a0ef315039c2304b09d5c575c5fde7defba3576a0311b863" "checksum gobject-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "61d55bc9202447ca776f6ad0048c36e3312010f66f82ab478e97513e93f3604b" "checksum gtk 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "709f1074259d4685b96133f92b75c7f35b504715b0fcdc96ec95de2607296a60" "checksum gtk-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bbd9395497ae1d1915d1d6e522d51ae8745bf613906c34ac191c411250fc4025" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" "checksum pango 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "393fa071b144f8ffb83ede273758983cf414ca3c0b1d2a5a9ce325b3ba3dd786" "checksum pango-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1ee97abcad820f9875e032656257ad1c790e7b11a0e6ce2516a8f5b0d8f8213f" "checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum rayon 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" "checksum rayon-core 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" "checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" "checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" "checksum sysinfo 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9b406411a35a1c863ee5ec1db99aade75c5b47eaa61e9b42482567bafc844bc2" "checksum toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b8c96d7873fa7ef8bdeb3a9cda3ac48389b4154f32b9803b4bc26220b677b039" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"